You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ja...@apache.org on 2014/12/18 16:48:42 UTC

svn commit: r1646482 - in /ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace: Workspace.java impl/DPHelper.java impl/WorkspaceImpl.java

Author: jawi
Date: Thu Dec 18 15:48:41 2014
New Revision: 1646482

URL: http://svn.apache.org/r1646482
Log:
ACE-502 - Allow import of DPs from other ACE server

- import all new/non-existing artifacts;
- recreate the features and distributions and link them to
  the artifacts as mentioned in the DP;
- added support for fix-packages and ensure that no artifacts
  are missing that are not available in the OBR.


Added:
    ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/DPHelper.java   (with props)
Modified:
    ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/Workspace.java
    ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/WorkspaceImpl.java

Modified: ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/Workspace.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/Workspace.java?rev=1646482&r1=1646481&r2=1646482&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/Workspace.java (original)
+++ ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/Workspace.java Thu Dec 18 15:48:41 2014
@@ -392,4 +392,25 @@ public interface Workspace {
 
     public boolean isCurrent() throws IOException;
 
+    /*** deployment package ***/
+
+    /**
+     * Imports a deployment package from a given URL and commits all changes to the workspace.
+     * 
+     * @param dpURL
+     *            the URL to the deployment package to import.
+     * @see #idp(String, boolean)
+     */
+    public void idp(String dpURL) throws Exception;
+
+    /**
+     * Imports a deployment package from a given URL.
+     * 
+     * @param dpURL
+     *            the URL to the deployment package to import;
+     * @param autoCommit
+     *            <code>true</code> if changes to the workspace should be committed automatically, <code>false</code>
+     *            otherwise.
+     */
+    public void idp(String dpURL, boolean autoCommit) throws Exception;
 }

Added: ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/DPHelper.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/DPHelper.java?rev=1646482&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/DPHelper.java (added)
+++ ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/DPHelper.java Thu Dec 18 15:48:41 2014
@@ -0,0 +1,323 @@
+/*
+ * 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.client.workspace.impl;
+
+import static org.apache.ace.client.workspace.Workspace.*;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.client.repository.object.Feature2DistributionAssociation;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.osgi.service.log.LogService;
+
+/**
+ * Utility class that allows a deployment package to be imported in a workspace in such way that it recreates all
+ * artifacts, features, distributions and their associations.
+ * <p>
+ * This functionality uploads all artifacts from the given deployment package, and for each feature and distribution
+ * will recreate(!) all associations. This means that associations that were made by hand are <b>not</b> preserved!<br>
+ * In addition, no attempt is made to clean up artifacts that are no longer used (which is currently not possible with
+ * the default OBR in ACE).
+ * </p>
+ */
+class DPHelper {
+    private static final Map<String, String> NO_TAGS = Collections.<String, String> emptyMap();
+
+    private static final String ARTIFACT_NAME = "artifactName";
+    private static final String ACE_REPOSITORY_PATH = "ACE-RepositoryPath";
+    private static final String BUNDLE_VERSION = "Bundle-Version";
+    private static final String BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
+    private static final String DEPLOYMENT_PACKAGE_CUSTOMIZER = "DeploymentPackage-Customizer";
+    private static final String DEPLOYMENT_PACKAGE_MISSING = "DeploymentPackage-Missing";
+
+    private final WorkspaceImpl m_workspace;
+    private final LogService m_log;
+
+    /**
+     * Creates a new DPHelper instance.
+     */
+    public DPHelper(WorkspaceImpl workspace, LogService log) {
+        m_workspace = workspace;
+        m_log = log;
+    }
+
+    public void importDeploymentPackage(String dpURL, boolean autoCommit) throws Exception {
+        URL url = URI.create(dpURL).toURL();
+
+        InputStream is = null;
+        JarInputStream jis = null;
+
+        try {
+            is = url.openStream();
+            jis = new JarInputStream(is);
+
+            importDeploymentPackage(jis, autoCommit);
+        }
+        finally {
+            closeSilently(is);
+            closeSilently(jis);
+        }
+    }
+
+    protected void importDeploymentPackage(JarInputStream is, boolean autoCommit) throws Exception {
+        Map<ArtifactObject, FeatureObject> a2fMap = new HashMap<ArtifactObject, FeatureObject>();
+        Map<FeatureObject, DistributionObject> f2dMap = new HashMap<FeatureObject, DistributionObject>();
+
+        // Upload all (new) artifact...
+        JarEntry jarEntry;
+        while ((jarEntry = is.getNextJarEntry()) != null) {
+            String name = jarEntry.getName();
+            Attributes attributes = jarEntry.getAttributes();
+
+            // Create & upload artifact...
+            ArtifactObject artifact = createArtifact(name, attributes, is);
+            if (artifact == null) {
+                throw new RuntimeException("Failed to import deployment package! Missing artifact: " + name + " in fix package which is also not present in the OBR!");
+            }
+
+            String repositoryPath = attributes.getValue(ACE_REPOSITORY_PATH);
+            if (repositoryPath == null) {
+                m_log.log(LogService.LOG_WARNING, String.format("No ACE-RepositoryPath attribute present for '%s'; not creating assocations...", name));
+                continue;
+            }
+            String[] names = repositoryPath.split(";");
+
+            // Create feature...
+            FeatureObject feature = createFeature(names[0]);
+
+            // Create distribution...
+            DistributionObject distribution = createDistribution(names[1]);
+
+            a2fMap.put(artifact, feature);
+            f2dMap.put(feature, distribution);
+
+            is.closeEntry();
+        }
+
+        // Pre-fill...
+        for (Map.Entry<String, Attributes> entry : is.getManifest().getEntries().entrySet()) {
+            String name = entry.getKey();
+            Attributes attributes = entry.getValue();
+            boolean missing = Boolean.parseBoolean(attributes.getValue(DEPLOYMENT_PACKAGE_MISSING));
+
+            ArtifactObject artifact = findArtifact(name, attributes);
+            if (artifact == null && missing) {
+                throw new RuntimeException("Failed to import deployment package! Artifact: " + name + " is missing in fix package, but not present in the OBR!");
+            }
+
+            String repositoryPath = attributes.getValue(ACE_REPOSITORY_PATH);
+            if (repositoryPath == null) {
+                m_log.log(LogService.LOG_WARNING, String.format("No ACE-RepositoryPath attribute present for '%s'; not creating assocations...", name));
+                continue;
+            }
+            String[] names = repositoryPath.split(";");
+
+            // Create feature...
+            FeatureObject feature = createFeature(names[0]);
+
+            // Create distribution...
+            DistributionObject distribution = createDistribution(names[1]);
+
+            a2fMap.put(artifact, feature);
+            f2dMap.put(feature, distribution);
+        }
+
+        // Remove existing associations for feature & distribution...
+        for (FeatureObject feature : f2dMap.keySet()) {
+            disassociateArtifactsFromFeature(feature);
+        }
+        for (DistributionObject distribution : f2dMap.values()) {
+            disassociateFeaturesFromDistribution(distribution);
+        }
+
+        // Create associations...
+        for (Map.Entry<ArtifactObject, FeatureObject> entry : a2fMap.entrySet()) {
+            associateArtifactToFeature(entry.getKey(), entry.getValue());
+        }
+
+        for (Map.Entry<FeatureObject, DistributionObject> entry : f2dMap.entrySet()) {
+            associateFeatureToDistribution(entry.getKey(), entry.getValue());
+        }
+
+        if (autoCommit) {
+            m_workspace.commit();
+        }
+    }
+
+    private void associateArtifactToFeature(ArtifactObject artifact, FeatureObject feature) throws Exception {
+        if (artifact.getAssociationsWith(feature).isEmpty()) {
+            m_log.log(LogService.LOG_DEBUG, String.format("Creating assocation between artifact '%s' and feature '%s'...", artifact.getName(), feature.getName()));
+
+            m_workspace.createAssocation(ARTIFACT2FEATURE, artifact.getAssociationFilter(null), feature.getAssociationFilter(null), "1", "1");
+        }
+    }
+
+    private void associateFeatureToDistribution(FeatureObject feature, DistributionObject distribution) throws Exception {
+        if (feature.getAssociationsWith(distribution).isEmpty()) {
+            m_log.log(LogService.LOG_DEBUG, String.format("Creating assocation between feature '%s' and distribution '%s'...", feature.getName(), distribution.getName()));
+
+            m_workspace.createAssocation(FEATURE2DISTRIBUTION, feature.getAssociationFilter(null), distribution.getAssociationFilter(null), "1", "1");
+        }
+    }
+
+    private void closeSilently(Closeable resource) {
+        try {
+            if (resource != null) {
+                resource.close();
+            }
+        }
+        catch (IOException exception) {
+            m_log.log(LogService.LOG_DEBUG, "Failed to close resource!", exception);
+        }
+    }
+
+    private ArtifactObject createArtifact(String name, Attributes attrs, InputStream is) throws Exception {
+        // Check what we've got, if it already exists in our repository...
+        ArtifactObject artifact = findArtifact(name, attrs);
+
+        if (artifact != null) {
+            return artifact;
+        }
+        else if (Boolean.parseBoolean(attrs.getValue(DEPLOYMENT_PACKAGE_MISSING))) {
+            m_log.log(LogService.LOG_WARNING, String.format("Unable to create artifact '%s' as it is missing...", name));
+            return null;
+        }
+        else {
+            m_log.log(LogService.LOG_INFO, String.format("Creating artifact '%s'...", name));
+
+            File file = storeArtifactContents(name, is);
+            try {
+                return m_workspace.createArtifact(file.toURI().toURL().toExternalForm(), true /* upload */);
+            }
+            finally {
+                file.delete();
+            }
+        }
+    }
+
+    private DistributionObject createDistribution(String name) throws Exception {
+        String keyName = DistributionObject.KEY_NAME;
+        List<DistributionObject> dists = m_workspace.ld(String.format("(%s=%s)", keyName, name));
+        if (dists.size() > 1) {
+            throw new RuntimeException("Multiple distributions found for: " + name + "!");
+        }
+        else if (dists.size() == 1) {
+            return dists.get(0);
+        }
+        return m_workspace.createDistribution(Collections.singletonMap(keyName, name), NO_TAGS);
+    }
+
+    private FeatureObject createFeature(String name) throws Exception {
+        String keyName = FeatureObject.KEY_NAME;
+        List<FeatureObject> feats = m_workspace.lf(String.format("(%s=%s)", keyName, name));
+        if (feats.size() > 1) {
+            throw new RuntimeException("Multiple features found for: " + name + "!");
+        }
+        else if (feats.size() == 1) {
+            return feats.get(0);
+        }
+        return m_workspace.createFeature(Collections.singletonMap(keyName, name), NO_TAGS);
+    }
+
+    private void disassociateArtifactsFromFeature(FeatureObject feature) {
+        for (ArtifactObject artifact : feature.getArtifacts()) {
+            for (Artifact2FeatureAssociation association : artifact.getAssociationsWith(feature)) {
+                m_workspace.da2f(association);
+            }
+        }
+    }
+
+    private void disassociateFeaturesFromDistribution(DistributionObject distribution) {
+        for (FeatureObject feature : distribution.getFeatures()) {
+            for (Feature2DistributionAssociation association : feature.getAssociationsWith(distribution)) {
+                m_workspace.df2d(association);
+            }
+        }
+    }
+
+    private ArtifactObject findArtifact(String name, Attributes attrs) throws Exception {
+        List<ArtifactObject> objs;
+
+        String customizer = attrs.getValue(DEPLOYMENT_PACKAGE_CUSTOMIZER);
+        String bsn = attrs.getValue(BUNDLE_SYMBOLIC_NAME);
+        String version = attrs.getValue(BUNDLE_VERSION);
+
+        if (bsn != null) {
+            String filter = String.format("(&(%s=%s)(%s=%s))", BUNDLE_SYMBOLIC_NAME, bsn, BUNDLE_VERSION, version);
+            if (customizer != null) {
+                // Resource processor...
+                objs = m_workspace.lrp(filter);
+            }
+            else {
+                // Plain bundle...
+                objs = m_workspace.la(filter);
+            }
+        }
+        else {
+            // Other artifact...
+            objs = m_workspace.la(String.format("(%s=%s)", ARTIFACT_NAME, name));
+        }
+
+        if (objs.size() > 1) {
+            throw new RuntimeException("Multiple artifacts found for: " + name + "!");
+        }
+        else if (objs.size() == 1) {
+            return objs.get(0);
+        }
+
+        return null;
+    }
+
+    private File storeArtifactContents(String name, InputStream is) throws IOException {
+        String tmpDir = System.getProperty("java.io.tmpdir");
+        File tempFile = new File(tmpDir, name);
+        FileOutputStream fos = null;
+
+        try {
+            fos = new FileOutputStream(tempFile);
+
+            byte[] buffer = new byte[4096];
+            int len;
+            while ((len = is.read(buffer)) > 0) {
+                fos.write(buffer, 0, len);
+            }
+        }
+        finally {
+            closeSilently(fos);
+        }
+
+        return tempFile;
+    }
+}

Propchange: ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/DPHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/WorkspaceImpl.java?rev=1646482&r1=1646481&r2=1646482&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/WorkspaceImpl.java (original)
+++ ace/trunk/org.apache.ace.client.workspace/src/org/apache/ace/client/workspace/impl/WorkspaceImpl.java Thu Dec 18 15:48:41 2014
@@ -58,8 +58,6 @@ import org.osgi.framework.Filter;
 import org.osgi.service.log.LogService;
 import org.osgi.service.useradmin.User;
 
-import com.sun.corba.se.impl.oa.poa.AOMEntry;
-
 public class WorkspaceImpl implements Workspace {
 
     private final String m_sessionID;
@@ -277,6 +275,17 @@ public class WorkspaceImpl implements Wo
         updateAssociationAttributes(entityType, repositoryObject);
         updateTags(tags, repositoryObject);
     }
+    
+    @Override
+    public void idp(String dpURL) throws Exception {
+        idp(dpURL, true /* autoCommit */);
+    }
+    
+    @Override
+    public void idp(String dpURL, boolean autoCommit) throws Exception {
+        // Delegate all complexity to a separate helper class...
+        new DPHelper(this, m_log).importDeploymentPackage(dpURL, autoCommit);
+    }
 
     private void updateTags(Map<String, String> tags, RepositoryObject repositoryObject) {
         Enumeration<String> keys;
@@ -491,7 +500,11 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void ca(String url, boolean upload) throws Exception {
-        m_artifactRepository.importArtifact(new URL(url), upload);
+        createArtifact(url, upload);
+    }
+
+    public ArtifactObject createArtifact(String url, boolean upload) throws Exception {
+        return m_artifactRepository.importArtifact(new URL(url), upload);
     }
 
     @Override
@@ -586,7 +599,11 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void cf(Map<String, String> attrs, Map<String, String> tags) {
-        createRepositoryObject(FEATURE, attrs, tags);
+        createFeature(attrs, tags);
+    }
+
+    public FeatureObject createFeature(Map<String, String> attrs, Map<String, String> tags) {
+        return (FeatureObject) createRepositoryObject(FEATURE, attrs, tags);
     }
 
     @Override
@@ -659,7 +676,11 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void cd(Map<String, String> attrs, Map<String, String> tags) {
-        createRepositoryObject(DISTRIBUTION, attrs, tags);
+        createDistribution(attrs, tags);
+    }
+
+    public DistributionObject createDistribution(Map<String, String> attrs, Map<String, String> tags) {
+        return (DistributionObject) createRepositoryObject(DISTRIBUTION, attrs, tags);
     }
 
     @Override