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 [6/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/repository/ArtifactRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/ArtifactRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,109 @@
+/*
+ * 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 java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.TargetObject;
+
+/**
+ * Interface to a ArtifactRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface ArtifactRepository extends ObjectRepository<ArtifactObject> {
+	/**
+	 * Gets a list of all ArtifactObject's which are resource processing bundles.
+	 */
+	public List<ArtifactObject> getResourceProcessors();
+
+	/**
+	 * Tries to import an artifact into storage, while extracting necessary metadata.
+	 * @param artifact a URL pointing to the 'physical' artifact.
+	 * @param upload Indicates whether this artifact should be uploaded to our own OBR.
+	 * @return An <code>ArtifactObject</code> representing the passed in artifact, if
+	 * (a) the artifact is recognized, (b) there is storage available and (c) there is
+	 * a resource processor available for this type of artifact.
+	 * @throws IllegalArgumentException when the <code>artifact</code> cannot be processed.
+	 * @throws IOException when there is a problem transferring the <code>artifact</code> to storage.
+	 */
+	public ArtifactObject importArtifact(URL artifact, boolean upload) throws IllegalArgumentException, IOException;
+
+	/**
+	 * Checks whether an artifact is 'usable', that is, there is a resource processor available for it,
+	 * if necessary.
+	 * @param artifact A URL pointing to an artifact.
+	 * @return <code>true</code> if the artifact is recognized, and a processor for it is available. <code>false</code>
+	 * otherwise, including when the artifact cannot be reached.
+	 */
+	public boolean recognizeArtifact(URL artifact);
+
+    /**
+     * Tries to import an artifact into storage, while extracting necessary metadata.
+     * @param artifact a URL pointing to the 'physical' artifact.
+     * @param mimetype a String giving this object's mimetype.
+     * @param upload Indicates whether this artifact should be uploaded to our own OBR.
+     * @return An <code>ArtifactObject</code> representing the passed in artifact, if
+     * (a) there is storage available and (b) there is a resource processor
+     * available for this type of artifact.
+     * @throws IllegalArgumentException when the <code>artifact</code> cannot be processed.
+     * @throws IOException when there is a problem transferring the <code>artifact</code> to storage.
+     */
+	public ArtifactObject importArtifact(URL artifact, String mimetype, boolean upload) throws IllegalArgumentException, IOException;
+
+	/**
+	 * Tries to locate a preprocessor for the passed artifact, an processes it. If no processing
+	 * needs to be done, the original artifact's URL will be returned.
+	 * @param artifact An artifact
+	 * @param props A tree of properties objects, to be used for replacement.
+	 * @param targetID The targetID of the target for which this artifact is being processed.
+	 * @param version The deployment version for which this artifact is being processed.
+	 * @return A URL to a new, processed artifact, or to the original one, in case nothing needed to be processed.
+     * @throws IOException Thrown if reading the original artifact goes wrong, or storing the processed one.
+	 */
+	public String preprocessArtifact(ArtifactObject artifact, TargetObject target, String targetID, String version) throws IOException ;
+
+    /**
+     * Indicates whether the template should be processed again, given the properties, and the version to which it
+     * should be compared.
+     * @param url A string representing a URL to the original artifact.
+     * @param props A PropertyResolver which can be used to fill in 'holes' in the template.
+     * @param targetID The targetID of the target for which this artifact is being processed.
+     * @param version The deployment version for which this artifact is being processed.
+     * @param lastVersion The deployment version to which the current one should be compared.
+     * @param newVersion The new, potential version.
+     * @param obrBase A base OBR to upload the new artifact to.
+     * @return Whether or not a new version has to be created.
+     * @throws IOException
+     */
+    public boolean needsNewVersion(ArtifactObject artifact, TargetObject target, String targetID, String fromVersion);
+
+	/**
+	 * Sets the OBR that this artifact repository should use to upload artifacts to.
+	 */
+	public void setObrBase(URL obrBase);
+
+    /**
+     * Gets the OBR that this artifact repository should use to upload artifacts to.
+     * Note that this method may return <code>null</code> if no base was set earlier.
+     */
+    public URL getObrBase();
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DeploymentVersionRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DeploymentVersionRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DeploymentVersionRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DeploymentVersionRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,46 @@
+package org.apache.ace.client.repository.repository;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.object.DeploymentArtifact;
+import org.apache.ace.client.repository.object.DeploymentVersionObject;
+
+
+/**
+ * Interface to a DeploymentVersionRepository. The functionality is defined by the generic ObjectRepository.
+ */
+public interface DeploymentVersionRepository extends ObjectRepository<DeploymentVersionObject> {
+    /**
+     * Creates a new inhabitant based on the given attributes and bundle URLs. The object
+     * will be stored in this repository's store, and will be returned.
+     * @throws IllegalArgumentException Will be thrown when the attributes cannot be accepted.
+     */
+    public DeploymentVersionObject create(Map<String, String> attributes, Map<String, String> tags, DeploymentArtifact[] artifacts);
+
+    /**
+     * Gets all available deployment versions for this target. If none can be
+     * found, an empty list will be returned.
+     * @param targetID The target to be used.
+     * @return A list of <code>DeploymentVersionObject</code>s which are related to
+     * this target, sorted lexically by version.
+     */
+    public List<DeploymentVersionObject> getDeploymentVersions(String targetID);
+
+    /**
+     * Get the most recent known deployment version for a given target.
+     * @param targetID The target to be used.
+     * @return A <code>DeploymentVersionObject</code> which is the most recent one to be deployed
+     * to the target. If none can be found, <code>null</code> will be returned.
+     */
+    public DeploymentVersionObject getMostRecentDeploymentVersion(String targetID);
+
+    /**
+     * Creates a DeploymentArtifact object.
+     * @param url The url to be used in this object.
+     * @param directives A map of directives to be packed into the object.
+     * @return The newly created deployment artifact object.
+     */
+    public DeploymentArtifact createDeploymentArtifact(String url, Map<String, String> directives);
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Distribution2TargetAssociationRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Distribution2TargetAssociationRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Distribution2TargetAssociationRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Distribution2TargetAssociationRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,38 @@
+/*
+ * 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.TargetObject;
+import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
+import org.apache.ace.client.repository.object.DistributionObject;
+
+/**
+ * Interface to a Distribution2TargetAssociationRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface Distribution2TargetAssociationRepository extends AssociationRepository<DistributionObject, TargetObject, Distribution2TargetAssociation> {
+    /**
+     * Creates an assocation from a given distribution to multiple targets, which correspond to the given
+     * filter string. For parameters to use in the filter, see <code>TargetObject</code>'s <code>KEY_</code> constants.
+     * @param distribution A distribution object for the left side of this association.
+     * @param targetFilter An LDAP-filter for the targets to use.
+     * @return The newly created association.
+     */
+    public Distribution2TargetAssociation createDistribution2TargetFilter(DistributionObject distribution, String targetFilter);
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DistributionRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DistributionRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DistributionRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/DistributionRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,28 @@
+/*
+ * 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.ObjectRepository;
+import org.apache.ace.client.repository.object.DistributionObject;
+
+/**
+ * Interface to a DistributionRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface DistributionRepository extends ObjectRepository<DistributionObject> {
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Feature2DistributionAssociationRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Feature2DistributionAssociationRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Feature2DistributionAssociationRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/Feature2DistributionAssociationRepository.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.Feature2DistributionAssociation;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+
+/**
+ * Interface to a Feature2DistributionAssociationRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface Feature2DistributionAssociationRepository extends AssociationRepository<FeatureObject, DistributionObject, Feature2DistributionAssociation> {
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/FeatureRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/FeatureRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/FeatureRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/FeatureRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,28 @@
+/*
+ * 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.ObjectRepository;
+import org.apache.ace.client.repository.object.FeatureObject;
+
+/**
+ * Interface to a FeatureRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface FeatureRepository extends ObjectRepository<FeatureObject> {
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/TargetRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/TargetRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/TargetRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/TargetRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,28 @@
+/*
+ * 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.ObjectRepository;
+import org.apache.ace.client.repository.object.TargetObject;
+
+/**
+ * Interface to a GatewayRepository. The functionality is defined by the generic AssociationRepository.
+ */
+public interface TargetRepository extends ObjectRepository<TargetObject>{
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/packageinfo?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/repository/packageinfo (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/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/stateful/StatefulTargetObject.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetObject.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetObject.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetObject.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,207 @@
+/*
+ * 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.stateful;
+
+import java.util.List;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.DeploymentArtifact;
+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 org.apache.ace.log.LogEvent;
+
+/**
+ * Represents the information that a <code>TargetObject</code>
+ * has, plus added functionality for gathering information from a deployment repository and,
+ * optionally, from an AuditLog.
+ */
+public interface StatefulTargetObject extends RepositoryObject {
+
+    public static final String TOPIC_ADDED = StatefulTargetObject.class.getName().replace('.', '/') + "/ADDED";
+    public static final String TOPIC_REMOVED = StatefulTargetObject.class.getName().replace('.', '/') + "/REMOVED";
+    public static final String TOPIC_CHANGED = StatefulTargetObject.class.getName().replace('.', '/') + "/CHANGED";
+    public static final String TOPIC_STATUS_CHANGED = StatefulTargetObject.class.getName().replace('.', '/') + "/STATUS_CHANGED";
+    /** Indicates a change to the audit events for the StatefulTargetObject in "entity".*/
+    public static final String TOPIC_AUDITEVENTS_CHANGED = StatefulTargetObject.class.getName().replace('.', '/') + "/AUDITEVENTS_CHANGED";
+    /** Key used in the event with topic <code>TOPIC_AUDITEVENTS_CHANGED</code>. Contains a List<LogDescriptor> containing all
+     *  events we have not seen yet. NOTE: The first auditevent "change" causing the <code>StatefulTargetObject</code> to
+     *  be instantiated will trigger a <code>TOPIC_AUDITEVENTS_CHANGED</code> event *before* a <code>TOPIC_ADDED</code> event. */
+    public static final String KEY_AUDITEVENTS = "auditevents";
+    public static final String TOPIC_ALL = StatefulTargetObject.class.getName().replace('.', '/') + "/*";
+
+    public final static String KEY_ID = TargetObject.KEY_ID;
+    public final static String KEY_REGISTRATION_STATE = "KEY_REGISTRATION_STATE";
+    public final static String KEY_STORE_STATE = "KEY_STORE_STATE";
+    public final static String KEY_PROVISIONING_STATE = "KEY_PROVISIONING_STATE";
+    public final static String KEY_LAST_INSTALL_VERSION = "KEY_LAST_INSTALL_VERSION";
+    public final static String KEY_LAST_INSTALL_SUCCESS = "KEY_LAST_INSTALL_SUCCESS";
+    public final static String KEY_ACKNOWLEDGED_INSTALL_VERSION = "KEY_ACKNOWLEDGED_INSTALL_VERSION";
+    public final static String[] KEYS_ALL = new String[] {KEY_ID, KEY_REGISTRATION_STATE, KEY_STORE_STATE, KEY_PROVISIONING_STATE, KEY_LAST_INSTALL_VERSION, KEY_LAST_INSTALL_SUCCESS, KEY_ACKNOWLEDGED_INSTALL_VERSION};
+
+    /**
+     * Represents a current deployment package version which cannot be found, i.e.,
+     * either there is no information about deployment packages in the AuditLog,
+     * or no AuditLog is available.
+     */
+    public final static String UNKNOWN_VERSION = "(unknown)";
+
+    /**
+     * Gets the current registration status of the target.
+     */
+    public RegistrationState getRegistrationState();
+
+    /**
+     * Gets the current store status of the target.
+     */
+    public StoreState getStoreState();
+
+    /**
+     * Gets the current provisioning status of the target.
+     */
+    public ProvisioningState getProvisioningState();
+
+    /**
+     * Gets the most recent deployment package version on the target, according
+     * to the deployment repository. If no version can be determined,
+     * <code>UNKNOWN_VERSION</code> will be returned.
+     */
+    public String getCurrentVersion();
+
+    /**
+     * Gets the list of AuditLog Events for this target. If no auditlog events
+     * can be found, and empty list will be returned. The events are ordered ascending by timestamp.
+     */
+    public List<LogEvent> getAuditEvents();
+
+    /**
+     * Registers this target, which for now only exists in the AuditLog, into the
+     * <code>TargetRepository</code>.
+     * @throws IllegalStateException when the precondition is not met, i.e., the
+     * target is not known only in the AuditLog, but also in the <code>TargetRepository</code>.
+     */
+    public void register() throws IllegalStateException;
+
+    /**
+     * Indicates whether this <code>StatefulTargetObject</code> is backed by a <code>TargetObject</code>.
+     * @return whether this <code>StatefulTargetObject</code> is backed by a <code>TargetObject</code>.
+     */
+    public boolean isRegistered();
+
+    /**
+     * Approves all differences between what is currently in the shop and target operator
+     * repository, and the deployment repository. This will generate a new version in the
+     * deployment repository.
+     * @return The number of the new version.
+     * @throws IllegalStateException when it is currently not possible to create a deployment version.
+     */
+    public String approve();
+
+    /**
+     * Indicates whether an <code>approve()</code> is necessary, i.e., there is a difference between
+     * the set of artifacts for this target according to the shop, and according to the deployment
+     * repository.
+     * @return <code>true</code> if there is a difference between the shop and deployment repository;
+     * <code>false</code> otherwise.
+     */
+    public boolean needsApprove();
+
+    /**
+     * Returns the auto-approval flag for this target.
+     * @return <code>true</code> if auto approve has been set;
+     * <code>false</code> otherwise.
+     */
+    public boolean getAutoApprove();
+
+    /**
+     * Set the auto approve value for this target, the property is stored within the target
+     * @param approve <code>true</code> to enable auto approve;
+     * <code>false</code> otherwise.
+     */
+    public void setAutoApprove(boolean approve);
+
+    /**
+     * Gets the list of artifact objects that should be on the target, according to the shop.
+     * @return the list of artifact objects that should be on the target, according to the shop, can only be <code>null</code> in case something went wrong gathering artifacts.
+     */
+    public ArtifactObject[] getArtifactsFromShop();
+
+    /**
+     * Gets the list of deployment artifacts that should be on the target, according to the deployment repository.
+     * @return the list of artifact objects that should be on the target, according to the deployment repository.
+     */
+    public DeploymentArtifact[] getArtifactsFromDeployment();
+
+    /**
+     * Returns the latest available installed version in the auditlog.
+     * @return The latest version statement from the auditlog for an install if
+     * one is available; otherwise, an empty string.
+     */
+    public String getLastInstallVersion();
+
+    /**
+     * Returns whether the last install on the target was successful.
+     * @return <code>true</code> if there information about a last install and
+     * that was successful, <code>false</code> otherwise.
+     */
+    public boolean getLastInstallSuccess();
+
+    /**
+     * Signals to the object that the outcome of a given install on the target
+     * is 'seen', and that the <code>ProvisioningState</code> can now return to <code>Idle</code>.
+     * @param version A string representing a version.
+     */
+    public void acknowledgeInstallVersion(String version);
+
+    /**
+     * Gets the underlying <code>TargetObject</code> of this <code>StatefulTargetObject</code>.
+     * @return The <code>TargetObject</code> linked to this <code>StatefulTargetObject</code>; if none
+     * is available, an <code>IllegalStateException</code> will be thrown.
+     */
+    public TargetObject getTargetObject();
+
+    /**
+     * 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();
+
+    public enum RegistrationState {
+        Unregistered, Registered;
+    }
+
+    public enum StoreState {
+        New, Unapproved, Approved;
+    }
+
+    public enum ProvisioningState {
+        Idle, InProgress, OK, Failed;
+    }
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetRepository.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetRepository.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetRepository.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/StatefulTargetRepository.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,55 @@
+/*
+ * 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.stateful;
+
+import java.util.Map;
+
+import org.apache.ace.client.repository.ObjectRepository;
+
+/**
+ * Represents a repository of <ode>StatefulTargetObject</code>'s.
+ */
+public interface StatefulTargetRepository extends ObjectRepository<StatefulTargetObject> {
+
+    /**
+     * Registers a target with given attributes. This will result in the creation
+     * of a <code>TargetObject</code> in the <code>TargetRepository</code>, and
+     * the creation of a <code>StatefulTargetObject</code>, which will also be
+     * returned.
+     * @param attributes The attributes to create the <code>TargetObject</code> with.
+     * @return The newly registered target object.
+     */
+    public StatefulTargetObject preregister(Map<String, String> attributes, Map<String, String> tags);
+
+    /**
+     * Unregisters a target, removing it from the <code>TargetRepository</code>. Note
+     * that a <code>StatefulTargetObject</code> might stay around if it is backed
+     * by audit log entries. If the given ID is not that of an existing <code>TargetObject</code>,
+     * an <code>IllegalArgumentException</code> will be thrown.
+     * @param targetID A string representing a target ID.
+     */
+    public void unregister(String targetID);
+
+    /**
+     * Explicitly instruct the <code>StatefulTargetRepository</code> to update
+     * its contents; for instance, after syncing the audit log.
+     */
+    public void refresh();
+
+}

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/LogEventComparator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/LogEventComparator.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/LogEventComparator.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/LogEventComparator.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,44 @@
+/*
+ * 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.stateful.impl;
+
+import java.util.Comparator;
+
+import org.apache.ace.log.LogEvent;
+
+public final class LogEventComparator implements Comparator<LogEvent> {
+	public int compare(LogEvent left, LogEvent right) {
+        if (left.getLogID() == right.getLogID()) {
+            return sgn(left.getTime() - right.getTime());
+        }
+        else {
+            return sgn(left.getLogID() - right.getLogID());
+        }
+    }
+
+	public int sgn(long number) {
+    	if (number < 0) {
+    		return -1;
+    	}
+    	else if (number > 0) {
+    		return 1;
+    	}
+    	return 0;
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/StatefulTargetObjectImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/StatefulTargetObjectImpl.java?rev=1463576&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/StatefulTargetObjectImpl.java (added)
+++ ace/trunk/org.apache.ace.client.repository/src/org/apache/ace/client/repository/stateful/impl/StatefulTargetObjectImpl.java Tue Apr  2 14:53:33 2013
@@ -0,0 +1,717 @@
+/*
+ * 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.stateful.impl;
+
+import java.io.IOException;
+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.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+import org.apache.ace.client.repository.Associatable;
+import org.apache.ace.client.repository.Association;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.DeploymentArtifact;
+import org.apache.ace.client.repository.object.DeploymentVersionObject;
+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 org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.log.AuditEvent;
+import org.apache.ace.log.LogDescriptor;
+import org.apache.ace.log.LogEvent;
+
+/**
+ * A <code>StatefulTargetObjectImpl</code> uses the interface of a <code>StatefulTargetObject</code>,
+ * but delegates most of its calls to either an embedded <code>TargetObject</code>, or to its
+ * parent <code>StatefulTargetRepository</code>. Once created, it will handle its own lifecycle
+ * and remove itself once is existence is no longer necessary.
+ */
+public class StatefulTargetObjectImpl implements StatefulTargetObject {
+    private final StatefulTargetRepositoryImpl m_repository;
+    private final Object m_lock = new Object();
+    private TargetObject m_targetObject;
+    private List<LogDescriptor> m_processedAuditEvents = new ArrayList<LogDescriptor>();
+    private Map<String, String> m_attributes = new HashMap<String, String>();
+    /** This boolean is used to suppress STATUS_CHANGED events during the creation of the object.*/
+    private boolean m_inConstructor = true;
+
+    /**
+     * Creates a new <code>StatefulTargetObjectImpl</code>. After creation, it will have the
+     * most recent data available, and has verified its own reasons for existence.
+     * @param repository The parent repository of this object.
+     * @param targetID A string representing a target ID.
+     */
+    StatefulTargetObjectImpl(StatefulTargetRepositoryImpl repository, String targetID) {
+        m_repository = repository;
+        addStatusAttribute(KEY_ID, targetID);
+        updateTargetObject(false);
+        updateAuditEvents(false);
+        updateDeploymentVersions(null);
+        verifyExistence();
+        m_inConstructor = false;
+    }
+
+    public String approve() throws IllegalStateException {
+        try {
+            String version = m_repository.approve(getID());
+            setStoreState(StoreState.Approved);
+            return version;
+        }
+        catch (IOException e) {
+            throw new IllegalStateException("Problem generating new deployment version: " + e.getMessage(), e);
+        }
+    }
+
+    public List<LogEvent> getAuditEvents() {
+        return m_repository.getAuditEvents(getID());
+    }
+
+    public String getCurrentVersion() {
+        DeploymentVersionObject version = m_repository.getMostRecentDeploymentVersion(getID());
+        if (version == null) {
+            return StatefulTargetObject.UNKNOWN_VERSION;
+        }
+        else {
+            return version.getVersion();
+        }
+    }
+
+    public void register() throws IllegalStateException {
+        m_repository.register(getID());
+    }
+
+    public boolean isRegistered() {
+        synchronized(m_lock) {
+            return (m_targetObject != null);
+        }
+    }
+
+    public TargetObject getTargetObject() {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject;
+        }
+    }
+
+    public DeploymentArtifact[] getArtifactsFromDeployment() {
+        synchronized(m_lock) {
+            DeploymentVersionObject mostRecentDeploymentVersion = m_repository.getMostRecentDeploymentVersion(getID());
+            if (mostRecentDeploymentVersion != null) {
+                return mostRecentDeploymentVersion.getDeploymentArtifacts();
+            }
+            return new DeploymentArtifact[0];
+        }
+    }
+
+    public ArtifactObject[] getArtifactsFromShop() {
+        return m_repository.getNecessaryArtifacts(getID());
+    }
+
+    public boolean getLastInstallSuccess() {
+        synchronized(m_lock) {
+            return Boolean.parseBoolean(getStatusAttribute(KEY_LAST_INSTALL_SUCCESS));
+        }
+    }
+
+    public String getLastInstallVersion() {
+        synchronized(m_lock) {
+            return getStatusAttribute(KEY_LAST_INSTALL_VERSION);
+        }
+    }
+
+    public void acknowledgeInstallVersion(String version) {
+        synchronized(m_lock) {
+            addStatusAttribute(KEY_ACKNOWLEDGED_INSTALL_VERSION, version);
+            if (version.equals(getStatusAttribute(KEY_LAST_INSTALL_VERSION))) {
+                setProvisioningState(ProvisioningState.Idle);
+            }
+        }
+    }
+
+    public boolean needsApprove() {
+        return getStoreState() == StoreState.Unapproved;
+    }
+
+    public ProvisioningState getProvisioningState() {
+        return ProvisioningState.valueOf(getStatusAttribute(KEY_PROVISIONING_STATE));
+    }
+
+    public RegistrationState getRegistrationState() {
+        return RegistrationState.valueOf(getStatusAttribute(KEY_REGISTRATION_STATE));
+    }
+
+    public StoreState getStoreState() {
+        String statusAttribute = getStatusAttribute(KEY_STORE_STATE);
+        if (statusAttribute != null) {
+            return StoreState.valueOf(statusAttribute);
+        }
+        return StoreState.New;
+    }
+
+    /**
+     * Signals this object that there has been a change to the <code>TargetObject</code> it represents.
+     * @param needsVerify States whether this update should make the object check for its
+     * reasons for existence.
+     */
+    void updateTargetObject(boolean needsVerify) {
+        synchronized(m_lock) {
+            m_targetObject = m_repository.getTargetObject(getID());
+            determineRegistrationState();
+            if (needsVerify) {
+                verifyExistence();
+            }
+        }
+    }
+
+    /**
+     * Signals this object that there has been a change to the auditlog which may interest
+     * this object.
+     * @param needsVerify States whether this update should make the object check for its
+     * reasons for existence.
+     */
+    void updateAuditEvents(boolean needsVerify) {
+        synchronized(m_lock) {
+            determineProvisioningState();
+            if (needsVerify) {
+                verifyExistence();
+            }
+        }
+    }
+
+    /**
+     * Signals this object that a new deployment version has been created in relation
+     * to the targetID this object manages.
+     */
+    void updateDeploymentVersions(DeploymentVersionObject deploymentVersionObject) {
+        synchronized(m_lock) {
+            determineProvisioningState();
+            determineStoreState(deploymentVersionObject);
+        }
+    }
+
+    /**
+     * Based on the information about a <code>TargetObject</code>, the
+     * <code>AuditEvent</code>s available, and the deployment information that
+     * the parent repository can give, determines the status of this target.
+     */
+    void determineStatus() {
+        determineRegistrationState();
+        determineProvisioningState();
+        determineStoreState(null);
+        verifyExistence();
+    }
+
+    private void determineRegistrationState() {
+        synchronized(m_lock) {
+            if (!isRegistered()) {
+                setRegistrationState(RegistrationState.Unregistered);
+            }
+            else {
+                setRegistrationState(RegistrationState.Registered);
+            }
+        }
+    }
+
+    private void determineStoreState(DeploymentVersionObject deploymentVersionObject) {
+        synchronized(m_lock) {
+            List<String> fromShop = new ArrayList<String>();
+            ArtifactObject[] artifactsFromShop = m_repository.getNecessaryArtifacts(getID());
+            DeploymentVersionObject mostRecentVersion;
+            if (deploymentVersionObject == null) {
+                mostRecentVersion = m_repository.getMostRecentDeploymentVersion(getID());
+            }
+            else {
+                mostRecentVersion = deploymentVersionObject;
+            }
+            if (artifactsFromShop == null) {
+                if (mostRecentVersion == null) {
+                    setStoreState(StoreState.New);
+                }
+                else {
+                    setStoreState(StoreState.Unapproved);
+                }
+                return;
+            }
+
+            for (ArtifactObject ao : artifactsFromShop) {
+                fromShop.add(ao.getURL());
+            }
+
+            List<String> fromDeployment = new ArrayList<String>();
+            for (DeploymentArtifact da : getArtifactsFromDeployment()) {
+                fromDeployment.add(da.getDirective(DeploymentArtifact.DIRECTIVE_KEY_BASEURL));
+            }
+
+            if ((mostRecentVersion == null) && fromShop.isEmpty()) {
+                setStoreState(StoreState.New);
+            }
+            else if (fromShop.containsAll(fromDeployment) && fromDeployment.containsAll(fromShop)) {
+                // great, we have the same artifacts. But... do they need to be reprocessed?
+                for (ArtifactObject ao : artifactsFromShop) {
+                    if (m_repository.needsNewVersion(ao, getID(), mostRecentVersion.getVersion())) {
+                        setStoreState(StoreState.Unapproved);
+                        return;
+                    }
+                }
+                setStoreState(StoreState.Approved);
+            }
+            else {
+                setStoreState(StoreState.Unapproved);
+            }
+        }
+    }
+
+    private void determineProvisioningState() {
+        /*
+         * This method gets all audit events it has not yet seen, and goes through them, backward
+         * in time, to find either and INSTALL or a COMPLETE event. A INSTALL event gives us a version,
+         * and tells us we're in InProgress. A COMPLETE tells gives us a version, and a success. The success
+         * will be stored, and also sets the state to OK or Failed, unless the version we found has already been
+         * acknowledged, the the state is set to Idle. Also, if there is no information whatsoever, we assume Idle.
+         */
+        synchronized(m_lock) {
+            List<LogDescriptor> allDescriptors = m_repository.getAllDescriptors(getID());
+            List<LogDescriptor> newDescriptors = m_repository.diffLogDescriptorLists(allDescriptors, m_processedAuditEvents);
+
+            List<LogEvent> newEvents = m_repository.getAuditEvents(newDescriptors);
+            for (int position = newEvents.size() - 1; position >= 0; position--) {
+                LogEvent event = newEvents.get(position);
+                
+                // TODO we need to check here if the deployment package is actually the right one
+                
+                String currentVersion = (String) event.getProperties().get(AuditEvent.KEY_VERSION);
+                if (event.getType() == AuditEvent.DEPLOYMENTCONTROL_INSTALL) {
+                    addStatusAttribute(KEY_LAST_INSTALL_VERSION, currentVersion);
+                    setProvisioningState(ProvisioningState.InProgress);
+                    sendNewAuditlog(newDescriptors);
+                    m_processedAuditEvents = allDescriptors;
+                    return;
+                }
+                else if (event.getType() == AuditEvent.DEPLOYMENTADMIN_COMPLETE) {
+                    addStatusAttribute(KEY_LAST_INSTALL_VERSION, currentVersion);
+                    if ((currentVersion != null) && currentVersion.equals(getStatusAttribute(KEY_ACKNOWLEDGED_INSTALL_VERSION))) {
+                        setProvisioningState(ProvisioningState.Idle);
+                        sendNewAuditlog(newDescriptors);
+                        m_processedAuditEvents = allDescriptors;
+                        return;
+                    }
+                    else {
+                        String value = (String) event.getProperties().get(AuditEvent.KEY_SUCCESS);
+                        addStatusAttribute(KEY_LAST_INSTALL_SUCCESS, value);
+                        if (Boolean.parseBoolean(value)) {
+                            setProvisioningState(ProvisioningState.OK);
+                            sendNewAuditlog(newDescriptors);
+                            m_processedAuditEvents = allDescriptors;
+                            return;
+                        }
+                        else {
+                            setProvisioningState(ProvisioningState.Failed);
+                            sendNewAuditlog(newDescriptors);
+                            m_processedAuditEvents = allDescriptors;
+                            return;
+                        }
+                    }
+                }
+            }
+
+            if (m_processedAuditEvents.isEmpty()) {
+                setProvisioningState(ProvisioningState.Idle);
+            }
+            sendNewAuditlog(newDescriptors);
+            m_processedAuditEvents = allDescriptors;
+        }
+    }
+
+    private void sendNewAuditlog(List<LogDescriptor> events) {
+        // Check whether there are actually events in the list.
+        boolean containsData = false;
+        for (LogDescriptor l : events) {
+            containsData |= (l.getRangeSet().getHigh() != 0);
+        }
+
+        if (containsData) {
+            Properties props = new Properties();
+            props.put(StatefulTargetObject.KEY_AUDITEVENTS, events);
+            m_repository.notifyChanged(this, TOPIC_AUDITEVENTS_CHANGED, props);
+        }
+    }
+
+    private void setRegistrationState(RegistrationState state) {
+        setStatus(KEY_REGISTRATION_STATE, state.toString());
+    }
+
+    private void setStoreState(StoreState state) {
+        setStatus(KEY_STORE_STATE, state.toString());
+    }
+
+    private void setProvisioningState(ProvisioningState state) {
+        setStatus(KEY_PROVISIONING_STATE, state.toString());
+    }
+
+    private void setStatus(String key, String status) {
+        if (!status.equals(getStatusAttribute(key))) {
+            addStatusAttribute(key, status);
+            handleStatechangeAutomation();
+            if (!m_inConstructor) {
+                m_repository.notifyChanged(this, TOPIC_STATUS_CHANGED);
+            }
+        }
+    }
+
+    private void handleStatechangeAutomation() {
+        if (getStoreState().equals(StoreState.Unapproved) && isRegistered() && getAutoApprove()) {
+            approve();
+        }
+    }
+
+    /**
+     * Verifies that this object should still be around. If the target is represents
+     * shows up in at least the target repository or the auditlog, it has a reason
+     * to exists; if not, it doesn't. When it is no longer necessary, it will remove itself
+     * from the parent repository.
+     * @return Whether or not this object should still exist.
+     */
+    boolean verifyExistence() {
+        synchronized(m_lock) {
+            if ((m_targetObject == null) && ((m_processedAuditEvents == null) || m_processedAuditEvents.isEmpty())) {
+                m_repository.removeStateful(this);
+                return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Helper method for the delegate methods below: most of these delegate their calls to a
+     * <code>TargetObject</code>, but in order to do so, one must be present.
+     */
+    private void ensureTargetPresent() {
+        if ((m_targetObject == null)) {
+            throw new IllegalStateException("This StatefulTargetObject is not backed by a TargetObject.");
+            // NOTE: we do not check the isDeleted state; the TargetObject itself will notify the user of this.
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if ((o == null) || !(o instanceof StatefulTargetObject)) {
+            return false;
+        }
+        return getID().equals(((StatefulTargetObject) o).getID());
+    }
+
+    private void addStatusAttribute(String key, String value) {
+        m_attributes.put(key, value);
+    }
+
+    private String getStatusAttribute(String key) {
+        return m_attributes.get(key);
+    }
+
+    /* ******************
+     * Delegates to TargetObject
+     */
+
+    public String getID() {
+        return getStatusAttribute(KEY_ID);
+    }
+
+    public boolean isDeleted() {
+        return !verifyExistence();
+    }
+
+    public List<Distribution2TargetAssociation> getAssociationsWith(DistributionObject distribution) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.getAssociationsWith(distribution);
+        }
+    }
+
+    public List<DistributionObject> getDistributions() {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.getDistributions();
+        }
+    }
+
+    public String addAttribute(String key, String value) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.addAttribute(key, value);
+        }
+    }
+
+    public String addTag(String key, String value) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.addTag(key, value);
+        }
+    }
+
+    public String getAttribute(String key) {
+        // retrieve from both
+        synchronized(m_lock) {
+            if (Arrays.binarySearch(KEYS_ALL, key) >= 0) {
+                return getStatusAttribute(key);
+            }
+            ensureTargetPresent();
+            return m_targetObject.getAttribute(key);
+        }
+    }
+
+    public Enumeration<String> getAttributeKeys() {
+        synchronized(m_lock) {
+            List<String> statusKeys = new ArrayList<String>();
+            for (String s : KEYS_ALL) {
+                statusKeys.add(s);
+            }
+            Enumeration<String> attributeKeys = null;
+            if (m_targetObject != null) {
+                attributeKeys = m_targetObject.getAttributeKeys();
+            }
+            return new ExtendedEnumeration<String>(attributeKeys, statusKeys, true);
+        }
+    }
+
+    public Dictionary<String, Object> getDictionary() {
+        // build our own dictionary
+        synchronized(m_lock) {
+            return new StatefulTargetObjectDictionary();
+        }
+    }
+
+    public String getTag(String key) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.getTag(key);
+        }
+    }
+
+    public Enumeration<String> getTagKeys() {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.getTagKeys();
+        }
+    }
+
+    public boolean getAutoApprove() {
+        synchronized(m_lock) {
+            if (m_targetObject != null) {
+                return m_targetObject.getAutoApprove();
+            }
+            else {
+                return false;
+            }
+
+        }
+    }
+
+    public void setAutoApprove(boolean approve) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            m_targetObject.setAutoApprove(approve);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends Associatable> void add(Association association, Class<T> clazz) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            m_targetObject.add(association, clazz);
+        }
+    }
+
+    public <T extends Associatable> List<T> getAssociations(Class<T> clazz) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.getAssociations(clazz);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends Associatable, A extends Association> List<A> getAssociationsWith(Associatable other, Class<T> clazz, Class<A> associationType) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.getAssociationsWith(other, clazz, associationType);
+        }
+    }
+
+    public <T extends Associatable> boolean isAssociated(Object obj, Class<T> clazz) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            return m_targetObject.isAssociated(obj, clazz);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends Associatable> void remove(Association association, Class<T> clazz) {
+        synchronized(m_lock) {
+            ensureTargetPresent();
+            m_targetObject.remove(association, clazz);
+        }
+    }
+
+    public String getDefinition() {
+        return "target-" + KEY_ID + "-" + getID();
+    }
+
+    private class ExtendedEnumeration<T> implements Enumeration<T> {
+        private Enumeration<T> m_source;
+        private List<T> m_extra;
+        private final boolean m_allowDuplicates;
+
+        ExtendedEnumeration(Enumeration<T> source, List<T> extra, boolean allowDuplicates) {
+            m_source = source;
+            m_extra = extra;
+            m_allowDuplicates = allowDuplicates;
+        }
+
+        public boolean hasMoreElements() {
+            boolean inSource = (m_source != null);
+            boolean inExtra = false;
+            if (m_extra != null) {
+                inExtra = !m_extra.isEmpty();
+            }
+            return inSource || inExtra;
+        }
+
+        public T nextElement() {
+            if (m_source != null) {
+                T result = m_source.nextElement();
+                if (!m_source.hasMoreElements()) {
+                    m_source = null;
+                }
+                if (!m_allowDuplicates) {
+                    m_extra.remove(result);
+                }
+                return result;
+            }
+            else if (!m_extra.isEmpty()) {
+                return m_extra.remove(0);
+            }
+            throw new NoSuchElementException();
+        }
+    }
+
+    private class StatefulTargetObjectDictionary extends Dictionary<String, Object> {
+        private final Dictionary<String, Object> m_dict;
+
+        StatefulTargetObjectDictionary() {
+            if (m_targetObject != null) {
+                m_dict = m_targetObject.getDictionary();
+            }
+            else {
+                m_dict = null;
+            }
+        }
+
+        @Override
+        public Enumeration<Object> elements() {
+            List<Object> statusVals = new ArrayList<Object>();
+            for (String key : KEYS_ALL) {
+                statusVals.add(getStatusAttribute(key));
+            }
+            Enumeration<Object> attributeVals = null;
+            if (m_dict != null) {
+                attributeVals = m_dict.elements();
+            }
+            return new ExtendedEnumeration<Object>(attributeVals, statusVals, true);
+        }
+
+        @Override
+        public Object get(Object key) {
+            for (String s : KEYS_ALL) {
+                if (s.equals(key)) {
+                    return getStatusAttribute((String) key);
+                }
+            }
+            String tag = m_targetObject.getTag((String)key);
+            String attr = m_targetObject.getAttribute((String)key);
+            if (tag == null) {
+                return attr;
+            }
+            else if (attr == null) {
+                return tag;
+            }
+            else {
+                return new String[] {attr, tag};
+            }
+        }
+
+        @Override
+        public boolean isEmpty() {
+            // This is always false, since we always have the status attributes.
+            return false;
+        }
+
+        @Override
+        public Enumeration<String> keys() {
+            List<String> statusKeys = new ArrayList<String>();
+            for (String key : KEYS_ALL) {
+                statusKeys.add(key);
+            }
+            Enumeration<String> attributeKeys = null;
+            if (m_dict != null) {
+                attributeKeys = m_dict.keys();
+            }
+            return new ExtendedEnumeration<String>(attributeKeys, statusKeys, false);
+        }
+
+        @Override
+        public Object put(String key, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Object remove(Object key) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            int result = 0;
+            Enumeration<String> keys = keys();
+            while (keys.hasMoreElements()) {
+                result++;
+                keys.nextElement();
+            }
+            return result;
+        }
+    }
+
+    public String getAssociationFilter(Map<String, String> properties) {
+        throw new UnsupportedOperationException("A StatefulTargetObject cannot return a filter; use the underlying TargetObject instead.");
+    }
+
+    public int getCardinality(Map<String, String> properties) {
+        throw new UnsupportedOperationException("A StatefulTargetObject cannot return a cardinality; use the underlying TargetObject instead.");
+    }
+
+    @SuppressWarnings("unchecked")
+    public Comparator getComparator() {
+        throw new UnsupportedOperationException("A StatefulTargetObject cannot return a comparator; use the underlying TargetObject instead.");
+    }
+}
\ No newline at end of file