Cannot bind properties to custom ItemWriter - spring-batch

I have a simple item writer looks like this:
public class EntityItemWriter<T> implements ItemWriter<T>, InitializingBean {
private String name;
#Override
public void write(List<? extends T> items) throws Exception {
//writes to db
}
#Override
public void afterPropertiesSet() throws Exception {
Assert.hasLength(name); //assertion fails
}
public void setName(String name) {
this.name = name;
}
}
and my job-definition.xml has a bean like this:
<bean id="EntityItemWriter" class="com.example.EntityItemWriter" scope="step">
<property name="name" value="someRandomString" />
</bean>
When the batch job is in writing step, name property of EntityItemWriter is not set to "someRandomString" and stays as null. Is there anything that I am missing?
spring-batch version: 2.1.0.M3
spring version: 3.1.0.RELEASE

Related

Ignite put a POJO bean value to cache by rest api

I want to put POJO bean key value pair to Ignite cache using REST API. I can put string key value pair to cache. but here i want to put a class as key value. How can i create REST url for this.
My config file look like this
<property name="cacheConfiguration">
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<!-- Set a cache name. -->
<property name="name" value="cacheName"/>
<!-- Set cache mode. -->
<property name="cacheMode" value="PARTITIONED"/>
<property name="indexedTypes">
<list>
<value>com.test.EntitlementKey</value>
<value>com.test.Entitlement</value>
</list>
</property>
</bean>
</property>
And Entitilement Class look like this
import org.apache.ignite.cache.query.annotations.QuerySqlField;
public class Entitlement {
#QuerySqlField
private String name;
#QuerySqlField
private String desc;
public String getname() {
return name;
}
public void setname(String name) {
this.name = name;
}
public String getdesc() {
return desc;
}
public void setdesc(String desc) {
this.desc = desc;
}
And EntitlementKey class
public class EntitlementKey {
#QuerySqlField(index = true)
private String eid;
#QuerySqlField
private String id;
public String getEid() {
return eid;
}
public void setEid(String eid) {
this.eid = eid;
}
public String getid() {
return id;
}
public void setid(String id) {
this.id = id;
}
Currently, in master, you can find an implementation of ticket, which allows using internal java types as keys and values.
At this moment if you want to use your custom types, you need to use ConnectorMessageInterceptor interface. Once you implement and configure it, you will start receiving a callback for all keys and values passed back and forth. So you can encode your object as a String somehow and then convert it to a real object inside interceptor. And the opposite: before returning object from the cache you can convert it to some String form.

Want to get total row count in footer of spring batch without customizing writer(Delegate Pattern)

This is my footer class:--
public class SummaryFooterCallback extends StepExecutionListenerSupport implements FlatFileFooterCallback{
private StepExecution stepExecution;
#Override
public void writeFooter(Writer writer) throws IOException {
writer.write("footer - number of items written: " + stepExecution.getWriteCount());
}
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
}
This is my xml:--
<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" ref="outputResource" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
</property>
<property name="headerCallback" ref="headerCopier" />
<property name="footerCallback" ref="footerCallback" />
</bean>
<bean id="footerCallback" class="org.springframework.batch.sample.support.SummaryFooterCallback"/>
Failing at stepExecution.getWriteCount() with nullpointer Exception.
No, I haven't registered callback as a listener in the step. I am new to Java and Spring Batch, referring to your book Pro Spring Batch but not able to get the solution of the assigned task.
You need to set the writer in scope step. Here you have a java based config that worked for me.
#Bean
#StepScope
public ItemStreamWriter<Entity> writer(FlatFileFooterCallback footerCallback) {
FlatFileItemWriter<Entity> writer = new FlatFileItemWriter<Entity>();
...
writer.setFooterCallback(footerCallback);
...
return writer;
}
#Bean
#StepScope
private FlatFileFooterCallback getFooterCallback(#Value("#{stepExecution}") final StepExecution context) {
return new FlatFileFooterCallback() {
#Override
public void writeFooter(Writer writer) throws IOException {
writer.append("count: ").append(String.valueOf(context.getWriteCount()));
}
};
}

Using MultiResourceItemReader to read 2 plain text file and write into single file

My batch job will generate 2 text files with string format per line. I created a reader
<bean id="myMultiResourceReader"
class=" org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="file:D:/MY/sample/*.txt" />
</bean>
<bean id="myFinalWriter" class="org.springframework.batch.item.file.FlatFileItemWriter"
scope="step">
<property name="resource" value="${test.file3}" />
<property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
</property>
<property name="footerCallback" ref="myFinalCustomItemWriter" />
<property name="headerCallback" ref="myFinalCustomItemWriter" />
</bean>
<bean id="myFinalCustomItemWriter" class="my.process.MyWriter"
scope="step">
<property name="delegate" ref="myFinalWriter" />
<property name="stepContext" value="#{stepExecution.stepName}" />
</bean>
I was getting this error:
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'com.sun.proxy.$Proxy68 implementing org.springframework.batch.item.file.ResourceAwareItemWriterItemStream,org.springframework.beans.factory.InitializingBean,org.springframework.batch.item.ItemStreamWriter,org.springframework.batch.item.ItemStream,org.springframework.aop.scope.ScopedObject,java.io.Serializable,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'org.springframework.batch.item.file.FlatFileItemWriter' for property 'delegate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy68 implementing org.springframework.batch.item.file.ResourceAwareItemWriterItemStream,org.springframework.beans.factory.InitializingBean,org.springframework.batch.item.ItemStreamWriter,org.springframework.batch.item.ItemStream,org.springframework.aop.scope.ScopedObject,java.io.Serializable,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.batch.item.file.FlatFileItemWriter] for property 'delegate': no matching editors or conversion strategy found
Basically I just want to combine two plain file, and append the total count at footer. Then delete away the both input file. Can help?
MyWriter.java
public class MyWriter implements ItemWriter<String>, FlatFileFooterCallback, FlatFileHeaderCallback, ItemStream{
private static Logger log = Logger.getLogger(MyWriter.class);
private FlatFileItemWriter<String> delegate;
private int recordCount = 0;
private String stepContext;
public void writeFooter(Writer writer) throws IOException {
writer.write("#" + recordCount);
}
public void writeHeader(Writer writer) throws IOException {
writer.write("#" + StringUtil.getSysDate());
}
public void setDelegate(FlatFileItemWriter<String> delegate) {
this.delegate = delegate;
}
public void write(List<? extends String> list) throws Exception {
int chunkRecord = 0;
for (String item : list) {
chunkRecord++;
}
delegate.write(list);
recordCount += chunkRecord;
}
public void close() throws ItemStreamException {
this.delegate.close();
}
public void open(ExecutionContext arg0) throws ItemStreamException {
this.delegate.open(arg0);
}
public void update(ExecutionContext arg0) throws ItemStreamException {
this.delegate.update(arg0);
}
public void setStepContext(String stepContext) {
this.stepContext = stepContext;
}
}
As Luca Basso Ricci already pointed out, the problem is your delegate definition in MyWriter. Since Spring creates proxies for it beans, it will not recognize your FlatFileItemReader as an actual instance of FlatFileItemWriter and, therefore, the setDelegate(FlatFileItemWriter delegate) will fail.
Use an ItemStreamWriter in MyWriter. As you see in the exception message, the created proxy does provide this interface. Hence, it can be inserted
This will solve the delegation to write, open, close, and update method. In order to write the header and footer, you need to implement a HeaderCallback and FooterCallback and set it directly in the definition of your FlatFileItemWriter.
Implementing the HeaderCallback is not a problem since you only set the systemdate.
As FooterCallback, make your own Bean. Use it in the FlatFileItemWriter to write the footer. Add an "increaseCount" method to it and use it in your MyWriter Bean to increase the written count.
public void write(List<? extends String> list) throws Exception {
myFooterCallback.increaseCount(list.size());
delegate.write(list);
}
Another possible option would be to directly extend MyWriter from FlatFileItemWriter:
public class MyWriter extends FlatFileItemWriter<String> implements FlatFileFooterCallback, FlatFileHeaderCallback{
private static Logger log = Logger.getLogger(MyWriter.class);
private int recordCount = 0;
private String stepContext;
public void writeFooter(Writer writer) throws IOException {
writer.write("#" + recordCount);
}
public void writeHeader(Writer writer) throws IOException {
writer.write("#" + StringUtil.getSysDate());
}
public void afterPropertiesSet() {
setFooterCallback(this);
setHeaderCallback(this);
super.afterPropertiesSet();
}
public void write(List<? extends String> list) throws Exception {
super.write(list);
recordCount += list.size();
}
}
Configuration in your XML would look like this:
<bean id="myFinalCustomItemWriter" class="my.process.MyWriter" scope="step">
<property name="resource" value="${test.file3}" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.PassThroughLineAggregator" />
</property>
<property name="stepContext" value="#{stepExecution.stepName}" />
</bean>

Use Dispatcher servlet for interceptor call only for selective URL

I am having a controller class with two types of methods. first type must execute its body as soon as URL is hit suppose "http://localhost:8080/SpringAOP1/welcome/two/aa". Here SpringAOP1 is my project,welcome is the controller class name(Which is set with annotation) and two is the function name with parameter "aa".
The Second type of function needs to be pre-handled by a interceptor.So when URL is hit suppose "http://localhost:8080/SpringAOP1/welcome/intercep/six/aa" the interceptor must be called. Here i am passing the "intercep" parameter with all those functions of second type which needs interceptor call.
Controller class:-
#Controlle
#RequestMapping("/welcome")
public class HelloController {
#RequestMapping(method = RequestMethod.GET)
public String printWelcome1(ModelMap model) {
model.addAttribute("message", "Spring 3 MVC Hello World 1");
return "hello";
}
#RequestMapping( value="two/{name}", method = RequestMethod.GET)
public String printWelcome2(#PathVariable String name,ModelMap model) {
model.addAttribute("message", "Spring 3 MVC Hello World 2");
model.addAttribute("value", name);
return "hello";
}
#RequestMapping( value="intercep/six/{name}", method = RequestMethod.GET)
public String printWelcome6(#PathVariable String name,ModelMap model) {
model.addAttribute("message", "Spring 3 MVC Hello World 6");
model.addAttribute("value", name);
return "hello";
}
`
Dispatcher servlet
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.mkyong.common.controller" />
<bean name="HijackBeforeMethod" class="com.mkyong.common.controller.HijackBeforeMethod"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/intercep/*"/>
<bean id="HijackBeforeMethod" class="com.mkyong.common.controller.HijackBeforeMethod" />
</mvc:interceptor>
</mvc:interceptors>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="0" />
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
HijackBeforeMethod
(This is the interceptor Class which is called)
public class HijackBeforeMethod extends HandlerInterceptorAdapter {
public Boolean comp(String u,String p)
{
Map mMap = new HashMap();
mMap.put("aa", "bb");
mMap.put("mm", "nn");
mMap.put("xx", "yy");
Iterator iter = mMap.entrySet().iterator();
String k,v;
Boolean flg=false;
while (iter.hasNext())
{
Map.Entry mEntry = (Map.Entry) iter.next();
if(mEntry.getKey().equals(u) && mEntry.getValue().equals(p))
flg=true;
}
return flg;
}
public boolean preHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o) throws Exception {
String u="aa";
String p="bb";
//System.out.println(" The username is--> "+u+" and pass is --> "+p);
Boolean ch=comp(u,p);
if(ch)
{
if(httpServletRequest.getMethod().equalsIgnoreCase("get")){
String uri = httpServletRequest.getRequestURI();
System.out.println("The method is GET :: "+ uri);
}
else
{
return false;
}
}
}
Please help me finding this. I am in greate trouble..
You can use the WebRequestInterceptor interface instead of HandlerInterceptorAdapter.
The following code segment is working fine for me.
public class FlashInterceptor implements WebRequestInterceptor {
private Flash flash;
#Autowired
public void setFlash(Flash flash) {
this.flash = flash;
}
#Override
public void preHandle(WebRequest request) throws Exception {
final Map<String, Object> messages = flash.getMessages();
if (messages.containsKey("flash_info")) {
request.setAttribute("flash_info", messages.get("flash_info"),
RequestAttributes.SCOPE_REQUEST);
request.setAttribute("flash_info_params",
messages.get("flash_info_params"),
RequestAttributes.SCOPE_REQUEST);
}
if (messages.containsKey("flash_error")) {
request.setAttribute("flash_error", messages.get("flash_error"),
RequestAttributes.SCOPE_REQUEST);
request.setAttribute("flash_error_params",
messages.get("flash_error_params"),
RequestAttributes.SCOPE_REQUEST);
}
if (messages.containsKey("flash_success")) {
request.setAttribute("flash_success",
messages.get("flash_success"),
RequestAttributes.SCOPE_REQUEST);
request.setAttribute("flash_success_params",
messages.get("flash_success_params"),
RequestAttributes.SCOPE_REQUEST);
}
flash.reset();
}
#Override
public void postHandle(WebRequest request, ModelMap model) throws Exception {
}
#Override
public void afterCompletion(WebRequest request, Exception ex)
throws Exception {
}
}

Spring Data MongoTemplate not throwing DataAccessException

I am trying to learn MongoDB and in the same time write a simple REST application using Spring framework.
I have a simple model:
#Document
public class Permission extends documentBase{
#Indexed(unique = true)
private String name;
public Permission(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then I have a simple DAO:
#Repository
#Transactional
#Profile({"production","repositoryTest","mongoIntegrationTest"})
public class DaoImpl implements DAO {
#Autowired
protected MongoTemplate mongoTemplate;
public <T> T addObject(T object) {
mongoTemplate.insert(object);
return object;
}
The I have my integration tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:mvc-dispatcher-servlet.xml", classpath:IntegrationContext.xml"},loader = TestXmlContextLoader.class)
#ActiveProfiles("mongoIntegrationTest")
public class RepositoryIntegrationTest extends AccountTestBase{
#Autowired DAO repository;
#Autowired WebApplicationContext wac;
#Test
public void AddPermission() {
Permission permission_1 = new Permission("test");
Permission permission_2 = new Permission("test");
repository.addObject(permission_1);
repository.addObject(permission_2);
}
}
My configuration:
<!-- MongoDB host -->
<mongo:mongo host="${mongo.host.name}" port="${mongo.host.port}"/>
<!-- Template for performing MongoDB operations -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"
c:mongo-ref="mongo" c:databaseName="${mongo.db.name}"/>
I am expecting that, on adding "permission_2" their would be a exception thrown from MongoDB, which would be translated by Spring,, and catched as a DataAccessException in the DAO.
Looking at the log files from MongoDb I can see that a duplicated exception is thrown but it never reaches my DAO.
So,, I guess I am doing something wrong,,, but at the moment,, I am blind to my own misstakes.
//lg
Make sure you configure the WriteConcern of the MongoTemplate to something non-default (e.g. WriteConcern.SAFE). By default MongoDB is in fire-and-forget mode and does not throw exceptions on index violations or server errors in general.
Still struggling with this.
Finnally I succeded to get the exeption translation working. MongoDb throws a exception which is translated to Spring Data exception.
Now I am stuck with another problem.
My DAO shown above has also the following code:
#ExceptionHandler(DataAccessException.class)
public void handleDataAccessException(DataAccessException ex) {
// For debug only
DataAccessException test = ex;
test.printStackTrace();
}
I was expecting this code to catch the exception thrown,, but this is not the case.
Why not?
//lasse