ace-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ma...@apache.org
Subject svn commit: r1464402 [2/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...
Date Thu, 04 Apr 2013 09:43:37 GMT
Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandler.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandler.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandler.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/BaseRepositoryHandler.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,293 @@
+/*
+ * 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.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Version;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Provides a SAX-based push parser for obtaining all versions of the deployment packages of a certain target.
+ */
+class BaseRepositoryHandler extends DefaultHandler {
+
+    private final String m_targetID;
+
+    /** Denotes the current tag in the XML structure. */
+    private XmlTag m_currentTag;
+    /** Denotes the current version of the found target. */
+    private Version m_currentVersion;
+    /** Denotes whether or not the requested target is found. */
+    private boolean m_targetFound;
+    /** Denotes the current deployment artifact. */
+    private XmlDeploymentArtifact m_currentArtifact;
+    /** Denotes the directive key of the current deployment artifact. */
+    private String m_currentDirectiveKey;
+
+    /**
+     * Creates a new {@link BaseRepositoryHandler} instance.
+     * 
+     * @param targetID the target ID to search for, cannot be <code>null</code>.
+     */
+    public BaseRepositoryHandler(String targetID) {
+        m_targetID = targetID;
+        m_currentTag = XmlTag.unknown;
+    }
+
+    /**
+     * Parses the given text as {@link Version}.
+     * 
+     * @param text the text to parse as version, can not be <code>null</code>.
+     * @return a {@link Version} if the given text represent a correct version, never <code>null</code>.
+     */
+    static final Version parseVersion(String text) {
+        try {
+            if (text != null) {
+                return Version.parseVersion(text);
+            }
+        }
+        catch (Exception e) {
+            // Ignore; simply return an empty version to denote this invalid version...
+        }
+        return Version.emptyVersion;
+    }
+
+    @Override
+    public void startDocument() throws SAXException {
+        m_currentTag = XmlTag.unknown;
+        m_currentVersion = null;
+        m_targetFound = false;
+        m_currentArtifact = null;
+        m_currentDirectiveKey = null;
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        if (XmlTag.targetID.equals(m_currentTag)) {
+            // verify whether we're in the DP for the requested target...
+            m_targetFound = m_targetID.equals(new String(ch, start, length));
+        }
+        else if (XmlTag.version.equals(m_currentTag)) {
+            // Don't assume we've got the desired version (yet)...
+            m_currentVersion = null;
+
+            if (m_targetFound) {
+                m_currentVersion = parseAsVersion(new String(ch, start, length));
+            }
+        }
+        else if (XmlTag.url.equals(m_currentTag)) {
+            try {
+                URL artifactUrl = new URL(new String(ch, start, length));
+                m_currentArtifact = new XmlDeploymentArtifact(artifactUrl);
+            }
+            catch (MalformedURLException e) {
+                throw new SAXException("Unexpected URL!", e);
+            }
+        }
+        else if (XmlTag.directives.equals(m_currentTag)) {
+            if (m_currentArtifact == null) {
+                throw new SAXException("Unexpected directive tag!");
+            }
+            String value = new String(ch, start, length).trim();
+            if (m_currentDirectiveKey != null && !value.equals("")) {
+                m_currentArtifact.m_directives.put(m_currentDirectiveKey, value);
+            }
+        }
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+        XmlTag tag = XmlTag.asXmlTag(qName);
+        // If the given element is an expected child of the current tag, we
+        // traverse deeper into the XML-hierarchy; otherwise, consider it an
+        // "unknown"/uninteresting child and keep the current tag as-is...
+        if (m_currentTag.isExpectedChild(tag)) {
+            m_currentTag = tag;
+        }
+
+        m_currentDirectiveKey = null;
+        // If we're parsing the directives of an artifact, take the name for
+        // later use (the literal text in this tag will be used as value)...
+        if (XmlTag.directives.equals(m_currentTag)) {
+            m_currentDirectiveKey = qName;
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        XmlTag tag = XmlTag.asXmlTag(qName);
+        // When we're ending the current tag, traverse up to its parent...
+        if (!XmlTag.unknown.equals(tag) && (m_currentTag == tag)) {
+            m_currentTag = tag.getParent();
+        }
+
+        // Invoke the callbacks for events we're interested in...
+        if (XmlTag.version.equals(tag)) {
+            if (m_currentVersion != null && !Version.emptyVersion.equals(m_currentVersion)) {
+                // Let the version be handled (if needed)...
+                handleVersion(m_currentVersion);
+            }
+
+            // Let the currentVersion field as-is! We want to reuse it for the artifacts...
+        }
+        else if (XmlTag.deploymentArtifact.equals(tag)) {
+            if (m_currentArtifact != null) {
+                // push out the current deployment artifact...
+                handleArtifact(m_currentVersion, m_currentArtifact);
+            }
+
+            m_currentArtifact = null;
+        }
+        else if (XmlTag.directives.equals(tag)) {
+            m_currentDirectiveKey = null;
+        }
+    }
+
+    /**
+     * Allows subclasses to handle the given version of a target's deployment package.
+     * <p>By default, this method does nothing.</p>
+     * 
+     * @param version the version found, never <code>null</code>.
+     */
+    protected void handleVersion(Version version) {
+        // NO-op
+    }
+
+    /**
+     * Allows subclasses to handle the given deployment artifact for the given version of the deployment package.
+     * <p>By default, this method does nothing.</p>
+     * 
+     * @param version the version of the deployment package;
+     * @param artifact the deployment artifact itself.
+     */
+    protected void handleArtifact(Version version, XmlDeploymentArtifact artifact) {
+        // NO-op
+    }
+
+    /**
+     * Parses the given text as {@link Version}.
+     * 
+     * @param text the text to parse as version, can not be <code>null</code>.
+     * @return a {@link Version} if the given text represent a correct version,
+     *         can be <code>null</code> in case of an incorrect/empty version..
+     */
+    protected final Version parseAsVersion(String text) {
+        Version result = parseVersion(text);
+        if (Version.emptyVersion.equals(result)) {
+            return null;
+        }
+        return result;
+    }
+
+    /**
+     * Helper class to store a pair of URL and directive, in which the directive may be empty.
+     */
+    public static class XmlDeploymentArtifact {
+        final private URL m_url;
+        final private Map<String, String> m_directives;
+
+        private XmlDeploymentArtifact(URL url) {
+            m_url = url;
+            m_directives = new HashMap<String, String>();
+        }
+
+        public URL getUrl() {
+            return m_url;
+        }
+
+        public Map<String, String> getDirective() {
+            return m_directives;
+        }
+    }
+
+    /**
+     * Defines the structure of our XML (only the parts we're interested in).
+     */
+    public static enum XmlTag {
+        targetID,
+        version,
+        attributes(targetID, version),
+        url,
+        directives,
+        deploymentArtifact(url, directives),
+        artifacts(deploymentArtifact),
+        tags,
+        deploymentversion(attributes, tags, artifacts),
+        deploymentversions(deploymentversion),
+        repository(deploymentversions),
+        unknown(repository);
+
+        private final XmlTag[] m_children;
+        private XmlTag m_parent;
+
+        private XmlTag(XmlTag... possibleChildren) {
+            m_children = possibleChildren;
+            // Update the children's parent...
+            for (int i = 0; i < m_children.length; i++) {
+                m_children[i].m_parent = this;
+            }
+        }
+
+        /**
+         * Returns the parent tag of this tag.
+         * 
+         * @return a parent tag, can be <code>null</code>.
+         */
+        public XmlTag getParent() {
+            return m_parent;
+        }
+
+        /**
+         * Returns whether the given XML tag is an expected child of this tag.
+         * 
+         * @param xmlTag the XML tag to test, cannot be <code>null</code>.
+         * @return <code>true</code> if the given tag is an expected child of this tag, <code>false</code> otherwise.
+         */
+        public boolean isExpectedChild(XmlTag xmlTag) {
+            for (int i = 0; i < m_children.length; i++) {
+                if (xmlTag.equals(m_children[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Provides a "safe" way of representing the given tag name as instance of this enum. If the given name does not represent a defined tag, "unknown" will be returned.
+         * 
+         * @param name the XML tag-name to represent as enum value, cannot be <code>null</code>.
+         * @return a {@link XmlTag} representation of the given tag-name, never <code>null</code>.
+         */
+        public static XmlTag asXmlTag(String name) {
+        	XmlTag[] values = { artifacts, attributes, deploymentArtifact, deploymentversion, deploymentversions, directives, repository, tags, targetID, url, version };
+			for (int i = 0; i < values.length; i++) {
+        		if (values[i].name().equals(name)) {
+        			return values[i];
+        		}
+        	}
+            return XmlTag.unknown;
+        }
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentArtifactCollector.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentArtifactCollector.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentArtifactCollector.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentArtifactCollector.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,93 @@
+/*
+ * 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Version;
+
+/**
+ * Provides {@link BaseRepositoryHandler} implementation that gathers all deployment
+ * artifacts of deployment packages for a specific target with a specific version.
+ */
+public class DeploymentArtifactCollector extends BaseRepositoryHandler {
+
+    private final List<Version> m_expectedVersions;
+    private final Map<Version, List<XmlDeploymentArtifact>> m_artifacts;
+
+    /**
+     * @param targetID the identification of the target to gather all artifacts for;
+     * @param versions the version of the deployment package to gather all artifacts for.
+     */
+    public DeploymentArtifactCollector(String targetID, String... versions) {
+        super(targetID);
+
+        m_artifacts = new HashMap<Version, List<XmlDeploymentArtifact>>();
+
+        m_expectedVersions = new ArrayList<Version>(versions.length);
+        for (int i = 0; i < versions.length; i++) {
+            Version v = parseVersion(versions[i]);
+            if (Version.emptyVersion.equals(v)) {
+                throw new IllegalArgumentException("Expected real version for " + versions[i]);
+            }
+            m_expectedVersions.add(v);
+        }
+    }
+
+    /**
+     * Returns all deployment artifacts of the requested target's deployment package.
+     * 
+     * @return an array with lists of all found deployment artifacts, never <code>null</code>.
+     *         The array contains the deployment artifacts per requested version, in the same
+     *         order as given in the class constructor.
+     */
+    public List<XmlDeploymentArtifact>[] getArtifacts() {
+        List<XmlDeploymentArtifact>[] result = new List[m_expectedVersions.size()];
+        int i = 0;
+        for (Version version : m_expectedVersions) {
+            List<XmlDeploymentArtifact> list = m_artifacts.get(version);
+            if (list == null) {
+                throw new IllegalArgumentException("No artifacts found for version " + version);
+            }
+            result[i++] = list;
+        }
+        return result;
+    }
+
+    @Override
+    protected void handleVersion(Version version) {
+        if (m_expectedVersions.contains(version)) {
+            List<XmlDeploymentArtifact> artifacts = m_artifacts.get(version);
+            if (artifacts == null) {
+                artifacts = new ArrayList<XmlDeploymentArtifact>();
+                m_artifacts.put(version, artifacts);
+            }
+        }
+    }
+
+    @Override
+    protected void handleArtifact(Version version, XmlDeploymentArtifact artifact) {
+        if (m_expectedVersions.contains(version)) {
+            m_artifacts.get(version).add(artifact);
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentPackageVersionCollector.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentPackageVersionCollector.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentPackageVersionCollector.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/DeploymentPackageVersionCollector.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,55 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.Version;
+
+/**
+ * Provides {@link BaseRepositoryHandler} implementation that gathers all versions of deployment packages for a specific target.
+ */
+public class DeploymentPackageVersionCollector extends BaseRepositoryHandler {
+
+    private final List<Version> m_versions;
+
+    /**
+     * @param targetID the target to gather all deployment package versions for.
+     */
+    public DeploymentPackageVersionCollector(String targetID) {
+        super(targetID);
+
+        m_versions = new ArrayList<Version>();
+    }
+
+    /**
+     * Returns a list of all found deployment package versions.
+     * 
+     * @return a list of {@link Version}s, never <code>null</code>.
+     */
+    public List<Version> getVersions() {
+        return m_versions;
+    }
+
+    @Override
+    protected void handleVersion(Version version) {
+        m_versions.add(version);
+    }
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/LRUMap.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/LRUMap.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/LRUMap.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/LRUMap.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,36 @@
+/*
+ * 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.util.LinkedHashMap;
+
+public class LRUMap<K, V> extends LinkedHashMap<K, V> {
+	private static final int INITIAL = 64;
+	private static final int MAX = 1024;
+	private static final float LOADFACTOR = 0.75f;
+
+	public LRUMap() {
+		super(INITIAL, LOADFACTOR, true);
+	}
+	
+	@Override
+	protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
+		return size() > MAX;
+	}
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/RepositoryBasedProvider.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/RepositoryBasedProvider.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/RepositoryBasedProvider.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/provider/repositorybased/RepositoryBasedProvider.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,479 @@
+/*
+ * 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.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.ace.deployment.provider.ArtifactData;
+import org.apache.ace.deployment.provider.DeploymentProvider;
+import org.apache.ace.deployment.provider.impl.ArtifactDataImpl;
+import org.apache.ace.deployment.provider.repositorybased.BaseRepositoryHandler.XmlDeploymentArtifact;
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.range.RangeIterator;
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.ext.BackupRepository;
+import org.apache.ace.repository.ext.CachedRepository;
+import org.apache.ace.repository.ext.impl.CachedRepositoryImpl;
+import org.apache.ace.repository.ext.impl.FilebasedBackupRepository;
+import org.apache.ace.repository.ext.impl.RemoteRepository;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * The RepositoryBasedProvider provides version information and bundle data by the DeploymentProvider interface. It uses a
+ * Repository to get its information from, which it parses using a SAX parser.
+ */
+public class RepositoryBasedProvider implements DeploymentProvider, ManagedService {
+    private static final String URL = "url";
+    private static final String NAME = "name";
+    private static final String CUSTOMER = "customer";
+    
+    /**
+     * Key, intended to be used for artifacts which are bundles and will publish
+     * a resource processor (see OSGi compendium section 114.10).
+     */
+    public static final String DIRECTIVE_ISCUSTOMIZER = "DeploymentPackage-Customizer";
+
+    /**
+     * Key, intended to be used for resources which require a resource processor
+     * (see OSGi compendium section 114.10).
+     */
+    public static final String DIRECTIVE_KEY_PROCESSORID = "Resource-Processor";
+
+    /**
+     * Key, intended to be used for artifacts which have a resourceID that's different
+     * from their generated name (based on URL).
+     */
+    public static final String DIRECTIVE_KEY_RESOURCE_ID = "Resource-ID";
+
+    /**
+     * Key, intended to be used for matching processed (see ArtifactPreprocessor) to their
+     * 'original' one.
+     */
+    public static final String DIRECTIVE_KEY_BASEURL = "Base-Url";
+
+	public static final String REPOSITORY_PATH = "ACE-RepositoryPath";
+
+    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 volatile LogService m_log;
+
+    /** This variable is volatile since it can be changed by the Updated() method. */
+    private volatile CachedRepository m_cachedRepository;
+
+    /**
+     * This variable is volatile since it can be changed by the updated() method. Furthermore, it will be used to inject a
+     * custom repository in the integration test.
+     */
+    private volatile Repository m_directRepository;
+    private volatile DependencyManager m_manager;
+    
+    private final SAXParserFactory m_saxParserFactory;
+    private final Map<String,List<String>> m_cachedVersionLists;
+
+    public RepositoryBasedProvider() {
+        m_saxParserFactory = SAXParserFactory.newInstance();
+        m_cachedVersionLists = new LRUMap<String, List<String>>();
+    }
+
+    public List<ArtifactData> getBundleData(String targetId, String version) throws IllegalArgumentException, IOException {
+        return getBundleData(targetId, null, version);
+    }
+
+    public List<ArtifactData> getBundleData(String targetId, String versionFrom, String versionTo) throws IllegalArgumentException, IOException {
+        try {
+            if (versionFrom != null) {
+                Version.parseVersion(versionFrom);
+            }
+            Version.parseVersion(versionTo);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(nfe);
+        }
+
+        InputStream input = null;
+        List<ArtifactData> dataVersionTo = null;
+        List<ArtifactData> dataVersionFrom = null;
+
+        List<XmlDeploymentArtifact>[] pairs = null;
+        try {
+            // ACE-240: do NOT allow local/remote repositories to be empty. If we're 
+            // asking for real artifacts, it means we must have a repository...
+            input = getRepositoryStream(true /* fail */);
+            if (versionFrom == null) {
+                pairs = getDeploymentArtifactPairs(input, targetId, new String[] { versionTo });
+            }
+            else {
+                pairs = getDeploymentArtifactPairs(input, targetId, new String[] { versionFrom, versionTo });
+            }
+        }
+        catch (IOException ioe) {
+            m_log.log(LogService.LOG_WARNING, "Problem parsing source version.", ioe);
+            throw ioe;
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException e) {
+                    m_log.log(LogService.LOG_DEBUG, "Error closing stream", e);
+                }
+            }
+        }
+
+        if ((pairs != null) && (pairs.length > 1)) {
+            dataVersionFrom = getAllArtifactData(pairs[0]);
+            dataVersionTo = getAllArtifactData(pairs[1]);
+            Iterator<ArtifactData> it = dataVersionTo.iterator();
+            while (it.hasNext()) {
+                ArtifactDataImpl bundleDataVersionTo = (ArtifactDataImpl) it.next();
+                // see if there was previously a version of this bundle, and update the 'changed' property accordingly.
+                if (bundleDataVersionTo.isBundle()) {
+                    ArtifactData bundleDataVersionFrom = getArtifactData(bundleDataVersionTo.getSymbolicName(), dataVersionFrom);
+                    bundleDataVersionTo.setChanged(!bundleDataVersionTo.equals(bundleDataVersionFrom));
+                }
+                else {
+                    ArtifactData bundleDataVersionFrom = getArtifactData(bundleDataVersionTo.getUrl(), dataVersionFrom);
+                    bundleDataVersionTo.setChanged(bundleDataVersionFrom == null);
+                }
+            }
+        }
+        else {
+            dataVersionTo = getAllArtifactData(pairs[0]);
+        }
+
+        return dataVersionTo != null ? dataVersionTo : new ArrayList<ArtifactData>();
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<String> getVersions(String targetId) throws IllegalArgumentException, IOException {
+    	// check if cache is up to date
+    	if (isCacheUpToDate()) {
+    		List<String> result = m_cachedVersionLists.get(targetId);
+    		if (result != null) {
+    			return result;
+    		}
+    	}
+    	else {
+    		m_cachedVersionLists.clear();
+    	}
+    	
+        List<String> stringVersionList = new ArrayList<String>();
+        InputStream input = null;
+
+        try {
+            // ACE-240: allow local/remote repositories to be empty; as the target 
+            // might be new & unregistered, it can have no repository yet... 
+            input = getRepositoryStream(false /* fail */);
+            List<Version> versionList = getAvailableVersions(input, targetId);
+            if (versionList.isEmpty()) {
+                m_log.log(LogService.LOG_DEBUG, "No versions found for target: " + targetId);
+            }
+            else {
+                // now sort the list of versions and convert all values to strings.
+                Collections.sort(versionList);
+                Iterator<Version> it = versionList.iterator();
+                while (it.hasNext()) {
+                    String version = (it.next()).toString();
+                    stringVersionList.add(version);
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // just move on.
+        }
+        catch (IOException ioe) {
+            m_log.log(LogService.LOG_DEBUG, "Problem parsing DeploymentRepository", ioe);
+            throw ioe;
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException e) {
+                    m_log.log(LogService.LOG_DEBUG, "Error closing stream", e);
+                }
+            }
+        }
+
+        m_log.log(LogService.LOG_DEBUG, "Cache added for " + targetId);
+
+        m_cachedVersionLists.put(targetId, stringVersionList);
+        return stringVersionList;
+    }
+
+    /**
+     * Helper method to get the bundledata given an inputstream to a repository xml file
+     *
+     * @param input An input stream to the XML data to be parsed.
+     * @return A list of ArtifactData object representing this version.
+     */
+    private List<ArtifactData> getAllArtifactData(List<XmlDeploymentArtifact> deploymentArtifacts) throws IllegalArgumentException {
+        List<ArtifactData> result = new ArrayList<ArtifactData>();
+
+        // get the bundledata for each URL
+        for (XmlDeploymentArtifact pair : deploymentArtifacts) {
+            Map<String, String> directives = pair.getDirective();
+
+            if (directives.get(DIRECTIVE_KEY_PROCESSORID) == null) {
+                // this is a bundle.
+                String symbolicName = directives.get(KEY_SYMBOLICNAME);
+                String bundleVersion = directives.get(KEY_VERSION);
+                if (symbolicName != null) {
+                    // it is the right symbolic name
+                    if (symbolicName.trim().equals("")) {
+                        m_log.log(LogService.LOG_WARNING, "Invalid bundle:" + pair.toString() + " the symbolic name is empty.");
+                    }
+                    else {
+                        result.add(new ArtifactDataImpl(pair.getUrl(), directives, symbolicName, bundleVersion, true));
+                    }
+                }
+            }
+            else {
+                // it is an artifact.
+                String filename = directives.get(DIRECTIVE_KEY_RESOURCE_ID);
+                result.add(new ArtifactDataImpl(filename, pair.getUrl(), directives, true));
+            }
+
+        }
+        return result;
+    }
+
+    /**
+     * Helper method check for the existence of artifact data in the collection for a bundle with the given url.
+     *
+     * @param url The url to be found.
+     * @return The <code>ArtifactData</code> object that has this <code>url</code>, or <code>null</code> if none can be
+     *         found.
+     */
+    private ArtifactData getArtifactData(URL url, Collection<ArtifactData> data) {
+        ArtifactData bundle = null;
+        URI uri = null;
+        try {
+            uri = url.toURI();
+        }
+        catch (URISyntaxException e) {
+            m_log.log(LogService.LOG_ERROR, "Could not convert URL " + url + " to a URI");
+            return null;
+        }
+        Iterator<ArtifactData> it = data.iterator();
+        while (it.hasNext()) {
+            bundle = it.next();
+            try {
+                if (uri.equals(bundle.getUrl().toURI())) {
+                    return bundle;
+                }
+            }
+            catch (URISyntaxException e) {
+                m_log.log(LogService.LOG_ERROR, "Could not convert bundle URL for " + bundle.getFilename() + " to a URI");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Helper method check for the existence of artifact data in the collection for a bundle with the given symbolic name.
+     *
+     * @param symbolicName The symbolic name to be found.
+     * @return The <code>ArtifactData</code> object that has this <code>symbolicName</code>, or <code>null</code> if none
+     *         can be found.
+     */
+    private ArtifactData getArtifactData(String symbolicName, Collection<ArtifactData> data) {
+        ArtifactData bundle = null;
+        Iterator<ArtifactData> it = data.iterator();
+        while (it.hasNext()) {
+            bundle = it.next();
+            String bsn = bundle.getSymbolicName();
+            if ((bsn != null) && bsn.equals(symbolicName)) {
+                return bundle;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the available deployment versions for a target
+     *
+     * @param input A dom document representation of the repository
+     * @param targetId The target identifier
+     * @return A list of available versions
+     */
+    private List<Version> getAvailableVersions(InputStream input, String targetId) throws IllegalArgumentException {
+        DeploymentPackageVersionCollector collector = new DeploymentPackageVersionCollector(targetId);
+        
+        try {
+            m_saxParserFactory.newSAXParser().parse(input, collector);
+            
+            return collector.getVersions();
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * Helper method to retrieve urls and directives for a target-version combination.
+     *
+     * @param input An input stream from which an XML representation of a deployment repository can be read.
+     * @param targetId The target identifier to be used
+     * @param versions An array of versions.
+     * @return An array of lists of URLDirectivePairs. For each version in <code>versions</code>, a separate list will be
+     *         created; the index of a version in the <code>versions</code> array is equal to the index of its result in the
+     *         result array.
+     * @throws IllegalArgumentException if the targetId or versions cannot be found in the input stream, or if
+     *         <code>input</code> does not contain an XML stream.
+     */
+    private List<XmlDeploymentArtifact>[] getDeploymentArtifactPairs(InputStream input, String targetId, String[] versions) throws IllegalArgumentException {
+        final DeploymentArtifactCollector collector = new DeploymentArtifactCollector(targetId, versions);
+        
+        try {
+            m_saxParserFactory.newSAXParser().parse(input, collector);
+
+            return collector.getArtifacts();
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * Helper to get an input stream to the currently used deployment repository.
+     *
+     * @return An input stream to the repository document. Will return an empty stream if none can be found.
+     * @throws java.io.IOException if there is a problem communicating with the local or remote repository.
+     */
+    private InputStream getRepositoryStream(boolean fail) throws IOException {
+        // cache the repositories, since we do not want them to change while we're in this method.
+        CachedRepository cachedRepository = m_cachedRepository;
+        Repository repository = m_directRepository;
+        InputStream result;
+
+        if (cachedRepository != null) {
+            // we can use the cached repository
+            if (cachedRepository.isCurrent()) {
+                result = cachedRepository.getLocal(fail);
+            }
+            else {
+                result = cachedRepository.checkout(fail);
+            }
+        }
+        else {
+            RangeIterator ri = repository.getRange().iterator();
+            long resultVersion = 0;
+            while (ri.hasNext()) {
+                resultVersion = ri.next();
+            }
+            if (resultVersion != 0) {
+                result = repository.checkout(resultVersion);
+            }
+            else {
+                throw new IllegalArgumentException("There is no deployment information available.");
+            }
+        }
+
+        return result;
+    }
+    
+    private boolean isCacheUpToDate() {
+        CachedRepository cachedRepository = m_cachedRepository;
+        try {
+			return (cachedRepository != null && cachedRepository.isCurrent());
+		}
+        catch (IOException ioe) {
+        	m_log.log(LogService.LOG_WARNING, "Failed to check if cache is current. Assuming it's not.", ioe);
+        	return false;
+		}
+    }
+
+    public void updated(Dictionary settings) throws ConfigurationException {
+        if (settings != null) {
+            String url = getNotNull(settings, URL, "DeploymentRepository URL not configured.");
+            String name = getNotNull(settings, NAME, "RepositoryName not configured.");
+            String customer = getNotNull(settings, CUSTOMER, "RepositoryCustomer not configured.");
+
+            // create the remote repository and set it.
+            try {
+                BackupRepository backup = new FilebasedBackupRepository(File.createTempFile("currentrepository", null), File.createTempFile("backuprepository", null));
+
+                // We always create the remote repository. If we can create a backup repository, we will wrap a CachedRepository
+                // around it.
+                m_directRepository = new RemoteRepository(new URL(url), customer, name);
+
+                m_manager.add(m_manager.createComponent()
+                    .setImplementation(m_directRepository)
+                    .add(m_manager.createServiceDependency()
+                        .setService(ConnectionFactory.class)
+                        .setRequired(true)));
+
+                m_cachedRepository = null;
+                if (backup != null) {
+                    m_cachedRepository = new CachedRepositoryImpl(m_directRepository, backup, CachedRepositoryImpl.UNCOMMITTED_VERSION);
+                }
+            }
+            catch (IllegalArgumentException e) {
+                throw new ConfigurationException("Authentication", e.getMessage());
+            }
+            catch (MalformedURLException mue) {
+                throw new ConfigurationException(URL, mue.getMessage());
+            }
+            catch (IOException e) {
+                m_log.log(LogService.LOG_WARNING, "Unable to create temporary files for FilebasedBackupRepository");
+            }
+        }
+    }
+
+    /**
+     * Convenience method for getting settings from a configuration dictionary.
+     */
+    private String getNotNull(Dictionary settings, String id, String errorMessage) throws ConfigurationException {
+        String result = (String) settings.get(id);
+        if (result == null) {
+            throw new ConfigurationException(id, errorMessage);
+        }
+        return result;
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/DeploymentService.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/DeploymentService.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/DeploymentService.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/DeploymentService.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,82 @@
+/*
+ * 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.service;
+
+import java.io.IOException;
+import java.util.SortedSet;
+
+import org.osgi.framework.Version;
+
+/**
+ * Deployment service can be used to talk to the management agent about deployment packages,
+ * versions and updates, and to actually perform them. This interface coexists with the
+ * tasks that are also published by the management agent and that are probably more convenient
+ * if you just want to schedule (checks for) updates.
+ */
+public interface DeploymentService {
+
+    /**
+     * Returns the highest version that is available locally (already installed).
+     * 
+     * @return The highest installed version, can be <code>null</code> if no version is locally available.
+     */
+    Version getHighestLocalVersion();
+
+    /**
+     * Returns the highest version that is available remotely.
+     * 
+     * @param url The URL to be used to retrieve the versions available on the remote.
+     * @return The highest version available on the remote or <code>null</code> if no versions were available or the remote could not be reached.
+     * @throws IOException in case of I/O problems obtaining the remote version.
+     */
+    Version getHighestRemoteVersion() throws IOException;
+
+    /**
+     * Returns all versions that are available remotely.
+     * 
+     * @return the remote versions, sorted, can be <code>null</code>.
+     * @throws IOException in case of I/O problems obtaining the remote versions.
+     */
+    SortedSet<Version> getRemoteVersions() throws IOException;
+
+    /**
+     * Installs the version specified by the highestRemoteVersion.
+     * 
+     * @param remoteVersion the version to retrieve and install;
+     * @param localVersion the current (local) version, can be <code>null</code> in case of no version is yet installed.
+     * @throws IOException in case of I/O problems installing the version;
+     * @throws Exception in case of other problems installing the version.
+     */
+    void installVersion(Version remoteVersion, Version localVersion) throws IOException, Exception;
+
+    /**
+     * Updates from the current local version to the given remote version.
+     * <p>
+     * This method is the same as calling:
+     * <pre>
+     * installVersion(toVersion, getHighestLocalVersion());
+     * </pre>
+     * </p>
+     * 
+     * @param toVersion the (remote) version to update to, cannot be <code>null</code>.
+     * @throws Exception in case of other problems updating to the requested version.
+     */
+    void update(Version toVersion) throws Exception;
+
+}

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/Activator.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/Activator.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/Activator.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,154 @@
+/*
+ * 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.service.impl;
+
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+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.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;
+
+/**
+ * Provides an activator for the deployment service.
+ */
+public class Activator extends DependencyActivatorBase implements ManagedServiceFactory {
+
+    private static final String PID_NAME = "org.apache.ace.deployment.task.base.factory";
+
+    private static final String MA_NAME = "ma";
+    private static final String DEFAULT_MA_NAME = null;
+
+    private final Map<String, Component> m_instances = new ConcurrentHashMap<String, Component>();
+
+    private volatile DependencyManager m_manager;
+
+    /**
+     * @see org.osgi.service.cm.ManagedServiceFactory#deleted(java.lang.String)
+     */
+    public void deleted(String pid) {
+        Component component;
+        synchronized (m_instances) {
+            component = m_instances.remove(pid);
+        }
+        
+        if (component != null) {
+            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 - base";
+    }
+
+    /**
+     * @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;
+
+        // Create a default deployment service instance...
+        m_manager.add(createService(DEFAULT_MA_NAME));
+
+        Properties props = new Properties();
+        props.put(Constants.SERVICE_PID, PID_NAME);
+
+        m_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 {
+        final String ma = (String) dict.get(MA_NAME);
+        
+        Component component = m_instances.get(pid);
+        if (component == null) {
+            component = createService(ma);
+            synchronized (m_instances) {
+                m_instances.put(pid, component);
+            }
+            m_manager.add(component);
+        }
+        else {
+            // TODO do we want to deal with changes here?
+        }
+    }
+
+    /**
+     * Creates the {@link DeploymentService} component 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 a {@link Component} instance for the {@link DeploymentService}, never <code>null</code>.
+     */
+    private Component createService(String ma) {
+        Dictionary deploymentProperties = new Properties();
+
+        String identificationFilter = "(" + Constants.OBJECTCLASS + "=" + Identification.class.getName() + ")";
+        String discoveryFilter = "(" + Constants.OBJECTCLASS + "=" + Discovery.class.getName() + ")";
+
+        if (ma == null || "".equals(ma.trim())) {
+            identificationFilter = String.format("(&%s(!(%s=*)))", identificationFilter, MA_NAME);
+            discoveryFilter = String.format("(&%s(!(%s=*)))", discoveryFilter, MA_NAME); ;
+        }
+        else {
+            identificationFilter = String.format("(&%s(%s=%s))", identificationFilter, MA_NAME, ma);
+            discoveryFilter = String.format("(&%s(%s=%s))", discoveryFilter, MA_NAME, ma);
+            deploymentProperties.put(MA_NAME, ma);
+        }
+
+        DeploymentServiceImpl deploymentService = new DeploymentServiceImpl();
+
+        return createComponent()
+            .setInterface(DeploymentService.class.getName(), deploymentProperties)
+            .setImplementation(deploymentService)
+            .add(createServiceDependency().setService(Deployment.class).setRequired(true))
+            .add(createServiceDependency().setService(ConnectionFactory.class).setRequired(true))
+            .add(createServiceDependency().setService(Identification.class, identificationFilter).setRequired(true))
+            .add(createServiceDependency().setService(Discovery.class, discoveryFilter).setRequired(true))
+            .add(createServiceDependency().setService(EventAdmin.class).setRequired(false))
+            .add(createServiceDependency().setService(LogService.class).setRequired(false));
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/DeploymentServiceImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/DeploymentServiceImpl.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/DeploymentServiceImpl.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/impl/DeploymentServiceImpl.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,300 @@
+/*
+ * 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.service.impl;
+
+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;
+
+/**
+ * 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.deployment/src/org/apache/ace/deployment/service/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/packageinfo?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/packageinfo (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/service/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/servlet/AceRestException.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AceRestException.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AceRestException.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/AceRestException.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,46 @@
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Handle common rest problems here.
+ * This can be thrown by services (unaware of how to handle it in the end) and handeled inside the catching servlet.
+ */
+public class AceRestException extends Exception {
+    private final int m_statusCode;
+    private final String m_description;
+
+    public AceRestException(int statusCode, String description) {
+        super(statusCode + ":" + description);
+        m_statusCode = statusCode;
+        m_description = description;
+    }
+
+    /**
+     * handling code where we turn <code>this</code> into http error.
+     *
+     * @param response
+     */
+    public void handleAsHttpError(HttpServletResponse response) throws IOException {
+        response.sendError(m_statusCode, m_description);
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/Activator.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,51 @@
+/*
+ * 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.servlet;
+
+import javax.servlet.Servlet;
+
+import org.apache.ace.deployment.processor.DeploymentProcessor;
+import org.apache.ace.deployment.provider.DeploymentProvider;
+import org.apache.ace.deployment.streamgenerator.StreamGenerator;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+    public static final String PID = "org.apache.ace.deployment.servlet";
+
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        manager.add(createComponent()
+            .setInterface(Servlet.class.getName(), null)
+            .setImplementation(DeploymentServlet.class)
+            .add(createConfigurationDependency().setPropagate(true).setPid(PID))
+            .add(createServiceDependency().setService(StreamGenerator.class).setRequired(true))
+            .add(createServiceDependency().setService(DeploymentProvider.class).setRequired(true))
+            .add(createServiceDependency().setService(DeploymentProcessor.class).setRequired(false).setCallbacks("addProcessor", "removeProcessor"))
+            .add(createServiceDependency().setService(LogService.class).setRequired(false))
+        );
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+        // do nothing
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/DeploymentServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/DeploymentServlet.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/DeploymentServlet.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/servlet/DeploymentServlet.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,311 @@
+/*
+ * 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.servlet;
+
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.authentication.api.AuthenticationService;
+import org.apache.ace.deployment.processor.DeploymentProcessor;
+import org.apache.ace.deployment.provider.DeploymentProvider;
+import org.apache.ace.deployment.streamgenerator.StreamGenerator;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.User;
+
+/**
+ * The DeploymentServlet class provides in a list of versions available for a target and a stream
+ * of data containing the DeploymentPackage (or fix package) for a specific target and version.
+ */
+public class DeploymentServlet extends HttpServlet implements ManagedService {
+    private static final long serialVersionUID = 1L;
+
+    /** A boolean denoting whether or not authentication is enabled. */
+    private static final String KEY_USE_AUTHENTICATION = "authentication.enabled";
+
+    public static final String CURRENT = "current";
+    public static final String PROCESSOR = "processor";
+    public static final String VERSIONS = "versions";
+    public static final String DP_MIMETYPE = "application/vnd.osgi.dp";
+    public static final String TEXT_MIMETYPE = "text/plain";
+    
+    private final ConcurrentMap<String, DeploymentProcessor> m_processors = new ConcurrentHashMap<String, DeploymentProcessor>();
+    
+    // injected by Dependency Manager
+    private volatile DependencyManager m_dm; 
+    private volatile LogService m_log;
+    private volatile StreamGenerator m_streamGenerator;
+    private volatile DeploymentProvider m_provider;
+    private volatile AuthenticationService m_authService;
+
+    private volatile boolean m_useAuth = false;
+
+    /**
+     * Responds to GET requests sent to this endpoint, the response depends on the requested path:
+     * <li>http://host/endpoint/targetid/versions/ returns a list of versions available for the specified target
+     * <li>http://host/endpoint/targetid/versions/x.y.z returns a deployment package stream for the specified target and version
+     *
+     * The status code of the response can be one of the following:
+     * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - If no target is specified or the request is malformed in a different way.
+     * <li><code>HttpServletResponse.SC_NOT_FOUND</code> - If the specified target or version does not exist.
+     * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - If there was a problem processing the request.
+     * <li><code>HttpServletResponse.SC_OK</code> - If all went fine
+     */
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        try {
+            String[] pathElements = verifyAndGetPathElements(request.getPathInfo());
+            String targetID = pathElements[1];
+            List<String> versions = getVersions(targetID);
+            int numberOfElements = pathElements.length;
+
+            if (numberOfElements == 3) {
+                handleVersionsRequest(versions, response);
+            }
+            else {
+                String version = pathElements[3];
+                handlePackageDelivery(targetID, version, versions, request, response);
+            }
+        }
+        catch (AceRestException e) {
+            m_log.log(LogService.LOG_WARNING, e.getMessage(), e);
+            e.handleAsHttpError(response);
+        }
+    }
+
+    /**
+     * Called by Dependency Manager upon initialization of this component.
+     * 
+     * @param comp the component to initialize, cannot be <code>null</code>.
+     */
+    protected void init(Component comp) {
+        comp.add(m_dm.createServiceDependency()
+            .setService(AuthenticationService.class)
+            .setRequired(m_useAuth)
+            .setInstanceBound(true)
+            );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        if (!authenticate(req)) {
+            // Authentication failed; don't proceed with the original request...
+            resp.sendError(SC_UNAUTHORIZED);
+        } else {
+            // Authentication successful, proceed with original request...
+            super.service(req, resp);
+        }
+    }
+
+    /**
+     * Authenticates, if needed the user with the information from the given request.
+     * 
+     * @param request the request to obtain the credentials from, cannot be <code>null</code>.
+     * @return <code>true</code> if the authentication was successful, <code>false</code> otherwise.
+     */
+    private boolean authenticate(HttpServletRequest request) {
+        if (m_useAuth) {
+            User user = m_authService.authenticate(request);
+            if (user == null) {
+                m_log.log(LogService.LOG_INFO, "Authentication failure!");
+            }
+            return (user != null);
+        }
+        return true;
+    }
+
+    /**
+     * Serve the case where requested path is like:
+     * http://host/endpoint/targetid/versions/ returns a list of versions available for the specified target
+     *
+     * @param versions versions to be put into response
+     * @param response response object.
+     */
+    private void handleVersionsRequest(List<String> versions, HttpServletResponse response) throws AceRestException {
+        ServletOutputStream output = null;
+
+        response.setContentType(TEXT_MIMETYPE);
+        try {
+            output = response.getOutputStream();
+            for (String version : versions) {
+                output.print(version);
+                output.print("\n");
+            }
+        }
+        catch (IOException e) {
+            throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid");
+        }
+        finally {
+            tryClose(output);
+        }
+    }
+
+    private void handlePackageDelivery(final String targetID, final String version, final List<String> versions, final HttpServletRequest request, final HttpServletResponse response) throws AceRestException {
+        ServletOutputStream output = null;
+
+        try {
+            if (!versions.contains(version)) {
+                throw new AceRestException(HttpServletResponse.SC_NOT_FOUND, "Unknown version (" + version + ")");
+            }
+            String current = request.getParameter(CURRENT);
+            String processor = request.getParameter(PROCESSOR);
+
+            InputStream inputStream;
+            if (current != null) {
+                inputStream = m_streamGenerator.getDeploymentPackage(targetID, current, version);
+            }
+            else {
+                inputStream = m_streamGenerator.getDeploymentPackage(targetID, version);
+            }
+
+            if (processor != null) {
+                DeploymentProcessor deploymentProcessor = m_processors.get(processor);
+                if (deploymentProcessor != null) {
+                    deploymentProcessor.process(inputStream, request, response);
+                    return;
+                }
+                else {
+                    throw new AceRestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not find a deployment processor called: " + processor);
+                }
+            }
+            response.setContentType(DP_MIMETYPE);
+            output = response.getOutputStream();
+            byte[] buffer = new byte[1024 * 32];
+            for (int bytesRead = inputStream.read(buffer); bytesRead != -1; bytesRead = inputStream.read(buffer)) {
+                output.write(buffer, 0, bytesRead);
+            }
+        }
+        catch (IllegalArgumentException e) {
+            throw (AceRestException) new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid").initCause(e);
+        }
+        catch (IOException e) {
+            throw (AceRestException) new AceRestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Could not deliver package").initCause(e);
+        }
+        finally {
+            tryClose(output);
+        }
+    }
+
+    private List<String> getVersions(String targetID) throws AceRestException {
+        try {
+            return m_provider.getVersions(targetID);
+        }
+        catch (IllegalArgumentException iae) {
+            throw new AceRestException(HttpServletResponse.SC_NOT_FOUND, "Unknown target (" + targetID + ")");
+        }
+        catch (IOException ioe) {
+            m_log.log(LogService.LOG_WARNING, "Error getting available versions.", ioe);
+            throw new AceRestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error getting available versions.");
+        }
+    }
+
+    private void tryClose(OutputStream output) {
+        try {
+            if (output != null) {
+                output.close();
+            }
+        }
+        catch (IOException e) {
+            m_log.log(LogService.LOG_WARNING, "Exception trying to close stream after request. ", e);
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Make sure the path is valid.
+     * Also returns the splited version of #path.
+     *
+     * @param path http request path
+     *
+     * @return splitted version of #path. Split delim is "/"
+     *
+     * @throws org.apache.ace.deployment.servlet.AceRestException if path is not valid or cannot be processed.
+     */
+    private String[] verifyAndGetPathElements(String path) throws AceRestException {
+        if (path == null) {
+            throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid");
+        }
+        String[] elements = path.split("/");
+        int numberOfElements = elements.length;
+
+        if ((numberOfElements < 3) || (numberOfElements > 4) || !VERSIONS.equals(elements[2])) {
+            throw new AceRestException(HttpServletResponse.SC_BAD_REQUEST, "Request URI is invalid");
+        }
+        return elements;
+    }
+
+    @Override
+    public String getServletInfo() {
+        return "Ace Deployment Servlet Endpoint";
+    }
+
+    public void updated(Dictionary settings) throws ConfigurationException {
+        if (settings != null) {
+            String useAuthString = (String) settings.get(KEY_USE_AUTHENTICATION);
+            if (useAuthString == null
+                || !("true".equalsIgnoreCase(useAuthString) || "false".equalsIgnoreCase(useAuthString))) {
+                throw new ConfigurationException(KEY_USE_AUTHENTICATION, "Missing or invalid value!");
+            }
+            boolean useAuth = Boolean.parseBoolean(useAuthString);
+
+            m_useAuth = useAuth;
+        }
+        else {
+            m_useAuth = false;
+        }
+    }
+
+    public void addProcessor(ServiceReference ref, DeploymentProcessor processor) {
+        String key = (String) ref.getProperty(PROCESSOR);
+        if (key == null) {
+            m_log.log(LogService.LOG_WARNING, "Deployment processor ignored, required service property '" + PROCESSOR + "' is missing.");
+            return;
+        }
+        m_processors.putIfAbsent(key, processor);
+    }
+
+    public void removeProcessor(ServiceReference ref, DeploymentProcessor processor) {
+        String key = (String) ref.getProperty(PROCESSOR);
+        if (key == null) {
+            // we do not log this here again, we already did so in 'addProcessor'
+            return;
+        }
+        m_processors.remove(key);
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/StreamGenerator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/StreamGenerator.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/StreamGenerator.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/StreamGenerator.java Thu Apr  4 09:43:34 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.deployment.streamgenerator;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface StreamGenerator
+{
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/Activator.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/Activator.java (added)
+++ ace/trunk/org.apache.ace.deployment/src/org/apache/ace/deployment/streamgenerator/impl/Activator.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,48 @@
+/*
+ * 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 org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.deployment.provider.DeploymentProvider;
+import org.apache.ace.deployment.streamgenerator.StreamGenerator;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+
+public class Activator extends DependencyActivatorBase {
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        manager.add(createComponent()
+            .setInterface(StreamGenerator.class.getName(), null)
+            .setImplementation(StreamGeneratorImpl.class)
+            .add(createServiceDependency()
+                .setService(DeploymentProvider.class)
+                .setRequired(true)
+            )
+            .add(createServiceDependency()
+                .setService(ConnectionFactory.class)
+                .setRequired(true)
+            )
+            );
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+    }
+}
\ No newline at end of file



Mime
View raw message