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 2009/06/27 17:53:26 UTC

svn commit: r788992 [10/25] - in /incubator/ace/trunk: gateway/ gateway/src/ gateway/src/net/ gateway/src/net/luminis/ gateway/src/net/luminis/liq/ gateway/src/net/luminis/liq/bootstrap/ gateway/src/net/luminis/liq/bootstrap/multigateway/ gateway/src/n...

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/AssociationRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,95 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.Association;
+import net.luminis.liq.client.repository.AssociationRepository;
+import net.luminis.liq.client.repository.RepositoryObject;
+
+/**
+ * A basic association repository. It cannot be directly instantiated, since some functionality
+ * is delagated to deriving classes, being
+ * <bl>
+ * <li>The creation of new inhabitants</li>
+ * <li>The generation of filter strings based on objects</li>
+ * </bl>
+ *
+ * @param <L> The left side of the associations which will be stored in this repository.
+ * @param <R> The right side of the associations which will be stored in this repository.
+ * @param <I> An implementation of the association from <code>L</code> to <code>R</code>.
+ * @param <T> The non-generic Association interface that <code>I</code> implements.
+ */
+public abstract class AssociationRepositoryImpl<L extends RepositoryObject, R extends RepositoryObject, I extends AssociationImpl<L, R, T>, T extends Association<L, R>> extends ObjectRepositoryImpl<I, T> implements AssociationRepository<L, R, T> {
+    private Object m_lock = new Object();
+
+    public AssociationRepositoryImpl(ChangeNotifier notifier, String xmlNode) {
+        super(notifier, xmlNode);
+    }
+
+    @SuppressWarnings("unchecked")
+    public T create(String left, int leftCardinality, String right, int rightCardinality) {
+        synchronized (m_lock) {
+            T association = null;
+            try {
+                Map<String, String> attributes = new HashMap<String, String>();
+                attributes.put(Association.LEFT_ENDPOINT, left);
+                attributes.put(Association.RIGHT_ENDPOINT, right);
+                attributes.put(Association.LEFT_CARDINALITY, "" + leftCardinality);
+                attributes.put(Association.RIGHT_CARDINALITY, "" + rightCardinality);
+                association = (T) createNewInhabitant(attributes);
+                add(association);
+            }
+            catch (Exception e) {
+                // We have not been able to instantiate our constructor. Not much to do about that.
+                e.printStackTrace();
+            }
+            return association;
+        }
+    }
+
+    public T create(String left, String right) {
+        return create(left, Integer.MAX_VALUE, right, Integer.MAX_VALUE);
+    }
+
+    public T create(L left, Map<String, String> leftProps, R right, Map<String, String> rightProps) {
+        return create(left.getAssociationFilter(leftProps), left.getCardinality(leftProps),
+            right.getAssociationFilter(rightProps), right.getCardinality(rightProps));
+    }
+
+    public T create(L left, R right) {
+        return create(left, null, right, null);
+    }
+
+    public T create(List<L> left, List<R> right) {
+        if ((left == null) || left.isEmpty()) {
+            throw new IllegalArgumentException("The left side of the association cannot be empty.");
+        }
+        if ((right == null) || right.isEmpty()) {
+            throw new IllegalArgumentException("The right side of the association cannot be empty.");
+        }
+
+        StringBuilder leftFilter = new StringBuilder("(|");
+        for (L l : left) {
+            leftFilter.append(l.getAssociationFilter(null));
+        }
+        leftFilter.append(")");
+
+        StringBuilder rightFilter = new StringBuilder("(|");
+        for (R r : right) {
+            rightFilter.append(r.getAssociationFilter(null));
+        }
+        rightFilter.append(")");
+
+        return create(leftFilter.toString(), Integer.MAX_VALUE, rightFilter.toString(), Integer.MAX_VALUE);
+    }
+
+    @Override
+    public void remove(T entity) {
+        synchronized (m_lock) {
+            super.remove(entity);
+            entity.remove();
+        }
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifier.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifier.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifier.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifier.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,37 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Properties;
+
+/**
+ * This interface defines a mechanism for notifying both internally interested parties, and
+ * external listeners, of changes to the a given 'inhabitant' of the model.
+ */
+public interface ChangeNotifier {
+    /**
+     * Notifies both internal and external listeners of a change to some object.
+     * @param topic A topic, as defined in the interface definitions of the various objects.
+     * Note that this is not a <i>full</i> topic, but merely the 'last part', such as "ADDED";
+     * this allows the ChangeNotifier to generate internal or external topics.
+     * @param props Properties to pack with the event. May be null.
+     */
+    public void notifyChanged(String topic, Properties props);
+
+    /**
+     * Notifies both internal and external listeners of a change to some object.
+     * @param topic A topic, as defined in the interface definitions of the various objects.
+     * Note that this is not a <i>full</i> topic, but merely the 'last part', such as "ADDED";
+     * this allows the ChangeNotifier to generate internal or external topics.
+     * @param props Properties to pack with the event. May be null.
+     * @param internalOnly Indicates this event is only for internal use, and the external
+     * events should not be sent.
+     */
+    public void notifyChanged(String topic, Properties props, boolean internalOnly);
+
+    /**
+     * Gets a topic name which allows subscription to all topics that this ChangeNotifier can send.
+     * @param publicTopic Indicates whether we are interested in the public (<code>true</code>) or the
+     * private topic (<code>false</code>).
+     * @return A topic name.
+     */
+    String getTopicAll(boolean publicTopic);
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,62 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Properties;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * ChangeNotifierImpl provides a basic implementation of a ChangeNotifier, intended to be used
+ * by classes related to the RepositoryAdmin.<br>
+ * <br>
+ * Topics are built up in the following fashion:
+ * <ul>
+ * <li><b>...TopicRoot</b> All topics start with a TopicRoot, which is the same for all related classes, and ends with a "/".
+ * There can be internal and external topics, hence two TopicRoot parameters in the constructor.</li>
+ * <li><b>entityRoot</b> This is followed by a class-specific root, usually consisting of the classname with an added "/".</li>
+ * <li>Finally, for each call to <code>notifyChanged</code>, a topic can be specified, which is something like
+ * "CHANGED" or "ADDED".</li>
+ * </ul>
+ */
+public class ChangeNotifierImpl implements ChangeNotifier {
+
+    private final EventAdmin m_eventAdmin;
+    private final String m_privateTopicRoot;
+    private final String m_publicTopicRoot;
+    private final String m_entityRoot;
+
+    /**
+     * Creates a new ChangeNotifierImpl.
+     * @param eventAdmin An EventAdmin to send events to.
+     * @param privateTopicRoot The root of all private topics; see TopicRoot in the description of this class.
+     * @param publicTopicRoot The root of all public topics; see TopicRoot in the description of this class.
+     * @param entityRoot A class-specific root for the class which will use this ChangeNotifierImpl.
+     */
+    ChangeNotifierImpl(EventAdmin eventAdmin, String privateTopicRoot, String publicTopicRoot, String entityRoot) {
+        m_eventAdmin = eventAdmin;
+        m_privateTopicRoot = privateTopicRoot;
+        m_publicTopicRoot = publicTopicRoot;
+        m_entityRoot = entityRoot;
+    }
+
+    public void notifyChanged(String topic, Properties props, boolean internalOnly) {
+        m_eventAdmin.sendEvent(new Event(m_privateTopicRoot + m_entityRoot + topic, props));
+        if (!internalOnly) {
+            m_eventAdmin.postEvent(new Event(m_publicTopicRoot + m_entityRoot + topic, props));
+        }
+    }
+
+    public void notifyChanged(String topic, Properties props) {
+        notifyChanged(topic, props, false);
+    }
+
+    public String getTopicAll(boolean publicTopic) {
+        if (publicTopic) {
+            return m_publicTopicRoot + m_entityRoot + "*";
+        }
+        else {
+            return m_privateTopicRoot + m_entityRoot + "*";
+        }
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierManager.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierManager.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierManager.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ChangeNotifierManager.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,34 @@
+package net.luminis.liq.client.repository.impl;
+
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * ChangeNotifierManager handles a number of ChangeNotifiers, so there is only
+ * one dependency on EventAdmin; this manager directs all calls from the ChangeNotifiers
+ * to the 'real' EventAdmin.
+ */
+public class ChangeNotifierManager implements EventAdmin {
+
+    private volatile EventAdmin m_eventAdmin; /* Will be injected by dependency manager */
+
+    /**
+     * Creates and configures a ChangeNotifier for use with the given topics.
+     * @param privateTopicRoot The root of all private topics; see TopicRoot in the description of {@link ChangeNotifierImpl}.
+     * @param publicTopicRoot The root of all public topics; see TopicRoot in the description of {@link ChangeNotifierImpl}.
+     * @param entityRoot A class-specific root for the class which will use this ChangeNotifierImpl.
+     * @return The newly configured ChangeNotifier.
+     */
+    public ChangeNotifier getConfiguredNotifier(String privateTopicRoot, String publicTopicRoot, String entityRoot) {
+        return new ChangeNotifierImpl(this, privateTopicRoot, publicTopicRoot, entityRoot);
+    }
+
+    public void postEvent(Event event) {
+        m_eventAdmin.postEvent(event);
+    }
+
+    public void sendEvent(Event event) {
+        m_eventAdmin.sendEvent(event);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentArtifactImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentArtifactImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentArtifactImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentArtifactImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,142 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+import net.luminis.liq.client.repository.object.DeploymentArtifact;
+
+/**
+ * This class is a basic implementation of DeploymentArtifact, with additional facilities
+ * for serializing this artifacts with its directives.
+ */
+public class DeploymentArtifactImpl implements DeploymentArtifact {
+    private final static String XML_NODE = "deploymentArtifact";
+    private final static String XML_NODE_URL = "url";
+    private final static String XML_NODE_DIRECTIVES = "directives";
+    private final String m_url;
+    private final Map<String, String> m_directives = new HashMap<String, String>();
+
+    /**
+     * Creates a new DeploymentArtifactImpl.
+     * @param url The url to the new artifact, as a string; should not be null;
+     */
+    public DeploymentArtifactImpl(String url) {
+        if (url == null) {
+            throw new IllegalArgumentException("The url should not be null.");
+        }
+        m_url = url;
+    }
+
+    /**
+     * Creates a new DeploymentArtifactImpl by deserializing it from an XML stream.
+     * @param reader A stream reader for the XML representation of this object.
+     */
+    public DeploymentArtifactImpl(HierarchicalStreamReader reader) {
+        reader.moveDown();
+        m_url = reader.getValue();
+        reader.moveUp();
+
+        reader.moveDown();
+        while (reader.hasMoreChildren()) {
+            reader.moveDown();
+            m_directives.put(reader.getNodeName(), reader.getValue());
+            reader.moveUp();
+        }
+        reader.moveUp();
+
+    }
+
+    /**
+     * Writes this object to an XML stream.
+     */
+    public void marshal(HierarchicalStreamWriter writer) {
+        writer.startNode(XML_NODE);
+
+        writer.startNode(XML_NODE_URL);
+        writer.setValue(m_url);
+        writer.endNode(); // url node
+
+        writer.startNode(XML_NODE_DIRECTIVES);
+        for (Map.Entry<String, String> entry : m_directives.entrySet()) {
+            writer.startNode(entry.getKey());
+            assert (entry.getValue() != null);
+            writer.setValue(entry.getValue());
+            writer.endNode(); // this directive entry
+        }
+        writer.endNode(); // directives node
+
+        writer.endNode(); // deploymentartifact node
+    }
+
+    /**
+     * Adds a directive to this object.
+     */
+    void addDirective(String key, String value) {
+        if ((key == null) || (value == null)) {
+            throw new IllegalArgumentException("Neither the key nor the value should be null.");
+        }
+        m_directives.put(key, value);
+    }
+
+    public String getDirective(String key) {
+        return m_directives.get(key);
+    }
+
+    public String[] getKeys() {
+        return m_directives.keySet().toArray(new String[m_directives.size()]);
+    }
+
+    public String getUrl() {
+        return m_url;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if ((other == null) || !getClass().equals(other.getClass())) {
+            return false;
+        }
+
+        DeploymentArtifactImpl dai = (DeploymentArtifactImpl) other;
+
+        boolean result = true;
+
+        result &= getUrl().equals(dai.getUrl());
+
+        result &= (getKeys().length == dai.getKeys().length);
+
+        for (String key : getKeys()) {
+            result &= getDirective(key).equals(dai.getDirective(key));
+        }
+
+        return result;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = getUrl().hashCode();
+
+        for (String key : getKeys()) {
+            result ^= getDirective(key).hashCode();
+        }
+
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder(getUrl() + " [ ");
+        for (String key : getKeys()) {
+            result.append(key)
+            .append(": ")
+            .append(getDirective(key))
+            .append(" ");
+        }
+        result.append("]");
+
+        return result.toString();
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionObjectImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionObjectImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionObjectImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionObjectImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,99 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.DeploymentArtifact;
+import net.luminis.liq.client.repository.object.DeploymentVersionObject;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+/**
+ * Implementation class for the DeploymentVersionObject. For 'what it does', see DeploymentVersionObject,
+ * for 'how it works', see RepositoryObjectImpl.
+ */
+public class DeploymentVersionObjectImpl extends RepositoryObjectImpl<DeploymentVersionObject> implements DeploymentVersionObject {
+    private final static String XML_NODE = "deploymentversion";
+    private final static String ARTIFACTS_XML_NODE = "artifacts";
+    private DeploymentArtifact[] m_deploymentArtifacts;
+
+    /**
+     * Creates a new <code>DeploymentVersionObjectImpl</code>.
+     * @param attributes A map of attributes; must include <code>KEY_GATEWAYID</code>, <code>KEY_VERSION</code>.
+     * @param deploymentArtifacts A (possibly empty) array of DeploymentArtifacts.
+     * @param notifier A change notifier to be used by this object.
+     */
+    DeploymentVersionObjectImpl(Map<String, String> attributes, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, new String[] {KEY_GATEWAYID, KEY_VERSION}, new boolean[] {false, false}), notifier, XML_NODE);
+    }
+
+    DeploymentVersionObjectImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, new String[] {KEY_GATEWAYID, KEY_VERSION}, new boolean[] {false, false}), tags, notifier, XML_NODE);
+    }
+
+    DeploymentVersionObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier) {
+        super(reader, notifier, XML_NODE);
+    }
+
+    synchronized void setDeploymentArtifacts(DeploymentArtifact[] deploymentArtifacts) {
+        if (m_deploymentArtifacts != null) {
+            throw new IllegalStateException("Deployment artifacts are already set; this can only be done once.");
+        }
+        if (deploymentArtifacts == null) {
+            throw new IllegalArgumentException("The argument should not be null.");
+        }
+        else {
+            m_deploymentArtifacts = deploymentArtifacts;
+        }
+    }
+
+    @Override
+    protected void readCustom(HierarchicalStreamReader reader) {
+        List<DeploymentArtifact> result = new ArrayList<DeploymentArtifact>();
+        reader.moveDown();
+        while (reader.hasMoreChildren()) {
+            reader.moveDown();
+            DeploymentArtifactImpl deploymentArtifactImpl = new DeploymentArtifactImpl(reader);
+            result.add(deploymentArtifactImpl);
+            reader.moveUp();
+        }
+        setDeploymentArtifacts(result.toArray(new DeploymentArtifact[result.size()]));
+        reader.moveUp();
+    }
+
+    @Override
+    protected synchronized void writeCustom(HierarchicalStreamWriter writer) {
+        if (m_deploymentArtifacts == null) {
+            throw new IllegalStateException("This object is not fully initialized, so it cannot be serialized.");
+        }
+        writer.startNode(ARTIFACTS_XML_NODE);
+        for (DeploymentArtifact da : m_deploymentArtifacts) {
+            ((DeploymentArtifactImpl) da).marshal(writer);
+        }
+        writer.endNode();
+    }
+
+    private static String[] DEFINING_KEYS = new String[] {KEY_GATEWAYID, KEY_VERSION};
+    @Override
+    String[] getDefiningKeys() {
+        return DEFINING_KEYS;
+    }
+
+    public String getGatewayID() {
+        return getAttribute(KEY_GATEWAYID);
+    }
+
+    public String getVersion() {
+        return getAttribute(KEY_VERSION);
+    }
+
+    public synchronized DeploymentArtifact[] getDeploymentArtifacts() {
+        if (m_deploymentArtifacts == null) {
+            throw new IllegalStateException("This object is not fully initialized yet.");
+        }
+        return m_deploymentArtifacts.clone();
+    }
+}
+

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/DeploymentVersionRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,106 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.RepositoryUtil;
+import net.luminis.liq.client.repository.object.DeploymentArtifact;
+import net.luminis.liq.client.repository.object.DeploymentVersionObject;
+import net.luminis.liq.client.repository.repository.DeploymentVersionRepository;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the GatewayRepository. For 'what it does', see GatewayRepository,
+ * for 'how it works', see ObjectRepositoryImpl.
+ * TODO: For now, this class reuses the functionality of ObjectRepositoryImpl. In the future, it
+ * might be useful to create a custom implementation for performance reasons.
+ */
+public class DeploymentVersionRepositoryImpl extends ObjectRepositoryImpl<DeploymentVersionObjectImpl, DeploymentVersionObject> implements DeploymentVersionRepository {
+    private final static String XML_NODE = "deploymentversions";
+
+    public DeploymentVersionRepositoryImpl(ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+    }
+
+    /*
+     * The mechanism below allows us to insert the artifacts that are passed to create
+     * into the newInhabitant, while still using the nice handling of ObjectRepositoryImpl.
+     */
+    private DeploymentArtifact[] m_tempDeploymentArtifacts;
+    private final Object m_creationLock = new Object();
+
+    @Override
+    DeploymentVersionObjectImpl createNewInhabitant(Map<String, String> attributes) {
+        synchronized (m_creationLock) {
+            DeploymentVersionObjectImpl result = new DeploymentVersionObjectImpl(attributes, this);
+            result.setDeploymentArtifacts(m_tempDeploymentArtifacts);
+            m_tempDeploymentArtifacts = null;
+            return result;
+        }
+    }
+
+    @Override
+    DeploymentVersionObjectImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        synchronized (m_creationLock) {
+            DeploymentVersionObjectImpl result = new DeploymentVersionObjectImpl(attributes, tags, this);
+            result.setDeploymentArtifacts(m_tempDeploymentArtifacts);
+            m_tempDeploymentArtifacts = null;
+            return result;
+        }
+    }
+
+    @Override
+    DeploymentVersionObjectImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        return new DeploymentVersionObjectImpl(reader, this);
+    }
+
+    public DeploymentVersionObject create(Map<String, String> attributes, Map<String, String> tags, DeploymentArtifact[] artifacts) {
+        synchronized (m_creationLock) {
+            m_tempDeploymentArtifacts = artifacts;
+            return super.create(attributes, tags);
+        }
+    }
+
+    private Comparator<DeploymentVersionObject> versionComparator = new Comparator<DeploymentVersionObject>() {
+        public int compare(DeploymentVersionObject o1, DeploymentVersionObject o2) {
+            return Version.parseVersion(o1.getVersion()).compareTo(Version.parseVersion(o2.getVersion()));
+        }
+    };
+
+    public List<DeploymentVersionObject> getDeploymentVersions(String gatewayID) {
+        List<DeploymentVersionObject> result = null;
+            try {
+                result = get(createFilter("(" + DeploymentVersionObject.KEY_GATEWAYID + "=" + RepositoryUtil.escapeFilterValue(gatewayID) + ")"));
+                Collections.sort(result, versionComparator);
+            }
+            catch (InvalidSyntaxException e) {
+                // Too bad, probably an illegal gatewayID.
+                result = new ArrayList<DeploymentVersionObject>();
+            }
+        return result;
+    }
+
+    public DeploymentVersionObject getMostRecentDeploymentVersion(String gatewayID) {
+        List<DeploymentVersionObject> versions = getDeploymentVersions(gatewayID);
+        DeploymentVersionObject result = null;
+        if ((versions != null) && (versions.size() > 0)) {
+            result = versions.get(versions.size() - 1);
+        }
+        return result;
+    }
+
+    public DeploymentArtifact createDeploymentArtifact(String url, Map<String, String> directives) {
+        DeploymentArtifactImpl result =  new DeploymentArtifactImpl(url);
+        for (Map.Entry<String, String> entry : directives.entrySet()) {
+            result.addDirective(entry.getKey(), entry.getValue());
+        }
+        return result;
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayObjectImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayObjectImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayObjectImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayObjectImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,66 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.GatewayObject;
+import net.luminis.liq.client.repository.object.License2GatewayAssociation;
+import net.luminis.liq.client.repository.object.LicenseObject;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the GatewayObject. For 'what it does', see GatewayObject,
+ * for 'how it works', see RepositoryObjectImpl.
+ */
+public class GatewayObjectImpl extends RepositoryObjectImpl<GatewayObject> implements GatewayObject {
+    private final static String XML_NODE = "gateway";
+
+    GatewayObjectImpl(Map<String, String> attributes, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_ID), notifier, XML_NODE);
+    }
+
+    GatewayObjectImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_ID), tags, notifier, XML_NODE);
+    }
+
+    GatewayObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier) {
+        super(reader, notifier, XML_NODE);
+        if(getAttribute(KEY_AUTO_APPROVE) == null) {
+            addAttribute(KEY_AUTO_APPROVE, String.valueOf(false));
+        }
+    }
+
+    public List<LicenseObject> getLicenses() {
+        return getAssociations(LicenseObject.class);
+    }
+
+    public String getID() {
+        return getAttribute(KEY_ID);
+    }
+
+    public List<License2GatewayAssociation> getAssociationsWith(LicenseObject license) {
+        return getAssociationsWith(license, LicenseObject.class, License2GatewayAssociation.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: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GatewayRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,36 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+import net.luminis.liq.client.repository.object.GatewayObject;
+import net.luminis.liq.client.repository.repository.GatewayRepository;
+
+/**
+ * Implementation class for the GatewayRepository. For 'what it does', see GatewayRepository,
+ * for 'how it works', see ObjectRepositoryImpl.
+ */
+public class GatewayRepositoryImpl extends ObjectRepositoryImpl<GatewayObjectImpl, GatewayObject> implements GatewayRepository {
+    private final static String XML_NODE = "gateways";
+
+    public GatewayRepositoryImpl(ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+    }
+
+    @Override
+    GatewayObjectImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        return new GatewayObjectImpl(attributes, tags, this);
+    }
+
+    @Override
+    GatewayObjectImpl createNewInhabitant(Map<String, String> attributes) {
+        return new GatewayObjectImpl(attributes, this);
+    }
+
+    @Override
+    GatewayObjectImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        return new GatewayObjectImpl(reader, this);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,30 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.Group2LicenseAssociation;
+import net.luminis.liq.client.repository.object.GroupObject;
+import net.luminis.liq.client.repository.object.LicenseObject;
+
+import org.osgi.framework.InvalidSyntaxException;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the Group2LicenseAssociation. For 'what it does', see Group2LicenseAssociation,
+ * for 'how it works', see AssociationImpl.
+ */
+public class Group2LicenseAssociationImpl extends AssociationImpl<GroupObject, LicenseObject, Group2LicenseAssociation> implements Group2LicenseAssociation {
+    private final static String XML_NODE = "group2license";
+
+    public Group2LicenseAssociationImpl(Map<String, String> attributes, ChangeNotifier notifier, GroupRepositoryImpl groupRepository, LicenseRepositoryImpl licenseRepository) throws InvalidSyntaxException {
+        super(attributes, notifier, GroupObject.class, LicenseObject.class, groupRepository, licenseRepository, XML_NODE);
+    }
+    public Group2LicenseAssociationImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier, GroupRepositoryImpl groupRepository, LicenseRepositoryImpl licenseRepository) throws InvalidSyntaxException {
+        super(attributes, tags, notifier, GroupObject.class, LicenseObject.class, groupRepository, licenseRepository, XML_NODE);
+    }
+    public Group2LicenseAssociationImpl(HierarchicalStreamReader reader, ChangeNotifier notifier, GroupRepositoryImpl groupRepository, LicenseRepositoryImpl licenseRepository) throws InvalidSyntaxException {
+        super(reader, notifier, GroupObject.class, LicenseObject.class, null, null, groupRepository, licenseRepository, XML_NODE);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/Group2LicenseAssociationRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,60 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.Group2LicenseAssociation;
+import net.luminis.liq.client.repository.object.GroupObject;
+import net.luminis.liq.client.repository.object.LicenseObject;
+import net.luminis.liq.client.repository.repository.Group2LicenseAssociationRepository;
+
+import org.osgi.framework.InvalidSyntaxException;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the Group2LicenseAssociationRepository. For 'what it does', see Group2LicenseAssociationRepository,
+ * for 'how it works', see AssociationRepositoryImpl.
+ */
+public class Group2LicenseAssociationRepositoryImpl extends AssociationRepositoryImpl<GroupObject, LicenseObject, Group2LicenseAssociationImpl, Group2LicenseAssociation> implements Group2LicenseAssociationRepository {
+    private final static String XML_NODE = "groups2licenses";
+
+    private final GroupRepositoryImpl m_groupRepository;
+    private final LicenseRepositoryImpl m_licenseRepository;
+
+    public Group2LicenseAssociationRepositoryImpl(GroupRepositoryImpl groupRepository, LicenseRepositoryImpl licenseRepository, ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+        m_groupRepository = groupRepository;
+        m_licenseRepository = licenseRepository;
+    }
+
+    @Override
+    Group2LicenseAssociationImpl createNewInhabitant(Map<String, String> attributes) {
+        try {
+            return new Group2LicenseAssociationImpl(attributes, this, m_groupRepository, m_licenseRepository);
+        }
+        catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Unable to create association: ", e);
+        }
+    }
+
+    @Override
+    Group2LicenseAssociationImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        try {
+            return new Group2LicenseAssociationImpl(attributes, tags, this, m_groupRepository, m_licenseRepository);
+        }
+        catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Unable to create association: ", e);
+        }
+    }
+
+    @Override
+    Group2LicenseAssociationImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        try {
+            return new Group2LicenseAssociationImpl(reader, this, m_groupRepository, m_licenseRepository);
+        }
+        catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Unable to create association: ", e);
+        }
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupObjectImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupObjectImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupObjectImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupObjectImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,71 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.Artifact2GroupAssociation;
+import net.luminis.liq.client.repository.object.ArtifactObject;
+import net.luminis.liq.client.repository.object.Group2LicenseAssociation;
+import net.luminis.liq.client.repository.object.GroupObject;
+import net.luminis.liq.client.repository.object.LicenseObject;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the GroupObject. For 'what it does', see GroupObject,
+ * for 'how it works', see RepositoryObjectImpl.
+ */
+public class GroupObjectImpl extends RepositoryObjectImpl<GroupObject> implements GroupObject {
+    private final static String XML_NODE = "group";
+
+    GroupObjectImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_NAME), tags, notifier, XML_NODE);
+    }
+
+    GroupObjectImpl(Map<String, String> attributes, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_NAME), notifier, XML_NODE);
+    }
+
+    GroupObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier) {
+        super(reader, notifier, XML_NODE);
+    }
+
+    public List<ArtifactObject> getArtifacts() {
+        return getAssociations(ArtifactObject.class);
+    }
+
+    public List<LicenseObject> getLicenses() {
+        return getAssociations(LicenseObject.class);
+    }
+
+    public String getDescription() {
+        return getAttribute(KEY_DESCRIPTION);
+    }
+
+    public String getName() {
+        return getAttribute(KEY_NAME);
+    }
+
+    public void setDescription(String description) {
+        addAttribute(KEY_DESCRIPTION, description);
+    }
+
+    public void setName(String name) {
+        addAttribute(KEY_NAME, name);
+    }
+
+    public List<Artifact2GroupAssociation> getAssociationsWith(ArtifactObject artifact) {
+        return getAssociationsWith(artifact, ArtifactObject.class, Artifact2GroupAssociation.class);
+    }
+
+    public List<Group2LicenseAssociation> getAssociationsWith(LicenseObject license) {
+        return getAssociationsWith(license, LicenseObject.class, Group2LicenseAssociation.class);
+    }
+
+    private static String[] DEFINING_KEYS = new String[] {KEY_NAME};
+    @Override
+    String[] getDefiningKeys() {
+        return DEFINING_KEYS;
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/GroupRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,36 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+import net.luminis.liq.client.repository.object.GroupObject;
+import net.luminis.liq.client.repository.repository.GroupRepository;
+
+/**
+ * Implementation class for the GroupRepository. For 'what it does', see GroupRepository,
+ * for 'how it works', see ObjectRepositoryImpl.
+ */
+public class GroupRepositoryImpl extends ObjectRepositoryImpl<GroupObjectImpl, GroupObject> implements GroupRepository {
+    private final static String XML_NODE = "groups";
+
+    public GroupRepositoryImpl(ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+    }
+
+    @Override
+    GroupObjectImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        return new GroupObjectImpl(attributes, tags, this);
+    }
+
+    @Override
+    GroupObjectImpl createNewInhabitant(Map<String, String> attributes) {
+        return new GroupObjectImpl(attributes, this);
+    }
+
+    @Override
+    GroupObjectImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        return new GroupObjectImpl(reader, this);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,30 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.GatewayObject;
+import net.luminis.liq.client.repository.object.License2GatewayAssociation;
+import net.luminis.liq.client.repository.object.LicenseObject;
+
+import org.osgi.framework.InvalidSyntaxException;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the License2GatewayAssociation. For 'what it does', see License2GatewayAssociation,
+ * for 'how it works', see AssociationImpl.
+ */
+public class License2GatewayAssociationImpl extends AssociationImpl<LicenseObject, GatewayObject, License2GatewayAssociation> implements License2GatewayAssociation {
+    private final static String XML_NODE = "license2gateway";
+
+    public License2GatewayAssociationImpl(Map<String, String> attributes, ChangeNotifier notifier, LicenseRepositoryImpl licenseRepository, GatewayRepositoryImpl gatewayRepository) throws InvalidSyntaxException {
+        super(attributes, notifier, LicenseObject.class, GatewayObject.class, licenseRepository, gatewayRepository, XML_NODE);
+    }
+    public License2GatewayAssociationImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier, LicenseRepositoryImpl licenseRepository, GatewayRepositoryImpl gatewayRepository) throws InvalidSyntaxException {
+        super(attributes, tags, notifier, LicenseObject.class, GatewayObject.class, licenseRepository, gatewayRepository, XML_NODE);
+    }
+    public License2GatewayAssociationImpl(HierarchicalStreamReader reader, ChangeNotifier notifier, LicenseRepositoryImpl licenseRepository, GatewayRepositoryImpl gatewayRepository) throws InvalidSyntaxException {
+        super(reader, notifier, LicenseObject.class, GatewayObject.class, null, null, licenseRepository, gatewayRepository, XML_NODE);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/License2GatewayAssociationRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,71 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.GatewayObject;
+import net.luminis.liq.client.repository.object.License2GatewayAssociation;
+import net.luminis.liq.client.repository.object.LicenseObject;
+import net.luminis.liq.client.repository.repository.License2GatewayAssociationRepository;
+
+import org.osgi.framework.InvalidSyntaxException;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+/**
+ * Implementation class for the License2GatewayAssociationRepository. For 'what it does', see License2GatewayAssociationRepository,
+ * for 'how it works', see AssociationRepositoryImpl.
+ */
+
+public class License2GatewayAssociationRepositoryImpl extends AssociationRepositoryImpl<LicenseObject, GatewayObject, License2GatewayAssociationImpl, License2GatewayAssociation> implements License2GatewayAssociationRepository {
+    private final static String XML_NODE = "licenses2gateways";
+
+    private final LicenseRepositoryImpl m_licenseRepository;
+    private final GatewayRepositoryImpl m_gatewayRepository;
+
+    public License2GatewayAssociationRepositoryImpl(LicenseRepositoryImpl licenseRepository, GatewayRepositoryImpl gatewayRepository, ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+        m_licenseRepository = licenseRepository;
+        m_gatewayRepository = gatewayRepository;
+    }
+
+    @Override
+    License2GatewayAssociationImpl createNewInhabitant(Map<String, String> attributes) {
+        try {
+            return new License2GatewayAssociationImpl(attributes, this, m_licenseRepository, m_gatewayRepository);
+        }
+        catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Unable to create association: ", e);
+        }
+    }
+
+    @Override
+    License2GatewayAssociationImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        try {
+            return new License2GatewayAssociationImpl(attributes, tags, this, m_licenseRepository, m_gatewayRepository);
+        }
+        catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Unable to create association: ", e);
+        }
+    }
+
+    @Override
+    License2GatewayAssociationImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        try {
+            return new License2GatewayAssociationImpl(reader, this, m_licenseRepository, m_gatewayRepository);
+        }
+        catch (InvalidSyntaxException e) {
+            throw new IllegalArgumentException("Unable to create association: ", e);
+        }
+    }
+
+    public License2GatewayAssociation createLicense2GatewayFilter(LicenseObject license, String gatewayFilter) {
+        try {
+            m_gatewayRepository.createFilter(gatewayFilter);
+        }
+        catch (InvalidSyntaxException ise) {
+            throw new IllegalArgumentException("Gateway filter '" + gatewayFilter + "' cannot be parsed into a valid Filter.", ise);
+        }
+
+        return create(license.getAssociationFilter(null), gatewayFilter);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseObjectImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseObjectImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseObjectImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseObjectImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,71 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.GatewayObject;
+import net.luminis.liq.client.repository.object.Group2LicenseAssociation;
+import net.luminis.liq.client.repository.object.GroupObject;
+import net.luminis.liq.client.repository.object.License2GatewayAssociation;
+import net.luminis.liq.client.repository.object.LicenseObject;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the LicenseObject. For 'what it does', see LicenseObject,
+ * for 'how it works', see RepositoryObjectImpl.
+ */
+public class LicenseObjectImpl extends RepositoryObjectImpl<LicenseObject> implements LicenseObject {
+    private final static String XML_NODE = "license";
+
+    LicenseObjectImpl(Map<String, String> attributes, Map<String, String> tags, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_NAME), tags, notifier, XML_NODE);
+    }
+
+    LicenseObjectImpl(Map<String, String> attributes, ChangeNotifier notifier) {
+        super(checkAttributes(attributes, KEY_NAME), notifier, XML_NODE);
+    }
+
+    LicenseObjectImpl(HierarchicalStreamReader reader, ChangeNotifier notifier) {
+        super(reader, notifier, XML_NODE);
+    }
+
+    public List<GatewayObject> getGateways() {
+        return getAssociations(GatewayObject.class);
+    }
+
+    public List<GroupObject> getGroups() {
+        return getAssociations(GroupObject.class);
+    }
+
+    public String getDescription() {
+        return getAttribute(KEY_DESCRIPTION);
+    }
+
+    public String getName() {
+        return getAttribute(KEY_NAME);
+    }
+
+    public void setDescription(String description) {
+        addAttribute(KEY_DESCRIPTION, description);
+    }
+
+    public void setName(String name) {
+        addAttribute(KEY_NAME, name);
+    }
+
+    public List<Group2LicenseAssociation> getAssociationsWith(GroupObject group) {
+        return getAssociationsWith(group, GroupObject.class, Group2LicenseAssociation.class);
+    }
+
+    public List<License2GatewayAssociation> getAssociationsWith(GatewayObject gateway) {
+        return getAssociationsWith(gateway, GatewayObject.class, License2GatewayAssociation.class);
+    }
+
+    private static String[] DEFINING_KEYS = new String[] {KEY_NAME};
+    @Override
+    String[] getDefiningKeys() {
+        return DEFINING_KEYS;
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/LicenseRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,36 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.Map;
+
+import net.luminis.liq.client.repository.object.LicenseObject;
+import net.luminis.liq.client.repository.repository.LicenseRepository;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+
+/**
+ * Implementation class for the LicenseRepository. For 'what it does', see LicenseRepository,
+ * for 'how it works', see ObjectRepositoryImpl.
+ */
+public class LicenseRepositoryImpl extends ObjectRepositoryImpl<LicenseObjectImpl, LicenseObject> implements LicenseRepository {
+    private final static String XML_NODE = "licenses";
+
+    public LicenseRepositoryImpl(ChangeNotifier notifier) {
+        super(notifier, XML_NODE);
+    }
+
+    @Override
+    LicenseObjectImpl createNewInhabitant(Map<String, String> attributes, Map<String, String> tags) {
+        return new LicenseObjectImpl(attributes, tags, this);
+    }
+
+    @Override
+    LicenseObjectImpl createNewInhabitant(Map<String, String> attributes) {
+        return new LicenseObjectImpl(attributes, this);
+    }
+
+    @Override
+    LicenseObjectImpl createNewInhabitant(HierarchicalStreamReader reader) {
+        return new LicenseObjectImpl(reader, this);
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ObjectRepositoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ObjectRepositoryImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ObjectRepositoryImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/ObjectRepositoryImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,238 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import net.luminis.liq.client.repository.ObjectRepository;
+import net.luminis.liq.client.repository.RepositoryObject;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+
+/**
+ * A basic Object Repository, having most of the functionality that the object repositories share.
+ * The creation of new inhabitants, and the deserialization of inhabitants is delegated to
+ * derived classes.
+ *
+ * @param <I> An implementation type of the repository object that this repository will store.
+ * @param <T> The non-generic interface that <code>I</code> implements.
+ */
+abstract class ObjectRepositoryImpl<I extends RepositoryObjectImpl<T>, T extends RepositoryObject> implements ObjectRepository<T>, EventHandler, ChangeNotifier {
+    protected BundleContext m_context; /* injected by dependency manager */
+
+    private final List<T> m_repo = new CopyOnWriteArrayList<T>();
+    private final ChangeNotifier m_notifier;
+
+    private final String m_xmlNode;
+
+    private volatile boolean m_busy = false;
+
+    /**
+     * The main constructor for this repository.
+     * @param xmlNode The tag that represents this repository (not its objects) in an XML representation.
+     */
+    public ObjectRepositoryImpl(ChangeNotifier notifier, String xmlNode) {
+        m_notifier = notifier;
+        m_xmlNode = xmlNode;
+    }
+
+
+    /**
+     * Creates a new inhabitant of this repository with the given attributes. The actual creation of the
+     * object is delagated to a derived class; this function will make sure the correct events get fired
+     * and the object gets stored.
+     */
+    // About this SuppressWarnings: for some reason, the compiler cannot see that I is a proper subtype of T.
+    @SuppressWarnings("unchecked")
+    public T create(Map<String, String> attributes, Map <String, String> tags) throws IllegalArgumentException {
+        if (m_busy) {
+            throw new IllegalStateException("The repository is currently busy, so no new objects can be created.");
+        }
+        T result =  (T) createNewInhabitant(attributes, tags);
+        if (add(result)) {
+            return result;
+        }
+        throw new IllegalArgumentException();
+    }
+
+    /**
+     * Helper method that stores an object in the repository, taking care of the right events.
+     * @param entity the object to be stored.
+     * @return true only when the object (or at least one identical to it) did not yet exist in the repository.
+     */
+    boolean add(T entity) {
+        boolean result = false;
+
+        synchronized (m_repo) {
+            if (!m_repo.contains(entity)) {
+                m_repo.add(entity);
+                result = true;
+            }
+        }
+        if (result) {
+            notifyChanged(entity, RepositoryObject.TOPIC_ADDED_SUFFIX);
+        }
+        return result;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void remove(T entity) {
+        if (m_busy) {
+            throw new IllegalStateException("The repository is currently busy, so no objects can be removed.");
+        }
+        boolean result = false;
+        synchronized (m_repo) {
+            if (m_repo.remove(entity)) {
+                ((I) entity).setDeleted();
+                result = true;
+            }
+        }
+        if (result) {
+            notifyChanged(entity, RepositoryObject.TOPIC_REMOVED_SUFFIX);
+        }
+    }
+
+    /**
+     * Removes all objects in this repository, without caring for the consistency and
+     * correct event firing.
+     */
+    @SuppressWarnings("unchecked")
+    void removeAll() {
+        synchronized(m_repo) {
+            for (T object : m_repo) {
+                ((I) object).setDeleted();
+            }
+            m_repo.clear();
+        }
+    }
+
+    /**
+     * Notifies listeners of a change to a given object. It will also notify
+     * listeners of any changes to the status of this repository.
+     * @param entity The object that has changed.
+     * @param topic The topic to use.
+     */
+    private void notifyChanged(T entity, String topic) {
+        Properties props = new Properties();
+        props.put(RepositoryObject.EVENT_ENTITY, entity);
+        notifyChanged(topic, props, m_busy);
+    }
+
+    public void notifyChanged(String topic, Properties props, boolean internalOnly) {
+        m_notifier.notifyChanged(topic, props, internalOnly);
+    }
+
+    public void notifyChanged(String topic, Properties props) {
+        notifyChanged(topic, props, false);
+    }
+
+    public String getTopicAll(boolean publicTopic) {
+        return m_notifier.getTopicAll(publicTopic);
+    }
+
+    public List<T> get() {
+        return new ArrayList<T>(m_repo);
+    }
+
+    public List<T> get(Filter filter) {
+        List<T> result = new ArrayList<T>();
+        for (T entry : m_repo) {
+            if (filter.match(entry.getDictionary())) {
+                result.add(entry);
+            }
+        }
+        return result;
+    }
+
+    Filter createFilter(String filter) throws InvalidSyntaxException {
+        return m_context.createFilter(filter);
+    }
+
+    /**
+     * Writes this repository and its inhabitants to an XML stream. The serialization of
+     * the inhabitants will be delegated to the inhabitants themselves.
+     * @param writer The writer to write the XML representation to.
+     */
+    @SuppressWarnings("unchecked")
+    public void marshal(HierarchicalStreamWriter writer) {
+        writer.startNode(m_xmlNode);
+        for (T inhabitant : m_repo) {
+            ((I) inhabitant).marshal(writer);
+        }
+        writer.endNode();
+    }
+
+    /**
+     * Reads the inhabitants of this repository from an XML stream.
+     * @param reader A reader of the XML representation.
+     */
+    @SuppressWarnings("unchecked")
+    public void unmarshal(HierarchicalStreamReader reader) {
+        try {
+            while (reader.hasMoreChildren()) {
+                reader.moveDown();
+                I newInhabitant = createNewInhabitant(reader);
+                newInhabitant.setBusy(m_busy);
+                add((T) newInhabitant);
+                reader.moveUp();
+            }
+        }
+        catch (Exception ex) {
+            throw new IllegalArgumentException(ex);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void handleEvent(Event e) {
+        for (T inhabitant : m_repo) {
+            ((I) inhabitant).handleEvent(e);
+        }
+    }
+
+    /**
+     * Creates a new inhabitant of the repository based on a map of attributes.
+     * @param attributes A map of attributes
+     * @param tags A map of tags
+     * @return The new inhabitant.
+     */
+    abstract I createNewInhabitant(Map<String, String> attributes, Map<String, String> tags);
+    /**
+     * Creates a new inhabitant of the repository based on a map of attributes.
+     * @param attributes A map of attributes
+     * @return The new inhabitant.
+     */
+    abstract I createNewInhabitant(Map<String, String> attributes);
+    /**
+     * Creates a new inhabitant of the repository based on an XML representation.
+     * @param reader A reader for the XML representation.
+     * @return The new inhabitant.
+     */
+    abstract I createNewInhabitant(HierarchicalStreamReader reader);
+
+    public String getXmlNode() {
+        return m_xmlNode;
+    }
+
+    /**
+     * Sets this repository to busy: this will be delegated to all inhabitants.
+     */
+    @SuppressWarnings("unchecked")
+    public void setBusy(boolean busy) {
+        synchronized(m_repo) {
+            m_busy = busy;
+            for (RepositoryObject o : m_repo) {
+                ((I) o).setBusy(busy);
+            }
+        }
+    }
+
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,372 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+
+import net.luminis.liq.client.repository.ObjectRepository;
+import net.luminis.liq.client.repository.RepositoryAdmin;
+import net.luminis.liq.client.repository.RepositoryAdminLoginContext;
+import net.luminis.liq.client.repository.RepositoryObject;
+import net.luminis.liq.client.repository.RepositoryObject.WorkingState;
+import net.luminis.liq.client.repository.impl.RepositoryAdminLoginContextImpl.RepositorySetDescriptor;
+import net.luminis.liq.client.repository.repository.ArtifactRepository;
+import net.luminis.liq.repository.Repository;
+import net.luminis.liq.repository.ext.BackupRepository;
+import net.luminis.liq.repository.ext.CachedRepository;
+import net.luminis.liq.repository.impl.CachedRepositoryImpl;
+import net.luminis.liq.repository.impl.FilebasedBackupRepository;
+import net.luminis.liq.repository.impl.RemoteRepository;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.osgi.service.useradmin.User;
+
+/**
+ * An implementation of RepositoryAdmin, responsible for managing <code>ObjectRepositoryImpl</code>
+ * descendants.<br>
+ * The actual repository managing is delegated to <code>RepositorySet</code>s, while the logic
+ * for binding these sets together is located in this class. Set actual <code>RepositorySet</code>s
+ * to be used are defined in <code>login(...)</code>.<br>
+ */
+public class RepositoryAdminImpl implements RepositoryAdmin {
+    /**
+     * Maps from interface classes of the ObjectRepositories to their implementations.
+     */
+    @SuppressWarnings("unchecked")
+    private Map<Class<? extends ObjectRepository>, ObjectRepositoryImpl> m_repositories;
+
+    private final ChangeNotifier m_changeNotifier;
+    private volatile BundleContext m_context; /* Injected by dependency manager */
+    private volatile PreferencesService m_preferences; /* Injected by dependency manager */
+    private volatile LogService m_log; /* Injected by dependency manager */
+
+    private final Object m_lock = new Object();
+
+    private final static String PREFS_LOCAL_FILE_ROOT = "ClientRepositoryAdmin";
+    private final static String PREFS_LOCAL_FILE_LOCATION = "FileLocation";
+    private final static String PREFS_LOCAL_FILE_CURRENT = "current";
+    private final static String PREFS_LOCAL_FILE_BACKUP = "backup";
+    private User m_user;
+    private RepositorySet[] m_repositorySets;
+    private final Activator m_repositoryFactory;
+
+    RepositoryAdminImpl(Activator factory, ChangeNotifier changeNotifier) {
+        m_repositoryFactory = factory;
+        m_changeNotifier = changeNotifier;
+    }
+
+    @SuppressWarnings("unchecked")
+    void initialize(Map<Class<? extends ObjectRepository>, ObjectRepositoryImpl> repositories) {
+        m_repositories = repositories;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void start() {
+        initialize(m_repositoryFactory.publishRepositories());
+    }
+
+    public void stop() {
+        m_repositoryFactory.pullRepositories();
+    }
+
+    public void checkout() throws IOException {
+        synchronized (m_lock) {
+            ensureLogin();
+            for (RepositorySet set : m_repositorySets) {
+                set.checkout();
+            }
+            m_changeNotifier.notifyChanged(TOPIC_REFRESH_SUFFIX, null);
+        }
+    }
+
+    public void commit() throws IOException {
+        synchronized (m_lock) {
+            ensureLogin();
+            for (RepositorySet set : m_repositorySets) {
+                set.commit();
+            }
+            m_changeNotifier.notifyChanged(TOPIC_REFRESH_SUFFIX, null);
+        }
+    }
+
+    public void flush() throws IOException {
+        synchronized (m_lock) {
+            ensureLogin();
+            for (RepositorySet set : m_repositorySets) {
+                set.writeLocal();
+                set.savePreferences();
+            }
+            m_changeNotifier.notifyChanged(TOPIC_FLUSHED_SUFFIX, null);
+        }
+    }
+
+    public void revert() throws IOException {
+        ensureLogin();
+        synchronized (m_lock) {
+            for (RepositorySet set : m_repositorySets) {
+                set.revert();
+            }
+            m_changeNotifier.notifyChanged(TOPIC_REFRESH_SUFFIX, null);
+        }
+    }
+
+    public boolean isCurrent() throws IOException {
+        ensureLogin();
+        synchronized (m_lock) {
+            boolean result = true;
+            for (RepositorySet set : m_repositorySets) {
+                    result &= (set.isCurrent() || !set.writeAccess());
+            }
+            return result;
+        }
+    }
+
+    public boolean isModified() {
+        ensureLogin();
+        boolean result = false;
+        for (RepositorySet set : m_repositorySets) {
+            result |= set.isModified();
+        }
+        return result;
+    }
+
+    public RepositoryAdminLoginContext createLoginContext(User user) {
+        if (user == null) {
+            throw new IllegalArgumentException("User may not be null.");
+        }
+        return new RepositoryAdminLoginContextImpl(user);
+    }
+
+    public void login(RepositoryAdminLoginContext context) throws IOException {
+        if (!(context instanceof RepositoryAdminLoginContextImpl)) {
+            throw new IllegalArgumentException("Only the RepositoryAdminLoginContext returned by createLoginContext can be used.");
+        }
+
+        RepositoryAdminLoginContextImpl impl = ((RepositoryAdminLoginContextImpl) context);
+        RepositorySet[] repositorySets = getRepositorySets(impl);
+        // TODO I don't like this line, it should not be here...
+        ((ArtifactRepositoryImpl) m_repositories.get(ArtifactRepository.class)).setObrBase(impl.getObrBase());
+        login(impl.getUser(), repositorySets);
+    }
+
+    /**
+     * Helper method for login; also allows injection of custom RepositorySet objects for
+     * testing purposes.
+     * @throws IOException
+     */
+    void login(User user, RepositorySet[] sets) throws IOException {
+        synchronized(m_lock) {
+            if (m_user != null) {
+                throw new IllegalStateException("Another user is logged in.");
+            }
+
+            m_user = user;
+            m_repositorySets = sets;
+            for (RepositorySet set : m_repositorySets) {
+                set.readLocal();
+                set.loadPreferences();
+            }
+        }
+
+        m_changeNotifier.notifyChanged(TOPIC_LOGIN_SUFFIX, null);
+    }
+
+    public void logout(boolean force) throws IOException {
+        IOException exception = null;
+        synchronized (m_lock) {
+            ensureLogin();
+
+            try {
+                flush();
+            }
+            catch (IOException e) {
+                if (!force) {
+                    throw e;
+                }
+                else {
+                    exception = e;
+                }
+            }
+
+            for (RepositorySet set : m_repositorySets) {
+                set.clearRepositories();
+                set.unregisterHandler();
+            }
+
+            m_user = null;
+            m_repositorySets = new RepositorySet[0];
+        }
+        m_changeNotifier.notifyChanged(TOPIC_LOGOUT_SUFFIX, null);
+        if (exception != null) {
+            throw exception;
+        }
+    }
+
+    boolean loggedIn() {
+        return m_user != null;
+    }
+
+    /**
+     * Helper method to make sure a user is logged in.
+     * @throws IllegalStateException
+     */
+    private void ensureLogin() throws IllegalStateException {
+        if (!loggedIn()) {
+            throw new IllegalStateException("This operation requires a user to be logged in.");
+        }
+    }
+
+    /**
+     * Helper method, creates RepositorySets based on the Login context.
+     */
+    @SuppressWarnings("unchecked")
+    private RepositorySet[] getRepositorySets(RepositoryAdminLoginContextImpl context) throws IOException {
+        // First, some sanity checks on the list of descriptors.
+        for (RepositorySetDescriptor rsd : context.getDescriptors()) {
+            for (Class c : rsd.m_objectRepositories) {
+                // Do we have an impl for each repository class?
+                if (!m_repositories.containsKey(c)) {
+                    throw new IllegalArgumentException(rsd.toString() + " references repository class " + c.getName() + " for which no implementation is available.");
+                }
+                // Do other sets have a reference to this same class?
+                for (RepositorySetDescriptor other : context.getDescriptors()) {
+                    if (other != rsd) {
+                        for (Class otherC : other.m_objectRepositories) {
+                            if (c.equals(otherC)) {
+                                throw new IllegalArgumentException(rsd.toString() + " references repository class " + c.getName() + ", but so does " + other.toString());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        RepositorySet[] result = new RepositorySet[context.getDescriptors().size()];
+
+        /*
+         * Create the lists of repositories and topics, and create and register
+         * the sets with these.
+         */
+        for (int nRsd = 0; nRsd < result.length; nRsd++) {
+            RepositorySetDescriptor rsd = context.getDescriptors().get(nRsd);
+            ObjectRepositoryImpl[] impls = new ObjectRepositoryImpl[rsd.m_objectRepositories.length];
+            String[] topics = new String[rsd.m_objectRepositories.length];
+            for (int nRepo = 0; nRepo < impls.length; nRepo++) {
+                impls[nRepo] = m_repositories.get(rsd.m_objectRepositories[nRepo]);
+                topics[nRepo] = impls[nRepo].getTopicAll(true);
+            }
+            result[nRsd] = loadRepositorySet(context.getUser(), rsd, impls);
+            result[nRsd].registerHandler(m_context, topics);
+        }
+
+        return result;
+    }
+
+    /**
+     * Helper method for login.
+     */
+    private Preferences getRepositoryPrefs(Preferences userPrefs, URL location, String customer, String name) {
+        // Note: we can only use the getAuthority part of the URL for indexing, because the full URL will contain
+        // in the protocol part.
+        Preferences repoPref = userPrefs.node(location.getAuthority() + location.getPath());
+        Preferences customerPref = repoPref.node(customer);
+        return customerPref.node(name);
+    }
+
+    /**
+     * Helper method for login.
+     * @throws IOException
+     */
+    private File getFileFromPreferences(Preferences repositoryPrefs, String type) throws IOException {
+        String directory = repositoryPrefs.get(PREFS_LOCAL_FILE_LOCATION, "");
+
+        if ((directory == "") || !m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory).isDirectory()) {
+            if (!m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory).isDirectory() && (directory != "")) {
+                m_log.log(LogService.LOG_WARNING, "Directory '" + directory + "' should exist according to the preferences, but it does not.");
+            }
+            // The file did not exist, so create a new one.
+            File directoryFile = null;
+            try {
+                File bundleDataDir = m_context.getDataFile(PREFS_LOCAL_FILE_ROOT);
+                if (!bundleDataDir.isDirectory()) {
+                    if (!bundleDataDir.mkdir()) {
+                        throw new IOException("Error creating the local repository root directory.");
+                    }
+                }
+                directoryFile = File.createTempFile("repo", "", bundleDataDir);
+            }
+            catch (IOException e) {
+                // We cannot create the temp file? Then something is seriously wrong, so rethrow.
+                throw e;
+            }
+
+            directoryFile.delete(); // No problem if this goes wrong, it just means it wasn't there yet.
+            if (!directoryFile.mkdir()) {
+                throw new IOException("Error creating the local repository storage directory.");
+            }
+            repositoryPrefs.put(PREFS_LOCAL_FILE_LOCATION, directoryFile.getName());
+            return new File(directoryFile, type);
+        }
+        else {
+            // Get the given file from that location.
+            return m_context.getDataFile(PREFS_LOCAL_FILE_ROOT + "/" + directory + "/" + type);
+        }
+    }
+
+    /**
+     * Helper method for login.
+     * @throws IOException
+     */
+    private BackupRepository getBackupFromPreferences(Preferences repositoryPrefs) throws IOException {
+        File current = getFileFromPreferences(repositoryPrefs, PREFS_LOCAL_FILE_CURRENT);
+        File backup = getFileFromPreferences(repositoryPrefs, PREFS_LOCAL_FILE_BACKUP);
+        return new FilebasedBackupRepository(current, backup);
+    }
+
+    /**
+     * Helper method for login.
+     * @throws IOException
+     */
+    private CachedRepository getCachedRepositoryFromPreferences(User user, Repository repository, Preferences repositoryPrefs) throws IOException {
+        long mostRecentVersion = repositoryPrefs.getLong("version", CachedRepositoryImpl.UNCOMMITTED_VERSION);
+        return new CachedRepositoryImpl(user, repository, getBackupFromPreferences(repositoryPrefs), mostRecentVersion);
+    }
+
+    /**
+     * Helper method for login, which loads a set of repositories.
+     * @param user A <code>User</code> object
+     * @param rsd A RepositorySetDescriptor, defining the set to be created.
+     * @param repos An array of <code>ObjectRepositoryImpl</code> which this set should manage. Each
+     * @return The newly created repository set.
+     * @throws IOException
+     */
+    @SuppressWarnings("unchecked")
+    public RepositorySet loadRepositorySet(User user, RepositorySetDescriptor rsd, ObjectRepositoryImpl[] repos) throws IOException {
+        Repository repo = new RemoteRepository(rsd.m_location, rsd.m_customer, rsd.m_name);
+        Preferences prefs = m_preferences.getUserPreferences(user.getName());
+        Preferences repoPrefs = getRepositoryPrefs(prefs, rsd.m_location, rsd.m_customer, rsd.m_name);
+        return new RepositorySet(m_changeNotifier, m_log, user, repoPrefs, repos, getCachedRepositoryFromPreferences(user, repo, repoPrefs), rsd.m_name, rsd.m_writeAccess);
+    }
+
+    public int getNumberWithWorkingState(Class<? extends RepositoryObject> clazz, WorkingState state) {
+        int result = 0;
+        for (RepositorySet set : m_repositorySets) {
+            result += set.getNumberWithWorkingState(clazz, state);
+        }
+        return result;
+    }
+
+    public WorkingState getWorkingState(RepositoryObject object) {
+        for (RepositorySet set : m_repositorySets) {
+            WorkingState result = set.getWorkingState(object);
+            if (result != null) {
+                return result;
+            }
+        }
+        return WorkingState.Unchanged;
+    }
+}

Added: incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminLoginContextImpl.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminLoginContextImpl.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminLoginContextImpl.java (added)
+++ incubator/ace/trunk/server/src/net/luminis/liq/client/repository/impl/RepositoryAdminLoginContextImpl.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,109 @@
+package net.luminis.liq.client.repository.impl;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.luminis.liq.client.repository.ObjectRepository;
+import net.luminis.liq.client.repository.RepositoryAdminLoginContext;
+import net.luminis.liq.client.repository.repository.Artifact2GroupAssociationRepository;
+import net.luminis.liq.client.repository.repository.ArtifactRepository;
+import net.luminis.liq.client.repository.repository.DeploymentVersionRepository;
+import net.luminis.liq.client.repository.repository.GatewayRepository;
+import net.luminis.liq.client.repository.repository.Group2LicenseAssociationRepository;
+import net.luminis.liq.client.repository.repository.GroupRepository;
+import net.luminis.liq.client.repository.repository.License2GatewayAssociationRepository;
+import net.luminis.liq.client.repository.repository.LicenseRepository;
+
+import org.osgi.service.useradmin.User;
+
+class RepositoryAdminLoginContextImpl implements RepositoryAdminLoginContext {
+    private final User m_user;
+    private final List<RepositorySetDescriptor> m_descriptors = new ArrayList<RepositorySetDescriptor>();
+    private URL m_obrBase;
+
+    RepositoryAdminLoginContextImpl(User user) {
+        m_user = user;
+    }
+
+    @SuppressWarnings("unchecked")
+    public RepositoryAdminLoginContext addRepositories(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess, Class<? extends ObjectRepository>... objectRepositories) {
+        if ((repositoryLocation == null) || (repositoryCustomer == null) || (repositoryName == null)) {
+            throw new IllegalArgumentException("No parameter should be null.");
+        }
+        if ((objectRepositories == null) || (objectRepositories.length == 0)) {
+            throw new IllegalArgumentException("objectRepositories should not be null or empty.");
+        }
+        m_descriptors.add(new RepositorySetDescriptor(repositoryLocation, repositoryCustomer, repositoryName, writeAccess, objectRepositories));
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public RepositoryAdminLoginContext addShopRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess) {
+        return addRepositories(repositoryLocation, repositoryCustomer, repositoryName, writeAccess,
+            ArtifactRepository.class,
+            GroupRepository.class,
+            Artifact2GroupAssociationRepository.class,
+            LicenseRepository.class,
+            Group2LicenseAssociationRepository.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    public RepositoryAdminLoginContext addGatewayRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess) {
+        return addRepositories(repositoryLocation, repositoryCustomer, repositoryName, writeAccess,
+            GatewayRepository.class,
+            License2GatewayAssociationRepository.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    public RepositoryAdminLoginContext addDeploymentRepository(URL repositoryLocation, String repositoryCustomer, String repositoryName, boolean writeAccess) {
+        return addRepositories(repositoryLocation, repositoryCustomer, repositoryName, writeAccess,
+            DeploymentVersionRepository.class);
+    }
+
+
+    public RepositoryAdminLoginContext setObrBase(URL base) {
+        m_obrBase = base;
+        return this;
+    }
+
+    URL getObrBase() {
+        return m_obrBase;
+    }
+
+    User getUser() {
+        return m_user;
+    }
+
+    public List<RepositorySetDescriptor> getDescriptors() {
+        return m_descriptors;
+    }
+
+    /**
+     * Helper class to store all relevant information about a repository in a convenient location before
+     * we start using it.
+     */
+    static class RepositorySetDescriptor {
+        public final URL m_location;
+        public final String m_customer;
+        public final String m_name;
+        public final boolean m_writeAccess;
+        @SuppressWarnings("unchecked")
+        public final Class<? extends ObjectRepository>[] m_objectRepositories;
+
+        @SuppressWarnings("unchecked")
+        RepositorySetDescriptor(URL location, String customer, String name, boolean writeAccess, Class<? extends ObjectRepository>... objectRepositories) {
+            m_location = location;
+            m_customer = customer;
+            m_name = name;
+            m_writeAccess = writeAccess;
+            m_objectRepositories = objectRepositories;
+        }
+
+        @Override
+        public String toString() {
+            return "Repository location " + m_location.toString() + ", customer " + m_customer + ", name " + m_name;
+        }
+    }
+
+}