/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.configuration;
import java.io.IOException;
import java.io.InputStream;
import org.ws4d.java.DPWSFramework;
import org.ws4d.java.io.buffered.BufferedInputStream;
import org.ws4d.java.structures.DataStructure;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.structures.HashSet;
import org.ws4d.java.structures.Iterator;
import org.ws4d.java.util.Log;
/**
* Class provides configuration file/ stream reading and manages to call the
* different property handlers. Standard handlers will be registered.
* <p>
* The file "example.properties" contains a example configuration.
* </p>
* Properties have to be initialized with one of the init methods:
* <ul>
* <li>{@link #init()}
* <li>{@link #init(InputStream)}
* <li>{@link #init(String)}
* </ul>
* <p>
* Properties should be initialized before starting the {@link DPWSFramework}.
* </p>
*/
public final class Properties {
public static final String PROP_HANDLER = "PropertiesHandler";
public static final String PROP_BINDING = "Binding";
public static final String PROP_CONFIGURATION_ID = "ConfigurationId";
// SECTIONS
public final static String[] SECTION_BINDINGS = { "Bindings" };
public final static String[] SECTION_GLOBAL = { "Global" };
public static final String[] SECTION_DEVICES = { "Devices" };
public static final String[] SECTION_SERVICES = { "Services" };
public static final String[] SECTION_EVENTING = { "Eventing" };
public static final String[] SECTION_DPWS = { "DPWS" };
public static final String[] SECTION_HTTP = { "HTTP" };
public static final String[] SECTION_IP = { "IP" };
public static final String[] SECTION_SECURITY = { "Security" };
public final static PropertyHeader HEADER_SECTION_BINDINGS = new PropertyHeader(SECTION_BINDINGS);
public final static PropertyHeader HEADER_SECTION_GLOBAL = new PropertyHeader(SECTION_GLOBAL);
public final static PropertyHeader HEADER_SECTION_DEVICES = new PropertyHeader(SECTION_DEVICES);
public final static PropertyHeader HEADER_SECTION_SERVICES = new PropertyHeader(SECTION_SERVICES);
public final static PropertyHeader HEADER_SECTION_EVENTING = new PropertyHeader(SECTION_EVENTING);
public final static PropertyHeader HEADER_SECTION_DPWS = new PropertyHeader(SECTION_DPWS);
public final static PropertyHeader HEADER_SECTION_HTTP = new PropertyHeader(SECTION_HTTP);
public final static PropertyHeader HEADER_SECTION_IP = new PropertyHeader(SECTION_IP);
public final static PropertyHeader HEADER_SECTION_SECURITY = new PropertyHeader(SECTION_SECURITY);
// DEVICE SUBSECTIONS
public static final PropertyHeader HEADER_SUBSECTION_DEVICE = new PropertyHeader("Device", SECTION_DEVICES);
// SERVICE SUBSECTIONS
public static final PropertyHeader HEADER_SUBSECTION_SERVICE = new PropertyHeader("Service", SECTION_SERVICES);
// EVENT SINK SUBSECTIONS
public static final PropertyHeader HEADER_SUBSECTION_EVENT_SINK = new PropertyHeader("EventSink", SECTION_EVENTING);
// GLOBAL SUBSECTIONS
public static final PropertyHeader HEADER_SUBSECTION_DISPATCHING = new PropertyHeader("Dispatching", SECTION_GLOBAL);
public static final PropertyHeader HEADER_SUBSECTION_LOGGING = new PropertyHeader("Logging", SECTION_GLOBAL);
public static final PropertyHeader HEADER_SUBSECTION_FRAMEWORK = new PropertyHeader("Framework", SECTION_GLOBAL);
public static final PropertyHeader HEADER_SUBSECTION_ATTACHMENT = new PropertyHeader("Attachments", SECTION_GLOBAL);
public static final int MAX_SECTION_DEPTH = 8;
static final String DEVICES_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.DevicesPropertiesHandler";
static final String SERVICES_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.ServicesPropertiesHandler";
static final String BINDING_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.BindingProperties";
static final String EVENTING_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.EventingProperties";
static final String ATTACHMENT_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.AttachmentProperties";
static final String GLOBAL_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.GlobalPropertiesHandler";
static final String DISPATCHING_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.DispatchingProperties";
static final String FRAMEWORK_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.FrameworkProperties";
static final String SECURITY_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.SecurityProperties";
// classes for the DPWS communication manager
public static final String DPWS_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.DPWSProperties";
public static final String HTTP_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.HTTPProperties";
public static final String IP_PROPERTIES_HANDLER_CLASS = "org.ws4d.java.configuration.IPProperties";
static final HashMap ownHandlers = new HashMap();
// key = qualified class name as String, value = PropertiesHandler instance
private static HashMap handlersPerClass = null;
// --------- VAR --------------
private HashMap loadedhandlerMap = new HashMap();
/** Map<PropertyHeader, PropertiesHandler */
private HashMap handlerMap = new HashMap();
private static Properties properties = null;
public BufferedInputStream stream = null;
public static synchronized PropertiesHandler forClassName(String className) {
PropertiesHandler handler;
if (handlersPerClass == null) {
handler = createHandlerInstance(className);
handlersPerClass = new HashMap();
handlersPerClass.put(className, handler);
} else {
handler = (PropertiesHandler) handlersPerClass.get(className);
if (handler == null) {
handler = createHandlerInstance(className);
handlersPerClass.put(className, handler);
}
}
return handler;
}
/**
* @param className
* @param handler
* @return
*/
private static PropertiesHandler createHandlerInstance(String className) {
try {
Class handlerClass = Class.forName(className);
return (PropertiesHandler) handlerClass.newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unknown properties handler class: " + className);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Unable to access properties handler class " + className);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Unbale to instantiate properties handler class " + className);
} catch (ClassCastException e) {
throw new IllegalArgumentException("Not a properties handler class: " + className);
}
}
/**
* Constructor. No file will be used to fill properties at init.
*
* @param stream
*/
private Properties() {
/*
* Prepare default property handlers from the framework implementation.
*/
ownHandlers.put(headerString(SECTION_BINDINGS), BINDING_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_GLOBAL), GLOBAL_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_DEVICES), DEVICES_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_SERVICES), SERVICES_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_EVENTING), EVENTING_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_DPWS), DPWS_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_HTTP), HTTP_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_IP), IP_PROPERTIES_HANDLER_CLASS);
ownHandlers.put(headerString(SECTION_SECURITY), SECURITY_PROPERTIES_HANDLER_CLASS);
}
private String headerString(String[] s) {
StringBuffer out = new StringBuffer(s.length * 16);
for (int i = 0; i < s.length; i++) {
out.append(s[i]);
if (i < s.length - 1) {
out.append("/");
}
}
return out.toString();
}
/**
* Get the one and only instance. To use the properties, one of the init
* method must be called.
*
* @return properties
*/
public static synchronized Properties getInstance() {
if (properties == null) {
properties = new Properties();
}
return properties;
}
private void initHandlers() {
/*
* Register core properties
*/
register(HEADER_SECTION_GLOBAL, forClassName(GLOBAL_PROPERTIES_HANDLER_CLASS));
register(HEADER_SUBSECTION_DISPATCHING, forClassName(DISPATCHING_PROPERTIES_HANDLER_CLASS));
register(HEADER_SUBSECTION_FRAMEWORK, forClassName(FRAMEWORK_PROPERTIES_HANDLER_CLASS));
register(HEADER_SECTION_BINDINGS, forClassName(BINDING_PROPERTIES_HANDLER_CLASS));
/*
* Register device and service properties
*/
if (DPWSFramework.hasModule(DPWSFramework.SERVICE_MODULE)) {
register(HEADER_SECTION_DEVICES, forClassName(DEVICES_PROPERTIES_HANDLER_CLASS));
register(HEADER_SECTION_SERVICES, forClassName(SERVICES_PROPERTIES_HANDLER_CLASS));
}
/*
* Register eventing properties
*/
if (DPWSFramework.hasModule(DPWSFramework.EVENTING_MODULE)) {
register(HEADER_SECTION_EVENTING, forClassName(EVENTING_PROPERTIES_HANDLER_CLASS));
}
/*
* Register attachment properties
*/
if (DPWSFramework.hasModule(DPWSFramework.ATTACHMENT_MODULE)) {
register(HEADER_SUBSECTION_ATTACHMENT, forClassName(ATTACHMENT_PROPERTIES_HANDLER_CLASS));
}
}
private void finishHandlers() {
// copy handlers, as finishing may remove handlers from map
HashSet handlers = new HashSet(handlerMap.size());
for (Iterator it = handlerMap.values().iterator(); it.hasNext();) {
handlers.add(it.next());
}
for (Iterator it = handlers.iterator(); it.hasNext();) {
PropertiesHandler handler = (PropertiesHandler) it.next();
handler.finishedSection(0);
}
}
/**
* Initialize Properties. Default property entries will be used.
*/
public void init() {
init((BufferedInputStream) null);
}
/**
* Initialize Properties. Property file will be used.
*
* @param filename The path within the filename must be relative to the
* working directory of the stack.
*/
public void init(String filename) {
try {
if (DPWSFramework.hasModule(DPWSFramework.PLATFORM_CLDC_MODULE)) {
Log.info("Reading via getResourceAsStream() ...");
init(getClass().getResourceAsStream(filename));
} else {
init(DPWSFramework.getLocalFileSystem().readFile(filename));
}
} catch (IOException e) {
Log.warn("Cannot load framework properties from " + filename + ". " + e.getMessage());
}
}
/**
* Initialize Properties. Property stream will be used.
*
* @param stream
*/
public void init(InputStream stream) {
initHandlers();
if (stream == null) {
if (this.stream == null) {
// no stream to fill properties.
finishHandlers();
return;
}
stream = this.stream;
}
PropertiesHandler[] handlers = new PropertiesHandler[MAX_SECTION_DEPTH];
PropertiesInputStream in = new PropertiesInputStream(stream);
PropertiesHandler handler = null;
PropertyHeader lastHeader;
PropertyHeader header;
Property property;
Class handlerClass = null;
header = readHeader(in);
lastHeader = header;
while (header != null) {
if (header.depth() > MAX_SECTION_DEPTH) {
// Section depth to big => ignore section
Log.error("Properties.init: Section depth too big " + header);
header = readHeader(in);
break;
}
// Fill header with super headers
header.initSuperHeaders(lastHeader);
if (handler != null) {
/*
* Finishing can be started with second header. Only headers
* with same or higher depth will be finished.
*/
int headerDepth = header.depth() - 1;
for (int i = lastHeader.depth() - 1; i >= headerDepth; i--) {
// Close finished section in handlers
if (handlers[i] != null) {
handlers[i].finishedSection(i + 1);
handlers[i] = null;
}
}
}
lastHeader = header;
try {
property = readNextProperty(in);
String loadClass = (String) ownHandlers.get(header.toString());
if (property != null && PROP_HANDLER.equals(property.key)) {
// First Property must be handler class name
if (property.value != null) {
handlerClass = Class.forName(property.value);
if (handlerClass == null) {
Log.error("Properties.init: unknown handler class " + handlerClass);
throw new ClassNotFoundException();
}
if (!loadedhandlerMap.containsKey(handlerClass.getName())) {
try {
PropertiesHandler loadedHandler = (PropertiesHandler) handlerClass.newInstance();
loadedhandlerMap.put(handlerClass.getName(), loadedHandler);
handler = loadedHandler;
} catch (Exception e) {
Log.printStackTrace(e);
Log.error("Properties.init: load handler exception " + handlerClass);
throw new ClassNotFoundException();
}
} else {
handler = (PropertiesHandler) loadedhandlerMap.get(handlerClass.getName());
}
handlers[header.depth() - 1] = handler;
}
// Read properties until next header
property = readNextProperty(in);
while (property != null) {
handler.setProperties(header, property);
property = readNextProperty(in);
}
} else if (property != null && !PROP_HANDLER.equals(property.key) && loadClass != null) {
handlerClass = Class.forName(loadClass);
if (handlerClass == null) {
Log.error("Properties.init: unknown handler class " + handlerClass);
throw new ClassNotFoundException();
}
if (!loadedhandlerMap.containsKey(handlerClass.getName())) {
try {
PropertiesHandler loadedHandler = (PropertiesHandler) handlerClass.newInstance();
loadedhandlerMap.put(handlerClass.getName(), loadedHandler);
handler = loadedHandler;
} catch (Exception e) {
Log.printStackTrace(e);
Log.error("Properties.init: load handler exception " + handlerClass);
throw new ClassNotFoundException();
}
} else {
handler = (PropertiesHandler) loadedhandlerMap.get(handlerClass.getName());
}
// Read properties until next header
property = readNextProperty(in);
while (property != null) {
handler.setProperties(header, property);
property = readNextProperty(in);
}
} else {
if (Log.isDebug()) {
Log.debug("Properties.init: " + PROP_HANDLER + " is not first in section " + header + ", checking super handlers", Log.DEBUG_LAYER_FRAMEWORK);
}
PropertiesHandler superHandler = null;
for (int i = header.depth() - 2; i >= 0; i--) {
superHandler = handlers[i];
if (superHandler != null) {
for (int j = i + 1; j <= header.depth() - 1; j++) {
handlers[j] = superHandler;
}
break;
}
}
boolean useSuperHandler = superHandler != null;
PropertiesHandler dedicatedHandler = null;
if (useSuperHandler) {
dedicatedHandler = (PropertiesHandler) handlerMap.get(header);
if (dedicatedHandler != null && dedicatedHandler != handler) {
useSuperHandler = false;
}
}
if (useSuperHandler) {
// Read properties until next header
while (property != null) {
handler.setProperties(header, property);
property = readNextProperty(in);
}
} else {
handler = dedicatedHandler == null ? (PropertiesHandler) handlerMap.get(header) : dedicatedHandler;
if (handler != null) {
handlers[header.depth() - 1] = handler;
while (property != null) {
handler.setProperties(header, property);
property = readNextProperty(in);
}
} else {
while (handler == null && header.depth() > 1) {
header = header.superHeader();
handler = (PropertiesHandler) handlerMap.get(header);
}
if (handler != null) {
for (int i = header.depth() - 1; i < lastHeader.depth(); i++) {
handlers[i] = handler;
}
header = lastHeader;
while (property != null) {
handler.setProperties(header, property);
property = readNextProperty(in);
}
} else if (Log.isDebug()) {
Log.debug("Properties.init: no super handler or registered handler for section " + header, Log.DEBUG_LAYER_FRAMEWORK);
}
}
}
}
} catch (ClassNotFoundException e) {
Log.error("Properties.init: Can't load handler class " + e.getMessage());
// use the other handlers
} catch (SectionHeaderFoundException e) {
// Log.debug("Properties.init: New header found");
}
header = readHeader(in);
}
for (int i = lastHeader.depth() - 1; i >= 0; i--) {
// Close finished section in handlers
if (handlers[i] != null) {
handlers[i].finishedSection(i + 1);
handlers[i] = null;
}
}
finishHandlers();
}
/**
* Get instance of property handler by name of class (Class.getName()).
*
* @param className Name of class
* @return property handler
*/
public PropertiesHandler getLoadedHandler(String className) {
return (PropertiesHandler) loadedhandlerMap.get(className);
}
/**
* Returns a DataStructure that contains instances of all loaded property
* handlers
*
* @return all loaded instances of classes that implement
* {@link #PropertiesHandler}
*/
public DataStructure getAllLoadedHandlers() {
DataStructure values = loadedhandlerMap.values();
return values;
}
// --------------------
/**
* Register handler for specified header.
*
* @param header
* @param handler
*/
void register(PropertyHeader header, PropertiesHandler handler) {
handlerMap.put(header, handler);
loadedhandlerMap.put(handler.getClass().getName(), handler);
}
/**
* Register handler for specified header.
*
* @param header
* @param handler
*/
public void register(PropertyHeader header, String className) {
register(header, forClassName(className));
}
/**
* Unregister handler.
*
* @param header
* @param handler
*/
void unregister(PropertyHeader header) {
PropertiesHandler handler = (PropertiesHandler) handlerMap.remove(header);
if (handler != null) {
loadedhandlerMap.remove(handler.getClass().getName());
}
}
/**
* Read lines until next header.
*
* @param in
* @return
*/
private static Property readNextProperty(PropertiesInputStream in) throws SectionHeaderFoundException {
int c;
String key = null;
String value;
c = in.read();
// XXX Think about CR+LF
while (c != 1) {
if (c == '#') {
// Handle COMMENT
do {
c = in.read();
} while (c != '\n' && c != -1);
if (c == -1) {
// Stop to read from stream
return null;
}
// Start to read next line
c = in.read();
} else if (c == ' ' || c == '\n') {
// Ignore ' ' at line begin
do {
c = in.read();
} while ((c == ' ' || c == '\n') && c != -1);
} else {
// Handle no COMMENT
StringBuffer buf = new StringBuffer(64);
boolean newline = false;
do {
switch (c) {
case '=':
if (key == null) {
key = buf.toString().trim();
buf = new StringBuffer(128);
} else {
// second '=', must be part of value;
buf.append((char) c);
}
break;
case '\r':
break;
case -1:
if (key == null) {
return null;
}
value = buf.toString().trim();
return new Property(key, value);
case '\n':
if (key == null) {
// CASE: nothing of interest read => read next
// line
newline = true;
break;
}
value = buf.toString().trim();
return new Property(key, value);
case ' ':
if (buf.length() < 1) {
// CASE: ' ' at begin of key or value => do
// nothing
break;
} else {
buf.append((char) c);
}
break;
case '[':
if (buf.length() < 1 && key == null) {
// CASE: '[' at the begin of line should be
// include a header
in.buffer = c;
throw new SectionHeaderFoundException(null);
}
// '[' should be appended to buffer if
default:
buf.append((char) c);
}
c = in.read();
} while (!newline);
}
}
return null;
}
private static PropertyHeader readHeader(PropertiesInputStream in) {
int depth = 0;
PropertyHeader header = null;
StringBuffer buf = new StringBuffer(64);
int c = in.read();
while (c != -1) {
if (c == '#') {
// Handle COMMENT
do {
c = in.read();
} while (c != '\n' && c != -1);
if (c == -1) {
return null;
}
} else if (c == ' ' || c == '\n') {
// Ignore line
do {
c = in.read();
} while ((c == ' ' || c == '\n') && c != -1);
if (c == -1) {
// Stop to read from stream
return null;
}
}
if (c == '[') {
do {
switch (c) {
case '[':
depth++;
break;
case ']':
if (header == null) {
header = new PropertyHeader(buf.toString().trim(), depth);
}
depth--;
break;
case -1:
return header;
case '\r':
// ignore
break;
case '\n':
if (header != null) {
// read until header found or eof
return header;
} else {
buf = new StringBuffer(64);
}
break;
default:
if (depth > 0) {
buf.append((char) c);
}
}
if (c != -1) {
c = in.read();
}
} while (c != -1);
}
c = in.read();
}
return header;
}
// public String toString(){
// StringBuffer out = new StringBuffer(50*loadedhandlerMap.size());
//
// for( Iterator it = loadedhandlerMap.entrySet().iterator(); it.hasNext();
// ){
// Map.Entry entry = (Map.Entry) it.next();
// out.append(entry.getKey() + "=" + entry.getValue() + " | ");
// }
//
// return out.toString();
// }
// -------------------------- INNER CLASS --------------------
private class PropertiesInputStream {
private InputStream in;
private int buffer = -1;
protected PropertiesInputStream(InputStream in) {
this.in = in;
}
public int read() {
if (buffer != -1) {
int ret = buffer;
buffer = -1;
return ret;
}
try {
int c = in.read();
return c;
} catch (IOException e) {
return -1;
}
}
}
}