/*
* #%L
* Nazgul Project: nazgul-core-osgi-test
* %%
* Copyright (C) 2010 - 2017 jGuru Europe AB
* %%
* Licensed under the jGuru Europe AB license (the "License"), based
* on Apache License, Version 2.0; you may not use this file except
* in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
*
* 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.
* #L%
*
*/
package se.jguru.nazgul.test.osgi;
import org.apache.felix.framework.FilterImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
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.ServiceEvent;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceObjects;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import se.jguru.nazgul.core.algorithms.api.Validate;
import se.jguru.nazgul.core.algorithms.event.api.producer.AbstractEventProducer;
import se.jguru.nazgul.core.algorithms.event.api.producer.EventConsumerCallback;
import se.jguru.nazgul.core.algorithms.event.api.producer.EventProducer;
import se.jguru.nazgul.test.osgi.event.BundleListenerAdapter;
import se.jguru.nazgul.test.osgi.event.ServiceListenerAdapter;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
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 java.util.Properties;
/**
* A mock implementation of an OSGi BundleContext, to be used in unit tests only.
*
* @author <a href="mailto:lj@jguru.se">Lennart Jörelid</a>, jGuru Europe AB
*/
@SuppressWarnings({"rawtypes", "unchecked", "serial"})
public class MockBundleContext implements BundleContext {
// Internal state
private Map<String, ServiceReference> serviceReferences = new HashMap<String, ServiceReference>();
private Map<ServiceReference, Object> services = new HashMap<ServiceReference, Object>();
private Map<Long, Bundle> bundles = new HashMap<Long, Bundle>();
private EventProducer<BundleListenerAdapter> bundleListeners =
new AbstractEventProducer<BundleListenerAdapter>("testBundleListeners", BundleListenerAdapter.class) {
};
private EventProducer<ServiceListenerAdapter> serviceListeners =
new AbstractEventProducer<ServiceListenerAdapter>("testServiceListeners", ServiceListenerAdapter.class) {
};
private final Bundle bundle;
private Properties properties;
public MockBundleContext(final Bundle bundle) {
// Assign internal state
this.bundle = bundle;
this.bundles.put(bundle.getBundleId(), bundle);
this.properties = new Properties();
properties.put(Constants.FRAMEWORK_VERSION, bundle.getVersion().toString());
}
/**
* Fires a BundleEvent to all registered bundleListeners, with the given
* [Event]ConsumerEventCallback implementation to define what happens
* within the event.
*
* @param consumerCallback The callback definition for each registered BundleListenerAdapter.
*/
public void fireBundleEvent(final EventConsumerCallback<BundleListenerAdapter> consumerCallback) {
bundleListeners.notifyConsumers(consumerCallback);
}
/**
* Fires an OSGi ServiceEvent to all registered bundleListeners, with the
* given visitor pattern ConsumerEventCallback implementatino to define what
* happens within the event.
*
* @param consumerCallback The callback definition for each registered ServiceListenerAdapter.
*/
public void fireServiceEvent(final EventConsumerCallback<ServiceListenerAdapter> consumerCallback) {
serviceListeners.notifyConsumers(consumerCallback);
}
/**
* Searches only properties defined within the MockBundleContext.
*/
@Override
public String getProperty(final String key) {
return properties.getProperty(key);
}
/**
* Retrieves the internal Bundle.
*/
@Override
public Bundle getBundle() {
return bundle;
}
/**
* Installs a bundle from the specified {@code InputStream} object.
* If the specified {@code InputStream} is {@code null}, the Framework must
* create the {@code InputStream} from which to read the bundle by
* interpreting, in an implementation dependent manner, the specified
* {@code location}.
* The specified {@code location} identifier will be used as the identity of
* the bundle. Every installed bundle is uniquely identified by its location
* identifier which is typically in the form of a URL.
* The following steps are required to install a bundle:
* <ol>
* <li>If a bundle containing the same location identifier is already
* installed, the {@code Bundle} object for that bundle is returned.
* <li>The bundle's content is read from the input stream. If this fails, a
* {@link org.osgi.framework.BundleException} is thrown.
* <li>The bundle's associated resources are allocated. The associated
* resources minimally consist of a unique identifier and a persistent
* storage area if the platform has file system support. If this step fails,
* a {@code BundleException} is thrown.
* <li>The bundle's state is set to {@code INSTALLED}.
* <li>A bundle event of type {@link org.osgi.framework.BundleEvent#INSTALLED} is fired.
* <li>The {@code Bundle} object for the newly or previously installed
* bundle is returned.
* </ol>
* <b>Postconditions, no exceptions thrown </b>
* <ul>
* <li>{@code getState()} in { {@code INSTALLED}, {@code RESOLVED}
* }.
* <li>Bundle has a unique ID.
* </ul>
* <b>Postconditions, when an exception is thrown </b>
* <ul>
* <li>Bundle is not installed. If there was an existing bundle for the
* specified location, then that bundle must still be in the state it was
* prior to calling this method.</li>
* </ul>
*
* @param location The location identifier of the bundle to install.
* @param input The {@code InputStream} object from which this bundle will
* be read or {@code null} to indicate the Framework must create the
* input stream from the specified location identifier. The input
* stream must always be closed when this method completes, even if
* an exception is thrown.
* @return The {@code Bundle} object of the installed bundle.
* @throws org.osgi.framework.BundleException If the installation failed. BundleException types
* thrown by this method include: {@link org.osgi.framework.BundleException#READ_ERROR}
* , {@link org.osgi.framework.BundleException#DUPLICATE_BUNDLE_ERROR},
* {@link org.osgi.framework.BundleException#MANIFEST_ERROR}, and
* {@link org.osgi.framework.BundleException#REJECTED_BY_HOOK}.
* @throws SecurityException If the caller does not have the appropriate
* {@code AdminPermission[installed bundle,LIFECYCLE]}, and the Java
* Runtime Environment supports permissions.
* @throws IllegalStateException If this BundleContext is no longer valid.
*/
@Override
public Bundle installBundle(final String location, final InputStream input) throws BundleException {
throw new UnsupportedOperationException("Use installBundle(Bundle) instead. This is a MockBundleContext.");
}
/**
* Installs a bundle from the specified {@code location} identifier.
* This method performs the same function as calling
* {@link #installBundle(String, java.io.InputStream)} with the specified
* {@code location} identifier and a {@code null} InputStream.
*
* @param location The location identifier of the bundle to install.
* @return The {@code Bundle} object of the installed bundle.
* @throws org.osgi.framework.BundleException If the installation failed. BundleException types
* thrown by this method include: {@link org.osgi.framework.BundleException#READ_ERROR}
* , {@link org.osgi.framework.BundleException#DUPLICATE_BUNDLE_ERROR},
* {@link org.osgi.framework.BundleException#MANIFEST_ERROR}, and
* {@link org.osgi.framework.BundleException#REJECTED_BY_HOOK}.
* @throws SecurityException If the caller does not have the appropriate
* {@code AdminPermission[installed bundle,LIFECYCLE]}, and the Java
* Runtime Environment supports permissions.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #installBundle(String, java.io.InputStream)
*/
@Override
public Bundle installBundle(final String location) throws BundleException {
throw new UnsupportedOperationException("Use installBundle(Bundle) instead. This is a MockBundleContext.");
}
/**
* Mock implementation of installing a provided bundle
* A bundle event of type {@link org.osgi.framework.BundleEvent#INSTALLED} is fired.
*
* @param bundle the bundle to install
* @return the given bundle
*/
public Bundle installBundle(final Bundle bundle) {
// Register the bundle
bundles.put(bundle.getBundleId(), bundle);
// Notify all listeners
final EventConsumerCallback<BundleListenerAdapter> consumerCallback
= new EventConsumerCallback<BundleListenerAdapter>() {
@Override
public void onEvent(final BundleListenerAdapter eventConsumer) {
eventConsumer.bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bundle));
}
};
fireBundleEvent(consumerCallback);
// All done.
return bundle;
}
/**
* Returns the bundle with the specified identifier.
*
* @param id The identifier of the bundle to retrieve.
* @return A {@code Bundle} object or {@code null} if the identifier does
* not match any installed bundle.
*/
@Override
public Bundle getBundle(final long id) {
return bundles.get(id);
}
/**
* Returns a list of all installed bundles.
* This method returns a list of all bundles installed in the OSGi
* environment at the time of the call to this method. However, since the
* Framework is a very dynamic environment, bundles can be installed or
* uninstalled at anytime.
*
* @return An array of {@code Bundle} objects, one object per installed
* bundle.
*/
@Override
public Bundle[] getBundles() {
return bundles.values().toArray(new Bundle[bundles.size()]);
}
/**
* Adds the specified {@code ServiceListener} object with the specified
* {@code filter} to the context bundle's list of listeners. See
* {@link org.osgi.framework.Filter} for a description of the filter syntax.
* {@code ServiceListener} objects are notified when a service has a
* lifecycle state change.
* If the context bundle's list of listeners already contains a listener
* {@code l} such that {@code (l==listener)}, then this method replaces that
* listener's filter (which may be {@code null}) with the specified one
* (which may be {@code null}).
* The listener is called if the filter criteria is met. To filter based
* upon the class of the service, the filter should reference the
* {@link org.osgi.framework.Constants#OBJECTCLASS} property. If {@code filter} is {@code null}
* , all services are considered to match the filter.
* When using a {@code filter}, it is possible that the {@code ServiceEvent}
* s for the complete lifecycle of a service will not be delivered to the
* listener. For example, if the {@code filter} only matches when the
* property {@code x} has the value {@code 1}, the listener will not be
* called if the service is registered with the property {@code x} not set
* to the value {@code 1}. Subsequently, when the service is modified
* setting property {@code x} to the value {@code 1}, the filter will match
* and the listener will be called with a {@code ServiceEvent} of type
* {@code MODIFIED}. Thus, the listener will not be called with a
* {@code ServiceEvent} of type {@code REGISTERED}.
* If the Java Runtime Environment supports permissions, the
* {@code ServiceListener} object will be notified of a service event only
* if the bundle that is registering it has the {@code ServicePermission} to
* get the service using at least one of the named classes the service was
* registered under.
*
* @param listener The {@code ServiceListener} object to be added.
* @param filter The filter criteria.
* @throws org.osgi.framework.InvalidSyntaxException If {@code filter} contains an invalid
* filter string that cannot be parsed.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see org.osgi.framework.ServiceEvent
* @see org.osgi.framework.ServiceListener
* @see org.osgi.framework.ServicePermission
*/
@Override
public void addServiceListener(final ServiceListener listener, final String filter) throws InvalidSyntaxException {
addServiceListener(listener);
}
/**
* Adds the specified {@code ServiceListener} object to the context bundle's
* list of listeners.
* This method is the same as calling
* {@code BundleContext.addServiceListener(ServiceListener listener,
* String filter)} with {@code filter} set to {@code null}.
*
* @param listener The {@code ServiceListener} object to be added.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #addServiceListener(org.osgi.framework.ServiceListener, String)
*/
@Override
public void addServiceListener(final ServiceListener listener) {
// Create a cluster-unique id
final ServiceListenerAdapter adapter = new ServiceListenerAdapter(
"ServiceListener_" + listener.hashCode(),
ServiceListenerAdapter.class,
listener);
// Add the ServiceListener to the known collection of consumers.
serviceListeners.addConsumer(adapter);
}
/**
* Removes the specified {@code ServiceListener} object from the context
* bundle's list of listeners.
* If {@code listener} is not contained in this context bundle's list of
* listeners, this method does nothing.
*
* @param listener The {@code ServiceListener} to be removed.
* @throws IllegalStateException If this BundleContext is no longer valid.
*/
@Override
public void removeServiceListener(final ServiceListener listener) {
// Re-create the ServiceListenerAdapter instance.
final ServiceListenerAdapter adapter = new ServiceListenerAdapter(
"ServiceListener_" + listener.hashCode(),
ServiceListenerAdapter.class,
listener);
// Remove the ServiceListener from the known collection of consumers.
serviceListeners.removeConsumer(adapter.getClusterId());
}
/**
* Adds the specified {@code BundleListener} object to the context bundle's
* list of listeners if not already present. BundleListener objects are
* notified when a bundle has a lifecycle state change.
* If the context bundle's list of listeners already contains a listener
* {@code l} such that {@code (l==listener)}, this method does nothing.
*
* @param listener The {@code BundleListener} to be added.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @throws SecurityException If listener is a
* {@code SynchronousBundleListener} and the caller does not have
* the appropriate {@code AdminPermission[context bundle,LISTENER]},
* and the Java Runtime Environment supports permissions.
* @see org.osgi.framework.BundleEvent
* @see org.osgi.framework.BundleListener
*/
@Override
public void addBundleListener(final BundleListener listener) {
final BundleListenerAdapter toAdd = new BundleListenerAdapter(
"BundleListener_" + listener.hashCode(),
BundleListenerAdapter.class,
listener);
bundleListeners.addConsumer(toAdd);
}
/**
* Removes the specified {@code BundleListener} object from the context
* bundle's list of listeners.
* If {@code listener} is not contained in the context bundle's list of
* listeners, this method does nothing.
*
* @param listener The {@code BundleListener} object to be removed.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @throws SecurityException If listener is a
* {@code SynchronousBundleListener} and the caller does not have
* the appropriate {@code AdminPermission[context bundle,LISTENER]},
* and the Java Runtime Environment supports permissions.
*/
@Override
public void removeBundleListener(final BundleListener listener) {
final BundleListenerAdapter toRemove = new BundleListenerAdapter(
"BundleListener_" + listener.hashCode(),
BundleListenerAdapter.class,
listener);
bundleListeners.removeConsumer(toRemove.getClusterId());
}
/**
* Adds the specified {@code FrameworkListener} object to the context
* bundle's list of listeners if not already present. FrameworkListeners are
* notified of general Framework events.
* If the context bundle's list of listeners already contains a listener
* {@code l} such that {@code (l==listener)}, this method does nothing.
*
* @param listener The {@code FrameworkListener} object to be added.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see org.osgi.framework.FrameworkEvent
* @see org.osgi.framework.FrameworkListener
*/
@Override
public void addFrameworkListener(final FrameworkListener listener) {
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* Removes the specified {@code FrameworkListener} object from the context
* bundle's list of listeners.
* If {@code listener} is not contained in the context bundle's list of
* listeners, this method does nothing.
*
* @param listener The {@code FrameworkListener} object to be removed.
* @throws IllegalStateException If this BundleContext is no longer valid.
*/
@Override
public void removeFrameworkListener(final FrameworkListener listener) {
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* Registers the specified service object with the specified properties
* under the specified class names into the Framework. A
* {@code ServiceRegistration} object is returned. The
* {@code ServiceRegistration} object is for the private use of the bundle
* registering the service and should not be shared with other bundles. The
* registering bundle is defined to be the context bundle. Other bundles can
* locate the service by using either the {@link #getServiceReferences} or
* {@link #getServiceReference} method.
* A bundle can register a service object that implements the
* {@link org.osgi.framework.ServiceFactory} interface to have more flexibility in providing
* service objects to other bundles.
* The following steps are required to register a service:
* <ol>
* <li>If {@code service} is not a {@code ServiceFactory}, an
* {@code IllegalArgumentException} is thrown if {@code service} is not an
* {@code instanceof} all the specified class names.
* <li>The Framework adds the following service properties to the service
* properties from the specified {@code Dictionary} (which may be {@code null}):
* A property named {@link org.osgi.framework.Constants#SERVICE_ID} identifying the
* registration number of the service. A property named {@link org.osgi.framework.Constants#OBJECTCLASS}
* containing all the specified classes.Properties with these names in the specified {@code Dictionary} will be
* ignored.
* <li>The service is added to the Framework service registry and may now be
* used by other bundles.
* <li>A service event of type {@link org.osgi.framework.ServiceEvent#REGISTERED} is fired.
* <li>A {@code ServiceRegistration} object for this registration is
* returned.
* </ol>
*
* @param classes The class names under which the service can be located.
* The class names in this array will be stored in the service's
* properties under the key {@link org.osgi.framework.Constants#OBJECTCLASS}.
* @param service The service object or a {@code ServiceFactory} object.
* @param properties The properties for this service. The keys in the
* properties object must all be {@code String} objects. See
* {@link org.osgi.framework.Constants} for a list of standard service property keys.
* Changes should not be made to this object after calling this
* method. To update the service's properties the
* {@link org.osgi.framework.ServiceRegistration#setProperties} method must be called.
* The set of properties may be {@code null} if the service has no
* properties.
* @return A {@code ServiceRegistration} object for use by the bundle
* registering the service to update the service's properties or to
* unregister the service.
* @throws IllegalArgumentException If one of the following is true:
* <ul>
* <li>{@code service} is {@code null}. <li>{@code service} is not a
* {@code ServiceFactory} object and is not an instance of all the
* named classes in {@code clazzes}. <li> {@code properties}
* contains case variants of the same key name.
* </ul>
* @throws SecurityException If the caller does not have the
* {@code ServicePermission} to register the service for all the
* named classes and the Java Runtime Environment supports
* permissions.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see org.osgi.framework.ServiceRegistration
* @see org.osgi.framework.ServiceFactory
*/
@Override
public ServiceRegistration registerService(@NotNull @Size(min = 1) final String[] classes,
final Object service,
final Dictionary properties) {
// Check sanity
Validate.notEmpty(classes, "classes");
Validate.notNull(service, "service");
Validate.notNull(properties, "properties");
properties.put(Constants.OBJECTCLASS, classes);
String serviceID = (String) properties.get(Constants.SERVICE_ID);
if (serviceID == null) {
serviceID = "Service_" + classes[0];
}
Integer serviceRanking = (Integer) properties.get(Constants.SERVICE_RANKING);
if (serviceRanking == null) {
serviceRanking = 0;
}
final MockServiceReference reference = new MockServiceReference(bundle,
Arrays.asList(classes),
properties,
serviceID,
serviceRanking);
services.put(reference, service);
for (final String clazz : classes) {
serviceReferences.put(clazz, reference);
}
fireServiceEvent(new EventConsumerCallback<ServiceListenerAdapter>() {
@Override
public void onEvent(ServiceListenerAdapter eventConsumer) {
eventConsumer.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, reference));
}
});
// All done.
return new MockServiceRegistration(reference, this, properties);
}
/**
* Unregisters the supplied ServiceReference instance from this MockBundleContext.
*
* @param reference The ServiceReference to unregister.
*/
public void unregister(final ServiceReference reference) {
final String[] classes = (String[]) reference.getProperty(Constants.OBJECTCLASS);
for (final String clazz : classes) {
serviceReferences.remove(clazz);
}
services.remove(reference);
fireServiceEvent(new EventConsumerCallback<ServiceListenerAdapter>() {
@Override
public void onEvent(ServiceListenerAdapter eventConsumer) {
eventConsumer.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, reference));
}
});
}
/**
* Registers the specified service object with the specified properties
* under the specified class name with the Framework.
* This method is otherwise identical to
* {@link #registerService(String[], Object, java.util.Dictionary)} and is provided as
* a convenience when {@code service} will only be registered under a single
* class name. Note that even in this case the value of the service's
* {@link org.osgi.framework.Constants#OBJECTCLASS} property will be an array of string, rather
* than just a single string.
*
* @param clazz The class name under which the service can be located.
* @param service The service object or a {@code ServiceFactory} object.
* @param properties The properties for this service.
* @return A {@code ServiceRegistration} object for use by the bundle
* registering the service to update the service's properties or to
* unregister the service.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #registerService(String[], Object, java.util.Dictionary)
*/
@Override
public ServiceRegistration<?> registerService(final String clazz,
final Object service,
final Dictionary<String, ?> properties) {
return registerService(new String[]{clazz}, service, properties);
}
/**
* Registers the specified service object with the specified properties
* under the specified class name with the Framework.
* This method is otherwise identical to
* {@link #registerService(String[], Object, java.util.Dictionary)} and is provided as
* a convenience when {@code service} will only be registered under a single
* class name. Note that even in this case the value of the service's
* {@link org.osgi.framework.Constants#OBJECTCLASS} property will be an array of string, rather
* than just a single string.
*
* @param <S> Type of Service.
* @param clazz The class name under which the service can be located.
* @param service The service object or a {@code ServiceFactory} object.
* @param properties The properties for this service.
* @return A {@code ServiceRegistration} object for use by the bundle
* registering the service to update the service's properties or to
* unregister the service.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #registerService(String[], Object, java.util.Dictionary)
* @since 1.6
*/
@Override
public <S> ServiceRegistration<S> registerService(final Class<S> clazz,
final S service,
final Dictionary<String, ?> properties) {
return registerService(new String[]{clazz.getName()}, service, properties);
}
/**
* Returns an array of {@code ServiceReference} objects. The returned array
* of {@code ServiceReference} objects contains services that were
* registered under the specified class, match the specified filter
* expression, and the packages for the class names under which the services
* were registered match the context bundle's packages as defined in
* {@link org.osgi.framework.ServiceReference#isAssignableTo(org.osgi.framework.Bundle, String)}.
* The list is valid at the time of the call to this method. However since
* the Framework is a very dynamic environment, services can be modified or
* unregistered at any time.
* The specified {@code filter} expression is used to select the registered
* services whose service properties contain keys and values which satisfy
* the filter expression. See {@link org.osgi.framework.Filter} for a description of the filter
* syntax. If the specified {@code filter} is {@code null}, all registered
* services are considered to match the filter. If the specified
* {@code filter} expression cannot be parsed, an
* {@link org.osgi.framework.InvalidSyntaxException} will be thrown with a human readable
* message where the filter became unparsable.
* The result is an array of {@code ServiceReference} objects for all
* services that meet all of the following conditions:
* <ul>
* <li>If the specified class name, {@code clazz}, is not {@code null}, the
* service must have been registered with the specified class name. The
* complete list of class names with which a service was registered is
* available from the service's {@link org.osgi.framework.Constants#OBJECTCLASS objectClass}
* property.
* <li>If the specified {@code filter} is not {@code null}, the filter
* expression must match the service.
* <li>If the Java Runtime Environment supports permissions, the caller must
* have {@code ServicePermission} with the {@code GET} action for at least
* one of the class names under which the service was registered.
* <li>For each class name with which the service was registered, calling
* {@link org.osgi.framework.ServiceReference#isAssignableTo(org.osgi.framework.Bundle, String)} with the context
* bundle and the class name on the service's {@code ServiceReference}
* object must return {@code true}
* </ul>
*
* @param clazz The class name with which the service was registered or
* {@code null} for all services.
* @param filter The filter expression or {@code null} for all services.
* @return An array of {@code ServiceReference} objects or {@code null} if
* no services are registered which satisfy the search.
* @throws org.osgi.framework.InvalidSyntaxException If the specified {@code filter} contains
* an invalid filter expression that cannot be parsed.
* @throws IllegalStateException If this BundleContext is no longer valid.
*/
@Override
public ServiceReference<?>[] getServiceReferences(final String clazz,
final String filter)
throws InvalidSyntaxException {
Validate.isTrue(filter == null,
"Filtering not implemented in MockBundleContext::getServiceReferences.");
final List<ServiceReference<?>> tmp = new ArrayList<ServiceReference<?>>();
if (clazz == null) {
for (ServiceReference<?> current : serviceReferences.values()) {
tmp.add(current);
}
} else {
final ServiceReference serviceReference = serviceReferences.get(clazz);
if (serviceReference != null) {
tmp.add(serviceReference);
}
}
ServiceReference<?>[] toReturn = new ServiceReference<?>[tmp.size()];
for (int i = 0; i < toReturn.length; i++) {
toReturn[i] = tmp.get(i);
}
return toReturn.length == 0 ? null : toReturn;
}
/**
* Returns an array of {@code ServiceReference} objects. The returned array
* of {@code ServiceReference} objects contains services that were
* registered under the specified class and match the specified filter
* expression.
* The list is valid at the time of the call to this method. However since
* the Framework is a very dynamic environment, services can be modified or
* unregistered at any time.
* The specified {@code filter} expression is used to select the registered
* services whose service properties contain keys and values which satisfy
* the filter expression. See {@link org.osgi.framework.Filter} for a description of the filter
* syntax. If the specified {@code filter} is {@code null}, all registered
* services are considered to match the filter. If the specified
* {@code filter} expression cannot be parsed, an
* {@link org.osgi.framework.InvalidSyntaxException} will be thrown with a human readable
* message where the filter became unparsable.
* The result is an array of {@code ServiceReference} objects for all
* services that meet all of the following conditions:
* <ul>
* <li>If the specified class name, {@code clazz}, is not {@code null}, the
* service must have been registered with the specified class name. The
* complete list of class names with which a service was registered is
* available from the service's {@link org.osgi.framework.Constants#OBJECTCLASS objectClass}
* property.
* <li>If the specified {@code filter} is not {@code null}, the filter
* expression must match the service.
* <li>If the Java Runtime Environment supports permissions, the caller must
* have {@code ServicePermission} with the {@code GET} action for at least
* one of the class names under which the service was registered.
* </ul>
*
* @param clazz The class name with which the service was registered or
* {@code null} for all services.
* @param filter The filter expression or {@code null} for all services.
* @return An array of {@code ServiceReference} objects or {@code null} if
* no services are registered which satisfy the search.
* @throws org.osgi.framework.InvalidSyntaxException If the specified {@code filter} contains
* an invalid filter expression that cannot be parsed.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @since 1.3
*/
@Override
public ServiceReference<?>[] getAllServiceReferences(final String clazz,
final String filter)
throws InvalidSyntaxException {
return getServiceReferences(clazz, filter);
}
/**
* Returns a {@code ServiceReference} object for a service that implements
* and was registered under the specified class.
* The returned {@code ServiceReference} object is valid at the time of the
* call to this method. However as the Framework is a very dynamic
* environment, services can be modified or unregistered at any time.
* This method is the same as calling
* {@link #getServiceReferences(String, String)} with a {@code null} filter
* expression and then finding the reference with the highest priority. It
* is provided as a convenience for when the caller is interested in any
* service that implements the specified class.
* If multiple such services exist, the service with the highest priority is
* selected. This priority is defined as the service reference with the
* highest ranking (as specified in its {@link org.osgi.framework.Constants#SERVICE_RANKING}
* property) is returned.
* If there is a tie in ranking, the service with the lowest service ID (as
* specified in its {@link org.osgi.framework.Constants#SERVICE_ID} property); that is, the
* service that was registered first is returned.
*
* @param clazz The class name with which the service was registered.
* @return A {@code ServiceReference} object, or {@code null} if no services
* are registered which implement the named class.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #getServiceReferences(String, String)
*/
@Override
public ServiceReference<?> getServiceReference(final String clazz) {
return serviceReferences.get(clazz);
}
/**
* Returns a {@code ServiceReference} object for a service that implements
* and was registered under the specified class.
* The returned {@code ServiceReference} object is valid at the time of the
* call to this method. However as the Framework is a very dynamic
* environment, services can be modified or unregistered at any time.
* This method is the same as calling
* {@link #getServiceReferences(Class, String)} with a {@code null} filter
* expression. It is provided as a convenience for when the caller is
* interested in any service that implements the specified class.
* If multiple such services exist, the service with the highest ranking (as
* specified in its {@link org.osgi.framework.Constants#SERVICE_RANKING} property) is returned.
* If there is a tie in ranking, the service with the lowest service ID (as
* specified in its {@link org.osgi.framework.Constants#SERVICE_ID} property); that is, the
* service that was registered first is returned.
*
* @param <S> Type of Service.
* @param clazz The class name with which the service was registered.
* @return A {@code ServiceReference} object, or {@code null} if no services
* are registered which implement the named class.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #getServiceReferences(Class, String)
* @since 1.6
*/
@Override
public <S> ServiceReference<S> getServiceReference(final Class<S> clazz) {
return (ServiceReference<S>) getServiceReference(clazz.getName());
}
/**
* Returns a collection of {@code ServiceReference} objects. The returned
* collection of {@code ServiceReference} objects contains services that
* were registered under the specified class, match the specified filter
* expression, and the packages for the class names under which the services
* were registered match the context bundle's packages as defined in
* {@link org.osgi.framework.ServiceReference#isAssignableTo(org.osgi.framework.Bundle, String)}.
* The collection is valid at the time of the call to this method. However
* since the Framework is a very dynamic environment, services can be
* modified or unregistered at any time.
* The specified {@code filter} expression is used to select the registered
* services whose service properties contain keys and values which satisfy
* the filter expression. See {@link org.osgi.framework.Filter} for a description of the filter
* syntax. If the specified {@code filter} is {@code null}, all registered
* services are considered to match the filter. If the specified
* {@code filter} expression cannot be parsed, an
* {@link org.osgi.framework.InvalidSyntaxException} will be thrown with a human readable
* message where the filter became unparsable.
* The result is a collection of {@code ServiceReference} objects for all
* services that meet all of the following conditions:
* <ul>
* <li>If the specified class name, {@code clazz}, is not {@code null}, the
* service must have been registered with the specified class name. The
* complete list of class names with which a service was registered is
* available from the service's {@link org.osgi.framework.Constants#OBJECTCLASS objectClass}
* property.
* <li>If the specified {@code filter} is not {@code null}, the filter
* expression must match the service.
* <li>If the Java Runtime Environment supports permissions, the caller must
* have {@code ServicePermission} with the {@code GET} action for at least
* one of the class names under which the service was registered.
* <li>For each class name with which the service was registered, calling
* {@link org.osgi.framework.ServiceReference#isAssignableTo(org.osgi.framework.Bundle, String)} with the context
* bundle and the class name on the service's {@code ServiceReference}
* object must return {@code true}
* </ul>
*
* @param <S> Type of Service
* @param clazz The class name with which the service was registered. Must
* not be {@code null}.
* @param filter The filter expression or {@code null} for all services.
* @return A collection of {@code ServiceReference} objects. May be empty if
* no services are registered which satisfy the search.
* @throws org.osgi.framework.InvalidSyntaxException If the specified {@code filter} contains
* an invalid filter expression that cannot be parsed.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @since 1.6
*/
@Override
public <S> Collection<ServiceReference<S>> getServiceReferences(final Class<S> clazz, final String filter)
throws InvalidSyntaxException {
throw new UnsupportedOperationException("Not yet implemented.");
}
/**
* Returns the service object referenced by the specified
* {@code ServiceReference} object.
* A bundle's use of a service is tracked by the bundle's use count of that
* service. Each time a service's service object is returned by
* {@link #getService(org.osgi.framework.ServiceReference)} the context bundle's use count for
* that service is incremented by one. Each time the service is released by
* {@link #ungetService(org.osgi.framework.ServiceReference)} the context bundle's use count
* for that service is decremented by one.
* When a bundle's use count for a service drops to zero, the bundle should
* no longer use that service.
* This method will always return {@code null} when the service associated
* with this {@code reference} has been unregistered.
* The following steps are required to get the service object:
* <ol>
* <li>If the service has been unregistered, {@code null} is returned.
* <li>If the context bundle's use count for the service is currently zero
* and the service was registered with an object implementing the
* {@code ServiceFactory} interface, the
* {@link org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration)} method is
* called to create a service object for the context bundle. If the service
* object returned by the {@code ServiceFactory} object is {@code null}, not
* an {@code instanceof} all the classes named when the service was
* registered or the {@code ServiceFactory} object throws an exception or
* will be recursively called for the context bundle, {@code null} is
* returned and a Framework event of type {@link org.osgi.framework.FrameworkEvent#ERROR}
* containing a {@link org.osgi.framework.ServiceException} describing the error is fired. <br>
* This service object is cached by the Framework. While the context
* bundle's use count for the service is greater than zero, subsequent calls
* to get the services's service object for the context bundle will return
* the cached service object.
* <li>The context bundle's use count for this service is incremented by
* one.
* <li>The service object for the service is returned.
* </ol>
*
* @param <S> Type of Service.
* @param reference A reference to the service.
* @return A service object for the service associated with
* {@code reference} or {@code null} if the service is not
* registered, the service object returned by a
* {@code ServiceFactory} does not implement the classes under which
* it was registered or the {@code ServiceFactory} threw an
* exception.
* @throws SecurityException If the caller does not have the
* {@code ServicePermission} to get the service using at least one
* of the named classes the service was registered under and the
* Java Runtime Environment supports permissions.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @throws IllegalArgumentException If the specified
* {@code ServiceReference} was not created by the same framework
* instance as this {@code BundleContext}.
* @see #ungetService(org.osgi.framework.ServiceReference)
* @see org.osgi.framework.ServiceFactory
*/
@Override
public <S> S getService(final ServiceReference<S> reference) {
return (S) services.get(reference);
}
/**
* Releases the service object referenced by the specified
* {@code ServiceReference} object. If the context bundle's use count for
* the service is zero, this method returns {@code false}. Otherwise, the
* context bundle's use count for the service is decremented by one.
* The service's service object should no longer be used and all references
* to it should be destroyed when a bundle's use count for the service drops
* to zero.
* The following steps are required to unget the service object:
* <ol>
* <li>If the context bundle's use count for the service is zero or the
* service has been unregistered, {@code false} is returned.
* <li>The context bundle's use count for this service is decremented by
* one.
* <li>If the context bundle's use count for the service is currently zero
* and the service was registered with a {@code ServiceFactory} object, the
* {@link org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, Object)}
* method is called to release the service object for the context bundle.
* <li>{@code true} is returned.
* </ol>
*
* @param reference A reference to the service to be released.
* @return {@code false} if the context bundle's use count for the service
* is zero or if the service has been unregistered; {@code true}
* otherwise.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @throws IllegalArgumentException If the specified
* {@code ServiceReference} was not created by the same framework
* instance as this {@code BundleContext}.
* @see #getService
* @see org.osgi.framework.ServiceFactory
*/
@Override
public boolean ungetService(final ServiceReference<?> reference) {
throw new UnsupportedOperationException("Not yet implemented.");
}
/**
* Creates a {@code File} object for a file in the persistent storage area
* provided for the bundle by the Framework. This method will return
* {@code null} if the platform does not have file system support.
* A {@code File} object for the base directory of the persistent storage
* area provided for the context bundle by the Framework can be obtained by
* calling this method with an empty string as {@code filename}.
* If the Java Runtime Environment supports permissions, the Framework will
* ensure that the bundle has the {@code java.io.FilePermission} with
* actions {@code read},{@code write},{@code delete} for all files
* (recursively) in the persistent storage area provided for the context
* bundle.
*
* @param filename A relative name to the file to be accessed.
* @return A {@code File} object that represents the requested file or
* {@code null} if the platform does not have file system support.
* @throws IllegalStateException If this BundleContext is no longer valid.
*/
@Override
public File getDataFile(final String filename) {
throw new UnsupportedOperationException("Not yet implemented.");
}
/**
* Creates a {@code Filter} object. This {@code Filter} object may be used
* to match a {@code ServiceReference} object or a {@code Dictionary}
* object.
* If the filter cannot be parsed, an {@link org.osgi.framework.InvalidSyntaxException} will be
* thrown with a human readable message where the filter became unparsable.
*
* @param filter The filter string.
* @return A {@code Filter} object encapsulating the filter string.
* @throws org.osgi.framework.InvalidSyntaxException If {@code filter} contains an invalid
* filter string that cannot be parsed.
* @throws NullPointerException If {@code filter} is null.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see "Framework specification for a description of the filter string syntax."
* @see org.osgi.framework.FrameworkUtil#createFilter(String)
* @since 1.1
*/
@Override
public Filter createFilter(final String filter) throws InvalidSyntaxException {
return new FilterImpl(filter);
}
/**
* Returns the bundle with the specified location.
*
* @param location The location of the bundle to retrieve.
* @return A {@code Bundle} object or {@code null} if the location does not
* match any installed bundle.
* @since 1.6
*/
@Override
public Bundle getBundle(final String location) {
return bundle;
}
/**
* Registers the specified service factory object with the specified
* properties under the name of the specified class with the Framework.
* <p>
* <p>
* This method is otherwise identical to
* {@link #registerService(Class, Object, Dictionary)} and is provided to
* return a type safe {@code ServiceRegistration} when registering a
* {@link ServiceFactory}.
*
* @param clazz The class under whose name the service can be located.
* @param factory The {@code ServiceFactory} object.
* @param properties The properties for this service.
* @return A {@code ServiceRegistration} object for use by the bundle
* registering the service to update the service's properties or to
* unregister the service.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @see #registerService(Class, Object, Dictionary)
* @since 1.8
*/
@Override
public <S> ServiceRegistration<S> registerService(final Class<S> clazz, final ServiceFactory<S> factory, final Dictionary<String, ?> properties) {
return null;
}
/**
* Returns the {@link ServiceObjects} object for the service referenced by
* the specified {@code ServiceReference} object.
* <p>
* <p>
* The {@link ServiceObjects} object can be used to obtain multiple service
* objects for services with {@link Constants#SCOPE_PROTOTYPE prototype}
* scope.
* <p>
* <p>
* For services with {@link Constants#SCOPE_SINGLETON singleton} or
* {@link Constants#SCOPE_BUNDLE bundle} scope, the
* {@link ServiceObjects#getService()} method behaves the same as the
* {@link #getService(ServiceReference)} method and the
* {@link ServiceObjects#ungetService(Object)} method behaves the same as
* the {@link #ungetService(ServiceReference)} method. That is, only one,
* use-counted service object is available from the {@link ServiceObjects}
* object.
* <p>
* <p>
* This method will always return {@code null} when the service associated
* with the specified {@code reference} has been unregistered.
*
* @param reference A reference to the service.
* @return A {@link ServiceObjects} object for the service associated with
* the specified {@code reference} or {@code null} if the service is
* not registered.
* @throws SecurityException If the caller does not have the
* {@code ServicePermission} to get the service using at least one
* of the named classes the service was registered under and the
* Java Runtime Environment supports permissions.
* @throws IllegalStateException If this BundleContext is no longer valid.
* @throws IllegalArgumentException If the specified
* {@code ServiceReference} was not created by the same framework
* instance as this {@code BundleContext}.
* @see PrototypeServiceFactory
* @since 1.8
*/
@Override
public <S> ServiceObjects<S> getServiceObjects(final ServiceReference<S> reference) {
return null;
}
}