/* * Copyright 2012 C24 Technologies. * * 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. */ package biz.c24.io.spring.batch.reader.source; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.StepExecution; import org.springframework.core.io.Resource; import biz.c24.io.spring.util.C24Utils; /** * An implementation of SplittingReaderSource which extracts its data from uncompressed files. * Expects to be told the path of the file to write to by the supplied Resource or, * if not specified, a property called input.file in the job parameters * (as populated by Spring Batch's org.springframework.batch.admin.integration.FileToJobLaunchRequestAdapter) * * @author Andrew Elmore */ public class FileSource implements SplittingReaderSource { private static final Logger LOG = LoggerFactory.getLogger(FileSource.class); private SplittingReader reader = null; private String name; private Resource resource = null; private String encoding = C24Utils.DEFAULT_FILE_ENCODING; private boolean consistentLineTerminators = true; /** * How many lines at the start of the file should we skip? */ private int skipLines = 0; /* * (non-Javadoc) * @see biz.c24.io.spring.batch.reader.source.SplittingReaderSource#getName() */ public String getName() { return name; } /* (non-Javadoc) * @see biz.c24.spring.batch.BufferedReaderSource#initialise(org.springframework.batch.core.StepExecution) */ public void initialise(StepExecution stepExecution) { try { // Get an InputStream and a name for where we're reading from // Use the Resource if supplied InputStream source = null; if(resource != null) { name = resource.getFilename(); source = resource.getInputStream(); } else { // If no resource supplied, fallback to a Job parameter called input.file name = stepExecution.getJobParameters().getString("input.file"); // Remove any leading file:// if it exists if(name.startsWith("file://")) { name = name.substring("file://".length()); } source = new FileInputStream(name); } // Prime the reader LOG.debug("Opening {} with encoding {}", name, getEncoding()); reader = new SplittingReader(new InputStreamReader(source, getEncoding()), consistentLineTerminators); if(skipLines > 0) { for(int i = 0; i < skipLines && reader.ready(); i++) { // Skip the line reader.readLine(); } } } catch (IOException e) { throw new RuntimeException(e); } } /* (non-Javadoc) * @see biz.c24.spring.batch.BufferedReaderSource#close() */ public void close() { if(reader != null) { try { reader.close(); // Spring Batch lifecycle will ensure that this doesn't happen while // someone is still trying to read (ie calling getReader and risking an NPE) reader = null; } catch (IOException e) { throw new RuntimeException(e); } } } /* (non-Javadoc) * @see biz.c24.spring.batch.BufferedReaderSource#getReader() */ public SplittingReader getReader() { try { if(reader != null && reader.ready()) { return reader; } else { return null; } } catch (IOException e) { // Stream has been closed beneath our feet. Nothing to read. return null; } } @Override public SplittingReader getNextReader() { SplittingReader retVal = reader; reader = null; return retVal; } @Override public boolean useMultipleThreadsPerReader() { return true; } @Override public void discard(SplittingReader reader) throws IOException { if(this.reader == reader) { reader.close(); this.reader = null; } } /** * How many lines will be skipped at the start of the file before the Reader is handed to callers? * @return the number of lines to skip at the start of the file */ public int getSkipLines() { return skipLines; } /** * How many lines should be skipped at the start of the file before the Reader is handed to callers? * @param skipLines */ public void setSkipLines(int skipLines) { this.skipLines = skipLines; } /** * The resource we acquire InputStreams from * @return the resource that we'll read from */ public Resource getResource() { return resource; } /** * Set the resource we acquire InputStreams from */ public void setResource(Resource resource) { this.resource = resource; } /** * Returns the encoding we are using when reading the file. * @return the encoding being used to read the file */ public String getEncoding() { return encoding; } /** * Sets the encoding to use to read the file * @param encoding the encoding the use */ public void setEncoding(String encoding) { this.encoding = encoding; } /** * Do we expect all lines in our input to use the same line terminator? * @return */ public boolean isConsistentLineTerminators() { return consistentLineTerminators; } /** * If we know that all lines within the file use the same line terminator, we can provide a hint to the * SplittingReader to optimise its data extraction * * @param consistentLineTerminators Set to true if all lines use the same line terminator for a speed boost during splitting */ public void setConsistentLineTerminators(boolean consistentLineTerminators) { this.consistentLineTerminators = consistentLineTerminators; } }