blob: e787494000e40710fc38f6da81abea71b5f1bc3d [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.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
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.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
import org.eclipse.virgo.test.stubs.support.TrueFilter;
/**
* A stub testing implementation of {@link BundleContext} as defined in section 6.1.6 of the OSGi Service Platform Core
* Specification.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* Threadsafe
*
*/
public final class StubBundleContext implements BundleContext {
private final StubBundle bundle;
private final Object bundleMonitor = new Object();
private final List<BundleListener> bundleListeners = new ArrayList<BundleListener>();
private final Object bundleListenersMonitor = new Object();
private final List<FrameworkListener> frameworkListeners = new ArrayList<FrameworkListener>();
private final Object frameworkListenersMonitor = new Object();
private final List<ServiceListener> serviceListeners = new ArrayList<ServiceListener>();
private final Object serviceListenersMonitor = new Object();
private volatile long installedBundleId = Long.valueOf(2);
private final Map<Long, StubBundle> installedBundles = new HashMap<Long, StubBundle>();
private final Object installedBundlesMonitor = new Object();
private final List<StubServiceRegistration<Object>> serviceRegistrations = new ArrayList<StubServiceRegistration<Object>>();
private final Map<StubServiceReference<Object>, Object> services = new HashMap<StubServiceReference<Object>, Object>();
private final Object servicesMonitor = new Object();
private final Map<String, File> dataFiles = new HashMap<String, File>();
private final Object dataFilesMonitor = new Object();
private final Map<String, String> properties = new HashMap<String, String>();
private final Object propertiesMonitor = new Object();
private final Map<String, Filter> filters = new HashMap<String, Filter>();
private final Object filtersMonitor = new Object();
/**
* Creates a new {@link StubBundleContext} and sets its initial state
*/
public StubBundleContext() {
this(new StubBundle());
}
/**
* Creates a new {@link StubBundleContext} and sets its initial state
*
* @param bundle The context bundle
*/
public StubBundleContext(StubBundle bundle) {
assertNotNull(bundle, "bundle");
this.bundle = bundle;
}
/**
* {@inheritDoc}
*/
public void addBundleListener(BundleListener listener) {
synchronized (this.bundleListenersMonitor) {
this.bundleListeners.add(listener);
}
}
/**
* {@inheritDoc}
*/
public void addFrameworkListener(FrameworkListener listener) {
synchronized (this.frameworkListenersMonitor) {
this.frameworkListeners.add(listener);
}
}
/**
* {@inheritDoc}
*/
public void addServiceListener(ServiceListener listener) {
try {
addServiceListener(listener, null);
} catch (InvalidSyntaxException e) {
// In theory this exception can never be thrown
}
}
/**
* {@inheritDoc}
*/
public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
synchronized (this.serviceListenersMonitor) {
this.serviceListeners.add(listener);
}
}
/**
* {@inheritDoc}
*/
public Filter createFilter(String filter) throws InvalidSyntaxException {
synchronized (this.filtersMonitor) {
if (filter == null) {
throw new NullPointerException();
}
if (!this.filters.containsKey(filter)) {
throw new InvalidSyntaxException(String.format(
"You must first add a filter mapping for '%s' with the addFilter(String, Filter) method", filter), filter);
}
return this.filters.get(filter);
}
}
/**
* Adds a mapping from a filter string to a {@link Filter} for all subsequent calls to {@link #createFilter(String)}
*
* @param filterString The filterString to map from
* @param filter The {@link Filter} to map to
* @return <code>this</code> instance of the {@link StubBundleContext}
*/
public StubBundleContext addFilter(String filterString, Filter filter) {
synchronized (this.filtersMonitor) {
this.filters.put(filterString, filter);
return this;
}
}
/**
* Adds filters for all subsequent calls to {@link #createFilter(String)}
*
* @param filters The {@link Filter}s to add
* @return <code>this</code> instance of the {@link StubBundleContext}
*/
public StubBundleContext addFilter(Filter... filters) {
for (Filter filter : filters) {
addFilter(filter.toString(), filter);
}
return this;
}
/**
* {@inheritDoc}
*/
public ServiceReference<?>[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
return getServiceReferences(clazz, filter);
}
/**
* {@inheritDoc}
*/
public Bundle getBundle() {
synchronized (this.bundleMonitor) {
return this.bundle;
}
}
/**
* {@inheritDoc}
*/
public Bundle getBundle(long id) {
synchronized (this.installedBundlesMonitor) {
return this.installedBundles.get(id);
}
}
/**
* {@inheritDoc}
*/
public Bundle[] getBundles() {
synchronized (this.installedBundlesMonitor) {
return this.installedBundles.values().toArray(new Bundle[this.installedBundles.values().size()]);
}
}
/**
* Adds a collection of {@link Bundle}s to this {@link BundleContext} to be returned for all subsequent calls to
* {@link #getBundle(long)} or {@link #getBundles()}.
*
* @param bundles The bundles to add
* @return <code>this</code> instance of the {@link StubBundleContext}
*/
public StubBundleContext addInstalledBundle(StubBundle... bundles) {
synchronized (this.installedBundlesMonitor) {
for (StubBundle bundle : bundles) {
this.installedBundles.put(bundle.getBundleId(), bundle);
}
return this;
}
}
/**
* {@inheritDoc}
*/
public File getDataFile(String filename) {
synchronized (this.dataFilesMonitor) {
return this.dataFiles.get(filename);
}
}
/**
* Adds a mapping from a filename to a {@link File} for all subsequent calls to {@link #getDataFile(String)}.
*
* @param filename The filename to map from
* @param file The {@link File} to map to
* @return <code>this</code> instance of the {@link StubBundleContext}
*/
public StubBundleContext addDataFile(String filename, File file) {
synchronized (this.dataFilesMonitor) {
this.dataFiles.put(filename, file);
return this;
}
}
/**
* {@inheritDoc}
*/
public String getProperty(String key) {
synchronized (this.propertiesMonitor) {
return this.properties.get(key);
}
}
/**
* Adds a mapping from a key to a value for all subsequent calls to {@link #getProperty(String)}.
*
* @param key The key to map from
* @param value The value to map to
* @return <code>this</code> instance of the {@link StubBundleContext}
*/
public StubBundleContext addProperty(String key, String value) {
synchronized (this.propertiesMonitor) {
this.properties.put(key, value);
return this;
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <S> S getService(ServiceReference<S> reference) {
synchronized (this.servicesMonitor) {
if (serviceUnregistered(reference)) {
return null;
}
return (S) this.services.get(reference);
}
}
/**
* {@inheritDoc}
*/
public ServiceReference<?> getServiceReference(String clazz) {
ServiceReference<?>[] serviceReferences = null;
try {
serviceReferences = getServiceReferences(clazz, null);
} catch (InvalidSyntaxException e) {
// In theory this exception can never be thrown
}
if (serviceReferences == null) {
return null;
} else if (serviceReferences.length == 1) {
return serviceReferences[0];
} else {
Arrays.sort(serviceReferences);
return serviceReferences[serviceReferences.length - 1];
}
}
/**
* {@inheritDoc}
*/
public ServiceReference<?>[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
synchronized (this.servicesMonitor) {
List<ServiceReference<?>> candidateReferences = new ArrayList<ServiceReference<?>>();
Filter f = getFilter(filter);
for (ServiceReference<?> serviceReference : this.services.keySet()) {
String[] objectClasses = (String[]) serviceReference.getProperty(Constants.OBJECTCLASS);
if (f.match(serviceReference) && matchesClass(clazz, objectClasses)) {
candidateReferences.add(serviceReference);
}
}
if (candidateReferences.isEmpty()) {
return null;
} else {
return candidateReferences.toArray(new ServiceReference[candidateReferences.size()]);
}
}
}
private Filter getFilter(String filter) throws InvalidSyntaxException {
if (filter == null) {
return new TrueFilter();
} else {
return createFilter(filter);
}
}
private boolean matchesClass(String clazz, String[] objectClasses) {
if (clazz == null) {
return true;
}
for (String objectClass : objectClasses) {
if (clazz.equals(objectClass)) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
public Bundle installBundle(String location) throws BundleException {
StubBundle bundle = new StubBundle(this.installedBundleId++, location, Version.emptyVersion, location);
bundle.setState(Bundle.INSTALLED);
return bundle;
}
/**
* {@inheritDoc}
*/
public Bundle installBundle(String location, InputStream input) throws BundleException {
return installBundle(location);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public ServiceRegistration<?> registerService(String[] clazzes, Object service, Dictionary<String, ?> properties) {
StubServiceRegistration serviceRegistration = createServiceRegistration(clazzes, properties);
StubServiceReference serviceReference = createServiceReference(clazzes, service, serviceRegistration);
synchronized (this.servicesMonitor) {
this.serviceRegistrations.add(serviceRegistration);
this.services.put(serviceReference, service);
}
return serviceRegistration;
}
private StubServiceRegistration<Object> createServiceRegistration(String[] clazzes, Dictionary<String, ?> properties) {
StubServiceRegistration<Object> serviceRegistration = new StubServiceRegistration<Object>(this, clazzes);
serviceRegistration.setProperties(properties);
return serviceRegistration;
}
private <S> StubServiceReference<S> createServiceReference(String[] clazzes, Object service, StubServiceRegistration<S> serviceRegistration) {
StubServiceReference<S> serviceReference = new StubServiceReference<S>(serviceRegistration);
serviceRegistration.setServiceReference(serviceReference);
return serviceReference;
}
/**
* {@inheritDoc}
*/
public ServiceRegistration<?> registerService(String clazz, Object service, Dictionary<String, ?> properties) {
return registerService(new String[] { clazz }, service, properties);
}
/**
* Gets the collection of {@link ServiceRegistration}s for this {@link BundleContext}
*
* @return The collection of {@link ServiceRegistration}s
*/
public List<StubServiceRegistration<Object>> getServiceRegistrations() {
synchronized (this.servicesMonitor) {
return shallowCopy(this.serviceRegistrations);
}
}
/**
* Removes a collection of {@link ServiceRegistration}s from this {@link StubBundleContext} to be returned for all
* subsequent calls to {@link #getServiceReference(String)} and {@link #getServiceReferences(String, String)}.
*
* @param serviceRegistrations The service registrations to remove
* @return <code>this</code> instance of the {@link StubBundleContext}
*/
public StubBundleContext removeRegisteredService(ServiceRegistration<?>... serviceRegistrations) {
synchronized (this.servicesMonitor) {
this.serviceRegistrations.removeAll(Arrays.asList(serviceRegistrations));
for (ServiceRegistration registration: serviceRegistrations) {
this.services.remove(registration.getReference());
}
return this;
}
}
/**
* {@inheritDoc}
*/
public void removeBundleListener(BundleListener listener) {
synchronized (this.bundleListenersMonitor) {
this.bundleListeners.remove(listener);
}
}
/**
* {@inheritDoc}
*/
public void removeFrameworkListener(FrameworkListener listener) {
synchronized (this.frameworkListenersMonitor) {
this.frameworkListeners.remove(listener);
}
}
/**
* {@inheritDoc}
*/
public void removeServiceListener(ServiceListener listener) {
synchronized (this.serviceListenersMonitor) {
this.serviceListeners.remove(listener);
}
}
/**
* Gets the collection of {@link FrameworkListener}s for this {@link BundleContext}
*
* @return The collection of {@link BundleListener}s
*/
public List<FrameworkListener> getFrameworkListeners() {
synchronized (this.frameworkListenersMonitor) {
return shallowCopy(this.frameworkListeners);
}
}
/**
* Gets the collection of {@link BundleListener}s for this {@link BundleContext}
*
* @return The collection of {@link BundleListener}s
*/
public List<BundleListener> getBundleListeners() {
synchronized (this.bundleListenersMonitor) {
return shallowCopy(this.bundleListeners);
}
}
/**
* Gets the collection of {@link ServiceListener}s for this {@link BundleContext}
*
* @return The collection of {@link ServiceListener}s
*/
public List<ServiceListener> getServiceListeners() {
synchronized (this.serviceListenersMonitor) {
return shallowCopy(this.serviceListeners);
}
}
/**
* {@inheritDoc}
*/
public boolean ungetService(ServiceReference<?> reference) {
return !serviceUnregistered(reference);
}
/**
* @return Returns the context bundle for this {@link BundleContext}
*/
public StubBundle getContextBundle() {
synchronized (this.bundleMonitor) {
return this.bundle;
}
}
private boolean serviceUnregistered(ServiceReference<?> reference) {
return ((StubServiceReference<?>) reference).getServiceRegistration().getUnregistered();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <S> ServiceRegistration<S> registerService(Class<S> clazz, S service, Dictionary<String, ?> properties) {
return (ServiceRegistration<S>) registerService(clazz.getName(), service, properties);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <S> ServiceReference<S> getServiceReference(Class<S> clazz) {
return (ServiceReference<S>) getServiceReference(clazz.getName());
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz, String filter) throws InvalidSyntaxException {
Collection<ServiceReference<S>> references = new ArrayList<ServiceReference<S>>();
ServiceReference<S>[] matchingReferences = (ServiceReference<S>[]) getServiceReferences(clazz.getName(), filter);
if (matchingReferences != null) {
for (ServiceReference<S> reference : matchingReferences) {
references.add(reference);
}
}
return references;
}
/**
* {@inheritDoc}
*/
public Bundle getBundle(String location) {
return null;
}
}