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 2013/10/28 18:03:54 UTC

svn commit: r1536411 [1/3] - in /ace/trunk: org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/ org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ org.apache.ace.webui.vaadin/src/org/apache/ace/web...

Author: jawi
Date: Mon Oct 28 17:03:53 2013
New Revision: 1536411

URL: http://svn.apache.org/r1536411
Log:
ACE-422 - improve DnD behaviour in Vaadin UI:

- allow artifacts to be directly dropped on the artifacts-panel;
- added grouping of bundle-artifacts on their symbolic name;
- improved the unlinking of associations by only enabling the
  buttons only when it is possible to unlink;
- added a simple status line to give more information about the
  current status of the client.


Added:
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedAssociationObject.java   (with props)
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationManager.java
      - copied, changed from r1535767, ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/OBRUtil.java   (with props)
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/UploadHelper.java   (with props)
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/StatusLine.java   (with props)
Removed:
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/ResourceTable.java
Modified:
    ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/ArtifactRepositoryImpl.java
    ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/VaadinClient.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/ArtifactsPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/AssociationHelper.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/BaseObjectPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/DistributionsPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/FeaturesPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/MainActionToolbar.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/TargetsPanel.java

Modified: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/ArtifactRepositoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/ArtifactRepositoryImpl.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/ArtifactRepositoryImpl.java (original)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/ArtifactRepositoryImpl.java Mon Oct 28 17:03:53 2013
@@ -282,54 +282,34 @@ public class ArtifactRepositoryImpl exte
     }
 
     public ArtifactObject importArtifact(URL artifact, boolean upload) throws IllegalArgumentException, IOException {
-        try {
-            if ((artifact == null) || (artifact.toString().length() == 0)) {
-                throw new IllegalArgumentException("The URL to import cannot be null or empty.");
-            }
-            checkURL(artifact);
-
-            Map<Class<?>, Object> fromArtifact = findRecognizerAndHelper(artifact);
-            ArtifactRecognizer recognizer = (ArtifactRecognizer) fromArtifact.get(ArtifactRecognizer.class);
-            ArtifactHelper helper = (ArtifactHelper) fromArtifact.get(ArtifactHelper.class);
-            String mimetype = (String) fromArtifact.get(String.class);
-
-            return importArtifact(artifact, recognizer, helper, mimetype, false, upload);
-        }
-        catch (IllegalArgumentException iae) {
-            m_log.log(LogService.LOG_WARNING, "Error importing artifact: " + iae.getMessage(), iae);
-            throw iae;
-        }
-        catch (IOException ioe) {
-            m_log.log(LogService.LOG_WARNING, "Error storing artifact: " + ioe.getMessage(), ioe);
-            throw ioe;
+        if ((artifact == null) || (artifact.toString().length() == 0)) {
+            throw new IllegalArgumentException("The URL to import cannot be null or empty.");
         }
+        checkURL(artifact);
+        
+        Map<Class<?>, Object> fromArtifact = findRecognizerAndHelper(artifact);
+        ArtifactRecognizer recognizer = (ArtifactRecognizer) fromArtifact.get(ArtifactRecognizer.class);
+        ArtifactHelper helper = (ArtifactHelper) fromArtifact.get(ArtifactHelper.class);
+        String mimetype = (String) fromArtifact.get(String.class);
+        
+        return importArtifact(artifact, recognizer, helper, mimetype, false, upload);
     }
 
     public ArtifactObject importArtifact(URL artifact, String mimetype, boolean upload) throws IllegalArgumentException, IOException {
-        try {
-            if ((artifact == null) || (artifact.toString().length() == 0)) {
-                throw new IllegalArgumentException("The URL to import cannot be null or empty.");
-            }
-            if ((mimetype == null) || (mimetype.length() == 0)) {
-                throw new IllegalArgumentException("The mimetype of the artifact to import cannot be null or empty.");
-            }
-
-            checkURL(artifact);
-
-            Map<Class<?>, Object> fromMimetype = findRecognizerAndHelper(mimetype);
-            ArtifactRecognizer recognizer = (ArtifactRecognizer) fromMimetype.get(ArtifactRecognizer.class);
-            ArtifactHelper helper = (ArtifactHelper) fromMimetype.get(ArtifactHelper.class);
-
-            return importArtifact(artifact, recognizer, helper, mimetype, true, upload);
+        if ((artifact == null) || (artifact.toString().length() == 0)) {
+            throw new IllegalArgumentException("The URL to import cannot be null or empty.");
         }
-        catch (IllegalArgumentException iae) {
-            m_log.log(LogService.LOG_WARNING, "Error importing artifact: " + iae.getMessage(), iae);
-            throw iae;
-        }
-        catch (IOException ioe) {
-            m_log.log(LogService.LOG_WARNING, "Error storing artifact: " + ioe.getMessage(), ioe);
-            throw ioe;
+        if ((mimetype == null) || (mimetype.length() == 0)) {
+            throw new IllegalArgumentException("The mimetype of the artifact to import cannot be null or empty.");
         }
+        
+        checkURL(artifact);
+        
+        Map<Class<?>, Object> fromMimetype = findRecognizerAndHelper(mimetype);
+        ArtifactRecognizer recognizer = (ArtifactRecognizer) fromMimetype.get(ArtifactRecognizer.class);
+        ArtifactHelper helper = (ArtifactHelper) fromMimetype.get(ArtifactHelper.class);
+        
+        return importArtifact(artifact, recognizer, helper, mimetype, true, upload);
     }
 
     private ArtifactObject importArtifact(URL artifact, ArtifactRecognizer recognizer, ArtifactHelper helper, String mimetype, boolean overwrite, boolean upload) throws IOException {
@@ -349,14 +329,10 @@ public class ArtifactRepositoryImpl exte
         attributes.put(ArtifactObject.KEY_URL, artifactURL);
 
         if (upload) {
-            try {
-                String location = upload(artifact, attributes.get("filename"), mimetype);
-                attributes.put(ArtifactObject.KEY_URL, location);
-            }
-            catch (IOException ex) {
-                throw ex;
-            }
+            String location = upload(artifact, attributes.get("filename"), mimetype);
+            attributes.put(ArtifactObject.KEY_URL, location);
         }
+
         ArtifactObject result = create(attributes, tags);
         return result;
     }
@@ -469,7 +445,7 @@ public class ArtifactRepositoryImpl exte
                         location = outputConn.getHeaderField("Location");
                         break;
                     case HttpURLConnection.HTTP_CONFLICT:
-                        throw new IOException("Artifact already exists in storage.");
+                        throw new ArtifactAlreadyExistsException(artifact, filename);
                     case HttpURLConnection.HTTP_INTERNAL_ERROR:
                         throw new IOException("The storage server returned an internal server error.");
                     default:
@@ -477,9 +453,6 @@ public class ArtifactRepositoryImpl exte
                 }
             }
         }
-        catch (IOException ioe) {
-            throw new IOException("Error importing artifact " + artifact.toString() + ": " + ioe.getMessage(), ioe);
-        }
         finally {
             if (input != null) {
                 try {

Modified: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java (original)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java Mon Oct 28 17:03:53 2013
@@ -30,67 +30,120 @@ import org.apache.ace.client.repository.
  * Interface to a ArtifactRepository. The functionality is defined by the generic AssociationRepository.
  */
 public interface ArtifactRepository extends ObjectRepository<ArtifactObject> {
-	/**
-	 * Gets a list of all ArtifactObject's which are resource processing bundles.
-	 */
-	public List<ArtifactObject> getResourceProcessors();
-
-	/**
-	 * Tries to import an artifact into storage, while extracting necessary metadata.
-	 * @param artifact a URL pointing to the 'physical' artifact.
-	 * @param upload Indicates whether this artifact should be uploaded to our own OBR.
-	 * @return An <code>ArtifactObject</code> representing the passed in artifact, if
-	 * (a) the artifact is recognized, (b) there is storage available and (c) there is
-	 * a resource processor available for this type of artifact.
-	 * @throws IllegalArgumentException when the <code>artifact</code> cannot be processed.
-	 * @throws IOException when there is a problem transferring the <code>artifact</code> to storage.
-	 */
-	public ArtifactObject importArtifact(URL artifact, boolean upload) throws IllegalArgumentException, IOException;
-
-	/**
-	 * Checks whether an artifact is 'usable', that is, there is a resource processor available for it,
-	 * if necessary.
-	 * @param artifact A URL pointing to an artifact.
-	 * @return <code>true</code> if the artifact is recognized, and a processor for it is available. <code>false</code>
-	 * otherwise, including when the artifact cannot be reached.
-	 */
-	public boolean recognizeArtifact(URL artifact);
+    static class ArtifactAlreadyExistsException extends IOException {
+        private final URL m_url;
+        private final String m_filename;
+
+        /**
+         * Creates a new {@link ArtifactAlreadyExistsException} instance.
+         */
+        public ArtifactAlreadyExistsException(URL artifactURL, String filename) {
+            m_url = artifactURL;
+            m_filename = filename;
+        }
+
+        /**
+         * @return the filename of the artifact that already exists, can be <code>null</code>.
+         */
+        public String getFilename() {
+            return m_filename;
+        }
+
+        /**
+         * @return the URL of the already existing artifact, cannot be <code>null</code>.
+         */
+        public URL getUrl() {
+            return m_url;
+        }
+    }
+
+    /**
+     * Gets a list of all ArtifactObject's which are resource processing bundles.
+     */
+    public List<ArtifactObject> getResourceProcessors();
+
+    /**
+     * Tries to import an artifact into storage, while extracting necessary metadata.
+     * 
+     * @param artifact
+     *            a URL pointing to the 'physical' artifact.
+     * @param upload
+     *            Indicates whether this artifact should be uploaded to our own OBR.
+     * @return An <code>ArtifactObject</code> representing the passed in artifact, if (a) the artifact is recognized,
+     *         (b) there is storage available and (c) there is a resource processor available for this type of artifact.
+     * @throws IllegalArgumentException
+     *             when the given <tt>artifact</tt> represents an invalid URL;
+     * @throws ArtifactAlreadyExistsException
+     *             in case the artifact could not be uploaded, as it already exists in the storage;
+     * @throws IOException
+     *             when there is a problem transferring the <code>artifact</code> to storage.
+     */
+    public ArtifactObject importArtifact(URL artifact, boolean upload) throws IllegalArgumentException, IOException;
+
+    /**
+     * Checks whether an artifact is 'usable', that is, there is a resource processor available for it, if necessary.
+     * 
+     * @param artifact
+     *            A URL pointing to an artifact.
+     * @return <code>true</code> if the artifact is recognized, and a processor for it is available. <code>false</code>
+     *         otherwise, including when the artifact cannot be reached.
+     */
+    public boolean recognizeArtifact(URL artifact);
 
     /**
      * Tries to import an artifact into storage, while extracting necessary metadata.
-     * @param artifact a URL pointing to the 'physical' artifact.
-     * @param mimetype a String giving this object's mimetype.
-     * @param upload Indicates whether this artifact should be uploaded to our own OBR.
-     * @return An <code>ArtifactObject</code> representing the passed in artifact, if
-     * (a) there is storage available and (b) there is a resource processor
-     * available for this type of artifact.
-     * @throws IllegalArgumentException when the <code>artifact</code> cannot be processed.
-     * @throws IOException when there is a problem transferring the <code>artifact</code> to storage.
-     */
-	public ArtifactObject importArtifact(URL artifact, String mimetype, boolean upload) throws IllegalArgumentException, IOException;
-
-	/**
-	 * Tries to locate a preprocessor for the passed artifact, an processes it. If no processing
-	 * needs to be done, the original artifact's URL will be returned.
-	 * @param artifact An artifact
-	 * @param props A tree of properties objects, to be used for replacement.
-	 * @param targetID The targetID of the target for which this artifact is being processed.
-	 * @param version The deployment version for which this artifact is being processed.
-	 * @return A URL to a new, processed artifact, or to the original one, in case nothing needed to be processed.
-     * @throws IOException Thrown if reading the original artifact goes wrong, or storing the processed one.
-	 */
-	public String preprocessArtifact(ArtifactObject artifact, TargetObject target, String targetID, String version) throws IOException ;
+     * 
+     * @param artifact
+     *            a URL pointing to the 'physical' artifact.
+     * @param mimetype
+     *            a String giving this object's mimetype.
+     * @param upload
+     *            Indicates whether this artifact should be uploaded to our own OBR.
+     * @return An <code>ArtifactObject</code> representing the passed in artifact, if (a) there is storage available and
+     *         (b) there is a resource processor available for this type of artifact.
+     * @throws IllegalArgumentException
+     *             when the <code>artifact</code> cannot be processed.
+     * @throws IOException
+     *             when there is a problem transferring the <code>artifact</code> to storage.
+     */
+    public ArtifactObject importArtifact(URL artifact, String mimetype, boolean upload) throws IllegalArgumentException, IOException;
+
+    /**
+     * Tries to locate a preprocessor for the passed artifact, an processes it. If no processing needs to be done, the
+     * original artifact's URL will be returned.
+     * 
+     * @param artifact
+     *            An artifact
+     * @param props
+     *            A tree of properties objects, to be used for replacement.
+     * @param targetID
+     *            The targetID of the target for which this artifact is being processed.
+     * @param version
+     *            The deployment version for which this artifact is being processed.
+     * @return A URL to a new, processed artifact, or to the original one, in case nothing needed to be processed.
+     * @throws IOException
+     *             Thrown if reading the original artifact goes wrong, or storing the processed one.
+     */
+    public String preprocessArtifact(ArtifactObject artifact, TargetObject target, String targetID, String version) throws IOException;
 
     /**
      * Indicates whether the template should be processed again, given the properties, and the version to which it
      * should be compared.
-     * @param url A string representing a URL to the original artifact.
-     * @param props A PropertyResolver which can be used to fill in 'holes' in the template.
-     * @param targetID The targetID of the target for which this artifact is being processed.
-     * @param version The deployment version for which this artifact is being processed.
-     * @param lastVersion The deployment version to which the current one should be compared.
-     * @param newVersion The new, potential version.
-     * @param obrBase A base OBR to upload the new artifact to.
+     * 
+     * @param url
+     *            A string representing a URL to the original artifact.
+     * @param props
+     *            A PropertyResolver which can be used to fill in 'holes' in the template.
+     * @param targetID
+     *            The targetID of the target for which this artifact is being processed.
+     * @param version
+     *            The deployment version for which this artifact is being processed.
+     * @param lastVersion
+     *            The deployment version to which the current one should be compared.
+     * @param newVersion
+     *            The new, potential version.
+     * @param obrBase
+     *            A base OBR to upload the new artifact to.
      * @return Whether or not a new version has to be created.
      * @throws IOException
      */

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java Mon Oct 28 17:03:53 2013
@@ -1,11 +1,33 @@
+/*
+ * 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.webui;
 
 import org.apache.ace.client.repository.RepositoryObject;
 
 public interface NamedObject {
     String getName();
+
     String getDescription();
+
     void setDescription(String description);
+
     RepositoryObject getObject();
+
     public String getDefinition();
-}
\ No newline at end of file
+}

Added: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedAssociationObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedAssociationObject.java?rev=1536411&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedAssociationObject.java (added)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedAssociationObject.java Mon Oct 28 17:03:53 2013
@@ -0,0 +1,62 @@
+/*
+ * 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.webui.domain;
+
+import org.apache.ace.client.repository.Association;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.webui.NamedObject;
+
+/**
+ * 
+ */
+public class NamedAssociationObject implements NamedObject {
+    private final Association<?, ?> m_association;
+    
+    /**
+     * Creates a new {@link NamedAssociationObject} instance.
+     */
+    public NamedAssociationObject(Association<?, ?> association) {
+        m_association = association;
+    }
+
+    @Override
+    public String getDefinition() {
+        return m_association.getDefinition();
+    }
+
+    @Override
+    public String getDescription() {
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public RepositoryObject getObject() {
+        return m_association;
+    }
+
+    @Override
+    public void setDescription(String description) {
+        // Ignore...
+    }
+}

Propchange: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedAssociationObject.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java Mon Oct 28 17:03:53 2013
@@ -18,6 +18,7 @@
  */
 package org.apache.ace.webui.domain;
 
+import org.apache.ace.client.repository.Association;
 import org.apache.ace.client.repository.RepositoryObject;
 import org.apache.ace.client.repository.object.ArtifactObject;
 import org.apache.ace.client.repository.object.DistributionObject;
@@ -47,6 +48,9 @@ public final class NamedObjectFactory {
         else if (object instanceof TargetObject) {
             return new NamedTargetObject((TargetObject) object);
         }
+        else if (object instanceof Association) {
+            return new NamedAssociationObject((Association<?, ?>) object);
+        }
         return null;
     }
 }

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java Mon Oct 28 17:03:53 2013
@@ -19,14 +19,13 @@
 package org.apache.ace.webui.domain;
 
 public class OBREntry {
-	
     private final String m_name;
     private final String m_symbolicName;
     private final String m_version;
     private final String m_uri;
 
     public OBREntry(String name, String symbolicName, String version, String uri) {
-    	m_name = name;
+        m_name = name;
         m_symbolicName = symbolicName;
         m_version = version;
         m_uri = uri;
@@ -57,4 +56,4 @@ public class OBREntry {
     public boolean equals(Object obj) {
         return m_uri.equals(((OBREntry) obj).m_uri);
     }
-}
\ No newline at end of file
+}

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java Mon Oct 28 17:03:53 2013
@@ -32,7 +32,6 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.service.http.HttpService;
 
-import com.vaadin.ui.Button;
 import com.vaadin.ui.Component;
 import com.vaadin.ui.Label;
 import com.vaadin.ui.VerticalLayout;
@@ -68,8 +67,7 @@ public class Activator extends Dependenc
                     vl.setCaption("Info");
                     final NamedObject namedObject = (NamedObject) context.get("object");
                     final StatefulTargetObject target = (StatefulTargetObject) namedObject.getObject();
-                    Label info = new Label(
-                        "Target ID          : " + namedObject.getName() + "\n" +
+                    Label info = new Label("Target ID          : " + namedObject.getName() + "\n" +
                         "Installed version  : " + (target.getLastInstallVersion() == null ? "(none)" : target.getLastInstallVersion()) + "\n" +
                         "Available version  : " + target.getCurrentVersion() + "\n" +
                         "Approval state     : " + target.getApprovalState() + "\n" +
@@ -81,7 +79,7 @@ public class Activator extends Dependenc
                     return vl;
                 }
             })
-        );
+            );
     }
 
     @Override

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java?rev=1536411&r1=1536410&r2=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java Mon Oct 28 17:03:53 2013
@@ -18,13 +18,8 @@
  */
 package org.apache.ace.webui.vaadin;
 
-import java.io.Closeable;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -33,258 +28,42 @@ import java.util.List;
 import java.util.Set;
 
 import javax.servlet.http.HttpServletResponse;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
 
-import org.apache.ace.client.repository.helper.bundle.BundleHelper;
 import org.apache.ace.client.repository.object.ArtifactObject;
 import org.apache.ace.client.repository.repository.ArtifactRepository;
+import org.apache.ace.connectionfactory.ConnectionFactory;
 import org.apache.ace.webui.domain.OBREntry;
+import org.apache.ace.webui.vaadin.UploadHelper.ArtifactDropHandler;
+import org.apache.ace.webui.vaadin.UploadHelper.GenericUploadHandler;
+import org.apache.ace.webui.vaadin.UploadHelper.UploadHandle;
 import org.osgi.service.log.LogService;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
 
 import com.vaadin.data.Item;
 import com.vaadin.data.util.IndexedContainer;
-import com.vaadin.event.dd.DragAndDropEvent;
-import com.vaadin.event.dd.DropHandler;
-import com.vaadin.event.dd.acceptcriteria.AcceptAll;
-import com.vaadin.event.dd.acceptcriteria.AcceptCriterion;
-import com.vaadin.terminal.StreamVariable;
 import com.vaadin.ui.Alignment;
 import com.vaadin.ui.Button;
 import com.vaadin.ui.Button.ClickEvent;
 import com.vaadin.ui.Button.ClickListener;
 import com.vaadin.ui.DragAndDropWrapper;
-import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable;
 import com.vaadin.ui.HorizontalLayout;
-import com.vaadin.ui.Html5File;
 import com.vaadin.ui.Table;
 import com.vaadin.ui.TextField;
 import com.vaadin.ui.Upload;
-import com.vaadin.ui.Upload.FailedEvent;
-import com.vaadin.ui.Upload.FailedListener;
-import com.vaadin.ui.Upload.SucceededEvent;
-import com.vaadin.ui.Upload.SucceededListener;
 import com.vaadin.ui.VerticalLayout;
 import com.vaadin.ui.Window;
+import com.vaadin.ui.themes.Reindeer;
 
 /**
  * Provides a dialog for uploading new artifacts to ACE, or selecting existing artifacts from the repository.
  */
 abstract class AddArtifactWindow extends Window {
+    private static final String PROPERTY_SYMBOLIC_NAME = "symbolic name";
+    private static final String PROPERTY_VERSION = "version";
+    private static final String PROPERTY_PURGE = "purge";
 
-    /**
-     * Provides a {@link DropHandler} implementation for handling dropped artifacts.
-     */
-    private static final class ArtifactDropHandler implements DropHandler {
-        private final StreamVariable m_html5uploadStreamVariable;
-
-        private ArtifactDropHandler(StreamVariable html5uploadStreamVariable) {
-            m_html5uploadStreamVariable = html5uploadStreamVariable;
-        }
-
-        /*
-         * @see com.vaadin.event.dd.DropHandler#drop(com.vaadin.event.dd.DragAndDropEvent)
-         */
-        public void drop(DragAndDropEvent dropEvent) {
-            // expecting this to be an html5 drag
-            WrapperTransferable tr = (WrapperTransferable) dropEvent.getTransferable();
-            Html5File[] files = tr.getFiles();
-            if (files != null) {
-                for (final Html5File html5File : files) {
-                    html5File.setStreamVariable(m_html5uploadStreamVariable);
-                }
-            }
-        }
-
-        /*
-         * @see com.vaadin.event.dd.DropHandler#getAcceptCriterion()
-         */
-        public AcceptCriterion getAcceptCriterion() {
-            // TODO only accept .jar files ?
-            return AcceptAll.get();
-        }
-    }
-
-    /**
-     * Provides a upload handler capable of handling "old school" uploads, and new HTML5-style uploads.
-     */
-    private static abstract class GenericUploadHandler implements StreamVariable, Upload.SucceededListener,
-        Upload.FailedListener, Upload.Receiver {
-        private final File m_sessionDir;
-
-        private FileOutputStream m_fos = null;
-        private File m_file;
-
-        /**
-         * @param sessionDir the session directory to temporarily store uploaded artifacts in, cannot be <code>null</code>.
-         */
-        private GenericUploadHandler(File sessionDir) {
-            m_sessionDir = sessionDir;
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#getOutputStream()
-         */
-        public final OutputStream getOutputStream() {
-            return m_fos;
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#isInterrupted()
-         */
-        public final boolean isInterrupted() {
-            return (m_fos == null);
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#listenProgress()
-         */
-        public final boolean listenProgress() {
-            return false;
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#onProgress(com.vaadin.terminal.StreamVariable.StreamingProgressEvent)
-         */
-        public final void onProgress(StreamingProgressEvent event) {
-            // Do nothing, no progress indicator (yet ?)
-        }
-
-        /*
-         * @see com.vaadin.ui.Upload.Receiver#receiveUpload(java.lang.String, java.lang.String)
-         */
-        public final OutputStream receiveUpload(String filename, String MIMEType) {
-            return prepareUpload(filename);
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#streamingFailed(com.vaadin.terminal.StreamVariable.StreamingErrorEvent)
-         */
-        public final void streamingFailed(StreamingErrorEvent event) {
-            handleUploadFailure(event.getFileName(), event.getException());
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#streamingFinished(com.vaadin.terminal.StreamVariable.StreamingEndEvent)
-         */
-        public final void streamingFinished(StreamingEndEvent event) {
-            finishUpload();
-        }
-
-        /*
-         * @see com.vaadin.terminal.StreamVariable#streamingStarted(com.vaadin.terminal.StreamVariable.StreamingStartEvent)
-         */
-        public final void streamingStarted(StreamingStartEvent event) {
-            prepareUpload(event.getFileName());
-        }
-
-        /*
-         * @see com.vaadin.ui.Upload.FailedListener#uploadFailed(com.vaadin.ui.Upload.FailedEvent)
-         */
-        public final void uploadFailed(FailedEvent event) {
-            handleUploadFailure(event.getFilename(), event.getReason());
-        }
-
-        /*
-         * @see com.vaadin.ui.Upload.SucceededListener#uploadSucceeded(com.vaadin.ui.Upload.SucceededEvent)
-         */
-        public final void uploadSucceeded(SucceededEvent event) {
-            finishUpload();
-        }
-
-        /**
-         * Called when the upload was successful.
-         * 
-         * @param uploadedArtifact the uploaded file to process, never <code>null</code>.
-         */
-        protected abstract void artifactUploaded(File uploadedArtifact);
-
-        /**
-         * Called when the upload failed.
-         * 
-         * @param uploadedArtifact the name of the artifact whose upload failed;
-         * @param throwable the (optional) exception that caused the upload to fail.
-         */
-        protected abstract void uploadFailed(String uploadedArtifact, Throwable throwable);
-
-        /**
-         * Called after successfully uploading the artifact. Calls the {@link #artifactUploaded(File)} method.
-         */
-        private void finishUpload() {
-            // Make sure the output stream is properly closed...
-            silentlyClose(m_fos);
-
-            try {
-                artifactUploaded(m_file);
-            }
-            finally {
-                m_fos = null;
-            }
-        }
-
-        /**
-         * Handles any failures during the upload by closing all streams and cleaning up all resources. Calls the {@link #uploadFailed(String, Throwable)}
-         * 
-         * @param fileName the name of the uploaded artifact that failed;
-         * @param throwable the (optional) exception, can be <code>null</code>.
-         */
-        private void handleUploadFailure(String fileName, Throwable throwable) {
-            silentlyClose(m_fos);
-            m_fos = null;
-            m_file.delete();
-
-            uploadFailed(fileName, throwable);
-        }
-
-        /**
-         * Prepares the actual upload by creating a proper {@link FileOutputStream} to (temporarily) store the artifact to.
-         * 
-         * @param fileName the name of the uploaded artifact, cannot be <code>null</code>.
-         */
-        private OutputStream prepareUpload(String fileName) {
-            try {
-                m_file = new File(m_sessionDir, fileName);
-
-                if (m_file.exists()) {
-                    throw new IOException("Uploaded file already exists: " + fileName);
-                }
-                m_fos = new FileOutputStream(m_file);
-            }
-            catch (final IOException e) {
-                uploadFailed(fileName, e);
-                m_fos = null;
-            }
-            return m_fos;
-        }
-
-        /**
-         * Silently closes the given {@link Closeable} implementation, ignoring any errors that come out of the {@link Closeable#close()} method.
-         * 
-         * @param closable the closeable to close, can be <code>null</code>.
-         */
-        private void silentlyClose(Closeable closable) {
-            if (closable != null) {
-                try {
-                    closable.close();
-                }
-                catch (IOException e) {
-                    // Best effort; nothing we can (or want) do about this...
-                }
-            }
-        }
-    }
-
-    private static final String XPATH_QUERY = "/repository/resource[@uri]";
-
+    private final String m_repositoryXML;
     private final File m_sessionDir;
     private final URL m_obrUrl;
-    private final String m_repositoryXML;
 
     private final List<File> m_uploadedArtifacts = new ArrayList<File>();
     private final Button m_searchButton;
@@ -294,8 +73,10 @@ abstract class AddArtifactWindow extends
     /**
      * Creates a new {@link AddArtifactWindow} instance.
      * 
-     * @param sessionDir the session directory to temporary place artifacts in;
-     * @param obrUrl the URL of the OBR to use.
+     * @param sessionDir
+     *            the session directory to temporary place artifacts in;
+     * @param obrUrl
+     *            the URL of the OBR to use.
      */
     public AddArtifactWindow(File sessionDir, URL obrUrl, String repositoryXML) {
         super("Add artifact");
@@ -307,10 +88,15 @@ abstract class AddArtifactWindow extends
         setModal(true);
         setWidth("50em");
 
-        m_artifactsTable = new ResourceTable();
-        m_artifactsTable.setCaption("Artifacts in repository");
-
-        final IndexedContainer dataSource = (IndexedContainer) m_artifactsTable.getContainerDataSource();
+        m_artifactsTable = new Table("Artifacts in repository");
+        m_artifactsTable.addContainerProperty(PROPERTY_SYMBOLIC_NAME, String.class, null);
+        m_artifactsTable.addContainerProperty(PROPERTY_VERSION, String.class, null);
+        m_artifactsTable.addContainerProperty(PROPERTY_PURGE, Button.class, null);
+        m_artifactsTable.setSizeFull();
+        m_artifactsTable.setSelectable(true);
+        m_artifactsTable.setMultiSelect(true);
+        m_artifactsTable.setImmediate(true);
+        m_artifactsTable.setHeight("15em");
 
         final Table uploadedArtifacts = new ArtifactTable();
         uploadedArtifacts.setCaption("Uploaded artifacts");
@@ -318,37 +104,33 @@ abstract class AddArtifactWindow extends
 
         final GenericUploadHandler uploadHandler = new GenericUploadHandler(m_sessionDir) {
             @Override
-            protected void artifactUploaded(File uploadedArtifact) {
-                try {
-                    URL artifact = uploadedArtifact.toURI().toURL();
-
-                    Item item = uploadedArtifacts.addItem(artifact);
-                    item.getItemProperty(ArtifactTable.PROPERTY_SYMBOLIC_NAME).setValue(uploadedArtifact.getName());
-                    item.getItemProperty(ArtifactTable.PROPERTY_VERSION).setValue("");
-
-                    m_uploadedArtifacts.add(uploadedArtifact);
-                }
-                catch (MalformedURLException e) {
-                    showErrorNotification("Upload artifact processing failed", "<br />Reason: " + e.getMessage());
-                    logError("Processing of " + uploadedArtifact + " failed.", e);
-                }
+            public void updateProgress(long readBytes, long contentLength) {
+                // TODO Auto-generated method stub
             }
 
             @Override
-            protected void uploadFailed(String uploadedArtifact, Throwable throwable) {
-                showErrorNotification("Upload artifact failed", "File "
-                    + uploadedArtifact
-                    + "<br />could not be accepted on the server.<br />"
-                    + "Reason: " + throwable);
+            protected void artifactsUploaded(List<UploadHandle> uploads) {
+                for (UploadHandle handle : uploads) {
+                    try {
+                        URL artifact = handle.getFile().toURI().toURL();
+
+                        Item item = uploadedArtifacts.addItem(artifact);
+                        item.getItemProperty(ArtifactTable.PROPERTY_SYMBOLIC_NAME).setValue(handle.getFilename());
+                        item.getItemProperty(ArtifactTable.PROPERTY_VERSION).setValue("");
 
-                logError("Upload of " + uploadedArtifact + " failed.");
+                        m_uploadedArtifacts.add(handle.getFile());
+                    }
+                    catch (MalformedURLException e) {
+                        showErrorNotification("Upload artifact processing failed", "<br />Reason: " + e.getMessage());
+                        logError("Processing of " + handle.getFilename() + " failed.", e);
+                    }
+                }
             }
         };
 
-        final Upload uploadArtifact = new Upload("Upload Artifact", uploadHandler);
-        uploadArtifact.addListener((SucceededListener) uploadHandler);
-        uploadArtifact.addListener((FailedListener) uploadHandler);
-        uploadArtifact.setImmediate(true);
+        final Upload uploadArtifact = new Upload();
+        uploadArtifact.setCaption("Upload Artifact");
+        uploadHandler.install(uploadArtifact);
 
         final DragAndDropWrapper finalUploadedArtifacts = new DragAndDropWrapper(uploadedArtifacts);
         finalUploadedArtifacts.setDropHandler(new ArtifactDropHandler(uploadHandler));
@@ -369,6 +151,8 @@ abstract class AddArtifactWindow extends
         searchField.setImmediate(true);
         searchField.setValue("");
 
+        final IndexedContainer dataSource = (IndexedContainer) m_artifactsTable.getContainerDataSource();
+
         m_searchButton = new Button("Search", new ClickListener() {
             public void buttonClick(ClickEvent event) {
                 String searchValue = (String) searchField.getValue();
@@ -421,32 +205,16 @@ abstract class AddArtifactWindow extends
     }
 
     /**
-     * Gets the actual text from a named item contained in the given node map.
-     * 
-     * @param map the node map to get the named item from;
-     * @param name the name of the item to get.
-     * @return the text of the named item, can be <code>null</code> in case the named item does not exist, or has no text.
-     */
-    private static String getNamedItemText(NamedNodeMap map, String name) {
-        Node namedItem = map.getNamedItem(name);
-        if (namedItem == null) {
-            return null;
-        }
-        else {
-            return namedItem.getTextContent();
-        }
-    }
-    
-    /**
      * Shows this dialog on the parent window.
      * 
-     * @param parent the parent for this window, cannot be <code>null</code>.
+     * @param parent
+     *            the parent for this window, cannot be <code>null</code>.
      */
     public final void showWindow(Window parent) {
         try {
             // Fill the artifacts table with the data from the OBR...
-            getBundles(m_artifactsTable);
-            
+            populateArtifactTable(m_artifactsTable, m_obrUrl);
+
             parent.addWindow(this);
         }
         catch (Exception e) {
@@ -467,17 +235,18 @@ abstract class AddArtifactWindow extends
     /**
      * Imports all local, i.e., that are already in our local OBR, bundles.
      * 
-     * @param artifacts the UI-table with artifacts to install, cannot be <code>null</code>.
+     * @param artifacts
+     *            the UI-table with artifacts to install, cannot be <code>null</code>.
      * @return the imported artifacts, never <code>null</code>.
      */
     final List<ArtifactObject> importLocalBundles(final Table artifacts) {
         final List<ArtifactObject> added = new ArrayList<ArtifactObject>();
 
-        Set<String> selectedItems = (Set<String>) artifacts.getValue();
+        Set<?> selectedItems = (Set<?>) artifacts.getValue();
         if (selectedItems != null && !selectedItems.isEmpty()) {
-            for (String itemID : selectedItems) {
+            for (Object itemID : selectedItems) {
                 try {
-                    added.add(importLocalBundle(new URL(m_obrUrl, itemID)));
+                    added.add(UploadHelper.importLocalBundle(getArtifactRepository(), createOBRUrl(itemID)));
                 }
                 catch (Exception exception) {
                     Item item = artifacts.getItem(itemID);
@@ -499,21 +268,22 @@ abstract class AddArtifactWindow extends
     /**
      * Import remote bundles.
      * 
-     * @param uploadedArtifacts the list with uploaded artifacts, never <code>null</code>.
+     * @param uploadedArtifacts
+     *            the list with uploaded artifacts, never <code>null</code>.
      * @return the list of imported bundles.
      */
     final List<ArtifactObject> importRemoteBundles(List<File> uploadedArtifacts) {
         List<ArtifactObject> added = new ArrayList<ArtifactObject>();
-        
+
         StringBuffer errors = new StringBuffer();
         int failedImports = 0;
         for (File artifact : uploadedArtifacts) {
             try {
-                added.add(importRemoteBundle(artifact.toURI().toURL()));
+                added.add(UploadHelper.importRemoteBundle(getArtifactRepository(), artifact));
             }
             catch (Exception exception) {
-            	failedImports++;
-            	errors.append("<br />" + exception.getMessage());
+                failedImports++;
+                errors.append("<br />" + exception.getMessage());
                 logError("Import of " + artifact.getAbsolutePath() + " failed.", exception);
             }
             finally {
@@ -521,56 +291,44 @@ abstract class AddArtifactWindow extends
             }
         }
         if (failedImports > 0) {
-        	if (failedImports == uploadedArtifacts.size()) {
-        		showErrorNotification("All " + failedImports + " artifacts failed", (failedImports > 30 ? "See the server log for a full list of failures." : errors.toString()));
-        	}
-        	else {
-        		showWarningNotification("" + failedImports + "/" + uploadedArtifacts.size() + " artifacts failed", (failedImports > 30 ? "See the server log for a full list of failures." : errors.toString()));
-        	}
+            if (failedImports == uploadedArtifacts.size()) {
+                showErrorNotification("All " + failedImports + " artifacts failed", (failedImports > 30 ? "See the server log for a full list of failures." : errors.toString()));
+            }
+            else {
+                showWarningNotification("" + failedImports + "/" + uploadedArtifacts.size() + " artifacts failed", (failedImports > 30 ? "See the server log for a full list of failures." : errors.toString()));
+            }
         }
         return added;
     }
 
     /**
-     * Shows an error message on screen.
-     * 
-     * @param aTitle the title of the error message;
-     * @param aMessage the error message itself.
-     */
-    final void showErrorNotification(final String aTitle, final String aMessage) {
-        getParent().showNotification(aTitle, aMessage, Notification.TYPE_ERROR_MESSAGE);
-    }
-    
-    /** Shows a warning messsage on screen. */
-    final void showWarningNotification(final String aTitle, final String aMessage) {
-        getParent().showNotification(aTitle, aMessage, Notification.TYPE_WARNING_MESSAGE);
-    }
-
-    /**
-     * Logs a given message at the error level.
-     * <p>If there's no log service present, this method will silently ignore the log statement.</p>
-     * 
-     * @param aMessage the message to log.
+     * Logs a given message + exception at the error level.
+     * <p>
+     * If there's no log service present, this method will silently ignore the log statement.
+     * </p>
+     * 
+     * @param aMessage
+     *            the message to log;
+     * @param aException
+     *            the exception to log.
      */
-    final void logError(String aMessage) {
+    final void logError(String aMessage, Throwable aException) {
         LogService logger = getLogger();
         if (logger != null) {
-            logger.log(LogService.LOG_ERROR, aMessage);
+            logger.log(LogService.LOG_ERROR, aMessage, aException);
         }
     }
 
     /**
-     * Logs a given message + exception at the error level.
-     * <p>If there's no log service present, this method will silently ignore the log statement.</p>
+     * Shows an error message on screen.
      * 
-     * @param aMessage the message to log;
-     * @param aException the exception to log.
+     * @param aTitle
+     *            the title of the error message;
+     * @param aMessage
+     *            the error message itself.
      */
-    final void logError(String aMessage, Throwable aException) {
-        LogService logger = getLogger();
-        if (logger != null) {
-            logger.log(LogService.LOG_ERROR, aMessage, aException);
-        }
+    final void showErrorNotification(final String aTitle, final String aMessage) {
+        getParent().showNotification(aTitle, aMessage, Notification.TYPE_ERROR_MESSAGE);
     }
 
     /**
@@ -579,285 +337,103 @@ abstract class AddArtifactWindow extends
     protected abstract ArtifactRepository getArtifactRepository();
 
     /**
-     * @return the log service.
-     */
-    protected abstract LogService getLogger();
-    
-    /**
-     * @param url the URL to connect to, cannot be <code>null</code>.
+     * @param url
+     *            the URL to connect to, cannot be <code>null</code>.
      * @return a valid {@link URLConnection} instance, never <code>null</code>.
      */
-    protected abstract URLConnection openConnection(URL url) throws IOException;
-
-    /**
-     * Converts a given artifact object to an OBR entry.
-     * 
-     * @param artifactObject the artifact object to convert;
-     * @param obrBase the obr base url.
-     * @return an OBR entry instance, never <code>null</code>.
-     */
-    private OBREntry convertToOBREntry(ArtifactObject artifactObject, String obrBase) {
-        String name = artifactObject.getName();
-        String symbolicName = artifactObject.getAttribute(BundleHelper.KEY_SYMBOLICNAME);
-        String version = artifactObject.getAttribute(BundleHelper.KEY_VERSION);
-        String relativeURL = artifactObject.getURL().substring(obrBase.length());
-        return new OBREntry(name, symbolicName, version, relativeURL);
-    }
-
-    /**
-     * Gets the bundles.
-     * 
-     * @param table the table
-     * @return the bundles
-     * @throws Exception the exception
-     */
-    private void getBundles(Table table) throws Exception {
-        getBundles(table, m_obrUrl);
-    }
+    protected abstract ConnectionFactory getConnectionFactory();
 
     /**
-     * Gets the bundles.
-     * 
-     * @param dataSource the datasource to fill;
-     * @param obrBaseUrl the obr base url
-     * @return the bundles
-     * @throws Exception the exception
+     * @return the log service.
      */
-    private void getBundles(Table dataSource, URL obrBaseUrl) throws Exception {
-        // retrieve the repository.xml as a stream
-        List<OBREntry> obrList = parseOBRRepository(obrBaseUrl);
-
-        // Create a list of filenames from the ArtifactRepository
-        // remove those from the OBR entries we already know
-        obrList.removeAll(getUsedOBRArtifacts(obrBaseUrl));
-
-        if (obrList.isEmpty()) {
-            logError("No new data in OBR.");
-            return;
-        }
-
-        // Create a list of all bundle names
-        for (final OBREntry entry : obrList) {
-            
-            String uri = entry.getUri();
-            String name = entry.getName();
-            String version = entry.getVersion();
-
-            Item item = dataSource.addItem(uri);
-            item.getItemProperty(ResourceTable.PROPERTY_SYMBOLIC_NAME).setValue(name);
-            item.getItemProperty(ResourceTable.PROPERTY_VERSION).setValue(version);
-            item.getItemProperty(ResourceTable.PROPERTY_PURGE).setValue(createDeleteOBREntryButton(entry));
-        }
-    }
-    
-    /**
-     * Builds a list of all OBR artifacts currently in use.
-     * 
-     * @param obrBaseUrl the base URL of the OBR, cannot be <code>null</code>.
-     * @return a list of used OBR entries, never <code>null</code>.
-     * @throws IOException in case an artifact repository is not present.
-     */
-    private List<OBREntry> getUsedOBRArtifacts(URL obrBaseUrl) throws IOException {
-        ArtifactRepository artifactRepository = getArtifactRepository();
-        if (artifactRepository == null) {
-            throw new IOException("No artifact repository present!");
-        }
-
-        final String baseURL = obrBaseUrl.toExternalForm();
-
-        List<OBREntry> fromRepository = new ArrayList<OBREntry>();
-
-        List<ArtifactObject> artifactObjects = artifactRepository.get();
-        artifactObjects.addAll(artifactRepository.getResourceProcessors());
-
-        for (ArtifactObject ao : artifactObjects) {
-            String artifactURL = ao.getURL();
-            if (artifactURL != null && artifactURL.startsWith(baseURL)) {
-                // we now know this artifact comes from the OBR we are querying,
-                // so we are interested.
-                fromRepository.add(convertToOBREntry(ao, baseURL));
-            }
-        }
-        return fromRepository;
-    }
-
-    /**
-     * Imports a local bundle (already contained in the OBR) bundle.
-     * 
-     * @param artifactURL the URL of the artifact to import, cannot be <code>null</code>.
-     * @return the imported artifact object, never <code>null</code>.
-     * @throws IOException in case an I/O exception has occurred.
-     */
-    private ArtifactObject importLocalBundle(URL artifactURL) throws IOException {
-        ArtifactRepository artifactRepository = getArtifactRepository();
-        if (artifactRepository == null) {
-            throw new IOException("No artifact repository present!");
-        }
-        return artifactRepository.importArtifact(artifactURL, false /* upload */);
-    }
-
-    /**
-     * Imports a remote bundle by uploading it to the OBR.
-     * 
-     * @param artifactURL the URL of the artifact to import, cannot be <code>null</code>.
-     * @return the imported artifact object, never <code>null</code>.
-     * @throws IOException in case an I/O exception has occurred.
-     */
-    private ArtifactObject importRemoteBundle(URL artifactURL) throws IOException {
-        ArtifactRepository artifactRepository = getArtifactRepository();
-        if (artifactRepository == null) {
-            throw new IOException("No artifact repository present!");
-        }
-        return artifactRepository.importArtifact(artifactURL, true /* upload */);
-    }
+    protected abstract LogService getLogger();
 
     /**
-     * Parses the 'repository.xml' from OBR.
+     * Create a new button that delete an OBREntry on-click.
      * 
-     * @param obrBaseUrl the base URL to access the OBR, cannot be <code>null</code>.
-     * @return a list of parsed OBR entries, never <code>null</code>.
-     * @throws XPathExpressionException in case OBR repository is invalid, or incorrect;
-     * @throws IOException in case of problems accessing the 'repository.xml' file.
+     * @param entry
+     *            The entry
+     * @return The button
      */
-    private List<OBREntry> parseOBRRepository(URL obrBaseUrl) throws XPathExpressionException, IOException {
-        URL url = null;
-        try {
-            url = new URL(obrBaseUrl, m_repositoryXML);
-        }
-        catch (MalformedURLException e) {
-            logError("Error retrieving repository.xml from " + obrBaseUrl);
-            throw e;
-        }
-
-        InputStream input = null;
-        NodeList resources = null;
-        try {
-            URLConnection connection = openConnection(url);
-            // We always want the newest repository.xml file.
-            connection.setUseCaches(false);
+    private Button createDeleteOBREntryButton(final OBREntry entry) {
+        Button button = new Button("x");
+        button.setStyleName(Reindeer.BUTTON_SMALL);
+        button.setDescription("Delete " + entry.getName());
 
-            input = connection.getInputStream();
+        button.addListener(new ClickListener() {
+            @Override
+            public void buttonClick(ClickEvent event) {
+                event.getButton().setEnabled(false);
 
-            try {
-                XPath xpath = XPathFactory.newInstance().newXPath();
-                // this XPath expressing will find all 'resource' elements which
-                // have an attribute 'uri'.
-                resources = (NodeList) xpath.evaluate(XPATH_QUERY, new InputSource(input), XPathConstants.NODESET);
-            }
-            catch (XPathExpressionException e) {
-                logError("Error evaluating XPath expression.", e);
-                throw e;
-            }
-        }
-        catch (IOException e) {
-            logError("Error reading repository metadata.", e);
-            throw e;
-        }
-        finally {
-            if (input != null) {
                 try {
-                    input.close();
+                    int code = OBRUtil.deleteOBREntry(getConnectionFactory(), entry, m_obrUrl);
+                    if (code == HttpServletResponse.SC_OK) {
+                        m_artifactsTable.removeItem(entry.getUri());
+                    }
+                    else {
+                        showErrorNotification("Failed to delete resource", "The OBR returned an unexpected response code: " + code);
+                    }
                 }
                 catch (IOException e) {
-                    // too bad, no worries.
+                    showErrorNotification("Failed to delete resource", "Reason: " + e.getMessage());
                 }
-            }
-        }
 
-        List<OBREntry> obrList = new ArrayList<OBREntry>();
-        for (int nResource = 0; nResource < resources.getLength(); nResource++) {
-            Node resource = resources.item(nResource);
-            NamedNodeMap attr = resource.getAttributes();
-
-            String uri = getNamedItemText(attr, "uri");
-            if (uri == null || uri.equals("")) {
-                logError("Skipping resource without uri from repository " + obrBaseUrl);
-                continue;
-            }
-
-            String name = getNamedItemText(attr, "presentationname");
-            String symbolicname = getNamedItemText(attr, "symbolicname");
-            String version = getNamedItemText(attr, "version");
-
-            if (name == null || name.equals("")) {
-                if (symbolicname != null && !symbolicname.equals("")) {
-                    name = symbolicname;
-                }
-                else {
-                    name = new File(uri).getName();
-                }
             }
+        });
 
-            obrList.add(new OBREntry(name, symbolicname, version, uri));
-        }
+        return button;
+    }
 
-        return obrList;
+    private URL createOBRUrl(Object itemID) throws MalformedURLException {
+        return new URL(m_obrUrl, String.valueOf(itemID));
     }
-    
+
     /**
-     * Delete an entry from the OBR.
+     * Gets the bundles.
      * 
-     * @param entry
-     *            The OBREntry
+     * @param dataSource
+     *            the datasource to fill;
      * @param obrBaseUrl
-     *            The OBR base url
-     * @return The HTTP response code
-     * @throws IOException
-     *             If the HTTP operation fails
+     *            the obr base url
+     * @return the bundles
+     * @throws Exception
+     *             the exception
      */
-    private int deleteOBREntry(OBREntry entry, URL obrBaseUrl) throws IOException {
-
-        HttpURLConnection connection = null;
-        try {
-            URL endpointUrl = new URL(obrBaseUrl, entry.getUri());
-            connection = (HttpURLConnection) openConnection(endpointUrl);
-            connection.setDoInput(true);
-            connection.setDoOutput(false);
-            connection.setInstanceFollowRedirects(false);
-            connection.setRequestMethod("DELETE");
-            connection.connect();
-            return connection.getResponseCode();
+    private void populateArtifactTable(Table dataSource, URL obrBaseUrl) throws Exception {
+        // retrieve the repository.xml as a stream
+        List<OBREntry> obrList = OBRUtil.getAvailableOBREntries(getConnectionFactory(), getArtifactRepository(), obrBaseUrl, m_repositoryXML);
+        if (obrList.isEmpty()) {
+            logError("No new data in OBR.");
+            return;
         }
-        finally {
-            if (connection != null) {
-                connection.disconnect();
-            }
+
+        // Create a list of all bundle names
+        for (OBREntry entry : obrList) {
+            Item item = dataSource.addItem(entry.getUri());
+            item.getItemProperty(PROPERTY_SYMBOLIC_NAME).setValue(entry.getName());
+            item.getItemProperty(PROPERTY_VERSION).setValue(entry.getVersion());
+            item.getItemProperty(PROPERTY_PURGE).setValue(createDeleteOBREntryButton(entry));
         }
     }
-    
+
     /**
-     * Create a new button that delete an OBREntry on-click.
+     * Logs a given message at the error level.
+     * <p>
+     * If there's no log service present, this method will silently ignore the log statement.
+     * </p>
      * 
-     * @param entry
-     *            The entry
-     * @return The button
+     * @param aMessage
+     *            the message to log.
      */
-    private Button createDeleteOBREntryButton(final OBREntry entry) {
-
-        return new Button("X", new ClickListener() {
-
-            @Override
-            public void buttonClick(ClickEvent event) {
-
-                try {
-                    int code = deleteOBREntry(entry, m_obrUrl);
-                    if (code == HttpServletResponse.SC_OK) {
-                        m_artifactsTable.removeItem(entry.getUri());
-                        m_artifactsTable.refreshRowCache();
-                    }
-                    else {
-                        getWindow().showNotification("The OBR returned an unexpected response code: " + code,
-                            Notification.TYPE_ERROR_MESSAGE);
-
-                    }
-                }
-                catch (IOException e) {
-                    getWindow().showNotification("Failed to delete resource!", "<br/>Reason: " + e.getMessage(),
-                        Notification.TYPE_ERROR_MESSAGE);
-                    e.printStackTrace();
-                }
+    private void logError(String aMessage) {
+        LogService logger = getLogger();
+        if (logger != null) {
+            logger.log(LogService.LOG_ERROR, aMessage);
+        }
+    }
 
-            }
-        });
+    /** Shows a warning messsage on screen. */
+    private void showWarningNotification(final String aTitle, final String aMessage) {
+        getParent().showNotification(aTitle, aMessage, Notification.TYPE_WARNING_MESSAGE);
     }
-}
\ No newline at end of file
+}

Copied: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationManager.java (from r1535767, ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java)
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationManager.java?p2=ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationManager.java&p1=ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java&r1=1535767&r2=1536411&rev=1536411&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationManager.java Mon Oct 28 17:03:53 2013
@@ -20,13 +20,23 @@
 package org.apache.ace.webui.vaadin;
 
 import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
+import org.apache.ace.client.repository.object.ArtifactObject;
 import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
+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.apache.ace.client.repository.stateful.StatefulTargetObject;
 
 /**
  * Defines methods for removing associations.
  */
-public interface AssociationRemover {
+public interface AssociationManager {
+
+    void createArtifact2FeatureAssociation(ArtifactObject artifact, FeatureObject feature);
+
+    void createDistribution2TargetAssociation(DistributionObject distribution, StatefulTargetObject target);
+
+    void createFeature2DistributionAssociation(FeatureObject feature, DistributionObject distribution);
 
     /**
      * @param association
@@ -36,11 +46,10 @@ public interface AssociationRemover {
     /**
      * @param association
      */
-    void removeAssociation(Feature2DistributionAssociation association);
-    
+    void removeAssociation(Distribution2TargetAssociation association);
+
     /**
      * @param association
      */
-    void removeAssociation(Distribution2TargetAssociation association);
-
+    void removeAssociation(Feature2DistributionAssociation association);
 }

Added: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/OBRUtil.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/OBRUtil.java?rev=1536411&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/OBRUtil.java (added)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/OBRUtil.java Mon Oct 28 17:03:53 2013
@@ -0,0 +1,227 @@
+/*
+ * 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.webui.vaadin;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.ace.client.repository.helper.bundle.BundleHelper;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.repository.ArtifactRepository;
+import org.apache.ace.connectionfactory.ConnectionFactory;
+import org.apache.ace.webui.domain.OBREntry;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * Utility methods for handling OBRs.
+ */
+public final class OBRUtil {
+    private static final String XPATH_QUERY = "/repository/resource[@uri]";
+
+    /**
+     * Returns all available OBR entries that can be added to the artifact repository.
+     * 
+     * @param connectionFactory
+     * @param artifactRepository
+     * @param obrBaseUrl
+     * @param repositoryName
+     * @return
+     * @throws XPathExpressionException
+     * @throws IOException
+     */
+    public static List<OBREntry> getAvailableOBREntries(ConnectionFactory connectionFactory, ArtifactRepository artifactRepository, URL obrBaseUrl, String repositoryName) throws XPathExpressionException, IOException {
+        List<OBREntry> entries = parseOBRRepository(connectionFactory, obrBaseUrl, repositoryName);
+        entries.removeAll(getUsedOBRArtifacts(artifactRepository, obrBaseUrl));
+        return entries;
+    }
+
+    /**
+     * Delete an entry from the OBR.
+     * 
+     * @param entry
+     *            The OBREntry
+     * @param obrBaseUrl
+     *            The OBR base url
+     * @return The HTTP response code
+     * @throws IOException
+     *             If the HTTP operation fails
+     */
+    public static int deleteOBREntry(ConnectionFactory connectionFactory, OBREntry entry, URL obrBaseUrl) throws IOException {
+        HttpURLConnection connection = null;
+
+        try {
+            URL endpointUrl = new URL(obrBaseUrl, entry.getUri());
+            connection = (HttpURLConnection) connectionFactory.createConnection(endpointUrl);
+            connection.setDoInput(true);
+            connection.setDoOutput(false);
+            connection.setInstanceFollowRedirects(false);
+            connection.setRequestMethod("DELETE");
+            connection.connect();
+            return connection.getResponseCode();
+        }
+        finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    /**
+     * Converts a given artifact object to an OBR entry.
+     * 
+     * @param artifactObject
+     *            the artifact object to convert;
+     * @param obrBase
+     *            the obr base url.
+     * @return an OBR entry instance, never <code>null</code>.
+     */
+    private static OBREntry convertToOBREntry(ArtifactObject artifactObject, String obrBase) {
+        String name = artifactObject.getName();
+        String symbolicName = artifactObject.getAttribute(BundleHelper.KEY_SYMBOLICNAME);
+        String version = artifactObject.getAttribute(BundleHelper.KEY_VERSION);
+        String relativeURL = artifactObject.getURL().substring(obrBase.length());
+        return new OBREntry(name, symbolicName, version, relativeURL);
+    }
+
+    /**
+     * Gets the actual text from a named item contained in the given node map.
+     * 
+     * @param map
+     *            the node map to get the named item from;
+     * @param name
+     *            the name of the item to get.
+     * @return the text of the named item, can be <code>null</code> in case the named item does not exist, or has no
+     *         text.
+     */
+    private static String getNamedItemText(NamedNodeMap map, String name) {
+        Node namedItem = map.getNamedItem(name);
+        if (namedItem == null) {
+            return null;
+        }
+        else {
+            return namedItem.getTextContent();
+        }
+    }
+
+    /**
+     * Builds a list of all OBR artifacts currently in use.
+     * 
+     * @param obrBaseUrl
+     *            the base URL of the OBR, cannot be <code>null</code>.
+     * @return a list of used OBR entries, never <code>null</code>.
+     * @throws IOException
+     *             in case an artifact repository is not present.
+     */
+    private static List<OBREntry> getUsedOBRArtifacts(ArtifactRepository artifactRepository, URL obrBaseUrl) throws IOException {
+        final String baseURL = obrBaseUrl.toExternalForm();
+
+        List<OBREntry> fromRepository = new ArrayList<OBREntry>();
+
+        List<ArtifactObject> artifactObjects = artifactRepository.get();
+        artifactObjects.addAll(artifactRepository.getResourceProcessors());
+
+        for (ArtifactObject ao : artifactObjects) {
+            String artifactURL = ao.getURL();
+            if ((artifactURL != null) && artifactURL.startsWith(baseURL)) {
+                // we now know this artifact comes from the OBR we are querying,
+                // so we are interested.
+                fromRepository.add(convertToOBREntry(ao, baseURL));
+            }
+        }
+        return fromRepository;
+    }
+
+    /**
+     * Parses the 'repository.xml' from OBR.
+     * 
+     * @param obrBaseUrl
+     *            the base URL to access the OBR, cannot be <code>null</code>.
+     * @return a list of parsed OBR entries, never <code>null</code>.
+     * @throws XPathExpressionException
+     *             in case OBR repository is invalid, or incorrect;
+     * @throws IOException
+     *             in case of problems accessing the 'repository.xml' file.
+     */
+    private static List<OBREntry> parseOBRRepository(ConnectionFactory connectionFactory, URL obrBaseUrl, String repositoryName) throws XPathExpressionException, IOException {
+        InputStream input = null;
+        NodeList resources = null;
+        try {
+            URL url = new URL(obrBaseUrl, repositoryName);
+            URLConnection connection = connectionFactory.createConnection(url);
+            // We always want the newest repository.xml file.
+            connection.setUseCaches(false);
+
+            input = connection.getInputStream();
+
+            XPath xpath = XPathFactory.newInstance().newXPath();
+            // this XPath expressing will find all 'resource' elements which
+            // have an attribute 'uri'.
+            resources = (NodeList) xpath.evaluate(XPATH_QUERY, new InputSource(input), XPathConstants.NODESET);
+        }
+        finally {
+            if (input != null) {
+                try {
+                    input.close();
+                }
+                catch (IOException e) {
+                    // too bad, no worries.
+                }
+            }
+        }
+
+        List<OBREntry> obrList = new ArrayList<OBREntry>();
+        for (int nResource = 0; nResource < resources.getLength(); nResource++) {
+            Node resource = resources.item(nResource);
+            NamedNodeMap attr = resource.getAttributes();
+
+            String uri = getNamedItemText(attr, "uri");
+            String name = getNamedItemText(attr, "presentationname");
+            String symbolicname = getNamedItemText(attr, "symbolicname");
+            String version = getNamedItemText(attr, "version");
+
+            if (name == null || name.equals("")) {
+                if (symbolicname != null && !symbolicname.equals("")) {
+                    name = symbolicname;
+                }
+                else {
+                    name = new File(uri).getName();
+                }
+            }
+
+            obrList.add(new OBREntry(name, symbolicname, version, uri));
+        }
+
+        return obrList;
+    }
+}

Propchange: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/OBRUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native