1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
package org.springframework.ldap.ldif.batch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.ldap.core.LdapAttributes;
import org.springframework.ldap.ldif.parser.LdifParser;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* The {@link LdifReader LdifReader} is an adaptation of the {@link org.springframework.batch.item.file.FlatFileItemReader FlatFileItemReader}
* built around an {@link LdifParser LdifParser}.
* <p>
* Unlike the {@link org.springframework.batch.item.file.FlatFileItemReader FlatFileItemReader}, the {@link LdifReader LdifReader}
* does not require a mapper. Instead, this version of the {@link LdifReader LdifReader} simply returns an {@link LdapAttributes LdapAttributes}
* object which can be consumed and manipulated as necessary by {@link org.springframework.batch.item.ItemProcessor ItemProcessor} or any
* output service. Alternatively, the {@link RecordMapper RecordMapper} interface can be implemented and set in a
* {@link MappingLdifReader MappingLdifReader} to map records to objects for return.
* <p>
* {@link LdifReader LdifReader} usage is mimics that of the {@link org.springframework.batch.item.file.FlatFileItemReader FlatFileItemReader}
* for all intensive purposes. Adjustments have been made to process records instead of lines, however. As such, the
* {@link #recordsToSkip recordsToSkip} attribute indicates the number of records from the top of the file that should not be processed.
* Implementations of the {@link RecordCallbackHandler RecordCallbackHandler} interface can be used to execute operations on those skipped records.
* <p>
* As with the {@link org.springframework.batch.item.file.FlatFileItemReader FlatFileItemReader}, the {@link #strict strict} option differentiates
* between whether or not to require the resource to exist before processing. In the case of a value set to false, a warning is logged instead of
* an exception being thrown.
*
* @author Keith Barlow
*
*/
public class LdifReader extends AbstractItemCountingItemStreamItemReader<LdapAttributes>
implements ResourceAwareItemReaderItemStream<LdapAttributes>, InitializingBean {
private static final Log log = LogFactory.getLog(LdifReader.class);
private Resource resource;
private LdifParser ldifParser;
private int recordCount = 0;
private int recordsToSkip = 0;
private boolean strict = true;
private RecordCallbackHandler skippedRecordsCallback;
public LdifReader() {
setName(ClassUtils.getShortName(LdifReader.class));
}
/**
* In strict mode the reader will throw an exception on
* {@link #open(org.springframework.batch.item.ExecutionContext)} if the
* input resource does not exist.
* @param strict false by default
*/
public void setStrict(boolean strict) {
this.strict = strict;
}
/**
* {@link RecordCallbackHandler RecordCallbackHandler} implementations can be used to take action on skipped records.
*
* @param skippedRecordsCallback will be called for each one of the initial
* skipped lines before any items are read.
*/
public void setSkippedRecordsCallback(RecordCallbackHandler skippedRecordsCallback) {
this.skippedRecordsCallback = skippedRecordsCallback;
}
/**
* Public setter for the number of lines to skip at the start of a file. Can
* be used if the file contains a header without useful (column name)
* information, and without a comment delimiter at the beginning of the
* lines.
*
* @param recordsToSkip the number of lines to skip
*/
public void setRecordsToSkip(int recordsToSkip) {
this.recordsToSkip = recordsToSkip;
}
@Override
protected void doClose() throws Exception {
if (ldifParser != null) {
ldifParser.close();
}
this.recordCount = 0;
}
@Override
protected void doOpen() throws Exception {
if (resource == null)
throw new IllegalStateException("A resource has not been set.");
if (!resource.exists()) {
if (strict) {
throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode): "+resource);
} else {
log.warn("Input resource does not exist " + resource.getDescription());
return;
}
}
ldifParser.open();
for (int i = 0; i < recordsToSkip; i++) {
LdapAttributes record = ldifParser.getRecord();
if (skippedRecordsCallback != null) {
skippedRecordsCallback.handleRecord(record);
}
}
}
@Override
protected LdapAttributes doRead() throws Exception {
LdapAttributes attributes = null;
try {
if (ldifParser != null) {
while (attributes == null && ldifParser.hasMoreRecords()) {
attributes = ldifParser.getRecord();
}
recordCount++;
}
return attributes;
} catch(Exception ex){
log.error("Parsing error at record " + recordCount + " in resource=" +
resource.getDescription() + ", input=[" + attributes + "]", ex);
throw ex;
}
}
public void setResource(Resource resource) {
this.resource = resource;
this.ldifParser = new LdifParser(resource);
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(resource, "A resource is required to parse.");
Assert.notNull(ldifParser);
}
}
|