ace-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j...@apache.org
Subject svn commit: r1360600 - in /ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin: BaseRepositoryAdminTest.java ClientAutomationTest.java StatefulTargetRepositoryTest.java TemplateProcessorTest.java
Date Thu, 12 Jul 2012 09:34:43 GMT
Author: jawi
Date: Thu Jul 12 09:34:42 2012
New Revision: 1360600

URL: http://svn.apache.org/viewvc?rev=1360600&view=rev
Log:
Fixed the repository admin itest; original test had some timing problems, which should be fixed by now.

Added:
    ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java   (with props)
    ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java   (with props)
    ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/StatefulTargetRepositoryTest.java   (with props)
    ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/TemplateProcessorTest.java   (with props)

Added: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java?rev=1360600&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java Thu Jul 12 09:34:42 2012
@@ -0,0 +1,419 @@
+/*
+ * 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.it.repositoryadmin;
+
+import static org.apache.ace.client.repository.RepositoryObject.PRIVATE_TOPIC_ROOT;
+import static org.apache.ace.client.repository.RepositoryObject.PUBLIC_TOPIC_ROOT;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_ALL;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.ace.client.repository.ObjectRepository;
+import org.apache.ace.client.repository.RepositoryAdmin;
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.SessionFactory;
+import org.apache.ace.client.repository.helper.bundle.BundleHelper;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.repository.Artifact2FeatureAssociationRepository;
+import org.apache.ace.client.repository.repository.ArtifactRepository;
+import org.apache.ace.client.repository.repository.DeploymentVersionRepository;
+import org.apache.ace.client.repository.repository.Distribution2TargetAssociationRepository;
+import org.apache.ace.client.repository.repository.DistributionRepository;
+import org.apache.ace.client.repository.repository.Feature2DistributionAssociationRepository;
+import org.apache.ace.client.repository.repository.FeatureRepository;
+import org.apache.ace.client.repository.repository.TargetRepository;
+import org.apache.ace.client.repository.stateful.StatefulTargetRepository;
+import org.apache.ace.http.listener.constants.HttpConstants;
+import org.apache.ace.it.IntegrationTestBase;
+import org.apache.ace.obr.storage.file.constants.OBRFileStoreConstants;
+import org.apache.ace.repository.Repository;
+import org.apache.ace.repository.impl.constants.RepositoryConstants;
+import org.apache.ace.server.log.store.LogStore;
+import org.apache.ace.test.constants.TestConstants;
+import org.apache.felix.dm.Component;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.useradmin.User;
+import org.osgi.util.tracker.ServiceTracker;
+
+public abstract class BaseRepositoryAdminTest extends IntegrationTestBase implements EventHandler {
+
+	final class MockUser implements User {
+        private final String m_name;
+
+        public MockUser() {
+            m_name = String.format("user-%s", Long.toHexString(System.nanoTime()));
+        }
+
+        public Dictionary getCredentials() {
+            return new Properties();
+        }
+
+        public String getName() {
+            return m_name;
+        }
+
+        public Dictionary getProperties() {
+            return new Properties();
+        }
+
+        public int getType() {
+            return 0;
+        }
+
+        public boolean hasCredential(String arg0, Object arg1) {
+            return false;
+        }
+    }
+
+    protected static final String ENDPOINT_NAME = "/AdminRepTest";
+    protected static final String HOST = "http://localhost:" + TestConstants.PORT;
+
+    protected volatile ConfigurationAdmin m_configAdmin; /* Injected by dependency manager */
+    protected volatile RepositoryAdmin m_repositoryAdmin; /* Injected by dependency manager */
+    protected volatile ArtifactRepository m_artifactRepository; /* Injected by dependency manager */
+    protected volatile Artifact2FeatureAssociationRepository m_artifact2featureRepository; /* Injected by dependency manager */
+    protected volatile FeatureRepository m_featureRepository; /* Injected by dependency manager */
+    protected volatile Feature2DistributionAssociationRepository m_feature2distributionRepository; /* Injected by dependency manager */
+    protected volatile DistributionRepository m_distributionRepository; /* Injected by dependency manager */
+    protected volatile Distribution2TargetAssociationRepository m_distribution2targetRepository; /* Injected by dependency manager */
+    protected volatile TargetRepository m_targetRepository; /* Injected by dependency manager */
+    protected volatile DeploymentVersionRepository m_deploymentVersionRepository; /* Injected by dependency manager */
+    protected volatile StatefulTargetRepository m_statefulTargetRepository; /* Injected by dependency manager */
+    protected volatile LogStore m_auditLogStore; /* Injected by dependency manager */
+    protected volatile List<String> m_waitingForTopic = Collections.synchronizedList(new ArrayList<String>());
+    protected volatile Semaphore m_semaphore;
+    
+    protected URL m_endpoint;
+    
+    private volatile boolean m_runAndWaitDebug = false;
+
+    public void handleEvent(Event event) {
+        if (m_runAndWaitDebug) {
+            System.err.println("Received event: " + event.getTopic());
+        }
+        if (m_waitingForTopic.remove(event.getTopic())) {
+            if (m_runAndWaitDebug) {
+                System.err.println("Event was expected.");
+            }
+            if ((m_semaphore != null) && m_waitingForTopic.isEmpty()) {
+                m_semaphore.release();
+                m_runAndWaitDebug = false;
+            }
+        }
+    }
+    
+    protected final void addObr(String endpoint, String fileLocation) throws IOException, InterruptedException {
+        configure("org.apache.ace.obr.servlet", "OBRInstance", "singleOBRServlet", "org.apache.ace.server.servlet.endpoint", endpoint, "authentication.enabled", "false");
+        configure("org.apache.ace.obr.storage.file", "OBRInstance", "singleOBRStore", OBRFileStoreConstants.FILE_LOCATION_KEY, fileLocation);
+
+        // Wait for the endpoint to respond.
+        // TODO below there is a similar url that does put a slash between port and endpoint, why?
+        URL url = new URL("http://localhost:" + TestConstants.PORT + endpoint + "/repository.xml");
+        int response = ((HttpURLConnection) url.openConnection()).getResponseCode();
+        int tries = 0;
+        while ((response != 200) && (tries++ < 50)) {
+            response = ((HttpURLConnection) url.openConnection()).getResponseCode();
+            Thread.sleep(100); // If we get interrupted, there will be a good reason for it.
+        }
+        if (tries == 50) {
+            throw new IOException("The OBR servlet does not seem to be responding well. Last response code: " + response);
+        }
+    }
+    
+    /* Configure a new repository instance */
+    protected final void addRepository(String instanceName, String customer, String name, boolean isMaster) throws IOException,
+        InterruptedException, InvalidSyntaxException {
+        // Publish configuration for a repository instance
+        Properties props = new Properties();
+        props.put(RepositoryConstants.REPOSITORY_CUSTOMER, customer);
+        props.put(RepositoryConstants.REPOSITORY_NAME, name);
+        props.put(RepositoryConstants.REPOSITORY_MASTER, String.valueOf(isMaster));
+        props.put("factory.instance.pid", instanceName);
+        Configuration config = m_configAdmin.createFactoryConfiguration("org.apache.ace.server.repository.factory", null);
+
+        ServiceTracker tracker = new ServiceTracker(m_bundleContext, m_bundleContext.createFilter("(factory.instance.pid=" + instanceName + ")"), null);
+        tracker.open();
+
+        config.update(props);
+
+        if (tracker.waitForService(5000) == null) {
+            throw new IOException("Did not get notified about new repository becoming available in time.");
+        }
+
+        tracker.close();
+    }
+
+    @Override
+	protected void after() throws Exception {
+        // remove all repositories, in case a test case does not reach it's cleanup section due to an exception
+        removeAllRepositories();
+    }
+
+    @Override
+	protected void before() throws Exception {
+        m_endpoint = new URL(HOST + ENDPOINT_NAME);
+
+        getService(SessionFactory.class).createSession("test-session-ID");
+        configureFactory("org.apache.ace.server.log.store.factory",
+            "name", "auditlog", "authentication.enabled", "false");
+    }
+
+    protected final void cleanUp() throws InvalidSyntaxException, InterruptedException {
+        // Simply remove all objects in the repository.
+        clearRepository(m_artifactRepository);
+        clearResourceProcessors(m_artifactRepository);
+        clearRepository(m_artifact2featureRepository);
+        clearRepository(m_feature2distributionRepository);
+        clearRepository(m_distribution2targetRepository);
+        clearRepository(m_artifactRepository);
+        clearRepository(m_featureRepository);
+        clearRepository(m_distributionRepository);
+        clearRepository(m_targetRepository);
+        clearRepository(m_deploymentVersionRepository);
+        m_statefulTargetRepository.refresh();
+        try {
+            m_repositoryAdmin.logout(true);
+        }
+        catch (Exception ioe) {
+//            ioe.printStackTrace(System.out);
+        }
+    }
+    
+    protected ArtifactObject createBasicArtifactObject(String name, String mimetype, String processorPID)
+        throws InterruptedException {
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(ArtifactObject.KEY_ARTIFACT_NAME, name);
+        attr.put(ArtifactObject.KEY_MIMETYPE, mimetype);
+        attr.put(ArtifactObject.KEY_URL, "http://" + name);
+        attr.put(ArtifactObject.KEY_PROCESSOR_PID, processorPID);
+        Map<String, String> tags = new HashMap<String, String>();
+
+        return m_artifactRepository.create(attr, tags);
+    }
+
+    protected ArtifactObject createBasicBundleObject(String symbolicName) {
+        return createBasicBundleObject(symbolicName, null, null);
+    }
+
+    protected ArtifactObject createBasicBundleObject(String symbolicName, String version, String processorPID) {
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(BundleHelper.KEY_SYMBOLICNAME, symbolicName);
+        attr.put(ArtifactObject.KEY_MIMETYPE, BundleHelper.MIMETYPE);
+        attr.put(ArtifactObject.KEY_URL, "http://" + symbolicName + "-" + ((version == null) ? "null" : version));
+        Map<String, String> tags = new HashMap<String, String>();
+
+        if (version != null) {
+            attr.put(BundleHelper.KEY_VERSION, version);
+        }
+        if (processorPID != null) {
+            attr.put(BundleHelper.KEY_RESOURCE_PROCESSOR_PID, processorPID);
+        }
+        return m_artifactRepository.create(attr, tags);
+    }
+
+    protected DistributionObject createBasicDistributionObject(String name) {
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(DistributionObject.KEY_NAME, name);
+        Map<String, String> tags = new HashMap<String, String>();
+
+        return m_distributionRepository.create(attr, tags);
+    }
+
+    protected FeatureObject createBasicFeatureObject(String name) {
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(FeatureObject.KEY_NAME, name);
+        Map<String, String> tags = new HashMap<String, String>();
+
+        return m_featureRepository.create(attr, tags);
+    }
+
+    protected TargetObject createBasicTargetObject(String id) {
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(TargetObject.KEY_ID, id);
+        Map<String, String> tags = new HashMap<String, String>();
+
+        return m_targetRepository.create(attr, tags);
+    }
+
+    protected void deleteObr(String endpoint) throws IOException, InvalidSyntaxException, InterruptedException {
+        // This is a little ugly: we cannot just delete the configuration, since that will result in a
+        // sharing violation between this bundle and the servlet bundle. In stead, we make the servlet
+        // use an invalid endpoint.
+        Properties propsServlet = new Properties();
+        propsServlet.put(HttpConstants.ENDPOINT, endpoint + "invalid");
+        propsServlet.put("OBRInstance", "singleOBRServlet");
+        Configuration configServlet = m_configAdmin.getConfiguration("org.apache.ace.obr.servlet");
+        configServlet.update(propsServlet);
+
+        URL url = new URL("http://localhost:" + TestConstants.PORT + "/" + endpoint + "/repository.xml");
+        int response = ((HttpURLConnection) url.openConnection()).getResponseCode();
+        int tries = 0;
+        while ((response != 404) && (tries < 50)) {
+            Thread.sleep(100); // If we get interrupted, there will be a good reason for it.
+            response = ((HttpURLConnection) url.openConnection()).getResponseCode();
+            tries++;
+        }
+        if (tries == 50) {
+            throw new IOException("The OBR servlet does not want to go away. Last response code: " + response);
+        }
+    }
+
+    protected Component[] getDependencies() {
+        Dictionary<String, Object> topics = new Hashtable<String, Object>();
+        topics.put(EventConstants.EVENT_TOPIC, new String[] { PUBLIC_TOPIC_ROOT + "*",
+            PRIVATE_TOPIC_ROOT + "*",
+            RepositoryAdmin.PUBLIC_TOPIC_ROOT + "*",
+            RepositoryAdmin.PRIVATE_TOPIC_ROOT + "*",
+            TOPIC_ALL });
+        return new Component[] {
+            createComponent()
+                .setInterface(EventHandler.class.getName(), topics)
+                .setImplementation(this)
+                .add(createServiceDependency().setService(HttpService.class).setRequired(true))
+                .add(createServiceDependency().setService(RepositoryAdmin.class).setRequired(true))
+                .add(createServiceDependency().setService(ArtifactRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(Artifact2FeatureAssociationRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(FeatureRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(Feature2DistributionAssociationRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(DistributionRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(Distribution2TargetAssociationRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(TargetRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(DeploymentVersionRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(StatefulTargetRepository.class).setRequired(true))
+                .add(createServiceDependency().setService(LogStore.class, "(&(" + Constants.OBJECTCLASS + "=" + LogStore.class.getName() + ")(name=auditlog))").setRequired(true))
+                .add(createServiceDependency().setService(ConfigurationAdmin.class).setRequired(true))
+        };
+    }
+
+    protected <T> T runAndWaitForEvent(Callable<T> callable, boolean debug, String... topic) throws Exception {
+        m_runAndWaitDebug = debug;
+        T result = null;
+        m_waitingForTopic.clear();
+        m_waitingForTopic.addAll(Arrays.asList(topic));
+        m_semaphore = new Semaphore(0);
+        result = callable.call();
+        assertTrue("We expect the event within a reasonable timeout.", m_semaphore.tryAcquire(15000, TimeUnit.MILLISECONDS));
+        m_semaphore = null;
+        return result;
+    }
+    
+    protected void startRepositoryService() throws IOException {
+        // configure the (replication)repository servlets
+        configure("org.apache.ace.repository.servlet.RepositoryServlet", HttpConstants.ENDPOINT,
+            ENDPOINT_NAME, "authentication.enabled", "false");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+    	try {
+			m_repositoryAdmin.logout(true);
+		} catch (RuntimeException e) {
+			// Ignore...
+		}
+
+    	try {
+			cleanUp();
+		} catch (Exception e) {
+			// Ignore...
+		}
+    	
+    	try {
+			removeAllRepositories();
+		} catch (IOException e) {
+			// Ignore...
+		}
+    }
+
+    private <T extends RepositoryObject> void clearRepository(ObjectRepository<T> rep) {
+        for (T entity : rep.get()) {
+            try {
+				rep.remove(entity);
+			} catch (RuntimeException e) {
+				// Ignore; try to recover...
+			}
+        }
+        assertEquals("Something went wrong clearing the repository.", 0, rep.get().size());
+    }
+
+    private void clearResourceProcessors(ArtifactRepository rep) {
+        for (ArtifactObject entity : rep.getResourceProcessors()) {
+            try {
+				rep.remove(entity);
+			} catch (RuntimeException e) {
+				// Ignore; try to recover...
+			}
+        }
+        assertEquals("Something went wrong clearing the repository.", 0, rep.get().size());
+    }
+
+    private void removeAllRepositories() throws IOException, InvalidSyntaxException, InterruptedException {
+        final Configuration[] configs = m_configAdmin.listConfigurations("(factory.instance.pid=*)");
+        if ((configs != null) && (configs.length > 0)) {
+            final Semaphore sem = new Semaphore(0);
+
+            ServiceTracker tracker =
+                new ServiceTracker(m_bundleContext, m_bundleContext.createFilter("(" + Constants.OBJECTCLASS + "="
+                    + Repository.class.getName() + ")"), null) {
+                    @Override
+                    public void removedService(ServiceReference reference, Object service) {
+                        super.removedService(reference, service);
+                        // config.length times two because the service tracker also sees added events for each instance
+                        if (size() == 0) {
+                            sem.release();
+                        }
+                    }
+                };
+            tracker.open();
+
+            for (int i = 0; i < configs.length; i++) {
+                configs[i].delete();
+            }
+
+            if (!sem.tryAcquire(1, TimeUnit.SECONDS)) {
+                throw new IOException("Not all instances were removed in time.");
+            }
+            tracker.close();
+        }
+    }
+}

Propchange: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/BaseRepositoryAdminTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java?rev=1360600&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java Thu Jul 12 09:34:42 2012
@@ -0,0 +1,143 @@
+/*
+ * 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.it.repositoryadmin;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+
+import org.apache.ace.client.repository.RepositoryAdmin;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.log.AuditEvent;
+import org.apache.ace.log.LogEvent;
+import org.apache.ace.scheduler.constants.SchedulerConstants;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.Configuration;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Provides test cases for the client automation functionality.
+ */
+public class ClientAutomationTest extends BaseRepositoryAdminTest {
+
+    /*
+     * The auto target operator is not yet session-aware; therefore, this test will fail. We should decide what
+     * to do with this operator.
+     */
+    public void testAutoTargetOperator() throws Exception {
+        startRepositoryService();
+
+        addRepository("storeInstance", "apache", "store", true);
+        addRepository("targetInstance", "apache", "target", true);
+        addRepository("deploymentInstance", "apache", "deployment", true);
+
+        // configure automation bundle; new configuration properties; bundle will start
+        final Properties props = new Properties();
+        props.put("registerTargetFilter", "(id=anotherTarget*)");
+        props.put("approveTargetFilter", "(id=DO_NOTHING)");
+        props.put("autoApproveTargetFilter", "(id=anotherTarget*)");
+        props.put("commitRepositories", "true");
+        props.put("targetRepository", "target");
+        props.put("deploymentRepository", "deployment");
+        props.put("storeRepository", "store");
+        props.put("customerName", "apache");
+        props.put("hostName", HOST);
+        props.put("endpoint", ENDPOINT_NAME);
+
+        final Configuration config = m_configAdmin.getConfiguration("org.apache.ace.client.automation", null);
+
+        /*
+         * First test the basic scenario where we create some auditlog data, this target should be auto-registered after max 1 sec.
+         */
+        runAndWaitForEvent(new Callable<Object>() {
+            public Object call() throws Exception {
+                config.update(props);
+                return null;
+            }
+        }, false, RepositoryAdmin.TOPIC_LOGIN);
+
+        doAutoTargetReg();
+
+        runAndWaitForEvent(new Callable<Object>() {
+            public Object call() throws Exception {
+                config.delete();
+                return null;
+            }
+        }, false, RepositoryAdmin.TOPIC_LOGOUT);
+    }
+
+    private void doAutoTargetReg() throws Exception {
+        List<LogEvent> events = new ArrayList<LogEvent>();
+        Properties props = new Properties();
+        events.add(new LogEvent("anotherTarget", 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+        // fill auditlog; no install data
+        m_auditLogStore.put(events);
+
+        int initRepoSize = m_statefulTargetRepository.get().size();
+
+        // Get the processauditlog task and run it
+        ServiceTracker tracker = new ServiceTracker(m_bundleContext, m_bundleContext.createFilter("(&(" + Constants.OBJECTCLASS + "="
+                + Runnable.class.getName() + ")(" + SchedulerConstants.SCHEDULER_NAME_KEY + "="
+                + "org.apache.ace.client.processauditlog" + "))"), null);
+        tracker.open();
+
+        final Runnable processAuditlog = (Runnable) tracker.waitForService(2000);
+
+        if (processAuditlog != null) {
+            // commit should be called
+            runAndWaitForEvent(new Callable<Object>() {
+                public Object call() throws Exception {
+                    processAuditlog.run();
+                    return null;
+                }
+            }, false, RepositoryAdmin.TOPIC_REFRESH);
+
+            assertEquals("After refresh, we expect 1 target based on auditlogdata;", initRepoSize + 1, m_statefulTargetRepository.get().size());
+            
+            List<StatefulTargetObject> sgoList = m_statefulTargetRepository.get(m_bundleContext.createFilter("(id=another*)"));
+            StatefulTargetObject sgo = sgoList.get(0);
+            assertTrue("Expected one (anotherTarget) in the list.", sgo != null);
+
+            // should be registered and auto approved
+            assertTrue("The automation target operator should have registered anotherTarget.", sgo.isRegistered());
+            assertTrue("The automation target operator should have auto-approved anotherTarget.", sgo.getAutoApprove());
+
+            // add a target which will not be autoregistered
+            events.clear();
+            events.add(new LogEvent("secondTarget", 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+            m_auditLogStore.put(events);
+
+            // do auto target action
+            processAuditlog.run();
+            assertEquals("After refresh, we expect an additional target based on auditlogdata;", initRepoSize + 2, m_statefulTargetRepository.get().size());
+
+            sgoList = m_statefulTargetRepository.get(m_bundleContext.createFilter("(id=second*)"));
+            sgo = sgoList.get(0);
+
+            // second target should not be registered
+            assertFalse("The automation target operator should not have registered secondTarget.", sgo.isRegistered());
+            assertFalse("The automation target operator should not have auto-approved myTarget.", sgo.getAutoApprove());
+        }
+        else
+        {
+            assertTrue("Could not get a reference to the processAuditLog task.", false);
+        }
+    }}

Propchange: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/ClientAutomationTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/StatefulTargetRepositoryTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/StatefulTargetRepositoryTest.java?rev=1360600&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/StatefulTargetRepositoryTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/StatefulTargetRepositoryTest.java Thu Jul 12 09:34:42 2012
@@ -0,0 +1,712 @@
+/*
+ * 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.it.repositoryadmin;
+
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.KEY_ID;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.KEY_REGISTRATION_STATE;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_ADDED;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_REMOVED;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_STATUS_CHANGED;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.UNKNOWN_VERSION;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+
+import org.apache.ace.client.repository.RepositoryAdminLoginContext;
+import org.apache.ace.client.repository.helper.bundle.BundleHelper;
+import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
+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.Distribution2TargetAssociation;
+import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject.ProvisioningState;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject.RegistrationState;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject.StoreState;
+import org.apache.ace.log.AuditEvent;
+import org.apache.ace.log.LogEvent;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Provides test cases for the stateful target repository.
+ */
+public class StatefulTargetRepositoryTest extends BaseRepositoryAdminTest {
+
+    public void testStatefulApprove() throws Exception {
+        setUpTestCase();
+
+        final String targetId = "myNewTarget2";
+        
+        final Map<String, String> attr = new HashMap<String, String>();
+		attr.put(TargetObject.KEY_ID, targetId);
+		
+		final Map<String, String> tags = new HashMap<String, String>();
+		final StatefulTargetObject sgo = runAndWaitForEvent(new Callable<StatefulTargetObject>() {
+		    public StatefulTargetObject call() throws Exception {
+		        return m_statefulTargetRepository.preregister(attr, tags);
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_ADDED);
+		
+		assertTrue("Without any deployment versions, and no information in the shop, we should not need to approve.", !sgo.needsApprove());
+		assertEquals("We expect the registration state to be Registered;", RegistrationState.Registered, sgo.getRegistrationState());
+		assertEquals("We expect the registration state to be New;", StoreState.New, sgo.getStoreState());
+		assertEquals(UNKNOWN_VERSION, sgo.getCurrentVersion());
+		
+		final ArtifactObject b11 = createBasicBundleObject("bundle1", "1", null);
+		
+		FeatureObject g1 = createBasicFeatureObject("feature1");
+		FeatureObject g2 = createBasicFeatureObject("feature2"); // note that this feature is not associated to a bundle.
+		
+		createDynamicBundle2FeatureAssociation(b11, g1);
+		
+		final DistributionObject l1 = createBasicDistributionObject("distribution1");
+		
+		m_feature2distributionRepository.create(g1, l1);
+		m_feature2distributionRepository.create(g2, l1);
+		
+		runAndWaitForEvent(new Callable<Distribution2TargetAssociation>() {
+		    public Distribution2TargetAssociation call() throws Exception {
+		        return m_distribution2targetRepository.create(l1, sgo.getTargetObject());
+		    }
+		}, false, Distribution2TargetAssociation.TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+		
+		assertTrue("We added information that influences our target, so we should need to approve it.", sgo.needsApprove());
+		assertEquals("We expect the registration state to be Registered;", RegistrationState.Registered, sgo.getRegistrationState());
+		assertEquals("We expect the registration state to be Unapproved;", StoreState.Unapproved, sgo.getStoreState());
+		assertEquals("According to the shop, this target needs 1 bundle", 1, sgo.getArtifactsFromShop().length);
+		assertEquals("According to the deployment, this target needs 0 bundles", 0, sgo.getArtifactsFromDeployment().length);
+		assertEquals(UNKNOWN_VERSION, sgo.getCurrentVersion());
+		
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        createBasicDeploymentVersionObject(targetId, "1", b11);
+		        return null;
+		    }
+		}, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+		
+		assertFalse("We manually created a deployment version that reflects the shop, so no approval should be necessary.", sgo.needsApprove());
+		assertEquals("We expect the registration state to be Registered;", RegistrationState.Registered, sgo.getRegistrationState());
+		assertEquals("We expect the registration state to be Approved;", StoreState.Approved, sgo.getStoreState());
+		assertEquals("According to the shop, this target needs 1 bundle;", 1, sgo.getArtifactsFromShop().length);
+		assertEquals("According to the deployment, this target needs 1 bundle;", 1, sgo.getArtifactsFromDeployment().length);
+		
+		runAndWaitForEvent(new Callable<ArtifactObject>() {
+		    public ArtifactObject call() throws Exception {
+		        return createBasicBundleObject("bundle1", "2", null);
+		    }
+		}, false, ArtifactObject.TOPIC_ADDED, Artifact2FeatureAssociation.TOPIC_CHANGED, TOPIC_STATUS_CHANGED);
+		
+		assertTrue("We added a new version of a bundle that is used by the target, so approval should be necessary.", sgo.needsApprove());
+		assertEquals("We expect the registration state to be Registered;", RegistrationState.Registered, sgo.getRegistrationState());
+		assertEquals("We expect the registration state to be Unapproved;", StoreState.Unapproved, sgo.getStoreState());
+		assertEquals("According to the shop, this target needs 1 bundle", 1, sgo.getArtifactsFromShop().length);
+		assertEquals("The shop should tell use we need bundle URL 'bundle1-2';", "http://bundle1-2", sgo.getArtifactsFromShop()[0].getURL());
+		assertEquals("According to the deployment, this target needs 1 bundle", 1, sgo.getArtifactsFromDeployment().length);
+		assertEquals("The deployment should tell use we need bundle URL 'bundle1-1';", "http://bundle1-1", sgo.getArtifactsFromDeployment()[0].getUrl());
+		assertEquals("1", sgo.getCurrentVersion());
+		
+		final String newVersion = runAndWaitForEvent(new Callable<String>() {
+		    public String call() throws Exception {
+		        return sgo.approve();
+		    }
+		}, false, DeploymentVersionObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+		
+		assertFalse("Immediately after approval, no approval is necessary.", sgo.needsApprove());
+		assertEquals("We expect the registration state to be Registered;", RegistrationState.Registered, sgo.getRegistrationState());
+		assertEquals("We expect the registration state to be Approved;", StoreState.Approved, sgo.getStoreState());
+		assertEquals("According to the shop, this target needs 1 bundle", 1, sgo.getArtifactsFromShop().length);
+		assertEquals("The shop should tell use we need bundle URL 'bundle1-2';", "http://bundle1-2", sgo.getArtifactsFromShop()[0].getURL());
+		assertEquals("According to the deployment, this target needs 1 bundle", 1, sgo.getArtifactsFromDeployment().length);
+		assertEquals("The deployment should tell use we need bundle URL 'bundle1-2';", "http://bundle1-2", sgo.getArtifactsFromDeployment()[0].getUrl());
+		assertEquals( "We expect two deployment versions;", 2, m_deploymentVersionRepository.get().size());
+		assertEquals(newVersion, sgo.getCurrentVersion());
+		
+		// clean up this object ourselves; we cannot rely on cleanUp() in this case.
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        m_statefulTargetRepository.unregister(targetId);
+		        return null;
+		    }
+		}, false, TargetObject.TOPIC_REMOVED, TOPIC_REMOVED);
+
+	 	StatefulTargetObject target = findStatefulTarget(targetId);
+	 	assertNull("Target should be removed?!", target);
+		
+		cleanUp();
+    }
+
+    public void testStatefulAuditAndRegister() throws Exception {
+        setUpTestCase();
+
+        // preregister target
+		final Map<String, String> attr = new HashMap<String, String>();
+		attr.put(TargetObject.KEY_ID, "myNewTarget3");
+		final Map<String, String> tags = new HashMap<String, String>();
+		
+		final StatefulTargetObject sgo1 = runAndWaitForEvent(new Callable<StatefulTargetObject>() {
+		    public StatefulTargetObject call() throws Exception {
+		        return m_statefulTargetRepository.preregister(attr, tags);
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_ADDED);
+		
+		// do checks
+		assertTrue("We just preregistered a target, so it should be registered.", sgo1.isRegistered());
+		
+		// add auditlog data
+		List<LogEvent> events = new ArrayList<LogEvent>();
+		Properties props = new Properties();
+		events.add(new LogEvent("myNewTarget3", 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+		m_auditLogStore.put(events);
+		m_statefulTargetRepository.refresh();
+		
+		// do checks
+		assertTrue("Adding auditlog data for a target does not influence its isRegistered().", sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		}
+		catch (IllegalStateException ise) {
+		    assertTrue("We should be able to get sgo1's targetObject.", false);
+		}
+		
+		// add auditlog data for other target
+		events = new ArrayList<LogEvent>();
+		props = new Properties();
+		events.add(new LogEvent("myNewTarget4", 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+		m_auditLogStore.put(events);
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        m_statefulTargetRepository.refresh();
+		        return false;
+		    }
+		}, false, TOPIC_ADDED);
+		final StatefulTargetObject sgo2 = findStatefulTarget("myNewTarget4");
+		
+		// do checks
+		assertTrue("Adding auditlog data for a target does not influence its isRegistered().", sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		}
+		catch (IllegalStateException ise) {
+		    assertTrue("We should be able to get sgo1's targetObject.", false);
+		}
+		assertTrue("sgo2 is only found in the auditlog, so it cannot be in registered.", !sgo2.isRegistered());
+		try {
+		    sgo2.getTargetObject();
+		    assertTrue("We should not be able to get sgo2's targetObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		
+		// remove original target
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        m_statefulTargetRepository.unregister(sgo1.getID());
+		        return null;
+		    }
+		}, false, TargetObject.TOPIC_REMOVED, TOPIC_STATUS_CHANGED);
+		
+		// do checks
+		assertTrue("sgo1 is now only found in the auditlog, so it cannot be registered.", !sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		    assertTrue("We should not be able to get sgo1's targetObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		assertTrue("sgo2 is only found in the auditlog, so it cannot be in registered.", !sgo2.isRegistered());
+		try {
+		    sgo2.getTargetObject();
+		    assertTrue("We should not be able to get sgo2's targetObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		
+		// register second target
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        sgo2.register();
+		        return null;
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+		
+		// do checks
+		assertTrue("sgo1 is now only found in the auditlog, so it cannot be in registered.", !sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		    assertTrue("We should not be able to get sgo1's targetObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		assertTrue("sgo2 has been registered.", sgo2.isRegistered());
+		try {
+		    sgo2.getTargetObject();
+		}
+		catch (IllegalStateException ise) {
+		    assertTrue("We should be able to get sgo2's targetObject.", false);
+		}
+		
+		int nrRegistered =
+		    m_statefulTargetRepository.get(
+		        m_bundleContext.createFilter("(" + KEY_REGISTRATION_STATE + "=" + RegistrationState.Registered + ")"))
+		        .size();
+		assert nrRegistered == 1 : "We expect to filter out one registered target, but we find " + nrRegistered;
+		
+		// Finally, create a distribution object
+		final DistributionObject l1 = createBasicDistributionObject("thedistribution");
+		
+		assertTrue("We just created a Staful GW object, is should not be registered", !sgo1.isRegistered());
+		
+		// register sgo1 again and create an association in 1 go
+		Distribution2TargetAssociation lgw1 = runAndWaitForEvent(new Callable<Distribution2TargetAssociation>() {
+		    public Distribution2TargetAssociation call() throws Exception {
+		        sgo1.register();
+		        return m_distribution2targetRepository.create(l1, sgo1.getTargetObject());
+		    }
+		}, false, Distribution2TargetAssociation.TOPIC_ADDED, TargetObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+		
+		// checks
+		nrRegistered =
+		    m_statefulTargetRepository.get(
+		        m_bundleContext.createFilter("(" + KEY_REGISTRATION_STATE + "=" + RegistrationState.Registered + ")"))
+		        .size();
+		assert nrRegistered == 2 : "We expect to filter out two registered targets, but we find " + nrRegistered;
+		assertTrue("A stateful gw object should be registered", sgo1.isRegistered());
+		assertTrue("The stateful gw object should be associated to thedistribution.", sgo1.isAssociated(l1, DistributionObject.class));
+		assertTrue("Both ends of distribution - stateful gw should be satisfied.", lgw1.isSatisfied());
+		
+		cleanUp();
+    }
+
+    public void testStatefulAuditAndRemove() throws Exception {
+        setUpTestCase();
+
+        // preregister gateway
+		final Map<String, String> attr = new HashMap<String, String>();
+		attr.put(TargetObject.KEY_ID, "myNewGatewayA");
+		final Map<String, String> tags = new HashMap<String, String>();
+		
+		final StatefulTargetObject sgo1 = runAndWaitForEvent(new Callable<StatefulTargetObject>() {
+		    public StatefulTargetObject call() throws Exception {
+		        return m_statefulTargetRepository.preregister(attr, tags);
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_ADDED);
+		
+		// do checks
+		assertTrue("We just preregistered a gateway, so it should be registered.", sgo1.isRegistered());
+		
+		// add auditlog data
+		List<LogEvent> events = new ArrayList<LogEvent>();
+		Properties props = new Properties();
+		events.add(new LogEvent("myNewGatewayA", 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+		m_auditLogStore.put(events);
+		m_statefulTargetRepository.refresh();
+		
+		// do checks
+		assertTrue("Adding auditlog data for a gateway does not influence its isRegistered().", sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		}
+		catch (IllegalStateException ise) {
+		    assertTrue("We should be able to get sgo1's gatewayObject.", false);
+		}
+		// add auditlog data for other gateway
+		events = new ArrayList<LogEvent>();
+		props = new Properties();
+		events.add(new LogEvent("myNewGatewayB", 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+		m_auditLogStore.put(events);
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        m_statefulTargetRepository.refresh();
+		        return false;
+		    }
+		}, false, TOPIC_ADDED);
+		final StatefulTargetObject sgo2 = findStatefulTarget("myNewGatewayB");
+		
+		// do checks
+		assertTrue("Adding auditlog data for a gateway does not influence its isRegistered().", sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		}
+		catch (IllegalStateException ise) {
+		    assertTrue("We should be able to get sgo1's gatewayObject.", false);
+		}
+		assertTrue("sgo2 is only found in the auditlog, so it cannot be in registered.", !sgo2.isRegistered());
+		try {
+		    sgo2.getTargetObject();
+		    assertTrue("We should not be able to get sgo2's gatewayObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		// remove original gateway
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        m_statefulTargetRepository.remove(sgo1);
+		        return null;
+		    }
+		}, false, TargetObject.TOPIC_REMOVED, TOPIC_REMOVED);
+		
+		// do checks
+		assertTrue("sgo1 is now only found in the auditlog, so it cannot be registered.", !sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		    assertTrue("We should not be able to get sgo1's gatewayObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		
+		assertTrue("sgo2 is only found in the auditlog, so it cannot be in registered.", !sgo2.isRegistered());
+		try {
+		    sgo2.getTargetObject();
+		    assertTrue("We should not be able to get sgo2's gatewayObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		
+		// register second gateway
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        sgo2.register();
+		        return null;
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+		
+		// do checks
+		assertTrue("sgo1 is now only found in the auditlog, so it cannot be in registered.", !sgo1.isRegistered());
+		try {
+		    sgo1.getTargetObject();
+		    assertTrue("We should not be able to get sgo1's gatewayObject.", false);
+		}
+		catch (IllegalStateException ise) {
+		    // expected
+		}
+		assertTrue("sgo2 has been registered.", sgo2.isRegistered());
+		try {
+		    sgo2.getTargetObject();
+		}
+		catch (IllegalStateException ise) {
+		    assertTrue("We should be able to get sgo2's gatewayObject.", false);
+		}
+		
+		int nrRegistered = m_statefulTargetRepository.get(m_bundleContext.createFilter("(" + KEY_REGISTRATION_STATE + "=" + RegistrationState.Registered + ")")).size();
+		assert nrRegistered == 1 : "We expect to filter out one registered gateway, but we find " + nrRegistered;
+		
+		// Finally, refresh the repository; it should cause sgo1 to be re-created (due to its audit log)...
+		// ACE-167 does not cover this scenario, but at a later time this should be fixed as well (see ACE-230).
+		m_statefulTargetRepository.refresh();
+		
+		int count = m_statefulTargetRepository.get(m_bundleContext.createFilter("(" + KEY_ID + "=myNewGatewayA)")).size();
+		assertTrue("We expected sgo1 to be re-created!", count == 1);
+		
+		cleanUp();
+    }
+
+    public void testStatefulAuditLog() throws Exception {
+        setUpTestCase();
+        
+        final String targetId = String.format("target-%s", Long.toHexString(System.nanoTime()));
+
+        List<LogEvent> events = new ArrayList<LogEvent>();
+		Properties props = new Properties();
+
+		events.add(new LogEvent(targetId, 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+		// fill auditlog; no install data
+		m_auditLogStore.put(events);
+		
+		runAndWaitForEvent(new Callable<Void>() {
+		    public Void call() throws Exception {
+		    	m_statefulTargetRepository.refresh();
+		        return null;
+		    }
+		}, false, StatefulTargetObject.TOPIC_AUDITEVENTS_CHANGED, StatefulTargetObject.TOPIC_ADDED);
+		
+		StatefulTargetObject sgo = findStatefulTarget(targetId);
+		assertNotNull("Expected new target object to become available!", sgo);
+		
+		assertEquals("We expect our object's provisioning state to be Idle;", ProvisioningState.Idle, sgo.getProvisioningState());
+		
+		// fill auditlog with complete-data
+		events = new ArrayList<LogEvent>();
+		props = new Properties();
+		props.put(AuditEvent.KEY_NAME, "mypackage");
+		props.put(AuditEvent.KEY_VERSION, "123");
+		events.add(new LogEvent(targetId, 1, 2, 2, AuditEvent.DEPLOYMENTCONTROL_INSTALL, props));
+		
+		m_auditLogStore.put(events);
+		
+		runAndWaitForEvent(new Callable<Void>() {
+		    public Void call() throws Exception {
+		    	m_statefulTargetRepository.refresh();
+		        return null;
+		    }
+		}, false, StatefulTargetObject.TOPIC_STATUS_CHANGED, StatefulTargetObject.TOPIC_AUDITEVENTS_CHANGED);
+
+		assertEquals("Our last install version should be 123;", "123", sgo.getLastInstallVersion());
+		assertEquals("We expect our object's provisioning state to be InProgress;", ProvisioningState.InProgress, sgo.getProvisioningState());
+		
+		// fill auditlog with install data
+		events = new ArrayList<LogEvent>();
+		props = new Properties();
+		props.put(AuditEvent.KEY_NAME, "mypackage");
+		props.put(AuditEvent.KEY_VERSION, "123");
+		props.put(AuditEvent.KEY_SUCCESS, "false");
+		events.add(new LogEvent(targetId, 1, 3, 3, AuditEvent.DEPLOYMENTADMIN_COMPLETE, props));
+		
+		m_auditLogStore.put(events);
+		
+		runAndWaitForEvent(new Callable<Void>() {
+		    public Void call() throws Exception {
+		    	m_statefulTargetRepository.refresh();
+		        return null;
+		    }
+		}, false, StatefulTargetObject.TOPIC_AUDITEVENTS_CHANGED, StatefulTargetObject.TOPIC_STATUS_CHANGED);
+		
+		assertEquals("Our last install version should be 123;", "123", sgo.getLastInstallVersion());
+		assertEquals("We expect our object's provisioning state to be Failed;", ProvisioningState.Failed, sgo.getProvisioningState());
+		assertFalse("Our last install was not successful, but according to the sgo it was.", sgo.getLastInstallSuccess());
+		
+		sgo.acknowledgeInstallVersion("123");
+		
+		assertEquals("We expect our object's provisioning state to be Idle;", ProvisioningState.Idle, sgo.getProvisioningState());
+		
+		// add another install event.
+		events = new ArrayList<LogEvent>();
+		props = new Properties();
+		props.put(AuditEvent.KEY_NAME, "mypackage");
+		props.put(AuditEvent.KEY_VERSION, "124");
+		events.add(new LogEvent(targetId, 1, 4, 4, AuditEvent.DEPLOYMENTCONTROL_INSTALL, props));
+		
+		m_auditLogStore.put(events);
+		
+		runAndWaitForEvent(new Callable<Void>() {
+		    public Void call() throws Exception {
+		    	m_statefulTargetRepository.refresh();
+		        return null;
+		    }
+		}, false, StatefulTargetObject.TOPIC_AUDITEVENTS_CHANGED, StatefulTargetObject.TOPIC_STATUS_CHANGED);
+		
+		assertEquals("Our last install version should be 124;", "124", sgo.getLastInstallVersion());
+		assertEquals("We expect our object's provisioning state to be InProgress;", ProvisioningState.InProgress, sgo.getProvisioningState());
+		
+		// fill auditlog with install data
+		events = new ArrayList<LogEvent>();
+		props = new Properties();
+		props.put(AuditEvent.KEY_NAME, "mypackage");
+		props.put(AuditEvent.KEY_VERSION, "124");
+		props.put(AuditEvent.KEY_SUCCESS, "true");
+		events.add(new LogEvent(targetId, 1, 5, 5, AuditEvent.DEPLOYMENTADMIN_COMPLETE, props));
+		
+		m_auditLogStore.put(events);
+		
+		runAndWaitForEvent(new Callable<Void>() {
+		    public Void call() throws Exception {
+		    	m_statefulTargetRepository.refresh();
+		        return null;
+		    }
+		}, false, StatefulTargetObject.TOPIC_AUDITEVENTS_CHANGED, StatefulTargetObject.TOPIC_STATUS_CHANGED);
+		
+		assertEquals("Our last install version should be 124;", "124", sgo.getLastInstallVersion());
+		assertEquals("We expect our object's provisioning state to be OK;", ProvisioningState.OK, sgo.getProvisioningState());
+		assertTrue("Our last install was successful, but according to the sgo it was not.", sgo.getLastInstallSuccess());
+		
+		sgo.acknowledgeInstallVersion("124");
+		
+		assertEquals("We expect our object's provisioning state to be Idle;", ProvisioningState.Idle, sgo.getProvisioningState());
+    }
+
+    public void testStatefulCreateRemove() throws Exception {
+        setUpTestCase();
+
+        final String targetId = "myNewTarget1";
+
+        final Map<String, String> attr = new HashMap<String, String>();
+		attr.put(TargetObject.KEY_ID, targetId);
+		final Map<String, String> tags = new HashMap<String, String>();
+		
+		try {
+		    m_statefulTargetRepository.create(attr, tags);
+		    fail("Creating a stateful target repository should not be allowed.");
+		}
+		catch (UnsupportedOperationException uoe) {
+		    // expected
+		}
+		
+		final StatefulTargetObject sgo = runAndWaitForEvent(new Callable<StatefulTargetObject>() {
+		    public StatefulTargetObject call() throws Exception {
+		        return m_statefulTargetRepository.preregister(attr, tags);
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_ADDED);
+		
+		assertNotNull("We expect to found our new target in the repository!", findStatefulTarget(targetId));
+		
+		// Removing stateful objects is now (partially) supported; see ACE-167 & ACE-230...
+		m_statefulTargetRepository.remove(sgo);
+		
+		assertNull("We expect to NOT found our target in the repository!", findStatefulTarget(targetId));
+		
+		cleanUp();
+    }
+
+    public void testStatefulSetAutoApprove() throws Exception {
+        setUpTestCase();
+
+        final String targetId = "a_target";
+        
+        // register target with
+		final Map<String, String> attr = new HashMap<String, String>();
+		attr.put(TargetObject.KEY_ID, targetId);
+		attr.put(TargetObject.KEY_AUTO_APPROVE, String.valueOf(true));
+		final Map<String, String> tags = new HashMap<String, String>();
+		
+		final StatefulTargetObject sgo = runAndWaitForEvent(new Callable<StatefulTargetObject>() {
+		    public StatefulTargetObject call() throws Exception {
+		        return m_statefulTargetRepository.preregister(attr, tags);
+		    }
+		}, false, TargetObject.TOPIC_ADDED, TOPIC_ADDED);
+		
+		assertNotNull("We expect to found our new target in the repository!", findStatefulTarget(targetId));
+		
+		assertTrue("The target should have auto approved value: true but got: false.", sgo.getAutoApprove());
+		
+		sgo.setAutoApprove(false);
+		
+		assertFalse("The target should have auto approved value: false but got: true.", sgo.getAutoApprove());
+		
+		// clean up
+		runAndWaitForEvent(new Callable<Object>() {
+		    public Object call() throws Exception {
+		        m_statefulTargetRepository.unregister(targetId);
+		        return null;
+		    }
+		}, false, TargetObject.TOPIC_REMOVED, TOPIC_REMOVED);
+    }
+
+	public void testStrangeNamesInTargets() throws Exception {
+        setUpTestCase();
+        
+        final String targetID = ":)";
+
+        List<LogEvent> events = new ArrayList<LogEvent>();
+		Properties props = new Properties();
+
+		// add a target with a weird name.
+		events.add(new LogEvent(targetID, 1, 1, 1, AuditEvent.FRAMEWORK_STARTED, props));
+		// fill auditlog; no install data
+		m_auditLogStore.put(events);
+
+		runAndWaitForEvent(new Callable<Void>() {
+		    public Void call() throws Exception {
+		    	m_statefulTargetRepository.refresh();
+		        return null;
+		    }
+		}, false, StatefulTargetObject.TOPIC_AUDITEVENTS_CHANGED, StatefulTargetObject.TOPIC_ADDED);
+
+		StatefulTargetObject sgo = findStatefulTarget(targetID);
+		assertNotNull("Target not added to repository?!", sgo);
+		
+		sgo.register();
+		
+		assertTrue("After registring our target, we assume it to be registered.", sgo.getRegistrationState().equals(RegistrationState.Registered));
+
+		assertEquals("We expect our object's provisioning state to be Idle;", ProvisioningState.Idle, sgo.getProvisioningState());
+    }
+
+    private DeploymentVersionObject createBasicDeploymentVersionObject(String targetID, String version,
+        ArtifactObject... bundles) {
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(DeploymentVersionObject.KEY_TARGETID, targetID);
+        attr.put(DeploymentVersionObject.KEY_VERSION, version);
+        Map<String, String> tags = new HashMap<String, String>();
+
+        List<DeploymentArtifact> artifacts = new ArrayList<DeploymentArtifact>();
+        for (ArtifactObject artifact : bundles) {
+            Map<String, String> directives = new HashMap<String, String>();
+            directives.put(BundleHelper.KEY_SYMBOLICNAME, artifact.getAttribute(BundleHelper.KEY_SYMBOLICNAME));
+            directives.put(DeploymentArtifact.DIRECTIVE_KEY_BASEURL, artifact.getURL());
+            if (artifact.getAttribute(BundleHelper.KEY_VERSION) != null) {
+                directives.put(BundleHelper.KEY_VERSION, artifact.getAttribute(BundleHelper.KEY_VERSION));
+            }
+            artifacts.add(m_deploymentVersionRepository.createDeploymentArtifact(artifact.getURL(), directives));
+        }
+        return m_deploymentVersionRepository.create(attr, tags, artifacts.toArray(new DeploymentArtifact[0]));
+    }
+    
+    private Artifact2FeatureAssociation createDynamicBundle2FeatureAssociation(ArtifactObject artifact,
+        FeatureObject feature) {
+        Map<String, String> properties = new HashMap<String, String>();
+        properties.put(BundleHelper.KEY_ASSOCIATION_VERSIONSTATEMENT, "0.0.0");
+        return m_artifact2featureRepository.create(artifact, properties, feature, null);
+    }
+
+    /**
+     * The following code is borrowed from RepositoryTest.java, and is used to instantiate and
+     * use repository servlets.
+     */
+    private StatefulTargetObject findStatefulTarget(String targetID) throws InvalidSyntaxException {
+        for (StatefulTargetObject sgo : m_statefulTargetRepository.get()) {
+            if (sgo.getID().equals(targetID)) {
+                return sgo;
+            }
+        }
+        return null;
+    }
+
+    /**
+	 * @throws IOException
+	 * @throws InterruptedException
+	 * @throws InvalidSyntaxException
+	 */
+	private void setUpTestCase() throws Exception {
+		User user = new MockUser();
+		String customer = "customer-" + Long.toHexString(System.currentTimeMillis());
+
+        startRepositoryService();
+
+        addRepository("storeInstance", customer, "store", true);
+        addRepository("targetInstance", customer, "target", true);
+        addRepository("deploymentInstance", customer, "deployment", true);
+
+        RepositoryAdminLoginContext loginContext = m_repositoryAdmin.createLoginContext(user);
+        loginContext
+            .add(loginContext.createShopRepositoryContext()
+                .setLocation(m_endpoint).setCustomer(customer).setName("store").setWriteable())
+            .add(loginContext.createTargetRepositoryContext()
+                .setLocation(m_endpoint).setCustomer(customer).setName("target").setWriteable())
+            .add(loginContext.createDeploymentRepositoryContext()
+                .setLocation(m_endpoint).setCustomer(customer).setName("deployment").setWriteable());
+
+        m_repositoryAdmin.login(loginContext);
+	}
+}

Propchange: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/StatefulTargetRepositoryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/TemplateProcessorTest.java
URL: http://svn.apache.org/viewvc/ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/TemplateProcessorTest.java?rev=1360600&view=auto
==============================================================================
--- ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/TemplateProcessorTest.java (added)
+++ ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/TemplateProcessorTest.java Thu Jul 12 09:34:42 2012
@@ -0,0 +1,428 @@
+/*
+ * 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.it.repositoryadmin;
+
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_ADDED;
+import static org.apache.ace.client.repository.stateful.StatefulTargetObject.TOPIC_STATUS_CHANGED;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+
+import org.apache.ace.client.repository.helper.ArtifactHelper;
+import org.apache.ace.client.repository.helper.ArtifactPreprocessor;
+import org.apache.ace.client.repository.helper.PropertyResolver;
+import org.apache.ace.client.repository.helper.bundle.BundleHelper;
+import org.apache.ace.client.repository.object.Artifact2FeatureAssociation;
+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.DistributionObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.test.constants.TestConstants;
+import org.apache.felix.dm.Component;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Test cases for the template processing functionality.
+ */
+public class TemplateProcessorTest extends BaseRepositoryAdminTest {
+
+	static class MockArtifactHelper implements ArtifactHelper {
+	    private final String m_mimetype;
+	    private final ArtifactPreprocessor m_preprocessor;
+	
+	    MockArtifactHelper(String mimetype) {
+	        this(mimetype, null);
+	    }
+	
+	    MockArtifactHelper(String mimetype, ArtifactPreprocessor preprocessor) {
+	        m_mimetype = mimetype;
+	        m_preprocessor = preprocessor;
+	    }
+	
+	    public boolean canUse(ArtifactObject object) {
+	        return object.getMimetype().equals(m_mimetype);
+	    }
+	
+	    public Map<String, String> checkAttributes(Map<String, String> attributes) {
+	        return attributes;
+	    }
+	
+	    public <TYPE extends ArtifactObject> String getAssociationFilter(TYPE obj, Map<String, String> properties) {
+	        return ("(" + ArtifactObject.KEY_URL + "=" + obj.getURL() + ")");
+	    }
+	
+	    public <TYPE extends ArtifactObject> int getCardinality(TYPE obj, Map<String, String> properties) {
+	        return 1;
+	    }
+	
+	    public Comparator<ArtifactObject> getComparator() {
+	        return null;
+	    }
+	
+	    public String[] getDefiningKeys() {
+	        return new String[] { ArtifactObject.KEY_URL };
+	    }
+	
+	    public String[] getMandatoryAttributes() {
+	        return new String[] { ArtifactObject.KEY_URL };
+	    }
+	
+	    public ArtifactPreprocessor getPreprocessor() {
+	        return m_preprocessor;
+	    }
+	};
+	
+	static class MockArtifactPreprocessor implements ArtifactPreprocessor {
+	    private PropertyResolver m_props;
+	
+	    public boolean needsNewVersion(String url, PropertyResolver props, String targetID, String fromVersion) {
+	        return false;
+	    }
+	
+	    public String preprocess(String url, PropertyResolver props, String targetID, String version, URL obrBase)
+	        throws IOException {
+	        m_props = props;
+	        return url;
+	    }
+	
+	    PropertyResolver getProps() {
+	        return m_props;
+	    }
+	}
+
+    public void testStatefulApprovalWithArtifacts() throws Exception {
+        // some setup: we need a helper.
+        ArtifactHelper myHelper = new MockArtifactHelper("mymime");
+
+        Properties serviceProps = new Properties();
+        serviceProps.put(ArtifactHelper.KEY_MIMETYPE, "mymime");
+
+        Component myHelperService = m_dependencyManager.createComponent()
+            .setInterface(ArtifactHelper.class.getName(), serviceProps)
+            .setImplementation(myHelper);
+
+        m_dependencyManager.add(myHelperService);
+
+        // Empty tag map to be reused througout test
+        final Map<String, String> tags = new HashMap<String, String>();
+
+        // First, create a bundle and two artifacts, but do not provide a processor for the artifacts.
+        ArtifactObject b1 = createBasicBundleObject("bundle1");
+        Map<String, String> attr = new HashMap<String, String>();
+        attr.put(ArtifactObject.KEY_URL, "http://myobject");
+        attr.put(ArtifactObject.KEY_PROCESSOR_PID, "my.processor.pid");
+        attr.put(ArtifactHelper.KEY_MIMETYPE, "mymime");
+
+        ArtifactObject a1 = m_artifactRepository.create(attr, tags);
+
+        attr = new HashMap<String, String>();
+        attr.put(ArtifactObject.KEY_URL, "http://myotherobject");
+        attr.put(ArtifactObject.KEY_PROCESSOR_PID, "my.processor.pid");
+        attr.put(ArtifactObject.KEY_RESOURCE_ID, "mymime");
+        attr.put(ArtifactHelper.KEY_MIMETYPE, "mymime");
+
+        ArtifactObject a2 = m_artifactRepository.create(attr, tags);
+
+        FeatureObject g = createBasicFeatureObject("feature");
+        DistributionObject l = createBasicDistributionObject("distribution");
+
+        attr = new HashMap<String, String>();
+        attr.put(TargetObject.KEY_ID, "myTarget");
+
+        StatefulTargetObject sgo = m_statefulTargetRepository.preregister(attr, tags);
+
+        m_artifact2featureRepository.create(b1, g);
+        m_artifact2featureRepository.create(a1, g);
+        m_artifact2featureRepository.create(a2, g);
+
+        m_feature2distributionRepository.create(g, l);
+
+        m_distribution2targetRepository.create(l, sgo.getTargetObject());
+
+        try {
+            sgo.approve();
+            assertTrue("Without a resource processor for our artifact, approve should go wrong.", false);
+        }
+        catch (IllegalStateException ise) {
+            // expected
+        }
+
+        // Now, add a processor for the artifact.
+        attr = new HashMap<String, String>();
+        attr.put(ArtifactObject.KEY_URL, "http://myprocessor");
+        attr.put(BundleHelper.KEY_RESOURCE_PROCESSOR_PID, "my.processor.pid");
+        attr.put(BundleHelper.KEY_SYMBOLICNAME, "my.processor.bundle");
+        attr.put(ArtifactHelper.KEY_MIMETYPE, BundleHelper.MIMETYPE);
+
+        ArtifactObject b2 = m_artifactRepository.create(attr, tags);
+
+        sgo.approve();
+
+        DeploymentVersionObject dep = m_deploymentVersionRepository.getMostRecentDeploymentVersion(sgo.getID());
+
+        DeploymentArtifact[] toDeploy = dep.getDeploymentArtifacts();
+
+        assertEquals("We expect to find four artifacts to deploy;", 4, toDeploy.length);
+        DeploymentArtifact bundle1 = toDeploy[0];
+        assertEquals(b1.getURL(), bundle1.getUrl());
+        
+        DeploymentArtifact bundle2 = toDeploy[1];
+        assertEquals(b2.getURL(), bundle2.getUrl());
+        assertEquals("true", bundle2.getDirective(DeploymentArtifact.DIRECTIVE_ISCUSTOMIZER));
+        
+        DeploymentArtifact artifact1 = toDeploy[2];
+        assertEquals(a1.getURL(), artifact1.getUrl());
+        assertEquals("my.processor.pid", artifact1.getDirective(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID));
+        
+        DeploymentArtifact artifact2 = toDeploy[3];
+        assertEquals(a2.getURL(), artifact2.getUrl());
+        assertEquals("my.processor.pid", artifact2.getDirective(DeploymentArtifact.DIRECTIVE_KEY_PROCESSORID));
+        assertEquals(a2.getResourceId(), artifact2.getDirective(DeploymentArtifact.DIRECTIVE_KEY_RESOURCE_ID));
+
+        cleanUp();
+
+        m_dependencyManager.remove(myHelperService);
+    }
+
+    /**
+     * Tests the full template mechanism, from importing templatable artifacts, to creating deployment
+     * versions with it. It uses the configuration (autoconf) helper, which uses a VelocityBased preprocessor.
+     */
+    public void testTemplateProcessing() throws Exception {
+        addObr("/obr", "store");
+        m_artifactRepository.setObrBase(new URL("http://localhost:" + TestConstants.PORT + "/obr/"));
+
+        // create some template things
+        String xmlHeader =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\"?><metatype:MetaData xmlns:metatype=\"http://www.osgi.org/xmlns/metatype/v1.0.0\">";
+        String xmlFooter = "</metatype:MetaData>";
+
+        String noTemplate = "<Attribute content=\"http://someURL\"/>";
+        String noTemplateProcessed = "<Attribute content=\"http://someURL\"/>";
+        final File noTemplateFile = createFileWithContents("template", ".xml", xmlHeader + noTemplate + xmlFooter);
+
+        String simpleTemplate = "<Attribute content=\"http://$context.name\"/>";
+        String simpleTemplateProcessed = "<Attribute content=\"http://mydistribution\"/>";
+        File simpleTemplateFile = createFileWithContents("template", "xml", xmlHeader + simpleTemplate + xmlFooter);
+
+        // create some tree from artifacts to a target
+        FeatureObject go = runAndWaitForEvent(new Callable<FeatureObject>() {
+            public FeatureObject call() throws Exception {
+                ArtifactObject b1 = createBasicBundleObject("myBundle");
+                createBasicBundleObject("myProcessor", "1.0.0", "org.osgi.deployment.rp.autoconf");
+                FeatureObject go = createBasicFeatureObject("myfeature");
+                DistributionObject lo = createBasicDistributionObject("mydistribution");
+                TargetObject gwo = createBasicTargetObject("templatetarget2");
+                m_artifact2featureRepository.create(b1, go);
+                // note that we do not associate b2: this is a resource processor, so it will be packed
+                // implicitly. It should not be available to a preprocessor either.
+                m_feature2distributionRepository.create(go, lo);
+                m_distribution2targetRepository.create(lo, gwo);
+                return go;
+            }
+        }, false, TOPIC_ADDED);
+
+        ArtifactObject a1 = m_artifactRepository.importArtifact(noTemplateFile.toURI().toURL(), true);
+        Artifact2FeatureAssociation a2g = m_artifact2featureRepository.create(a1, go);
+
+        final StatefulTargetObject sgo =
+            m_statefulTargetRepository.get(
+                m_bundleContext.createFilter("(" + TargetObject.KEY_ID + "=templatetarget2)")).get(0);
+
+        // create a deploymentversion
+        assertTrue("With the new assignments, the SGO should need approval.", sgo.needsApprove());
+        
+        runAndWaitForEvent(new Callable<Object>() {
+            public Object call() throws Exception {
+                sgo.approve();
+                return null;
+            }
+        }, false, TOPIC_STATUS_CHANGED);
+
+        // find the deployment version
+        DeploymentVersionObject dvo = m_deploymentVersionRepository.getMostRecentDeploymentVersion("templatetarget2");
+        String inFile = tryGetStringFromURL(findXmlUrlInDeploymentObject(dvo), 10, 100);
+
+        assertEquals(xmlHeader + noTemplateProcessed + xmlFooter, inFile);
+
+        // try the simple template
+        m_artifact2featureRepository.remove(a2g);
+        a1 = m_artifactRepository.importArtifact(simpleTemplateFile.toURI().toURL(), true);
+        a2g = m_artifact2featureRepository.create(a1, go);
+
+        sgo.approve();
+
+        // find the deployment version
+        dvo = m_deploymentVersionRepository.getMostRecentDeploymentVersion("templatetarget2");
+        // sleep for a while, to allow the OBR to process the file.
+        Thread.sleep(250);
+
+        inFile = tryGetStringFromURL(findXmlUrlInDeploymentObject(dvo), 10, 100);
+
+        assertEquals(xmlHeader + simpleTemplateProcessed + xmlFooter, inFile);
+
+        deleteObr("/obr");
+    }
+
+    /**
+     * Tests the template processing mechanism: given a custom processor, do the correct calls go out?
+     */
+    public void testTemplateProcessingInfrastructure() throws Exception {
+        // create a preprocessor
+        MockArtifactPreprocessor preprocessor = new MockArtifactPreprocessor();
+
+        // create a helper
+        MockArtifactHelper helper = new MockArtifactHelper("mymime", preprocessor);
+
+        // register preprocessor and helper
+        Properties serviceProps = new Properties();
+        serviceProps.put(ArtifactHelper.KEY_MIMETYPE, "mymime");
+
+        Component helperService = m_dependencyManager.createComponent()
+            .setInterface(ArtifactHelper.class.getName(), serviceProps)
+            .setImplementation(helper);
+
+        m_dependencyManager.add(helperService);
+        
+        String targetId = "templatetarget";
+
+        createBasicBundleObject("myProcessor", "1.0.0", "myProcessor.pid");
+        final ArtifactObject b1 = createBasicBundleObject("myBundle");
+        final ArtifactObject a1 = createBasicArtifactObject("myArtifact", "mymime", "myProcessor.pid");
+        final FeatureObject go = createBasicFeatureObject("myfeature");
+        final DistributionObject lo = createBasicDistributionObject("mydistribution");
+		final TargetObject gwo = createBasicTargetObject(targetId);
+
+        // create some tree from artifacts to a target
+        runAndWaitForEvent(new Callable<Object>() {
+            public Object call() throws Exception {
+                m_artifact2featureRepository.create(b1, go);
+                // note that we do not associate b2: this is a resource processor, so it will be packed
+                // implicitly. It should not be available to a preprocessor either.
+                m_artifact2featureRepository.create(a1, go);
+                m_feature2distributionRepository.create(go, lo);
+                m_distribution2targetRepository.create(lo, gwo);
+                return null;
+            }
+        }, false, TOPIC_ADDED, TOPIC_STATUS_CHANGED);
+
+        StatefulTargetObject sgo = findStatefulTarget(targetId);
+        assertNotNull("Failed to find our target in the repository?!", sgo);
+
+        // wait until needsApprove is true; depending on timing, this could have happened before or after the TOPIC_ADDED.
+        int attempts = 0;
+        while (!sgo.needsApprove() && (attempts++ < 10)) {
+            Thread.sleep(100);
+        }
+        
+        assertTrue("With the new assignments, the SGO should need approval.", sgo.needsApprove());
+        // create a deploymentversion
+        sgo.approve();
+
+        // the preprocessor now has gotten its properties; inspect these
+        PropertyResolver target = preprocessor.getProps();
+        assertTrue("The property resolver should be able to resolve 'id'.", target.get("id").startsWith(targetId));
+        assertTrue("The property resolver should be able to resolve 'name'.", target.get("name").startsWith("mydistribution"));
+        assertNull("The property resolver should not be able to resolve 'someunknownproperty'.", target.get("someunknownproperty"));
+
+        cleanUp(); // we need to do this before the helper goes away
+
+        m_dependencyManager.remove(helperService);
+    }
+
+    private String tryGetStringFromURL(URL url, int tries, int interval) throws Exception {
+        while (true) {
+            try {
+                List<String> result = getResponse(url);
+                
+                StringBuilder sb = new StringBuilder();
+                for (String line : result) {
+                	if (sb.length() > 0) {
+                		sb.append('\n');
+                	}
+                	sb.append(line);
+                }
+                
+                return sb.toString();
+            }
+            catch (IOException ioe) {
+                if (--tries == 0) {
+                    throw ioe;
+                } else {
+                	Thread.sleep(interval);
+                }
+            }
+        }
+    }
+
+    /**
+     * The following code is borrowed from RepositoryTest.java, and is used to instantiate and
+     * use repository servlets.
+     */
+    private StatefulTargetObject findStatefulTarget(String targetID) throws InvalidSyntaxException {
+        for (StatefulTargetObject sgo : m_statefulTargetRepository.get()) {
+            if (sgo.getID().equals(targetID)) {
+                return sgo;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Creates a temporary file with the given name and extension, and stores the given
+     * contents in it.
+     */
+    private File createFileWithContents(String name, String extension, String contents) throws IOException {
+        File file = File.createTempFile(name, extension);
+        file.deleteOnExit();
+        Writer w = new OutputStreamWriter(new FileOutputStream(file));
+        w.write(contents);
+        w.close();
+        return file;
+    }
+
+    /**
+     * Helper method for testTemplateProcessing; finds the URL of the first deploymentartifact
+     * with 'xml' in its url.
+     */
+    private URL findXmlUrlInDeploymentObject(DeploymentVersionObject dvo) throws MalformedURLException {
+        DeploymentArtifact[] artifacts = dvo.getDeploymentArtifacts();
+        for (DeploymentArtifact da : artifacts) {
+            if (da.getUrl().contains("xml")) {
+                return new URL(da.getUrl());
+            }
+        }
+        return null;
+    }
+}

Propchange: ace/sandbox/marrs/org.apache.ace.client.repository.itest/src/org/apache/ace/it/repositoryadmin/TemplateProcessorTest.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message