/* * eGovFrame EasyBatch * Copyright The eGovFrame Open Community (http://open.egovframe.go.kr)). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @author 서경석(슈퍼개발자K3) */ package egovframework.rte.bat.item; import egovframework.rte.bat.core.item.file.mapping.EgovDefaultLineMapper; import egovframework.rte.bat.core.item.file.mapping.EgovObjectMapper; import egovframework.rte.bat.core.item.file.transform.EgovDelimitedLineTokenizer; import egovframework.rte.bat.core.item.file.transform.EgovFixedLengthTokenizer; import egovframework.rte.bat.core.item.file.transform.EgovLineTokenizer; import javax.sql.DataSource; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.annotation.BeforeStep; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemStream; 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; import org.springframework.batch.item.database.JdbcCursorItemReader; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.transform.Range; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.jdbc.core.BeanPropertyRowMapper; /** * @author 서경석 * @since 2014.11.05 * @version 1.0 * @see * * <pre> * << 개정이력(Modification Information) >> * * 수정일 수정자 수정내용 * ------- -------- --------------------------- * 2014.11.05 서경석 최초 생성 * 2014.11.28 표준프레임워크 공통컴포넌트 추가 적용 (패키지 변경) * * </pre> */ public class DefaultItemReader<T> implements ItemStreamReader<T>{ // Input Resource Type - key private static final String XML_CONF_FLAG_KEY = ".reader.xml.conf.flag"; private static final String READER_RESOURCE_TYPE_KEY = ".reader.resource.type"; private static final String READER_RESOURCE_NAME_KEY = ".reader.resource.name"; private static final String READER_FIELD_NAMES_KEY = ".reader.field.names"; private static final String READER_VO_TYPE_KEY = ".reader.vo.type"; private static final String READER_DELIMITER_KEY = ".reader.delimiter"; private static final String READER_COLUMNS_KEY = ".reader.columns"; private static final String READER_SQL_KEY = ".reader.sql"; private static final String READER_PARAMS_KEY = ".reader.params"; // Input Resource Type - Value private static final String DELIMITED_FILE_TYPE = "delimitedFile"; private static final String FIXED_LENGTH_FILE_TYPE = "fixedLengthFile"; private static final String JDBC_DB_TYPE = "jdbcDb"; // XML 설정 내용을 출력하기 위한 설정 boolean printXmlConf = false; // 실제 동작하는 Reader ItemReader<T> reader; // 공통 설정 String stepName; JobParameters jobParameters; String readerResourceType; // File 입력인 경우 사용되는 설정 Resource resource; // 공통 String resourceName; String[] fieldNames; // 공통 String names; @SuppressWarnings("rawtypes") Class voType; // 공통 String delimiter; // delimited 방식인 경우 Range[] ranges; // fixedLength 방식인 경우 String columns; // (fixedLength 방식인 경우) // DB 입력인 경우 사용되는 설정 // DB 입력인 경우 사용되는 설정 private DataSource dataSource; private String sql; @SuppressWarnings("unused") private String[] params; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } @BeforeStep public void beforeStep(StepExecution stepExecution) { this.stepName = stepExecution.getStepName(); this.jobParameters = stepExecution.getJobParameters(); String flag = jobParameters.getString(stepName + XML_CONF_FLAG_KEY); if((flag != null) && "true".equalsIgnoreCase(flag)) { printXmlConf = true; } // Input Resource Type에 따라 필요한 설정 값 세팅 makeReaderConfigValue(); } @Override public void close() throws ItemStreamException { if(this.reader instanceof ItemStream) { ((ItemStream) this.reader).close(); } } @Override public void open(ExecutionContext executionContext) throws ItemStreamException { // ItemReader 생성 makeItemReader(); if(this.reader instanceof ItemStream) { ((ItemStream) this.reader).open(executionContext); } } @Override public void update(ExecutionContext executionContext) throws ItemStreamException { if(this.reader instanceof ItemStream) { ((ItemStream) this.reader).update(executionContext); } } @Override public T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException { return reader.read(); } // Input Resource Type 별로 설정 값 세팅 private void makeReaderConfigValue() { if(jobParameters.getString(stepName + READER_RESOURCE_TYPE_KEY) != null) { this.readerResourceType = jobParameters.getString(stepName + READER_RESOURCE_TYPE_KEY); if(DELIMITED_FILE_TYPE.equalsIgnoreCase(this.readerResourceType) || FIXED_LENGTH_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)) { // 입력 리소스가 File인 경우 공통 처리 부분 this.resourceName = jobParameters.getString(stepName + READER_RESOURCE_NAME_KEY); this.names = jobParameters.getString(stepName + READER_FIELD_NAMES_KEY); String type = jobParameters.getString(stepName + READER_VO_TYPE_KEY); if(DELIMITED_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)){ this.delimiter = jobParameters.getString(stepName + READER_DELIMITER_KEY); if(resourceName == null || this.delimiter == null || names == null || type == null) { throw new RuntimeException(stepName + "스텝의 Reader 설정에서 resourceName, delimiter, names, type 은 필수입니다. 다음 처럼 설정하세요.\n" + stepName + READER_RESOURCE_NAME_KEY + "=./inputs/csvData.csv " + stepName + READER_DELIMITER_KEY + "=, " + stepName + READER_FIELD_NAMES_KEY + "=name,age " + stepName + READER_VO_TYPE_KEY + "=aa.bb.TestVo" ); } } else if(FIXED_LENGTH_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)) { this.columns = jobParameters.getString(stepName + READER_COLUMNS_KEY); if(resourceName == null || columns == null || names == null || type == null) { throw new RuntimeException(stepName + "스텝의 Reader 설정에서 resourceName, columns, names, type 은 필수입니다. 다음 처럼 설정하세요.\n" + stepName + READER_RESOURCE_NAME_KEY + "=./inputs/csvData.csv " + stepName + READER_COLUMNS_KEY + "=1-9,10-11 " + stepName + READER_FIELD_NAMES_KEY + "=name,age " + stepName + READER_VO_TYPE_KEY + "=aa.bb.TestVo" ); } String[] columnArray = columns.split(","); ranges = new Range[columnArray.length]; for(int idx=0; idx<columnArray.length; idx++) { ranges[idx] = new Range(Integer.parseInt(columnArray[idx].split("-")[0]), Integer.parseInt(columnArray[idx].split("-")[1])); } } this.resource = new FileSystemResource(resourceName); this.fieldNames = names.split(","); try { this.voType = Class.forName(type); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } else if(JDBC_DB_TYPE.equalsIgnoreCase(this.readerResourceType)){ this.sql = jobParameters.getString(stepName + READER_SQL_KEY); String tempParams = jobParameters.getString(stepName + READER_PARAMS_KEY); String type = jobParameters.getString(stepName + READER_VO_TYPE_KEY); if(this.sql == null || type == null) { throw new RuntimeException(stepName + "스텝의 Writer 설정에서 sql, type 는 필수입니다. 다음 처럼 설정하세요.\n" + stepName + ".writer.sql=select ID, NAME, CREDIT from CUSTOMER " + stepName + ".writer.params=credit,name " + stepName + READER_VO_TYPE_KEY + "=aa.bb.TestVo"); } if(tempParams != null) { this.params = tempParams.split(","); } try { this.voType = Class.forName(type); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } } else { throw new RuntimeException(stepName + READER_RESOURCE_TYPE_KEY + "=delimitedFile'처럼, 입력 리소스 타입을 Job 파라미터로 입력하세요.\n" + "리소스 타입 종류) delimitedFile, fixedLengthFile, jdbcDb"); } } private EgovDelimitedLineTokenizer makeEgovDelimitedLineTokenizer() { EgovDelimitedLineTokenizer tokenizer = new EgovDelimitedLineTokenizer(); tokenizer.setDelimiter(this.delimiter); return tokenizer; } private EgovFixedLengthTokenizer makeEgovFixedLengthTokenizer() { EgovFixedLengthTokenizer tokenizer = new EgovFixedLengthTokenizer(); tokenizer.setColumns(this.ranges); return tokenizer; } @SuppressWarnings("unchecked") private EgovObjectMapper<T> makeEgovObjectMapper() { EgovObjectMapper<T> objectMapper = new EgovObjectMapper<T>(); objectMapper.setNames(fieldNames); objectMapper.setType(voType); try { objectMapper.afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException(e); } return objectMapper; } private EgovDefaultLineMapper<T> makeEgovDefaultLineMapper(EgovLineTokenizer<T> tokenizer, EgovObjectMapper<T> objectMapper) { EgovDefaultLineMapper<T> lineMapper = new EgovDefaultLineMapper<T>(); lineMapper.setLineTokenizer(tokenizer); lineMapper.setObjectMapper(objectMapper); lineMapper.afterPropertiesSet(); return lineMapper; } @SuppressWarnings({ "rawtypes", "unchecked" }) private void makeItemReader() { if(DELIMITED_FILE_TYPE.equalsIgnoreCase(this.readerResourceType) || FIXED_LENGTH_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)) { EgovLineTokenizer tokenizer; if(DELIMITED_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)) { tokenizer = makeEgovDelimitedLineTokenizer(); } else { tokenizer = makeEgovFixedLengthTokenizer(); } EgovObjectMapper<T> objectMapper = makeEgovObjectMapper(); EgovDefaultLineMapper<T> lineMapper = makeEgovDefaultLineMapper(tokenizer, objectMapper); this.reader = new FlatFileItemReader<T>(); ((FlatFileItemReader<T>)this.reader).setLineMapper(lineMapper); ((FlatFileItemReader<T>)this.reader).setResource(resource); try { ((FlatFileItemReader<T>)this.reader).afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException(this.readerResourceType + " 타입의 File을 read 하기 위한 FlatFileItemReader 생성에 실패 하였습니다."); } } else if(JDBC_DB_TYPE.equalsIgnoreCase(this.readerResourceType)) { BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper(); rowMapper.setMappedClass(this.voType); this.reader = new JdbcCursorItemReader(); try { ((JdbcCursorItemReader)this.reader).setDataSource(this.dataSource); ((JdbcCursorItemReader)this.reader).setRowMapper(rowMapper); ((JdbcCursorItemReader)this.reader).setSql(this.sql); ((JdbcCursorItemReader)this.reader).afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException(this.readerResourceType + " 타입의 DB을 read 하기 위한 JdbcCursorItemReader 생성에 실패 하였습니다."); } } printXmlConfig(); } private void printXmlConfig() { if(printXmlConf) { if(DELIMITED_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)) { System.out.println("======= " + stepName + " READER 설정(XML 버전) =========\n" + "<bean id=\"" + stepName + ".reader\" class=\"org.springframework.batch.item.file.FlatFileItemReader\" scope=\"step\">\n" + " <property name=\"resource\" value=\"" + this.resourceName + "\" />\n" + " <property name=\"lineMapper\">\n" + " <bean class=\"egovframework.rte.bat.core.item.file.mapping.EgovDefaultLineMapper\">\n" + " <property name=\"lineTokenizer\">\n" + " <bean class=\"egovframework.rte.bat.core.item.file.transform.EgovDelimitedLineTokenizer\">\n" + " <property name=\"delimiter\" value=\"" + this.delimiter + "\" />\n" + " </bean>\n" + " </property>\n" + " <property name=\"objectMapper\">\n" + " <bean class=\"egovframework.rte.bat.core.item.file.mapping.EgovObjectMapper\">\n" + " <property name=\"type\" value=\"" + voType.getName() + "\" />\n" + " <property name=\"names\" value=\"" + this.names + "\" />\n" + " </bean>\n" + " </property>\n" + " </bean>\n" + " </property>\n" + "</bean>\n" + "================================================"); } else if(FIXED_LENGTH_FILE_TYPE.equalsIgnoreCase(this.readerResourceType)) { System.out.println("======= " + stepName + " READER 설정(XML 버전) =========\n" + "<bean id=\"" + stepName + ".reader\" class=\"org.springframework.batch.item.file.FlatFileItemReader\" scope=\"step\">\n" + " <property name=\"resource\" value=\"" + this.resourceName + "\" />\n" + " <property name=\"lineMapper\">\n" + " <bean class=\"egovframework.rte.bat.core.item.file.mapping.EgovDefaultLineMapper\">\n" + " <property name=\"lineTokenizer\">\n" + " <bean class=\"egovframework.rte.bat.core.item.file.transform.EgovFixedLengthTokenizer\">\n" + " <property name=\"columns\" value=\"" + this.columns + "\" />\n" + " </bean>\n" + " </property>\n" + " <property name=\"objectMapper\">\n" + " <bean class=\"egovframework.rte.bat.core.item.file.mapping.EgovObjectMapper\">\n" + " <property name=\"type\" value=\"" + voType.getName() + "\" />\n" + " <property name=\"names\" value=\"" + this.names + "\" />\n" + " </bean>\n" + " </property>\n" + " </bean>\n" + " </property>\n" + "</bean>\n" + "================================================"); } else if(JDBC_DB_TYPE.equalsIgnoreCase(this.readerResourceType)){ System.out.println("======= " + stepName + " READER 설정(XML 버전) =========\n" + "<bean id=\"" + stepName + ".reader\" class=\"org.springframework.batch.item.database.JdbcCursorItemReader\" scope=\"step\">\n" + " <property name=\"dataSource\" ref=\"dataSource\" />\n" + " <property name=\"sql\" value=\"" + this.sql + "\" />\n" + " <property name=\"rowMapper\">\n" + " <bean class=\"org.springframework.jdbc.core.BeanPropertyRowMapper\" />\n" + " <property name=\"mappedClass\" value=\"" + voType.getName() + "\">\n" + " </bean>\n" + "</property>\n" + "</bean>\n" + "================================================"); } } } }