You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by br...@apache.org on 2013/05/07 12:25:15 UTC

svn commit: r1479841 [2/3] - in /ace/trunk: org.apache.ace.agent.itest/ org.apache.ace.agent.itest/.settings/ org.apache.ace.agent.itest/resources/ org.apache.ace.agent.itest/src/ org.apache.ace.agent.itest/src/org/ org.apache.ace.agent.itest/src/org/a...

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentServiceImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentServiceImpl.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentServiceImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentServiceImpl.java Tue May  7 10:25:13 2013
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.deployment;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.deployment.Deployment;
+import org.apache.ace.deployment.service.DeploymentService;
+import org.apache.ace.discovery.Discovery;
+import org.apache.ace.identification.Identification;
+import org.osgi.framework.Version;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.log.LogService;
+
+//FIXME This is a of the org.apache.ace.deployment it is private and may be better located here.
+
+/**
+ * Provides an implementation for {@link DeploymentService}.
+ */
+public class DeploymentServiceImpl implements DeploymentService {
+    
+    private final String TOPIC_DEPLOYMENTPACKAGE_INSTALL = "org/apache/ace/deployment/INSTALL";
+
+    // injected by dependencymanager
+    protected volatile Deployment m_deployer;
+    protected volatile Identification m_identification;
+    protected volatile Discovery m_discovery;
+    protected volatile LogService m_log;
+    protected volatile EventAdmin m_eventAdmin;
+    protected volatile ConnectionFactory m_connectionFactory;
+
+    /**
+     * @see org.apache.ace.deployment.service.DeploymentService#getHighestLocalVersion()
+     */
+    public Version getHighestLocalVersion() {
+        Object[] installedPackages = m_deployer.list();
+        List versions = new ArrayList();
+        for (int i = 0; i < installedPackages.length; i++) {
+            if (m_deployer.getName(installedPackages[i]).equals(m_identification.getID())) {
+                versions.add(m_deployer.getVersion(installedPackages[i]));
+            }
+        }
+        return getHighestVersion(versions);
+    }
+
+    /**
+     * @see org.apache.ace.deployment.service.DeploymentService#getHighestRemoteVersion()
+     */
+    public Version getHighestRemoteVersion() throws IOException {
+        SortedSet<Version> versions = getRemoteVersions(getURL());
+        return ((versions == null) || versions.isEmpty()) ? null : versions.last();
+    }
+
+    /**
+     * @see org.apache.ace.deployment.service.DeploymentService#getRemoteVersions()
+     */
+    public SortedSet<Version> getRemoteVersions() throws IOException {
+        return getRemoteVersions(getURL());
+    }
+
+    /**
+     * @see org.apache.ace.deployment.service.DeploymentService#installVersion(org.osgi.framework.Version, org.osgi.framework.Version)
+     */
+    public void installVersion(Version highestRemoteVersion, Version highestLocalVersion) throws IOException, Exception {
+        InputStream inputStream = null;
+        
+        m_log.log(LogService.LOG_INFO, "Installing version: " + highestRemoteVersion);
+        
+        try {
+            String version = highestRemoteVersion.toString();
+            URL baseURL = getURL();
+            boolean isFileBasedProtocol = "file".equals(baseURL.getProtocol());
+            if (highestLocalVersion != null && !isFileBasedProtocol) {
+                version += "?current=" + highestLocalVersion.toString();
+            }
+			URL dataURL = new URL(baseURL, version);
+			if (isFileBasedProtocol) {
+                File file = urlToFile(dataURL);
+                inputStream = new FileInputStream(file);
+            }
+            else {
+                inputStream = getContents(dataURL);
+            }
+
+            // Post event for auditlog
+            m_eventAdmin.postEvent(createEvent(version, dataURL));
+
+            m_deployer.install(inputStream);
+        }
+        finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                }
+                catch (Exception ex) {
+                    // Not much we can do.
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.ace.deployment.service.DeploymentService#update(org.osgi.framework.Version)
+     */
+    public void update(Version toVersion) throws Exception {
+        installVersion(toVersion, getHighestLocalVersion());
+    }
+
+    /**
+     * @param url
+     * @return
+     * @throws IOException
+     */
+    final SortedSet<Version> getRemoteVersions(URL url) throws IOException {
+        if (url == null) {
+            return null;
+        }
+        
+        if ("file".equals(url.getProtocol())) {
+            return getVersionsFromDirectory(url);
+        }
+        else {
+            return getVersionsFromServer(url);
+        }
+    }
+
+    /**
+     * @param version
+     * @param dataURL
+     * @return
+     */
+    private Event createEvent(String version, URL dataURL) {
+        Dictionary properties = new Properties();
+        properties.put("deploymentpackage.url", dataURL.toString());
+        properties.put("deploymentpackage.version", version);
+        Event event = new Event(TOPIC_DEPLOYMENTPACKAGE_INSTALL, properties);
+        return event;
+    }
+
+    /**
+     * @param versions
+     * @return
+     */
+    private Version getHighestVersion(List versions) {
+        Version highestVersion = null;
+        for (Iterator i = versions.iterator(); i.hasNext();) {
+            Version version = (Version) i.next();
+            if (highestVersion == null) {
+                highestVersion = version;
+            }
+            else if (version.compareTo(highestVersion) > 0) {
+                highestVersion = version;
+            }
+        }
+        return highestVersion;
+    }
+
+    /**
+     * @return
+     */
+    private URL getURL() {
+        URL host = m_discovery.discover();
+        if (host == null) {
+            return null;
+        }
+        try {
+            return new URL(host, "deployment/" + m_identification.getID() + "/versions/");
+        }
+        catch (MalformedURLException e) {
+            m_log.log(LogService.LOG_WARNING, "Malformed URL", e);
+            return null;
+        }
+    }
+
+    /**
+     * @param url
+     * @return
+     */
+    private SortedSet<Version> getVersionsFromDirectory(URL url) {
+        File file = urlToFile(url);
+        if (!file.isDirectory()) {
+            return null;
+        }
+            
+        final File[] files = file.listFiles();
+        SortedSet<Version> versions = new TreeSet<Version>();
+        for (File f : files) {
+            try {
+                Version version = Version.parseVersion(f.getName());
+                if (version != Version.emptyVersion) {
+                    versions.add(version);
+                }
+            }
+            catch (IllegalArgumentException e) {
+                // if the file is not a valid version, we skip it
+            }
+        }
+        return versions;
+    }
+
+    /**
+     * @param url
+     * @return
+     */
+    private SortedSet<Version> getVersionsFromServer(URL url) {
+        BufferedReader bufReader = null;
+        try {
+            bufReader = new BufferedReader(new InputStreamReader(getContents(url)));
+            SortedSet<Version> versions = new TreeSet<Version>();
+            
+            String versionString;
+            while ((versionString = bufReader.readLine()) != null) {
+                try {
+                    Version version = Version.parseVersion(versionString);
+                    if (version != Version.emptyVersion) {
+                        versions.add(version);
+                    }
+                }
+                catch (IllegalArgumentException iae) {
+                    m_log.log(LogService.LOG_WARNING, "Received malformed version, ignoring: " + versionString);
+                }
+            }
+            
+            return versions;
+        }
+        catch (IOException ioe) {
+            m_log.log(LogService.LOG_DEBUG, "I/O error accessing server!", ioe);
+            return null;
+        }
+        finally {
+            if (bufReader != null) {
+                try {
+                    bufReader.close();
+                }
+                catch (Exception ex) {
+                    // not much we can do
+                }
+            }
+        }
+    }
+
+    /**
+     * @param url
+     * @return
+     */
+    private File urlToFile(URL url) {
+        File file;
+        // See: http://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html
+        // makes a best effort to convert a file URL to a File
+        try {
+            file = new File(url.toURI());
+        }
+        catch (URISyntaxException e) {
+            file = new File(url.getPath());
+        }
+        return file;
+    }
+
+    /**
+     * @param url the remote URL to connect to, cannot be <code>null</code>.
+     * @return an {@link InputStream} to the remote URL, never <code>null</code>.
+     * @throws IOException in case of I/O problems opening the remote connection.
+     */
+    private InputStream getContents(URL url) throws IOException {
+        URLConnection conn = m_connectionFactory.createConnection(url);
+        return conn.getInputStream();
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTask.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTask.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTask.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTask.java Tue May  7 10:25:13 2013
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.deployment;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.apache.ace.deployment.service.DeploymentService;
+import org.osgi.framework.Version;
+import org.osgi.service.log.LogService;
+
+//FIXME This is a of the org.apache.ace.deployment it is private and may be better located here.
+
+/**
+ * Implementation of the <code>Runnable</code> interface that updates software configurations by using the
+ * <code>DeploymentService</code> to determine the current local version and to actually install new versions.
+ */
+public class DeploymentUpdateTask implements Runnable {
+
+    private volatile DeploymentService m_service;
+    private volatile LogService m_log;
+
+    /**
+     * When run a check is made if a higher version is available on the remote. If so, an attempt is made to install
+     * this new version.
+     */
+    public void run() {
+        try {
+            Version highestLocalVersion = m_service.getHighestLocalVersion();
+            Version highestRemoteVersion = m_service.getHighestRemoteVersion();
+
+            if (highestRemoteVersion == null) {
+                // expected if there's no discovered ps or relay server
+                // ACE-220: lower log level; not of real interest...
+                m_log.log(LogService.LOG_DEBUG, "Highest remote: unknown / Highest local: " + highestLocalVersion);
+                return;
+            }
+            // ACE-220: lower log level; not of real interest...
+            m_log.log(LogService.LOG_DEBUG, "Highest remote: " + highestRemoteVersion + " / Highest local: " + highestLocalVersion);
+
+            if ((highestRemoteVersion != null) && ((highestLocalVersion == null) || (highestRemoteVersion.compareTo(highestLocalVersion) > 0))) {
+                // no local version or local version lower than remote, install the update
+                m_service.installVersion(highestRemoteVersion, highestLocalVersion);
+            }
+        }
+        catch (MalformedURLException e) {
+            m_log.log(LogService.LOG_ERROR, "Error creating endpoint url", e);
+        }
+        catch (IOException e) {
+            m_log.log(LogService.LOG_ERROR, "Error accessing resources", e);
+        }
+        catch (Exception e) {
+            m_log.log(LogService.LOG_ERROR, "Error installing update", e);
+        }
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTaskFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTaskFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTaskFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/deployment/DeploymentUpdateTaskFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.deployment;
+
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ace.agent.spi.OneComponentFactoryBase;
+import org.apache.ace.deployment.service.DeploymentService;
+import org.apache.ace.scheduler.constants.SchedulerConstants;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Creates a executor whiteboard {@link Runnable} service component with a {@link DeploymentUpdateTask} implementation.
+ * 
+ */
+public class DeploymentUpdateTaskFactory extends OneComponentFactoryBase {
+
+    @Override
+    public Component createComponent(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) throws ConfigurationException {
+
+        Properties properties = getAgentproperties(configuration);
+        properties.put(SchedulerConstants.SCHEDULER_NAME_KEY, DeploymentUpdateTask.class.getSimpleName());
+        properties.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, "Task that synchronizes the artifacts (bundles, resources) installed on this target with the server.");
+        properties.put(SchedulerConstants.SCHEDULER_RECIPE, 2000);
+
+        return manager.createComponent()
+            .setInterface(Runnable.class.getName(), properties)
+            .setImplementation(new DeploymentUpdateTask())
+            .add(manager.createServiceDependency().setService(DeploymentService.class, getAgentFilter(configuration, null)).setRequired(true))
+            .add(manager.createServiceDependency().setService(LogService.class).setRequired(false));
+    }
+
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/discovery/PropertyBasedDiscoveryFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/discovery/PropertyBasedDiscoveryFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/discovery/PropertyBasedDiscoveryFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/discovery/PropertyBasedDiscoveryFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.discovery;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+import org.apache.ace.agent.spi.OneComponentFactoryBase;
+import org.apache.ace.discovery.Discovery;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Creates a {@link Discovery} service component with an implementation that returns the configured
+ * <code>identification.property.value</code>
+ * 
+ */
+public class PropertyBasedDiscoveryFactory extends OneComponentFactoryBase {
+
+    public static final String DISCOVERY_PROPERTY_VALUE = "serverurl";
+
+    @Override
+    public Component createComponent(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) throws ConfigurationException {
+
+        final String urlStr = (String) configuration.get(DISCOVERY_PROPERTY_VALUE);
+        if (urlStr == null || urlStr.equals("")) {
+            throw new ConfigurationException(DISCOVERY_PROPERTY_VALUE, "Missing a valid discovery value");
+        }
+
+        try {
+            final URL url = new URL(urlStr);
+            Discovery impl = new Discovery() {
+
+                @Override
+                public URL discover() {
+                    return url;
+                }
+            };
+
+            return manager.createComponent()
+                .setInterface(Discovery.class.getName(), getAgentproperties(configuration)).setImplementation(impl);
+        }
+        catch (MalformedURLException e) {
+            throw new ConfigurationException(DISCOVERY_PROPERTY_VALUE, "Discovery URL is bad: " + urlStr);
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IdentifierBasedIdentificationFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IdentifierBasedIdentificationFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IdentifierBasedIdentificationFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IdentifierBasedIdentificationFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.identification;
+
+import java.util.Map;
+
+import org.apache.ace.agent.spi.OneComponentFactoryBase;
+import org.apache.ace.identification.Identification;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * Creates a {@link Identification} service component with an implementation that returns the configured agent
+ * identifier.
+ * 
+ */
+public class IdentifierBasedIdentificationFactory extends OneComponentFactoryBase {
+
+    @Override
+    public Component createComponent(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) {
+
+        final String value = getAgentIdentifier(configuration);
+        Identification impl = new Identification() {
+
+            @Override
+            public String getID() {
+                return value;
+            }
+        };
+
+        return manager.createComponent()
+            .setInterface(Identification.class.getName(), getAgentproperties(configuration)).setImplementation(impl);
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IfconfigIdentification.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IfconfigIdentification.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IfconfigIdentification.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/IfconfigIdentification.java Tue May  7 10:25:13 2013
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.identification;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.ace.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_targetID = null;
+
+    public synchronized String getID() {
+        if (m_targetID == null) {
+            BufferedReader reader = null;
+            try {
+                Process process = Runtime.getRuntime().exec(IFCONFIG_COMMAND);
+                InputStream inputStream = process.getInputStream();
+                reader = new BufferedReader(new InputStreamReader(inputStream));
+                m_targetID = parseIfconfigOutput(reader).toLowerCase();
+            }
+            catch (IOException ioe) {
+                m_log.log(LogService.LOG_WARNING, "Unable to determine ifconfig based mac-address target identification.", ioe);
+                return null;
+            }
+            finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    }
+                    catch (IOException e) {
+                        // not much we can do
+                    }
+                }
+            }
+        }
+        return m_targetID;
+    }
+
+    /**
+     * 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 java.io.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;
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/PropertyBasedIdentificationFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/PropertyBasedIdentificationFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/PropertyBasedIdentificationFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/PropertyBasedIdentificationFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.identification;
+
+import java.util.Map;
+
+import org.apache.ace.agent.spi.OneComponentFactoryBase;
+import org.apache.ace.discovery.Discovery;
+import org.apache.ace.identification.Identification;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+
+/**
+ * Creates a {@link Discovery} service component with an implementation that returns the configured
+ * <code>identification.property.value</code>
+ * 
+ */
+public class PropertyBasedIdentificationFactory extends OneComponentFactoryBase {
+
+    public static final String IDENTIFICATION_PROPERTY_VALUE = "identification";
+
+    @Override
+    public Component createComponent(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) throws ConfigurationException {
+
+        final String value = configuration.get(IDENTIFICATION_PROPERTY_VALUE);
+        if (value == null || value.equals("")) {
+            throw new ConfigurationException(IDENTIFICATION_PROPERTY_VALUE, "Missing a valid identification value");
+        }
+        Identification impl = new Identification() {
+
+            @Override
+            public String getID() {
+                return value;
+            }
+        };
+
+        return manager.createComponent()
+            .setInterface(Identification.class.getName(), getAgentproperties(configuration)).setImplementation(impl);
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/ifconfigBasedIdentificationFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/ifconfigBasedIdentificationFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/ifconfigBasedIdentificationFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/identification/ifconfigBasedIdentificationFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.identification;
+
+import java.util.Map;
+
+import org.apache.ace.agent.spi.OneComponentFactoryBase;
+import org.apache.ace.identification.Identification;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * Component factory for a {@link Identification} service with a {@link IfconfigIdentification} implementation.
+ * 
+ */
+public class ifconfigBasedIdentificationFactory extends OneComponentFactoryBase {
+
+    @Override
+    public Component createComponent(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) {
+
+        Identification impl = new IfconfigIdentification();
+        return manager.createComponent()
+            .setInterface(Identification.class.getName(), getAgentproperties(configuration)).setImplementation(impl);
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/Activator.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/Activator.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/Activator.java Tue May  7 10:25:13 2013
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.impl;
+
+import static org.apache.ace.agent.Constants.FACTORY_PID;
+
+import java.util.Properties;
+
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+
+/**
+ * OSGi {@link BundleActivator} for the Apache ACE ManagementAGent.
+ * 
+ */
+public class Activator extends DependencyActivatorBase {
+
+    private final BundleActivator[] m_activators = new BundleActivator[] {
+        new org.apache.ace.connectionfactory.impl.Activator(),
+        new org.apache.ace.scheduler.Activator(),
+        new org.apache.ace.consolelogger.Activator(),
+        new org.apache.felix.deploymentadmin.Activator()
+    };
+
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+
+        Properties properties = new Properties();
+        properties.put(Constants.SERVICE_PID, FACTORY_PID);
+        ManagementAgentFactory factory = new ManagementAgentFactory();
+        manager.add(createComponent()
+            .setInterface(ManagedServiceFactory.class.getName(), properties)
+            .setImplementation(factory)
+            .add(createServiceDependency().setService(LogService.class).setRequired(false)));
+
+        StaticConfigurationHandler handler = new StaticConfigurationHandler();
+        manager.add(createComponent()
+            .setImplementation(handler)
+            .add(createServiceDependency().setService(ManagedServiceFactory.class, "(" + Constants.SERVICE_PID + "=" + FACTORY_PID + ")").setRequired(true))
+            .add(createServiceDependency().setService(LogService.class).setRequired(false)));
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ManagementAgentFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ManagementAgentFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ManagementAgentFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/ManagementAgentFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.impl;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ace.agent.ManagementAgent;
+import org.apache.ace.agent.connection.ConnectionFactoryFactory;
+import org.apache.ace.agent.deployment.DeploymentAdminDeployerFactory;
+import org.apache.ace.agent.deployment.DeploymentCheckTaskFactory;
+import org.apache.ace.agent.deployment.DeploymentServiceFactory;
+import org.apache.ace.agent.deployment.DeploymentUpdateTaskFactory;
+import org.apache.ace.agent.discovery.PropertyBasedDiscoveryFactory;
+import org.apache.ace.agent.identification.IdentifierBasedIdentificationFactory;
+import org.apache.ace.agent.logging.LogFactory;
+import org.apache.ace.agent.logging.LogStoreFactory;
+import org.apache.ace.agent.logging.LogSyncTaskFactory;
+import org.apache.ace.agent.spi.ComponentFactory;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+
+/**
+ * Factory that handles configuration of management agents services. For every subsystem a {@link ComponentFactory} is
+ * used to instantiate the actual components. Factories can be specified through configuration using a
+ * <code>&lt;subsystem&gt;.factory</code> property.
+ */
+public class ManagementAgentFactory implements ManagedServiceFactory {
+
+    private static final Set<String> DEFAULT_FACTORIES = new HashSet<String>();
+
+    static {
+        DEFAULT_FACTORIES.add(IdentifierBasedIdentificationFactory.class.getName());
+        DEFAULT_FACTORIES.add(PropertyBasedDiscoveryFactory.class.getName());
+        DEFAULT_FACTORIES.add(LogFactory.class.getName());
+        DEFAULT_FACTORIES.add(LogStoreFactory.class.getName());
+        DEFAULT_FACTORIES.add(LogSyncTaskFactory.class.getName());
+        DEFAULT_FACTORIES.add(DeploymentServiceFactory.class.getName());
+        DEFAULT_FACTORIES.add(DeploymentAdminDeployerFactory.class.getName());
+        DEFAULT_FACTORIES.add(DeploymentCheckTaskFactory.class.getName());
+        DEFAULT_FACTORIES.add(DeploymentUpdateTaskFactory.class.getName());
+        DEFAULT_FACTORIES.add(ConnectionFactoryFactory.class.getName());
+    }
+
+    private final Map<String, Set<Component>> m_components = new HashMap<String, Set<Component>>();
+
+    private volatile BundleContext m_context;
+    private volatile DependencyManager m_manager;
+    private volatile LogService m_logService;
+
+    @Override
+    public String getName() {
+        return ManagementAgentFactory.class.getSimpleName();
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public void updated(String pid, Dictionary /* <string, String> */properties) throws ConfigurationException {
+
+        Map<String, String> configuration = getConfigurationMap(properties);
+        String agent = getAgentIdentifier(configuration);
+        m_logService.log(LogService.LOG_DEBUG, "Receiving updated for pid/agent : " + pid + "/" + agent);
+
+        Set<ComponentFactory> componentFactories = getComponentFactories(configuration);
+        Set<Component> components = new HashSet<Component>();
+
+        for (ComponentFactory componentFactory : componentFactories) {
+            components.addAll(componentFactory.createComponents(m_context, m_manager, m_logService, configuration));
+        }
+
+        // This is kind of void but at present the only way for user-space consumers (like itest) to see that we
+        // successfully configured the agent. Could be replaced by events. but this API may prove usefull in future to
+        // expose limited functionality into user-space.
+        Properties agentProperties = new Properties();
+        agentProperties.put("agent", agent);
+        Component agentComponent = m_manager.createComponent()
+            .setInterface(ManagementAgent.class.getName(), agentProperties)
+            .setImplementation(new ManagementAgent() {
+            });
+        components.add(agentComponent);
+
+        synchronized (m_components) {
+            m_components.put(pid, components);
+
+        }
+
+        for (Component component : components) {
+            m_manager.add(component);
+        }
+    }
+
+    @Override
+    public void deleted(String pid) {
+        Set<Component> deleted;
+        synchronized (m_components) {
+            deleted = m_components.remove(pid);
+        }
+        if (deleted != null) {
+            for (Component component : deleted) {
+                m_manager.remove(component);
+            }
+        }
+    }
+
+    private Set<ComponentFactory> getComponentFactories(Map<String, String> configuration) throws ConfigurationException {
+
+        Set<ComponentFactory> componentFactories = new HashSet<ComponentFactory>();
+
+        String factoriesProperty = ((String) configuration.get("factories"));
+        if (factoriesProperty == null || factoriesProperty.equals("")) {
+            for (String componentFactoryName : DEFAULT_FACTORIES) {
+                ComponentFactory componentFactory = getComponentFactory(componentFactoryName);
+                componentFactories.add(componentFactory);
+            }
+        }
+        else {
+            String[] componentFactoryNames = factoriesProperty.split(",");
+            for (String componentFactoryName : componentFactoryNames) {
+                ComponentFactory componentFactory = getComponentFactory(componentFactoryName.trim());
+                componentFactories.add(componentFactory);
+            }
+        }
+        return componentFactories;
+    }
+
+    /**
+     * Converts the configuration dictionary to a Map<String, String>.
+     */
+    @SuppressWarnings("rawtypes")
+    private Map<String, String> getConfigurationMap(Dictionary /* <String, String> */properties) {
+        Map<String, String> configuration = new HashMap<String, String>();
+        Enumeration/* <String> */enumeration = properties.keys();
+        while (enumeration.hasMoreElements()) {
+            Object key = enumeration.nextElement();
+            configuration.put(key.toString(), properties.get(key).toString());
+        }
+        return configuration;
+    }
+
+    /**
+     * Extracts the agent identifier form the configuration.
+     */
+    private String getAgentIdentifier(Map<String, String> configuration) throws ConfigurationException {
+        String agentIdentifier = ((String) configuration.get("agent"));
+        if (agentIdentifier != null) {
+            agentIdentifier = agentIdentifier.trim();
+        }
+        if (agentIdentifier == null || agentIdentifier.equals("")) {
+            throw new ConfigurationException("agent", "Updating an agent requires a valid configuration (empty name)");
+        }
+        return agentIdentifier;
+    }
+
+    /**
+     * Returns a factory based on the specified FQN.
+     */
+    private ComponentFactory getComponentFactory(String componentFactoryName) throws ConfigurationException {
+
+        try {
+            Class<?> clazz = ManagementAgentFactory.class.getClassLoader().loadClass(componentFactoryName);
+            if (!ComponentFactory.class.isAssignableFrom(clazz)) {
+                throw new ConfigurationException("factories", "Factory class does not implement ComponentFactory interface: " + componentFactoryName);
+            }
+            try {
+                Object instance = clazz.newInstance();
+                return (ComponentFactory) instance;
+            }
+            catch (InstantiationException e) {
+                throw new ConfigurationException("factories", "Factory class does not have a default constructor: " + componentFactoryName);
+            }
+            catch (IllegalAccessException e) {
+                throw new ConfigurationException("factories", "Factory class does not have a default constructor: " + componentFactoryName);
+            }
+        }
+        catch (ClassNotFoundException e) {
+            throw new ConfigurationException("factories", "Factory class not found: " + componentFactoryName);
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/StaticConfigurationHandler.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/StaticConfigurationHandler.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/StaticConfigurationHandler.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/impl/StaticConfigurationHandler.java Tue May  7 10:25:13 2013
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.impl;
+
+import static org.apache.ace.agent.Constants.FACTORY_PID;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Set;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.log.LogService;
+
+/**
+ * Configuration component that reads a management agent properties file and calls
+ * {@link ManagementAgentFactory#updated(String, Dictionary)} for every agent configuration.
+ * 
+ */
+@SuppressWarnings("restriction")
+public class StaticConfigurationHandler {
+
+    private static final Set<String> DEFAULT_ACTIVATORS = new HashSet<String>();
+
+    static {
+        DEFAULT_ACTIVATORS.add(org.apache.ace.connectionfactory.impl.Activator.class.getName());
+        DEFAULT_ACTIVATORS.add(org.apache.ace.scheduler.Activator.class.getName());
+        DEFAULT_ACTIVATORS.add(org.apache.felix.deploymentadmin.Activator.class.getName());
+    }
+
+    private final Set<BundleActivator> m_activators = new HashSet<BundleActivator>();
+
+    // injected services
+    private volatile BundleContext m_context;
+    private volatile ManagedServiceFactory m_agentFactory;
+    private volatile LogService m_logService;
+
+    // lifecycle callback
+    public void start() throws Exception {
+        loadStaticConfiguration();
+    }
+
+    // lifecycle callback
+    public void stop() throws Exception {
+        for (BundleActivator activator : m_activators) {
+            try {
+                activator.stop(m_context);
+            }
+            catch (Exception e) {
+                m_logService.log(LogService.LOG_WARNING, "Activator stop exception", e);
+            }
+        }
+        m_activators.clear();
+    }
+
+    /**
+     * Load static configuration that may hold multiple agents.
+     * 
+     */
+    @SuppressWarnings({ "rawtypes" })
+    private void loadStaticConfiguration() throws Exception {
+
+        Dictionary configuration = null;
+
+        String staticConfigurationFile = System.getProperty(FACTORY_PID);
+        if (staticConfigurationFile != null) {
+            configuration = loadProperties(new File(staticConfigurationFile));
+        }
+        if (configuration == null) {
+            configuration = new Hashtable();
+        }
+
+        startActivators(configuration);
+        configureAgents(configuration);
+    }
+
+    private void configureAgents(@SuppressWarnings("rawtypes") Dictionary configuration) throws Exception {
+
+        String agentsProperty = (String) configuration.get("agents");
+        if (agentsProperty == null || agentsProperty.equals("")) {
+            m_logService.log(LogService.LOG_WARNING, "Configuration does not specify any agents");
+            return;
+        }
+
+        String[] agents = agentsProperty.split(",");
+        for (String agent : agents) {
+            @SuppressWarnings("rawtypes")
+            Dictionary dictionary = getAgentConfiguration(agent, configuration);
+            m_agentFactory.updated("static-" + agent, dictionary);
+        }
+    }
+
+    private void startActivators(@SuppressWarnings("rawtypes") Dictionary configuration) throws Exception {
+
+        Set<String> bundleActivators = null;
+        String activatorsProperty = (String) configuration.get("activators");
+        if (activatorsProperty == null || activatorsProperty.equals("")) {
+            bundleActivators = DEFAULT_ACTIVATORS;
+        }
+        else {
+            bundleActivators = new HashSet<String>();
+            String[] activators = activatorsProperty.split(",");
+            for (String activator : activators) {
+                bundleActivators.add(activator.trim());
+            }
+        }
+
+        for (String bundleActivatorName : bundleActivators) {
+            BundleActivator activator = getBundleActivator(bundleActivatorName);
+            activator.start(m_context);
+            m_activators.add(activator);
+        }
+    }
+
+    /**
+     * Extract an agent specific configuration.
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private Dictionary getAgentConfiguration(String agent, Dictionary configuration) throws Exception {
+        String agentPrefix = agent + ".";
+        Dictionary dictionary = new Hashtable();
+
+        // first map all global properties
+        Enumeration/* <String> */keys = configuration.keys();
+        while (keys.hasMoreElements()) {
+            String key = (String) keys.nextElement();
+            if (!key.startsWith(agentPrefix)) {
+                dictionary.put(key, configuration.get(key));
+            }
+        }
+
+        // overwrite with agent specific properties
+        keys = configuration.keys();
+        while (keys.hasMoreElements()) {
+            String key = (String) keys.nextElement();
+            if (key.startsWith(agentPrefix)) {
+                dictionary.put(key.replaceFirst(agentPrefix, ""), configuration.get(key));
+            }
+        }
+
+        dictionary.put("agent", agent);
+        return dictionary;
+    }
+
+    /**
+     * Load the properties file from disk.
+     */
+    @SuppressWarnings({ "rawtypes" })
+    private Dictionary loadProperties(File configurationFile) throws Exception {
+        if (!configurationFile.exists()) {
+            m_logService.log(LogService.LOG_WARNING, "Specified configuration file does not exist: " + configurationFile.getAbsolutePath());
+            return null;
+        }
+        if (!configurationFile.isFile()) {
+            m_logService.log(LogService.LOG_WARNING, "Specified configuration file is not a regular file: " + configurationFile.getAbsolutePath());
+            return null;
+        }
+        if (!configurationFile.canRead()) {
+            m_logService.log(LogService.LOG_WARNING, "Specified configuration file can not be read: " + configurationFile.getAbsolutePath());
+            return null;
+        }
+        Properties configurationProperties = new Properties();
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(configurationFile);
+            configurationProperties.load(fis);
+            return configurationProperties;
+        }
+        catch (IOException e) {
+            m_logService.log(LogService.LOG_WARNING, "Specified configuration file is invalid: " + configurationFile.getAbsolutePath(), e);
+            return null;
+        }
+        finally {
+            try {
+                if (fis != null)
+                    fis.close();
+            }
+            catch (IOException e) {
+            }
+        }
+    }
+
+    /**
+     * Returns a bundle activator based on the specified FQN.
+     */
+    private BundleActivator getBundleActivator(String bundleActivatorName) throws ConfigurationException {
+
+        try {
+            Class<?> clazz = StaticConfigurationHandler.class.getClassLoader().loadClass(bundleActivatorName);
+            if (!BundleActivator.class.isAssignableFrom(clazz)) {
+                throw new ConfigurationException("activators", "Factory class does not implement ComponentFactory interface: " + bundleActivatorName);
+            }
+            try {
+                Object instance = clazz.newInstance();
+                return (BundleActivator) instance;
+            }
+            catch (InstantiationException e) {
+                throw new ConfigurationException("activators", "BundleActivator class does not have a default constructor: " + bundleActivatorName);
+            }
+            catch (IllegalAccessException e) {
+                throw new ConfigurationException("activators", "BundleActivator class does not have a default constructor: " + bundleActivatorName);
+            }
+        }
+        catch (ClassNotFoundException e) {
+            throw new ConfigurationException("activators", "BundleActivator class not found: " + bundleActivatorName);
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.logging;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ace.agent.spi.ComponentFactoryBase;
+import org.apache.ace.log.Log;
+import org.apache.ace.log.target.store.LogStore;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * Creates {@link Log} service components with a {@link LogImpl} implementation for every configured store.
+ * 
+ */
+public class LogFactory extends ComponentFactoryBase {
+
+    public static final String LOG_STORES = "logstores";
+    public static final String LOG_NAME = "name";
+
+    @Override
+    public Set<Component> createComponents(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) {
+
+        Set<Component> components = new HashSet<Component>();
+        String value = configuration.get(LOG_STORES);
+        String[] stores = value.split(",");
+        for (String store : stores) {
+            components.add(createLogComponent(context, manager, logService, configuration, store.trim()));
+        }
+        return components;
+    }
+
+    private Component createLogComponent(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration, String store) {
+
+        Properties properties = getAgentproperties(configuration);
+        properties.put("name", store);
+        
+        return manager.createComponent()
+            .setInterface(Log.class.getName(), properties)
+            .setImplementation(new LogImpl())
+            .add(manager.createServiceDependency()
+                .setService(LogStore.class, getAgentFilter(configuration, "(name=" + store + ")"))
+                .setRequired(true))
+            .add(manager.createServiceDependency()
+                .setService(LogService.class)
+                .setRequired(false));
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogImpl.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogImpl.java Tue May  7 10:25:13 2013
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.logging;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import org.apache.ace.log.Log;
+import org.apache.ace.log.LogEvent;
+import org.apache.ace.log.target.store.LogStore;
+import org.osgi.service.log.LogService;
+
+//FIXME This is a of the org.apache.ace.log it is private and may be better located here.
+
+public class LogImpl implements Log {
+    private volatile LogStore m_store;
+    private volatile LogService m_log;
+
+    public void log(int type, Dictionary properties) {
+        try {
+            m_store.put(type, properties);
+        }
+        catch (NullPointerException e) {
+            // if we cannot store the event, we log it to the normal log as extensively as possible
+            m_log.log(LogService.LOG_WARNING, "Could not store event: " + (new LogEvent("", 0, 0, 0, type, properties)).toRepresentation(), e);
+        }
+        catch (IOException e) {
+            // if we cannot store the event, we log it to the normal log as extensively as possible
+            m_log.log(LogService.LOG_WARNING, "Could not store event: " + (new LogEvent("", 0, 0, 0, type, properties)).toRepresentation(), e);
+        }
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreFactory.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreFactory.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreFactory.java Tue May  7 10:25:13 2013
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.logging;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ace.agent.spi.ComponentFactoryBase;
+import org.apache.ace.identification.Identification;
+import org.apache.ace.log.target.store.LogStore;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+/**
+ * Creates {@link LogStore} service components with a {@link LogStoreImpl} implementation for every configured store.
+ * 
+ */
+public class LogStoreFactory extends ComponentFactoryBase {
+
+    @Override
+    public Set<Component> createComponents(BundleContext context, DependencyManager manager, LogService logService, Map<String, String> configuration) {
+
+        Set<Component> components = new HashSet<Component>();
+        String value = configuration.get(LogFactory.LOG_STORES);
+        String[] stores = value.split(",");
+        for (String store : stores) {
+            components.add(createLogStoreComponent(context, manager, configuration, logService, store.trim()));
+        }
+        return components;
+    }
+
+    private Component createLogStoreComponent(BundleContext context, DependencyManager manager, Map<String, String> configuration, LogService logService, String store) {
+
+        Properties properties = getAgentproperties(configuration);
+        properties.put(LogFactory.LOG_NAME, store);
+
+        File baseDir = new File(context.getDataFile(""), getAgentIdentifier(configuration));
+
+        return manager.createComponent()
+            .setInterface(LogStore.class.getName(), properties)
+            .setImplementation(new LogStoreImpl(baseDir))
+            .add(manager.createServiceDependency()
+                .setService(Identification.class, getAgentFilter(configuration, null))
+                .setRequired(true))
+            .add(manager.createServiceDependency()
+                .setService(LogService.class)
+                .setRequired(false));
+    }
+}

Added: ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreImpl.java?rev=1479841&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreImpl.java (added)
+++ ace/trunk/org.apache.ace.agent/src/org/apache/ace/agent/logging/LogStoreImpl.java Tue May  7 10:25:13 2013
@@ -0,0 +1,561 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.agent.logging;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.ace.identification.Identification;
+import org.apache.ace.log.LogEvent;
+import org.apache.ace.log.target.store.LogStore;
+import org.osgi.service.log.LogService;
+
+//FIXME This is a of the org.apache.ace.log it is private and may be better located here.
+
+/**
+ * This class provides an implementation of the LogStore service. It tries to
+ * repair broken log files to make them readable again. However, this might lead
+ * to loss of data. Additionally, a new file is used when an error is detected.
+ */
+public class LogStoreImpl implements LogStore {
+    // injected by dependencymanager
+    volatile Identification m_identification;
+    volatile LogService m_log;
+
+    // The current store
+    private Store m_store = null;
+    private final File m_baseDir;
+    private long m_highest;
+
+    /**
+     * Create new instance using the specified directory as root directory.
+     * 
+     * @param baseDir
+     *            Root directory to use for storage.
+     */
+    public LogStoreImpl(File baseDir) {
+        m_baseDir = new File(baseDir, "store");
+    }
+
+    /**
+     * Init the current store.
+     * 
+     * @throws java.io.IOException
+     */
+    protected synchronized void start() throws IOException {
+        if (!m_baseDir.isDirectory() && !m_baseDir.mkdirs()) {
+            throw new IllegalArgumentException("Need valid dir");
+        }
+        long current = -1;
+        File[] files = (File[]) notNull(m_baseDir.listFiles());
+        for (int i = 0; i < files.length; i++) {
+            long id = Long.parseLong(files[i].getName());
+            current = Math.max(id, current);
+        }
+        try {
+            if (current == -1) {
+                m_store = newStore();
+            } else {
+                m_store = createStore(current);
+                try {
+                    m_store.init();
+                } 
+                catch (IOException ex) {
+                    handleException(m_store, ex);
+                }
+            }
+        } 
+        catch (IOException ex) {
+            // We should be able to recover from the error.
+            m_log.log(LogService.LOG_ERROR, "Exception during log store init",
+                    ex);
+        }
+    }
+
+    /**
+     * Close the current store.
+     * 
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    protected synchronized void stop() throws IOException {
+        m_store.close();
+        m_store = null;
+    }
+
+    /**
+     * Create a store object for a new log.
+     * 
+     * @return a store object for a new log.
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    protected Store newStore() throws IOException {
+        long id = System.currentTimeMillis();
+
+        while (!(new File(m_baseDir, String.valueOf(id))).createNewFile()) {
+            id++;
+        }
+
+        return new Store(new File(m_baseDir, String.valueOf(id)), id);
+    }
+
+    /**
+     * Create a store object for the given log. This should not be used to
+     * create a new log.
+     * 
+     * @param id
+     *            the id of the log.
+     * @return a new store object for the given log.
+     * @throws java.io.IOException
+     *             in case of an IO error.
+     */
+    protected Store createStore(long id) throws IOException {
+        return new Store(new File(m_baseDir, String.valueOf(id)), id);
+    }
+
+    /**
+     * Get the entries in the given range from the given log.
+     * 
+     * @param logID
+     *            the id of the log.
+     * @param from
+     *            the lower bound of the range.
+     * @param to
+     *            the upper bound of the range.
+     * @return a list of entries from the given log in the given range.
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    public synchronized List get(long logID, long from, long to)
+            throws IOException {
+        Store store = getLog(logID);
+        List result = new ArrayList();
+        try {
+            if (store.getCurrent() > from) {
+                store.reset();
+            }
+
+            while (store.hasNext()) {
+                long eventID = store.readCurrentID();
+                if ((eventID >= from) && (eventID <= to)) {
+                    result.add(new LogEvent(new String(store.read())));
+                } else {
+                    store.skip();
+                }
+            }
+        } 
+        catch (Exception ex) {
+            handleException(store, ex);
+        } 
+        finally {
+            closeIfNeeded(store);
+        }
+        return result;
+    }
+
+    /**
+     * Try to repair the given store, log the given exception and rethrow it. In
+     * case the store is the current log switch to a new one if possible.
+     * 
+     * @param store
+     *            the store to repair/close.
+     * @param exception
+     *            the exception to log and rethrow.
+     * @throws java.io.IOException
+     *             the given exception if it is an IOException else the message
+     *             of the given exception wrapped in an IOException.
+     */
+    protected void handleException(Store store, Exception exception)
+            throws IOException {
+        m_log.log(LogService.LOG_WARNING, "Exception accessing the log: "
+                + store.getId(), exception);
+        if (store == m_store) {
+            m_store = newStore();
+        }
+
+        try {
+            store.truncate();
+        } 
+        catch (IOException ex) {
+            m_log.log(LogService.LOG_WARNING, "Exception during truncate: "
+                    + store.getId(), ex);
+        }
+        try {
+            store.close();
+        } 
+        catch (IOException ex) {
+            // Not much we can do
+        }
+        if (exception instanceof IOException) {
+            throw (IOException) exception;
+        }
+        throw new IOException("Unable to read log entry: "
+                + exception.getMessage());
+    }
+
+    /**
+     * Get all entries of the given log.
+     * 
+     * @param logID
+     *            the id of the log.
+     * @return a list of all entries in this log.
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    public List get(long logID) throws IOException {
+        return get(logID, 0, Long.MAX_VALUE);
+    }
+
+    /**
+     * Get the current log ids.
+     * 
+     * @return the ids of the current logs.
+     */
+    public long[] getLogIDs() throws IOException {
+        File[] files = (File[]) notNull(m_baseDir.listFiles());
+        long[] result = new long[files.length];
+        for (int i = 0; i < files.length; i++) {
+            result[i] = Long.parseLong(files[i].getName());
+        }
+        return result;
+    }
+
+    /**
+     * Create and add a LogEvent to the current log.
+     * 
+     * @param type
+     *            the type the event.
+     * @param props
+     *            the properties of the event.
+     * @return the new event.
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    public synchronized LogEvent put(int type, Dictionary props) throws IOException {
+        try {
+            LogEvent result = new LogEvent(null, m_store.getId(), getNextID(), System.currentTimeMillis(), type, props);
+            m_store.append(result.getID(), result.toRepresentation().getBytes());
+            return result;
+        } 
+        catch (IOException ex) {
+            handleException(m_store, ex);
+        }
+        return null;
+    }
+
+    /**
+     * Get the highest entry id of the given log.
+     * 
+     * @param logID
+     *            the id of the log.
+     * @return the id of the highest entry.
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    public synchronized long getHighestID(long logID) throws IOException {
+        Store store = getLog(logID);
+        try {
+            if (m_highest == 0) {
+                store.init();
+                return (m_highest = store.getCurrent());
+            } else {
+                return m_highest;
+            }
+        } 
+        catch (IOException ex) {
+            handleException(store, ex);
+        } 
+        finally {
+            closeIfNeeded(store);
+        }
+        return -1;
+    }
+
+    /**
+     * Close the given store if it is not the current store. IO errors are
+     * ignored.
+     * 
+     * @param store
+     *            the store to close.
+     */
+    protected void closeIfNeeded(Store store) {
+        if (store != m_store) {
+            try {
+                store.close();
+            } 
+            catch (IOException ex) {
+                // Not much we can do;
+            }
+        }
+    }
+
+    /**
+     * Get a Store object for the log of the given logid.
+     * 
+     * @param logID
+     *            the id for which to return (and possibly create) a store.
+     * @return either a new or the current Store object.
+     * @throws java.io.IOException
+     *             in case of any IO error.
+     */
+    protected Store getLog(long logID) throws IOException {
+        if (m_store.getId() == logID) {
+            return m_store;
+        }
+        return createStore(logID);
+    }
+
+    /**
+     * Get the next id for the current store.
+     * 
+     * @return the next free log id of the current store.
+     * @throws java.io.IOException
+     */
+    protected long getNextID() throws IOException {
+        return (m_highest = getHighestID(m_store.m_id) + 1);
+    }
+
+    /*
+     * throw IOException in case the target is null else return the target.
+     */
+    private Object notNull(Object target) throws IOException {
+        if (target == null) {
+            throw new IOException(
+                    "Unknown IO error while trying to access the store.");
+        }
+        return target;
+    }
+
+    /**
+     * The general idea is to provide easy access to a file of records. It
+     * supports iterating over records both by skipping and by reading.
+     * Furthermore, files can be truncated. Most methods will make an effort to
+     * reset to the last good record in case of an error -- hence, a call to
+     * truncate after an IOException might make the store readable again.
+     */
+    class Store {
+        private final RandomAccessFile m_store;
+        private final long m_id;
+        private long m_current;
+
+        /**
+         * Create a new File based Store.
+         * 
+         * @param store
+         *            the file to use as backend.
+         * @param id
+         *            the log id of the store
+         * @throws java.io.IOException
+         *             in case the file is not rw.
+         */
+        Store(File store, long id) throws IOException {
+            m_store = new RandomAccessFile(store, "rwd");
+            m_id = id;
+        }
+
+        /**
+         * Get the id of the current record.
+         * 
+         * @return the idea of the current record.
+         */
+        public long getCurrent() throws IOException {
+            long pos = m_store.getFilePointer();
+            if (m_store.length() == 0) {
+                return 0;
+            }
+            long result = 0;
+            try {
+                m_store.seek(m_current);
+                result = readCurrentID();
+                m_store.seek(pos);
+            } 
+            catch (IOException ex) {
+                handle(pos, ex);
+            }
+            return result;
+        }
+
+        /**
+         * Get the log id of this store.
+         * 
+         * @return the log id of this store.
+         */
+        public long getId() {
+            return m_id;
+        }
+
+        /**
+         * Reset the store to the beginning of the records
+         * 
+         * @throws java.io.IOException
+         *             in case of an IO error.
+         */
+        public void reset() throws IOException {
+            m_store.seek(0);
+            m_current = 0;
+        }
+
+        /**
+         * Determine whether there are any records left based on the current
+         * postion.
+         * 
+         * @return <code>true</code> if there are still records to be read.
+         * @throws java.io.IOException
+         *             in case of an IO error.
+         */
+        public boolean hasNext() throws IOException {
+            return m_store.getFilePointer() < m_store.length();
+        }
+
+        public byte[] read() throws IOException {
+            long pos = m_store.getFilePointer();
+            try {
+                if (pos < m_store.length()) {
+                    long current = m_store.getFilePointer();
+                    long id = m_store.readLong();
+                    int next = m_store.readInt();
+                    byte[] entry = new byte[next];
+                    m_store.readFully(entry);
+                    m_current = current;
+                    return entry;
+                }
+            } 
+            catch (IOException ex) {
+                handle(pos, ex);
+            }
+            return null;
+        }
+
+        public long readCurrentID() throws IOException {
+            long pos = m_store.getFilePointer();
+            try {
+                if (pos < m_store.length()) {
+                    long id = m_store.readLong();
+                    m_store.seek(pos);
+                    return id;
+                }
+            } 
+            catch (IOException ex) {
+                handle(pos, ex);
+            }
+            return -1;
+        }
+
+        /**
+         * Make sure the store is readable. As a result, the store is at the end
+         * of the records.
+         * 
+         * @throws java.io.IOException
+         *             in case of any IO error.
+         */
+        public void init() throws IOException {
+            reset();
+            try {
+                while (true) {
+                    skip();
+                }
+            } 
+            catch (EOFException ex) {
+                // done
+            }
+        }
+
+        /**
+         * Skip the next record if there is any.
+         * 
+         * @throws java.io.IOException
+         *             in case of any IO error or if there is no record left.
+         */
+        public void skip() throws IOException {
+            long pos = m_store.getFilePointer();
+            try {
+                long id = m_store.readLong();
+                int next = m_store.readInt();
+                if (m_store.length() < next + m_store.getFilePointer()) {
+                    throw new IOException("Unexpected end of file");
+                }
+                m_store.seek(m_store.getFilePointer() + next);
+                m_current = pos;
+                pos = m_store.getFilePointer();
+            } 
+            catch (IOException ex) {
+                handle(pos, ex);
+            }
+        }
+
+        /**
+         * Store the given record data as the next record.
+         * 
+         * @param entry
+         *            the data of the record to store.
+         * @throws java.io.IOException
+         *             in case of any IO error.
+         */
+        public void append(long id, byte[] entry) throws IOException {
+            long pos = m_store.getFilePointer();
+            try {
+                m_store.seek(m_store.length());
+                long current = m_store.getFilePointer();
+                m_store.writeLong(id);
+                m_store.writeInt(entry.length);
+                m_store.write(entry);
+                m_store.seek(pos);
+            } 
+            catch (IOException ex) {
+                handle(pos, ex);
+            }
+        }
+
+        /**
+         * Try to truncate the store at the current record.
+         * 
+         * @throws java.io.IOException
+         *             in case of any IO error.
+         */
+        public void truncate() throws IOException {
+            m_store.setLength(m_store.getFilePointer());
+        }
+
+        /**
+         * Release any resources.
+         * 
+         * @throws java.io.IOException
+         *             in case of any IO error.
+         */
+        public void close() throws IOException {
+            m_store.close();
+        }
+
+        private void handle(long pos, IOException exception) throws IOException {
+            try {
+                m_store.seek(pos);
+            } 
+            catch (IOException ex) {
+                m_log.log(LogService.LOG_WARNING, "Exception during seek!", ex);
+            }
+            throw exception;
+        }
+    }
+}
\ No newline at end of file