/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.dal.spi;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import org.apache.log4j.Logger;
/**
* Helper class which helps manage different plug implementations.<p>This
* class expects specifc keywords in configuration properties, which define
* which plugs are present and which instances can be used for particular
* plug.</p>
*
* @author Igor Kriznar (igor.kriznarATcosylab.com)
*/
public final class Plugs{
/*
*
* STATIC DECLARATIONS, COMMON TO ALL PLUGS OBJECTS
*
*/
/**
* Keyword used in System properties to configure DAL logging.
* Valie values are:
* <ul>
* <li>true - DAL will call basic log4j configuration, which enables logging to console.</li>
* <li>false - DAL will not configure logging to console.</li>
* </ul>
*
* By default true is assumed and logging will be configured.
*
* <p>
* Regardless of this setting an application can configure own appenders for DAL logging.
* See {@link Plugs#getLogger()} for details.
* </p>
*
* @see #getLogger()
*/
public static final String PLUGS_LOGGING = "dal.logging";
/**
* Keyword for list of plugs. Plugs in list must be separated with
* comman.
*/
public static final String PLUGS = "dal.plugs";
/** Keyword for default plug name. */
public static final String PLUGS_DEFAULT = "dal.plugs.default";
/** Keyword declaring device factory class name for particular plug. */
public static final String PLUG_DEVICE_FACTORY_CLASS = "dal.devicefactory.";
/** Keyword declaring property factory class name for particular plug. */
public static final String PLUG_PROPERTY_FACTORY_CLASS = "dal.propertyfactory.";
/**
* Optional system configuration property which tells to plug implementations
* what is expected timeout for remote operations in milliseconds.
*/
public static final String CONNECTION_TIMEOUT = "dal.connectionTimeout";
/**
* Optional system configuration property, which defines the timeout for
* establishing or initializing the connections in milliseconds.
*/
public static final String INITIAL_CONNECTION_TIMEOUT = "dal.initialConnectionTimeout";
/**
* System configuration property which tells to property factory implementations
* to use properties from cache when connection is requested. By default new property is created.
*/
public static final String PROPERTIES_FROM_CACHE = "dal.propertiesFromCache";
/**
* An optional application configuration property name under which is in AbstractAppplicationContext stored an instance of
* PropertyFactoryService. This service implementation must be able to produce PropertyFactory
* implementations for all supported and available plug types within application runtime context.
*/
public static final String PROPERTY_FACTORY_SERVICE_IMPLEMENTATION = "dal.propertyfactoryServiceImplementation";
/**
* DAL default connection timeout value in milliseconds. Used if CONNECTION_TIMEOUT is not defined.
*/
public static final long DEFAULT_CONNECTION_TIMEOUT = 30000;
/**
* DAL default initial connection timeout value in milliseconds. Used if INITIAL_CONNECTION_TIMEOUT is not defined.
*/
public static final long DEFAULT_INITIAL_CONNECTION_TIMEOUT = 200;
public static final String SIMULATOR_PLUG_TYPE = "Simulator";
private static Plugs plugs;
private Properties properties;
private static Logger logger;
/**
* Returns an instance of <code>Plugs</code> with System <code>Properties</code> as storage.
* @return an instance of <code>Plugs</code> with System <code>Properties</code> as storage
*/
public static final synchronized Plugs getInstance() {
if (plugs == null)
plugs = new Plugs(System.getProperties());
return plugs;
}
/**
* Returns an instance of <code>Plugs</code> with the specified list
* of <code>Properties</code>.
* @return an instance of <code>Plugs</code>
*/
public static Plugs getInstance(Properties properties) {
return new Plugs(properties);
}
/**
* Copies the <code>Properties</code> to this <code>Plugs</code>.
* @param properties the <code>Properties</code> to copy from
*/
public void putAll(Properties properties) {
this.properties.putAll(properties);
}
/**
* Returns array with plug names defined in provided properties.
*
* @param prop properties
*
* @return array with plug names
*/
public static String[] getPlugNames(Properties prop){
String names = prop.getProperty(PLUGS);
if (names == null)
return new String[0];
String[] r = names.split(",");
if (r == null)
return new String[0];
return r;
}
/**
* Returns property factory class for plug name. Class name is
* obtained from provided configuration.
*
* @param name plug name
* @param prop properties
*
* @return property factory class
*
* @throws IllegalArgumentException if properties does not contin proper configuration
*/
public static Class<?> getPropertyFactoryClassForPlug(String name, Properties prop){
String cn = prop.getProperty(PLUG_PROPERTY_FACTORY_CLASS + name);
if (cn != null) {
Class<?> cl = null;
try {
cl = Class.forName(cn);
} catch (Throwable t) {
t.printStackTrace();
throw new IllegalArgumentException("Could not load class '" + cn + "' for factory implementation: " + t);
}
if (!PropertyFactory.class.isAssignableFrom(cl))
throw new IllegalArgumentException("Class '" + cn + "' is not a factory implementation!");
return cl;
}
throw new IllegalArgumentException("No factory class defined for plug '" + name + "'.");
}
/**
* Returns device factory class for plug name. Class name is
* obtained from provided configuration.
*
* @param name plug name
* @param prop properties
*
* @return device factory class
*
* @throws IllegalArgumentException if properties does not contin proper configuration
*/
public static Class<?> getDeviceFactoryClassForPlug(String name, Properties prop){
String cn = prop.getProperty(PLUG_DEVICE_FACTORY_CLASS + name);
if (cn != null) {
Class<?> cl = null;
try {
cl = Class.forName(cn);
} catch (Throwable t) {
throw new IllegalArgumentException("Could not load class '" + cn + "' for factory implementation: " + t);
}
if (!DeviceFactory.class.isAssignableFrom(cl))
throw new IllegalArgumentException("Class '" + cn + "' is not a factory implementation!");
return cl;
}
throw new IllegalArgumentException("No factory class defined for plug '" + name + "'.");
}
/**
* Tries to locate and load default property factory class. First
* tries to find <code>PropertyFactoryService.DEFAULT_FACTORY_IMPL</code>
* key of factory key for <code>PLUGS_DEFAULT</code> (in this order) in
* provided properties. If this fails or properties are null, then System
* properties are searched.
*
* @param prop properties, may be <code>null</code>
*
* @return property factory class or <code>null</code> if no configuration
* found
*
* @throws ClassNotFoundException if class loading failed
*/
public static Class<?> getDefaultPropertyFactory(Properties prop) throws ClassNotFoundException{
String cn = null;
if (prop != null) {
cn = prop.getProperty(PropertyFactoryService.DEFAULT_FACTORY_IMPL);
if (cn == null) {
String pl = prop.getProperty(PLUGS_DEFAULT);
if (pl != null)
cn = prop.getProperty(PLUG_PROPERTY_FACTORY_CLASS + pl);
}
}
if (cn == null) {
prop = System.getProperties();
cn = prop.getProperty(PropertyFactoryService.DEFAULT_FACTORY_IMPL);
if (cn == null) {
String pl = prop.getProperty(PLUGS_DEFAULT);
if (pl != null)
cn = prop.getProperty(PLUG_PROPERTY_FACTORY_CLASS + pl);
}
}
if (cn == null)
return null;
Class<?> cl = Class.forName(cn);
if (!PropertyFactory.class.isAssignableFrom(cl))
throw new ClassNotFoundException("Class '" + cn + "' is not a factory implementation!");
return cl;
}
/**
* Tries to locate default plug name. First
* tries to find key for <code>PLUGS_DEFAULT</code> in
* provided properties. If this fails or properties are null, then System
* properties are searched.
*
* @param prop properties, may be <code>null</code>
*
* @return default plug name or <code>null</code> if no configuration
* found
*
* @throws ClassNotFoundException if class loading failed
*/
public static String getDefaultPlug(Properties prop){
if (prop != null) {
String pl = prop.getProperty(PLUGS_DEFAULT);
if (pl!=null)
return pl;
}
prop = System.getProperties();
String pl = prop.getProperty(PLUGS_DEFAULT);
return pl;
}
/**
* Tries to locate and load default device factory class. First
* tries to find <code>DeviceFactoryService.DEFAULT_DEVICE_IMPL</code>
* key of factory key for <code>PLUGS_DEFAULT</code> (in this order) in
* provided properties. If this fails or properties are null, then System
* properties are searched.
*
* @param prop properties, may be <code>null</code>
*
* @return device factory class or <code>null</code> if no configuration
* found
*
* @throws ClassNotFoundException if class loading failed
*/
public static Class<?> getDefaultDeviceFactory(Properties prop) throws ClassNotFoundException{
String cn = null;
if (prop != null) {
cn = prop.getProperty(DeviceFactoryService.DEFAULT_FACTORY_IMPL);
if (cn == null) {
String pl = prop.getProperty(PLUGS_DEFAULT);
if (pl != null)
cn = prop.getProperty(PLUG_DEVICE_FACTORY_CLASS + pl);
}
}
if (cn == null) {
prop = System.getProperties();
cn = prop.getProperty(DeviceFactoryService.DEFAULT_FACTORY_IMPL);
if (cn == null) {
String pl = prop.getProperty(PLUGS_DEFAULT);
if (pl != null)
cn = prop.getProperty(PLUG_DEVICE_FACTORY_CLASS + pl);
}
}
if (cn == null)
return null;
Class<?> cl = Class.forName(cn);
if (!DeviceFactory.class.isAssignableFrom(cl))
throw new ClassNotFoundException("Class '" + cn + "' is not a factory implementation!");
return cl;
}
/**
* Convenience method which tries to get connection timeout first from provided properties,
* then from system properties and if both fails returns provided default value.
* @param p properties, may be null
* @param def default fallback values
* @return connection timeout propety value
*/
public static final long getConnectionTimeout(Properties p, long def){
String s = null;
if (p != null) {
s = p.getProperty(CONNECTION_TIMEOUT);
if (s != null) {
try {
return Long.parseLong(s);
} catch (Exception e) {
Logger.getLogger(Plugs.class).warn("System defined property "+CONNECTION_TIMEOUT+" could not be parsed as long.", e);
}
}
}
return Long.getLong(CONNECTION_TIMEOUT, def);
}
/**
* Convenience method which tries to get connection timeout first from provided properties,
* then from system properties and if both fails returns DAL default value.
* @param p properties, may be null
* @param def default fallback values
* @return connection timeout propety value
*/
public static final long getConnectionTimeout(Properties p){
return getConnectionTimeout(p, DEFAULT_CONNECTION_TIMEOUT);
}
public long getConnectionTimeout() {
return getConnectionTimeout(properties);
}
/**
* Convenience method which tries to get initial connection timeout first from provided properties,
* then from system properties and if both fails returns provided default value.
* @param p properties, may be null
* @param def default fallback values
* @return connection timeout property value
*/
public static final long getInitialConnectionTimeout(Properties p, long def){
String s = null;
if (p != null) {
s = p.getProperty(INITIAL_CONNECTION_TIMEOUT);
if (s != null) {
try {
return Long.parseLong(s);
} catch (Exception e) {
Logger.getLogger(Plugs.class).warn("System defined property "+INITIAL_CONNECTION_TIMEOUT+" could not be parsed as long.", e);
}
}
}
return Long.getLong(INITIAL_CONNECTION_TIMEOUT, def);
}
/**
* Convenience method which tries to get initial connection timeout first from provided properties,
* then from system properties and if both fails returns DAL default value.
* @param p properties, may be null
* @param def default fallback values
* @return initial connection timeout property value
*/
public static final long getInitialConnectionTimeout(Properties p){
return getInitialConnectionTimeout(p, DEFAULT_INITIAL_CONNECTION_TIMEOUT);
}
/**
* Return logger, which is parent for all DAL plug loggers.
*
* <p>
* DAL Plug loggers collect and distribute messages, which are intended for general plublic.
* E.g. application which is not interested in internal structure, but wants to display progress when some channel
* was connected or some user initiated action failed.
* </p>
*
* <p>
* Parent DAL logger name is 'DAL'. Names of plug loggers are 'DAL.PLUG_NAME', for example 'DAL.EPICS'.
* </p>
*
* <p>
* Default configuration of appenders is controlled with System parameter {@link Plugs#PLUGS_LOGGING}.
* </p>
*
* @return parent logger for all DAL plug loggers.
*
* @see #PLUGS_LOGGING
* @see #getPlugLogger(String)
*/
public static final Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger("DAL");
boolean log= Boolean.parseBoolean(System.getProperty(PLUGS_LOGGING, Boolean.TRUE.toString()));
}
return logger;
}
/**
* Returns logger for particular DAL plug, this logger shuld be used for general messages about plug activity.
*
* @param plugName the name ov the plug for which logger is to be obtained.
*
* @return logger for DAL plug
*
*/
public static final Logger getPlugLogger(String plugName) {
return getLogger().getLogger("DAL."+plugName);
}
/*
*
* INSTANCE SPECIFIC ONLY DECLARATIONS
*
*/
/**
* Gets the <code>Properties</code> of this <code>Plug</code>.
* @return the <code>Properties</code>
*/
public Properties getProperties() {
return properties;
}
/**
*
*/
private Plugs(Properties properties){
this.properties = properties;
configureSimulatorPlug(properties);
}
/**
* Returns array with plug names defined in properties for this Plugs instance.
*
* @return array with plug names
*/
public String[] getPlugNames(){
return getPlugNames(properties);
}
/**
* Returns property factory class for plug name. Class name is
* obtained from properties.
*
* @param name plug name
*
* @return property factory class
*/
public Class<?> getPropertyFactoryClassForPlug(String name){
return getPropertyFactoryClassForPlug(name, properties);
}
/**
* Returns device factory class for plug name. Class name is
* obtained from properties.
*
* @param name plug name
*
* @return device factory class
*/
public Class<?> getDeviceFactoryClassForPlug(String name){
return getDeviceFactoryClassForPlug(name, properties);
}
public long getInitialConnectionTimeout() {
return getInitialConnectionTimeout(properties);
}
public Class<?> getDefaultDeviceFacotry() throws ClassNotFoundException {
return getDefaultDeviceFactory(properties);
}
public Class<?> getDefaultPropertyFactory() throws ClassNotFoundException {
return getDefaultPropertyFactory(properties);
}
public void setConnectionTimeout(long timeout) {
properties.setProperty(CONNECTION_TIMEOUT, (new Long(timeout)).toString());
}
public void registerDefaultDeviceFactory(Class<? extends DeviceFactory> cl) {
properties.setProperty(DeviceFactoryService.DEFAULT_FACTORY_IMPL, cl.getCanonicalName());
}
public void registerDefaultPropertyFactory(Class<? extends PropertyFactory> cl) {
properties.setProperty(PropertyFactoryService.DEFAULT_FACTORY_IMPL, cl.getCanonicalName());
}
public void registerDeviceFactoryClassForPlug(String name, Class<? extends DeviceFactory> cl) {
properties.setProperty(PLUG_DEVICE_FACTORY_CLASS+name, cl.getCanonicalName());
}
public void registerPropertyFactoryClassForPlug(String name, Class<? extends PropertyFactory> cl) {
properties.setProperty(PLUG_PROPERTY_FACTORY_CLASS+name, cl.getCanonicalName());
}
public void setPlugNames(String[] names) {
StringBuilder sb = new StringBuilder();
if (names != null && names.length > 0) {
sb.append(names[0]);
for (int i = 1; i < names.length; i++)
sb.append(','+names[i]);
}
properties.setProperty(PLUGS, sb.toString());
}
/**
* Loads to properties configuration, which enables EPICS plug.
* @param p configuration
*/
public static void configureSimulatorPlug(Properties p){
String[] s = getPlugNames(p);
Set<String> set = new HashSet<String>(Arrays.asList(s));
if (!set.contains(SIMULATOR_PLUG_TYPE)) {
set.add(SIMULATOR_PLUG_TYPE);
StringBuffer sb = new StringBuffer();
for (Iterator<String> iter = set.iterator(); iter.hasNext();) {
if (sb.length() > 0)
sb.append(',');
sb.append(iter.next());
}
p.put(PLUGS, sb.toString());
}
p.put(PLUGS_DEFAULT, SIMULATOR_PLUG_TYPE);
p.put(PLUG_PROPERTY_FACTORY_CLASS + SIMULATOR_PLUG_TYPE, "org.csstudio.dal.simulation.PropertyFactoryImpl");
p.put(PLUG_DEVICE_FACTORY_CLASS + SIMULATOR_PLUG_TYPE, "org.csstudio.dal.simulation.DeviceFactoryImpl");
}
}