I want to create a batch using Spring batch to read from an ini file and save the data in database but when I chekced the org.springframework.batch.item.file.FlatFileItemReader class I didn't find a way to parse my data from the ini file , I tried to combine the ini4j API with the spring batch but no result
my ini file :
[Cat]
a=1
b= 2
c= 3
d= 4
e= 5
f= 6
[Cat2]
a=11
b= 21
c= 31
d= 41
e= 51
f= 61
What you can do is define your own ItemStreamReader that wraps a delegate ItemStreamReader, which is just a FlatFileItemReader that uses a PatternMatchingCompositeLineMapper as the line mapper. In your ItemStreamReader, loop to read lines from your delegate and if the line is an instance of a Property domain object then add it to a list in a Section domain object. What the PatternMatchingCompositeLineMapper allows you do do is check the line for a pattern match, and pass it to the right tokenizer and fieldSetMapper for the job.
Doing it this way will allow you to read multiple lines into one Section domain object that holds a List<Property>.
public class Section {
private String name;
private List<Property> properties;
// getters and setters
#Override
public String toString() {
StringBuilder sb = new StringBuilder(name);
for (Property prop: properties) {
sb.append("," + prop.getKey() + "=" + prop.getValue());
}
return sb.toString();
}
}
public class Property {
private String key;
private String value;
// getters and setters
}
For you custom ItemStreamReader you would do this. You can see that the reading is delegated to another reader, which you will define later
import java.util.ArrayList;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
public class IniFileItemReader implements ItemStreamReader<Object> {
private Object curItem = null;
private ItemStreamReader<Object> delegate;
#Override
public Object read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
if (curItem == null) {
curItem = (Section) delegate.read();
}
Section section = (Section) curItem;
curItem = null;
if (section != null) {
section.setProperties(new ArrayList<Property>());
while (peek() instanceof Property) {
section.getProperties().add((Property) curItem);
curItem = null;
}
}
return section;
}
private Object peek() throws Exception {
if (curItem == null) {
curItem = delegate.read();
}
return curItem;
}
public void setDelegate(ItemStreamReader<Object> delegate) {
this.delegate = delegate;
}
#Override
public void close() throws ItemStreamException {
delegate.close();
}
#Override
public void open(ExecutionContext arg0) throws ItemStreamException {
delegate.open(arg0);
}
#Override
public void update(ExecutionContext arg0) throws ItemStreamException {
delegate.update(arg0);
}
}
Then in your config you define the deleagte reader with the PatternMatchingCompositeLineMapper
<bean id="inputFile" class="org.springframework.core.io.FileSystemResource"
scope="step">
<constructor-arg value="#{jobParameters[inputFile]}"></constructor-arg>
</bean>
<bean id="sectionFileReader"
class="com.underdogdevs.springbatch.reader.IniFileItemReader">
<property name="delegate" ref="trueSectionFileReader"></property>
</bean>
<bean id="trueSectionFileReader"
class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="lineMapper">
<bean
class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper">
<property name="tokenizers">
<map>
<entry key="[*" value-ref="sectionLineTokenizer">
</entry>
<entry key="*" value-ref="propertyLineTokenizer"></entry>
</map>
</property>
<property name="fieldSetMappers">
<map>
<entry key="[*" value-ref="sectionFieldSetMapper">
</entry>
<entry key="*" value-ref="propertyFieldSetMapper">
</entry>
</map>
</property>
</bean>
</property>
<property name="resource" ref="inputFile"></property>
</bean>
<bean id="sectionLineTokenizer"
class="com.underdogdevs.springbatch.tokenizer.SectionLineTokenizer">
</bean>
<bean id="sectionFieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="section"></property>
</bean>
<bean id="section" class="com.underdogdevs.springbatch.domain.Section"
scope="prototype">
</bean>
<bean id="propertyLineTokenizer"
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="="></property>
<property name="names" value="key,value"></property>
</bean>
<bean id="propertyFieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="property"></property>
</bean>
<bean id="property" class="com.underdogdevs.springbatch.domain.Property"
scope="prototype">
</bean>
You see that I also used a custom LineTozenizer. I probably could've just used a DelimitedLineTokenizer, but by the time I realized it, I had already defined the class
import org.springframework.batch.item.file.transform.DefaultFieldSetFactory;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.file.transform.FieldSetFactory;
import org.springframework.batch.item.file.transform.LineTokenizer;
public class SectionLineTokenizer implements LineTokenizer {
private final String nameField = "name";
private final FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory();
#Override
public FieldSet tokenize(String line) {
String name = line.replaceAll("\\[", "").replaceAll("\\]", "").trim();
return fieldSetFactory.create(new String[] { name },
new String[] { nameField });
}
}
Using the following writer and job
<bean id="outputFile"
class="org.springframework.core.io.FileSystemResource" scope="step">
<constructor-arg value="#{jobParameters[outputFile]}"></constructor-arg>
</bean>
<bean id="outputFileWriter"
class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" ref="outputFile"></property>
<property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.PassThroughLineAggregator">
</bean>
</property>
</bean>
<batch:step id="outputStep">
<batch:tasklet>
<batch:chunk commit-interval="10" reader="sectionFileReader"
writer="outputFileWriter">
<batch:streams>
<batch:stream ref="sectionFileReader" />
<batch:stream ref="trueSectionFileReader" />
</batch:streams>
</batch:chunk>
</batch:tasklet>
</batch:step>
<batch:job id="iniJob">
<batch:step id="step1" parent="outputStep"></batch:step>
</batch:job>
And using this ini file
[Cat]
a=1
b=2
c=3
d=4
e=5
f=6
[Cat2]
a=11
b=21
c=31
d=41
e=51
f=61
I get the following output, which is the format in my toString() of the Section class
Cat,a=1,b=2,c=3,d=4,e=5,f=6
Cat2,a=11,b=21,c=31,d=41,e=51,f=61
Related
Introduction
In order to extract some data from a database, I am trying to setup a basic hibernate and spring batch project. The goal is to provide one query (HQL) and based on this query the spring batch application extracts all the data to a flat file.
One of the requirements of the application is that the user should not have to configure the mappings of the columns. As such I am trying to create a DynamicRecordProcessor that evaluates the input and passes the input (a table for example Address) to the writer in such a way that the flat file item writer can use a PassThroughFieldExtractor.
Below the reader-processor-writer xml configuration:
<!-- Standard Spring Hibernate Reader -->
<bean id="hibernateItemReader" class="org.springframework.batch.item.database.HibernateCursorItemReader">
<property name="sessionFactory" ref="sessionFactory" />
<property name="queryString" value="from Address" />
</bean>
<!-- Custom Processor -->
<bean id="dynamicRecordProcessor" class="nl.sander.mieras.processor.DynamicRecordProcessor"/>
<!-- Standard Spring Writer -->
<bean id="itemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:target/extract/output.txt" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="|"/>
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.PassThroughFieldExtractor"/>
</property>
</bean>
</property>
</bean>
EDIT:
And the job configuration:
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="cacheableMappingLocations" value="classpath*:META-INF/mappings/*.hbm.xml"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager" lazy-init="true">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Problem
My processor looks as following:
public class DynamicRecordProcessor<Input,Output> implements ItemProcessor<Input,Output> {
private static final String DELIMITER = "|";
private boolean areNamesSetup = false;
private List<String> names = new ArrayList<String>();
private Input item;
#SuppressWarnings("unchecked")
#Override
public Output process(Input item) throws Exception {
this.item = item;
initMapping();
return (Output) extract();
}
private void initMapping() {
if (!areNamesSetup) {
mapColumns();
}
areNamesSetup = true;
}
private void mapColumns() {
Field[] allFields = item.getClass().getDeclaredFields();
for (Field field : allFields) {
if (!field.getType().equals(Set.class) && Modifier.isPrivate(field.getModifiers())) {
names.add(field.getName());
}
}
}
private Object extract() {
List<Object> values = new ArrayList<Object>();
BeanWrapper bw = new BeanWrapperImpl(item);
for (String propertyName : this.names) {
values.add(bw.getPropertyValue(propertyName));
}
return StringUtils.collectionToDelimitedString(values, DELIMITER);
}
}
The table Address has the following field:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="city_id", nullable=false)
public City getCity() {
return this.city;
}
And the corresponding column in city:
#Column(name="city_id", unique=true, nullable=false)
public Short getCityId() {
return this.cityId;
}
When using values.add(bw.getPropertyValue(propertyName)); with propertyName being "city" the following exception occurs:
org.hibernate.SessionException: proxies cannot be fetched by a stateless session
at org.hibernate.internal.StatelessSessionImpl.immediateLoad(StatelessSessionImpl.java:292)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:156)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:260)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:68)
at nl.sander.mieras.localhost.sakila.City_$$_jvstc2c_d.toString(City_$$_jvstc2c_d.java)
at java.lang.String.valueOf(String.java:2982)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at org.springframework.util.StringUtils.collectionToDelimitedString(StringUtils.java:1132)
at org.springframework.util.StringUtils.collectionToDelimitedString(StringUtils.java:1148)
at nl.sander.mieras.processor.DynamicRecordProcessor.extract(DynamicRecordProcessor.java:52)
at nl.sander.mieras.processor.DynamicRecordProcessor.process(DynamicRecordProcessor.java:27)
The value, however, is availabe as shown in the screenshot below.
Concrete question: How can I get the value 300?
I have tried getting the value using reflection API, but I couldn't reach the actual value I want to get...
Reproduce
I have setup a public repo. However, you still need a local database to be able to reproduce exactly the issue. https://github.com/Weirdfishees/hibernate-batch-example. Any suggestions how isolate this issue further are more then welcome.
Just flip your reader to be state-full instead of stateless with the useStatelessSession property:
<!-- Standard Spring Hibernate Reader -->
<bean id="hibernateItemReader" class="org.springframework.batch.item.database.HibernateCursorItemReader">
<property name="sessionFactory" ref="sessionFactory" />
<property name="queryString" value="from Address" />
<property name="useStatelessSession" value="false" />
</bean>
In my app I've been passing my SessionFatory in my method parameters when I need to access my database within those methods. It's instantiated in my Controller using:
#Autowired
private SessionFactory sessionFactory;
And when I need to access my database I use lines like this:
sessionFactory.getCurrentSession().save()
// or
List products = sessionFactory.getCurrentSession().createSQLQuery("SELECT * FROM PRODUCTS").list();
Should I be creating new sessionFactories to get my current session or should is it okay to pass it as a parameter?
edit [added for clarity]:
From HomeController.java:
#Controller
public class HomeController {
#Autowired
private SessionFactory sessionFactory;
//Correlations Insert Handler
//DOCUMENT CREATE
#RequestMapping(value="/documents", method = RequestMethod.PUT)
public #ResponseBody String insertDocument(HttpServletRequest request, HttpServletResponse response){
DocumentControl documentControl = new DocumentControl();
documentControl.insertDocument(request, response, sessionFactory);
return "went through just find";
}
//DOCUMENT READ
#RequestMapping(value="/documents", method = RequestMethod.GET)
public #ResponseBody List<Documents> getAllDocuments(){
//List documents = sessionFactory.getCurrentSession().createQuery("from Documents").list();
DocumentControl documentControl = new DocumentControl();
List documents = documentControl.getAllDocuments(sessionFactory);
return documents;
}
From DocumentControl.java:
public class DocumentControl {
private static Logger logger = Logger.getLogger(DocumentControl.class.getName());
public DocumentControl(){};
//CREATE
public void insertDocument(HttpServletRequest request, HttpServletResponse response, SessionFactory sessionFactory){
Documents document = new Documents(request)
sessionFactory.getCurrentSession().save(document);
}
//READ
public List<DocumentReturn> getAllDocuments(SessionFactory sessionFactory){
List documents = sessionFactory.getCurrentSession().createQuery("from Documents").list();
return documents;
}
private boolean ifProductExists (String productCode, SessionFactory sessionFactory) {
boolean exists = false;
List<Products> productList = sessionFactory.getCurrentSession().createCriteria(Products.class).add( Restrictions.eq("productCode", productCode)).list();
if ( !productList.isEmpty() && productList.size() > 0 ) {
exists = true;
}
return exists;
}
private boolean ifStandardExists (String standardCode, SessionFactory sessionFactory) {
boolean exists = false;
List<Standards> standardList = sessionFactory.getCurrentSession().createCriteria(Standards.class).add( Restrictions.eq("standardCode", standardCode)).list();
if ( !standardList.isEmpty() && standardList.size() > 0 ) {
exists = true;
}
return exists;
}
}
hibernate.cfg.xml:
hibernate-configuration>
<session-factory>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">100</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.max_statements">10</property>
<property name="hibernate.c3p0.min_size">10</property>
<property name="hibernate.c3p0.timeout">100</property>
<mapping class="***.*****.********.model.Documents"/>
</session-factory>
</hibernate-configuration>
persistence-context.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key=" hibernate.use_sql_comments">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<
property name="sessionFactory" ref="sessionFactory" />
</bean>
SessionFactory is a Singleton. Therefore, you should get it from the ApplicationContext (i.e. using the #Autowired annotation). Passing it as a parameter will only complicate your API.
I think that you should leave creation of SessionFactory to the framework you are using and inject it as a resource just as you are doing by now. Besides you are not creating it in your code at all. Good practice is to create separate session for every chunk of data manipulation, if it is not done by a framework ofc. Session should be your main unit of work.
All. Attached herewith all the files. If I give the same name for both bean property name and reference, it returns the object. But it returns null, in case both are different. Please help. Thank you so much.
The AuthenticateAction.java file has got the detailed comments inside it.....
below are my files list:
WIRAdminBaseAction.java ---> my base action
AuthenticateAction.java ---> my java file that calls the bean here
applicationContext_MNC.xml
The below way of calling works...... This is not encouraged, as we
should not use applicationContext always
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletRequest().getSession().getServletConte xt());
ICacheDelegate cacheAction = (ICacheDelegate)applicationContext.getBean("BMOCacheDelegate");
The below way of calling does NOT work .... returns null value....
Please help... I assume that, since I have extended the
WIRAdminBaseAction, i should be able to call the getCacheDelegate
directly and it should return my cacheDelegate object ... Again,
Please note.....if I change my applicationContext_MNC.xml as below,
the below way of calling works fine. But, i don't want to change my
applicationContext_MNC.xml as below, due to some necessity.
<bean id="BMOWIRAdminBaseAction" class="com.symcor.wiradmin.web.action.WIRAdminBase Action">
<property name="cacheDelegate">
<ref bean="cacheDelegate" />
</property>
</bean>
<bean id="cacheDelegate" class="com.symcor.wiradmin.delegate.CacheDelegate" >
<property name="statusDBDAO"><ref bean="BMOStatusDBDAO" /></property>
</bean>
Is it that the name and bean should have the same value.... ??? No
Need to be.....Am i right ? Please advise.
getCacheDelegate().getActorAction(1); // this way of calling doesn't work and returns null value. please help.
MY CODE Follows below.
AuthenticateAction.java
package com.symcor.wiradmin.web.action;
import java.net.UnknownHostException;
import java.sql.SQLException;
import org.bouncycastle.crypto.CryptoException;
import org.springframework.context.ApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.web.context.support.WebApplica tionContextUtils;
import com.symcor.wiradmin.delegate.ICacheDelegate;
public class AuthenticateAction extends WIRAdminBaseAction {
private static final long serialVersionUID = 1L;
public String authenticate() throws UnknownHostException, CryptoException,
DataAccessException, SQLException{
/** This way of calling works...... This is not encouraged, as we should not use applicationContext always **/
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContex t(getServletRequest().getSession().getServletConte xt());
ICacheDelegate cacheAction = (ICacheDelegate) applicationContext.getBean("BMOCacheDelegate");
/** The below way of calling does NOT work .... returns null value.... Please help...
* I assume that, since I have extended the WIRAdminBaseAction, i should be able to call the getCacheDelegate directly
* and it should return my cacheDelegate object ...
* Again, Please note.....if I change my applicationContext_MNC.xml as below, the below way of calling works fine...
* but, i don't want to change my applicationContext_MNC.xml as below, due to some necessity.
<bean id="BMOWIRAdminBaseAction" class="com.symcor.wiradmin.web.action.WIRAdminBase Action">
<property name="cacheDelegate">
<ref bean="cacheDelegate" />
</property>
</bean>
<bean id="cacheDelegate" class="com.symcor.wiradmin.delegate.CacheDelegate" >
<property name="statusDBDAO"><ref bean="BMOStatusDBDAO" /></property>
</bean>
... is it that the name and bean should have the same value.... ??? No Need to be.....Am i right ? Please advise.
*
* **/
getCacheDelegate().getActorAction(1); // this way of calling doesn't work and returns null value. please help.
return "success";
}
}
WIRAdminBaseAction.java
package com.symcor.wiradmin.web.action;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.interceptor.ParameterAware;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Preparable;
import com.opensymphony.xwork2.config.entities.Parameteri zable;
import com.symcor.wiradmin.delegate.ICacheDelegate;
public class WIRAdminBaseAction extends ActionSupport implements Preparable, ParameterAware, Parameterizable, SessionAware,RequestAware {
private HttpServletRequest request;
private static final long serialVersionUID = 1L;
private HttpServletResponse response;
private ICacheDelegate cacheDelegate;
private Map session;
private Map<String, String> params;
private Map parameters;
public void prepare() throws Exception {
}
public String execute() throws Exception {
return SUCCESS;
}
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public HttpServletRequest getServletRequest() {
return this.request;
}
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
public HttpServletResponse getServletResponse() {
return this.response;
}
public ICacheDelegate getCacheDelegate() {
return cacheDelegate;
}
public void setCacheDelegate(ICacheDelegate cacheDelegate) {
this.cacheDelegate = cacheDelegate;
}
public void addParam(final String key, final String value) {
this.params.put(key, value);
}
public Map getParams() {
return params;
}
public void setParams(final Map<String, String> params) {
this.params = params;
}
public Map getSession() {
return this.session;
}
public void setSession(final Map session) {
this.session = session;
}
public void setParameters(final Map param) {
this.parameters = param;
}
}
applicationContext_MNC.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- database configuration from property file -->
<bean id="BMOAdminDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" lazy-init="default" autowire="default" dependency-check="default">
<property name="driverClass" value="${jdbc.driver}" ></property>
<property name="jdbcUrl" value="${admin.jdbc.url}" ></property>
<property name="user" value="${admin.jdbc.user}" ></property>
<property name="password" value="${admin.jdbc.password}" ></property>
<property name="initialPoolSize" value="3" ></property>
<property name="minPoolSize" value="3" ></property>
<property name="maxPoolSize" value="25" ></property>
<property name="acquireIncrement" value="1" ></property>
<property name="acquireRetryDelay" value="1000" ></property>
<property name="debugUnreturnedConnectionStackTraces" value="true" ></property>
<property name="maxIdleTime" value="300" ></property>
<property name="unreturnedConnectionTimeout" value="300000" ></property>
<property name="preferredTestQuery" value="SELECT COUNT(*) FROM LOCALE_CODE" ></property>
<property name="checkoutTimeout" value="300000" ></property>
<property name="idleConnectionTestPeriod" value="600000" ></property>
</bean>
<bean id="BMOWIRAdminBaseAction" class="com.symcor.wiradmin.web.action.WIRAdminBase Action">
<property name="cacheDelegate">
<ref bean="BMOCacheDelegate" />
</property>
</bean>
<bean id="BMOCacheDelegate" class="com.symcor.wiradmin.delegate.CacheDelegate" >
<property name="statusDBDAO"><ref bean="BMOStatusDBDAO" /></property>
</bean>
<bean id="BMOStatusDBDAO" class="com.symcor.wiradmin.dao.StatusDBDAO">
<property name="dataSource">
<ref bean="BMOAdminDataSource" />
</property>
</bean>
<!-- this bean is set to map the constants which needs to be configured as per
the environment to the java constants file -->
<bean id="envConstantsConfigbean" class="com.symcor.wiradmin.util.constants.Environm entConstantsSetter">
<property name="loginUrl" value="${login.url}"/>
<property name="logoutIR" value="${logout.from.image.retrieval}"/>
<property name="adminModuleUrl" value="${admin.url}"/>
<property name="adminUrlSym" value="${admin.url.sym}"/>
<property name="envProperty" value="${env.property}"/>
</bean>
</beans>
I've encorporated quartz scheduler in diffrent java file in my JSP Struts/Hibernate application. My execute method looks like below:
public void execute(JobExecutionContext jExeCtx) throws JobExecutionException {
try {
userDetailManager = new UserDetailManagerImpl();
userDetailManager.sendMailTxnDetailsEveryNight();
} catch (ApplicationException ex) {
java.util.logging.Logger.getLogger(JobClass.class.getName()).log(Level.SEVERE, null, ex);
}
}
Then im calling sendMailTxnDetailsEveryNight method and looks like:
public void sendMailTxnDetailsEveryNight() throws ApplicationException{
List<RemittanceTransactionBean> rBean = remittanceTransactionDao.getTodayTxnSummary();
return rBean;
}
Im getting nullpointerException at given point :
java.lang.NullPointerException
at com.mtmrs.business.user.impl.UserDetailManagerImpl.sendMailTxnDetailsEveryNight(UserDetailManagerImpl.java:754)
at com.mtmrs.util.common.JobClass.execute(JobClass.java:44)
How do i need to call the getTodayTxnSummary(); so that i dont get any error. I defined the remittanceTransactionDao in applicationContext as well:
<bean id="userDetailManager" parent="abstractTxDefinition">
<property name="target">
<bean class="com.mtmrs.business.user.impl.UserDetailManagerImpl">
<property name="remittanceTransactionDao" ref="remittanceTransactionDao"></property>
</bean>
</property>
</bean>
I tried debugging and see, the remittanceTransactionDao is null. I also tried setting below before calling method remittanceTransactionDao. This way the session variable im using in sendMailTxnDetailsEveryNight is null. I m stuck here.
remittanceTransactionDao = new RemittanceTransactionDaoHibernate(RemittanceTransaction.class);
I removed the classes and configured the follwing in my application-Context Dao.xml.
<bean name="runMeJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.mtmrs.util.common.RunMeJob" />
<property name="jobDataAsMap">
<map>
<entry key="userDetailManager" value-ref="userDetailManager" />
</map>
</property>
</bean>
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="runMeJob" />
<property name="cronExpression" value="0 0 10,12,14,16,18,20,22,23 * * ?" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="runMeJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
Then i removed everything about the quartz implementaion code whatever i was trying to make, and added this class:
package com.mtmrs.util.common;
import com.mtmrs.business.user.UserDetailManager;
import com.mtmrs.util.logger.MTMRSLogger;
import com.mtmrs.util.logger.ApplicationException;
import java.util.logging.Level;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class RunMeJob extends QuartzJobBean {
UserDetailManager userDetailManager;
public UserDetailManager getUserDetailManager() {
return userDetailManager;
}
public void setUserDetailManager(UserDetailManager userDetailManager) {
this.userDetailManager = userDetailManager;
}
#Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
System.out.print("Entering Class: Job Class , method: execute");
try {
userDetailManager.sendMailTxnDetailsEveryNight();
} catch (ApplicationException ex) {
java.util.logging.Logger.getLogger(RunMeJob.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.print("Exiting Class: Job Class , method: execute");
}
}
Also i downgraded the version of quartz jar to 1.x.
I needed to use a method in userDetailManager (ie sendMailTxnDetailsEveryNight) object as the job that run every 2 hours as specified in the trigger.
Basically i followed this link: Mykong
Hope this can be of help to someone.
The resolvers customizeFault method isnt being hit when I set the mapped endpoints below. If I remove it, it gets hit. Any help? The exception is thrown in my resolver class.
MembershipManagementEndpoint.java
package com.openclass.adapter.ws;
#Endpoint
public class MembershipManagementEndpoint {
Spring Config
<bean id="membershipExceptionResolver" class="com.openclass.adapter.ws.resolvers.LisMembershipResolverInterceptor">
<property name="order" value="1"></property>
<property name="mappedEndpoints">
<set>
<value>com.openclass.adapter.ws.MembershipManagementEndpoint</value>
</set>
</property>
</bean>
LisSoapFaultTranslatorExceptionResolver.java
package com.openclass.adapter.ws.resolvers;
public class LisMembershipResolverInterceptor extends LisSoapFaultTranslatorExceptionResolver {
#Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws Exception {
throw new LisMembershipException();
return true;
}
LisSoapFaultTranslatorExceptionResolver.java
public abstract class LisSoapFaultTranslatorExceptionResolver extends
SimpleSoapExceptionResolver implements SoapEndpointInterceptor {
I add to build separate interceptor/resolver for each endpoint and then test in the interceptor/resolver and test for the correct endpoint.
SoapFaultExceptionResolver
#Override
protected final boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception ex) {
MethodEndpoint methodEndpoint = (MethodEndpoint)endpoint;
if(methodEndpoint != null && methodEndpoint.getBean().getClass().getSimpleName().equalsIgnoreCase(getEndpointClass().getSimpleName()))
{
Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse(),
"SimpleSoapExceptionResolver requires a SoapMessage");
SoapMessage response = (SoapMessage) messageContext.getResponse();
String faultString = StringUtils.hasLength(ex.getMessage()) ? ex.getMessage() : ex.toString();
SoapBody body = response.getSoapBody();
SoapFault fault = body.addServerOrReceiverFault(faultString, getLocale());
customizeFault(messageContext, endpoint, ex, fault);
return true;
}
return false;
}
Config
<bean id="courseSectionExceptionResolver" class="com.test.adapter.ws.resolvers.LisCourseSectionResolverInterceptor">
<property name="order" value="1"></property>
</bean>
<bean id="personExceptionResolver" class="com.test.adapter.ws.resolvers.LisPersonResolverInterceptor">
<property name="order" value="2"></property>
</bean>
<bean id="membershipExceptionResolver" class="com.test.adapter.ws.resolvers.LisMembershipResolverInterceptor">
<property name="order" value="3"></property>
</bean>
<bean id="bdemsExceptionResolver" class="com.test.adapter.ws.resolvers.LisBdemsResolverInterceptor">
<property name="order" value="4"></property>
</bean>
<bean id="outcomeExceptionResolver" class="com.test.adapter.ws.resolvers.LisOutcomeResolverInterceptor">
<property name="order" value="5"></property>
</bean>
<sws:interceptors>
<sws:payloadRoot namespaceUri="http://www.imsglobal.org/services/lis/cmsv1p0/wsdl11/sync/imscms_v1p0">
<bean class="com.test.adapter.ws.resolvers.LisCourseSectionResolverInterceptor"/>
</sws:payloadRoot>
<sws:payloadRoot namespaceUri="http://www.imsglobal.org/services/lis/pms2p0/wsdl11/sync/imspms_v2p0">
<bean class="com.test.adapter.ws.resolvers.LisPersonResolverInterceptor"/>
</sws:payloadRoot>
<sws:payloadRoot namespaceUri="http://www.imsglobal.org/services/lis/mms2p0/wsdl11/sync/imsmms_v2p0">
<bean class="com.test.adapter.ws.resolvers.LisMembershipResolverInterceptor"/>
</sws:payloadRoot>
<sws:payloadRoot namespaceUri="http://www.imsglobal.org/services/lis/bdems1p0/wsdl11/sync/imsbdems_v1p0">
<bean class="com.test.adapter.ws.resolvers.LisBdemsResolverInterceptor"/>
</sws:payloadRoot>
<sws:payloadRoot namespaceUri="http://www.imsglobal.org/services/lis/oms1p0/wsdl11/sync/imsoms_v1p0">
<bean class="com.test.adapter.ws.resolvers.LisOutcomeResolverInterceptor"/>
</sws:payloadRoot>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor">
<property name="logRequest" value="true"/>
<property name="logResponse" value="true"/>
</bean>
</sws:interceptors>