Add simple test for update handlers
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1649494 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/test/java/org/apache/sling/installer/it/UpdateHandlerTest.java b/src/test/java/org/apache/sling/installer/it/UpdateHandlerTest.java
new file mode 100644
index 0000000..2892782
--- /dev/null
+++ b/src/test/java/org/apache/sling/installer/it/UpdateHandlerTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.sling.installer.it;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.api.ResourceChangeListener;
+import org.apache.sling.installer.api.UpdateHandler;
+import org.apache.sling.installer.api.UpdateResult;
+import org.apache.sling.installer.api.event.InstallationEvent;
+import org.apache.sling.installer.api.event.InstallationListener;
+import org.apache.sling.installer.api.tasks.ChangeStateTask;
+import org.apache.sling.installer.api.tasks.InstallTask;
+import org.apache.sling.installer.api.tasks.InstallTaskFactory;
+import org.apache.sling.installer.api.tasks.RegisteredResource;
+import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.api.tasks.ResourceTransformer;
+import org.apache.sling.installer.api.tasks.TaskResource;
+import org.apache.sling.installer.api.tasks.TaskResourceGroup;
+import org.apache.sling.installer.api.tasks.TransformationResult;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+@RunWith(PaxExam.class)
+
+public class UpdateHandlerTest extends OsgiInstallerTestBase {
+
+ private static final String TYPE = "special";
+
+ private final List<ServiceRegistration<?>> serviceRegistrations = new ArrayList<ServiceRegistration<?>>();
+
+ private final Map<String, Dictionary<String, Object>> installed = new HashMap<String, Dictionary<String,Object>>();
+
+ @org.ops4j.pax.exam.Configuration
+ public Option[] config() {
+ return defaultConfiguration();
+ }
+
+ @Before
+ public void setUp() {
+ setupInstaller();
+ serviceRegistrations.clear();
+
+ final Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(Constants.SERVICE_RANKING, 1000);
+
+ serviceRegistrations.add(this.bundleContext.registerService(ResourceTransformer.class,
+ new ResourceTransformer() {
+
+ public TransformationResult[] transform(final RegisteredResource resource) {
+ final int lastDot = resource.getURL().lastIndexOf('.');
+ final int lastSlash = resource.getURL().lastIndexOf('/');
+ if ( resource.getURL().substring(lastDot + 1).equals(TYPE) ) {
+ final String id = resource.getURL().substring(lastSlash + 1, lastDot);
+ final TransformationResult tr = new TransformationResult();
+ tr.setId(id);
+ tr.setResourceType(TYPE);
+
+ return new TransformationResult[] {tr};
+ }
+ return null;
+ }
+ }, props));
+ serviceRegistrations.add(this.bundleContext.registerService(InstallTaskFactory.class,
+ new InstallTaskFactory() {
+
+ public InstallTask createTask(final TaskResourceGroup toActivate) {
+ final TaskResource tr = toActivate.getActiveResource();
+ if ( tr != null && tr.getEntityId().startsWith(TYPE) ) {
+ if ( tr.getState() == ResourceState.INSTALL ) {
+ installed.put(tr.getEntityId(), tr.getDictionary());
+ return new ChangeStateTask(toActivate, ResourceState.INSTALLED);
+ } else {
+ installed.remove(tr.getEntityId());
+ return new ChangeStateTask(toActivate, ResourceState.UNINSTALLED);
+ }
+ }
+ return null;
+ }
+ }, props));
+ }
+
+ @Override
+ @After
+ public void tearDown() {
+ for(final ServiceRegistration<?> reg : this.serviceRegistrations) {
+ reg.unregister();
+ }
+ this.serviceRegistrations.clear();
+ super.tearDown();
+ }
+
+ private static final class UpdateHandlerImpl implements UpdateHandler {
+
+ private final Barrier barrier = new Barrier(2);
+
+ private volatile UpdateResult result;
+
+ public UpdateResult handleUpdate(String resourceType, String id,
+ String url, InputStream is, Map<String, Object> attributes) {
+ // we only test dictionaries
+ return null;
+ }
+
+ public UpdateResult handleUpdate(final String resourceType,
+ final String id,
+ final String url,
+ final Dictionary<String, Object> dict,
+ final Map<String, Object> attributes) {
+ if ( resourceType.equals(TYPE)) {
+ final UpdateResult ur = new UpdateResult(TYPE + ":/resource/b/" + id + "." + resourceType);
+ this.result = ur;
+ this.barrier.block();
+
+ return ur;
+ }
+ return null;
+ }
+
+ public UpdateResult handleRemoval(String resourceType, String id, String url) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public UpdateResult waitForUpdate() {
+ barrier.block();
+
+ final UpdateResult r = this.result;
+ this.result = null;
+ barrier.reset();
+ return r;
+ }
+ };
+
+ private Barrier getInstallerListenerBarrier() {
+ final Barrier b = new Barrier(2);
+ final InstallationListener il = new InstallationListener() {
+ public void onEvent(final InstallationEvent event) {
+ if ( event.getType() == InstallationEvent.TYPE.PROCESSED ) {
+ b.block();
+ }
+ }
+ };
+ b.reg = bundleContext.registerService(InstallationListener.class.getName(), il, null);
+ return b;
+ }
+
+ @Test
+ public void testSimpleUpdate() throws Exception {
+ final UpdateHandlerImpl up = new UpdateHandlerImpl();
+ final Dictionary<String, Object> dict = new Hashtable<String, Object>();
+ dict.put(UpdateHandler.PROPERTY_SCHEMES, TYPE);
+
+ this.serviceRegistrations.add(this.bundleContext.registerService(UpdateHandler.class, up, dict));
+
+ final Dictionary<String, Object> data = new Hashtable<String, Object>();
+ data.put("foo", "bar");
+
+ final InstallableResource ir = new InstallableResource("/resource/a." + TYPE,
+ null, data, null, InstallableResource.TYPE_PROPERTIES, null);
+
+ final Barrier b = this.getInstallerListenerBarrier();
+ this.installer.registerResources(TYPE, new InstallableResource[] {ir});
+ b.block();
+ b.reg.unregister();
+
+ assertNotNull("Resource should be installed: " + installed, installed.get(TYPE) + ":a");
+
+ final ResourceChangeListener rcl = (ResourceChangeListener)this.installer;
+
+ final Dictionary<String, Object> newData = new Hashtable<String, Object>();
+ data.put("bar", "foo");
+ rcl.resourceAddedOrUpdated(TYPE, "a", null, newData, null);
+
+ final UpdateResult ur = up.waitForUpdate();
+ assertNotNull(ur);
+ assertEquals(TYPE + ":/resource/b/a." + TYPE, ur.getURL());
+ }
+
+ /** Simplified version of the cyclic barrier class for testing. */
+ public static final class Barrier extends CyclicBarrier {
+
+ public Barrier(final int parties) {
+ super(parties);
+ }
+
+ public void block() {
+ try {
+ this.await();
+ } catch (InterruptedException e) {
+ // ignore
+ } catch (BrokenBarrierException e) {
+ // ignore
+ }
+ }
+
+ public boolean block(int seconds) {
+ try {
+ this.await(seconds, TimeUnit.SECONDS);
+ return true;
+ } catch (InterruptedException e) {
+ // ignore
+ } catch (BrokenBarrierException e) {
+ // ignore
+ } catch (TimeoutException e) {
+ // ignore
+ }
+ this.reset();
+ return false;
+ }
+
+ public ServiceRegistration<?> reg;
+ }
+}