You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2009/06/27 17:53:26 UTC
svn commit: r788992 [3/25] - in /incubator/ace/trunk: gateway/ gateway/src/
gateway/src/net/ gateway/src/net/luminis/ gateway/src/net/luminis/liq/
gateway/src/net/luminis/liq/bootstrap/
gateway/src/net/luminis/liq/bootstrap/multigateway/ gateway/src/ne...
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/identification/ifconfig/IfconfigIdentification.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/identification/ifconfig/IfconfigIdentification.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/identification/ifconfig/IfconfigIdentification.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/identification/ifconfig/IfconfigIdentification.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,114 @@
+package net.luminis.liq.identification.ifconfig;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import net.luminis.liq.identification.Identification;
+
+import org.osgi.service.log.LogService;
+
+/**
+ * Implementation of the <code>Identification</code> interface which will determine a mac-address based ID which is determined
+ * by running the ifconfig command. The first adapter that has been assigned an ip address is used.
+ *
+ * The identification has been tested on <code>ifconfig 1.42 (2001-04-13)</code> which comes with Debian Linux. Similar
+ * versions of ifconfig are likely to work.
+ */
+public class IfconfigIdentification implements Identification {
+
+ private static final String IFCONFIG_COMMAND = "ifconfig";
+ private static final String MAC_IDENTIFIER = "HWaddr ";
+ private static final String IP_IDENTIFIER = "inet addr";
+
+ private volatile LogService m_log; // injected by dependency manager
+
+ private String m_gatewayID = null;
+
+ public synchronized String getID() {
+ if (m_gatewayID == null) {
+ BufferedReader reader = null;
+ try {
+ Process process = Runtime.getRuntime().exec(IFCONFIG_COMMAND);
+ InputStream inputStream = process.getInputStream();
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+ m_gatewayID = parseIfconfigOutput(reader).toLowerCase();
+ }
+ catch (IOException ioe) {
+ m_log.log(LogService.LOG_WARNING, "Unable to determine ifconfig based mac-address gateway identification.", ioe);
+ return null;
+ }
+ finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ }
+ catch (IOException e) {
+ // not much we can do
+ }
+ }
+ }
+ }
+ return m_gatewayID;
+ }
+
+ /**
+ * Parses the mac address of the first active adapter from the output of the ifconfig command.
+ *
+ * @param ifconfigOutput Reader pointing to the output of the ifconfig command.
+ * @return String containing the mac address or <code>null</code> if no valid mac address could be determined.
+ * @throws IOException If the specified reader could not be read correctly.
+ */
+ protected String parseIfconfigOutput(BufferedReader ifconfigOutput) throws IOException {
+ // Sample output (the part that matters):
+ // eth0 Link encap:Ethernet HWaddr 00:00:21:CF:76:47
+ // inet addr:192.168.1.65 Bcast:192.168.1.255 Mask:255.255.255.0
+ String previousLine = "";
+ String line;
+ while ((line = ifconfigOutput.readLine()) != null) {
+ if (line.indexOf(IP_IDENTIFIER) != -1) {
+ if (previousLine.indexOf(MAC_IDENTIFIER) != -1) {
+ String macAddress = previousLine.substring(previousLine.lastIndexOf(MAC_IDENTIFIER) + MAC_IDENTIFIER.length(), previousLine.length());
+ macAddress = macAddress.trim();
+ if (isValidMac(macAddress)) {
+ return macAddress;
+ }
+ else {
+ return null;
+ }
+ }
+ }
+ previousLine = line;
+ }
+ return null;
+ }
+
+ /**
+ * Verifies whether a string contains a valid mac addres, a valid mac address consists of
+ * 6 pairs of [A-F,a-f,0-9] separated by ':', e.g. <code>0A:F6:33:19:DE:2A</code>.
+ *
+ * @param mac String containing the possible mac address
+ * @return true If the specified string contains a valid mac address, false otherwise.
+ */
+ protected boolean isValidMac(String mac) {
+ if (mac.length() != 17) {
+ return false;
+ }
+ char[] chars = mac.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (i % 3 == 2) {
+ if (':' != c) {
+ return false;
+ }
+ }
+ else if (!(('0' <= c) && (c <= '9')) &&
+ !(('a' <= c) && (c <= 'f')) &&
+ !(('A' <= c) && (c <= 'F'))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,27 @@
+package net.luminis.liq.identification.property;
+
+import net.luminis.liq.identification.Identification;
+import net.luminis.liq.identification.property.constants.IdentificationConstants;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public synchronized void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(new String[] {Identification.class.getName()}, null)
+ .setImplementation(PropertyBasedIdentification.class)
+ .add(createConfigurationDependency()
+ .setPid(IdentificationConstants.IDENTIFICATION_PID))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false))
+ );
+ }
+
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+}
\ No newline at end of file
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/PropertyBasedIdentification.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/PropertyBasedIdentification.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/PropertyBasedIdentification.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/PropertyBasedIdentification.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,41 @@
+package net.luminis.liq.identification.property;
+
+import java.util.Dictionary;
+
+import net.luminis.liq.identification.Identification;
+import net.luminis.liq.identification.property.constants.IdentificationConstants;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * Simple implementation of the <code>Identification</code> interface. Because
+ * a gateway identification should not change during it's lifetime the user of this
+ * implementation should set the ID only once.
+ */
+public class PropertyBasedIdentification implements ManagedService, Identification {
+ private volatile LogService m_log;
+ private String m_gatewayID;
+
+ public synchronized String getID() {
+ return m_gatewayID;
+ }
+
+ public void updated(Dictionary dictionary) throws ConfigurationException {
+ if (dictionary != null) {
+ String id = (String) dictionary.get(IdentificationConstants.IDENTIFICATION_GATEWAYID_KEY);
+ if ((id == null) || (id.length() == 0)) {
+ // illegal config
+ throw new ConfigurationException(IdentificationConstants.IDENTIFICATION_GATEWAYID_KEY, "Illegal gateway ID supplied");
+ }
+ if (m_gatewayID != null) {
+ m_log.log(LogService.LOG_WARNING, "Gateway ID is being changed from " + m_gatewayID + " to " + id);
+ }
+ // legal config, set configuration
+ synchronized (this) {
+ m_gatewayID = id;
+ }
+ }
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/constants/IdentificationConstants.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/constants/IdentificationConstants.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/constants/IdentificationConstants.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/identification/property/constants/IdentificationConstants.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,6 @@
+package net.luminis.liq.identification.property.constants;
+
+public interface IdentificationConstants {
+ public static final String IDENTIFICATION_PID = "net.luminis.liq.identification.property";
+ public static final String IDENTIFICATION_GATEWAYID_KEY = "gatewayID";
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,61 @@
+package net.luminis.liq.log.listener;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import net.luminis.liq.log.Log;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Activator for the bundle that listens to all life-cycle events, and logs them to the
+ * log service. The BundleEvents, FrameworkEvents and the events related to
+ * Deployment Packages are relevant for the audit log.
+ * <p>
+ * Furthermore this bundle takes care of the situation when the real log is not
+ * yet available within the framework, by using a cache that temporarily stores the
+ * log entries, and flushing those when the real log service comes up.
+ * BundleEvents and Framework events are always available, but events related to
+ * Deployment Packages will only be available when the EventAdmin is present.
+ */
+public class Activator implements BundleActivator {
+
+ private static final String LOG_NAME = "auditlog";
+
+ private final static String [] topics = new String[] { "org/osgi/service/deployment/*", "net/luminis/liq/deployment/*" };
+ private ServiceTracker m_logTracker;
+ private ListenerImpl m_listener;
+
+ public synchronized void start(BundleContext context) throws Exception {
+ LogProxy logProxy = new LogProxy();
+ m_listener = new ListenerImpl(context, logProxy);
+ m_listener.startInternal();
+ // listen for bundle and framework events
+ context.addBundleListener(m_listener);
+ context.addFrameworkListener(m_listener);
+
+ // listen for deployment events
+ Dictionary dict = new Hashtable();
+ dict.put(EventConstants.EVENT_TOPIC, topics);
+ context.registerService(EventHandler.class.getName(), m_listener, dict);
+
+ // keep track of when the real log is available
+ ServiceTrackerCustomizer logTrackerCust = new LogTracker(context, logProxy);
+ m_logTracker = new ServiceTracker(context, context.createFilter("(&(" + Constants.OBJECTCLASS + "=" + Log.class.getName() + ")(name=" + LOG_NAME + "))"), logTrackerCust);
+ m_logTracker.open();
+ }
+
+ public synchronized void stop(BundleContext context) throws Exception {
+ // cleanup
+ m_logTracker.close();
+ context.removeFrameworkListener(m_listener);
+ context.removeBundleListener(m_listener);
+ m_listener.stopInternal();
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/ListenerImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/ListenerImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/ListenerImpl.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/ListenerImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,306 @@
+package net.luminis.liq.log.listener;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import net.luminis.liq.log.AuditEvent;
+import net.luminis.liq.log.Log;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.deploymentadmin.DeploymentAdmin;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+/**
+ * This class implements all listening actions to be done. It listens for BundleEvents, FrameworkEvents and events related to
+ * Deployment Packages. Whenever an event is received, it is transformed as defined in AuditEvent, and consequently logged in
+ * the AuditLog.
+ */
+public class ListenerImpl implements BundleListener, FrameworkListener, EventHandler {
+
+ private final String TOPIC_INSTALL = "org/osgi/service/deployment/INSTALL";
+ private final String TOPIC_UNINSTALL = "org/osgi/service/deployment/UNINSTALL";
+ private final String TOPIC_COMPLETE = "org/osgi/service/deployment/COMPLETE";
+
+ private final String TOPIC_DEPLOYMENTPACKAGE_INSTALL = "net/luminis/liq/deployment/INSTALL";
+
+ volatile BundleContext m_context;
+ volatile Log m_auditLog;
+
+ private final List m_queue = new ArrayList();
+
+ public ListenerImpl(BundleContext context, Log log) {
+ m_context = context;
+ m_auditLog = log;
+ }
+
+ /**
+ * Whenever a BundleEvent is received, an event is logged on the AuditLog. The event details logged are first transformed as
+ * defined in AuditEvent before actually being logged.
+ */
+ public void bundleChanged(final BundleEvent event) {
+ synchronized (m_queue) {
+ m_queue.add(new Runnable() {
+ public void run() {
+ int eventType = AuditEvent.BUNDLE_BASE;
+ Properties props = new Properties();
+ Bundle bundle = event.getBundle();
+ props.put(AuditEvent.KEY_ID, Long.toString(bundle.getBundleId()));
+
+ switch (event.getType()) {
+ case BundleEvent.INSTALLED:
+ eventType = AuditEvent.BUNDLE_INSTALLED;
+ if (bundle.getSymbolicName() != null) {
+ props.put(AuditEvent.KEY_NAME, bundle.getSymbolicName());
+ }
+ String version = (String) bundle.getHeaders().get(Constants.BUNDLE_VERSION);
+ if (version != null) {
+ props.put(AuditEvent.KEY_VERSION, version);
+ }
+ props.put(AuditEvent.KEY_LOCATION, bundle.getLocation());
+ break;
+ case BundleEvent.RESOLVED:
+ eventType = AuditEvent.BUNDLE_RESOLVED;
+ break;
+ case BundleEvent.STARTED:
+ eventType = AuditEvent.BUNDLE_STARTED;
+ break;
+ case BundleEvent.STOPPED:
+ eventType = AuditEvent.BUNDLE_STOPPED;
+ break;
+ case BundleEvent.UNRESOLVED:
+ eventType = AuditEvent.BUNDLE_UNRESOLVED;
+ break;
+ case BundleEvent.UPDATED:
+ eventType = AuditEvent.BUNDLE_UPDATED;
+ version = (String) bundle.getHeaders().get(Constants.BUNDLE_VERSION);
+ if (version != null) {
+ props.put(AuditEvent.KEY_VERSION, version);
+ }
+ props.put(AuditEvent.KEY_LOCATION, bundle.getLocation());
+ break;
+ case BundleEvent.UNINSTALLED:
+ eventType = AuditEvent.BUNDLE_UNINSTALLED;
+ break;
+ case BundleEvent.STARTING:
+ eventType = AuditEvent.BUNDLE_STARTING;
+ break;
+ case BundleEvent.STOPPING:
+ eventType = AuditEvent.BUNDLE_STOPPING;
+ break;
+ }
+ m_auditLog.log(eventType, props);
+ }
+ });
+ m_queue.notifyAll();
+ }
+ }
+
+ /**
+ * Whenever a FrameworkEvent is received, an event is logged on the AuditLog. The event details logged are first transformed
+ * as defined in AuditEvent before actually being logged.
+ */
+ public void frameworkEvent(final FrameworkEvent event) {
+ synchronized (m_queue) {
+ m_queue.add(new Runnable() {
+ public void run() {
+ int eventType = AuditEvent.FRAMEWORK_BASE;
+ Properties props = new Properties();
+ Bundle bundle = event.getBundle();
+
+ if (bundle != null) {
+ props.put(AuditEvent.KEY_ID, Long.toString(bundle.getBundleId()));
+ }
+
+ String msg = null;
+ String type = null;
+ Throwable exception = event.getThrowable();
+ if (exception != null) {
+ msg = exception.getMessage();
+ type = exception.getClass().getName();
+ }
+
+ switch (event.getType()) {
+ case FrameworkEvent.INFO:
+ eventType = AuditEvent.FRAMEWORK_INFO;
+ if (msg != null) {
+ props.put(AuditEvent.KEY_MSG, msg);
+ }
+ if (type != null) {
+ props.put(AuditEvent.KEY_TYPE, type);
+ }
+ break;
+ case FrameworkEvent.WARNING:
+ eventType = AuditEvent.FRAMEWORK_WARNING;
+ if (msg != null) {
+ props.put(AuditEvent.KEY_MSG, msg);
+ }
+ if (type != null) {
+ props.put(AuditEvent.KEY_TYPE, type);
+ }
+ break;
+ case FrameworkEvent.ERROR:
+ eventType = AuditEvent.FRAMEWORK_ERROR;
+ if (msg != null) {
+ props.put(AuditEvent.KEY_MSG, msg);
+ }
+ if (type != null) {
+ props.put(AuditEvent.KEY_TYPE, type);
+ }
+ break;
+ case FrameworkEvent.PACKAGES_REFRESHED:
+ eventType = AuditEvent.FRAMEWORK_REFRESH;
+ break;
+ case FrameworkEvent.STARTED:
+ eventType = AuditEvent.FRAMEWORK_STARTED;
+ break;
+ case FrameworkEvent.STARTLEVEL_CHANGED:
+ eventType = AuditEvent.FRAMEWORK_STARTLEVEL;
+ break;
+ }
+ m_auditLog.log(eventType, props);
+ }
+ });
+ m_queue.notifyAll();
+ }
+ }
+
+ /**
+ * Only expects events related to Deployment Packages. Whenever an event is received, the event is logged on the AuditLog.
+ * The event details logged are first transformed as defined in AuditEvent before actually being logged.
+ */
+ public void handleEvent(final Event event) {
+ synchronized (m_queue) {
+ m_queue.add(new Runnable() {
+ public void run() {
+ int eventType = AuditEvent.DEPLOYMENTADMIN_BASE;
+ Dictionary props = new Properties();
+
+ String topic = event.getTopic();
+
+ if (topic.equals(TOPIC_DEPLOYMENTPACKAGE_INSTALL)) {
+ String url = (String) event.getProperty("deploymentpackage.url");
+ String version = (String) event.getProperty("deploymentpackage.version");
+
+ eventType = AuditEvent.DEPLOYMENTCONTROL_INSTALL;
+ props.put(AuditEvent.KEY_VERSION, version);
+ props.put(AuditEvent.KEY_NAME, url);
+ }
+ else if (topic.equals(TOPIC_INSTALL)) {
+ String deplPackName = (String) event.getProperty("deploymentpackage.name");
+ eventType = AuditEvent.DEPLOYMENTADMIN_INSTALL;
+ props.put(AuditEvent.KEY_NAME, deplPackName);
+ }
+
+ else if (topic.equals(TOPIC_UNINSTALL)) {
+ String deplPackName = (String) event.getProperty("deploymentpackage.name");
+ eventType = AuditEvent.DEPLOYMENTADMIN_UNINSTALL;
+ props.put(AuditEvent.KEY_NAME, deplPackName);
+ }
+ else if (topic.equals(TOPIC_COMPLETE)) {
+ String deplPackName = (String) event.getProperty("deploymentpackage.name");
+
+ // to retrieve the version, DeploymentAdmin has to be used
+ ServiceReference ref = m_context.getServiceReference(DeploymentAdmin.class.getName());
+ if (ref != null) {
+ DeploymentAdmin deplAdmin = (DeploymentAdmin) m_context.getService(ref);
+ if (deplAdmin != null) {
+ DeploymentPackage dp = deplAdmin.getDeploymentPackage(deplPackName);
+ if (dp != null) {
+ Version version = dp.getVersion();
+ if (version != null) {
+ props.put(AuditEvent.KEY_VERSION, version.toString());
+ }
+ }
+ // after use, release the service as is it not needed anymore
+ m_context.ungetService(ref);
+ }
+ }
+
+ eventType = AuditEvent.DEPLOYMENTADMIN_COMPLETE;
+ props.put(AuditEvent.KEY_NAME, deplPackName);
+ Boolean success = (Boolean) event.getProperty("successful");
+ props.put(AuditEvent.KEY_SUCCESS, success.toString());
+ }
+
+ m_auditLog.log(eventType, props);
+ }
+ });
+ m_queue.notifyAll();
+ }
+ }
+
+ synchronized void startInternal() {
+ initInternal();
+ if (!m_thread.isAlive()) {
+ m_thread.start();
+ }
+ }
+
+ synchronized void stopInternal() {
+ if (m_thread != null) {
+ m_thread.interrupt();
+ try {
+ m_thread.join();
+ }
+ catch (InterruptedException e) {
+ // Not much we can do
+ }
+ m_thread = null;
+ }
+ }
+
+ private Thread m_thread = null;
+
+ synchronized void initInternal() {
+ if ((m_thread == null) || (!m_thread.isAlive())) {
+ m_thread = new Thread("AuditLogListenerThread") {
+ public void run() {
+
+ Runnable next = null;
+ do {
+ synchronized (m_queue) {
+ while (m_queue.isEmpty() && !isInterrupted()) {
+ try {
+ m_queue.wait();
+ }
+ catch (InterruptedException ex) {
+ interrupt();
+ }
+ }
+ if (!m_queue.isEmpty()) {
+ next = (Runnable) m_queue.remove(0);
+ }
+ else {
+ next = null;
+ }
+ }
+ if (next != null) {
+ try {
+ next.run();
+ }
+ catch (Exception ex) {
+ // Not much we can do
+ // FIXME:
+ ex.printStackTrace(System.err);
+ }
+ }
+ }
+ while (next != null);
+ }
+ };
+ m_thread.setDaemon(true);
+ }
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogCache.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogCache.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogCache.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogCache.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,60 @@
+package net.luminis.liq.log.listener;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Iterator;
+import java.util.List;
+
+import net.luminis.liq.log.Log;
+
+/**
+ * This cache is used whenever the real log service is not available. When
+ * the real log becomes available, all cached log entries should be flushed
+ * to the real log service and leaving the cache empty afterwards.
+ */
+public class LogCache implements Log {
+
+ private final List m_logEntries = new ArrayList();
+
+ /**
+ * Log the entry in the cache for flushing to the real log service later on.
+ */
+ public synchronized void log(int type, Dictionary properties) {
+ m_logEntries.add(new LogEntry(type, properties));
+ }
+
+ /**
+ * Flushes all cached log entries to the specified Log and leaves the cache empty
+ * after flushing. Will do nothing when a null is passed as parameter.
+ * @param log The log service to flush the cached log entries to
+ */
+ public synchronized void flushTo(Log log) {
+ if (log != null) {
+ for (Iterator iterator = m_logEntries.iterator(); iterator.hasNext();) {
+ LogEntry entry = (LogEntry) iterator.next();
+ log.log(entry.getType(), entry.getProperties());
+ }
+ m_logEntries.clear();
+ }
+ else {
+ // do nothing, as you want to keep using the cache
+ }
+ }
+
+ private class LogEntry {
+ private int m_type;
+ private Dictionary m_properties;
+ public LogEntry(int type, Dictionary properties) {
+ m_type = type;
+ m_properties = properties;
+ }
+
+ public int getType() {
+ return m_type;
+ }
+
+ public Dictionary getProperties() {
+ return m_properties;
+ }
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogProxy.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogProxy.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogProxy.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogProxy.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,44 @@
+package net.luminis.liq.log.listener;
+
+import java.util.Dictionary;
+
+import net.luminis.liq.log.Log;
+
+/**
+ * Class responsible for being the object to talk to when trying to log events. This class
+ * will decide whether to log it to cache, or to the actual log.
+ */
+public class LogProxy implements Log {
+
+ private Log m_log;
+ private LogCache m_logCache;
+
+ public LogProxy() {
+ m_logCache = new LogCache();
+ }
+
+ /**
+ * Logs the log entry either to the real log service or to the cache, depending on
+ * whether the real service is available.
+ */
+ public synchronized void log(int type, Dictionary properties) {
+ if (m_log != null) {
+ m_log.log(type, properties);
+ }
+ else {
+ m_logCache.log(type, properties);
+ }
+ }
+
+ /**
+ * Sets the log, and flushes the cached log entries when a log service
+ * is passed into this method. When null is passed as parameter, the log service
+ * is not available anymore, and the cache should be used instead.
+ * @param log the log service to use, when null the cache will be used instead
+ */
+ public synchronized void setLog(Log log) {
+ m_log = log;
+ // flush content of the cache to the real log
+ m_logCache.flushTo(m_log);
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogTracker.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogTracker.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogTracker.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/log/listener/LogTracker.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,53 @@
+package net.luminis.liq.log.listener;
+
+import net.luminis.liq.log.Log;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+/**
+ * Keep track of whether the log is available. If available, use the real log,
+ * else use the cache version. When the real log becomes available, flush all events
+ * from the cache to the real log.
+ *
+ */
+public class LogTracker implements ServiceTrackerCustomizer {
+
+ private BundleContext m_context;
+ private LogProxy m_proxy;
+
+ public LogTracker (BundleContext context, LogProxy proxy) {
+ m_context = context;
+ m_proxy = proxy;
+ }
+
+ /**
+ * Called when the log service has been added. As result, the real
+ * log service will be used instead of the cache.
+ */
+ public Object addingService(ServiceReference ref) {
+ // get the service based upon the reference, and return it
+ // make sure the real Log will be used, and all events in the
+ // cache are being flushed to the real Log.
+ Log externalLog = (Log) m_context.getService(ref);
+ m_proxy.setLog(externalLog);
+ return externalLog;
+ }
+
+ /**
+ * Called when the Log service is not available anymore. As result,
+ * the cache version of the Log will be used until the Log
+ * service is added again.
+ */
+ public void removedService(ServiceReference ref, Object log) {
+ // make sure the LogCache is used instead of the real Log
+ m_proxy.setLog(null);
+ // unget the service again
+ m_context.ungetService(ref);
+ }
+
+ public void modifiedService(ServiceReference ref, Object log) {
+ // do nothing
+ }
+}
+
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/ma/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/ma/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/ma/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/ma/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,51 @@
+package net.luminis.liq.ma;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+// TODO work in progress
+public class Activator implements BundleActivator {
+ private net.luminis.liq.identification.property.Activator m_identification;
+ private net.luminis.liq.discovery.property.Activator m_discovery;
+ private net.luminis.liq.deployment.deploymentadmin.Activator m_deployment;
+ private net.luminis.liq.deployment.task.Activator m_task;
+ private net.luminis.liq.scheduler.Activator m_scheduler;
+ private net.luminis.liq.configurator.Activator m_configurator;
+ private net.luminis.liq.gateway.log.store.impl.Activator m_store;
+ private net.luminis.liq.gateway.log.Activator m_log;
+ private net.luminis.liq.log.listener.Activator m_logListener;
+
+ public void start(BundleContext context) throws Exception {
+ m_identification = new net.luminis.liq.identification.property.Activator();
+ m_discovery = new net.luminis.liq.discovery.property.Activator();
+ m_deployment = new net.luminis.liq.deployment.deploymentadmin.Activator();
+ m_task = new net.luminis.liq.deployment.task.Activator();
+ m_scheduler = new net.luminis.liq.scheduler.Activator();
+ m_configurator = new net.luminis.liq.configurator.Activator();
+ m_store = new net.luminis.liq.gateway.log.store.impl.Activator();
+ m_log = new net.luminis.liq.gateway.log.Activator();
+ m_logListener = new net.luminis.liq.log.listener.Activator();
+
+ m_identification.start(context);
+ m_discovery.start(context);
+ m_deployment.start(context);
+ m_task.start(context);
+ m_scheduler.start(context);
+ m_configurator.start(context);
+ m_store.start(context);
+ m_log.start(context);
+ m_logListener.start(context);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ m_identification.stop(context);
+ m_discovery.stop(context);
+ m_deployment.stop(context);
+ m_task.stop(context);
+ m_scheduler.stop(context);
+ m_configurator.stop(context);
+ m_store.stop(context);
+ m_log.stop(context);
+ m_logListener.stop(context);
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,119 @@
+package net.luminis.liq.ma.webstart;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.Properties;
+
+import net.luminis.liq.discovery.Discovery;
+import net.luminis.liq.gateway.log.LogImpl;
+import net.luminis.liq.gateway.log.store.LogStore;
+import net.luminis.liq.gateway.log.store.impl.LogStoreImpl;
+import net.luminis.liq.gateway.log.task.LogSyncTask;
+import net.luminis.liq.identification.Identification;
+import net.luminis.liq.log.Log;
+import net.luminis.liq.scheduler.constants.SchedulerConstants;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.log.LogService;
+
+/**
+ * Bundle activator for a completely self-contained management agent that is designed
+ * to be used with Java Web Start. It does not use configuration admin and our configurator
+ * since we don't have a directory with configuration data when we webstart an application.
+ * Instead, we pass on configuration through system properties (that can be put in the
+ * JNLP launcher file) and use separate discovery and identification services that pick up
+ * on those.
+ */
+public class Activator extends DependencyActivatorBase {
+ private BundleActivator m_deployment;
+ private BundleActivator m_task;
+ private BundleActivator m_scheduler;
+ private BundleActivator m_logListener;
+ private BundleActivator m_deploymentAdmin;
+ private BundleActivator m_eventAdmin;
+
+ public void start(BundleContext context) throws Exception {
+ // start the log listener first so it can listen to as many events as possible
+ m_logListener = new net.luminis.liq.log.listener.Activator();
+ m_logListener.start(context);
+
+ // invoke standard superclass behaviour, which will eventually call our init() method
+ super.start(context);
+
+ m_deployment = new net.luminis.liq.deployment.deploymentadmin.Activator();
+ m_deployment.start(context);
+ m_task = new net.luminis.liq.deployment.task.Activator();
+ m_task.start(context);
+ m_eventAdmin = (BundleActivator) Class.forName("org.apache.felix.eventadmin.impl.Activator").newInstance();
+ m_eventAdmin.start(context);
+ m_deploymentAdmin = (BundleActivator) Class.forName("org.apache.felix.deploymentadmin.Activator").newInstance();
+ m_deploymentAdmin.start(context);
+ m_scheduler = new net.luminis.liq.scheduler.Activator();
+ m_scheduler.start(context);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ m_deployment.stop(context);
+ m_task.stop(context);
+ m_scheduler.stop(context);
+ m_deploymentAdmin.stop(context);
+ m_eventAdmin.stop(context);
+
+ // invoke standard superclass behaviour, which will eventually call our destroy() method
+ super.stop(context);
+
+ // stop the log listener as late as possible
+ m_logListener.stop(context);
+ }
+
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createService()
+ .setInterface(Identification.class.getName(), null)
+ .setImplementation(SystemPropertyIdentification.class)
+ );
+
+ manager.add(createService()
+ .setInterface(Discovery.class.getName(), null)
+ .setImplementation(SystemPropertyDiscovery.class)
+ );
+
+ // we create an audit log store ourselves, since we don't need the flexibility of managed
+ // service factories configured by config admin here
+ Properties logProps = new Properties();
+ logProps.put("name", "auditlog");
+ manager.add(createService()
+ .setInterface(LogStore.class.getName(), logProps)
+ .setImplementation(new LogStoreImpl(new File(context.getDataFile(""), "audit")))
+ .add(createServiceDependency().setService(Identification.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ );
+
+ // same for the log service and sync task
+ manager.add(createService()
+ .setInterface(Log.class.getName(), logProps)
+ .setImplementation(LogImpl.class)
+ .add(createServiceDependency().setService(LogStore.class, "(&(" + Constants.OBJECTCLASS + "=" + LogStore.class.getName() + ")(name=auditlog))").setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ );
+ Dictionary properties = new Properties();
+ properties.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, "Task that synchronizes audit log store on the gateway and server");
+ properties.put(SchedulerConstants.SCHEDULER_NAME_KEY, "auditlog");
+ properties.put(SchedulerConstants.SCHEDULER_RECIPE, "2000");
+
+ manager.add(createService()
+ .setInterface(Runnable.class.getName(), properties)
+ .setImplementation(new LogSyncTask("auditlog"))
+ .add(createServiceDependency().setService(LogStore.class, "(&(" + Constants.OBJECTCLASS + "=" + LogStore.class.getName() + ")(name=auditlog))").setRequired(true))
+ .add(createServiceDependency().setService(Discovery.class).setRequired(true))
+ .add(createServiceDependency().setService(Identification.class).setRequired(true))
+ .add(createServiceDependency().setService(LogService.class).setRequired(false))
+ );
+ }
+
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Override.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Override.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Override.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/Override.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,34 @@
+package net.luminis.liq.ma.webstart;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+public class Override {
+ private static Properties props = null;
+
+ public static String getProperty(String key) throws NoSuchElementException {
+ if (props == null) {
+ File appHome = new File(System.getProperty("application.home", System.getProperty("user.home")));
+ File configFile = new File(appHome, "config.properties");
+ if (configFile.isFile()) {
+ props = new Properties();
+ try {
+ props.load(new FileInputStream(configFile));
+ }
+ catch (IOException e) {
+ props = null;
+ }
+ }
+ }
+ if (props != null) {
+ String value = props.getProperty(key);
+ if (value != null) {
+ return value;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyDiscovery.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyDiscovery.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyDiscovery.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyDiscovery.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,28 @@
+package net.luminis.liq.ma.webstart;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.NoSuchElementException;
+
+import net.luminis.liq.discovery.Discovery;
+
+public class SystemPropertyDiscovery implements Discovery {
+ private URL m_discovery;
+
+ public SystemPropertyDiscovery() throws MalformedURLException {
+ m_discovery = new URL(System.getProperty("gateway.discovery", "http://localhost:8080/"));
+
+ // for debugging purposes, we have an override system
+ try {
+ m_discovery = new URL(Override.getProperty("gateway.discovery"));
+ }
+ catch (NoSuchElementException e) {
+ // ignore the exception
+ }
+ }
+
+ public URL discover() {
+ return m_discovery;
+ }
+
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyIdentification.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyIdentification.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyIdentification.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/ma/webstart/SystemPropertyIdentification.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,25 @@
+package net.luminis.liq.ma.webstart;
+
+import java.util.NoSuchElementException;
+
+import net.luminis.liq.identification.Identification;
+
+public class SystemPropertyIdentification implements Identification {
+ private String m_identification;
+
+ public SystemPropertyIdentification() {
+ String defaultID = "Unknown-" + System.currentTimeMillis();
+ m_identification = System.getProperty("gateway.identification", defaultID);
+ // for debugging purposes, we have an override system
+ try {
+ m_identification = Override.getProperty("gateway.identification");
+ }
+ catch (NoSuchElementException e) {
+ // ignore the exception
+ }
+ }
+
+ public String getID() {
+ return m_identification;
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,66 @@
+package net.luminis.liq.scheduler;
+
+import java.util.Properties;
+
+import net.luminis.liq.scheduler.constants.SchedulerConstants;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * Activator for the scheduler service. This activator will monitor <code>Runnable</code>s coming available,
+ * and if they are intended to be scheduled, gets the necessary information and passes that to
+ * the scheduler.
+ */
+public class Activator extends DependencyActivatorBase {
+
+ private Scheduler m_scheduler;
+
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ m_scheduler = new Scheduler();
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, SchedulerConstants.SCHEDULER_PID);
+ manager.add(createService()
+ .setInterface(ManagedService.class.getName(), props)
+ .setImplementation(m_scheduler)
+ .add(createServiceDependency()
+ .setService(LogService.class).setRequired(false))
+ .add(createServiceDependency()
+ .setService(Runnable.class).setRequired(false)
+ .setAutoConfig(false)
+ .setCallbacks(this, "addRunnable", "addRunnable", "removeRunnable")));
+ }
+
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+
+ /**
+ * Handler for both adding and updating runnable service registrations.
+ * @throws ConfigurationException Is thrown when the <code>SCHEDULER_RECIPE</code> contained in <code>ref</code>'s
+ * service dictionary cannot be parsed by the scheduler.
+ */
+ public void addRunnable(ServiceReference ref, Runnable task) throws ConfigurationException {
+ String name = (String) ref.getProperty(SchedulerConstants.SCHEDULER_NAME_KEY);
+ if (name != null) {
+ String description = (String) ref.getProperty(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY);
+ Object recipe = ref.getProperty(SchedulerConstants.SCHEDULER_RECIPE);
+ boolean recipeOverride = Boolean.valueOf((String) ref.getProperty(SchedulerConstants.SCHEDULER_RECIPE_OVERRIDE)).booleanValue();
+ m_scheduler.addRunnable(name, task, description, recipe, recipeOverride);
+ }
+ }
+
+ public synchronized void removeRunnable(ServiceReference ref, Runnable task) {
+ String name = (String) ref.getProperty(SchedulerConstants.SCHEDULER_NAME_KEY);
+ if (name != null) {
+ m_scheduler.removeRunnable(name);
+ }
+ }
+
+}
\ No newline at end of file
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Executer.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Executer.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Executer.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Executer.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,84 @@
+package net.luminis.liq.scheduler;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class wraps a <code>Runnable</code> in a <code>TimerTask</code> that allows
+ * it to be periodically run and to be stopped as soon as possible.
+ */
+public class Executer extends TimerTask {
+ private final Timer m_timer = new Timer();
+ private final Runnable m_task;
+ private boolean m_stop = false;
+ private boolean m_stopped = true;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param task The task that should be periodically run.
+ */
+ public Executer(Runnable task) {
+ m_task = task;
+ }
+
+ /**
+ * Start executing the task repeatedly with an interval as specified.
+ *
+ * @param interval The interval between executions of the task, in milliseconds.
+ */
+ void start(long interval) {
+ if (interval > 0) {
+ m_timer.schedule(this, 0, interval);
+ }
+ }
+
+ /**
+ * Stop periodically executing this task. If the task is currently executing it
+ * will never be run again after the current execution, otherwise it will simply
+ * never run (again).
+ */
+ void stop() {
+ synchronized (m_timer) {
+ if (!m_stop) {
+ m_stop = true;
+ cancel();
+ m_timer.cancel();
+ }
+
+ boolean interrupted = false;
+ while (!m_stopped) {
+ try {
+ m_timer.wait();
+ }
+ catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ public void run() {
+ synchronized (m_timer) {
+ m_stopped = false;
+ if (m_stop) {
+ m_stopped = true;
+ m_timer.notifyAll();
+ return;
+ }
+ }
+ try {
+ m_task.run();
+ }
+ catch (Exception e) {
+ // TODO we should log this somehow
+ }
+ synchronized (m_timer) {
+ m_stopped = true;
+ m_timer.notifyAll();
+ }
+ }
+}
\ No newline at end of file
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Scheduler.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Scheduler.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Scheduler.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/Scheduler.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,103 @@
+package net.luminis.liq.scheduler;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * The scheduler periodically runs tasks based on a scheduling recipe. Tasks can be added and
+ * removed using the <code>addRunnable</code> and <code>removeRunnable</code> methods. Recipes are
+ * supplied using configuration properties using the <code>updated</code> method, or are
+ * passed in the task's properties.<br>
+ *
+ * A task will be scheduled if both a <code>Runnable</code> and a <code>recipe</code> are available
+ * for it.
+ */
+public class Scheduler implements ManagedService {
+ protected Map m_tasks = new HashMap/*<String, SchedulerTask>*/();
+
+ /**
+ * Adds a new runnable to this scheduler. The runnable will be created if necessary, registered, and processed.
+ * @param name A name for this task.
+ * @param task A runnable to run for this task.
+ * @param description A description of the task.
+ * @param recipe Optionally, a recipe for running this task.
+ * @param recipeOverride Indicates whether or not the <code>recipe</code> passed in prevails over
+ * any recipe provided by the <code>Scheduler</code>'s configuration.
+ * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
+ * be decoded into a recipe.
+ */
+ public synchronized void addRunnable(String name, Runnable task, String description, Object recipe, boolean recipeOverride) throws ConfigurationException {
+ SchedulerTask schedTask = (SchedulerTask) m_tasks.get(name);
+ if (schedTask == null) {
+ schedTask = new SchedulerTask(name);
+ m_tasks.put(name, schedTask);
+ }
+ schedTask.updateTask(task, description, recipe, recipeOverride);
+ schedTask.process();
+ }
+
+ /**
+ * Removes a runnable from this scheduler.
+ * @param name The name of the runnable. If the name does not indicate a valid runnable,
+ * nothing is done.
+ */
+ public synchronized void removeRunnable(String name) {
+ SchedulerTask schedTask = (SchedulerTask) m_tasks.get(name);
+ if (schedTask != null) {
+ try {
+ schedTask.updateTask(null, null, null, false);
+ }
+ catch (ConfigurationException e) {
+ // Will not occur; a null recipe will not cause an exception.
+ }
+ if (!schedTask.process()) {
+ m_tasks.remove(name);
+ }
+ }
+ }
+
+ /**
+ * Updates the configuration of the scheduler. The scheduler expects the configuration
+ * to contain recipes for scheduling. The key of a property should be the name identifying
+ * a task and the value should be a string describing the scheduling recipe for this task.
+ */
+ public void updated(Dictionary properties) throws ConfigurationException {
+ if (properties != null) {
+ // first remove all the old schedules.
+ for (Iterator i = m_tasks.keySet().iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ SchedulerTask schedTask = (SchedulerTask) m_tasks.get(name);
+ schedTask.updateConfigurationRecipe(null);
+ }
+
+ // then apply the new ones
+ properties.remove(Constants.SERVICE_PID);
+ Enumeration keys = properties.keys();
+ while (keys.hasMoreElements()) {
+ String name = (String) keys.nextElement();
+ SchedulerTask schedTask = (SchedulerTask) m_tasks.get(name);
+ if (schedTask == null) {
+ schedTask = new SchedulerTask(name);
+ m_tasks.put(name, schedTask);
+ }
+ schedTask.updateConfigurationRecipe(properties.get(name));
+ }
+
+ // and remove all tasks that now have no schedule or runnable
+ for (Iterator i = m_tasks.keySet().iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ SchedulerTask schedTask = (SchedulerTask) m_tasks.get(name);
+ if (!schedTask.process()) {
+ m_tasks.remove(name);
+ }
+ }
+ }
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/SchedulerTask.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/SchedulerTask.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/SchedulerTask.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/liq/scheduler/SchedulerTask.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,166 @@
+package net.luminis.liq.scheduler;
+
+import org.osgi.service.cm.ConfigurationException;
+
+/**
+ * Wrapper class for collecting a <code>Runnable</code> and its corresponding <code>recipe</code>(s).
+ * Will schedule the task when both a schedule and a <code>Runnable</code> are available.<br>
+ */
+public class SchedulerTask {
+ private final String m_name;
+ private Runnable m_task;
+ private String m_description;
+ private Object m_configurationRecipe;
+ private Object m_taskRecipe;
+ private boolean m_recipeOverride;
+ private Object m_currentRecipe;
+ private Executer m_executer;
+
+ /**
+ * Creates instance of this class.
+ * @param name The name of the runnable task.
+ */
+ SchedulerTask(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("A SchedulerTask's name cannot be null.");
+ }
+ m_name = name;
+ }
+
+
+ public String getName() {
+ return m_name;
+ }
+
+ public String getDescription() {
+ return m_description;
+ }
+
+ public Runnable getTask() {
+ return m_task;
+ }
+
+ /**
+ * Returns the currently most suited recipe, if any. This function not returning
+ * <code>null</code> does not mean that the task is scheduled (it may still be missing
+ * a <code>Runnable</code>).
+ */
+ public Object getCurrentRecipe() {
+ return m_currentRecipe;
+ }
+
+ /**
+ * Indicates whether this task is actually scheduled, and thus will run at some time
+ * in the future, unless the schedule is set to <code>null</code>, or the <code>Runnable</code>
+ * is removed.
+ */
+ public boolean isScheduled() {
+ return m_executer != null;
+ }
+
+ /**
+ * States a new set of properties for this task.
+ * @param task A runnable to run for this task.
+ * @param description A description of the task.
+ * @param taskRecipe Optionally, a recipe for running this task.
+ * @param recipeOverride Indicates whether or not the <code>recipe</code> passed in prevails over
+ * any recipe provided by the <code>Scheduler</code>'s configuration.
+ * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
+ * be decoded into a recipe.
+ */
+ public void updateTask(Runnable task, String description, Object taskRecipe, boolean recipeOverride) throws ConfigurationException {
+ checkRecipe(taskRecipe);
+ m_task = task;
+ m_description = description;
+ m_taskRecipe = taskRecipe;
+ m_recipeOverride = recipeOverride;
+ }
+
+ /**
+ * States a new recipe as coming from a configuration.
+ * @param recipe Optionally, a recipe for running this task.
+ * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
+ * be decoded into a recipe.
+ */
+ public void updateConfigurationRecipe(Object recipe) throws ConfigurationException {
+ checkRecipe(recipe);
+ m_configurationRecipe = recipe;
+ }
+
+ public boolean process() {
+ Object recipe = findRecipe();
+ if ((recipe != null) && (m_task != null)) {
+ if (!recipe.equals(m_currentRecipe) && (m_executer != null)) {
+ m_executer.stop();
+ m_executer = null;
+ }
+ if (m_executer == null) {
+ m_executer = new Executer(m_task);
+ m_executer.start(parseScheduleRecipe(recipe));
+ }
+ }
+ else {
+ // there is nothing to do, since there is no recipe or task
+ if (m_executer != null) {
+ m_executer.stop();
+ m_executer = null;
+ }
+ }
+ m_currentRecipe = recipe;
+ return ((recipe != null) || (m_task != null));
+ }
+
+ /**
+ * Finds the most suitable recipe for the given task, using both the properties published
+ * with the task, and the scheduler service's properties.
+ * @return An <code>Object</code> representing the scheduler recipe, if any. If no suitable recipe can be found,
+ * <code>null</code> will be returned.
+ */
+ private Object findRecipe() {
+ if (m_recipeOverride) {
+ if (m_taskRecipe != null) {
+ return m_taskRecipe;
+ }
+ else if (m_configurationRecipe != null) {
+ return m_configurationRecipe;
+ }
+ }
+ else {
+ if (m_configurationRecipe != null) {
+ return m_configurationRecipe;
+ }
+ else if (m_taskRecipe != null) {
+ return m_taskRecipe;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Decodes an Object into a schedule.
+ * @param recipe An object representing a recipe.
+ * @return A decoded representation of the recipe.
+ */
+ private long parseScheduleRecipe(Object recipe) {
+ // For now assume just the number of milliseconds is in the string, we may want to do a
+ // more 'cron-like' scheduling in the future
+ return Long.valueOf(recipe.toString()).longValue();
+ }
+
+ /**
+ * Helper method that checks whether a recipe is valid.
+ * @throws ConfigurationException When <code>recipe</code> is not <code>null</code>, and cannot
+ * be decoded into a recipe.
+ */
+ private void checkRecipe(Object recipe) throws ConfigurationException {
+ if (recipe != null) {
+ try {
+ parseScheduleRecipe(recipe);
+ }
+ catch (NumberFormatException nfe) {
+ throw new ConfigurationException(m_name, "Could not parse scheduling recipe for task", nfe);
+ }
+ }
+ }
+
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,26 @@
+package net.luminis.sample.managedservice;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedService;
+
+public class Activator extends DependencyActivatorBase {
+
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Dictionary properties = new Hashtable();
+ properties.put(Constants.SERVICE_PID, "net.luminis.sample.managedservice");
+ manager.add(createService()
+ .setInterface(ManagedService.class.getName(), properties)
+ .setImplementation(Impl.class));
+ }
+
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+
+}
\ No newline at end of file
Added: incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Impl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Impl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Impl.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/sample/managedservice/Impl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,24 @@
+package net.luminis.sample.managedservice;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+
+public class Impl implements ManagedService {
+
+ public synchronized void updated(Dictionary dictionary) throws ConfigurationException {
+ if(dictionary != null) {
+ Enumeration keys = dictionary.keys();
+ System.out.println("Dictionary contains:");
+ while (keys.hasMoreElements()) {
+ Object nextElement = keys.nextElement();
+ System.out.println("KEY=(" + nextElement + ") VALUE=(" + dictionary.get(nextElement) +")");
+ }
+ } else {
+ System.out.println("Empty dictionary was supplied.");
+ }
+ }
+
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Activator.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,24 @@
+package net.luminis.sample.managedservicefactory;
+
+import java.util.Properties;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+
+public class Activator extends DependencyActivatorBase {
+
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, "net.luminis.sample.managedservicefactory");
+ manager.add(createService()
+ .setInterface(ManagedServiceFactory.class.getName(), props)
+ .setImplementation(new Impl()));
+ }
+
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do here
+ }
+}
Added: incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Impl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Impl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Impl.java (added)
+++ incubator/ace/trunk/gateway/src/net/luminis/sample/managedservicefactory/Impl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,41 @@
+package net.luminis.sample.managedservicefactory;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+
+public class Impl implements ManagedServiceFactory {
+
+ private List m_pids = new ArrayList();
+
+ public synchronized void deleted(String pid) {
+ System.out.println("Factory instance removed: " + pid);
+ m_pids.remove(pid);
+ System.out.println("Remaining instances: " + m_pids);
+ }
+
+ public String getName() {
+ return "Sample Managed Service Factory";
+ }
+
+ public synchronized void updated(String pid, Dictionary dictionary) throws ConfigurationException {
+ System.out.println("New factory instance: " + pid);
+ System.out.println("Other instances:" + m_pids);
+ m_pids.add(pid);
+ if(dictionary != null) {
+ Enumeration keys = dictionary.keys();
+ System.out.println("Dictionary contains:");
+ while (keys.hasMoreElements()) {
+ Object nextElement = keys.nextElement();
+ System.out.println("KEY=(" + nextElement + ") VALUE=(" + dictionary.get(nextElement) +")");
+ }
+ } else {
+ System.out.println("Empty dictionary was supplied.");
+ }
+ }
+
+}