You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2012/07/05 14:10:06 UTC

svn commit: r1357570 [31/34] - in /ace/sandbox/marrs: cnf/ cnf/ext/ cnf/lib/ cnf/releaserepo/ cnf/repo/ cnf/repo/.obrcache/ cnf/repo/.obrcache/http%3A%2F%2Fbundles.bndtools.org.s3.amazonaws.com%2Fcom.jcraft.jsch/ cnf/repo/.obrcache/http%3A%2F%2Fbundles...

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/NamedObject.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,11 @@
+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/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/UIExtensionFactory.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/UIExtensionFactory.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/UIExtensionFactory.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/UIExtensionFactory.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,41 @@
+/*
+ * 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 java.util.Map;
+
+import com.vaadin.ui.Component;
+
+/**
+ * Creates components for named extension points in the Vaadin UI. Extension factories
+ * are used throughout the UI to allow other bundles to contribute features.
+ */
+public interface UIExtensionFactory {
+    public static final String EXTENSION_POINT_KEY = "extension_point";
+    public static final String EXTENSION_POINT_VALUE_ARTIFACT = "artifact";
+    public static final String EXTENSION_POINT_VALUE_FEATURE = "feature";
+    public static final String EXTENSION_POINT_VALUE_DISTRIBUTION = "distribution";
+    public static final String EXTENSION_POINT_VALUE_TARGET = "target";
+    
+    /**
+     * Creates a UI component for use in the extension point. The contents of the
+     * context are extension-point dependent.
+     */
+    Component create(Map<String, Object> context);
+}

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedArtifactObject.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedArtifactObject.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedArtifactObject.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedArtifactObject.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.webui.domain;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.webui.NamedObject;
+
+public class NamedArtifactObject implements NamedObject {
+    private final ArtifactObject m_target;
+
+    public NamedArtifactObject(ArtifactObject target) {
+        m_target = target;
+    }
+
+    public String getName() {
+        return m_target.getName();
+    }
+
+    public String getDescription() {
+        return m_target.getDescription();
+    }
+
+    public void setDescription(String description) {
+        m_target.setDescription(description);
+    }
+
+    public RepositoryObject getObject() {
+        return m_target;
+    }
+
+    public String getDefinition() {
+        return m_target.getDefinition();
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedDistributionObject.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedDistributionObject.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedDistributionObject.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedDistributionObject.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.webui.domain;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.webui.NamedObject;
+
+public class NamedDistributionObject implements NamedObject {
+    private final DistributionObject m_target;
+
+    public NamedDistributionObject(DistributionObject target) {
+        m_target = target;
+    }
+
+    public String getName() {
+        return m_target.getName();
+    }
+
+    public String getDescription() {
+        return m_target.getDescription();
+    }
+
+    public void setDescription(String description) {
+        m_target.setDescription(description);
+    }
+
+    public RepositoryObject getObject() {
+        return m_target;
+    }
+
+    public String getDefinition() {
+        return m_target.getDefinition();
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedFeatureObject.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedFeatureObject.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedFeatureObject.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedFeatureObject.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.webui.domain;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.webui.NamedObject;
+
+public class NamedFeatureObject implements NamedObject {
+    private final FeatureObject m_target;
+
+    public NamedFeatureObject(FeatureObject target) {
+        m_target = target;
+    }
+
+    public String getName() {
+        return m_target.getName();
+    }
+
+    public String getDescription() {
+        return m_target.getDescription();
+    }
+
+    public void setDescription(String description) {
+        m_target.setDescription(description);
+    }
+
+    public RepositoryObject getObject() {
+        return m_target;
+    }
+
+    public String getDefinition() {
+        return m_target.getDefinition();
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedTargetObject.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedTargetObject.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedTargetObject.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedTargetObject.java Thu Jul  5 12:09:30 2012
@@ -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.RepositoryObject;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.webui.NamedObject;
+
+public class NamedTargetObject implements NamedObject {
+    private final RepositoryObject m_target;
+
+    public NamedTargetObject(StatefulTargetObject target) {
+        m_target = target;
+    }
+
+    public NamedTargetObject(TargetObject target) {
+        m_target = target;
+    }
+
+    public String getName() {
+        if (m_target instanceof StatefulTargetObject) {
+            return ((StatefulTargetObject) m_target).getID();
+        }
+        else if (m_target instanceof TargetObject) {
+            return ((TargetObject) m_target).getID();
+        }
+        return null;
+    }
+
+    public String getDescription() {
+        return "";
+    }
+
+    public void setDescription(String description) {
+        throw new IllegalArgumentException();
+    }
+
+    public RepositoryObject getObject() {
+        return m_target;
+    }
+
+    public String getDefinition() {
+        return m_target.getDefinition();
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/OBREntry.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+public class OBREntry {
+    private final String m_symbolicName;
+    private final String m_version;
+    private final String m_uri;
+
+    public OBREntry(String symbolicName, String version, String uri) {
+        m_symbolicName = symbolicName;
+        m_version = version;
+        m_uri = uri;
+    }
+
+    public String getVersion() {
+        return m_version;
+    }
+
+    public String getSymbolicName() {
+        return m_symbolicName;
+    }
+
+    public String getUri() {
+        return m_uri;
+    }
+
+    @Override
+    public int hashCode() {
+        return m_uri.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return m_uri.equals(((OBREntry) obj).m_uri);
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/packageinfo
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/packageinfo?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/packageinfo (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/packageinfo Thu Jul  5 12:09:30 2012
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Activator.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,88 @@
+/*
+ * 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.util.Map;
+import java.util.Properties;
+
+import javax.servlet.Servlet;
+
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.webui.NamedObject;
+import org.apache.ace.webui.UIExtensionFactory;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.http.HttpService;
+
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.VerticalLayout;
+
+public class Activator extends DependencyActivatorBase {
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        manager.add(createComponent()
+            .setImplementation(VaadinResourceHandler.class)
+            .add(createServiceDependency()
+                .setService(HttpService.class)
+                .setRequired(true)
+            )
+            );
+        // register the main application for the ACE UI client
+        manager.add(createComponent()
+            .setInterface(Servlet.class.getName(), null)
+            .setImplementation(VaadinServlet.class)
+            .add(createConfigurationDependency()
+                .setPid(VaadinServlet.PID).setPropagate(true))
+            );
+
+        Properties props = new Properties();
+        props.put(UIExtensionFactory.EXTENSION_POINT_KEY, UIExtensionFactory.EXTENSION_POINT_VALUE_TARGET);
+        props.put(Constants.SERVICE_RANKING, Integer.valueOf(100));
+
+        // shows off components that are contributed by extensions
+        manager.add(createComponent()
+            .setInterface(UIExtensionFactory.class.getName(), props)
+            .setImplementation(new UIExtensionFactory() {
+                public Component create(Map<String, Object> context) {
+                    VerticalLayout vl = new VerticalLayout();
+                    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" +
+                        "Installed version  : " + (target.getLastInstallVersion() == null ? "(none)" : target.getLastInstallVersion()) + "\n" +
+                        "Available version  : " + target.getCurrentVersion() + "\n" +
+                        "Store state        : " + target.getStoreState() + "\n" +
+                        "Provisioning state : " + target.getProvisioningState() + "\n" +
+                        "Registration state : " + target.getRegistrationState());
+                    info.setContentMode(Label.CONTENT_PREFORMATTED);
+                    vl.addComponent(info);
+                    return vl;
+                }
+            })
+            );
+    }
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+    }
+}

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AddArtifactWindow.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,764 @@
+/*
+ * 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.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+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.webui.domain.OBREntry;
+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;
+
+/**
+ * Provides a dialog for uploading new artifacts to ACE, or selecting existing artifacts from the repository.
+ */
+abstract class AddArtifactWindow extends Window {
+
+    /**
+     * 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 REPOSITORY_XML = "repository.xml";
+    private static final String XPATH_QUERY = "/repository/resource[@uri]";
+
+    private final File m_sessionDir;
+    private final URL m_obrUrl;
+
+    private final List<File> m_uploadedArtifacts = new ArrayList<File>();
+    private final Button m_searchButton;
+    private final Button m_closeButton;
+    private final Table m_artifactsTable;
+
+    /**
+     * 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.
+     */
+    public AddArtifactWindow(File sessionDir, URL obrUrl) {
+        super("Add artifact");
+
+        m_sessionDir = sessionDir;
+        m_obrUrl = obrUrl;
+
+        setModal(true);
+        setWidth("50em");
+
+        m_artifactsTable = new ArtifactTable();
+        m_artifactsTable.setCaption("Artifacts in repository");
+
+        final IndexedContainer dataSource = (IndexedContainer) m_artifactsTable.getContainerDataSource();
+
+        final Table uploadedArtifacts = new ArtifactTable();
+        uploadedArtifacts.setCaption("Uploaded artifacts");
+        uploadedArtifacts.setSelectable(false);
+
+        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);
+                }
+            }
+
+            @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);
+
+                logError("Upload of " + uploadedArtifact + " failed.");
+            }
+        };
+
+        final Upload uploadArtifact = new Upload("Upload Artifact", uploadHandler);
+        uploadArtifact.addListener((SucceededListener) uploadHandler);
+        uploadArtifact.addListener((FailedListener) uploadHandler);
+        uploadArtifact.setImmediate(true);
+
+        final DragAndDropWrapper finalUploadedArtifacts = new DragAndDropWrapper(uploadedArtifacts);
+        finalUploadedArtifacts.setDropHandler(new ArtifactDropHandler(uploadHandler));
+
+        addListener(new Window.CloseListener() {
+            public void windowClose(CloseEvent e) {
+                for (File artifact : m_uploadedArtifacts) {
+                    artifact.delete();
+                }
+            }
+        });
+
+        HorizontalLayout searchBar = new HorizontalLayout();
+        searchBar.setMargin(false);
+        searchBar.setSpacing(true);
+
+        final TextField searchField = new TextField();
+        searchField.setImmediate(true);
+        searchField.setValue("");
+
+        m_searchButton = new Button("Search", new ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                String searchValue = (String) searchField.getValue();
+
+                dataSource.removeAllContainerFilters();
+
+                if (searchValue != null && searchValue.trim().length() > 0) {
+                    dataSource.addContainerFilter(ArtifactTable.PROPERTY_SYMBOLIC_NAME, searchValue,
+                        true /* ignoreCase */, false /* onlyMatchPrefix */);
+                }
+            }
+        });
+        m_searchButton.setImmediate(true);
+
+        searchBar.addComponent(searchField);
+        searchBar.addComponent(m_searchButton);
+
+        m_closeButton = new Button("Add", new ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                // Import all "local" (existing) bundles...
+                importLocalBundles(m_artifactsTable);
+                // Import all "remote" (non existing) bundles...
+                importRemoteBundles(m_uploadedArtifacts);
+
+                closeWindow();
+
+                // TODO: make a decision here so now we have enough information
+                // to show a list of imported artifacts (added) but do we want
+                // to show this list or do we just assume the user will see the
+                // new artifacts in the left most column? do we also report
+                // failures? or only report if there were failures?
+            }
+        });
+        m_closeButton.setImmediate(true);
+
+        VerticalLayout layout = (VerticalLayout) getContent();
+        layout.setMargin(true);
+        layout.setSpacing(true);
+
+        layout.addComponent(searchBar);
+        layout.addComponent(m_artifactsTable);
+        layout.addComponent(uploadArtifact);
+        layout.addComponent(finalUploadedArtifacts);
+        // The components added to the window are actually added to the window's
+        // layout; you can use either. Alignments are set using the layout
+        layout.addComponent(m_closeButton);
+        layout.setComponentAlignment(m_closeButton, Alignment.MIDDLE_RIGHT);
+
+        searchField.focus();
+    }
+
+    /**
+     * 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>.
+     */
+    public final void showWindow(Window parent) {
+        try {
+            // Fill the artifacts table with the data from the OBR...
+            getBundles(m_artifactsTable);
+            
+            parent.addWindow(this);
+        }
+        catch (Exception e) {
+            // We've not yet added this window to the given parent, so we cannot use #showErrorNotification here...
+            parent.showNotification("Failed to retrieve OBR repository!", "Reason: <br/>" + e.getMessage(), Notification.TYPE_ERROR_MESSAGE);
+            logError("Failed to retrieve OBR repository!", e);
+        }
+    }
+
+    /**
+     * Closes this window.
+     */
+    final void closeWindow() {
+        // close the window by removing it from the parent window
+        getParent().removeWindow(this);
+    }
+
+    /**
+     * 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>.
+     * @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();
+        if (selectedItems != null && !selectedItems.isEmpty()) {
+            for (String itemID : selectedItems) {
+                try {
+                    added.add(importLocalBundle(new URL(m_obrUrl, itemID)));
+                }
+                catch (Exception exception) {
+                    Item item = artifacts.getItem(itemID);
+
+                    Object symbolicName = item.getItemProperty(ArtifactTable.PROPERTY_SYMBOLIC_NAME).getValue();
+                    Object version = item.getItemProperty(ArtifactTable.PROPERTY_VERSION).getValue();
+
+                    showErrorNotification("Import artifact failed", "Artifact " + symbolicName + " " + version
+                        + "<br />could not be imported into the repository."
+                        + "<br />Reason: " + exception.getMessage());
+
+                    logError("Import of " + symbolicName + " " + version + " failed.", exception);
+                }
+            }
+        }
+        return added;
+    }
+
+    /**
+     * Import remote bundles.
+     * 
+     * @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>();
+
+        for (File artifact : uploadedArtifacts) {
+            try {
+                added.add(importRemoteBundle(artifact.toURI().toURL()));
+            }
+            catch (Exception exception) {
+                showErrorNotification("Import artifact failed", "<br/>Artifact '"
+                    + artifact.getName()
+                    + "' could not be imported into the repository.<br />"
+                    + "Reason: " + exception.getMessage());
+
+                logError("Import of " + artifact.getAbsolutePath() + " failed.", exception);
+            }
+            finally {
+                artifact.delete();
+            }
+        }
+
+        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);
+    }
+
+    /**
+     * 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.
+     */
+    final void logError(String aMessage) {
+        LogService logger = getLogger();
+        if (logger != null) {
+            logger.log(LogService.LOG_ERROR, aMessage);
+        }
+    }
+
+    /**
+     * 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, Throwable aException) {
+        LogService logger = getLogger();
+        if (logger != null) {
+            logger.log(LogService.LOG_ERROR, aMessage, aException);
+        }
+    }
+
+    /**
+     * @return the artifact repository.
+     */
+    protected abstract ArtifactRepository getArtifactRepository();
+
+    /**
+     * @return the log service.
+     */
+    protected abstract LogService getLogger();
+    
+    /**
+     * @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 artifactURL the artifact url.
+     * @return an OBR entry instance, never <code>null</code>.
+     */
+    private OBREntry convertToOBREntry(ArtifactObject artifactObject, String artifactURL) {
+        return new OBREntry(artifactObject.getName(), artifactObject.getAttribute(BundleHelper.KEY_VERSION), new File(artifactURL).getName());
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Gets the bundles.
+     * 
+     * @param dataSource the datasource to fill;
+     * @param obrBaseUrl the obr base url
+     * @return the bundles
+     * @throws Exception the exception
+     */
+    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 (OBREntry s : obrList) {
+            String uri = s.getUri();
+            String symbolicName = s.getSymbolicName();
+            if (symbolicName == null || symbolicName.length() == 0) {
+                symbolicName = uri;
+            }
+            String version = s.getVersion();
+
+            Item item = dataSource.addItem(uri);
+            item.getItemProperty(ArtifactTable.PROPERTY_SYMBOLIC_NAME).setValue(symbolicName);
+            item.getItemProperty(ArtifactTable.PROPERTY_VERSION).setValue(version);
+        }
+    }
+
+    /**
+     * 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.startsWith(baseURL)) {
+                // we now know this artifact comes from the OBR we are querying,
+                // so we are interested.
+                fromRepository.add(convertToOBREntry(ao, artifactURL));
+            }
+        }
+        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 */);
+    }
+
+    /**
+     * 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 List<OBREntry> parseOBRRepository(URL obrBaseUrl) throws XPathExpressionException, IOException {
+        URL url = null;
+        try {
+            url = new URL(obrBaseUrl, REPOSITORY_XML);
+        }
+        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);
+
+            input = connection.getInputStream();
+
+            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();
+                }
+                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 symbolicname = getNamedItemText(attr, "symbolicname");
+            String version = getNamedItemText(attr, "version");
+
+            obrList.add(new OBREntry(symbolicname, version, uri));
+        }
+
+        return obrList;
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/ArtifactTable.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/ArtifactTable.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/ArtifactTable.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/ArtifactTable.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,45 @@
+/*
+ * 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 com.vaadin.ui.Table;
+
+/**
+ * Provides a custom table for displaying artifacts by their symbolic name and version.
+ */
+public class ArtifactTable extends Table {
+
+    public static final String PROPERTY_SYMBOLIC_NAME = "symbolic name";
+    public static final String PROPERTY_VERSION = "version";
+
+    public ArtifactTable() {
+        super("Artifacts");
+
+        addContainerProperty(PROPERTY_SYMBOLIC_NAME, String.class, null);
+        addContainerProperty(PROPERTY_VERSION, String.class, null);
+
+        setSizeFull();
+
+        setSelectable(true);
+        setMultiSelect(true);
+        setImmediate(true);
+
+        setHeight("15em");
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/AssociationRemover.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ace.webui.vaadin;
+
+import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
+import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
+import org.apache.ace.client.repository.object.Feature2DistributionAssociation;
+
+/**
+ * Defines methods for removing associations.
+ */
+public interface AssociationRemover {
+
+    /**
+     * @param association
+     */
+    void removeAssociation(Artifact2FeatureAssociation association);
+
+    /**
+     * @param association
+     */
+    void removeAssociation(Feature2DistributionAssociation association);
+    
+    /**
+     * @param association
+     */
+    void removeAssociation(Distribution2TargetAssociation association);
+
+}

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,241 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.webui.NamedObject;
+import org.apache.ace.webui.domain.NamedArtifactObject;
+import org.apache.ace.webui.domain.NamedDistributionObject;
+import org.apache.ace.webui.domain.NamedFeatureObject;
+import org.apache.ace.webui.domain.NamedTargetObject;
+
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.ui.Table;
+import com.vaadin.ui.Table.CellStyleGenerator;
+
+public class Associations {
+    private List<RepositoryObject> m_associatedItems = new ArrayList<RepositoryObject>();
+    private List<RepositoryObject> m_relatedItems = new ArrayList<RepositoryObject>();
+    private Table m_activeTable;
+    private Set<?> m_activeSelection;
+    private SelectionListener m_activeSelectionListener;
+
+    public void removeAssociatedItem(RepositoryObject item) {
+        m_associatedItems.remove(item);
+    }
+
+    public void clear() {
+        m_associatedItems.clear();
+        m_relatedItems.clear();
+    }
+
+    public boolean isActiveTable(Table table) {
+        return (m_activeTable != null) ? m_activeTable.equals(table) : false;
+    }
+
+    public Set<?> getActiveSelection() {
+        return m_activeSelection;
+    }
+
+    public RepositoryObject lookupInActiveSelection(Object item) {
+        return m_activeSelectionListener.lookup(item);
+    }
+
+    public void addAssociatedItems(List items) {
+        m_associatedItems.addAll(items);
+    }
+
+    public void addRelatedItems(List items) {
+        m_relatedItems.addAll(items);
+    }
+
+    public CellStyleGenerator createCellStyleGenerator() {
+        return new CellStyleGenerator() {
+            public String getStyle(Object itemId, Object propertyId) {
+                if (propertyId == null) {
+                    // no propertyId, styling row
+                    for (RepositoryObject o : m_associatedItems) {
+                        if (equals(itemId, o)) {
+                            return "associated";
+                        }
+                    }
+                    for (RepositoryObject o : m_relatedItems) {
+                        if (equals(itemId, o)) {
+                            return "related";
+                        }
+                    }
+                }
+                return null;
+            }
+
+            public boolean equals(Object itemId, RepositoryObject object) {
+                return (getNamedObject(object).getDefinition().equals(itemId));
+            }
+        };
+    }
+
+    public NamedObject getNamedObject(RepositoryObject object) {
+        if (object instanceof ArtifactObject) {
+            return new NamedArtifactObject((ArtifactObject) object);
+        }
+        else if (object instanceof FeatureObject) {
+            return new NamedFeatureObject((FeatureObject) object);
+        }
+        else if (object instanceof DistributionObject) {
+            return new NamedDistributionObject((DistributionObject) object);
+        }
+        else if (object instanceof StatefulTargetObject) {
+            return new NamedTargetObject((StatefulTargetObject) object);
+        }
+        else if (object instanceof TargetObject) {
+            return new NamedTargetObject((TargetObject) object);
+        }
+        return null;
+    }
+
+    /**
+     * Helper method to find all related {@link RepositoryObject}s in a given 'direction'
+     */
+    private <FROM extends RepositoryObject, TO extends RepositoryObject> List<TO> getRelated(FROM from,
+        Class<TO> toClass) {
+        // if the SGO is not backed by a GO yet, this will cause an exception
+        return from.getAssociations(toClass);
+    }
+
+    /**
+     * Helper method to find all related {@link RepositoryObject}s in a given 'direction', starting with a list of objects
+     */
+    private <FROM extends RepositoryObject, TO extends RepositoryObject> List<TO> getRelated(List<FROM> from,
+        Class<TO> toClass) {
+        List<TO> result = new ArrayList<TO>();
+        for (RepositoryObject o : from) {
+            result.addAll(getRelated(o, toClass));
+        }
+        return result;
+    }
+
+    public class SelectionListener implements Table.ValueChangeListener {
+        private final Table m_table;
+        private final Table[] m_tablesToRefresh;
+        private final ObjectRepository<? extends RepositoryObject> m_repository;
+        private final Class[] m_left;
+        private final Class[] m_right;
+
+        public SelectionListener(final Table table, final ObjectRepository<? extends RepositoryObject> repository,
+            final Class[] left, final Class[] right, final Table[] tablesToRefresh) {
+            m_table = table;
+            m_repository = repository;
+            m_left = left;
+            m_right = right;
+            m_tablesToRefresh = tablesToRefresh;
+        }
+
+        public void valueChange(ValueChangeEvent event) {
+
+            if (m_activeSelection != null && m_activeTable != null) {
+                if (!m_activeTable.equals(m_table)) {
+                    for (Object val : m_activeSelection) {
+                        m_activeTable.unselect(val);
+                    }
+                    m_table.requestRepaint();
+                }
+            }
+
+            m_activeSelectionListener = SelectionListener.this;
+
+            // set the active table
+            m_activeTable = m_table;
+
+            // in multiselect mode, a Set of itemIds is returned,
+            // in singleselect mode the itemId is returned directly
+            Set<?> value = (Set<?>) event.getProperty().getValue();
+
+            // remember the active selection too
+            m_activeSelection = value;
+
+            if (value != null) {
+                clear();
+                for (Object val : value) {
+                    RepositoryObject lo = lookup(val);
+                    if (lo != null) {
+                        List related = null;
+                        for (int i = 0; i < m_left.length; i++) {
+                            if (i == 0) {
+                                related = getRelated(lo, m_left[i]);
+                                m_associatedItems.addAll(related);
+                            }
+                            else {
+                                related = getRelated(related, m_left[i]);
+                                m_relatedItems.addAll(related);
+                            }
+                        }
+                        for (int i = 0; i < m_right.length; i++) {
+                            if (i == 0) {
+                                related = getRelated(lo, m_right[i]);
+                                m_associatedItems.addAll(related);
+                            }
+                            else {
+                                related = getRelated(related, m_right[i]);
+                                m_relatedItems.addAll(related);
+                            }
+                        }
+                    }
+
+                    for (Table t : m_tablesToRefresh) {
+                        t.requestRepaint();
+                    }
+                }
+            }
+        }
+
+        public RepositoryObject lookup(Object value) {
+            RepositoryObject object = null;
+            if (value instanceof String) {
+                object = m_repository.get((String) value);
+                if (object instanceof StatefulTargetObject) {
+                    StatefulTargetObject sgo = (StatefulTargetObject) object;
+                    if (sgo.isRegistered()) {
+                        object = sgo.getTargetObject();
+                    }
+                    else {
+                        object = null;
+                    }
+                }
+            }
+            return object;
+        }
+
+    }
+
+    public SelectionListener createSelectionListener(final Table table,
+        final ObjectRepository<? extends RepositoryObject> repository, final Class[] left, final Class[] right,
+        final Table[] tablesToRefresh) {
+        return new SelectionListener(table, repository, left, right, tablesToRefresh);
+    }
+}

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/EditWindow.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/EditWindow.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/EditWindow.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/EditWindow.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,178 @@
+/*
+ * 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.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ace.webui.NamedObject;
+import org.apache.ace.webui.UIExtensionFactory;
+
+import com.vaadin.event.ShortcutAction.KeyCode;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+
+/**
+ * Provides a generic editor for repository objects.
+ */
+public abstract class EditWindow extends Window {
+
+    protected final TextField m_name;
+    protected final TextField m_description;
+
+    /**
+     * @param object
+     * @param factories
+     */
+    public EditWindow(String caption, NamedObject object, List<UIExtensionFactory> factories) {
+        setModal(true);
+        setWidth("50em");
+        setCaption(caption);
+
+        m_name = new TextField("Name", object.getName());
+        m_name.setReadOnly(true);
+        m_name.setWidth("100%");
+
+        m_description = new TextField("Description", object.getDescription());
+        m_description.setWidth("100%");
+
+        initDialog(object, factories);
+    }
+
+    /**
+     * Shows this dialog on screen.
+     * 
+     * @param window the parent window to show this dialog on, cannot be <code>null</code>.
+     */
+    public void show(Window parent) {
+        if (getParent() != null) {
+            // window is already showing
+            parent.showNotification("Window is already open!");
+        }
+        else {
+            parent.addWindow(this);
+        }
+        setRelevantFocus();
+    }
+
+    /**
+     * Closes this dialog by removing it from the parent window.
+     */
+    protected void closeDialog() {
+        // close the window by removing it from the parent window
+        getParent().removeWindow(this);
+    }
+
+    /**
+     * Called when the {@link #onOk(String, String)} method failed with an exception.
+     * 
+     * @param e the exception to handle, never <code>null</code>.
+     */
+    protected abstract void handleError(Exception e);
+
+    /**
+     * @param object
+     * @param factories
+     */
+    protected void initDialog(final NamedObject object, List<UIExtensionFactory> factories) {
+        VerticalLayout fields = new VerticalLayout();
+        fields.setSpacing(true);
+        fields.addComponent(m_name);
+        fields.addComponent(m_description);
+
+        TabSheet tabs = new TabSheet();
+        tabs.setHeight("350px");
+        tabs.setWidth("100%");
+        tabs.setVisible(!factories.isEmpty());
+
+        Map<String, Object> context = new HashMap<String, Object>();
+        context.put("object", object);
+
+        for (UIExtensionFactory factory : factories) {
+            try {
+                tabs.addTab(factory.create(context));
+            }
+            catch (Throwable ex) {
+                // We ignore extension factories that throw exceptions
+                // TODO: log this or something
+                ex.printStackTrace();
+            }
+        }
+
+        Button okButton = new Button("Ok", new Button.ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                try {
+                    onOk((String) m_name.getValue(), (String) m_description.getValue());
+                    closeDialog();
+                }
+                catch (Exception e) {
+                    handleError(e);
+                }
+            }
+        });
+        // Allow enter to be used to close this dialog with enter directly...
+        okButton.setClickShortcut(KeyCode.ENTER);
+        okButton.addStyleName("primary");
+
+        Button cancelButton = new Button("Cancel", new Button.ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                closeDialog();
+            }
+        });
+
+        HorizontalLayout buttonBar = new HorizontalLayout();
+        buttonBar.setSpacing(true);
+        buttonBar.addComponent(okButton);
+        buttonBar.addComponent(cancelButton);
+
+        VerticalLayout layout = (VerticalLayout) getContent();
+        layout.setMargin(true);
+        layout.setSpacing(true);
+        layout.addComponent(fields);
+        layout.addComponent(tabs);
+        layout.addComponent(buttonBar);
+
+        // The components added to the window are actually added to the window's
+        // layout; you can use either. Alignments are set using the layout
+        layout.setComponentAlignment(buttonBar, Alignment.BOTTOM_RIGHT);
+    }
+
+    /**
+     * Called when the user acknowledges this window by pressing Ok.
+     * 
+     * @param name the value of the name field;
+     * @param description the value of the description field.
+     * @throws Exception in case the creation failed.
+     */
+    protected abstract void onOk(String name, String description) throws Exception;
+
+    /**
+     * Sets the focus to the name field.
+     */
+    private void setRelevantFocus() {
+        m_name.focus();
+    }
+}

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/GenericAddWindow.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/GenericAddWindow.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/GenericAddWindow.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/GenericAddWindow.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,142 @@
+/*
+ * 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 com.vaadin.event.ShortcutAction.KeyCode;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+
+public abstract class GenericAddWindow extends Window {
+
+    protected final TextField m_name;
+    protected final TextField m_description;
+
+    public GenericAddWindow(String caption) {
+        setModal(true);
+        setWidth("15em");
+        setCaption(caption);
+
+        m_name = new TextField("Name");
+        m_name.setWidth("100%");
+
+        m_description = new TextField("Description");
+        m_description.setWidth("100%");
+
+        initDialog();
+    }
+
+    /**
+     * Shows this dialog on screen.
+     * 
+     * @param window the parent window to show this dialog on, cannot be <code>null</code>.
+     */
+    public void show(final Window window) {
+        if (getParent() != null) {
+            // window is already showing
+            window.showNotification("Window is already open");
+        }
+        else {
+            // Open the subwindow by adding it to the parent window
+            window.addWindow(this);
+        }
+        setRelevantFocus();
+    }
+
+    /**
+     * Closes this dialog by removing it from the parent window.
+     */
+    protected void closeDialog() {
+        // close the window by removing it from the parent window
+        getParent().removeWindow(this);
+    }
+
+    /**
+     * Called when the {@link #onOk(String, String)} method failed with an exception.
+     * 
+     * @param e the exception to handle, never <code>null</code>.
+     */
+    protected abstract void handleError(Exception e);
+
+    /**
+     * Initializes this dialog by placing all components on it.
+     */
+    protected void initDialog() {
+        VerticalLayout fields = new VerticalLayout();
+        fields.setSpacing(true);
+        fields.addComponent(m_name);
+        fields.addComponent(m_description);
+
+        Button okButton = new Button("Ok", new Button.ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                try {
+                    onOk((String) m_name.getValue(), (String) m_description.getValue());
+                    closeDialog();
+                }
+                catch (Exception e) {
+                    handleError(e);
+                }
+            }
+        });
+        // Allow enter to be used to close this dialog with enter directly...
+        okButton.setClickShortcut(KeyCode.ENTER);
+        okButton.addStyleName("primary");
+
+        Button cancelButton = new Button("Cancel", new Button.ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                closeDialog();
+            }
+        });
+
+        HorizontalLayout buttonBar = new HorizontalLayout();
+        buttonBar.setSpacing(true);
+        buttonBar.addComponent(okButton);
+        buttonBar.addComponent(cancelButton);
+
+        VerticalLayout layout = (VerticalLayout) getContent();
+        layout.setMargin(true);
+        layout.setSpacing(true);
+        layout.addComponent(fields);
+        layout.addComponent(buttonBar);
+
+        // The components added to the window are actually added to the window's
+        // layout; you can use either. Alignments are set using the layout
+        layout.setComponentAlignment(buttonBar, Alignment.BOTTOM_RIGHT);
+    }
+
+    /**
+     * Called when the user acknowledges this window by pressing Ok.
+     * 
+     * @param name the value of the name field;
+     * @param description the value of the description field.
+     * @throws Exception in case the creation failed.
+     */
+    protected abstract void onOk(String name, String description) throws Exception;
+
+    /**
+     * Sets the focus to the name field.
+     */
+    private void setRelevantFocus() {
+        m_name.focus();
+    }
+}
\ No newline at end of file

Added: ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/LoginWindow.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/LoginWindow.java?rev=1357570&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/LoginWindow.java (added)
+++ ace/sandbox/marrs/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/LoginWindow.java Thu Jul  5 12:09:30 2012
@@ -0,0 +1,127 @@
+/*
+ * 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 org.osgi.service.log.LogService;
+
+import com.vaadin.event.ShortcutAction.KeyCode;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.PasswordField;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+
+/**
+ * Provides a simple login dialog.
+ */
+public class LoginWindow extends Window {
+
+    public interface LoginFunction {
+        boolean login(String name, String password);
+    }
+
+    private volatile LogService m_log;
+
+    private TextField m_name;
+    private PasswordField m_password;
+    private Button m_loginButton;
+    private LoginFunction m_loginFunction;
+
+    public LoginWindow(final LogService log, final LoginFunction loginFunction) {
+        super("Apache ACE Login");
+
+        m_log = log;
+        m_loginFunction = loginFunction;
+
+        setResizable(false);
+        setClosable(false);
+        setModal(true);
+        setWidth("15em");
+
+        m_name = new TextField("Name", "");
+        m_name.setImmediate(true);
+
+        m_password = new PasswordField("Password", "");
+        m_password.setImmediate(true);
+
+        m_loginButton = new Button("Login");
+        m_loginButton.setImmediate(true);
+        // Allow enter to be used to login directly...
+        m_loginButton.setClickShortcut(KeyCode.ENTER);
+        // Highlight this button as the default one...
+        m_loginButton.addStyleName("primary");
+
+        m_loginButton.addListener(new Button.ClickListener() {
+            public void buttonClick(ClickEvent event) {
+                final Button button = event.getButton();
+                button.setEnabled(false);
+
+                try {
+                    String username = (String) m_name.getValue();
+                    String password = (String) m_password.getValue();
+
+                    if (m_loginFunction.login(username, password)) {
+                        m_log.log(LogService.LOG_INFO, "Apache Ace WebUI succesfull login by user: " + username);
+
+                        closeWindow();
+                    }
+                    else {
+                        m_log.log(LogService.LOG_WARNING, "Apache Ace WebUI invalid username or password entered.");
+
+                        getParent().showNotification("Invalid username or password!");
+                        setRelevantFocus();
+                    }
+                }
+                finally {
+                    button.setEnabled(true);
+                }
+            }
+        });
+
+        final VerticalLayout content = (VerticalLayout) getContent();
+        content.setSpacing(true);
+        content.setMargin(true);
+        content.setSizeFull();
+
+        content.addComponent(m_name);
+        content.addComponent(m_password);
+        content.addComponent(m_loginButton);
+
+        content.setComponentAlignment(m_loginButton, Alignment.BOTTOM_CENTER);
+
+        setRelevantFocus();
+    }
+
+    /**
+     * Gives the username field the current focus.
+     */
+    void setRelevantFocus() {
+        m_name.focus();
+        m_name.selectAll();
+    }
+
+    /**
+     * Closes this login window.
+     */
+    public void closeWindow() {
+        getParent().removeWindow(this);
+    }
+}
\ No newline at end of file