OpenJpa batch insert - spring

I've read already this and that but it does not fit me.
I have openjpa realization and I need to make batch insert.
I have some entity and with Spring I inject my transaction-manager and EntityManager from EntityManagerFactory like
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="JpaPersistenceUnit"/>
<property name="jpaPropertyMap">
<map>
<entry key="openjpa.jdbc.DBDictionary" value="org.apache.openjpa.jdbc.sql.OracleDictionary" />
<entry key="openjpa.ConnectionFactoryProperties" value="PrintParameters=true" />
</map>
</property>
</bean>
In class I have
#PersistenceContext
protected EntityManager em;
AtomicInteger size = new AtomicInteger();
#TransactionalRollback
public void saveLogs(Logs log) {
int i = size.incrementAndGet();
int batchSize=100;
em.persist(log);
if(i%batchSize==0){
em.flush();
em.clear();
}
}
And here, I don't know, what should I do. Because, if I use flush() I got error, that I can't do anything with Shared EntityManager. When I do only persist, it's Ok, but every data commit immediately.
Maybe is it possible to make batch insert with SpringBuilder and some of nativeQuery?
Like
String insert = "INSERT into SIEBEL_METHOD_LOGS (?1,?2,?3,?4) VALUES ";
StringBuilder builder = new StringBuilder(insert);
builder.append(
"(" + log.getMethod() + "), "
+ "(" + log.getClient() + "), "
+ "(" + log.getStartDate() + "), "
+ "(" + log.getResponseTime() + "), "
+ "(" + log.getIsError() + ")");
Query query = em.createNativeQuery(builder.toString());
query.executeUpdate();
But, I'm not sure about that.
P.S. Also, I don't understand meaning of that
org.apache.openjpa.jdbc.sql.OracleDictionary has field
// batch limit
private int defaultBatchLimit = 100;
When does it work? Because whenever I persist and end my method, I get flush into DB and it doesn't depend on 100 or 5 elements I've persisted into DB. They would be there.

I used that solution, but maybe is it possible more elegance
public class LoggerClass {
private static final int batchSize = 100;
private List<MethodsLog> logsList;
private AtomicLong sizeList;
private void initList() {
this.logsList = new ArrayList<>(batchSize);
this.sizeList = new AtomicLong();
}
public void saveLogs(MethodsLog log) {
long i = sizeList.incrementAndGet();
logsList.add(log.toJpa());
if (i % batchSize == 0) {
saveToDBAndClear(logsList);
initList();
}
}
private synchronized void saveToDBAndClear(List<MethodsLog> logs) {
MethodsService.saveLogs(logs);
}
#TransactionalRollback
public void saveLogs(List<MethodLogs> logs) {
for (MethodLogs log : logs) {
em.persist(log);
}
}
}

Related

DefaultMessageListenerContainer not receiving all messages from ActiveMQ queue

ActiveMQ/Spring experts,
I am running into a very strange problem with ActiveMQ and DefaultMessageListenerContainer/SimpleMessageListenerContainer combination. We have a web application built using Spring (we are at 4.x). One of the transactions is related to processing a bulk upload of files and each file has a number of lines. Each line will become a message for processing.
A publisher publishes each line as a message to a persistent Queue. When examined through the ActiveMQ console we can see the message in the queue. To the same queue, we have a group of listeners configured using DefaultMessageListenerContainer(DMLC)/SimpleMessageListenerContainer(SMLC) (tried both) for consuming messages.
When the publisher publishes 100 messages sometimes only 99 or 98 messages are delivered but the rest are stuck in queue. The configuration is as follows:
ActiveMQ broker is running in standalone mode not networked or embedded in WildFly.
In the Spring application, we tried both DMLC and SMLC but both ran into this issue. Tried simpleMQConnectionFactory as well as PooledConnectionFactory and both times ran into same problem.
Tried setting the prefetch limit to "1" on the PooledConnectionFactory and ran into the same problem. Spring SMLC is throwing an exception when set to "0".
Max concurrent consumers is set to 50
When messages are stuck, if we restart WildFly the remaining messages in the queue are delivered to the consumers.
We are not using transacted sessions rather set the acknowledgModeName = "CLIENT_ACKNOWLEDGE"
We initialize the queue using a spring bean and use that for initializing the SMLC or DMLC
I am running out of options to try at this. If you share your experiences in this regard it is highly appreciated. This application is in production and the problem happens almost every other day sometimes multiple times in a day.
private void publishDMRMessage(DmrDTO p_dmrDTO, long jobID, int numDMRs) {
//Create a DMR message for each of the unique keys and publish it to
try {
DMRImportMessage message = new DMRImportMessage();
message.setDmrDTO(p_dmrDTO);
message.setDmrKey(p_dmrDTO.toString());
message.setDmrImportJobID(new Long(jobID));
message.setTask(Command.INITIALIZE_DMR_FORM);
message.setNumDMRForms(new Long(numDMRs));
sender.sendMessage(message);
} catch (Exception e) {
System.out.println(" JMS Exception = " + e.getMessage());
e.printStackTrace();
}
}
public class DMRMessageListener implements MessageListener {
private DMRImportManager manager;
private JMSMessageSender sender;
private DmrFormInitService formService;
private ProcessDMRValidationMessage validateService;
private ImportDmrService dmrService;
private static final Logger log = Logger.getLogger(DMRMessageListener.class);
public ImportDmrService getDmrService() {
return dmrService;
}
public void setDmrService(ImportDmrService dmrService) {
this.dmrService = dmrService;
}
public ProcessDMRValidationMessage getValidateService() {
return validateService;
}
public void setValidateService(ProcessDMRValidationMessage validateService) {
this.validateService = validateService;
}
public DmrFormInitService getFormService() {
return formService;
}
public void setFormService(DmrFormInitService formService) {
this.formService = formService;
}
public JMSMessageSender getSender() {
return sender;
}
public void setSender(JMSMessageSender sender) {
this.sender = sender;
}
public DMRImportManager getManager() {
return manager;
}
public void setManager(DMRImportManager manager) {
this.manager = manager;
}
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
try {
ObjectMessage objectMessage = (ObjectMessage) message;
DMRImportMessage dmrMessage = (DMRImportMessage)objectMessage.getObject();
log.info("============= Message Received =========================");
log.info("Message Type = " + dmrMessage.getTask() + " for JOB ID = " + dmrMessage.getDmrImportJobID());
log.info("Message Received === DMR ID = " + dmrMessage.getDmrID());
log.info("Message Received === DMR key = " + dmrMessage.getDmrKey());
log.info("============= Message Received =========================");
//Process the message
processDMRMessage(dmrMessage);
DMRProcessingStatus status = manager.updateStatus(dmrMessage);
if (status.isStatus()) {
log.info(" One stage is complete, the next stage should start for JOB ID = " + dmrMessage.getDmrImportJobID());
publishMessageForNextStepOfProcessing(dmrMessage, status);
}
}
catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
else {
log.error(" ***** Received an invalid message -- NOT AN Object message so cannot be processed and will result in stuck jobs **** ");
throw new IllegalArgumentException("Message must be of type ObjectMessage");
}
//Send the next message in the chain
}
/**
* It will examine the message content and based on the message type it will invoke the appropriate
* service.
*
* #param dmrMessage DMRImportMessage
*/
private void processDMRMessage(DMRImportMessage dmrMessage) {
if (dmrMessage.getTask() == Command.INITIALIZE_DMR_FORM) {
Map<String, String> dmrInitResults = formService.initDmrForm(dmrMessage.getDmrDTO());
//Indicate in message that this DMR Key is not in ICIS
if (dmrInitResults != null) {
if (StringUtils.equalsIgnoreCase(dmrInitResults.get("wsUnscheduleDmrError"), "truee")) {
log.info("DMR Key is not in ICIS: " + dmrMessage.getDmrDTO().toString());
dmrMessage.setDmrKeyInICIS(false);
} else if (StringUtils.equalsIgnoreCase(dmrInitResults.get("wsDBDown"), "truee")) {
log.error("Web Service call failed for DMR Key: " + dmrMessage.getDmrDTO().toString());
}
}
}
try {
if (dmrMessage.getTask() == Command.IMPORT_DMR_PARAMETER) {
//Process the Parameter line
ParameterProcessingStatus status = dmrService.processLine(dmrMessage.getDmrImportJobID(), dmrMessage.getDmrParameterSubmission(), new Integer(dmrMessage.getLineNumber()), dmrMessage.getDmrKeysNotInICIS());
System.out.println("LINE = " + dmrMessage.getLineNumber() + " Status = " + status.isStatus());
dmrMessage.setProcessingStatus(status.isStatus());
dmrMessage.setDmrID(status.getDmrID());
dmrMessage.setDmrComment(status.getDmrComment());
return;
}
} catch(Exception e) {
log.error("An exception occurred during processing of line " + dmrMessage.getLineNumber() + " in job " + dmrMessage.getDmrImportJobID());
e.printStackTrace();
dmrMessage.setProcessingStatus(false);
dmrMessage.setDmrID(0L);
}
try {
if (dmrMessage.getTask() == Command.END_DMR_PARAMETER_IMPORT) {
//Process the Parameter line
//ParameterProcessingStatus status = dmrService.processLine(dmrMessage.getDmrImportJobID(), dmrMessage.getDmrParameterSubmission(), 100);
dmrMessage.setProcessingStatus(true);
dmrMessage.setDmrID(0L);
return;
}
} catch(Exception e) {
e.printStackTrace();
dmrMessage.setProcessingStatus(false);
dmrMessage.setDmrID(0L);
}
try {
if (dmrMessage.getTask() == Command.POST_PROCESS_DMR) {
//Validate DMRs
validateService.validateDMR(dmrMessage);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void publishMessageForNextStepOfProcessing(DMRImportMessage dmrMessage, DMRProcessingStatus status) throws JMSException {
log.info(" =========== Publish a message for next step of processing for JOB ID = " + dmrMessage.getDmrImportJobID());
if (dmrMessage.getTask() == Command.INITIALIZE_DMR_FORM) {
//Start the DMR Parameter Processing
sender.sendDMRControlMessage(this.createControlMessage(ProcessPhase.START_PARAMETER_PROCESSING, dmrMessage.getDmrImportJobID(), status.getDmrKeysNotInICIS()));
return;
}
if ((dmrMessage.getTask() == Command.IMPORT_DMR_PARAMETER)
|| (dmrMessage.getTask() == Command.END_DMR_PARAMETER_IMPORT)) {
//Start the DMR Validation Process
dmrService.postProcessParameters(dmrMessage.getDmrImportJobID(), status.getSuccessfulLines(), status.getErroredLines());
DMRImportControlMessage message = this.createControlMessage(ProcessPhase.START_DMR_VALIDATION, dmrMessage.getDmrImportJobID());
message.setDmrIDsWithComments(status.getDmrIDsWithComments());
sender.sendDMRControlMessage(message);
return;
}
if (dmrMessage.getTask() == Command.POST_PROCESS_DMR) {
//Start the next DMR import process
sender.sendDMRControlMessage(this.createControlMessage(ProcessPhase.START_DMR_FORM_INIT, dmrMessage.getDmrImportJobID()));
return;
}
log.info(" =========== End Publish a message for next step of processing for JOB ID = " + dmrMessage.getDmrImportJobID());
}
private DMRImportControlMessage createControlMessage(DMRImportControlMessage.ProcessPhase phase, Long jobID) {
return createControlMessage(phase, jobID, null);
}
private DMRImportControlMessage createControlMessage(DMRImportControlMessage.ProcessPhase p_phase, Long p_jobID, Set<DmrDTO> p_dmrDTOs) {
DMRImportControlMessage message = new DMRImportControlMessage();
message.setDmrImportJobID(p_jobID);
message.setPhase(p_phase);
if (p_dmrDTOs != null) {
message.setDmrKeysNotInICIS(p_dmrDTOs);
}
return message;
}
//Bean Configs.
<bean id="prefetchPolicy" class="org.apache.activemq.ActiveMQPrefetchPolicy">
<property name="queuePrefetch" value="0"/>
</bean>
<bean id="jmsFactoryPub" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
</bean>
<bean id="jmsFactoryReceive" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
<property name="prefetchPolicy" ref="prefetchPolicy" />
</bean>
<bean id="jmsFactoryControlMsg" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>tcp://localhost:61616</value>
</property>
</bean>
<bean id="dmrQueue"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="DMRQueue" />
</bean>
<bean id="dmrControlQueue"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="DMRControlQueue" />
</bean>
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactoryPub" />
</bean>
<bean id="jmsQueueTemplateControlMsg" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsFactoryControlMsg" />
</bean>
<bean id="messageCreator" class="net.exchangenetwork.netdmr.service.DMRMessageCreator">
</bean>
<bean id="dmrMessageListener" class="net.exchangenetwork.netdmr.service.DMRMessageListener">
<property name="manager" ref="dmrImportManager"/>
<property name="sender" ref="messagePublisher"/>
<property name="formService" ref="dmrFormInit"/>
<property name="validateService" ref="dmrValidator"/>
<property name="dmrService" ref="importDmrService"/>
</bean>
<bean id="messageSender" class="net.exchangenetwork.netdmr.service.JMSMessageSender">
<property name="jmsTemplate" ref="jmsQueueTemplate" />
<property name="sendQueue" ref="dmrQueue" />
<property name="creator" ref="messageCreator" />
</bean>
<bean id="messagePublisher" class="net.exchangenetwork.netdmr.service.JMSMessageSender">
<property name="jmsTemplate" ref="jmsQueueTemplateControlMsg" />
<property name="sendQueue" ref="dmrControlQueue" />
<property name="creator" ref="messageCreator" />
</bean>
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactoryReceive"/>
<!-- this is the queue we will listen on -->
<property name="destination" ref="dmrQueue" />
<property name="messageListener" ref="dmrMessageListener"/>
<property name="concurrentConsumers" value="60"/>
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE"/>
<property name="errorHandler" ref="jmsErrorHandler"/>
<property name="exceptionListener" ref="jmsExceptionHandler"/>
<property name="receiveTimeout" value="0"/>
</bean>

Spring Batch read step running in loop

I came across a piece of code that reads some data as the following:
public class StudioReader implements ItemReader<List<Studio>> {
#Setter private AreaDao areaDao;
#Getter #Setter private BatchContext context;
private HopsService hopsService = new HopsService();
#Override
public List<Studio> read() throws Exception {
List<Studio> list = hopsService.getStudioHops();
if (!isEmpty(list)) {
for (Studio studio : list) {
log.info("Studio being read: {}", studio.getCode());
List areaList = areaDao.getArea(studio
.getCode());
if (areaList.size() > 0) {
studio.setArea((String) areaList.get(0));
log.info("Area {1} is fetched for studio {2}", areaList.get(0), studio.getCode());
}
this.getContext().setReadCount(1);
}
}
return list;
}
However when I run the job this read is running in a loop. I found from another stackoverflow answer that it is the expected behavior. My question then is what is the best solution given this particular example? Extend StudioReader from JdbcCursorItemReader ? I found one example that defines everything in the xml which I don't want. And here is the context.xml part for the reader:
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.syc.studio.reader.StudioReader" scope="step">
<property name="context" ref="BatchContext" />
<property name="areaDao" ref="AreaDao" />
</bean>
And here is the job definition in xml:
<bean id="StudioJob" class="org.springframework.batch.core.job.SimpleJob">
<property name="steps">
<list>
<bean id="StudioStep" parent="SimpleStep" >
<property name="itemReader" ref="ItemReader"/>
<property name="itemWriter" ref="ItemWriter"/>
<property name="retryableExceptionClasses">
<map>
<entry key="com.syc.studio.exception.CustomException" value="true"/>
</map>
</property>
<property name="retryLimit" value="2" />
</bean>
</list>
</property>
<property name="jobRepository" ref="jobRepository" />
</bean>
Writer:
public void write(List<? extends Object> obj) throws Exception {
List<Studio> list = (List<Studio>) obj.get(0);
for (int i = 0; i <= list.size(); i++) {
Studio studio = list.get(i);
if (apiClient == null) {
apiClient = new APIClient("v2");
}
this.uploadXML(studio);
}
The read method after suggestion from #holi-java:
public List<Studio> read() throws Exception {
if (this.listIterator == null) {
this.listIterator = initializing();
}
return this.listIterator.hasNext() ? this.listIterator.next() : null;
}
private Iterator<List<Studio>> initializing() {
List<Studio> listOfStudiosFromApi = hopsService.getStudioLocations();
for (Studio studio : listOfStudiosFromApi) {
log.info("Studio being read: {}", studio.getCode());
List areaList = areaDao.getArea(studio.getCode());
if (areaList.size() > 0) {
studio.setArea((String) areaList.get(0));
log.info("Area {1} is fetched for studio {2}", areaList.get(0), studio.getCode());
}
this.getContext().setReadCount(1);
}
return Collections.singletonList(listOfStudiosFromApi).iterator();
}
spring-batch documentation for ItemReader.read assert:
Implementations must return null at the end of the input data set.
But your read method is always return a List and should be like this:
public Studio read() throws Exception {
if (this.results == null) {
List<Studio> list = hopsService.getStudioHops();
...
this.results=list.iterator();
}
return this.results.hasNext() ? this.results.next() : null;
}
if you want your read method return a List then you must paging the results like this:
public List<Studio> read() throws Exception {
List<Studio> results=hopsService.getStudioHops(this.page++);
...
return results.isEmpty()?null:results;
}
if you can't paging the results from Service you can solved like this:
public List<Studio> read() throws Exception {
if(this.results==null){
this.results = Collections.singletonList(hopsService.getStudioHops()).iterator();
}
return this.results.hasNext()?this.results.next():null;
}
it's better not read a list of items List<Studio>, read an item at a time Studio instead. when you read a list of item you possibly duplicated iterate logic between writers and processors as you have shown the demo in comments. if you have a huge of data list to processing you can combine pagination in your reader, for example:
public Studio read() throws Exception {
if (this.results == null || !this.results.hasNext()) {
List<Studio> list = hopsService.getStudioHops(this.page++);
...
this.results=list.iterator();
}
return this.results.hasNext() ? this.results.next() : null;
}
Maybe you need to see step processing mechanism.
ItemReader - read an item at a time.
ItemProcessor - processing an item at a time.
ItemWriter - write entire chunk of items out.

Reading multiple files resides in a file system which matches the job parameters using MultiResourceItemReader

Use Case :
I would like to launch a job which takes employee id as job parameters, which will be multiple employee ids.
In a file system, files will be residing which contains employee ids as part of the file name (It is a remote file system, not local)
i need to process those files where file name contains the employee-id and passing it to the reader.
I am thinking of using MultiResourceItemReader but i am confused how to match the file name with Employee Id (Job Parameter) which is there in a file system.
Please suggest.
The class MultiResourceItemReader has a method setResources(Resources[] resources) which lets you specify resources to read either with an explicit list or with a wildcard expression (or both).
Example (explicit list) :
<bean class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources">
<list>
<value>file:C:/myFiles/employee-1.csv</value>
<value>file:C:/myFiles/employee-2.csv</value>
</list>
</property>
</bean>
Example (wildcard) :
<bean class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="file:C:/myFiles/employee-*.csv" />
</bean>
As you may know, you can use job parameters in configuration by using #{jobParameters['key']} :
<bean class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="file:C:/myFiles/employee-#{jobParameters['id']}.csv" />
</bean>
Unfortunately, wildcard expressions can't manage an OR expression over a list of value with a separator (id1, id2, id3...). And I'm guessing you don't know how many distinct values you'll have to declare an explicit list with a predefined number of variables.
However a working solution would be to use the Loop mechanism of Spring Batch with a classic FlatFileItemReader. The principle is basically to set the next="" on the last step to the first step until you have exhausted every item to read. I will provide code samples if needed.
EDIT
Let's say you have a single chunk to read one file at a time. First of all, you'd need to put the current id from the job parameter in the context to pass it to the reader.
public class ParametersManagerTasklet implements Tasklet, StepExecutionListener {
private Integer count = 0;
private Boolean repeat = true;
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
// Get job parameter and split
String[] ids = chunkContext.getStepContext().getJobParameters().getString(PARAMETER_KEY).split(DELIMITER);
// Check for end of list
if (count >= ids.length) {
// Stop loop
repeat = false;
} else {
// Save current id and increment counter
chunkContext.getStepContext().getJobExecutionContext().put(CURRENT_ID_KEY, ids[count++];
}
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
if (!repeat) {
return new ExitStatus("FINISHED");
} else {
return new ExitStatus("CONTINUE");
}
}
}
Now you declare this step in your XML and create a loop :
<batch:step id="ParametersStep">
<batch:tasklet>
<bean class="xx.xx.xx.ParametersManagerTasklet" />
</batch:tasklet>
<batch:next on="CONTINUE" to="ReadStep" />
<batch:end on="FINISHED" />
</batch:step>
<batch:step id="ReadStep">
<batch:tasklet>
<batch:chunk commit-interval="10">
<batch:reader>
<bean class="org.springframework.batch.item.file.MultiResourceItemReader">
<property name="resources" value="file:C:/myFiles/employee-#{jobExecutionContext[CURRENT_ID_KEY]}.csv" />
</bean>
</batch:reader>
<batch:writer>
</batch:writer>
</batch:chunk>
</batch:tasklet>
<batch:next on="*" to="ParametersStep" />
</batch:step>
You can write your own FactoryBean to perform a custom resources search.
public class ResourcesFactoryBean extends AbstractFactoryBean<Resource[]> {
String[] ids;
String path;
public void setIds(String[] ids) {
this.ids = ids;
}
public void setPath(String path) {
this.path = path;
}
#Override
protected Resource[] createInstance() throws Exception {
final List<Resource> l = new ArrayList<Resource>();
final PathMatchingResourcePatternResolver x = new PathMatchingResourcePatternResolver();
for(final String id : ids)
{
final String p = String.format(path, id);
l.addAll(Arrays.asList(x.getResources(p)));
}
return l.toArray(new Resource[l.size()]);
}
#Override
public Class<?> getObjectType() {
return Resource[].class;
}
}
---
<bean id="reader" class="org.springframework.batch.item.file.MultiResourceItemReader" scope="step">
<property name="delegate" ref="itemReader" />
<property name="resources">
<bean class="ResourcesFactoryBean">
<property name="path"><value>file:C:/myFiles/employee-%s.cvs</value> </property>
<property name="ids">
<value>#{jobParameters['id']}</value>
</property>
</bean>
</property>
</bean>
jobParameter 'id' is a comma separated list of your ID.

method in spring to read txt file

I am having a requirement wherein I need to read the contents of a text file through spring framework. For this purpose I made a method in my service implementation class as below-
public String readFile(File file)
This method will take the file name as input and read the file.
I was writing the code in XML for spring as below-
<bean id="fstream" class="java.io.FileInputStream">
<constructor-arg value="C:/text.txt" />
</bean>
<bean id="in" class="java.io.DataInputStream">
<constructor-arg ref="fstream"/>
</bean>
<bean id="isr" class="java.io.InputStreamReader">
<constructor-arg ref="in"/>
</bean>
<bean id="br" class="java.io.BufferedReader">
<constructor-arg ref="isr"/>
</bean>
Following code goes in my method-
public String readFile(File file)
{
String line = null;
String content = "";
try
{
ApplicationContext context = new ClassPathXmlApplicationContext("FileDBJob.xml");
BufferedReader br = (BufferedReader) context.getBean("br");
while((line = br.readLine())!=null)
content = content.concat(line);
}
catch (Exception e)
{
e.printStackTrace();
}
return content;
}
But here the issue is that i need to hardcode the file name in XML, so there is no use of file parameter.
Kindly help in finding the solution. As I am new to spring and trying my hands with it so it may be possible that I am missing something. Any help would be of great help.
Don't inject the streams and readers, that's not really how Spring is intended to be used. I'd inject the file itself:
public class MyFileReader {
private File file;
public String readFile() {
StringBuilder builder = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(getFile()));
String line = null;
while ((line = reader.readLine()) != null)
builder.append(line);
} catch (IOException e) {
e.printStackTrace();
} finally {
closeQuietly(reader);
}
return builder.toString();
}
private void closeQuietly(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException ignored) {}
}
}
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
}
Then your bean def looks like this:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:app.properties"/>
</bean>
<bean class="com.myapp.MyFileReader">
<property name="file" value="${filePath}" />
</bean>
All that is left is to create your app.properties file with the correct info. You can also set the value by invoking the app with a -DfilePath=/foo/bar/whatever.txt
I have tested this code its working.....
Try to implement....you have to copy paste schedular.xml file in ur proj configuration folder(where applicationContext.xml file in ur application and it has to be
contextConfigLocation
WEB-INF/config/*.xml
in ur web.xml file).
Then configure SvhedularTask bean in ur service classes xml file....it will trigger for every minute.
////SCHEDULARTASK.JAVA//////
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletContext;
import org.springframework.web.context.ServletContextAware;
/**
* The Class SchedulerTask.
*/
public class SchedulerTask implements ServletContextAware{
private ServletContext servletContext;
#Override
public void setServletContext(ServletContext arg0) {
// TODO Auto-generated method stub
this.servletContext = arg0;
}
public void unZipProcess() throws IOException{
System.out.println(servletContext);
File folder = new File("C:/Users/rerrabelli/Desktop/test");
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null){
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
if (listOfFiles[i].getName().endsWith(".txt")) {
File file = new File("C:/Users/rerrabelli/Desktop/test" + File.separator
+ listOfFiles[i].getName());
long millisec = file.lastModified();
Date dt = new Date(millisec);
long difference = new Date().getTime()-dt.getTime();
System.out.println((difference/1000)/60);
if(((difference/1000)/60)<1){
FileInputStream fin = new FileInputStream(
file);
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
byte b;
while ((b = (byte) fin.read()) != -1) {
tmp.write(b);
}
byte[] customerData = tmp.toByteArray();
String data = new String(customerData);
System.out.println(data);
servletContext.setAttribute(file.getName(), data);
}
}
}
}
}
System.out.println(servletContext.getAttribute("test.txt"));
}
}
//////APPLICATION CONTEXT.xml/////////
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="schedulerTask" class="com.altimetrik.simreg.service.impl.SchedulerTask">
</bean>
</beans>
======================
SCHEDULAR.XML
===========
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<import resource="applicationContext.xml"/>
<bean id="schedulerTask1"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"> <ref bean="schedulerTask" /> </property>
<property name="targetMethod"> <value>unZipProcess</value> </property>
<property name="concurrent"> <value>false</value> </property>
</bean>
<bean id="UnzipTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail"> <ref bean="schedulerTask1" /> </property>
<property name="cronExpression"> <value>0 0/1 * * * ?</value> </property>
</bean>
<bean
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<!-- Add triggers here-->
<ref bean="UnzipTrigger" />
</list>
</property>
</bean>
</beans>

Spring init-method params

I am new to spring and I wanted to ask whether or not it is possible to pass params to the init and destroy methods of a bean.
Thanks.
No, you can't. If you need parameters, you will have to inject them as fields beforehand.
Sample Bean
public class Foo{
#Autowired
private Bar bar;
public void init(){
bar.doSomething();
}
}
Sample XML:
<bean class="Foo" init-method="init" />
This method is especially useful when you cannot change the class you are trying to create like in the previous answer but you are rather working with an API and must use the provided bean as it is.
You could always create a class (MyObjectFactory) that implements FactoryBean and inside the getObject() method you should write :
#Autowired
private MyReferenceObject myRef;
public Object getObject()
{
MyObject myObj = new MyObject();
myObj.init(myRef);
return myObj;
}
And in the spring context.xml you would have a simple :
<bean id="myObject" class="MyObjectFactory"/>
protected void invokeCustomInitMethod(String beanName, Object bean, String initMethodName)
throws Throwable {
if (logger.isDebugEnabled()) {
logger.debug("Invoking custom init method '" + initMethodName +
"' on bean with beanName '" + beanName + "'");
}
try {
Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
if (initMethod == null) {
throw new NoSuchMethodException("Couldn't find an init method named '" + initMethodName +
"' on bean with name '" + beanName + "'");
}
if (!Modifier.isPublic(initMethod.getModifiers())) {
initMethod.setAccessible(true);
}
initMethod.invoke(bean, (Object[]) null);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
see spring soruce code in Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
the init method is find and param is null
You cannot pass params to init-method but you can still achieve the same effect using this way:
<bean id="beanToInitialize" class="com.xyz.Test"/>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="beanToInitialize" />
<property name="targetMethod" value="init"/> <!-- you can use any name -->
<property name="arguments" ref="parameter" /> <!-- reference to init parameter, can be value as well -->
</bean>
Note: you can also pass multiple arguments as a list using this
<property name="arguments">
<list>
<ref local="param1" />
<ref local="param2" />
</list>
</property>

Resources