/*
* Copyright (C) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package interactivespaces.osgi.service;
import interactivespaces.resource.Version;
import interactivespaces.service.Service;
import interactivespaces.service.ServiceRegistry;
import interactivespaces.service.SupportedService;
import interactivespaces.system.InteractiveSpacesEnvironment;
import interactivespaces.util.resource.ManagedResource;
import interactivespaces.util.resource.ManagedResources;
import com.google.common.collect.Lists;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* A base class for creating OSGi BundleActivator subclasses for Interactive Spaces services and other OSGi bundles from
* Interactive Spaces.
*
* @author Keith M. Hughes
*/
public abstract class InteractiveSpacesServiceOsgiBundleActivator implements BundleActivator {
/**
* All OSGi service registrations from this bundle.
*/
private final List<ServiceRegistration<?>> osgiServiceRegistrations = Lists.newArrayList();
/**
* All services registered by this bundle.
*/
private final List<Service> registeredServices = Lists.newArrayList();
/**
* OSGi service tracker for the interactive spaces environment.
*/
private MyServiceTracker<InteractiveSpacesEnvironment> interactiveSpacesEnvironmentTracker;
/**
* OSGi bundle context for this bundle.
*/
private BundleContext bundleContext;
/**
* A collection of managed resources.
*/
private ManagedResources managedResources;
/**
* All service trackers we have.
*/
private final Map<String, MyServiceTracker<?>> serviceTrackers = new HashMap<String, MyServiceTracker<?>>();
/**
* Object to give lock for putting this bundle's services together.
*/
private final Object serviceLock = new Object();
@Override
public void start(BundleContext context) throws Exception {
this.bundleContext = context;
interactiveSpacesEnvironmentTracker = newMyServiceTracker(InteractiveSpacesEnvironment.class.getName());
// Get the registrations from the subclass.
onStart();
// Open all the trackers.
for (MyServiceTracker<?> tracker : serviceTrackers.values()) {
tracker.open();
}
}
/**
* The bundle is starting. Add any requests for services.
*/
protected void onStart() {
// Default is to do nothing.
}
@Override
public void stop(BundleContext context) throws Exception {
onStop();
unregisterOsgiServices();
unregisterInteractiveSpacesServices();
if (managedResources != null) {
managedResources.shutdownResourcesAndClear();
}
// Close all the trackers.
for (MyServiceTracker<?> tracker : serviceTrackers.values()) {
tracker.close();
}
serviceTrackers.clear();
}
/**
* Bundle is shutting down. Do any extra cleanup.
*/
protected void onStop() {
// Default is do nothing.
}
/**
* Unregister all OSGi-registered services.
*/
private void unregisterOsgiServices() {
for (ServiceRegistration<?> service : osgiServiceRegistrations) {
service.unregister();
}
osgiServiceRegistrations.clear();
}
/**
* Unregister and shutdown all services registered with interactive spaces.
*/
private void unregisterInteractiveSpacesServices() {
ServiceRegistry serviceRegistry = interactiveSpacesEnvironmentTracker.getMyService().getServiceRegistry();
for (Service service : registeredServices) {
serviceRegistry.unregisterService(service);
if (SupportedService.class.isAssignableFrom(service.getClass())) {
((SupportedService) service).shutdown();
}
}
registeredServices.clear();
}
/**
* Register an InteractiveSpaces service as a new OSGi service.
*
* @param name
* name for the OSGi service
* @param service
* the Interactive Spaces service
*/
public void registerOsgiService(String name, Service service) {
osgiServiceRegistrations.add(bundleContext.registerService(name, service, null));
}
/**
* Register a generic OSGi framework service.
*
* @param name
* name for the OSGi service
* @param service
* the service to be registered
*/
public void registerOsgiFrameworkService(String name, Object service) {
osgiServiceRegistrations.add(bundleContext.registerService(name, service, null));
}
/**
* Got another reference from a dependency.
*/
protected void gotAnotherReference() {
synchronized (serviceLock) {
// If missing any of our needed services, punt.
for (MyServiceTracker<?> tracker : serviceTrackers.values()) {
if (tracker.getMyService() == null) {
return;
}
}
managedResources = new ManagedResources(interactiveSpacesEnvironmentTracker.getMyService().getLog());
allRequiredServicesAvailable();
managedResources.startupResources();
}
}
/**
* All required services are available.
*/
protected abstract void allRequiredServicesAvailable();
/**
* Register a new service with IS.
*
* @param service
* the service to be registered
*/
public void registerNewInteractiveSpacesService(Service service) {
try {
interactiveSpacesEnvironmentTracker.getMyService().getServiceRegistry().registerService(service);
if (SupportedService.class.isAssignableFrom(service.getClass())) {
((SupportedService) service).startup();
}
registeredServices.add(service);
} catch (Exception e) {
interactiveSpacesEnvironmentTracker.getMyService().getLog()
.error(String.format("Error while starting up service %s", service.getName()), e);
}
}
/**
* Add in a managed resource.
*
* @param resource
* the managed resource to add
*/
public void addManagedResource(ManagedResource resource) {
managedResources.addResource(resource);
}
/**
* Get the version of the bundle.
*
* @return the bundle version
*/
public String getBundleVersion() {
return bundleContext.getBundle().getVersion().toString();
}
/**
* Get the version of the bundle.
*
* @return the version of the bundle
*/
public Version getBundleVersionAsVersion() {
return Version.parseVersion(getBundleVersion());
}
/**
* Create a new service tracker.
*
* @param serviceName
* name of the service class
* @param <T>
* class being tracked by the service tracker
*
* @return the service tracker
*/
protected <T> MyServiceTracker<T> newMyServiceTracker(String serviceName) {
MyServiceTracker<T> tracker = new MyServiceTracker<T>(bundleContext, serviceName);
serviceTrackers.put(serviceName, tracker);
return tracker;
}
/**
* Get the bundle context for the bundle.
*
* @return the bundle context
*/
public BundleContext getBundleContext() {
return bundleContext;
}
/**
* Get the service tracker for the Interactive Spaces environment.
*
* @return the service tracker
*/
public MyServiceTracker<InteractiveSpacesEnvironment> getInteractiveSpacesEnvironmentTracker() {
return interactiveSpacesEnvironmentTracker;
}
/**
* An OSGi service tracking class.
*
* @param <T>
* the class of the service being tracked
*
* @author Keith M. Hughes
*/
public final class MyServiceTracker<T> extends ServiceTracker {
/**
* The reference for the service object being waited for.
*/
private final AtomicReference<T> serviceReference = new AtomicReference<T>();
/**
* Construct a service tracker.
*
* @param context
* bundle context the tracker is running under
* @param serviceName
* the name of the service
*/
public MyServiceTracker(BundleContext context, String serviceName) {
super(context, serviceName, null);
}
@Override
public Object addingService(ServiceReference reference) {
@SuppressWarnings("unchecked")
T service = (T) super.addingService(reference);
if (serviceReference.compareAndSet(null, service)) {
gotAnotherReference();
}
return service;
}
/**
* Get the service needed.
*
* @return the service, or {@code null} if it hasn't been obtained yet.
*/
public T getMyService() {
return serviceReference.get();
}
}
}