/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.felix.dm.runtime; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.felix.dm.Component; import org.apache.felix.dm.DependencyManager; import org.osgi.framework.Bundle; import org.osgi.service.packageadmin.PackageAdmin; /** * This class parses service descriptors generated by the annotation bnd processor. * The descriptors are located under META-INF/dependencymanager directory. Such files are actually * referenced by a specific "DependendencyManager-Component" manifest header. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class DependencyManagerRuntime { /** * Map between bundles and their corresponding DependencyManager objects used to create bundle's components. * Notice that we can safely use this map without synchronization because we are relying on the new DM4 thread * model which serialize all component events safely. */ private final Map<Bundle, DependencyManager> m_managers = new HashMap<Bundle, DependencyManager>(); /** * Parser used to scan component descriptors defined in bundles meta data. */ private final DescriptorParser m_parser; /** * We use the PackageAdmin service to allow support for annotations in fragment bundles. */ private volatile PackageAdmin m_packageAdmin; /** * Our constructor. We'll initialize here our DM component builders. */ public DependencyManagerRuntime() { // Instantiates our descriptor parser, and register our service builders into it. m_parser = new DescriptorParser(); m_parser.addBuilder(new ComponentBuilder()); m_parser.addBuilder(new AspectServiceBuilder()); m_parser.addBuilder(new AdapterServiceBuilder()); m_parser.addBuilder(new BundleAdapterServiceBuilder()); m_parser.addBuilder(new FactoryConfigurationAdapterServiceBuilder()); m_parser.addBuilder(new ResourceAdapterServiceBuilder()); } /** * Return our Object Composition (the Activator will inject dependencies into it) */ protected Object[] getComposition() { return new Object[] { this, Log.instance() }; } /** * Starts our Service (at this point, we have been injected with our bundle context, as well * as with our log service. We'll listen to bundle start/stop events (we implement the * SynchronousBundleListener interface). */ protected void start() { Log.instance().info("Starting Dependency Manager annotation runtime."); } /** * Stops our service. We'll stop all activated DependencyManager services. */ protected void stop() { Log.instance().info("Runtime: stopping services"); for (DependencyManager dm : m_managers.values()) { List<Component> services = new ArrayList<Component>(dm.getComponents()); for (Component service : services) { dm.remove(service); } } m_managers.clear(); } /** * Load the DM descriptors from the started bundle. We also check possible fragments * attached to the bundle, which might also contain some DM descriptors. * @param bundle the started bundle which contains a DependencyManager-Component header */ protected void bundleStarted(Bundle bundle) { Log.instance().info("Scanning started bundle %s", bundle.getSymbolicName()); List<URL> descriptorURLs = new ArrayList<URL>(); collectDescriptors(bundle, descriptorURLs); Bundle[] fragments = m_packageAdmin.getFragments(bundle); if (fragments != null) { for (Bundle fragment : fragments) { collectDescriptors(fragment, descriptorURLs); } } for (URL descriptorURL : descriptorURLs) { loadDescriptor(bundle, descriptorURL); } } /** * Unregisters all services for a stopping bundle. * @param b */ protected void bundleStopped(Bundle b) { Log.instance().info("Runtime: Removing services from stopping bundle: %s", b.getSymbolicName()); DependencyManager dm = m_managers.remove(b); if (dm != null) { List<Component> services = new ArrayList<Component>(dm.getComponents()); for (Component service : services) { Log.instance().info("Runtime: Removing service: %s", service); dm.remove(service); } } } /** * Collect all descriptors found from a given bundle, including its possible attached fragments. * @param bundle a started bundle containing some DM descriptors * @param out the list of descriptors' URLS found from the started bundle, as well as from possibly * attached fragments. */ private void collectDescriptors(Bundle bundle, List<URL> out) { String descriptorPaths = (String) bundle.getHeaders().get("DependencyManager-Component"); if (descriptorPaths == null) { return; } for (String descriptorPath : descriptorPaths.split(",")) { URL descriptorURL = bundle.getEntry(descriptorPath); if (descriptorURL == null) { Log.instance() .error("Runtime: " + "DependencyManager component descriptor not found: %s", descriptorPath); continue; } out.add(descriptorURL); } } /** * Load a DependencyManager component descriptor from a given bundle. * @param b * @param descriptorURL */ private void loadDescriptor(Bundle b, URL descriptorURL) { Log.instance().debug("Parsing descriptor %s from bundle %s", descriptorURL, b.getSymbolicName()); BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(descriptorURL.openStream())); DependencyManager dm = m_managers.get(b); if (dm == null) { dm = new DependencyManager(b.getBundleContext()); m_managers.put(b, dm); } m_parser.parse(in, b, dm); } catch (Throwable t) { Log.instance().error("Runtime: Error while parsing descriptor %s from bundle %s", t, descriptorURL, b.getSymbolicName()); } finally { if (in != null) { try { in.close(); } catch (IOException ignored) { } } } } }