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.");
+        }
+    }
+
+}