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 2013/04/04 11:43:37 UTC

svn commit: r1464402 [3/11] - in /ace/trunk: org.apache.ace.deployment.api/ org.apache.ace.deployment.deploymentadmin/ org.apache.ace.deployment.itest/ org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/ org.apache.ace.deployment.provider...

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/StreamGeneratorImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/StreamGeneratorImpl.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/StreamGeneratorImpl.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/StreamGeneratorImpl.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,260 @@
+/*
+ * 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.deployment.streamgenerator.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.SoftReference;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.deployment.provider.ArtifactData;
+import org.apache.ace.deployment.provider.DeploymentProvider;
+import org.apache.ace.deployment.streamgenerator.StreamGenerator;
+
+/**
+ * Stream generator for deployment packages. Communicates with a data provider to get the meta data for the streams. Part of the
+ * meta
+ */
+public class StreamGeneratorImpl implements StreamGenerator {
+    private volatile DeploymentProvider m_provider;
+    private volatile ConnectionFactory m_connectionFactory;
+
+    /**
+     * Returns an input stream with the requested deployment package.
+     *
+     * @param id the ID of the package
+     * @param version the version of the package
+     * @return an input stream
+     * @throws java.io.IOException when the stream could not be generated
+     */
+    public InputStream getDeploymentPackage(String id, String version) throws IOException {
+        List<ArtifactData> data = m_provider.getBundleData(id, version);
+        Manifest manifest = new Manifest();
+        Attributes main = manifest.getMainAttributes();
+
+        main.putValue("Manifest-Version", "1.0");
+        main.putValue("DeploymentPackage-SymbolicName", id);
+        main.putValue("DeploymentPackage-Version", version);
+
+        // Note: getEntries() returns a map. This means that the order of the entries
+        // in the manifest is _not_ defined; this should be fine, as far as the
+        // deployment admin spec goes.
+        for (ArtifactData bd : data) {
+            manifest.getEntries().put(bd.getFilename(), bd.getManifestAttributes(false));
+        }
+
+        return DeploymentPackageStream.createStreamForThread(m_connectionFactory, manifest, data.iterator(), false);
+    }
+
+    /**
+     * Returns an input stream with the requested deployment fix package.
+     *
+     * @param id the ID of the package.
+     * @param fromVersion the version of the target.
+     * @param toVersion the version the target should be in after applying the package.
+     * @return an input stream.
+     * @throws java.io.IOException when the stream could not be generated.
+     */
+    public InputStream getDeploymentPackage(String id, String fromVersion, String toVersion) throws IOException {
+        //return execute(new WorkerFixPackage(id, fromVersion, toVersion));
+        List<ArtifactData> data = m_provider.getBundleData(id, fromVersion, toVersion);
+        Manifest manifest = new Manifest();
+        Attributes main = manifest.getMainAttributes();
+
+        main.putValue("Manifest-Version", "1.0");
+        main.putValue("DeploymentPackage-SymbolicName", id);
+        main.putValue("DeploymentPackage-Version", toVersion);
+        main.putValue("DeploymentPackage-FixPack", "[" + fromVersion + "," + toVersion + ")");
+
+        for (ArtifactData bd : data) {
+            manifest.getEntries().put(bd.getFilename(), bd.getManifestAttributes(true));
+        }
+
+        return DeploymentPackageStream.createStreamForThread(m_connectionFactory, manifest, data.iterator(), true);
+    }
+
+    private static final class DeploymentPackageStream extends InputStream {
+        private byte[] m_readBuffer;
+        private byte[] m_buffer;
+        private JarOutputStream m_output;
+        private Iterator<ArtifactData> m_iter;
+        private InputStream m_current = null;
+        private int m_pos = 0;
+        private int m_max = 0;
+        private boolean m_fixPack;
+        
+        private final OutputBuffer m_outputBuffer = new OutputBuffer(this);
+        private final ConnectionFactory m_connectionFactory;
+
+        private DeploymentPackageStream(ConnectionFactory connectionFactory) {
+            this(connectionFactory, 64 * 1024);
+        }
+
+        private DeploymentPackageStream(ConnectionFactory connectionFactory, int bufferSize) {
+            m_connectionFactory = connectionFactory;
+            m_buffer = new byte[bufferSize];
+            m_readBuffer = new byte[bufferSize];
+        }
+
+        private static final ThreadLocal<SoftReference<DeploymentPackageStream>> m_cache = new ThreadLocal<SoftReference<DeploymentPackageStream>>();
+
+        static DeploymentPackageStream createStreamForThread(ConnectionFactory connectionFactory, Manifest man, Iterator<ArtifactData> iter, boolean fixpack) throws IOException {
+            SoftReference<DeploymentPackageStream> ref = m_cache.get();
+            DeploymentPackageStream dps = null;
+            if (ref != null) {
+                dps = ref.get();
+            }
+
+            if (dps == null) {
+                dps = new DeploymentPackageStream(connectionFactory);
+                m_cache.set(new SoftReference<DeploymentPackageStream>(dps));
+            }
+
+            if (dps.isInUse()) {
+                dps = new DeploymentPackageStream(connectionFactory);
+            }
+
+            dps.init(man, iter, fixpack);
+
+            return dps;
+        }
+
+        private boolean isInUse() {
+            return m_output == null;
+        }
+
+        private void init(Manifest man, Iterator<ArtifactData> iter, boolean fixPack) throws IOException {
+            m_max = 0;
+            m_pos = 0;
+            m_output = new JarOutputStream(m_outputBuffer, man);
+            m_output.flush();
+            m_iter = iter;
+            m_fixPack = fixPack;
+            next();
+        }
+
+        private void next() throws IOException {
+            ArtifactData current = (m_iter.hasNext()) ? m_iter.next() : null;
+
+            if (current == null) {
+                m_output.close();
+            }
+            else if (!m_fixPack || current.hasChanged()) {
+                m_current = openStream(current);
+                m_output.putNextEntry(new ZipEntry(current.getFilename()));
+            }
+            else {
+                next();
+            }
+        }
+
+        private InputStream openStream(ArtifactData data) throws IOException {
+            URLConnection conn = m_connectionFactory.createConnection(data.getUrl());
+            return conn.getInputStream();
+        }
+
+        @Override
+        public int read() throws IOException {
+            while (m_pos == m_max) {
+                if (m_current == null) {
+                    if (m_output != null) {
+                        m_output.close();
+                    }
+                    m_output = null;
+                    m_iter = null;
+                    return -1;
+                }
+                m_pos = 0;
+                m_max = 0;
+                int len = m_current.read(m_readBuffer);
+                if (len != -1) {
+                    m_output.write(m_readBuffer, 0, len);
+                    m_output.flush();
+                }
+                else {
+                    try {
+                        m_current.close();
+                    }
+                    catch (Exception ex) {
+                        // Not much we can do
+                    }
+                    m_current = null;
+                    m_output.closeEntry();
+                    m_output.flush();
+                    next();
+                }
+            }
+
+            return m_buffer[m_pos++] & 0xFF;
+        }
+
+        void write(int b) {
+            if (m_max == m_buffer.length) {
+                byte[] tmp = new byte[m_buffer.length + 8192];
+                System.arraycopy(m_buffer, 0, tmp, 0, m_buffer.length);
+                m_buffer = tmp;
+            }
+            m_buffer[m_max++] = (byte) b;
+        }
+
+        @Override
+        public void close() {
+            if (m_output != null) {
+                try {
+                    m_output.close();
+                    m_output = null;
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+            if (m_current != null) {
+                try {
+                    m_current.close();
+                    m_current = null;
+                }
+                catch (Exception ex) {
+                    // Not much we can do
+                }
+            }
+            m_iter = null;
+        }
+
+        private static final class OutputBuffer extends OutputStream {
+            private final DeploymentPackageStream m_stream;
+
+            public OutputBuffer(DeploymentPackageStream stream) {
+                m_stream = stream;
+            }
+
+            @Override
+            public void write(int b) {
+                m_stream.write(b);
+            }
+        }
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/packageinfo?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/packageinfo (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/packageinfo Thu Apr  4 09:43:34 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/Activator.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/Activator.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/Activator.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,192 @@
+/*
+ * 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.deployment.task;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+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.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedServiceFactory;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase implements ManagedServiceFactory {
+
+    private static final String PID_NAME = "org.apache.ace.deployment.task.default.factory";
+
+    private static final String MA_NAME = "ma";
+    private static final String DEFAULT_INTERVAL = "5000";
+
+    private final Map<String, List<Component>> m_instances = new HashMap<String, List<Component>>();
+
+    private volatile DependencyManager m_manager;
+
+    /**
+     * @see org.osgi.service.cm.ManagedServiceFactory#deleted(java.lang.String)
+     */
+    public void deleted(String pid) {
+        List<Component> components;
+        synchronized (m_instances) {
+            components = m_instances.remove(pid);
+        }
+        if (components != null) {
+            for (Component component : components) {
+                m_manager.remove(component);
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.felix.dm.DependencyActivatorBase#destroy(org.osgi.framework.BundleContext, org.apache.felix.dm.DependencyManager)
+     */
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+        // do nothing
+    }
+
+    /**
+     * @see org.osgi.service.cm.ManagedServiceFactory#getName()
+     */
+    public String getName() {
+        return "Deployment Service - default check/update tasks";
+    }
+
+    /**
+     * @see org.apache.felix.dm.DependencyActivatorBase#init(org.osgi.framework.BundleContext, org.apache.felix.dm.DependencyManager)
+     */
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        m_manager = manager;
+
+        List<Component> components = createServices(null);
+        for (Component component : components) {
+            m_manager.add(component);
+        }
+
+        Properties props = new Properties();
+        props.put(Constants.SERVICE_PID, PID_NAME);
+
+        manager.add(createComponent()
+            .setInterface(ManagedServiceFactory.class.getName(), props)
+            .setImplementation(this)
+            );
+    }
+
+    /**
+     * @see org.osgi.service.cm.ManagedServiceFactory#updated(java.lang.String, java.util.Dictionary)
+     */
+    public void updated(String pid, Dictionary dict) throws ConfigurationException {
+        String ma = (String) dict.get(MA_NAME);
+
+        List<Component> components = m_instances.get(pid);
+        if (components == null) {
+            components = createServices(ma);
+            synchronized (m_instances) {
+                m_instances.put(pid, components);
+            }
+            for (Component component : components) {
+                m_manager.add(component);
+            }
+        }
+        else {
+            // TODO do we want to deal with changes here?
+        }
+    }
+
+    /**
+     * Creates the check/update task components for the given management agent name.
+     * 
+     * @param ma the name of the management agent to create the service for, can be <code>null</code>.
+     * @return an array with {@link Component} instances for the different tasks, never <code>null</code>.
+     */
+    private List<Component> createServices(String ma) {
+        Dictionary updateProperties = new Properties();
+        Dictionary checkProperties = new Properties();
+        Dictionary deploymentProperties = new Properties();
+        
+        String updateSchedulerName = DeploymentUpdateTask.class.getName();
+        String updateDescription = "Task that synchronizes the artifacts (bundles, resources) installed on this target with the server.";
+        
+        String checkSchedulerName = DeploymentCheckTask.class.getName();
+        String checkDescription = "Task that checks for updates of artifacts installed on this target with the server.";
+        
+        String deploymentFilter = "(" + Constants.OBJECTCLASS + "=" + DeploymentService.class.getName() + ")";
+
+        if (ma == null || "".equals(ma)) {
+            deploymentFilter = String.format("(&%s(!(%s=*)))", deploymentFilter, MA_NAME);
+        }
+        else {
+            updateSchedulerName = "ma=" + ma + ";name=" + updateSchedulerName;
+            updateDescription = "Task that synchronizes the artifacts (bundles, resources) installed on this target with the server with ma=" + ma + ".";
+            
+            checkSchedulerName = "ma=" + ma + ";name=" + checkSchedulerName;
+            checkDescription = "Task that checks for updates of artifacts installed on this target with the server with ma=" + ma + ".";
+            
+            deploymentFilter = String.format("(&%s(%s=%s))", deploymentFilter, MA_NAME, ma);
+            
+            updateProperties.put(MA_NAME, ma);
+            checkProperties.put(MA_NAME, ma);
+            deploymentProperties.put(MA_NAME, ma);
+        }
+
+        List<Component> result = new ArrayList<Component>();
+
+        updateProperties.put(SchedulerConstants.SCHEDULER_NAME_KEY, updateSchedulerName);
+        updateProperties.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, updateDescription);
+        updateProperties.put(SchedulerConstants.SCHEDULER_RECIPE, DEFAULT_INTERVAL);
+
+        DeploymentUpdateTask updateTask = new DeploymentUpdateTask();
+
+        Component updateTaskComponent =
+            createComponent()
+                .setInterface(Runnable.class.getName(), updateProperties)
+                .setImplementation(updateTask)
+                .add(createServiceDependency().setService(DeploymentService.class, deploymentFilter).setRequired(true))
+                .add(createServiceDependency().setService(LogService.class).setRequired(false));
+
+        checkProperties.put(SchedulerConstants.SCHEDULER_NAME_KEY, checkSchedulerName);
+        checkProperties.put(SchedulerConstants.SCHEDULER_DESCRIPTION_KEY, checkDescription);
+        checkProperties.put(SchedulerConstants.SCHEDULER_RECIPE, DEFAULT_INTERVAL);
+
+        result.add(updateTaskComponent);
+
+        DeploymentCheckTask checkTask = new DeploymentCheckTask();
+
+        Component checkTaskComponent =
+            createComponent()
+                .setInterface(Runnable.class.getName(), checkProperties)
+                .setImplementation(checkTask)
+                .add(createServiceDependency().setService(DeploymentService.class, deploymentFilter).setRequired(true))
+                .add(createServiceDependency().setService(EventAdmin.class).setRequired(false))
+                .add(createServiceDependency().setService(LogService.class).setRequired(false));
+
+        result.add(checkTaskComponent);
+
+        return result;
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentCheckTask.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentCheckTask.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentCheckTask.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentCheckTask.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,87 @@
+/*
+ * 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.deployment.task;
+
+import java.net.MalformedURLException;
+import java.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.ace.deployment.service.DeploymentService;
+import org.osgi.framework.Version;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.log.LogService;
+
+/**
+ * Task that checks for a new version and sends out an event if there is a new version. It does not actually
+ * download or install it.
+ */
+public class DeploymentCheckTask implements Runnable {
+
+    private static final String TOPIC_UPDATE_AVAILABLE = "org/apache/ace/deployment/UPDATEAVAILABLE";
+
+    private volatile LogService m_log;
+    private volatile EventAdmin m_eventAdmin;
+    private volatile DeploymentService m_service;
+
+    /**
+     * When run a check is made if a higher version is available on the remote. If so, send out an event.
+     */
+    public void run() {
+        try {
+            Version localVersion = m_service.getHighestLocalVersion();
+            Version remoteVersion = m_service.getHighestRemoteVersion();
+
+            if (remoteVersion == 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: " + localVersion);
+                return;
+            }
+
+            // ACE-220: lower log level; not of real interest...
+            m_log.log(LogService.LOG_DEBUG, "Highest remote: " + remoteVersion + " / Highest local: " + localVersion);
+
+            if ((remoteVersion != null) && ((localVersion == null) || (remoteVersion.compareTo(localVersion) > 0))) {
+                m_eventAdmin.postEvent(createEvent(localVersion, remoteVersion));
+            }
+        }
+        catch (MalformedURLException e) {
+            m_log.log(LogService.LOG_ERROR, "Error creating endpoint url", e);
+        }
+        catch (Exception e) {
+            m_log.log(LogService.LOG_ERROR, "Error checking for update", e);
+        }
+    }
+
+    /**
+     * Creates an event for notifying listeners that a new version can be installed.
+     * 
+     * @param localVersion the highest local version;
+     * @param remoteVersion the higest remote version.
+     * @return a new {@link Event} instance, never <code>null</code>.
+     */
+    private Event createEvent(Version localVersion, Version remoteVersion) {
+        Properties properties = new Properties();
+        properties.put("deploymentpackage.localversion", ((localVersion == null) ? Version.emptyVersion : localVersion));
+        properties.put("deploymentpackage.remoteversion", remoteVersion);
+
+        return new Event(TOPIC_UPDATE_AVAILABLE, (Dictionary) properties);
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentUpdateTask.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentUpdateTask.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentUpdateTask.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/task/DeploymentUpdateTask.java Thu Apr  4 09:43:34 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.deployment.task;
+
+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;
+
+/**
+ * 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.deployment/src/org/apache/ace/deployment/util/test/BundleStreamGenerator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/BundleStreamGenerator.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/BundleStreamGenerator.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/BundleStreamGenerator.java Thu Apr  4 09:43:34 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.deployment.util.test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ace.deployment.provider.ArtifactData;
+import org.osgi.framework.Constants;
+
+public class BundleStreamGenerator {
+
+    public static Manifest getBundleManifest(String symbolicname, String version, Map<String, String> additionalHeaders) {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().putValue("Manifest-Version", "1");
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, symbolicname);
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, version.toString());
+        for (Map.Entry<String, String> entry : additionalHeaders.entrySet()) {
+            manifest.getMainAttributes().putValue(entry.getKey(), entry.getValue());
+        }
+        return manifest;
+    }
+
+    public static void generateBundle(ArtifactData data, Map<String, String> additionalHeaders) throws IOException {
+        OutputStream bundleStream = null;
+        try {
+            File dataFile = new File(data.getUrl().toURI());
+            OutputStream fileStream = new FileOutputStream(dataFile);
+            bundleStream = new JarOutputStream(fileStream, getBundleManifest(data.getSymbolicName(), data.getVersion(), additionalHeaders));
+            bundleStream.flush();
+        } catch (URISyntaxException e) {
+            throw new IOException();
+        } finally {
+            if (bundleStream != null) {
+                bundleStream.close();
+            }
+        }
+    }
+
+    public static void generateBundle(ArtifactData data) throws IOException {
+        generateBundle(data, new HashMap<String, String>());
+    }
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestData.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestData.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestData.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestData.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,85 @@
+/*
+ * 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.deployment.util.test;
+
+import java.net.URL;
+import java.util.jar.Attributes;
+
+import org.apache.ace.deployment.provider.ArtifactData;
+
+public class TestData implements ArtifactData {
+    private final String m_fileName;
+    private final String m_symbolicName;
+    private final URL m_url;
+    private final String m_version;
+    private final boolean m_changed;
+
+    public TestData(String fileName, String symbolicName, URL url, String version, boolean changed) {
+        m_fileName = fileName;
+        m_symbolicName = symbolicName;
+        m_url = url;
+        m_version = version;
+        m_changed = changed;
+    }
+
+    public boolean hasChanged() {
+        return m_changed;
+    }
+
+    public String getFilename() {
+        return m_fileName;
+    }
+
+    public String getSymbolicName() {
+        return m_symbolicName;
+    }
+
+    public URL getUrl() {
+        return m_url;
+    }
+
+    public String getVersion() {
+        return m_version;
+    }
+
+    public String getDirective() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Attributes getManifestAttributes(boolean fixPackage) {
+        Attributes a = new Attributes();
+        a.putValue("Bundle-SymbolicName", getSymbolicName());
+        a.putValue("Bundle-Version", getVersion());
+        return a;
+    }
+
+    public String getProcessorPid() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public boolean isBundle() {
+        return true;
+    }
+
+    public boolean isCustomizer() {
+        return false;
+    }
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestProvider.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestProvider.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestProvider.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/TestProvider.java Thu Apr  4 09:43:34 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.deployment.util.test;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.ace.deployment.provider.ArtifactData;
+import org.apache.ace.deployment.provider.DeploymentProvider;
+
+public class TestProvider implements DeploymentProvider {
+    private List<ArtifactData> m_collection;
+    private List<String> m_versions;
+
+    public TestProvider() throws Exception {
+        m_collection = new ArrayList<ArtifactData>();
+        m_versions = new ArrayList<String>();
+    }
+
+    public void addData(String fileName, String symbolicName, URL url, String version) {
+        addData(fileName, symbolicName, url, version, true);
+    }
+
+    public void addData(String fileName, String symbolicName, URL url, String version, boolean changed) {
+        m_collection.add(new TestData(fileName, symbolicName, url, version, changed));
+        m_versions.add(version);
+    }
+
+    public List<ArtifactData> getBundleData(String id, String version) {
+        return m_collection;
+    }
+
+    public List<ArtifactData> getBundleData(String id, String versionFrom, String versionTo) {
+        return m_collection;
+    }
+
+    public List<String> getVersions(String id) throws IllegalArgumentException {
+        Collections.sort(m_versions);
+        return m_versions;
+    }
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/packageinfo?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/packageinfo (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/util/test/packageinfo Thu Apr  4 09:43:34 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/streamgenerator.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/streamgenerator.bnd?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/streamgenerator.bnd (added)
+++ ace/trunk/org.apache.ace.deployment/streamgenerator.bnd Thu Apr  4 09:43:34 2013
@@ -0,0 +1,4 @@
+Private-Package: org.apache.ace.deployment.streamgenerator.impl
+Bundle-Activator: org.apache.ace.deployment.streamgenerator.impl.Activator
+Export-Package: org.apache.ace.deployment.streamgenerator
+Bundle-Version: 1.0.0

Added: ace/trunk/org.apache.ace.deployment/task.base.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/task.base.bnd?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/task.base.bnd (added)
+++ ace/trunk/org.apache.ace.deployment/task.base.bnd Thu Apr  4 09:43:34 2013
@@ -0,0 +1,4 @@
+Private-Package: org.apache.ace.deployment.service.impl
+Bundle-Activator: org.apache.ace.deployment.service.impl.Activator
+Export-Package: org.apache.ace.deployment.service
+Bundle-Version: 1.0.0

Added: ace/trunk/org.apache.ace.deployment/task.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/task.bnd?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/task.bnd (added)
+++ ace/trunk/org.apache.ace.deployment/task.bnd Thu Apr  4 09:43:34 2013
@@ -0,0 +1,3 @@
+Private-Package: org.apache.ace.deployment.task
+Bundle-Activator: org.apache.ace.deployment.task.Activator
+Bundle-Version: 1.0.0

Added: ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/DeploymentTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/DeploymentTest.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/DeploymentTest.java (added)
+++ ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/DeploymentTest.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,92 @@
+/*
+ * 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.deployment;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import java.io.InputStream;
+
+import org.apache.ace.deployment.deploymentadmin.DeploymentAdminDeployer;
+import org.apache.ace.test.utils.TestUtils;
+import org.osgi.framework.Version;
+import org.osgi.service.deploymentadmin.DeploymentAdmin;
+import org.osgi.service.deploymentadmin.DeploymentPackage;
+import org.osgi.service.log.LogService;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+public class DeploymentTest {
+
+    private DeploymentAdminDeployer m_deploymentAdminDeployer;
+    private DeploymentPackage m_mockDeploymentPackage;
+
+    private static final String MOCK_NAME = "MockName";
+    private static final Version MOCK_VERSION = new Version("0.1");
+
+    @BeforeTest(alwaysRun = true)
+    protected void setUp() throws Exception {
+        m_deploymentAdminDeployer = new DeploymentAdminDeployer();
+        TestUtils.configureObject(m_deploymentAdminDeployer, LogService.class);
+        Object mockDeploymentAdmin = TestUtils.createMockObjectAdapter(DeploymentAdmin.class, new MockDeploymentAdmin());
+        TestUtils.configureObject(m_deploymentAdminDeployer, DeploymentAdmin.class, mockDeploymentAdmin);
+        m_mockDeploymentPackage = TestUtils.createMockObjectAdapter(DeploymentPackage.class, new MockDeploymentPackage());
+    }
+
+    @Test(groups = { UNIT })
+    public void testDeployment() throws Exception {
+        Object deploymentPackage = m_deploymentAdminDeployer.install(null);
+        assert m_deploymentAdminDeployer.getName(deploymentPackage).equals(MOCK_NAME) : "Installation of mock deployment package failed";
+        assert m_deploymentAdminDeployer.getVersion(deploymentPackage).equals(MOCK_VERSION) : "Installation of mock deployment package failed";
+        assert ((DeploymentPackage) m_deploymentAdminDeployer.list()[0]).getName().equals(MOCK_NAME) : "List result does not match expected result";
+        boolean exceptionthrown = false;
+        try {
+            m_deploymentAdminDeployer.getName(new String("illegalargument"));
+        } catch (IllegalArgumentException iae) {
+            exceptionthrown = true;
+        }
+        assert exceptionthrown : "Illegal argument for getName() did not throw exception";
+        exceptionthrown = false;
+        try {
+            m_deploymentAdminDeployer.getVersion(new String("illegalargument"));
+        } catch (IllegalArgumentException iae) {
+            exceptionthrown = true;
+        }
+        assert exceptionthrown : "Illegal argument for getVersion() did not throw exception";
+    }
+
+    private class MockDeploymentAdmin {
+        public DeploymentPackage installDeploymentPackage(InputStream is) {
+            return m_mockDeploymentPackage;
+        }
+
+        public DeploymentPackage[] listDeploymentPackages() {
+            return new DeploymentPackage[] {m_mockDeploymentPackage};
+        }
+    }
+
+    private class MockDeploymentPackage {
+        public String getName() {
+            return MOCK_NAME;
+        }
+
+        public Version getVersion() {
+            return MOCK_VERSION;
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/filebased/FileBasedProviderTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/filebased/FileBasedProviderTest.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/filebased/FileBasedProviderTest.java (added)
+++ ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/filebased/FileBasedProviderTest.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,292 @@
+/*
+ * 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.deployment.provider.filebased;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.ace.deployment.provider.ArtifactData;
+import org.apache.ace.deployment.provider.impl.ArtifactDataImpl;
+import org.apache.ace.deployment.util.test.BundleStreamGenerator;
+import org.apache.ace.test.utils.FileUtils;
+import org.apache.ace.test.utils.TestUtils;
+import org.osgi.service.log.LogService;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * This test class tests the FileBasedProvider class.
+ * This class implements 2 backend interfaces,
+ * and both are tested here.
+ */
+public class FileBasedProviderTest {
+
+    private FileBasedProvider m_backend;
+
+    private File m_tempDirectory;
+
+    private final String VERSION1 = "1.0.0";
+    private final String VERSION2 = "2.0.0";
+    private final String VERSION3 = "3.0.0";
+    private final String VERSION4 = "4.0.0";
+    private final String INVALIDVERSION = "Invalid.version.directory";
+
+    private final String TARGET = "target";
+    private final String MULTIPLEVERSIONTARGET = "multi-version-target";
+    private final String INVALIDVERSIONTARGET = "illegal-version-target";
+    private ArtifactData BUNDLE1;
+    private ArtifactData BUNDLE3;
+    private ArtifactData BUNDLE4;
+    private ArtifactData BUNDLE4_1;
+    private ArtifactData BUNDLE5;
+    private ArtifactData BUNDLE3_2;
+    private ArtifactData BUNDLE4_2;
+
+    @SuppressWarnings("serial")
+    @BeforeTest(alwaysRun = true)
+    protected void setUp() throws Exception {
+
+        // first create a file
+        m_tempDirectory = FileUtils.createTempFile(null);
+        // and make a directory with that name.
+        m_tempDirectory.mkdir();
+        setupSampleData();
+
+        m_backend = new FileBasedProvider();
+        TestUtils.configureObject(m_backend, LogService.class);
+        m_backend.updated(new Properties() {{put("BaseDirectoryName", m_tempDirectory.getAbsolutePath());}});
+    }
+
+    /**
+     * make a bundle with the given symbolic name and version in the given file.
+     */
+    private ArtifactData generateBundle(File file, String symbolicName, String version) throws Exception {
+        ArtifactData bundle = new ArtifactDataImpl(file.getName(), symbolicName, version, file.toURI().toURL(), false);
+        BundleStreamGenerator.generateBundle(bundle);
+        return bundle;
+    }
+
+    /**
+     * Create the test targets, versions and testbundles..
+     */
+    private void setupSampleData() throws Exception {
+        File target = new File(m_tempDirectory, TARGET);
+        target.mkdirs();
+        File targetVersion1 = new File(target, VERSION1);
+        targetVersion1.mkdirs();
+        BUNDLE1 = generateBundle(FileUtils.createTempFile(targetVersion1), "Bundle1", "1.0.0");
+
+        File illegalVersionTarget = new File(m_tempDirectory, INVALIDVERSIONTARGET);
+        illegalVersionTarget.mkdirs();
+        File faultyVersion = new File(illegalVersionTarget, INVALIDVERSION);
+        faultyVersion.mkdirs();
+        // this bundle should never be accessed
+        generateBundle(FileUtils.createTempFile(faultyVersion), "Bundle2", "2.0.0");
+
+        File multipleVersionTarget = new File(m_tempDirectory, MULTIPLEVERSIONTARGET);
+        multipleVersionTarget.mkdir();
+        File multipleVersionTargetVersion1 = new File(multipleVersionTarget, VERSION1);
+        multipleVersionTargetVersion1.mkdir();
+        BUNDLE3 = generateBundle(FileUtils.createTempFile(multipleVersionTargetVersion1), "Bundle3", "3.0.0");
+        BUNDLE4 = generateBundle(FileUtils.createTempFile(multipleVersionTargetVersion1), "Bundle4", "4.0.0");
+        File multipleVersionTargetVersion2 = new File(multipleVersionTarget, VERSION2);
+        multipleVersionTargetVersion2.mkdir();
+        BUNDLE4_1 = generateBundle(FileUtils.createTempFile(multipleVersionTargetVersion2), "Bundle4", "4.1.0");
+        BUNDLE5 = generateBundle(FileUtils.createTempFile(multipleVersionTargetVersion2), "Bundle5", "5.0.0");
+        File multipleVersionTargetVersion3 = new File(multipleVersionTarget, VERSION3);
+        multipleVersionTargetVersion3.mkdir();
+        File multipleVersionTargetVersion4 = new File(multipleVersionTarget, VERSION4);
+        multipleVersionTargetVersion4.mkdir();
+        BUNDLE3_2 = generateBundle(FileUtils.createTempFile(multipleVersionTargetVersion4), "Bundle3", "3.0.0");
+        BUNDLE4_2 = generateBundle(FileUtils.createTempFile(multipleVersionTargetVersion4), "Bundle4", "5.0.0");
+    }
+
+    /**
+     * See if the getVersions() methods normal output works
+     */
+    @Test(groups = { UNIT })
+    public void testGetVersion() {
+        List<String> versions = m_backend.getVersions(TARGET);
+        assert versions.size() == 1 : "Expected one version to be found, but found " + versions.size();
+        assert versions.get(0).equals(VERSION1) : "Expected version " + VERSION1 + " but found " + versions.get(0);
+    }
+
+    /**
+     * Test the getVersions method with an illegal version (not in org.osgi.framework.Version format)
+     */
+    @Test(groups = { UNIT })
+    public void testIllegalVersion() {
+        // an illegal version should be silently ignored
+        List<String> versions = m_backend.getVersions(INVALIDVERSIONTARGET);
+        assert versions.isEmpty() : "Expected no versions to be found, but found " + versions.size();
+    }
+
+    /**
+     * Test with multiple versions. It expects all versions in an ascending order.
+     */
+    @Test(groups = { UNIT })
+    public void testMultipleVersions() {
+        List<String> versions = m_backend.getVersions(MULTIPLEVERSIONTARGET);
+        assert versions.size() == 4 : "Expected three version to be found, but found " + versions.size();
+        // all versions should be in ascending order
+        assert versions.get(0).equals(VERSION1) : "Expected version " + VERSION1 + " but found " + versions.get(0);
+        assert versions.get(1).equals(VERSION2) : "Expected version " + VERSION2 + " but found " + versions.get(1);
+        assert versions.get(2).equals(VERSION3) : "Expected version " + VERSION3 + " but found " + versions.get(2);
+        assert versions.get(3).equals(VERSION4) : "Expected version " + VERSION4 + " but found " + versions.get(3);
+    }
+
+    /**
+     * Test the getBundleData for a single version, returning a single bundle
+     */
+    @Test(groups = { UNIT })
+    public void testSingleBundleSingleVersionBundleData() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(TARGET, VERSION1);
+        assert bundleData.size() == 1 : "Expected one bundle to be found, but found " + bundleData.size();
+        assert bundleData.contains(BUNDLE1) : "Expected to find bundle " + BUNDLE1.getSymbolicName();
+    }
+
+    /**
+     * Test the getBundleData for a single version, returning a multiple bundles
+     */
+    @Test(groups = { UNIT })
+    public void testMultipleBundleSingleVersionBundleData() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(MULTIPLEVERSIONTARGET, VERSION1);
+        assert bundleData.size() == 2 : "Expected two bundle to be found, but found " + bundleData.size();
+        assert bundleData.contains(BUNDLE3) : "Expected to find bundle " + BUNDLE3.getSymbolicName();
+        assert bundleData.contains(BUNDLE4) : "Expected to find bundle " + BUNDLE4.getSymbolicName();
+    }
+
+    /**
+     * Test the getBundleData with an illegal version (i.e. a version that doesn't exist)
+     */
+    @Test(groups = { UNIT })
+    public void testInvalidVersionBundleData() {
+        try {
+            m_backend.getBundleData(INVALIDVERSIONTARGET, INVALIDVERSION);
+            assert false : "Expected an error because version " + INVALIDVERSION + " doesn't exist for target" + INVALIDVERSIONTARGET;
+        } catch (IllegalArgumentException iae) {
+            // expected, because the version doesn't exist
+        }
+    }
+
+    /**
+     * Test the getBundleData for a two versions, returning a single bundle that hasn't changed
+     */
+    @Test(groups = { UNIT })
+    public void testSingleUnchangedBundleMultipleVersions() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(TARGET, VERSION1, VERSION1);
+        assert bundleData.size() == 1 : "Expect one bundle, got " + bundleData.size();
+        Iterator<ArtifactData> it = bundleData.iterator();
+        while(it.hasNext()) {
+            ArtifactData data = it.next();
+            assert !data.hasChanged() : "The data should not have been changed.";
+        }
+    }
+
+    /**
+     * Test the getBundleData for a two versions, returning multiple bundles that haven't changed
+     */
+    @Test(groups = { UNIT })
+    public void testMultipleBundlesMultipleVersions() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(MULTIPLEVERSIONTARGET, VERSION1, VERSION1);
+        assert bundleData.size() == 2 : "Expected two bundle to be found, but found " + bundleData.size();
+        Iterator<ArtifactData> it = bundleData.iterator();
+        while(it.hasNext()) {
+            ArtifactData data = it.next();
+            assert !data.hasChanged() : "The data should not have been changed.";
+        }
+    }
+
+    /**
+     * Test the getBundleData for a two versions, where in the second version a bundle is removed
+     */
+    @Test(groups = { UNIT })
+    public void testRemovedBundleMultipleVersions() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(MULTIPLEVERSIONTARGET, VERSION1, VERSION3);
+        assert bundleData.size() == 0 : "Expected zero bundle to be found, but found " + bundleData.size();
+    }
+
+    /**
+     * Test the getBundleData for a two versions, where in the second version a bundle is added
+     */
+    @Test(groups = { UNIT })
+    public void testAddedBundleMultipleVersions() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(MULTIPLEVERSIONTARGET, VERSION3, VERSION1);
+        assert bundleData.size() == 2 : "Expected two bundle to be found, but found " + bundleData.size();
+        Iterator<ArtifactData> it = bundleData.iterator();
+        while(it.hasNext()) {
+            ArtifactData data = it.next();
+            assert data.hasChanged() : "The data should have been changed.";
+        }
+    }
+
+    /**
+     * Test the getBundleData for a two versions, where in the second version one bundle has changed and another hasn't
+     */
+    @Test(groups = { UNIT })
+    public void testSingleChangedBundleMultipleVersions() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(MULTIPLEVERSIONTARGET, VERSION1, VERSION4);
+        assert bundleData.size() == 2 : "Expected one bundle to be found, but found " + bundleData.size();
+        Iterator<ArtifactData> it = bundleData.iterator();
+        while(it.hasNext()) {
+            ArtifactData data = it.next();
+            if (data.equals(BUNDLE3_2)) {
+                assert !data.hasChanged() : "The data should not have been changed.";
+            } else if (data.equals(BUNDLE4_2)) {
+                assert data.hasChanged() : "The data should have been changed.";
+            } else {
+                assert false : "Unknown bundle found";
+            }
+        }
+    }
+
+    /**
+     * Test the getBundleData for a two versions, where two bundles have changed
+     */
+    @Test(groups = { UNIT })
+    public void testMultipleChangedBundlesMultipleVersions() {
+        Collection<ArtifactData> bundleData = m_backend.getBundleData(MULTIPLEVERSIONTARGET, VERSION1, VERSION2);
+        assert bundleData.size() == 2 : "Expected one bundle to be found, but found " + bundleData.size();
+        Iterator<ArtifactData> it = bundleData.iterator();
+        while(it.hasNext()) {
+            ArtifactData data = it.next();
+            if (data.equals(BUNDLE4_1)) {
+                assert data.hasChanged() : "The data should have been changed.";
+            } else if (data.equals(BUNDLE5)) {
+                assert data.hasChanged() : "The data should have been changed.";
+            } else {
+                assert false : "Unknown bundle found";
+            }
+        }
+    }
+
+
+    @AfterTest(alwaysRun = true)
+    public void tearDown() throws Exception {
+        FileUtils.removeDirectoryWithContent(m_tempDirectory);
+    }
+
+
+}

Added: ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandlerTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandlerTest.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandlerTest.java (added)
+++ ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandlerTest.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,371 @@
+/*
+ * 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.deployment.provider.repositorybased;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.ace.deployment.provider.repositorybased.BaseRepositoryHandler.XmlDeploymentArtifact;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Test cases for {@link BaseRepositoryHandler}.
+ */
+public class BaseRepositoryHandlerTest {
+
+    private static final String TAGS_TAG = "tags";
+    private static final String VERSION_TAG = "version";
+    private static final String TARGETID_TAG = "targetID";
+    private static final String ARTIFACTS_TAG = "artifacts";
+    private static final String ARTIFACT_TAG = "deploymentArtifact";
+    private static final String URL_TAG = "url";
+    private static final String DIRECTIVES_TAG = "directives";
+    private static final String ATTRIBUTES_TAG = "attributes";
+    private static final String DEPLOYMENTVERSION_TAG = "deploymentversion";
+
+    private static final String TARGET = "target";
+    private static final String MULTIPLEVERSIONTARGET = "multi-version-target";
+    private static final String EMPTYVERSIONTARGET = "empty-version-target";
+
+    private static final String VERSION1 = "1.0.0";
+    private static final String VERSION2 = "2.0.0";
+    private static final String VERSION3 = "3.0.0";
+    private static final String VERSION3_2 = "3.2.0";
+    private static final String VERSION4 = "4.0.0";
+    private static final String VERSION4_1 = "4.1.0";
+    private static final String VERSION4_2 = "4.2.0";
+    private static final String VERSION5 = "5.0.0";
+
+    public static final String KEY_SYMBOLICNAME = Constants.BUNDLE_SYMBOLICNAME;
+    public static final String KEY_NAME = Constants.BUNDLE_NAME;
+    public static final String KEY_VERSION = Constants.BUNDLE_VERSION;
+    public static final String KEY_VENDOR = Constants.BUNDLE_VENDOR;
+    public static final String KEY_RESOURCE_PROCESSOR_PID = "Deployment-ProvidesResourceProcessor";
+
+    public static final String MIMETYPE = "application/vnd.osgi.bundle";
+
+    
+    private InputStream m_inputStream;
+    private SAXParser m_parser;
+
+    @BeforeMethod(alwaysRun = true)
+    protected void setUp() throws Exception {
+        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+        m_parser = saxParserFactory.newSAXParser();
+
+        String xml = generateValidTestXml();
+        m_inputStream = new ByteArrayInputStream(xml.getBytes());
+    }
+
+    /**
+     * Tests that a deployment package with a single version can be parsed & found.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherSingleVersionOk() throws Exception {
+        DeploymentPackageVersionCollector handler = new DeploymentPackageVersionCollector(TARGET);
+
+        m_parser.parse(m_inputStream, handler);
+
+        List<Version> versions = handler.getVersions();
+        assert versions.size() == 1 : "Expected a single version to be found!";
+
+        assert Version.parseVersion(VERSION1).equals(versions.get(0)) : "Expected version1 to be found!";
+    }
+
+    /**
+     * Tests that the single artifact of a deployment package with a single version can be parsed & found.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherSingleArtifactOk() throws Exception {
+        DeploymentArtifactCollector handler = new DeploymentArtifactCollector(TARGET, VERSION1);
+
+        m_parser.parse(m_inputStream, handler);
+
+        List<XmlDeploymentArtifact>[] artifacts = handler.getArtifacts();
+        assert artifacts.length == 1 : "Expected a single artifact to be found!";
+        assert artifacts[0].size() == 1 : "Expected a single artifact to be found!";
+
+        XmlDeploymentArtifact artifact1 = artifacts[0].get(0);
+        assert new URL("file:///bundle1").equals(artifact1.getUrl()) : "Expected 'file:///bundle1' URL to be found!";
+        assert artifact1.getDirective().size() == 2 : "Expected two directives to be found!";
+        assert "bundle1".equals(artifact1.getDirective().get(KEY_SYMBOLICNAME)) : "Expected correct symbolic name to be found!";
+        assert "1.0.0".equals(artifact1.getDirective().get(KEY_VERSION)) : "Expected correct bundle version to be found!";
+    }
+
+    /**
+     * Tests that a deployment package with multiple versions can be parsed & found.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherMultipleVersionOk() throws Exception {
+        DeploymentPackageVersionCollector handler = new DeploymentPackageVersionCollector(MULTIPLEVERSIONTARGET);
+
+        m_parser.parse(m_inputStream, handler);
+
+        List<Version> versions = handler.getVersions();
+        assert versions.size() == 4 : "Expected four versions to be found!";
+
+        assert Version.parseVersion(VERSION1).equals(versions.get(0)) : "Expected version1 to be found!";
+        assert Version.parseVersion(VERSION2).equals(versions.get(1)) : "Expected version2 to be found!";
+        assert Version.parseVersion(VERSION3).equals(versions.get(2)) : "Expected version3 to be found!";
+        assert Version.parseVersion(VERSION4).equals(versions.get(3)) : "Expected version4 to be found!";
+    }
+
+    /**
+     * Tests that multiple artifacts of a deployment package with a single version can be parsed & found.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherMultipleArtifactsOfMultipleVersionTargetOk() throws Exception {
+        DeploymentArtifactCollector handler = new DeploymentArtifactCollector(MULTIPLEVERSIONTARGET, VERSION2);
+
+        m_parser.parse(m_inputStream, handler);
+
+        List<XmlDeploymentArtifact>[] artifacts = handler.getArtifacts();
+        assert artifacts.length == 1 : "Expected two artifacts to be found!";
+        assert artifacts[0].size() == 2 : "Expected two artifacts to be found!";
+
+        XmlDeploymentArtifact artifact1 = artifacts[0].get(0);
+        assert new URL("file:///bundle4.1").equals(artifact1.getUrl()) : "Expected 'file:///bundle4.1' URL to be found!";
+        assert artifact1.getDirective().size() == 2 : "Expected two directives to be found!";
+        assert "bundle4.1".equals(artifact1.getDirective().get(KEY_SYMBOLICNAME)) : "Expected correct symbolic name to be found!";
+        assert "4.1.0".equals(artifact1.getDirective().get(KEY_VERSION)) : "Expected correct bundle version to be found!";
+
+        XmlDeploymentArtifact artifact2 = artifacts[0].get(1);
+        assert new URL("file:///bundle5").equals(artifact2.getUrl()) : "Expected 'file:///bundle5' URL to be found!";
+        assert artifact2.getDirective().size() == 2 : "Expected two directives to be found!";
+        assert "bundle5".equals(artifact2.getDirective().get(KEY_SYMBOLICNAME)) : "Expected correct symbolic name to be found!";
+        assert "5.0.0".equals(artifact2.getDirective().get(KEY_VERSION)) : "Expected correct bundle version to be found!";
+    }
+
+    /**
+     * Tests that single artifact of a deployment package with multiple versions can be parsed & found.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherSingleArtifactsOfMultipleVersionTargetOk() throws Exception {
+        DeploymentArtifactCollector handler = new DeploymentArtifactCollector(MULTIPLEVERSIONTARGET, VERSION3);
+
+        m_parser.parse(m_inputStream, handler);
+
+        List<XmlDeploymentArtifact>[] artifacts = handler.getArtifacts();
+        assert artifacts.length == 1 : "Expected a single artifact to be found!";
+        assert artifacts[0].size() == 1 : "Expected a single artifact to be found!";
+
+        XmlDeploymentArtifact artifact1 = artifacts[0].get(0);
+        assert new URL("file:///bundle4").equals(artifact1.getUrl()) : "Expected 'file:///bundle4' URL to be found!";
+        assert artifact1.getDirective().size() == 2 : "Expected two directives to be found!";
+        assert "bundle4".equals(artifact1.getDirective().get(KEY_SYMBOLICNAME)) : "Expected correct symbolic name to be found!";
+        assert "4.0.0".equals(artifact1.getDirective().get(KEY_VERSION)) : "Expected correct bundle version to be found!";
+    }
+
+    /**
+     * Tests that non existing artifacts of a deployment package with multiple versions can be parsed.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherNonExistingArtifactsOfMultipleVersionTargetOk() throws Exception {
+        DeploymentArtifactCollector handler = new DeploymentArtifactCollector(EMPTYVERSIONTARGET, VERSION2);
+
+        m_parser.parse(m_inputStream, handler);
+
+        List<XmlDeploymentArtifact>[] artifacts = handler.getArtifacts();
+        assert artifacts.length == 1 : "Expected a single artifact to be found!";
+        assert artifacts[0].isEmpty() : "Expected no deployment artifacts to be found!";
+    }
+
+    /**
+     * Tests that requesting the artifacts of a deployment package with an invalid version can be done.
+     * 
+     * @throws Exception not part of this test case.
+     */
+    @Test(groups = { UNIT })
+    public void testGatherArtifactsOfMultipleVersionTargetWithInvalidVersionOk() throws Exception {
+        DeploymentArtifactCollector handler = new DeploymentArtifactCollector(EMPTYVERSIONTARGET, VERSION3);
+        
+        m_parser.parse(m_inputStream, handler);
+        
+        try {
+            handler.getArtifacts();
+            
+            assert false : "Expected no deployment artifacts to be found!";
+        }
+        catch (IllegalArgumentException e) {
+            // Ok; expected...
+        }
+    }
+
+    /**
+     * @return a valid repository XML; never <code>null</code>.
+     */
+    private String generateValidTestXml() {
+        Document doc = null;
+        try {
+            DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+            doc = docBuilder.newDocument();
+        }
+        catch (ParserConfigurationException e) {
+            // Should not happen
+            e.printStackTrace();
+        }
+
+        // create the root element
+        Element root = doc.createElement("repository");
+        doc.appendChild(root);
+
+        // create the versions element
+        Element versions = doc.createElement("deploymentversions");
+        root.appendChild(versions);
+
+        // create deployment versions
+        versions.appendChild(generateDeploymentVersion(doc, TARGET, VERSION1, new String[] { "file:///bundle1",
+            KEY_SYMBOLICNAME, "bundle1", KEY_VERSION, VERSION1 }));
+
+        versions.appendChild(generateDeploymentVersion(doc, MULTIPLEVERSIONTARGET, VERSION1, new String[] {
+            "file:///bundle3", KEY_SYMBOLICNAME, "bundle3", KEY_VERSION, VERSION3 },
+            new String[] { "file:///bundle4", KEY_SYMBOLICNAME, "bundle4", KEY_VERSION,
+                VERSION4 }));
+        versions.appendChild(generateDeploymentVersion(doc, MULTIPLEVERSIONTARGET, VERSION2, new String[] {
+            "file:///bundle4.1", KEY_SYMBOLICNAME, "bundle4.1", KEY_VERSION, VERSION4_1 },
+            new String[] { "file:///bundle5", KEY_SYMBOLICNAME, "bundle5", KEY_VERSION,
+                VERSION5 }));
+        versions.appendChild(generateDeploymentVersion(doc, MULTIPLEVERSIONTARGET, VERSION3, new String[] {
+            "file:///bundle4", KEY_SYMBOLICNAME, "bundle4", KEY_VERSION, VERSION4 }));
+        versions.appendChild(generateDeploymentVersion(doc, MULTIPLEVERSIONTARGET, VERSION4, new String[] {
+            "file:///bundle3.2", KEY_SYMBOLICNAME, "bundle3.2", KEY_VERSION, VERSION3_2 },
+            new String[] { "file:///bundle4.2", KEY_SYMBOLICNAME, "bundle4.2", KEY_VERSION,
+                VERSION4_2 }));
+
+        // Add a valid deployment version (5.0.0) with no bundle urls (empty package)
+        versions.appendChild(generateDeploymentVersion(doc, EMPTYVERSIONTARGET, VERSION1, new String[] {
+            "file:///bundle1", KEY_SYMBOLICNAME, "bundle1", KEY_VERSION, VERSION1 }));
+        versions.appendChild(generateDeploymentVersion(doc, EMPTYVERSIONTARGET, VERSION2));
+
+        // transform the document to string
+        TransformerFactory tFactory = TransformerFactory.newInstance();
+        Transformer transformer = null;
+        StringWriter sw = null;
+        try {
+            transformer = tFactory.newTransformer();
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            DOMSource source = new DOMSource(doc);
+            sw = new StringWriter();
+            StreamResult result = new StreamResult(sw);
+            transformer.transform(source, result);
+        }
+        catch (TransformerConfigurationException e) {
+            // Should not happen
+            e.printStackTrace();
+        }
+        catch (TransformerException e) {
+            // Should not happen
+            e.printStackTrace();
+        }
+
+        return sw.toString();
+    }
+
+    /**
+     * Helper method to create the description of a deploymentpacakge with given data.
+     * 
+     * @param doc The document to add the version to.
+     * @param targetText The targetId in the deploymentversion.
+     * @param versionText The version in the deploymentversion.
+     * @param data An array of data for the deployment artifact. [0] is the url, and each following item is
+     *        first a directive key, and a directive value. For example,<br>
+     *        <code>new String[] { "http://mybundle", "somedirective", "somevalue" }</code>
+     * @return
+     */
+    private Node generateDeploymentVersion(Document doc, String targetText, String versionText, String[]... data) {
+        Element deploymentversion = doc.createElement(DEPLOYMENTVERSION_TAG);
+        Element attr = doc.createElement(ATTRIBUTES_TAG);
+        deploymentversion.appendChild(attr);
+
+        // Create and add targetId Tag
+        Element elem = null;
+        elem = doc.createElement(TARGETID_TAG);
+        elem.setTextContent(targetText);
+        attr.appendChild(elem);
+
+        // Create and add versionTag
+        elem = doc.createElement(VERSION_TAG);
+        elem.setTextContent(versionText);
+        attr.appendChild(elem);
+
+        // Create and add empty tagsTag to deploymentversion
+        elem = doc.createElement(TAGS_TAG);
+        deploymentversion.appendChild(elem);
+
+        // create and add bundlesTag
+        elem = doc.createElement(ARTIFACTS_TAG);
+        for (String[] s : data) {
+            Element artifact = doc.createElement(ARTIFACT_TAG);
+            Element url = doc.createElement(URL_TAG);
+            url.setTextContent(s[0]);
+            artifact.appendChild(url);
+            Element directives = doc.createElement(DIRECTIVES_TAG);
+            for (int i = 1; i < s.length; i += 2) {
+                Element directive = doc.createElement(s[i]);
+                directive.setTextContent(s[i + 1]);
+                directives.appendChild(directive);
+            }
+            artifact.appendChild(directives);
+            elem.appendChild(artifact);
+        }
+
+        deploymentversion.appendChild(elem);
+
+        return deploymentversion;
+    }
+
+}

Added: ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/CacheTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/CacheTest.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/CacheTest.java (added)
+++ ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/CacheTest.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,66 @@
+/*
+ * 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.deployment.provider.repositorybased;
+
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class CacheTest {
+	@Test(groups = { UNIT })
+	public void testFillCacheToLimitAndCheckIfEverythingFits() {
+		LRUMap<String, String> map = new LRUMap<String, String>();
+		for (int i = 0; i < 1024; i++) {
+			String key = "" + i;
+			map.put(key, key);
+		}
+		for (int i = 0; i < 1024; i++) {
+			String key = "" + i;
+			Assert.assertEquals(map.get(key), key);
+		}
+	}
+
+	@Test(groups = { UNIT })
+	public void testOverflowCacheAndValidateOldestElementDisappears() {
+		LRUMap<String, String> map = new LRUMap<String, String>();
+		// add one too many
+		for (int i = 0; i < 1025; i++) {
+			String key = "" + i;
+			map.put(key, key);
+		}
+		// retrieve in same order (first one should be gone)
+		for (int i = 0; i < 1025; i++) {
+			String key = "" + i;
+			if (i == 0) {
+				Assert.assertNull(map.get(key));
+			}
+			else {
+				Assert.assertEquals(map.get(key), key);
+			}
+		}
+		// access the second one
+		map.get("1");
+		// add another one
+		String key = "1025";
+		map.put(key, key);
+		// make sure the third is gone now
+		Assert.assertNull(map.get("2"));
+	}
+}

Added: ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/MockDeploymentRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/MockDeploymentRepository.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/MockDeploymentRepository.java (added)
+++ ace/trunk/org.apache.ace.deployment/test/org/apache/ace/deployment/provider/repositorybased/MockDeploymentRepository.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,60 @@
+/*
+ * 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.deployment.provider.repositorybased;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ace.range.SortedRangeSet;
+import org.apache.ace.repository.Repository;
+
+public class MockDeploymentRepository implements Repository {
+
+    private String m_range;
+    private String m_xmlRepository;
+
+    public MockDeploymentRepository(String range, String xmlRepository) {
+        m_range = range;
+        m_xmlRepository = xmlRepository;
+    }
+
+    /* (non-Javadoc)
+     * Magic number version 1, generates an IOException, else return
+     * @see org.apache.ace.repository.Repository#checkout(long)
+     */
+    public InputStream checkout(long version) throws IOException, IllegalArgumentException {
+        if (version == 1) {
+            //throw an IOException!
+            throw new IOException("Checkout exception.");
+        }
+        else {
+            return new ByteArrayInputStream(m_xmlRepository.getBytes());
+        }
+    }
+
+    public boolean commit(InputStream data, long fromVersion) throws IOException, IllegalArgumentException {
+        // Not used in test
+        return false;
+    }
+
+    public SortedRangeSet getRange() throws IOException {
+        return new SortedRangeSet(m_range);
+    }
+}