blob: fe7b195fdafae04c47b593542d6721a4ad3cb371 [file] [log] [blame]
/*******************************************************************************
* 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.test.stubs.framework;
import static org.eclipse.virgo.test.stubs.internal.Assert.assertNotNull;
import static org.eclipse.virgo.test.stubs.internal.Duplicator.shallowCopy;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
/**
* A stub testing implementation of {@link Bundle} as defined in section 6.1.4 of the OSGi Service Platform Core
* Specification.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* Threadsafe
*
*/
public final class StubBundle implements Bundle {
private static final Long DEFAULT_BUNDLE_ID = Long.valueOf(1);
private static final String DEFAULT_SYMBOLIC_NAME = "org.eclipse.virgo.test.stubs.testbundle";
private static final Version DEFAULT_VERSION = Version.emptyVersion;
private static final String DEFAULT_LOCATION = "/";
private final Long bundleId;
private final String symbolicName;
private final Version version;
private final String location;
private volatile int state;
private final Object stateMonitor = new Object();
private volatile long lastModified;
private final Object lastModifiedMonitor = new Object();
private volatile StubBundleContext bundleContext;
private final Object bundleContextMonitor = new Object();
private volatile Dictionary<String, String> headers = new Hashtable<String, String>();
private final Object headersMonitor = new Object();
private volatile Dictionary<String, String> localizedHeaders = new Hashtable<String, String>();
private final Object localizedHeadersMonitor = new Object();
private final Map<String, Class<?>> loadClasses = new HashMap<String, Class<?>>();
private final Object loadClassesMonitor = new Object();
private final Map<String, URL> entries = new HashMap<String, URL>();
private final Object entriesMonitor = new Object();
private final Map<String, Enumeration<String>> entryPaths = new HashMap<String, Enumeration<String>>();
private final Object entryPathsMonitor = new Object();
private final Map<Object, Boolean> permissions = new HashMap<Object, Boolean>();
private final Object permissionsMonitor = new Object();
private final Map<String, URL> resource = new HashMap<String, URL>();
private final Object resourceMonitor = new Object();
private final Map<String, Enumeration<URL>> resources = new HashMap<String, Enumeration<URL>>();
private final Object resourcesMonitor = new Object();
private volatile FindEntriesDelegate findEntriesDelegate;
private final Object findEntriesMonitor = new Object();
private final List<StubServiceReference<Object>> registeredServices = new ArrayList<StubServiceReference<Object>>();
private final Object registeredServicesMonitor = new Object();
private final List<StubServiceReference<Object>> servicesInUse = new ArrayList<StubServiceReference<Object>>();
private final Object servicesInUseMonitor = new Object();
private volatile UpdateDelegate updateDelegate;
private final Object updateDelegateMonitor = new Object();
/**
* Creates a new {@link StubServiceRegistration} and sets its initial state. This constructor sets
* <code>bundleId</code> to <code>1</code>, <code>symbolicName</code> to
* <code>org.eclipse.virgo.test.stubs.testbundle</code>, <code>version</code> to <code>0.0.0</code>, and
* <code>location</code> to <code>/</code>.
*/
public StubBundle() {
this(DEFAULT_SYMBOLIC_NAME, DEFAULT_VERSION);
}
/**
* Creates a new {@link StubServiceRegistration} and sets its initial state. This constructor sets
* <code>bundleId</code> to <code>1</code> and <code>location</code> to <code>/</code>.
*
* @param symbolicName The symbolic name of this bundle
* @param version The version of this bundle
*/
public StubBundle(String symbolicName, Version version) {
this(DEFAULT_BUNDLE_ID, symbolicName, version, DEFAULT_LOCATION);
}
/**
* Creates a new {@link StubBundle} and sets its initial state
*
* @param bundleId The id of this bundle
* @param symbolicName The symbolic name of this bundle
* @param version The version of this bundle
* @param location The location of this bundle
*/
public StubBundle(Long bundleId, String symbolicName, Version version, String location) {
assertNotNull(bundleId, "bundleId");
assertNotNull(symbolicName, "symbolicName");
assertNotNull(version, "version");
assertNotNull(location, "location");
this.bundleId = bundleId;
this.symbolicName = symbolicName;
this.version = version;
this.location = location;
this.bundleContext = new StubBundleContext(this);
this.state = STARTING;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) {
synchronized (this.findEntriesMonitor) {
if (this.findEntriesDelegate != null) {
return this.findEntriesDelegate.findEntries(path, filePattern, recurse);
}
return null;
}
}
/**
* Sets the {@link FindEntriesDelegate} to use for all subsequent calls to
* {@link #findEntries(String, String, boolean)}.
*
* @param delegate the delegate to use
*
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle setFindEntriesDelegate(FindEntriesDelegate delegate) {
synchronized (this.findEntriesMonitor) {
this.findEntriesDelegate = delegate;
return this;
}
}
/**
* {@inheritDoc}
*/
public BundleContext getBundleContext() {
synchronized (this.bundleContextMonitor) {
return this.bundleContext;
}
}
/**
* Sets the {@link BundleContext} to return for all subsequent calls to {@link #getBundleContext()}.
*
* @param bundleContext The @{link BundleContext} to return
*
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle setBundleContext(StubBundleContext bundleContext) {
assertNotNull(bundleContext, "bundleContext");
synchronized (bundleContextMonitor) {
this.bundleContext = bundleContext;
return this;
}
}
/**
* {@inheritDoc}
*/
public long getBundleId() {
return this.bundleId;
}
/**
* {@inheritDoc}
*/
public URL getEntry(String path) {
synchronized (this.entriesMonitor) {
return this.entries.get(path);
}
}
/**
* Adds a mapping from a path to a {@link URL} for all subsequent calls to {@link #getEntry(String)}.
*
* @param path The path to map from
* @param url The {@link URL} to map to
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addEntry(String path, URL url) {
synchronized (this.entriesMonitor) {
this.entries.put(path, url);
return this;
}
}
/**
* {@inheritDoc}
*/
public Enumeration<String> getEntryPaths(String path) {
synchronized (this.entryPathsMonitor) {
return this.entryPaths.get(path);
}
}
/**
* Adds a mapping from a path to a {@link Enumeration} for all subsequent calls to {@link #getEntryPaths(String)}.
*
* @param path The path to map from
* @param paths The {@link Enumeration} to map to
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addEntryPaths(String path, Enumeration<String> paths) {
synchronized (this.entryPathsMonitor) {
this.entryPaths.put(path, paths);
return this;
}
}
/**
* {@inheritDoc}
*/
public Dictionary<String, String> getHeaders() {
synchronized (this.headersMonitor) {
return shallowCopy(this.headers);
}
}
/**
* Adds a header mapping for all subsequent calls to {@link #getHeaders()}.
*
* @param key The key to map from
* @param value The value to map to
*
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addHeader(String key, String value) {
synchronized (this.headersMonitor) {
this.headers.put(key, value);
return this;
}
}
/**
* {@inheritDoc}
*/
public Dictionary<String, String> getHeaders(String locale) {
synchronized (this.localizedHeadersMonitor) {
return shallowCopy(this.localizedHeaders);
}
}
/**
* Sets the localized headers to return for all subsequent calls to {@link #getHeaders(String)}.
*
* @param dictionary The headers to return
*
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle setLocalizedHeaders(Dictionary<String, String> dictionary) {
synchronized (this.localizedHeadersMonitor) {
this.localizedHeaders = dictionary;
return this;
}
}
/**
* {@inheritDoc}
*/
public long getLastModified() {
synchronized (this.lastModifiedMonitor) {
return this.lastModified;
}
}
/**
* Sets the last modified date to return for all subsequent calls to {@link #getLastModified()}. A call to any other
* modifying method will update this.
*
* @param lastModified The new last modified date
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle setLastModified(long lastModified) {
synchronized (this.lastModifiedMonitor) {
this.lastModified = lastModified;
return this;
}
}
/**
* {@inheritDoc}
*/
public String getLocation() {
return this.location;
}
/**
* {@inheritDoc}
*/
public ServiceReference<?>[] getRegisteredServices() {
synchronized (this.registeredServicesMonitor) {
if (this.registeredServices.isEmpty()) {
return null;
}
return this.registeredServices.toArray(new ServiceReference[this.registeredServices.size()]);
}
}
/**
* Adds a {@link ServiceReference} for all subsequent calls to {@link #getRegisteredServices()}.
*
* @param serviceReference The {@link ServiceReference}
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addRegisteredService(StubServiceReference<Object> serviceReference) {
synchronized (this.registeredServicesMonitor) {
serviceReference.setBundle(this);
this.registeredServices.add(serviceReference);
return this;
}
}
/**
* {@inheritDoc}
*/
public URL getResource(String name) {
synchronized (this.resourceMonitor) {
return this.resource.get(name);
}
}
/**
* Adds a mapping from a name to a {@link URL} for all subsequent calls to {@link #getResource(String)}.
*
* @param name The name to map from
* @param url The {@link URL} to map to
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addResource(String name, URL url) {
synchronized (this.resourceMonitor) {
this.resource.put(name, url);
return this;
}
}
/**
* {@inheritDoc}
*/
public Enumeration<URL> getResources(String name) throws IOException {
synchronized (this.resourcesMonitor) {
return this.resources.get(name);
}
}
/**
* Adds a mapping from a name to a {@link Enumeration} for all subsequent calls to {@link #getResources(String)}.
*
* @param name The name to map from
* @param resources The {@link Enumeration} to map to
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addResources(String name, Enumeration<URL> resources) {
synchronized (this.resourcesMonitor) {
this.resources.put(name, resources);
return this;
}
}
/**
* {@inheritDoc}
*/
public ServiceReference<?>[] getServicesInUse() {
synchronized (this.servicesInUseMonitor) {
if (this.servicesInUse.isEmpty()) {
return null;
}
return this.servicesInUse.toArray(new ServiceReference[this.servicesInUse.size()]);
}
}
/**
* Adds a {@link ServiceReference} for all subsequent calls to {@link #getServicesInUse()}.
*
* @param serviceReference The {@link ServiceReference}
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addServiceInUse(StubServiceReference<Object> serviceReference) {
synchronized (this.servicesInUseMonitor) {
serviceReference.addUsingBundles(this);
this.servicesInUse.add(serviceReference);
return this;
}
}
/**
* {@inheritDoc}
*/
public int getState() {
synchronized (this.stateMonitor) {
return this.state;
}
}
/**
* Sets the state to return for all subsequent calls to {@link #getState()}. A call to any state modifying method
* will change this value.
*
* @param state The state to return
*
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle setState(int state) {
synchronized (this.stateMonitor) {
this.state = state;
return this;
}
}
/**
* {@inheritDoc}
*/
public String getSymbolicName() {
return this.symbolicName;
}
/**
* {@inheritDoc}
*/
public Version getVersion() {
return this.version;
}
/**
* {@inheritDoc}
*/
public boolean hasPermission(Object permission) {
synchronized (this.permissionsMonitor) {
if (this.permissions.containsKey(permission)) {
return this.permissions.get(permission);
}
return true;
}
}
/**
* Adds a mapping from a permission to a {@link Boolean} of whether that permission is valid for all subsequent
* calls to {@link #hasPermission(Object)}.
*
* @param permission the permission to add
* @param hasPermission whether this permission is valid
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addPermission(Object permission, boolean hasPermission) {
synchronized (this.permissionsMonitor) {
this.permissions.put(permission, hasPermission);
return this;
}
}
/**
* {@inheritDoc}
*/
public Class<?> loadClass(String name) throws ClassNotFoundException {
synchronized (this.loadClassesMonitor) {
if (this.loadClasses.containsKey(name)) {
return this.loadClasses.get(name);
}
throw new ClassNotFoundException("'" + name + "' cannot be loaded");
}
}
/**
* Adds a mapping from a class name to a {@link Class} for all subsequent calls to {@link #loadClass(String)}.
*
* @param name The name of the class to map from
* @param clazz The class to map to
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle addLoadClass(String name, Class<?> clazz) {
synchronized (this.loadClassesMonitor) {
this.loadClasses.put(name, clazz);
return this;
}
}
/**
* {@inheritDoc}
*/
public void start() throws BundleException {
start(0);
}
/**
* {@inheritDoc}
*/
public void start(int options) throws BundleException {
synchronized (this.stateMonitor) {
if (this.getState() == ACTIVE) {
return;
}
setState(RESOLVED);
setState(STARTING);
setState(ACTIVE);
}
}
/**
* {@inheritDoc}
*/
public void stop() throws BundleException {
stop(0);
}
/**
* {@inheritDoc}
*/
public void stop(int options) throws BundleException {
synchronized (this.stateMonitor) {
if (this.getState() != ACTIVE) {
return;
}
setState(STOPPING);
synchronized (this.registeredServicesMonitor) {
for (StubServiceReference<Object> serviceReference : this.registeredServices) {
serviceReference.setBundle(null);
}
this.registeredServices.clear();
}
synchronized (this.servicesInUseMonitor) {
for (StubServiceReference<Object> serviceReference : this.servicesInUse) {
serviceReference.removeUsingBundles(this);
}
this.servicesInUse.clear();
}
setState(RESOLVED);
}
}
/**
* {@inheritDoc}
*/
public void uninstall() throws BundleException {
synchronized (this.stateMonitor) {
int initialState = getState();
stopBundleIfNeeded(initialState);
setState(UNINSTALLED);
}
}
/**
* {@inheritDoc}
*/
public void update() throws BundleException {
synchronized (this.updateDelegateMonitor) {
synchronized (this.stateMonitor) {
int initialState = getState();
stopBundleIfNeeded(initialState);
if (this.updateDelegate != null) {
this.updateDelegate.update(this);
}
setState(INSTALLED);
startBundleIfNeeded(initialState);
}
}
}
/**
* {@inheritDoc}
*/
public void update(InputStream in) throws BundleException {
update();
}
/**
* Sets the {@link UpdateDelegate} to use for all subsequent calls to {@link #update()} or
* {@link #update(InputStream)}.
*
* @param delegate the delegate to use
*
* @return <code>this</code> instance of the {@link StubBundle}
*/
public StubBundle setUpdateDelegate(UpdateDelegate delegate) {
synchronized (this.updateDelegateMonitor) {
this.updateDelegate = delegate;
return this;
}
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + bundleId.hashCode();
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
StubBundle other = (StubBundle) obj;
if (!bundleId.equals(other.bundleId)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return String.format("id: %d, symbolic name: %s, version: %s, state: %d", this.bundleId, this.symbolicName, this.version, this.state);
}
private void startBundleIfNeeded(int initialState) throws BundleException {
if (initialState == ACTIVE) {
this.start();
}
}
private void stopBundleIfNeeded(int initialState) throws BundleException {
if (initialState == Bundle.ACTIVE || initialState == Bundle.STARTING || initialState == Bundle.STOPPING) {
this.stop();
}
}
/**
* {@inheritDoc}
*/
public int compareTo(Bundle o) {
int bundleIdCompare = Long.valueOf(o.getBundleId()).compareTo(this.bundleId);
if (bundleIdCompare != 0) {
return bundleIdCompare;
}
int symbolicNameCompare = o.getSymbolicName().compareTo(this.symbolicName);
if (symbolicNameCompare != 0) {
return symbolicNameCompare;
}
int bundleVersionCompare = o.getVersion().compareTo(this.version);
if (bundleVersionCompare != 0) {
return bundleVersionCompare;
}
int bundleLocationCompare = o.getLocation().compareTo(this.location);
if (bundleLocationCompare != 0) {
return bundleLocationCompare;
}
return 0;
}
/**
* {@inheritDoc}
*/
public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) {
return new HashMap<X509Certificate, List<X509Certificate>>();
}
/**
* {@inheritDoc}
*/
public <A> A adapt(Class<A> type) {
return null;
}
/**
* {@inheritDoc}
*/
public File getDataFile(String filename) {
return null;
}
}