You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2013/04/02 16:53:35 UTC

svn commit: r1463576 [5/8] - in /ace/trunk: org.apache.ace.client.repository.api/ org.apache.ace.client.repository.helper.base/ org.apache.ace.client.repository.helper.bundle/ org.apache.ace.client.repository.helper.configuration/ org.apache.ace.client...

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryObjectImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryObjectImpl.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryObjectImpl.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryObjectImpl.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,588 @@
+package org.apache.ace.client.repository.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ace.client.repository.Associatable;
+import org.apache.ace.client.repository.Association;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.RepositoryUtil;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+/**
+ * Represents a value-object as is part of the repository.<br>
+ * It stores the 'member' values of the repository object, and allows putting tags on this object by using <code>put()</code>
+ * and <code>remove()</code>. It 'looks' like a dictionary to allow filtering of it, using an ldap filter.
+ */
+public class RepositoryObjectImpl<T extends RepositoryObject> extends Dictionary<String, Object> implements RepositoryObject, EventHandler {
+    private final Map<String, String> m_attributes = new HashMap<String, String>();
+    private final Map<String, String> m_tags = new HashMap<String, String>();
+    @SuppressWarnings("unchecked")
+    private final Map<Class, List<Association>> m_associations = new HashMap<Class, List<Association>>();
+    private final ChangeNotifier m_notifier;
+    private final String m_xmlNode;
+    
+    private volatile boolean m_deleted = false;
+    private volatile boolean m_busy = false;
+
+    public RepositoryObjectImpl(ChangeNotifier notifier, String xmlNode) {
+        this((Map<String, String>) null, null, notifier, xmlNode);
+    }
+
+    public RepositoryObjectImpl(Map<String, String> attributes, ChangeNotifier notifier, String xmlNode) {
+        this(attributes, null, notifier, xmlNode);
+    }
+
+    public RepositoryObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier, String xmlNode) {
+        this(readMap(reader), readMap(reader), notifier, xmlNode);
+        readCustom(reader);
+    }
+
+    public RepositoryObjectImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier, String xmlNode) {
+        m_xmlNode = xmlNode;
+        if (attributes != null) {
+            m_attributes.putAll(attributes);
+        }
+        if (tags != null) {
+            m_tags.putAll(tags);
+        }
+        if (notifier == null) {
+            throw new IllegalArgumentException();
+        }
+        m_notifier = notifier;
+    }
+
+    protected void notifyChanged(Properties props) {
+        if (props == null) {
+            props = new Properties();
+        }
+        props.put(EVENT_ENTITY, this);
+        m_notifier.notifyChanged(TOPIC_CHANGED_SUFFIX, props, m_busy);
+    }
+
+    /**
+     * Returns an enumeration of the values in this object's dictionary.
+     */
+    @Override
+    public Enumeration<Object> elements() {
+        synchronized (m_attributes) {
+            return new ValuesEnumeration();
+        }
+    }
+
+    /**
+     * Gets the object associated with this key. Will return null when the key is not used; if the key is available for both the
+     * tags and the object's basic information, an array of two Strings will be returned.
+     */
+    @Override
+    public Object get(Object key) {
+        synchronized (m_attributes) {
+            String manifest = m_attributes.get(key);
+            String tag = m_tags.get(key);
+
+            if (manifest == null) {
+                return tag;
+            }
+            else if (tag == null) {
+                return manifest;
+            }
+            else {
+                return new String[] { tag, manifest };
+            }
+        }
+    }
+
+    /**
+     * Return whether the dictionary is empty.
+     */
+    @Override
+    public boolean isEmpty() {
+        synchronized (m_attributes) {
+            return m_attributes.isEmpty() && m_tags.isEmpty();
+        }
+    }
+
+    /**
+     * Returns an enumeration of the keys in this object.
+     */
+    @Override
+    public Enumeration<String> keys() {
+        synchronized (m_attributes) {
+            Set<String> keys = new HashSet<String>();
+            keys.addAll(m_attributes.keySet());
+            keys.addAll(m_tags.keySet());
+            return new KeysEnumeration(keys.iterator());
+        }
+    }
+
+    /**
+     * Unsupported operation.
+     * 
+     * @throws UnsupportedOperationException always
+     */
+    @Override
+    public Object put(String key, Object value) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Unsupported operation.
+     * 
+     * @throws UnsupportedOperationException always
+     */
+    @Override
+    public Object remove(Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns the number of keys in both this object's tags, and its 'member' keys.
+     */
+    @Override
+    public int size() {
+        synchronized (m_attributes) {
+            Set<String> keys = new HashSet<String>();
+            keys.addAll(m_attributes.keySet());
+            keys.addAll(m_tags.keySet());
+            return keys.size();
+        }
+    }
+
+    public String addAttribute(String key, String value) {
+        for (String s : getDefiningKeys()) {
+            if (s.equals(key)) {
+                throw new UnsupportedOperationException("The defining attribute " + key + " is not allowed to be changed.");
+            }
+        }
+        synchronized (m_attributes) {
+            ensureCurrent();
+            notifyChanged(null);
+            return m_attributes.put(key, value);
+        }
+    }
+
+    public String addTag(String key, String value) {
+        synchronized (m_attributes) {
+            ensureCurrent();
+            notifyChanged(null);
+            return m_tags.put(key, value);
+        }
+    }
+
+    public String getAttribute(String key) {
+        synchronized (m_attributes) {
+            return m_attributes.get(key);
+        }
+    }
+
+    public String getTag(String key) {
+        synchronized (m_attributes) {
+            return m_tags.get(key);
+        }
+    }
+
+    public Enumeration<String> getAttributeKeys() {
+        synchronized (m_attributes) {
+            return new KeysEnumeration(new HashSet<String>(m_attributes.keySet()).iterator());
+        }
+    }
+
+    public Dictionary<String, Object> getDictionary() {
+        return this;
+    }
+
+    public Enumeration<String> getTagKeys() {
+        synchronized (m_attributes) {
+            return new KeysEnumeration(new HashSet<String>(m_tags.keySet()).iterator());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void add(Association association, Class clazz) {
+        synchronized (m_associations) {
+            List<Association> associations = m_associations.get(clazz);
+            if (associations == null) {
+                associations = new ArrayList<Association>();
+                m_associations.put(clazz, associations);
+            }
+            associations.add(association);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void remove(Association association, Class clazz) {
+        synchronized (m_associations) {
+            List<Association> associations = m_associations.get(clazz);
+            if (associations != null) {
+                associations.remove(association);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <A extends Associatable> List<A> getAssociations(Class<A> clazz) {
+        synchronized (m_associations) {
+            List<A> result = new ArrayList<A>();
+            List<Association> associations = m_associations.get(clazz);
+            if (associations != null) {
+                for (Association association : associations) {
+                    List<A> otherSide = association.getTo(this);
+                    if (otherSide != null) {
+                        // If the other side is null, the association
+                        // is not satisfied.
+                        result.addAll(otherSide);
+                    }
+                }
+            }
+            return result;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean isAssociated(Object obj, Class clazz) {
+        synchronized (m_associations) {
+            if (obj == null) {
+                return false;
+            }
+            List<Association> associations = m_associations.get(clazz);
+            if (associations != null) {
+                for (Association association : associations) {
+                    if (association.getTo(this).contains(obj)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <TA extends Associatable, A extends Association> List<A> getAssociationsWith(Associatable other, Class<TA> clazz, Class<A> associationType) {
+        List<A> result = new ArrayList<A>();
+        synchronized (m_associations) {
+            if (other == null) {
+                return result;
+            }
+            List<Association> associations = m_associations.get(clazz);
+            if (associations != null) {
+                for (Association association : associations) {
+                    if (association.getTo(this).contains(other)) {
+                        result.add((A) association);
+                    }
+                }
+            }
+            return result;
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public boolean equals(Object o) {
+        synchronized(m_attributes) {
+            if ((o == null) || !(getClass().isInstance(o))) {
+                return false;
+            }
+            if (m_attributes.size() == 0) {
+                return this == o;
+            }
+            for (String s : getDefiningKeys()) {
+                String ourAttribute = m_attributes.get(s);
+                String otherAttribute = (String) ((RepositoryObjectImpl) o).m_attributes.get(s);
+                if ((ourAttribute == null) && (otherAttribute != null)) {
+                    return false;
+                }
+                else if ((ourAttribute != null) && (otherAttribute == null)) {
+                    return false;
+                }
+                else if ((ourAttribute == null) && (otherAttribute == null)) {
+                    continue;
+                }
+                else if (!otherAttribute.equals(ourAttribute)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Returns an array of keys which are considered to be defining for this object's
+     * attributes. The basic implementation just uses every key that is used; this
+     * function is intended to be overridden by deriving classes, providing a real
+     * set.
+     *
+     * Note that the array returned from this function should be read from only;
+     * writing to it will change the state of the object.
+     */
+    String[] getDefiningKeys() {
+        return m_attributes.keySet().toArray(new String[m_attributes.size()]);
+    }
+
+    @Override
+    public int hashCode() {
+        synchronized(m_attributes) {
+            return m_attributes.hashCode();
+        }
+    }
+
+    void marshal(HierarchicalStreamWriter writer) {
+        synchronized (m_attributes) {
+            writer.startNode(m_xmlNode);
+            writeMap(writer, m_attributes, "attributes");
+            writeMap(writer, m_tags, "tags");
+            writeCustom(writer);
+            writer.endNode();
+        }
+    }
+
+    /**
+     * This method is intended to be overridden by deriving classes, to read custom information
+     * from the XML representation of this object. This method should end with the writer at
+     * the same 'level' as before, that is, using equally many moveDown() and moveUp() calls.
+     * @param reader A reader to read from the XML stream.
+     */
+    protected void readCustom(HierarchicalStreamReader reader) {
+        // do nothing
+    }
+
+    /**
+     * This method is intended to be overridden by deriving classes, to write custom information
+     * to the XML representation of this object. This method should end with the writer at
+     * the same 'level' as before, that is, using equally many moveDown() and moveUp() calls.
+     * @param writer A writer to write to the XML stream.
+     */
+    protected void writeCustom(HierarchicalStreamWriter writer) {
+        // do nothing
+    }
+
+    static void writeMap(HierarchicalStreamWriter writer, Map<String, String> entries, String name) {
+        writer.startNode(name);
+        for (Map.Entry<String, String> entry : entries.entrySet()) {
+            if (entry.getKey() != null && entry.getValue() != null) {
+                // we only write values that have non null keys and values
+                writer.startNode(entry.getKey());
+                writer.setValue(entry.getValue());
+                writer.endNode();
+            }
+        }
+        writer.endNode();
+    }
+
+    static Map<String, String> readMap(HierarchicalStreamReader reader) {
+        reader.moveDown();
+        Map<String, String> result = new HashMap<String, String>();
+        while (reader.hasMoreChildren()) {
+            reader.moveDown();
+            result.put(reader.getNodeName(), reader.getValue());
+            reader.moveUp();
+        }
+        reader.moveUp();
+        return result;
+    }
+
+    /**
+     * Helper function to check the existence of keys in a map. Each attribute is required
+     * to be non-empty.
+     * @param attributes A map of attribute-value combinations.
+     * @param mandatory An array of attributes which have to be present in the map.
+     * @return <code>attributes</code> if this map meets the requirements. If not, <code>IllegalArgumentException</code>
+     * will be thrown.
+     */
+    static Map<String, String> checkAttributes(Map<String, String> attributes, String... mandatory) {
+        boolean[] booleans = new boolean[mandatory.length];
+        Arrays.fill(booleans, false);
+        return checkAttributes(attributes, mandatory, booleans);
+    }
+
+    /**
+     * Helper function to check the existence of keys in a map.
+     * @param attributes A map of attribute-value combinations.
+     * @param mandatory An array of attributes which have to be present in the map.
+     * @param emptyAttributeAllowed An array of booleans, indicating which of the attributes is allowed
+     * to be equal. Items in this array are matched by index on the elements in <code>mandatory</code>, so
+     * the length should be equal.
+     * @return <code>attributes</code> if this map meets the requirements. If not, <code>IllegalArgumentException</code>
+     * will be thrown.
+     */
+    static Map<String, String> checkAttributes(Map<String, String> attributes, String[] mandatory, boolean[] emptyAttributeAllowed) {
+        if (!(mandatory.length == emptyAttributeAllowed.length)) {
+            throw new IllegalArgumentException("The length of the mandatory- and the emptyAttributeAllow-array should be equal.");
+        }
+        for (int i = 0; i < mandatory.length; i++) {
+            String attribute = mandatory[i];
+            boolean emptyAllowed = emptyAttributeAllowed[i];
+            if (!attributes.containsKey(attribute)) {
+                throw new IllegalArgumentException(attribute + " is a mandatory attribute.");
+            }
+            else if ((!emptyAllowed) && (attributes.get(attribute).length() == 0)) {
+                throw new IllegalArgumentException(attribute + " is not allowed to be empty.");
+            }
+        }
+        return attributes;
+    }
+
+    public String getXmlNode() {
+        return m_xmlNode;
+    }
+
+    public void handleEvent(Event e) {
+    }
+
+    void setDeleted() {
+        m_deleted = true;
+    }
+
+    public boolean isDeleted() {
+        return m_deleted;
+    }
+
+    void ensureNotDeleted() {
+        if (isDeleted()) {
+            throw new IllegalStateException("This object is deleted, and should no longer be used.");
+        }
+    }
+
+    void setBusy(boolean busy) {
+        // setBusy should 'wait' until all altering operations have passed. To do so,
+        // it gets the locks for the other 'set' objects. Once it has all these locks,
+        // we are sure no thread is performing a set-action.
+        synchronized(m_associations) {
+            synchronized(m_attributes) {
+                if (m_busy && !busy) {
+                    m_associations.notifyAll();
+                    m_attributes.notifyAll();
+                }
+                m_busy = busy;
+            }
+        }
+    }
+
+    public boolean getBusy() {
+        return m_busy;
+    }
+
+    // NEVER CALL WITHOUT m_attributes lock
+    private void ensureNotBusy() {
+        boolean interrupted = false;
+        while (m_busy) {
+            try {
+                m_attributes.wait();
+            }
+            catch (InterruptedException e) {
+                interrupted = true;
+            }
+        }
+        if (interrupted) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    void ensureCurrent() {
+    	ensureNotDeleted();
+        ensureNotBusy();
+    }
+
+    public String getDefinition() {
+        StringBuilder result = new StringBuilder();
+
+        result.append(getXmlNode().replaceAll("\\\\", "\\\\\\\\").replaceAll("-", "\\-"));
+
+        for (String key : getDefiningKeys()) {
+            String value = getAttribute(key);
+            if (value != null) {
+                result.append("-")
+                .append(key.replaceAll("\\\\", "\\\\\\\\").replaceAll("-", "\\-").replaceAll("\\/", "&#47"))
+                .append("-")
+                .append(value.replaceAll("\\\\", "\\\\\\\\").replaceAll("-", "\\-").replaceAll("\\/", "&#47"));
+                // About the &#47: the forward slash will be used by the preference admin, but we don't want that.
+            }
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * Creates a filter string for use in associations, optionally with some
+     * additional properties. The basic implementation will use all <code>getDefiningKeys</code>.
+     * @param properties Properties indicating specifics of the filter to be created.
+     * @return A string representation of a filter, for use in <code>Association</code>s.
+     */
+    public String getAssociationFilter(Map<String, String> properties) {
+        StringBuilder filter = new StringBuilder("(&");
+
+        for (String key : getDefiningKeys()) {
+            filter.append("(" + key + "=" + RepositoryUtil.escapeFilterValue(getAttribute(key)) + ")");
+        }
+
+        filter.append(")");
+
+        return filter.toString();
+    }
+
+    /**
+     * Determines the cardinality of this endpoint of an association, given
+     * the passed properties.
+     * @param properties Properties indicating specifics of this endpoint.
+     * @return The necessary cardinality.
+     */
+    public int getCardinality(Map<String, String> properties) {
+        return Integer.MAX_VALUE;
+    }
+
+    /**
+     * Returns a <code>Comparator</code> for this type of object, suitable
+     * for the endpoint properties that are passed.
+     * @return A <code>Comparator</code> for this type of object
+     */
+    public Comparator<T> getComparator() {
+        return null;
+    }
+
+    /**
+     * Helper class that implements an enumeration for use in <code>keys()</code>.
+     */
+    private static class KeysEnumeration implements Enumeration<String> {
+        private final Iterator<String> m_iter;
+
+        public KeysEnumeration(Iterator<String> iter) {
+            m_iter = iter;
+        }
+
+        public boolean hasMoreElements() {
+            return m_iter.hasNext();
+        }
+
+        public String nextElement() {
+            return m_iter.next();
+        }
+    }
+
+    /**
+     * Helper class that implements an enumeration for use in <code>elements()</code>.
+     */
+    private class ValuesEnumeration implements Enumeration<Object> {
+        private final Enumeration<String> m_iter = keys();
+
+        public boolean hasMoreElements() {
+            return m_iter.hasMoreElements();
+        }
+
+        public Object nextElement() {
+            return get(m_iter.nextElement());
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryPropertyResolver.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryPropertyResolver.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryPropertyResolver.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositoryPropertyResolver.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,82 @@
+package org.apache.ace.client.repository.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.helper.PropertyResolver;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+
+/**
+ * This PropertyResolver first tries to resolve the key in the
+ * current repository object. If not found, it looks for the key
+ * in its children. 
+ */
+public class RepositoryPropertyResolver implements PropertyResolver {
+	
+	private final RepositoryObject m_repositoryObject;
+	
+	public RepositoryPropertyResolver(RepositoryObject repositoryObject) {
+		m_repositoryObject = repositoryObject;
+	}
+	
+    public String get(String key) {
+		return get(key, m_repositoryObject);
+    }
+
+    private String get(String key, RepositoryObject ro) {
+        // Is it in this object?
+        String result = findKeyInObject(ro, key);
+        if (result != null) {
+            return result;
+        }
+
+        // Is it in one of the children?
+        List<? extends RepositoryObject> children = getChildren(ro);
+        for (RepositoryObject child : children) {
+            result = findKeyInObject(child, key);
+            if (result != null) {
+                return result;
+            }
+        }
+
+        // Not found yet? then continue to the next level recursively.
+        for (RepositoryObject child : children) {
+            result = get(key, child);
+            if (result != null) {
+                return result;
+            }
+        }
+        return result;
+    }
+
+    protected List<? extends RepositoryObject> getChildren() {
+    	return getChildren(m_repositoryObject);
+    }
+    
+    protected List<? extends RepositoryObject> getChildren(RepositoryObject ob) {
+        if (ob instanceof TargetObject) {
+            return ((TargetObject) ob).getDistributions();
+        }
+        else if (ob instanceof DistributionObject) {
+            return ((DistributionObject) ob).getFeatures();
+        }
+        else if (ob instanceof FeatureObject) {
+            return ((FeatureObject) ob).getArtifacts();
+        }
+        return new ArrayList<RepositoryObject>();
+    }
+
+    private String findKeyInObject(RepositoryObject ro, String key) {
+        String result;
+        if ((result = ro.getAttribute(key)) != null) {
+            return result;
+        }
+        if ((result = ro.getTag(key)) != null) {
+            return result;
+        }
+        return null;
+    }	
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySerializer.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySerializer.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySerializer.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySerializer.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+/**
+ * Helper class that takes a RepositorySet<br>
+ * TODO We might move out xstream at some time in the future; before that
+ * time, it could be a smart idea to wrap xstream's writer in a delegate
+ * object, so this will not require changes to the repositories
+ * and objects.
+ */
+class RepositorySerializer implements Converter {
+    @SuppressWarnings("unchecked")
+    private final Map<String, ObjectRepositoryImpl> m_tagToRepo = new HashMap<String, ObjectRepositoryImpl>();
+
+    private final RepositorySet m_set;
+
+    private final XStream m_stream;
+
+    @SuppressWarnings("unchecked")
+    RepositorySerializer(RepositorySet set) {
+        m_set = set;
+        for (ObjectRepositoryImpl repo : m_set.getRepos()) {
+            m_tagToRepo.put(repo.getXmlNode(), repo);
+        }
+        m_stream = new XStream();
+        m_stream.alias("repository", getClass());
+        m_stream.registerConverter(this);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void marshal(Object target, HierarchicalStreamWriter writer, MarshallingContext context) {
+        for (ObjectRepositoryImpl repo : m_set.getRepos()) {
+            repo.marshal(writer);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        while(reader.hasMoreChildren()) {
+            reader.moveDown();
+            String nodeName = reader.getNodeName();
+            ObjectRepositoryImpl o = m_tagToRepo.get(nodeName);
+            o.unmarshal(reader);
+            reader.moveUp();
+        }
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public boolean canConvert(Class target) {
+        return target == getClass();
+    }
+
+    @SuppressWarnings("unchecked")
+    public void toXML(OutputStream out) {
+        for (ObjectRepositoryImpl repo : m_set.getRepos()) {
+            repo.setBusy(true);
+        }
+        try {
+            m_stream.toXML(this, out);
+        }
+        finally {
+            // Ensure all busy flags are reset at all times...
+            for (ObjectRepositoryImpl repo : m_set.getRepos()) {
+                repo.setBusy(false);
+            }
+        }
+    }
+
+    /**
+     * Reads the repositories with which this RepositoryRoot had been initialized with from the
+     * given XML file.
+     * @param in The input stream.
+     */
+    @SuppressWarnings("unchecked")
+    public void fromXML(InputStream in) {
+        // The repositories get cleared, since a user *could* add stuff before
+        // checking out.
+        for (ObjectRepositoryImpl repo : m_set.getRepos()) {
+            repo.setBusy(true);
+            repo.removeAll();
+        }
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+            if (in.available() > 0) {
+                m_stream.fromXML(in, this);
+            }
+        }
+        catch (IOException e) {
+            // This means the stream has been closed before we got it.
+            // Since the repository is now in a consistent state, just move on.
+            e.printStackTrace();
+        }
+        finally {
+            Thread.currentThread().setContextClassLoader(cl);
+            // Ensure all busy flags are reset at all times...
+            for (ObjectRepositoryImpl repo : m_set.getRepos()) {
+                repo.setBusy(false);
+            }
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySet.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySet.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySet.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/RepositorySet.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.impl;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.RepositoryAdmin;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.RepositoryObject.WorkingState;
+import org.apache.ace.client.repository.SessionFactory;
+import org.apache.ace.repository.ext.CachedRepository;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.useradmin.User;
+
+/**
+ * This class encapsulates a set of <code>ObjectRepositoryImpl</code>s, and manages
+ * auxiliary information and functionality that is linked to that set.
+ */
+class RepositorySet {
+    private final static String PREFS_LOCAL_WORKING_STATE = "workingState";
+    private final static String PREFS_LOCAL_WORKING_STATE_VALUE = "workingStateValue";
+    private final static String PREFS_LOCAL_FILE_VERSION = "version";
+
+    private final User m_user;
+    private final Preferences m_prefs;
+    @SuppressWarnings("unchecked")
+    private final ObjectRepositoryImpl[] m_repos;
+    private final CachedRepository m_repository;
+    private final String m_name;
+    private final boolean m_writeAccess;
+    private final ConcurrentMap<RepositoryObject, WorkingState> m_workingState;
+    private final ChangeNotifier m_notifier;
+    private final LogService m_log;
+    
+    private volatile ServiceRegistration m_modifiedHandler;
+
+    /* ********
+     * Basics
+     * ********/
+
+    @SuppressWarnings("unchecked")
+    /**
+     * Creates a new <code>RepositorySet</code>. Notes:
+     * <ul>
+     * <li>When storing association repositories in <code>repos</code>, it is wise to
+     * put these in last. This has to do with the method of deserialization, which assumes
+     * that endpoints of an association are available at the time of deserialization.</li>
+     * </ul>
+     */
+    RepositorySet(ChangeNotifier notifier, LogService log, User user, Preferences prefs, ObjectRepositoryImpl[] repos, CachedRepository repository, String name, boolean writeAccess) {
+        m_workingState = new ConcurrentHashMap<RepositoryObject, WorkingState>();
+        m_notifier = notifier;
+        m_log = log;
+        m_user = user;
+        m_prefs = prefs;
+        m_repos = repos;
+        m_repository = repository;
+        m_name = name;
+        m_writeAccess = writeAccess;
+    }
+
+     void unregisterHandler() {
+        m_modifiedHandler.unregister();
+        m_modifiedHandler = null;
+    }
+
+    boolean writeAccess() {
+        return m_writeAccess;
+    }
+
+    boolean isModified() {
+        for (WorkingState workingState : m_workingState.values()) {
+            if (!WorkingState.Unchanged.equals(workingState)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    User getUser() {
+        return m_user;
+    }
+
+    @SuppressWarnings("unchecked")
+    ObjectRepositoryImpl[] getRepos() {
+        return m_repos;
+    }
+
+    String getName() {
+        return m_name;
+    }
+
+    /* ********
+     * Preferences
+     * ********/
+
+    void savePreferences() {
+        Preferences workingNode = m_prefs.node(PREFS_LOCAL_WORKING_STATE);
+        try {
+            workingNode.clear();
+        }
+        catch (BackingStoreException e) {
+            // Something went wrong clearing the node... Too bad, this means we
+            // cannot store the properties.
+            m_log.log(LogService.LOG_WARNING, "Could not store all preferences for " + workingNode.absolutePath());
+            e.printStackTrace();
+        }
+        
+        for (Map.Entry<RepositoryObject, WorkingState> entry : m_workingState.entrySet()) {
+            workingNode.node(entry.getKey().getDefinition()).put(PREFS_LOCAL_WORKING_STATE_VALUE, entry.getValue().toString());
+        }
+
+        m_prefs.putLong(PREFS_LOCAL_FILE_VERSION, m_repository.getMostRecentVersion());
+    }
+
+    /**
+     * Only call this after the repository has been deserialized.
+     */
+    void loadPreferences() {
+        Preferences workingNode = m_prefs.node(PREFS_LOCAL_WORKING_STATE);
+        Map<String, WorkingState> entries = new HashMap<String, WorkingState>();
+        // First, get all nodes and their workingstate.
+        try {
+            String defaultWorkingState = WorkingState.Unchanged.toString();
+
+            for (String node : workingNode.childrenNames()) {
+                String state = workingNode.node(node).get(PREFS_LOCAL_WORKING_STATE_VALUE, defaultWorkingState);
+                entries.put(node, WorkingState.valueOf(state));
+            }
+        }
+        catch (BackingStoreException e) {
+            // Something went wrong reading from the store, just work with whatever we have in the map.
+            m_log.log(LogService.LOG_WARNING, "Could not load all preferences for " + workingNode.absolutePath());
+            e.printStackTrace();
+        }
+        // Then, go through all objects and check whether they match a definition we know.
+        // This prevents calling getDefinition more than once per object.
+        for (ObjectRepository<RepositoryObject> repo : m_repos) {
+            for (RepositoryObject o : repo.get()) {
+                WorkingState state = entries.get(o.getDefinition());
+                if (state != null) {
+                    m_workingState.put(o, state);
+                }
+            }
+        }
+    }
+
+    /* ********
+     * Persistence
+     * ********/
+
+    boolean readLocal() throws IOException {
+        InputStream input = m_repository.getLocal(false /* fail */);
+        if (input.available() > 0) {
+            read(input);
+            return true;
+        }
+        else {
+            closeSafely(input);
+            return false;
+        }
+    }
+
+    void read(InputStream input) {
+        new RepositorySerializer(this).fromXML(input);
+        closeSafely(input);
+    }
+
+    void writeLocal() throws IOException {
+        final PipedInputStream input = new PipedInputStream();
+        final PipedOutputStream output = new PipedOutputStream(input);
+        new Thread(new Runnable() {
+            public void run() {
+                new RepositorySerializer(RepositorySet.this).toXML(output);
+                try {
+                    output.flush();
+                    output.close();
+                }
+                catch (IOException e) {
+                    // There is no way to tell this to the user, but the other side will
+                    // notice that the pipe is broken.
+                }
+            }
+        }, "write(" + m_name + ")").start();
+        m_repository.writeLocal(input);
+        input.close();
+    }
+
+    void commit() throws IOException {
+        if (!isCurrent()) {
+            throw new IllegalStateException("When committing the " + m_name + ", it should be current.");
+        }
+        writeLocal();
+        if (m_writeAccess) {
+            m_repository.commit();
+        }
+        resetModified(false);
+    }
+
+    void checkout() throws IOException {
+        read(m_repository.checkout(false));
+        resetModified(true);
+    }
+
+    void revert() throws IOException {
+        m_repository.revert();
+        read(m_repository.getLocal(false));
+        resetModified(false);
+    }
+
+    boolean isCurrent() throws IOException {
+        return m_repository.isCurrent();
+    }
+
+    @SuppressWarnings("unchecked")
+    void clearRepositories() {
+        for (ObjectRepositoryImpl repo : getRepos()) {
+            repo.setBusy(true);
+        }
+
+        try {
+            for (ObjectRepositoryImpl repo : getRepos()) {
+                repo.removeAll();
+            }
+        }
+        finally {
+            // Ensure all busy flags are reset at all times...
+            for (ObjectRepositoryImpl repo : getRepos()) {
+                repo.setBusy(false);
+            }
+        }
+    }
+    
+    void deleteLocal() {
+    	try {
+			m_repository.deleteLocal();
+		}
+    	catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+    }
+
+    /* ********
+     * Event handling
+     * ********/
+
+    void registerHandler(BundleContext context, String sessionID, String... topics) {
+        if (m_modifiedHandler != null) {
+            throw new IllegalStateException("A handler is already registered; only one can be used at a time.");
+        }
+        Dictionary topic = new Hashtable();
+        topic.put(EventConstants.EVENT_TOPIC, topics);
+        topic.put(EventConstants.EVENT_FILTER, "(" + SessionFactory.SERVICE_SID + "=" + sessionID + ")");
+        m_modifiedHandler = context.registerService(EventHandler.class.getName(), new ModifiedHandler(), topic);
+    }
+
+    WorkingState getWorkingState(RepositoryObject object) {
+        return m_workingState.get(object);
+    }
+
+    int getNumberWithWorkingState(Class<? extends RepositoryObject> clazz, WorkingState state) {
+        int result = 0;
+        for (Map.Entry<RepositoryObject, WorkingState> entry : m_workingState.entrySet()) {
+            if (clazz.isInstance(entry.getKey()) && state.equals(entry.getValue())) {
+                result++;
+            }
+        }
+        return result;
+    }
+
+    private void resetModified(boolean fill) {
+        m_workingState.clear();
+        if (fill) {
+            for (ObjectRepository<? extends RepositoryObject> repo : m_repos) {
+                for (RepositoryObject object : repo.get()) {
+                    m_workingState.put(object, WorkingState.Unchanged);
+                }
+            }
+        }
+        m_notifier.notifyChanged(RepositoryAdmin.TOPIC_STATUSCHANGED_SUFFIX, null);
+    }
+    
+    private void closeSafely(Closeable resource) {
+        if (resource != null) {
+            try {
+                resource.close();
+            }
+            catch (IOException e) {
+                // Ignored; not much we can do about this...
+                m_log.log(LogService.LOG_DEBUG, "Closing resource failed!", e);
+            }
+        }
+    }
+    
+    final class ModifiedHandler implements EventHandler {
+        
+        public void handleEvent(Event event) {
+            /*
+             * NOTE: if recalculating the state for every event turns out to be
+             * too expensive, we can cache the 'modified' state and not recalculate
+             * it every time.
+             */
+            
+            boolean wasModified = isModified();
+            
+            RepositoryObject object = (RepositoryObject) event.getProperty(RepositoryObject.EVENT_ENTITY);
+            String topic = event.getTopic();
+            
+            WorkingState newState = WorkingState.Unchanged;
+            if (topic.endsWith("/ADDED")) {
+                newState = WorkingState.New;
+            }
+            else if (topic.endsWith("/CHANGED")) {
+                newState = WorkingState.Changed;
+            }
+            else if (topic.endsWith("/REMOVED")) {
+                newState = WorkingState.Removed;
+            }
+            
+            if (!newState.equals(m_workingState.get(object))) {
+                m_workingState.put(object, newState);
+                
+                Properties props = new Properties();
+                props.put(RepositoryObject.EVENT_ENTITY, object);
+                m_notifier.notifyChanged(RepositoryAdmin.TOPIC_STATUSCHANGED_SUFFIX, props);
+            }
+            
+            if (!wasModified) {
+                m_notifier.notifyChanged(RepositoryAdmin.TOPIC_STATUSCHANGED_SUFFIX, null);
+            }
+        }
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetObjectImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetObjectImpl.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetObjectImpl.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetObjectImpl.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
+import org.apache.ace.client.repository.object.DistributionObject;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the TargetObject. For 'what it does', see TargetObject,
+ * for 'how it works', see RepositoryObjectImpl.
+ */
+public class TargetObjectImpl extends RepositoryObjectImpl<TargetObject> implements TargetObject {
+    private final static String XML_NODE = "target";
+
+    TargetObjectImpl(Map<String, String> attributes, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_ID), notifier, XML_NODE);
+    }
+
+    TargetObjectImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_ID), tags, notifier, XML_NODE);
+    }
+
+    TargetObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier) {
+        super(reader, notifier, XML_NODE);
+        if(getAttribute(KEY_AUTO_APPROVE) == null) {
+            addAttribute(KEY_AUTO_APPROVE, String.valueOf(false));
+        }
+    }
+
+    public List<DistributionObject> getDistributions() {
+        return getAssociations(DistributionObject.class);
+    }
+
+    public String getID() {
+        return getAttribute(KEY_ID);
+    }
+
+    public List<Distribution2TargetAssociation> getAssociationsWith(DistributionObject distribution) {
+        return getAssociationsWith(distribution, DistributionObject.class, Distribution2TargetAssociation.class);
+    }
+
+    private static String[] DEFINING_KEYS = new String[] {KEY_ID};
+    @Override
+    String[] getDefiningKeys() {
+        return DEFINING_KEYS;
+    }
+
+    public boolean getAutoApprove() {
+        String value = getAttribute(KEY_AUTO_APPROVE);
+        if (value == null) {
+            return false;
+        }
+        else {
+            return Boolean.parseBoolean(value);
+        }
+    }
+
+    public void setAutoApprove(boolean approve) {
+       addAttribute(KEY_AUTO_APPROVE, String.valueOf(approve));
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetPropertyResolver.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetPropertyResolver.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetPropertyResolver.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetPropertyResolver.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.impl;
+
+import java.util.*;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.helper.PropertyResolver;
+import org.apache.ace.client.repository.object.TargetObject;
+
+/**
+ * Top-level property resolver, also able to return collections
+ * of distributions, features and artifacts linked to this target
+ * repository object.
+ */
+public class TargetPropertyResolver extends RepositoryPropertyResolver {
+
+    public TargetPropertyResolver(TargetObject to) {
+        super(to);
+    }
+
+    public Collection<PropertyResolver> getDistributions() {
+        List<PropertyResolver> list = new ArrayList<PropertyResolver>();
+
+        List<RepositoryObject> distributions = (List<RepositoryObject>)getChildren();
+
+        for (RepositoryObject repo : distributions) {
+            list.add(new RepositoryPropertyResolver(repo));
+        }
+
+        return list;
+    }
+
+    public Collection<PropertyResolver> getFeatures() {
+        List<PropertyResolver> list = new ArrayList<PropertyResolver>();
+
+        Set<RepositoryObject> features = new HashSet<RepositoryObject>();
+
+        for (RepositoryObject repositoryObject : getChildren()) {
+            features.addAll(getChildren(repositoryObject));
+        }
+
+        for (RepositoryObject repo : features) {
+            list.add(new RepositoryPropertyResolver(repo));
+        }
+        return list;
+    }
+
+    public Collection<PropertyResolver> getArtifacts() {
+        List<PropertyResolver> list = new ArrayList<PropertyResolver>();
+
+        Set<RepositoryObject> artifacts = new HashSet<RepositoryObject>();
+        Set<RepositoryObject> features = new HashSet<RepositoryObject>();
+
+        for (RepositoryObject repositoryObject : getChildren()) {
+            features.addAll(getChildren(repositoryObject));
+        }
+
+        for (RepositoryObject repositoryObject : features) {
+            artifacts.addAll(getChildren(repositoryObject));
+        }
+
+        for (RepositoryObject repo : artifacts) {
+            list.add(new RepositoryPropertyResolver(repo));
+        }
+        return list;
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetRepositoryImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetRepositoryImpl.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetRepositoryImpl.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/impl/TargetRepositoryImpl.java Tue Apr  2 14:53:33 2013
@@ -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.client.repository.impl;
+
+import java.util.Map;
+
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.repository.TargetRepository;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the TargetRepository. For 'what it does', see TargetRepository,
+ * for 'how it works', see ObjectRepositoryImpl.
+ */
+public class TargetRepositoryImpl extends ObjectRepositoryImpl<TargetObjectImpl, TargetObject> implements TargetRepository {
+    private final static String XML_NODE = "targets";
+
+    public TargetRepositoryImpl(ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+    }
+
+    @Override
+    TargetObjectImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        return new TargetObjectImpl(attributes, tags, this);
+    }
+
+    @Override
+    TargetObjectImpl createNewInhabitant(Map<String, String> attributes) {
+        return new TargetObjectImpl(attributes, this);
+    }
+
+    @Override
+    TargetObjectImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        return new TargetObjectImpl(reader, this);
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Artifact2FeatureAssociation.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Artifact2FeatureAssociation.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Artifact2FeatureAssociation.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Artifact2FeatureAssociation.java Tue Apr  2 14:53:33 2013
@@ -0,0 +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.client.repository.object;
+
+import org.apache.ace.client.repository.Association;
+
+/**
+ * Interface to a Artifact2FeatureAssociation. Most functionality is defined by the generic Association.
+ */
+public interface Artifact2FeatureAssociation extends Association<ArtifactObject, FeatureObject> {
+    public static final String TOPIC_ENTITY_ROOT = Artifact2FeatureAssociation.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/ArtifactObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/ArtifactObject.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/ArtifactObject.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/ArtifactObject.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.object;
+
+import java.util.List;
+
+import org.apache.ace.client.repository.RepositoryObject;
+
+/**
+ * Interface to an ArtifactObject. The basic functionality is defined by RepositoryObject, but extended for
+ * artifact-specific information.
+ */
+public interface ArtifactObject extends RepositoryObject {
+    /**
+     * Key to be used in the <code>ArtifactObject</code>'s attributes.
+     * Indicates the location of the persistent storage of the artifact.
+     */
+    public static final String KEY_URL = "url";
+    /**
+     * Attribute key, stating the unique name for this resource. Different versions of the same logical resource
+     * can share the same Id.
+     */
+    public static final String KEY_RESOURCE_ID = "resourceId";
+    /**
+     * Key to be used in the <code>ArtifactObject</code>'s attributes.
+     * Indicates the PID of the resource processor that should be used to process this artifact.
+     * For a bundle, it is empty.
+     */
+    public static final String KEY_PROCESSOR_PID = "processorPid";
+    /**
+     * Key to be used in the <code>ArtifactObject</code>'s attributes.
+     * Indicates the mimetype of this artifact. For artifacts which do not
+     * have an adequately discriminating mimetype, it can be extended with
+     * something non-standard.
+     */
+    public static final String KEY_MIMETYPE = "mimetype";
+    /**
+     * Key to be used in the <code>ArtifactObject</code>'s attributes.
+     * Holds a human-readable name for this artifact.
+     */
+    public static final String KEY_ARTIFACT_NAME = "artifactName";
+    /**
+     * Key to be used in the <code>ArtifactObject</code>'s attributes.
+     * Holds a human-readable description for this artifact.
+     */
+    public static final String KEY_ARTIFACT_DESCRIPTION = "artifactDescription";
+
+    public static final String TOPIC_ENTITY_ROOT = ArtifactObject.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+
+    /**
+     * Returns all <code>GroupObject</code>s this object is associated with. If there
+     * are none, an empty list will be returned.
+     */
+    public List<FeatureObject> getFeatures();
+    /**
+     * Returns all associations this artifact has with a given group.
+     */
+    public List<Artifact2FeatureAssociation> getAssociationsWith(FeatureObject group);
+
+    /**
+     * Returns the mimetype of this artifact.
+     */
+    public String getMimetype();
+    /**
+     * Returns the PID of the resource processor of this artifact.
+     */
+    public String getProcessorPID();
+    /**
+     * Sets the PID of the resource processor of this artifact.
+     */
+    public void setProcessorPID(String processorPID);
+    /**
+     * Returns the URL to this artifact.
+     */
+    public String getURL();
+    /**
+     * Returns a ResourceId, if that has been customized. 
+     */
+    public String getResourceId();
+    /**
+     * Return a descriptive name for this object. May return <code>null</code>.
+     */
+    public String getName();
+    /**
+     * Returns a description for this object. May return <code>null</code>.
+     */
+    public String getDescription();
+    /**
+     * Sets a description for this artifact.
+     */
+    public void setDescription(String value);
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentArtifact.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentArtifact.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentArtifact.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentArtifact.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.object;
+
+/**
+ * Interface to a deployment artifact, which is used to gather information about
+ * the deployment of a single artifact.
+ */
+public interface DeploymentArtifact {
+
+    /**
+     * Key, intended to be used for artifacts which are bundles and will publish
+     * a resource processor (see OSGi compendium section 114.10).
+     */
+    public static final String DIRECTIVE_ISCUSTOMIZER = "DeploymentPackage-Customizer";
+
+    /**
+     * Key, intended to be used for resources which require a resource processor
+     * (see OSGi compendium section 114.10).
+     */
+    public static final String DIRECTIVE_KEY_PROCESSORID = "Resource-Processor";
+
+    /**
+     * Key, intended to be used for artifacts which have a resourceID that's different
+     * from their generated name (based on URL).
+     */
+    public static final String DIRECTIVE_KEY_RESOURCE_ID = "Resource-ID";
+
+    /**
+     * Key, intended to be used for matching processed (see ArtifactPreprocessor) to their
+     * 'original' one.
+     */
+    public static final String DIRECTIVE_KEY_BASEURL = "Base-Url";
+
+	public static final String REPOSITORY_PATH = "ACE-RepositoryPath";
+
+    /**
+     * @return the URL for this deployment artifact.
+     */
+    public String getUrl();
+
+    /**
+     * @param key A key String, such as the <code>DIRECTIVE_</code> constants in
+     * <code>DeploymentArtifact</code>.
+     * @return the value for the given directive key, or <code>null</code> if not found.
+     */
+    public String getDirective(String key);
+
+    /**
+     * @return an array of all keys that are used in this object, to be used in <code>getDirective</code>.
+     */
+    public String[] getKeys();
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentVersionObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentVersionObject.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentVersionObject.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DeploymentVersionObject.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.object;
+
+import org.apache.ace.client.repository.RepositoryObject;
+
+/**
+ * The interface to a DeploymentVersion. The basic functionality is defined
+ * by RepositoryObject, but extended for deployment version-specific information.
+ *
+ * DeploymentVersions need some additional information about the artifacts they
+ * are associated with; see DeploymentArtifact.
+ */
+public interface DeploymentVersionObject extends RepositoryObject {
+
+    public static final String KEY_TARGETID = "targetID";
+    public static final String KEY_VERSION = "version";
+
+    public static final String TOPIC_ENTITY_ROOT = DeploymentVersionObject.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+
+    /**
+     * Gets the target which is related to this version.
+     */
+    public String getTargetID();
+
+    /**
+     * Gets the version number of this deployment version.
+     */
+    public String getVersion();
+
+    /**
+     * @return an array of all deployment artifacts that will be part of this deployment version.
+     * The order of the artifacts in the array is equal to the order they should appear in a
+     * deployment package.
+     */
+    public DeploymentArtifact[] getDeploymentArtifacts();
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Distribution2TargetAssociation.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Distribution2TargetAssociation.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Distribution2TargetAssociation.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Distribution2TargetAssociation.java Tue Apr  2 14:53:33 2013
@@ -0,0 +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.client.repository.object;
+
+import org.apache.ace.client.repository.Association;
+
+/**
+ * Interface to a Distribution2TargetAssociation. Most functionality is defined by the generic Association.
+ */
+public interface Distribution2TargetAssociation extends Association<DistributionObject, TargetObject> {
+    public static final String TOPIC_ENTITY_ROOT = Distribution2TargetAssociation.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DistributionObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DistributionObject.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DistributionObject.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/DistributionObject.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.object;
+
+import java.util.List;
+
+import org.apache.ace.client.repository.RepositoryObject;
+
+/**
+ * Interface to a DistributionObject. The basic functionality is defined by RepositoryObject, but extended for
+ * distribution-specific information.
+ */
+public interface DistributionObject extends RepositoryObject {
+    public static final String TOPIC_ENTITY_ROOT = DistributionObject.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+
+    public static final String KEY_DESCRIPTION = "description";
+    public static final String KEY_NAME = "name";
+
+    /**
+     * Returns all <code>FeatureObject</code>s this object is associated with. If there
+     * are none, an empty list will be returned.
+     */
+    public List<FeatureObject> getFeatures();
+    
+    /**
+     * Returns all <code>TargetObject</code>s this object is associated with. If there
+     * are none, an empty list will be returned.
+     */
+    public List<TargetObject> getTargets();
+
+    /**
+     * Returns all associations this distribution has with a given feature.
+     */
+    public List<Feature2DistributionAssociation> getAssociationsWith(FeatureObject feature);
+
+    /**
+     * Returns all associations this distribution has with a given target.
+     */
+    public List<Distribution2TargetAssociation> getAssociationsWith(TargetObject target);
+
+    /**
+     * Returns the name of this distribution.
+     */
+    public String getName();
+    
+    /**
+     * Sets the name of this distribution.
+     */
+    public void setName(String name);
+
+    /**
+     * Returns the description of this distribution.
+     */
+    public String getDescription();
+    
+    /**
+     * Sets the description of this distribution.
+     */
+    public void setDescription(String description);
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Feature2DistributionAssociation.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Feature2DistributionAssociation.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Feature2DistributionAssociation.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/Feature2DistributionAssociation.java Tue Apr  2 14:53:33 2013
@@ -0,0 +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.client.repository.object;
+
+import org.apache.ace.client.repository.Association;
+
+/**
+ * Interface to a Feature2DistributionAssociation. Most functionality is defined by the generic Association.
+ */
+public interface Feature2DistributionAssociation extends Association<FeatureObject, DistributionObject> {
+    public static final String TOPIC_ENTITY_ROOT = Feature2DistributionAssociation.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/FeatureObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/FeatureObject.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/FeatureObject.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/FeatureObject.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.object;
+
+import java.util.List;
+
+import org.apache.ace.client.repository.RepositoryObject;
+
+/**
+ * Interface to a FeatureObject. The basic functionality is defined by RepositoryObject, but extended for
+ * feature-specific information.
+ */
+public interface FeatureObject extends RepositoryObject {
+    public static final String KEY_DESCRIPTION = "description";
+    public static final String KEY_NAME = "name";
+
+    public static final String TOPIC_ENTITY_ROOT = FeatureObject.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+
+    /**
+     * Returns all <code>ArtifactObject</code>s this object is associated with. If there
+     * are none, an empty list will be returned.
+     */
+    public List<ArtifactObject> getArtifacts();
+    
+    /**
+     * Returns all <code>DistributionObject</code>s this object is associated with. If there
+     * are none, an empty list will be returned.
+     */
+    public List<DistributionObject> getDistributions();
+
+    /**
+     * Returns all associations this feature has with a given artifact.
+     */
+    public List<Artifact2FeatureAssociation> getAssociationsWith(ArtifactObject artifact);
+    
+    /**
+     * Returns all associations this feature has with a given distribution.
+     */
+    public List<Feature2DistributionAssociation> getAssociationsWith(DistributionObject distribution);
+
+    /**
+     * Returns the name of this feature.
+     */
+    public String getName();
+
+    /**
+     * Sets the name of this feature.
+     */
+    public void setName(String name);
+    
+    /**
+     * Returns the description of this feature.
+     */
+    public String getDescription();
+    
+    /**
+     * Sets the description of this feature.
+     */
+    public void setDescription(String description);
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/TargetObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/TargetObject.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/TargetObject.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/TargetObject.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.object;
+
+import java.util.List;
+
+import org.apache.ace.client.repository.RepositoryObject;
+
+public interface TargetObject extends RepositoryObject {
+    public static final String KEY_ID = "id";
+    public static final String KEY_AUTO_APPROVE = "autoapprove";
+
+    public static final String TOPIC_ENTITY_ROOT = TargetObject.class.getSimpleName() + "/";
+
+    public static final String TOPIC_ADDED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ADDED_SUFFIX;
+    public static final String TOPIC_REMOVED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_REMOVED_SUFFIX;
+    public static final String TOPIC_CHANGED = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_CHANGED_SUFFIX;
+    public static final String TOPIC_ALL = PUBLIC_TOPIC_ROOT + TOPIC_ENTITY_ROOT + TOPIC_ALL_SUFFIX;
+
+    /**
+     * Returns all <code>DistributionObject</code>s this object is associated with. If there
+     * are none, an empty list will be returned.
+     */
+    public List<DistributionObject> getDistributions();
+    /**
+     * Returns all associations this target has with a given distribution.
+     */
+    public List<Distribution2TargetAssociation> getAssociationsWith(DistributionObject distribution);
+
+    /**
+     * Gets the ID of this TargetObject.
+     */
+    public String getID();
+
+    /**
+     * Enable or disable automatic approval.
+     */
+    public void setAutoApprove(boolean approve);
+
+    /**
+     * Get the auto approval value of this target.
+     */
+    public boolean getAutoApprove();
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/packageinfo?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/packageinfo (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/object/packageinfo Tue Apr  2 14:53:33 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/packageinfo?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/packageinfo (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/packageinfo Tue Apr  2 14:53:33 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Artifact2FeatureAssociationRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Artifact2FeatureAssociationRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Artifact2FeatureAssociationRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Artifact2FeatureAssociationRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ace.client.repository.repository;
+
+import org.apache.ace.client.repository.AssociationRepository;
+import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+
+/**
+ * Interface to a Artifact2FeatureAssociationRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface Artifact2FeatureAssociationRepository extends AssociationRepository<ArtifactObject, FeatureObject, Artifact2FeatureAssociation> {
+}