quickfix.SessionSettings Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quickfixj-core Show documentation
Show all versions of quickfixj-core Show documentation
The core QuickFIX/J engine
The newest version!
/*******************************************************************************
* Copyright (c) quickfixengine.org All rights reserved.
*
* This file is part of the QuickFIX FIX Engine
*
* This file may be distributed under the terms of the quickfixengine.org
* license as defined by quickfixengine.org and appearing in the file
* LICENSE included in the packaging of this file.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* See http://www.quickfixengine.org/LICENSE for licensing information.
*
* Contact [email protected] if any conditions of this licensing
* are not clear to you.
******************************************************************************/
package quickfix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import quickfix.field.converter.BooleanConverter;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Settings for sessions. Settings are grouped by FIX version and target company
* ID. There is also a default settings section that is inherited by the
* session-specific sections.
*
* Setting constants are declared in the classes using the settings. To find the
* string constants, navigate to the class constant for the setting, select the
* link for the setting and then and select the "Constant Field Values" link in
* the detailed field description.
*
* @see quickfix.Acceptor
* @see quickfix.Initiator
* @see quickfix.FileLogFactory
* @see quickfix.ScreenLogFactory
* @see quickfix.FileStoreFactory
* @see quickfix.JdbcSetting
* @see quickfix.Session
* @see quickfix.DefaultSessionFactory
*/
public class SessionSettings {
private static final Logger log = LoggerFactory.getLogger(SessionSettings.class);
private static final SessionID DEFAULT_SESSION_ID = new SessionID("DEFAULT", "", "");
private static final String SESSION_SECTION_NAME = "session";
private static final String DEFAULT_SECTION_NAME = "default";
public static final String BEGINSTRING = "BeginString";
public static final String SENDERCOMPID = "SenderCompID";
public static final String SENDERSUBID = "SenderSubID";
public static final String SENDERLOCID = "SenderLocationID";
public static final String TARGETCOMPID = "TargetCompID";
public static final String TARGETSUBID = "TargetSubID";
public static final String TARGETLOCID = "TargetLocationID";
public static final String SESSION_QUALIFIER = "SessionQualifier";
// This was using the line.separator system property but that caused
// problems with moving configuration files between *nix and Windows.
private static final String NEWLINE = "\r\n";
private Properties variableValues;
/**
* Creates an empty session settings object.
*/
public SessionSettings() {
this(System.getProperties());
}
/**
* Creates an empty session settings object with custom source of variable values in the settings.
* @param variableValues custom source of variable values in the settings
*/
public SessionSettings(Properties variableValues) {
sections.put(DEFAULT_SESSION_ID, new Properties());
this.variableValues = variableValues;
}
/**
* Loads session settings from a file with custom source of variable values in the settings.
*
* @param filename the path to the file containing the session settings
* @param variableValues custom source of variable values in the settings
* @throws quickfix.ConfigError when file could not be loaded
*/
public SessionSettings(String filename, Properties variableValues) throws ConfigError {
this(variableValues);
loadFromFile(filename);
}
/**
* Loads session settings from a file.
*
* @param filename the path to the file containing the session settings
* @throws quickfix.ConfigError when file could not be loaded
*/
public SessionSettings(String filename) throws ConfigError {
this();
loadFromFile(filename);
}
/**
* Loads session settings from an input stream.
*
* @param stream the input stream
* @throws ConfigError
*/
public SessionSettings(InputStream stream) throws ConfigError {
this();
load(stream);
}
/**
* Loads session settings from an input stream with custom source of variable values in the settings.
*
* @param stream the input stream
* @param variableValues custom source of variable values in the settings
* @throws ConfigError
*/
public SessionSettings(InputStream stream, Properties variableValues) throws ConfigError {
this(variableValues);
load(stream);
}
/**
* Loads session settings from a list of strings.
*
* @param listValues the list of strings
* @throws ConfigError
*/
public SessionSettings(List listValues) throws ConfigError {
this();
loadFromList(listValues);
}
/**
* Loads session settings from a list of strings with custom source of variable values in the settings.
*
* @param listValues the list of strings
* @param variableValues custom source of variable values in the settings
* @throws ConfigError
*/
public SessionSettings(List listValues, Properties variableValues) throws ConfigError {
this(variableValues);
loadFromList(listValues);
}
/**
* Gets a string from the default section of the settings.
*
* @param key
* @return the default string value
* @throws ConfigError
*/
public String getString(String key) throws ConfigError {
return getString(DEFAULT_SESSION_ID, key);
}
/**
* Get a settings string.
*
* @param sessionID the session ID
* @param key the settings key
* @return the string value for the setting
* @throws ConfigError configuration error, probably a missing setting.
*/
public String getString(SessionID sessionID, String key) throws ConfigError {
final String value = interpolate(getSessionProperties(sessionID).getProperty(key));
if (value == null) {
throw new ConfigError(key + " not defined");
}
return value;
}
/**
* Return the settings for a session as a Properties object.
*
* @param sessionID
* @param includeDefaults if true, include settings defaults in properties
* @return the Properties object with the session settings
* @throws ConfigError
* @see java.util.Properties
*/
public Properties getSessionProperties(SessionID sessionID, boolean includeDefaults)
throws ConfigError {
final Properties p = sections.get(sessionID);
if (p == null) {
throw new ConfigError("Session not found");
}
if (includeDefaults) {
final Properties mergedProperties = new Properties();
mergedProperties.putAll(sections.get(DEFAULT_SESSION_ID));
mergedProperties.putAll(p);
return mergedProperties;
} else {
return p;
}
}
/**
* Return the settings for a session as a Properties object.
*
* @param sessionID
* @return the Properties object with the session settings
* @throws ConfigError
* @see java.util.Properties
*/
public Properties getSessionProperties(SessionID sessionID) throws ConfigError {
return getSessionProperties(sessionID, false);
}
/**
* Returns the defaults for the session-level settings.
*
* @return the default properties
*/
public Properties getDefaultProperties() {
try {
return getSessionProperties(DEFAULT_SESSION_ID);
} catch (final ConfigError e) {
// shouldn't happen
return new Properties();
}
}
/**
* Gets a long from the default section of settings.
*
* @param key
* @return the default value
* @throws ConfigError
* @throws FieldConvertError
*/
public long getLong(String key) throws ConfigError, FieldConvertError {
return getLong(DEFAULT_SESSION_ID, key);
}
/**
* Get a settings value as a long integer.
*
* @param sessionID the session ID
* @param key the settings key
* @return the long integer value for the setting
* @throws ConfigError configuration error, probably a missing setting.
* @throws FieldConvertError error during field type conversion.
*/
public long getLong(SessionID sessionID, String key) throws ConfigError, FieldConvertError {
try {
return Long.parseLong(getString(sessionID, key));
} catch (final NumberFormatException e) {
throw new FieldConvertError(e.getMessage());
}
}
/**
* Gets an int from the default section of settings.
*
* @param key
* @return the default value
* @throws ConfigError
* @throws FieldConvertError
*/
public int getInt(String key) throws ConfigError, FieldConvertError {
return getInt(DEFAULT_SESSION_ID, key);
}
/**
* Get a settings value as an integer.
*
* @param sessionID the session ID
* @param key the settings key
* @return the long integer value for the setting
* @throws ConfigError configuration error, probably a missing setting.
* @throws FieldConvertError error during field type conversion.
*/
public int getInt(SessionID sessionID, String key) throws ConfigError, FieldConvertError {
try {
return Integer.parseInt(getString(sessionID, key));
} catch (final NumberFormatException e) {
throw new FieldConvertError(e.getMessage());
}
}
private Properties getOrCreateSessionProperties(SessionID sessionID) {
return sections.computeIfAbsent(sessionID, k -> new Properties(sections.get(DEFAULT_SESSION_ID)));
}
/**
* Gets a double value from the default section of the settings.
*
* @param key
* @return the default value
* @throws ConfigError
* @throws FieldConvertError
*/
public double getDouble(String key) throws ConfigError, FieldConvertError {
return getDouble(DEFAULT_SESSION_ID, key);
}
/**
* Get a settings value as a double number.
*
* @param sessionID the session ID
* @param key the settings key
* @return the double number value for the setting
* @throws ConfigError configuration error, probably a missing setting.
* @throws FieldConvertError error during field type conversion.
*/
public double getDouble(SessionID sessionID, String key) throws ConfigError, FieldConvertError {
try {
return Double.parseDouble(getString(sessionID, key));
} catch (final NumberFormatException e) {
throw new FieldConvertError(e.getMessage());
}
}
/**
* Gets a boolean value from the default section of the settings.
*
* @param key
* @return the boolean value
* @throws ConfigError
* @throws FieldConvertError
*/
public boolean getBool(String key) throws ConfigError, FieldConvertError {
return getBool(DEFAULT_SESSION_ID, key);
}
/**
* Get a settings value as a boolean value.
*
* @param sessionID the session ID
* @param key the settings key
* @return the boolean value for the setting
* @throws ConfigError configuration error, probably a missing setting.
* @throws FieldConvertError error during field type conversion.
*/
public boolean getBool(SessionID sessionID, String key) throws ConfigError, FieldConvertError {
try {
return BooleanConverter.convert(getString(sessionID, key));
} catch (final FieldConvertError e) {
throw new ConfigError(e);
}
}
/**
* Sets a string-valued session setting.
*
* @param sessionID the session ID
* @param key the setting key
* @param value the string value
*/
public void setString(SessionID sessionID, String key, String value) {
getOrCreateSessionProperties(sessionID).setProperty(key, value.trim());
}
/**
* Sets a long integer-valued session setting.
*
* @param sessionID the session ID
* @param key the setting key
* @param value the long integer value
*/
public void setLong(SessionID sessionID, String key, long value) {
getOrCreateSessionProperties(sessionID).setProperty(key, Long.toString(value));
}
/**
* Sets a double-valued session setting.
*
* @param sessionID the session ID
* @param key the setting key
* @param value the double value
*/
public void setDouble(SessionID sessionID, String key, double value) {
getOrCreateSessionProperties(sessionID).setProperty(key, Double.toString(value));
}
/**
* Sets a boolean-valued session setting.
*
* @param sessionID the session ID
* @param key the setting key
* @param value the boolean value
*/
public void setBool(SessionID sessionID, String key, boolean value) {
getOrCreateSessionProperties(sessionID).setProperty(key, BooleanConverter.convert(value));
}
private final ConcurrentMap sections = new ConcurrentHashMap<>();
public Iterator sectionIterator() {
final HashSet nondefaultSessions = new HashSet<>(sections.keySet());
nondefaultSessions.remove(DEFAULT_SESSION_ID);
return nondefaultSessions.iterator();
}
private void loadFromFile(String filename) throws ConfigError {
try (InputStream in = getClass().getClassLoader().getResourceAsStream(filename)) {
if (in != null) {
load(in);
} else {
try (InputStream in2 = new FileInputStream(filename)) {
load(in2);
}
}
} catch (final IOException ex) {
throw new ConfigError(ex.getMessage());
}
}
private void loadFromList(List listValues) throws ConfigError {
byte[] bytes = listValues.stream().collect(Collectors.joining(System.lineSeparator())).getBytes();
InputStream in = new ByteArrayInputStream(bytes);
load(in);
}
private void load(InputStream inputStream) throws ConfigError {
try (final Reader reader = new InputStreamReader(inputStream)) {
Properties currentSection = null;
String currentSectionId = null;
final Tokenizer tokenizer = new Tokenizer();
Tokenizer.Token token = tokenizer.getToken(reader);
while (token != null) {
if (token.getType() == Tokenizer.SECTION_TOKEN) {
storeSection(currentSectionId, currentSection);
if (token.getValue().equalsIgnoreCase(DEFAULT_SECTION_NAME)) {
currentSectionId = DEFAULT_SECTION_NAME;
currentSection = getSessionProperties(DEFAULT_SESSION_ID);
} else if (token.getValue().equalsIgnoreCase(SESSION_SECTION_NAME)) {
currentSectionId = SESSION_SECTION_NAME;
currentSection = new Properties(getSessionProperties(DEFAULT_SESSION_ID));
}
} else if (token.getType() == Tokenizer.ID_TOKEN) {
final Tokenizer.Token valueToken = tokenizer.getToken(reader);
if (currentSection != null) {
final String value = interpolate(valueToken.getValue());
currentSection.put(token.getValue(), value);
}
}
token = tokenizer.getToken(reader);
}
storeSection(currentSectionId, currentSection);
} catch (final IOException e) {
final ConfigError configError = new ConfigError(e.getMessage());
throw configError;
}
}
private void storeSection(String currentSectionId, Properties currentSection) {
if (currentSectionId != null && currentSectionId.equals(SESSION_SECTION_NAME)) {
final SessionID sessionId = new SessionID(currentSection.getProperty(BEGINSTRING),
currentSection.getProperty(SENDERCOMPID),
currentSection.getProperty(SENDERSUBID),
currentSection.getProperty(SENDERLOCID),
currentSection.getProperty(TARGETCOMPID),
currentSection.getProperty(TARGETSUBID),
currentSection.getProperty(TARGETLOCID),
currentSection.getProperty(SESSION_QUALIFIER));
sections.put(sessionId, currentSection);
}
}
/**
* Predicate for determining if a setting is in the default section.
*
* @param key
* @return true if setting is in the defaults, false otherwise
*/
public boolean isSetting(String key) {
return isSetting(DEFAULT_SESSION_ID, key);
}
/**
* Predicate for determining if a setting exists.
*
* @param sessionID the session ID
* @param key the setting key
* @return true is setting exists, false otherwise.
*/
public boolean isSetting(SessionID sessionID, String key) {
return getOrCreateSessionProperties(sessionID).getProperty(key) != null;
}
public void removeSetting(SessionID sessionID, String key) {
getOrCreateSessionProperties(sessionID).remove(key);
}
private static class Tokenizer {
//public static final int NONE_TOKEN = 1;
public static final int ID_TOKEN = 2;
public static final int VALUE_TOKEN = 3;
public static final int SECTION_TOKEN = 4;
private static class Token {
private final int type;
private final String value;
public Token(int type, String value) {
super();
this.type = type;
this.value = value;
}
public int getType() {
return type;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return type + ": " + value;
}
}
private char ch = '\0';
private final StringBuilder sb = new StringBuilder();
private Token getToken(Reader reader) throws IOException {
if (ch == '\0') {
ch = nextCharacter(reader);
}
skipWhitespace(reader);
if (isLabelCharacter(ch)) {
sb.setLength(0);
do {
sb.append(ch);
ch = nextCharacter(reader);
} while (isLabelCharacter(ch));
return new Token(ID_TOKEN, sb.toString());
} else if (ch == '=') {
ch = nextCharacter(reader);
sb.setLength(0);
if (isValueCharacter(ch)) {
do {
sb.append(ch);
ch = nextCharacter(reader);
} while (isValueCharacter(ch));
}
return new Token(VALUE_TOKEN, sb.toString().trim());
} else if (ch == '[') {
ch = nextCharacter(reader);
final Token id = getToken(reader);
// check ]
ch = nextCharacter(reader); // skip ]
return new Token(SECTION_TOKEN, id.getValue());
} else if (ch == '#') {
do {
ch = nextCharacter(reader);
} while (isValueCharacter(ch));
return getToken(reader);
}
return null;
}
private boolean isNewLineCharacter(char ch) {
return NEWLINE.indexOf(ch) != -1;
}
private boolean isLabelCharacter(char ch) {
return !isEndOfStream(ch) && "[]=#".indexOf(ch) == -1;
}
private boolean isValueCharacter(char ch) {
return !isEndOfStream(ch) && !isNewLineCharacter(ch);
}
private boolean isEndOfStream(char ch) {
return (byte) ch == -1;
}
private char nextCharacter(Reader reader) throws IOException {
return (char) reader.read();
}
private void skipWhitespace(Reader reader) throws IOException {
if (Character.isWhitespace(ch)) {
do {
ch = nextCharacter(reader);
} while (Character.isWhitespace(ch));
}
}
}
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{(.+?)}");
private String interpolate(String value) {
if (value == null || value.indexOf('$') == -1) {
return value;
}
final StringBuffer buffer = new StringBuffer();
final Matcher m = VARIABLE_PATTERN.matcher(value);
while (m.find()) {
if (m.start() > 0 && value.charAt(m.start() - 1) == '\\') {
continue;
}
final String variable = m.group(1);
final String variableValue = variableValues.getProperty(variable);
if (variableValue != null) {
m.appendReplacement(buffer, variableValue);
}
}
m.appendTail(buffer);
return buffer.toString();
}
/**
* Set properties that will be the source of variable values in the settings. A variable
* is of the form ${variable} and will be replaced with values from the
* map when the setting is retrieved.
*
* By default, the System properties are used for variable values. If
* you use this method, it will override the defaults so use the Properties
* default value mechanism if you want to chain a custom properties object
* with System properties as the default.
*
*
* // Custom properties with System properties as default
* Properties myprops = new Properties(System.getProperties());
* myprops.load(getPropertiesInputStream());
* settings.setVariableValues(myprops);
*
* // Custom properties with System properties as override
* Properties myprops = new Properties();
* myprops.load(getPropertiesInputStream());
* myprops.putAll(System.getProperties());
* settings.setVariableValues(myprops);
*
*
* @param variableValues
*
* @see java.util.Properties
* @see java.lang.System
*/
public void setVariableValues(Properties variableValues) {
this.variableValues = variableValues;
}
/**
* Adds defaults to the settings. Will not delete existing settings not
* overlapping with the new defaults, but will overwrite existing settings
* specified in this call.
*
* @param defaults
*/
public void set(Map