/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.kernel.deployer.test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.Version;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListener;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListenerSupport;
import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
import org.eclipse.virgo.kernel.deployer.core.DeploymentIdentity;
import org.eclipse.virgo.util.math.Sets;
/**
* Test refresh of artifacts in a Par
*
*/
public class ParRefreshTests extends AbstractDeployerIntegrationTest {
private static final File PAR_FILE = new File("src/test/resources/ParRefreshPar.par");
private static final String PAR_SYMBOLIC_NAME = "RefreshPar";
private static final Version PAR_VERSION = new Version(1,0,0);
private static final String BUNDLE_A_SYMBOLIC_NAME = PAR_SYMBOLIC_NAME + "-1-bundleA";
private static final String BUNDLE_B_SYMBOLIC_NAME = PAR_SYMBOLIC_NAME + "-1-bundleB";
private static final Version BUNDLE_VERSION = new Version(1,0,0);
private static final String SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME = PAR_SYMBOLIC_NAME + "-1-synthetic.context";
private ArtifactListener artifactListener = new ArtifactListener();
@Before
public void parRefreshSetup() throws Exception {
this.context.registerService(InstallArtifactLifecycleListener.class.getName(), artifactListener, null);
}
@Test
public void testRefreshOfBundleInPar() throws DeploymentException {
DeploymentIdentity parIdentity = this.deployer.deploy(PAR_FILE.toURI());
assertBundlePresent(BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION);
this.artifactListener.clear();
Set<ArtifactLifecycleEvent> expectedEventSet = new HashSet<ArtifactLifecycleEvent>();
//events expected due to explicit refresh;
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPING, "bundle", BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPED, "bundle", BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.UNRESOLVED, "bundle", BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.RESOLVED, "bundle", BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTING, "bundle", BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTED, "bundle", BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION));
//events caused by PackageAdmin propagation
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPING, "bundle", SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPED, "bundle", SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.UNRESOLVED, "bundle", SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.RESOLVED, "bundle", SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTING, "bundle", SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTED, "bundle", SYNTHETIC_CONTEXT_BUNDLE_SYMBOLIC_NAME, BUNDLE_VERSION));
// bundleB depends on (imports) bundleA
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPING, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.UNRESOLVED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.RESOLVED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTING, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
// Refresh the bundle, get back 'new' par identity
DeploymentIdentity newIdentity = this.deployer.refresh(PAR_FILE.toURI(), BUNDLE_A_SYMBOLIC_NAME);
waitForAndCheckEventsReceived(expectedEventSet, 10000L); // ten seconds backstop
Set<ArtifactLifecycleEvent> actualEventSet = new HashSet<ArtifactLifecycleEvent>(artifactListener.extract());
Set<ArtifactLifecycleEvent> extraEvents = Sets.difference(actualEventSet, expectedEventSet);
Set<ArtifactLifecycleEvent> missingEvents = Sets.difference(expectedEventSet, actualEventSet);
assertTrue("Extra events were received: " + extraEvents, extraEvents.isEmpty());
assertTrue("Events were missing: " + missingEvents, missingEvents.isEmpty());
assertBundlePresent(BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION);
assertDeploymentIdentityEquals(newIdentity, PAR_SYMBOLIC_NAME, "par", PAR_SYMBOLIC_NAME, PAR_VERSION.toString());
this.deployer.undeploy(parIdentity);
assertBundleNotPresent(BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION);
}
@Test
public void testRefreshOfDependentBundleInPar() throws DeploymentException {
DeploymentIdentity parIdentity = this.deployer.deploy(PAR_FILE.toURI());
assertBundlePresent(BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION);
assertBundlePresent(BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION);
this.artifactListener.clear();
Set<ArtifactLifecycleEvent> expectedEventSet = new HashSet<ArtifactLifecycleEvent>();
//events expected due to explicit refresh;
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPING, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STOPPED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.UNRESOLVED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.RESOLVED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTING, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
expectedEventSet.add(new ArtifactLifecycleEvent(TestLifecycleEvent.STARTED, "bundle", BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION));
// Refresh the configuration, get back 'new' par identity
DeploymentIdentity newIdentity = this.deployer.refresh(PAR_FILE.toURI(), BUNDLE_B_SYMBOLIC_NAME);
waitForAndCheckEventsReceived(expectedEventSet, 10000L); // ten seconds backstop
assertBundlePresent(BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION);
assertBundlePresent(BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION);
assertDeploymentIdentityEquals(newIdentity, PAR_SYMBOLIC_NAME, "par", PAR_SYMBOLIC_NAME, PAR_VERSION.toString());
this.deployer.undeploy(parIdentity);
assertBundleNotPresent(BUNDLE_A_SYMBOLIC_NAME, BUNDLE_VERSION);
assertBundleNotPresent(BUNDLE_B_SYMBOLIC_NAME, BUNDLE_VERSION);
}
private void waitForAndCheckEventsReceived(Set<ArtifactLifecycleEvent> expectedEventSet, long timeout) {
artifactListener.waitForEvents(expectedEventSet, timeout);
Set<ArtifactLifecycleEvent> actualEventSet = new HashSet<ArtifactLifecycleEvent>(this.artifactListener.extract());
Set<ArtifactLifecycleEvent> extraEvents = Sets.difference(actualEventSet, expectedEventSet);
Set<ArtifactLifecycleEvent> missingEvents = Sets.difference(expectedEventSet, actualEventSet);
assertTrue("Extra events were received: " + extraEvents, extraEvents.isEmpty());
assertTrue("Events were missing: " + missingEvents, missingEvents.isEmpty());
}
private void assertBundlePresent(String symbolicName, Version version) {
Bundle[] bundles = this.context.getBundles();
for (Bundle bundle : bundles) {
if (symbolicName.equals(bundle.getSymbolicName()) && version.equals(bundle.getVersion())) {
return;
}
}
fail("The bundle " + symbolicName + " " + version + " was not found.");
}
private void assertBundleNotPresent(String symbolicName, Version version) {
Bundle[] bundles = this.context.getBundles();
for (Bundle bundle : bundles) {
if (symbolicName.equals(bundle.getSymbolicName()) && version.equals(bundle.getVersion())) {
fail("Bundle " + bundle + " should not be present");
}
}
}
private static enum TestLifecycleEvent {
INSTALLED, INSTALLING, RESOLVED, RESOLVING, STARTED, STARTING, STOPPED, STOPPING, UNINSTALLED, UNINSTALLING, UNRESOLVED
}
private static class ArtifactLifecycleEvent {
public ArtifactLifecycleEvent(TestLifecycleEvent lifecycleEvent, String type, String name, Version version) {
this.lifeCycleEvent = lifecycleEvent;
this.type = type;
this.name = name;
this.version = version;
}
public boolean equals(Object obj) {
if (obj instanceof ArtifactLifecycleEvent) {
ArtifactLifecycleEvent other = (ArtifactLifecycleEvent) obj;
return (this.lifeCycleEvent.equals(other.lifeCycleEvent)
&& this.type.equals(other.type)
&& this.name.equals(other.name)
&& this.version.equals(other.version));
}
return false;
}
public int hashCode() {
final int prime = 17;
int result = this.lifeCycleEvent.hashCode() + prime
* (this.name.hashCode() + prime * (this.type.hashCode() + prime * (this.version.hashCode())));
return result;
}
public String toString() {
StringBuilder sb = new StringBuilder("[");
sb.append(this.lifeCycleEvent).append(", ");
sb.append(this.type).append(", ");
sb.append(this.name).append(", ");
sb.append(this.version).append("]");
return sb.toString();
}
private final TestLifecycleEvent lifeCycleEvent;
private final String type;
private final String name;
private final Version version;
}
private static class ArtifactListener extends InstallArtifactLifecycleListenerSupport {
private final Object monitor = new Object();
List<ArtifactLifecycleEvent> eventList = new ArrayList<ArtifactLifecycleEvent>();
public void clear() {
synchronized (this.monitor) {
this.eventList.clear();
}
}
public boolean waitForEvents(final Set<ArtifactLifecycleEvent> expectedEventSet, long timeout) {
boolean allReceived = eventsReceived(expectedEventSet);
while (!allReceived && timeout>0) {
timeout -= 50L;
try {
Thread.sleep(50L);
} catch (InterruptedException _) {
// do nothing
}
allReceived = eventsReceived(expectedEventSet);
}
return allReceived;
}
private boolean eventsReceived(Set<ArtifactLifecycleEvent> eventSet) {
synchronized (this.monitor) {
for (ArtifactLifecycleEvent event : eventSet) {
if (!this.eventList.contains(event)) {
return false;
}
}
return true;
}
}
public List<ArtifactLifecycleEvent> extract() {
synchronized (this.monitor) {
return new ArrayList<ArtifactLifecycleEvent>(this.eventList);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onInstalled(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.INSTALLED, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onInstalling(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.INSTALLING, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onResolved(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.RESOLVED, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onResolving(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.RESOLVING, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onStarted(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.STARTED, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onStarting(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.STARTING, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onStopped(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.STOPPED, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onStopping(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.STOPPING, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onUninstalled(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.UNINSTALLED, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onUninstalling(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.UNINSTALLING, installArtifact);
}
/**
* {@inheritDoc}
*/
@Override
public void onUnresolved(InstallArtifact installArtifact) {
addEvent(TestLifecycleEvent.UNRESOLVED, installArtifact);
}
private void addEvent(TestLifecycleEvent event, InstallArtifact installArtifact) {
synchronized (this.monitor) {
this.eventList.add(new ArtifactLifecycleEvent(event, installArtifact.getType(), installArtifact.getName(),
installArtifact.getVersion()));
}
}
}
}