ParserFactory.java
package emissary.parser;
import emissary.config.ConfigUtil;
import emissary.config.Configurator;
import emissary.core.Factory;
import emissary.util.shell.Executrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.channels.SeekableByteChannel;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Provide a factory for getting the proper type of input parser Provide the implementing classes for that match the
* configured Data Identifier Engine in PARSER_NIO_IMPL_[type] variants if available. All configured parsers must
* implement emissary.parser.SessionParser.
*
* When no proper mappings are found or the specified parser cannot be instantiated, the SimpleNioParser is used
* instead. If these cannot be instantiated, then something is likely seriously wrong.
*
* If an NIO parser is requested, see makeSessionParser(FileChannel), but cannot be found for the data type, the Channel
* is evaluated and if under MAX_NIO_FALLBACK_SIZE, then the bytes are consumed and a standard parser is produced if one
* is available.
*
* The ID engine is configured with the ID_ENGINE_CLASS in the configuration file and must be an instance of
* emissary.parser.DataIdentifier.
*/
public class ParserFactory {
// Logger
protected static final Logger logger = LoggerFactory.getLogger(ParserFactory.class.getName());
// Map of dataType to FileChannel parser implementation class name
// Read from config file
protected Map<String, String> nioTypeMap = new HashMap<>();
// For channel sizes larger than this no fallback to a byte[]
// parser is attempted.
protected long nioFallbackMax = 1024L * 1024L * 100L; // 100 Mb
protected static final String DEFAULT_NIO_PARSER = "emissary.parser.SimpleNioParser";
protected String nioParser = DEFAULT_NIO_PARSER;
// Data type identification engine
@Nullable
DataIdentifier idEngine = null;
/**
* Public constructor causes default configuration to be read
*/
public ParserFactory() {
reconfigure();
}
/**
* Construct factory with specified configuration
*
* @param config the configuration to use for this instance
* @since 3.7.1
*/
public ParserFactory(Configurator config) {
reconfigure(config);
}
/**
* Configure this factory with default config (for keeping the API backward compatible)
*/
public void reconfigure() {
reconfigure(null);
}
/**
* Reconfigure the factory causes the configuration to be reloaded It is not threadsafe to call this while data is being
* identified or parsers are being instantiated.
*
* @param config the configuration to use or null for the default
* @since 3.7.1
*/
public void reconfigure(@Nullable Configurator config) {
try {
if (config == null) {
config = ConfigUtil.getConfigInfo(ParserFactory.class);
}
Map<String, String> m = config.findStringMatchMap("PARSER_NIO_IMPL_", Configurator.PRESERVE_CASE);
nioFallbackMax = config.findSizeEntry("MAX_NIO_FALLBACK_SIZE", nioFallbackMax);
nioTypeMap.clear();
nioTypeMap.putAll(m);
logger.debug("Loaded {} nio parsers with fallback size {}", nioTypeMap.size(), nioFallbackMax);
// change this to "DEFAULT_PARSER"
nioParser = config.findStringEntry("DEFAULT_NIO_PARSER", DEFAULT_NIO_PARSER);
String idEngineClass = config.findStringEntry("ID_ENGINE_CLASS", null);
if (idEngineClass != null) {
makeIdEngine(idEngineClass);
}
} catch (IOException ex) {
logger.error("Unable to read configuration", ex);
}
}
/**
* Make a session parser with the data in channel. If no NIO parser is configured for the type of this data, a standard
* byte[] parser will be produced if there is one available and the size of the data in the channel is less than the
* configured MAX_NIO_FALLBACK_SIZE. Otherwise the default NIO parser will be used.
*
* @param channel the data to be parsed
* @return SessionParser implementation
*/
public SessionParser makeSessionParser(SeekableByteChannel channel) {
String id = identify(channel);
return makeSessionParser(id, channel);
}
/**
* Make a session parser with the data in channel. If no NIO parser is configured for the type of this data, a standard
* byte[] parser will be produced if there is one available and the size of the data in the channel is less than the
* configured MAX_NIO_FALLBACK_SIZE. Otherwise the default NIO parser will be used.
*
* @param type the type of data
* @param channel the data to be parsed
* @return SessionParser implementation
* @deprecated use {@link #makeSessionParser(SeekableByteChannel, String)}
*/
@Deprecated
@SuppressWarnings("InconsistentOverloads")
public SessionParser makeSessionParser(String type, SeekableByteChannel channel) {
return makeSessionParser(channel, type);
}
/**
* Make a session parser with the data in channel. If no NIO parser is configured for the type of this data, a standard
* byte[] parser will be produced if there is one available and the size of the data in the channel is less than the
* configured MAX_NIO_FALLBACK_SIZE. Otherwise the default NIO parser will be used.
*
* @param channel the data to be parsed
* @param type the type of data
* @return SessionParser implementation
*/
public SessionParser makeSessionParser(SeekableByteChannel channel, String type) {
SessionParser sp;
if (nioTypeMap.containsKey(type)) {
sp = makeSessionParserClass(nioTypeMap.get(type), channel);
} else {
sp = makeSessionParserClass(nioParser, channel);
}
return sp;
}
/**
* Make a session parser for the specified data type with the args
*
* @param clazz the class name of the parser to create
* @param args arguments to the parser constructor
* @return SessionParser implementation
*/
@Nullable
protected SessionParser makeSessionParserClass(@Nullable String clazz, Object... args) {
// Choose implementation class based on data type
if (clazz == null) {
logger.warn("Cannot make a session parser for a null class");
return null;
}
SessionParser sp = null;
try {
sp = (SessionParser) Factory.create(clazz, args);
} catch (RuntimeException e) {
logger.error("Unable to instantiate {}", clazz, e);
}
return sp;
}
/**
* Instantiate the specified DataIdentifier class for typing the data
*/
protected void makeIdEngine(String clazz) {
try {
DataIdentifier d = (DataIdentifier) Factory.create(clazz);
idEngine = d;
} catch (RuntimeException ex) {
logger.warn("Cannot make data identifier from " + clazz, ex);
}
}
/**
* Return the key identification type fo the data in the channel
*
* @param channel the channel containing bytes to identify
* @return string matching the keys in ParserFactory.cfg
*/
public String identify(SeekableByteChannel channel) {
if (idEngine != null) {
try {
long pos = channel.position();
byte[] buf = Executrix.readDataFromChannel(channel, pos, idEngine.DATA_ID_STR_SZ);
channel.position(pos);
return idEngine.identify(buf);
} catch (IOException e) {
logger.warn("Unable to reposition file channel", e);
}
}
return DataIdentifier.UNKNOWN_TYPE;
}
}