001/* 002 * Copyright 2002-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 java.lang.reflect.Field; 020import java.sql.Types; 021import javax.sql.DataSource; 022 023import org.apache.commons.logging.Log; 024import org.apache.commons.logging.LogFactory; 025 026import org.springframework.batch.core.repository.ExecutionContextSerializer; 027import org.springframework.batch.core.repository.dao.AbstractJdbcBatchMetadataDao; 028import org.springframework.batch.core.repository.dao.ExecutionContextDao; 029import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer; 030import org.springframework.batch.core.repository.dao.JdbcExecutionContextDao; 031import org.springframework.batch.core.repository.dao.JdbcJobExecutionDao; 032import org.springframework.batch.core.repository.dao.JdbcJobInstanceDao; 033import org.springframework.batch.core.repository.dao.JdbcStepExecutionDao; 034import org.springframework.batch.core.repository.dao.JobExecutionDao; 035import org.springframework.batch.core.repository.dao.JobInstanceDao; 036import org.springframework.batch.core.repository.dao.StepExecutionDao; 037import org.springframework.batch.item.database.support.DataFieldMaxValueIncrementerFactory; 038import org.springframework.batch.item.database.support.DefaultDataFieldMaxValueIncrementerFactory; 039import org.springframework.batch.support.DatabaseType; 040import org.springframework.beans.factory.FactoryBean; 041import org.springframework.beans.factory.InitializingBean; 042import org.springframework.jdbc.core.JdbcOperations; 043import org.springframework.jdbc.core.JdbcTemplate; 044import org.springframework.jdbc.support.lob.DefaultLobHandler; 045import org.springframework.jdbc.support.lob.LobHandler; 046import org.springframework.util.Assert; 047import org.springframework.util.StringUtils; 048 049import static org.springframework.batch.support.DatabaseType.SYBASE; 050 051/** 052 * A {@link FactoryBean} that automates the creation of a 053 * {@link SimpleJobRepository} using JDBC DAO implementations which persist 054 * batch metadata in database. Requires the user to describe what kind of 055 * database they are using. 056 * 057 * @author Ben Hale 058 * @author Lucas Ward 059 * @author Dave Syer 060 * @author Michael Minella 061 */ 062public class JobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean implements InitializingBean { 063 064 protected static final Log logger = LogFactory.getLog(JobRepositoryFactoryBean.class); 065 066 private DataSource dataSource; 067 068 private JdbcOperations jdbcOperations; 069 070 private String databaseType; 071 072 private String tablePrefix = AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX; 073 074 private DataFieldMaxValueIncrementerFactory incrementerFactory; 075 076 private int maxVarCharLength = AbstractJdbcBatchMetadataDao.DEFAULT_EXIT_MESSAGE_LENGTH; 077 078 private LobHandler lobHandler; 079 080 private ExecutionContextSerializer serializer; 081 082 private Integer lobType; 083 084 /** 085 * @param type a value from the {@link java.sql.Types} class to indicate the type to use for a CLOB 086 */ 087 public void setClobType(int type) { 088 this.lobType = type; 089 } 090 091 /** 092 * A custom implementation of the {@link ExecutionContextSerializer}. 093 * The default, if not injected, is the {@link Jackson2ExecutionContextStringSerializer}. 094 * 095 * @param serializer used to serialize/deserialize {@link org.springframework.batch.item.ExecutionContext} 096 * @see ExecutionContextSerializer 097 */ 098 public void setSerializer(ExecutionContextSerializer serializer) { 099 this.serializer = serializer; 100 } 101 102 /** 103 * A special handler for large objects. The default is usually fine, except 104 * for some (usually older) versions of Oracle. The default is determined 105 * from the data base type. 106 * 107 * @param lobHandler the {@link LobHandler} to set 108 * 109 * @see LobHandler 110 */ 111 public void setLobHandler(LobHandler lobHandler) { 112 this.lobHandler = lobHandler; 113 } 114 115 /** 116 * Public setter for the length of long string columns in database. Do not 117 * set this if you haven't modified the schema. Note this value will be used 118 * for the exit message in both {@link JdbcJobExecutionDao} and 119 * {@link JdbcStepExecutionDao} and also the short version of the execution 120 * context in {@link JdbcExecutionContextDao} . For databases with 121 * multi-byte character sets this number can be smaller (by up to a factor 122 * of 2 for 2-byte characters) than the declaration of the column length in 123 * the DDL for the tables. 124 * 125 * @param maxVarCharLength the exitMessageLength to set 126 */ 127 public void setMaxVarCharLength(int maxVarCharLength) { 128 this.maxVarCharLength = maxVarCharLength; 129 } 130 131 /** 132 * Public setter for the {@link DataSource}. 133 * @param dataSource a {@link DataSource} 134 */ 135 public void setDataSource(DataSource dataSource) { 136 this.dataSource = dataSource; 137 } 138 139 /** 140 * Public setter for the {@link JdbcOperations}. If this property is not set explicitly, 141 * a new {@link JdbcTemplate} will be created for the configured DataSource by default. 142 * @param jdbcOperations a {@link JdbcOperations} 143 */ 144 public void setJdbcOperations(JdbcOperations jdbcOperations) { 145 this.jdbcOperations = jdbcOperations; 146 } 147 148 /** 149 * Sets the database type. 150 * @param dbType as specified by 151 * {@link DefaultDataFieldMaxValueIncrementerFactory} 152 */ 153 public void setDatabaseType(String dbType) { 154 this.databaseType = dbType; 155 } 156 157 /** 158 * Sets the table prefix for all the batch meta-data tables. 159 * @param tablePrefix prefix prepended to batch meta-data tables 160 */ 161 public void setTablePrefix(String tablePrefix) { 162 this.tablePrefix = tablePrefix; 163 } 164 165 public void setIncrementerFactory(DataFieldMaxValueIncrementerFactory incrementerFactory) { 166 this.incrementerFactory = incrementerFactory; 167 } 168 169 @Override 170 public void afterPropertiesSet() throws Exception { 171 172 Assert.notNull(dataSource, "DataSource must not be null."); 173 174 if (jdbcOperations == null) { 175 jdbcOperations = new JdbcTemplate(dataSource); 176 } 177 178 if (incrementerFactory == null) { 179 incrementerFactory = new DefaultDataFieldMaxValueIncrementerFactory(dataSource); 180 } 181 182 if (databaseType == null) { 183 databaseType = DatabaseType.fromMetaData(dataSource).name(); 184 logger.info("No database type set, using meta data indicating: " + databaseType); 185 } 186 187 if (lobHandler == null && databaseType.equalsIgnoreCase(DatabaseType.ORACLE.toString())) { 188 lobHandler = new DefaultLobHandler(); 189 } 190 191 if(serializer == null) { 192 Jackson2ExecutionContextStringSerializer defaultSerializer = new Jackson2ExecutionContextStringSerializer(); 193 194 serializer = defaultSerializer; 195 } 196 197 Assert.isTrue(incrementerFactory.isSupportedIncrementerType(databaseType), "'" + databaseType 198 + "' is an unsupported database type. The supported database types are " 199 + StringUtils.arrayToCommaDelimitedString(incrementerFactory.getSupportedIncrementerTypes())); 200 201 if(lobType != null) { 202 Assert.isTrue(isValidTypes(lobType), "lobType must be a value from the java.sql.Types class"); 203 } 204 205 super.afterPropertiesSet(); 206 } 207 208 @Override 209 protected JobInstanceDao createJobInstanceDao() throws Exception { 210 JdbcJobInstanceDao dao = new JdbcJobInstanceDao(); 211 dao.setJdbcTemplate(jdbcOperations); 212 dao.setJobIncrementer(incrementerFactory.getIncrementer(databaseType, tablePrefix + "JOB_SEQ")); 213 dao.setTablePrefix(tablePrefix); 214 dao.afterPropertiesSet(); 215 return dao; 216 } 217 218 @Override 219 protected JobExecutionDao createJobExecutionDao() throws Exception { 220 JdbcJobExecutionDao dao = new JdbcJobExecutionDao(); 221 dao.setJdbcTemplate(jdbcOperations); 222 dao.setJobExecutionIncrementer(incrementerFactory.getIncrementer(databaseType, tablePrefix 223 + "JOB_EXECUTION_SEQ")); 224 dao.setTablePrefix(tablePrefix); 225 dao.setClobTypeToUse(determineClobTypeToUse(this.databaseType)); 226 dao.setExitMessageLength(maxVarCharLength); 227 dao.afterPropertiesSet(); 228 return dao; 229 } 230 231 @Override 232 protected StepExecutionDao createStepExecutionDao() throws Exception { 233 JdbcStepExecutionDao dao = new JdbcStepExecutionDao(); 234 dao.setJdbcTemplate(jdbcOperations); 235 dao.setStepExecutionIncrementer(incrementerFactory.getIncrementer(databaseType, tablePrefix 236 + "STEP_EXECUTION_SEQ")); 237 dao.setTablePrefix(tablePrefix); 238 dao.setClobTypeToUse(determineClobTypeToUse(this.databaseType)); 239 dao.setExitMessageLength(maxVarCharLength); 240 dao.afterPropertiesSet(); 241 return dao; 242 } 243 244 @Override 245 protected ExecutionContextDao createExecutionContextDao() throws Exception { 246 JdbcExecutionContextDao dao = new JdbcExecutionContextDao(); 247 dao.setJdbcTemplate(jdbcOperations); 248 dao.setTablePrefix(tablePrefix); 249 dao.setClobTypeToUse(determineClobTypeToUse(this.databaseType)); 250 dao.setSerializer(serializer); 251 252 if (lobHandler != null) { 253 dao.setLobHandler(lobHandler); 254 } 255 256 dao.afterPropertiesSet(); 257 // Assume the same length. 258 dao.setShortContextLength(maxVarCharLength); 259 return dao; 260 } 261 262 private int determineClobTypeToUse(String databaseType) throws Exception { 263 if(lobType != null) { 264 return lobType; 265 } else { 266 if (SYBASE == DatabaseType.valueOf(databaseType.toUpperCase())) { 267 return Types.LONGVARCHAR; 268 } 269 else { 270 return Types.CLOB; 271 } 272 } 273 } 274 275 private boolean isValidTypes(int value) throws Exception { 276 boolean result = false; 277 278 for (Field field : Types.class.getFields()) { 279 int curValue = field.getInt(null); 280 if(curValue == value) { 281 result = true; 282 break; 283 } 284 } 285 286 return result; 287 } 288 289}