blob: b6994f80f22176e1ec80180e0deeabc708f733d5 [file] [log] [blame]
/*
* 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)
{
}
}
}
}
}