I am new to Spring Integration.
I've configured a Spring file inbound-channel-adapter, e.g.
<file:inbound-channel-adapter channel="channel1" directory="${location}" prevent-duplicates="true" filename-pattern="*.csv">
<si:poller>
<si:interval-trigger interval="1000"/>
</si:poller>
</file:inbound-channel-adapter>
<si:service-activator input-channel="channel1" output-channel="channel2" ref="filenameGenerator" method="generate"/>
Now this is working fine.
But this needs to be deployed in a clustered environment. I want to make sure that multiple instances in the cluster do not attempt to read the same file. So will this work in such environment?
If no, can I use Quartz scheduler like this:
<file:inbound-channel-adapter channel="channel1" directory="${location}" prevent-duplicates="true" filename-pattern="*.csv">
<si:poller task-executor="taskExecutor" fixed-rate="1000"/>
</file:inbound-channel-adapter>
<si:service-activator input-channel="channel1" output-channel="channel2" ref="filenameGenerator" method="generate"/>
<bean id="taskExecutor" class="org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor">
<property name="threadCount" value="20"/>
<property name="threadNamePrefix" value="consumer"/>
</bean>
Will this work and solve my problem??
Or do I have to use Transaction?
I hope the question is clear.
Thanks,
Adi
When multiple processes are reading from the same directory it can be
desirable to lock files to prevent them from being picked up
concurrently. To do this you can use a FileLocker
Check out the documentation around file lockers here. It seems that you can do soemthing like this:
<file:inbound-channel-adapter ... >
<file:nio-locker/>
</file:inbound-channel-adapter>
When multiple processes are reading from the same directory it can be
desirable to lock files to prevent them from being picked up
concurrently. To do this you can use a FileLocker
To ensure that a quartz-scheduled job executes once and only once within a cluster, configure a persistent, clustered quartz job schedule. Here's a sample config, for Quartz 1.6.6:
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<!-- Set whether any jobs defined on this SchedulerFactoryBean should
overwrite existing job definitions.
-->
<property name="overwriteExistingJobs" value="true" />
<property name="dataSource" ref="myTransactionalDataSource" />
<!-- nonTransactionalDataSource is only necessary with clustered Quartz with an XA DataSource.
-->
<property name="nonTransactionalDataSource" ref="myNonTransactionalDataSource" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.jobStore.selectWithLockSQL">SELECT * FROM {0}LOCKS WITH(UPDLOCK,HOLDLOCK) WHERE LOCK_NAME = ?</prop>
<!--
Run in cluster. Quartz ensures persisted jobs are executed once within the
cluster
-->
<prop key="org.quartz.jobStore.isClustered">true</prop>
<!-- Each node in the cluster must have a unique instance id.
-->
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<!-- Default clusterCheckinInterval is 15000
-->
<!-- <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
-->
</props>
</property>
<property name="transactionManager" ref="transactionManager" />
- <!--
In Quartz 1.6.6, Quartz's ThreadPool interface is used when firing job triggers,
in org.quartz.core.QuartzSchedulerThread.
Quartz 1.x still starts some unmanaged threads, notably org.quartz.impl.jdbcjobstore.JobStoreSupport's
ClusterManager which is used when clustered=true. Quartz 2.0 should correct this problem.
-->
<property name="taskExecutor" ref="myTaskExecutor" />
</bean>
Related
I'm working on an existing Spring application that uses JDBC (DAO's extend NamedParameterJdbcDaoSupport). There were four datasources configured, each with it's own DataSourceTransactionManager. (though only one was registered with tx:annotation-driven for some reason)
I've recently added JPA (Spring-data-JPA) into the application and configured two entityManagerFactories (for now I don't need the other two datasources). I also configured two JpaTransactionManagers and removed the corresponding DataSourceTransactionManagers for these dataSources, since the JpaTransactionManagers can also be used for JDBC transactions. (correct me if I'm wrong)
It appears I need to be able to have distributed transactions, since the two datasources (to two different databases) need to be accessed (through JPA) in one service method. Since I did not have all I need to set up JTA (missing XA-driver for one of the databases) I've decided to give the Spring ChainedTransactionManager a try. Sadly this didn't work out as expected. All works fine if I just call a service method that only uses JPA.
Though when I call an existing service method that uses a JDBC find that has a class level #transactional annotation with it's propagation set to NOT_SUPPORTED and call another service method after that with a JPA call and a #transactional, I get an exception:
Caused by: java.lang.IllegalStateException: Already value [org.springframework.jdbc.datasource.ConnectionHolder#462cf9d9] for key [org.jboss.jca.adapters.jdbc.WrapperDataSource#3fbb4c32] bound to thread [http-/127.0.0.1:8080-5]
at org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(TransactionSynchronizationManager.java:189) [spring-tx-3.2.5.RELEASE.jar:3.2.5.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:403) [spring-orm-3.2.5.RELEASE.jar:3.2.5.RELEASE]
After some debugging, I found out that the transactions in Spring get added to a map on a ThreadLocal in the "TransactionSynchronizationManager.bindResource" method. The problem is that when using a JDBC call with #transactional and propogation NOT_SUPPORTED, a transaction is made anyway and registered through that method. When the JpaTransactionManager tries to bind it's resource, it is already on the map (and not marked as void) which causes the error to occur.
Changing the propagation to the default "REQUIRED" for the service call that encapsulates the JDBC call fixes the problem.
I have no idea why Spring is still creating that transaction when the transactional annotation is NOT_SUPPORTED. And if it creates that transaction, it should not bypass the JpaTransactionManager.
So what I'd like to know is if there is some way to tell Spring to use the JpaTransactionManager also when it creates a transaction itself inside the NamedParameterJdbcDaoSupport. (Well actually the JdbcDaoSupport... Well actually the DataSourceUtils)
We're using Spring 3.2.5, spring-data-jpa 1.6.0 and I've used Hibernate 4.2.0 as JpaVendor.
This problem doesn't occur without the ChainedTransactionManager.
Datasources:
<bean id="dataSourceCompta" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/comptaDS"/>
</bean>
<bean id="dataSourceUnisys" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/insoverDS"/>
</bean>
<bean id="dataSourceInsoverwebMysql" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/insoverWebDS"/>
</bean>
<bean id="dataSourceBatch" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/batchDS"/>
</bean>
Single remaining JDBC transaction manager (no JPA counterpart):
<bean id="transactionManagerBatch" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceBatch"/>
</bean>
JPA Transaction Managers:
<bean id="jpaUnisysTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryUnisys"/>
<qualifier value="unisys" />
</bean>
<bean id="jpaMysqlTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryMysql"/>
<qualifier value="mysql" />
</bean>
My ChainedTransactionManager:
<bean id="chainedTransactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
<constructor-arg>
<list>
<ref bean="jpaUnisysTransactionManager" />
<ref bean="jpaMysqlTransactionManager" />
</list>
</constructor-arg>
</bean>
JPA Entity manager factories:
<bean name="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<bean id="entityManagerFactoryUnisys" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/some-persistence.xml"/>
<property name="dataSource" ref="dataSourceUnisys"/>
<property name="persistenceUnitName" value="unisysPU"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaProperties">
<!-- properties -->
</property>
</bean>
<bean id="entityManagerFactoryMysql" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath:META-INF/some-persistence.xml"/>
<property name="dataSource" ref="dataSourceCompta"/>
<property name="persistenceUnitName" value="mysqlPU"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<property name="jpaProperties">
<!-- properties -->
</property>
</bean>
For now I've "fixed" this, by changing all the class-level transactional annotations to have propagation.REQUIRED (default) instead of NOT_SUPPORTED. Though I do not really like this solutions, since it might be somebody set those propagations to NOT_SUPPORTED with a good reason. I've also tried SUPPORTED, but using that had the same issue as NOT_SUPPORTED: a transaction was being made anyway by the Spring DataSourceUtils when the query was being executed by the NamedParameterJdbcDaoSupport DAO.
When no transactional annotation is set on the service, all works well too.
I am upgrading Spring from Spring 2.x to Spring 4.2.4 and Quartz from 1.8 to 2.3
Following bean throws "Invalid property 'durability' of bean class [org.springframework.scheduling.quartz.SchedulerFactoryBean]"
If I remove the durability property, I get "org.quartz.SchedulerException: Jobs added with no trigger must be durable"
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="durability" value="true"/>
<property name="recover" value="false"/>
<property name="jobDetails">
<list>
<ref bean="deleteTempFileJobScheduler" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="deleteTempFileJobSchedulerTrigger" />
</list>
</property>
</bean>
Any suggestion?
Thanks in advance.
A Quartz-Job has a attribute called durability.
This property determines whether a Job without triggers should automatically deleted.
I.e. if you set
<property name="durability" value="true"/>
than Jobs remain in the JobStore even if no triggers point to it anymore.
But if you set
<property name="durability" value="false"/>
than jobs should be removed from the JobStore if no triggers point to it.
In that case Quartz gives you the mentioned Exception if you try to add a Job without Triggers to the JobStore (since you add a Job which will be immediately removed).
To prevent such an Exception you could add a Job together with an Trigger to the JobStore.
You need set durability property to JobDetailFactoryBean
(in your case, this is deleteTempFileJobScheduler) and not to SchedulerFactoryBeam
When we are creating job itself , you can add storeDurably(true)
. It is work for me .
JobDetail job = newJob(JobScheduler.class)
.withIdentity( sc.getId()+ "_Job")
.usingJobData(getJobDataMap(sc))
.storeDurably(true)
.build();
How can i set the permission of a custom worflow?
I deployed a BPMN 2.0 workflow, by placing the bpmn20.xml in /alfresco/extension and also using the following workflow-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="acme.workflowBootstrap" parent="workflowDeployer">
<property name="workflowDefinitions">
<list>
<props>
<prop key="engineId">activiti</prop>
<prop key="location">alfresco/extension/workflows/activiti/superflow.bpmn20.xml</prop>
<prop key="mimetype">text/xml</prop>
<prop key="redeploy">false</prop>
</props>
</list>
</property>
</bean>
</beans>
The workflow can by found in the Worfkflow console and can be run.
The problem remaing is, that I can only see this worfklow as admin in alfresco share. Not as admin#acme.
How can I set permission to use this workflow as different user than admin?
There is an alfresco extension project available to achieve workflow permissions https://addons.alfresco.com/addons/workflow-permissions
Configuration is done in your share custom config, using the elelement "permission-workflows" like this :
<config evaluator="string-compare" condition="Workflow">
<!-- A list of workflow definitions that are NOT displayed
<hidden-workflows>
<!-- Hide all WCM related workflows -->
<workflow name="jbpm$wcmwf:*" />
<workflow name="jbpm$wf:*" />
<workflow name="activiti$activiti*" />
<workflow name="activiti$quickStart*" />
</hidden-workflows>
<!-- A list of workflow tasks that are NOT displayed inShare -->
<hidden-tasks>
<!-- Hide all WCM related tasks -->
<task type="wcmwf:*" />
</hidden-tasks>
<permission-workflows default="allow">
<permission-workflow name="activiti$epubcreate">
<authorities>
<authority type="group">GROUP_ALFRESCO_ADMINISTRATORS</authority>
</authorities>
</permission-workflow>
</permission-workflows>
</config>
I cannot really speak to how much of the Activiti engine that is bundled with Alfresco is actually exposed. But in the stand alone Activiti product you can assign "candidate starters" to any process.
http://www.activiti.org/userguide/#_add_a_candidate_starter_to_a_process_definition
This uses the identityLinks mechanism that is also available for tasks, instances and executions.
This involves using a JSR job, so I do not have access to use the tasklet definition in the job specification. The direction I've been given is to support only JSR 352 features as far as job specification goes.
Here is an example of a job:
<job id="xmlWriter" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="firstStep">
<chunk item-count="2">
<reader ref="DelimitedFlatFileReader">
<properties>
<!-- Reads in from file Test.csv -->
<property name="fileNameAndPath" value="#{jobParameters['readerFileNameAndPath']}" />
<property name="fieldNames" value="firstName, lastName, city" />
<property name="fullyQualifiedTargetClass" value="com.test.test.DatabaseMember" />
</properties>
</reader>
<processor ref="com.test.test.ItemProcessor" />
<writer ref="FlatFileWriter" >
<properties>
<property name="appendOn" value="true" />
<!-- Read to file demoXMLReaderFlatFileWriterOutput.txt -->
<property name="fileNameAndPath" value="#{jobParameters['writerFileNameAndPath']}" />
<property name="fullyQualifiedTargetClass" value="com.test.test.DatabaseMember" />
</properties>
</writer>
</chunk>
</step>
In the ItemProcessor I am interested in having some type of callback/listener that could trigger roll back for transactional items that occur inside it should the chunck for example fail during the write step.
So a good example would be I have some sort of db or jms operation that occurs in the ItemProcessor. If during the writer portion that chunck fails I would want to roll back all of those operations that occurred during the item processor that were part of this iteration (or several with item count > 1).
I have looked into the various listeners available based on the jsr spec and nothing seems to allow for this type of implementation. Also spring does not seem to wrap the processor in a transaction like it does when it is a true spring batch. Testing I did in creating db connections and testing them to see if they were transactional aware always returned false.
Is what I'm looking for even possible outside a entirely custom implementation and if so any guidance in how to accomplish it.
We have jobs that might process up to 20,000 files. We are using a MultiResourcePartitioner to set things up. The job does run, but we have noticed a bottleneck.
SpringBatch is creating entries in the BATCH_STEP_EXECUTION table for each file found, and will not process any files until it has created a table entry for every file. The loading of this table seems to take a very long time.
In local testing, trying to process just 1,000 files, it is taking 38-40 minutes to add the rows to 'BATCH_STEP_EXECUTION'. Once the table is loaded, the files are processed quite rapidly (usually under 1 minute).
I would hope that this is not typical behavior and that I am just missing something.
Here is how the database is set up (we really subclass the 'OracleDataSource' (we are using 'ojdbc6.jar' file to get to the class) and the db_file is a properties file to get to the url, password, etc.):
<bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
<constructor-arg value="db_file" />
<property name="connectionCachingEnabled" value="true" />
<property name="connectionCacheProperties">
<props merge="default">
<prop key="InitialLimit">10</prop>
<prop key="MinLimit">25</prop>
<prop key="MaxLimit">50</prop>
<prop key="InactivityTimeout">1800</prop>
<prop key="AbandonedConnectionTimeout">900</prop>
<prop key="MaxStatementsLimit">20</prop>
<prop key="PropertyCheckInterval">20</prop>
</props>
</property>
</bean>
Here is the rest of the JobRepository definition:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean" >
<property name="databaseType" value="oracle" />
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="isolationLevelForCreate" value="ISOLATION_DEFAULT"/>
</bean>
<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobParametersIncrementer" class="org.springframework.batch.core.launch.support.RunIdIncrementer" />
Anyone have any ideas?
As an FYI, SpringSource has identified this as a bug: Batch-1908.
As a workaround, we are simply lowering the number of files to process with a given run, and then increasing the number of times that the job runs in a given day.
We are using 2,000 as our file limit as it provides acceptable performance.
Take this as alternate approach.
For loading the table from files better to use LOADDATA .
http://infolab.stanford.edu/~ullman/fcdb/oracle/or-load.html
This will improve the performance in a better way. For me its take only 30 seconds to process a file with 1 million records