001/* 002 * Copyright 2006-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.core.repository.support; 018 019import org.aopalliance.intercept.MethodInterceptor; 020import org.aopalliance.intercept.MethodInvocation; 021import org.springframework.aop.framework.ProxyFactory; 022import org.springframework.aop.support.DefaultPointcutAdvisor; 023import org.springframework.aop.support.NameMatchMethodPointcut; 024import org.springframework.batch.core.repository.JobRepository; 025import org.springframework.batch.core.repository.dao.ExecutionContextDao; 026import org.springframework.batch.core.repository.dao.JobExecutionDao; 027import org.springframework.batch.core.repository.dao.JobInstanceDao; 028import org.springframework.batch.core.repository.dao.StepExecutionDao; 029import org.springframework.batch.support.PropertiesConverter; 030import org.springframework.beans.factory.FactoryBean; 031import org.springframework.beans.factory.InitializingBean; 032import org.springframework.transaction.PlatformTransactionManager; 033import org.springframework.transaction.interceptor.TransactionInterceptor; 034import org.springframework.transaction.support.TransactionSynchronizationManager; 035import org.springframework.util.Assert; 036 037/** 038 * A {@link FactoryBean} that automates the creation of a 039 * {@link SimpleJobRepository}. Declares abstract methods for providing DAO 040 * object implementations. 041 * 042 * @see JobRepositoryFactoryBean 043 * @see MapJobRepositoryFactoryBean 044 * 045 * @author Ben Hale 046 * @author Lucas Ward 047 * @author Robert Kasanicky 048 */ 049public abstract class AbstractJobRepositoryFactoryBean implements FactoryBean<JobRepository>, InitializingBean { 050 051 private PlatformTransactionManager transactionManager; 052 053 private ProxyFactory proxyFactory; 054 055 private String isolationLevelForCreate = DEFAULT_ISOLATION_LEVEL; 056 057 private boolean validateTransactionState = true; 058 059 /** 060 * Default value for isolation level in create* method. 061 */ 062 private static final String DEFAULT_ISOLATION_LEVEL = "ISOLATION_SERIALIZABLE"; 063 064 /** 065 * @return fully configured {@link JobInstanceDao} implementation. 066 * 067 * @throws Exception thrown if error occurs creating JobInstanceDao. 068 */ 069 protected abstract JobInstanceDao createJobInstanceDao() throws Exception; 070 071 /** 072 * @return fully configured {@link JobExecutionDao} implementation. 073 * 074 * @throws Exception thrown if error occurs creating JobExecutionDao. 075 */ 076 protected abstract JobExecutionDao createJobExecutionDao() throws Exception; 077 078 /** 079 * @return fully configured {@link StepExecutionDao} implementation. 080 * 081 * @throws Exception thrown if error occurs creating StepExecutionDao. 082 */ 083 protected abstract StepExecutionDao createStepExecutionDao() throws Exception; 084 085 /** 086 * @return fully configured {@link ExecutionContextDao} implementation. 087 * 088 * @throws Exception thrown if error occurs creating ExecutionContextDao. 089 */ 090 protected abstract ExecutionContextDao createExecutionContextDao() throws Exception; 091 092 /** 093 * The type of object to be returned from {@link #getObject()}. 094 * 095 * @return JobRepository.class 096 * @see org.springframework.beans.factory.FactoryBean#getObjectType() 097 */ 098 @Override 099 public Class<JobRepository> getObjectType() { 100 return JobRepository.class; 101 } 102 103 @Override 104 public boolean isSingleton() { 105 return true; 106 } 107 108 /** 109 * Flag to determine whether to check for an existing transaction when a 110 * JobExecution is created. Defaults to true because it is usually a 111 * mistake, and leads to problems with restartability and also to deadlocks 112 * in multi-threaded steps. 113 * 114 * @param validateTransactionState the flag to set 115 */ 116 public void setValidateTransactionState(boolean validateTransactionState) { 117 this.validateTransactionState = validateTransactionState; 118 } 119 120 /** 121 * public setter for the isolation level to be used for the transaction when 122 * job execution entities are initially created. The default is 123 * ISOLATION_SERIALIZABLE, which prevents accidental concurrent execution of 124 * the same job (ISOLATION_REPEATABLE_READ would work as well). 125 * 126 * @param isolationLevelForCreate the isolation level name to set 127 * 128 * @see SimpleJobRepository#createJobExecution(String, 129 * org.springframework.batch.core.JobParameters) 130 */ 131 public void setIsolationLevelForCreate(String isolationLevelForCreate) { 132 this.isolationLevelForCreate = isolationLevelForCreate; 133 } 134 135 /** 136 * Public setter for the {@link PlatformTransactionManager}. 137 * @param transactionManager the transactionManager to set 138 */ 139 public void setTransactionManager(PlatformTransactionManager transactionManager) { 140 this.transactionManager = transactionManager; 141 } 142 143 /** 144 * The transaction manager used in this factory. Useful to inject into steps 145 * and jobs, to ensure that they are using the same instance. 146 * 147 * @return the transactionManager 148 */ 149 public PlatformTransactionManager getTransactionManager() { 150 return transactionManager; 151 } 152 153 /** 154 * Convenience method for clients to grab the {@link JobRepository} without 155 * a cast. 156 * @return the {@link JobRepository} from {@link #getObject()} 157 * @throws Exception if the repository could not be created 158 * @deprecated use {@link #getObject()} instead 159 */ 160 @Deprecated 161 public JobRepository getJobRepository() throws Exception { 162 return getObject(); 163 } 164 165 private void initializeProxy() throws Exception { 166 if (proxyFactory == null) { 167 proxyFactory = new ProxyFactory(); 168 TransactionInterceptor advice = new TransactionInterceptor(transactionManager, 169 PropertiesConverter.stringToProperties("create*=PROPAGATION_REQUIRES_NEW," 170 + isolationLevelForCreate + "\ngetLastJobExecution*=PROPAGATION_REQUIRES_NEW," 171 + isolationLevelForCreate + "\n*=PROPAGATION_REQUIRED")); 172 if (validateTransactionState) { 173 DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(new MethodInterceptor() { 174 @Override 175 public Object invoke(MethodInvocation invocation) throws Throwable { 176 if (TransactionSynchronizationManager.isActualTransactionActive()) { 177 throw new IllegalStateException( 178 "Existing transaction detected in JobRepository. " 179 + "Please fix this and try again (e.g. remove @Transactional annotations from client)."); 180 } 181 return invocation.proceed(); 182 } 183 }); 184 NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); 185 pointcut.addMethodName("create*"); 186 advisor.setPointcut(pointcut); 187 proxyFactory.addAdvisor(advisor); 188 } 189 proxyFactory.addAdvice(advice); 190 proxyFactory.setProxyTargetClass(false); 191 proxyFactory.addInterface(JobRepository.class); 192 proxyFactory.setTarget(getTarget()); 193 } 194 } 195 196 @Override 197 public void afterPropertiesSet() throws Exception { 198 Assert.notNull(transactionManager, "TransactionManager must not be null."); 199 200 initializeProxy(); 201 } 202 203 private Object getTarget() throws Exception { 204 return new SimpleJobRepository(createJobInstanceDao(), createJobExecutionDao(), createStepExecutionDao(), 205 createExecutionContextDao()); 206 } 207 208 @Override 209 public JobRepository getObject() throws Exception { 210 if (proxyFactory == null) { 211 afterPropertiesSet(); 212 } 213 return (JobRepository) proxyFactory.getProxy(getClass().getClassLoader()); 214 } 215 216}