| /******************************************************************************* |
| * Copyright (c) 2005, 2006 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.ds; |
| |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.equinox.ds.model.ComponentDescription; |
| import org.eclipse.equinox.ds.model.ComponentDescriptionCache; |
| import org.eclipse.equinox.ds.parser.XMLParserNotAvailableException; |
| import org.eclipse.equinox.ds.resolver.Resolver; |
| import org.eclipse.equinox.ds.tracker.BundleTracker; |
| import org.eclipse.equinox.ds.tracker.BundleTrackerCustomizer; |
| import org.eclipse.equinox.ds.workqueue.WorkDispatcher; |
| import org.eclipse.equinox.ds.workqueue.WorkQueue; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleActivator; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.SynchronousBundleListener; |
| import org.osgi.service.log.LogService; |
| import org.osgi.service.packageadmin.PackageAdmin; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * |
| * Main class for the SCR. This class will start the SCR bundle and begin |
| * processing other bundles. |
| * |
| * @version $Revision: 1.5 $ |
| */ |
| public class Activator implements BundleActivator, BundleTrackerCustomizer, WorkDispatcher, SynchronousBundleListener { |
| |
| public BundleContext context; |
| private FrameworkHook framework; |
| private ComponentDescriptionCache cache; |
| private BundleTracker bundleTracker; |
| public Resolver resolver; |
| private ServiceTracker packageAdminTracker; |
| public WorkQueue workQueue; |
| |
| private Hashtable bundleToComponentDescriptions; |
| private Hashtable bundleToLastModified; |
| |
| /** |
| * New Service Components (ComponentDescription)s are added |
| */ |
| public static final int ADD = 1; |
| |
| /** |
| * Start the SCR bundle. |
| * |
| * @param bundleContext BundleContext for SCR implementation. |
| */ |
| public void start(BundleContext bundleContext) { |
| this.context = bundleContext; |
| framework = new FrameworkHook(); |
| Log.init(context); |
| cache = new ComponentDescriptionCache(this); |
| bundleToComponentDescriptions = new Hashtable(); |
| bundleToLastModified = new Hashtable(); |
| |
| packageAdminTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null); |
| packageAdminTracker.open(); |
| |
| //notify this object when bundles enter (or exit) the Bundle.ACTIVE state |
| bundleTracker = new BundleTracker(context, Bundle.ACTIVE, this); |
| |
| workQueue = new WorkQueue("SCR Work Queue"); //$NON-NLS-1$ |
| workQueue.setDaemon(true); // make sure the work queue is daemon |
| resolver = new Resolver(this); |
| workQueue.start(); |
| bundleContext.addBundleListener(this); //track UNRESOLVED events |
| bundleTracker.open(); |
| } |
| |
| /** |
| * Stop the SCR bundle. |
| * |
| * @param bundleContext BundleContext for SCR implementation. |
| */ |
| public void stop(BundleContext bundleContext) { |
| |
| bundleTracker.close(); |
| |
| //process all remaining events in queue and then shut it down |
| workQueue.closeAndJoin(); |
| |
| resolver.dispose(); |
| packageAdminTracker.close(); |
| |
| //shut down cache (write to disk) |
| cache.dispose(); |
| |
| Log.dispose(); |
| |
| cache = null; |
| framework = null; |
| resolver = null; |
| bundleToComponentDescriptions = null; |
| bundleToLastModified = null; |
| this.context = null; |
| } |
| |
| /** |
| * Returns the bundle's Service-Component manifest header. If the bundle |
| * has header, then the bundle will be tracked. If not, null is returned |
| * and the bundle will not be tracked. |
| * |
| * If the bundle contains service components, parse the service component xml |
| * file(s) and create an {@link ComponentDescription ComponentDescription} object for every service |
| * component in the bundle. Add the {@link ComponentDescription ComponentDescriptions} to |
| * the queue to be sent to the resolver. |
| * |
| * @param bundle Candidate bundle to be tracked. |
| * @return the bundle's Service-Component manifest header or null if the |
| * bundle does not specify the header. |
| */ |
| public Object addingBundle(Bundle bundle) { |
| |
| List enableComponentDescriptions = new ArrayList(); |
| |
| PackageAdmin packageAdmin = (PackageAdmin) packageAdminTracker.getService(); |
| if (packageAdmin.getBundleType(bundle) != 0) { |
| return null; // don't process fragments. |
| } |
| |
| Long bundleID = new Long(bundle.getBundleId()); |
| |
| // get the bundle's last modified date |
| Long bundleLastModified = new Long(bundle.getLastModified()); |
| |
| // get the last saved value for the bundle's last modified date |
| Long bundleOldLastModified = (Long) bundleToLastModified.get(bundleID); |
| |
| try { |
| // compare the two and if changed ( or if first time ) update the maps |
| if ((!bundleLastModified.equals(bundleOldLastModified)) || (bundleOldLastModified == null)) { |
| |
| // get the BundleContext for this bundle (framework impl dependent) |
| BundleContext bundleContext = framework.getBundleContext(bundle); |
| |
| // get all ComponentDescriptions for this bundle |
| // throws XMLParserNotAvailableException if no XML parser |
| List componentDescriptions = cache.getComponentDescriptions(bundleContext); |
| |
| // update map of bundle to ComponentDescriptions |
| bundleToComponentDescriptions.put(bundleID, componentDescriptions); |
| |
| // update bundle:lastModifiedDate map |
| bundleToLastModified.put(bundleID, bundleLastModified); |
| |
| // for each CD in bundle set enable flag based on autoenable |
| |
| Iterator it = componentDescriptions.iterator(); |
| while (it.hasNext()) { |
| ComponentDescription componentDescription = (ComponentDescription) it.next(); |
| validate(componentDescription); |
| if (componentDescription.isAutoenable() && componentDescription.isValid()) { |
| componentDescription.setEnabled(true); |
| enableComponentDescriptions.add(componentDescription); |
| } |
| } |
| } |
| |
| // publish all CDs to be enabled, to resolver (add to the workqueue and |
| // publish event) |
| if (!enableComponentDescriptions.isEmpty()) { |
| workQueue.enqueueWork(this, ADD, enableComponentDescriptions); |
| } |
| |
| } catch (XMLParserNotAvailableException e) { |
| //cache.getComponentDescriptions(bundleContext); threw this exception |
| //because we needed an XML parser and we didn't have one |
| //when an XML parser becomes available this method will be called again |
| //log a message but still return the bundleId so this bundle will be tracked |
| Log.log(LogService.LOG_INFO, "Declarative Services waiting for XML parser for bundle " + bundleID); |
| } |
| return bundleID; |
| } |
| |
| /** |
| * Empty implementation. No work is needed for modifiedBundle. |
| * |
| * @param bundle |
| * @param object |
| */ |
| public void modifiedBundle(Bundle bundle, Object object) { |
| |
| Long bundleID = new Long(bundle.getBundleId()); |
| |
| // flush map |
| bundleToComponentDescriptions.remove(bundleID); |
| |
| // flush map |
| bundleToLastModified.remove(bundleID); |
| |
| } |
| |
| /** |
| * A bundle is going to an in-ACTIVE state. Dispose and remove it's service |
| * components from the system. Disposal is done synchronously so all of the |
| * service components have been disposed before this method returns. |
| * |
| * @param bundle Bundle becoming untracked. |
| * @param object Value returned by addingBundle. |
| */ |
| |
| public void removedBundle(Bundle bundle, Object object) { |
| |
| List disableComponentDescriptions = new ArrayList(); |
| Long bundleID = new Long(bundle.getBundleId()); |
| |
| // get CD's for this bundle |
| List ComponentDescriptions = (List) bundleToComponentDescriptions.get(new Long(bundle.getBundleId())); |
| if (ComponentDescriptions != null) { |
| Iterator it = ComponentDescriptions.iterator(); |
| |
| // for each CD in bundle |
| while (it.hasNext()) { |
| ComponentDescription ComponentDescription = (ComponentDescription) it.next(); |
| |
| // check if enabled && satisfied |
| if ((ComponentDescription.isEnabled())) { |
| |
| // add to disabled list |
| disableComponentDescriptions.add(ComponentDescription); |
| |
| // mark disabled |
| ComponentDescription.setEnabled(false); |
| } |
| } |
| } |
| |
| // remove the bundle from the lists/maps |
| bundleToComponentDescriptions.remove(bundleID); |
| bundleToLastModified.remove(bundleID); |
| |
| resolver.disableComponents(disableComponentDescriptions); |
| return; |
| } |
| |
| /** |
| * Called by Service Component code via ComponentContext |
| * |
| * Enable the component(s) and put them on the queue for the resolver. |
| * |
| * @param name The name of a component or <code>null</code> to indicate |
| * all components in the bundle. |
| * |
| * @param bundle The bundle which contains the Service Component to be |
| * enabled |
| */ |
| public void enableComponent(String name, Bundle bundle) { |
| |
| // get all ComponentDescriptions for this bundle |
| List componentDescriptions = (List) bundleToComponentDescriptions.get(new Long(bundle.getBundleId())); |
| |
| // Create the list of CD's to be enabled |
| List enableCDs = new ArrayList(); |
| |
| if (componentDescriptions != null) { |
| Iterator it = componentDescriptions.iterator(); |
| |
| // for each CD in list |
| while (it.hasNext()) { |
| ComponentDescription componentDescription = (ComponentDescription) it.next(); |
| validate(componentDescription); |
| |
| // if name is null then enable ALL Component Descriptions in |
| // this bundle |
| if (name == null) { |
| |
| // if CD is valid and is disabled then enable it |
| if (componentDescription.isValid() && !componentDescription.isEnabled()) { |
| |
| // add to list of CDs to enable |
| enableCDs.add(componentDescription); |
| |
| // set CD enabled |
| componentDescription.setEnabled(true); |
| } |
| } else { |
| if (componentDescription.getName().equals(name)) { |
| |
| // if CD is valid and is disabled then enable it |
| if (componentDescription.isValid() && !componentDescription.isEnabled()) { |
| |
| // add to list of CDs to enable |
| enableCDs.add(componentDescription); |
| |
| // set CD enabled |
| componentDescription.setEnabled(true); |
| } |
| } |
| } |
| // else it is either not valid or it is already enabled - do |
| // nothing |
| } |
| } |
| |
| // publish to resolver the list of CDs to enable |
| if (!enableCDs.isEmpty()) |
| workQueue.enqueueWork(this, ADD, enableCDs); |
| } |
| |
| /** |
| * Disable a Service Component - The specified component name must be in the |
| * bundle as this component. Called by Service Component via ComponentContext. |
| * |
| * Synchronously disable the component. All component configurations (component configurations) |
| * are disposed before this method returns. |
| * |
| * @param name The name of a component to disable |
| * |
| * @param bundle The bundle which contains the Service Component to be |
| * disabled |
| */ |
| |
| public void disableComponent(String name, Bundle bundle) { |
| |
| List disableComponentDescriptions = new ArrayList(); |
| |
| // Get the list of CDs for this bundle |
| List componentDescriptionsList = (List) bundleToComponentDescriptions.get(new Long(bundle.getBundleId())); |
| |
| if (componentDescriptionsList != null) { |
| Iterator it = componentDescriptionsList.iterator(); |
| |
| // for each ComponentDescription in list |
| while (it.hasNext()) { |
| ComponentDescription componentDescription = (ComponentDescription) it.next(); |
| |
| // find the ComponentDescription with the specified name |
| if (componentDescription.getName().equals(name)) { |
| |
| // if enabled then add to disabled list and mark disabled |
| if (componentDescription.isEnabled()) { |
| |
| disableComponentDescriptions.add(componentDescription); |
| |
| componentDescription.setEnabled(false); |
| |
| } |
| } |
| } |
| } |
| |
| // publish to resolver the list of CDs to disable |
| resolver.disableComponents(disableComponentDescriptions); |
| return; |
| } |
| |
| /** |
| * Put a job on the work queue to be done later (asynchronously) by the work |
| * queue thread. |
| * @param workAction currently only valid value is Main.ADD |
| * @param workObject work object to be acted upon |
| * @see org.eclipse.equinox.ds.workqueue.WorkDispatcher#dispatchWork(int, |
| * java.lang.Object) |
| */ |
| public void dispatchWork(int workAction, Object workObject) { |
| |
| List descriptions; |
| descriptions = (List) workObject; |
| switch (workAction) { |
| case ADD : |
| resolver.enableComponents(descriptions); |
| break; |
| } |
| } |
| |
| /** |
| * Validate the Component Description |
| * |
| * If error is found log and throw exception. |
| * |
| * @param cd to be validated |
| * @throws Throwable if fatal problem is found |
| */ |
| private void validate(ComponentDescription componentDescription) { |
| |
| if ((componentDescription.getFactory() != null) && (componentDescription.getService() != null) && (componentDescription.getService().isServicefactory())) { |
| componentDescription.setValid(false); |
| Log.log(1, "validate cd: ", new Throwable("invalid to specify both ComponentFactory and ServiceFactory")); |
| } else if ((componentDescription.isImmediate()) && (componentDescription.getService() != null) && (componentDescription.getService().isServicefactory())) { |
| componentDescription.setValid(false); |
| Log.log(1, "validate cd: ", new Throwable("invalid to specify both immediate and ServiceFactory")); |
| } else if ((componentDescription.isImmediate()) && (componentDescription.getFactory() != null)) { |
| componentDescription.setValid(false); |
| Log.log(1, "validate cd: ", new Throwable("invalid to specify both immediate and ComponentFactory")); |
| } else if ((!componentDescription.isImmediate()) && (componentDescription.getService() == null) && (componentDescription.getFactory() == null)) { |
| componentDescription.setValid(false); |
| Log.log(1, "validate cd: ", new Throwable("invalid set immediate to false and provide no Service")); |
| } else { |
| componentDescription.setValid(true); |
| } |
| |
| } |
| |
| /** |
| * SynchronousBundleListener - when a bundle we are tracking is UNRESOLVED, |
| * clear it's reflection cache. |
| */ |
| public void bundleChanged(BundleEvent event) { |
| if (event.getType() == BundleEvent.UNRESOLVED) { |
| |
| // get cached ComponentDescriptions for this bundle, if any |
| List componentDescriptions = cache.getCachedComponentDescriptions(event.getBundle()); |
| |
| Iterator itr = componentDescriptions.iterator(); |
| while(itr.hasNext()) { |
| ComponentDescription componentDescription = (ComponentDescription)itr.next(); |
| |
| componentDescription.clearReflectionMethods(); |
| } |
| } //end if event type == UNRESOLVED |
| } |
| |
| } |