blob: 5fc541010cae777d3e856026305318c67ed4114e [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.List;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.Component;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.log.LogService;
/**
* This class parses service descriptors generated by the annotation bnd processor.
* The descriptors are located under OSGI-INF/dependencymanager directory. Such files are actually
* referenced by a specific "DependendencyManager-Component" manifest header.
*/
public class DependencyManagerRuntime implements SynchronousBundleListener
{
private ConcurrentHashMap<Bundle, DependencyManager> m_managers = new ConcurrentHashMap<Bundle, DependencyManager>();
private static LogService m_log; // Injected
private BundleContext m_bctx; // Injected
private DescriptorParser m_parser;
/**
* 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()
{
// Register our log service.
Log.instance().setLogService(m_log);
// 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());
// Load already started bundles
for (Bundle b: m_bctx.getBundles())
{
if (b.getState() == Bundle.ACTIVE)
{
bundleChanged(new BundleEvent(Bundle.ACTIVE, b));
}
}
// Track bundles
m_bctx.addBundleListener(this);
}
/**
* Stops our service. We'll stop all activated DependencyManager services.
*/
@SuppressWarnings("unchecked")
protected void stop()
{
m_log.log(LogService.LOG_INFO, "Runtime: stopping services");
for (DependencyManager dm: m_managers.values())
{
List<Component> services = new ArrayList<Component>(dm.getServices());
for (Component service: services)
{
dm.remove(service);
}
}
m_managers.clear();
}
/**
* Register a LogService, so other components may use it
*/
protected void bind(LogService logService) {
m_log = logService;
Log.instance().setLogService(logService);
}
/**
* Handle a bundle event, and eventually parse started bundles.
*/
public void bundleChanged(BundleEvent event)
{
Bundle b = event.getBundle();
if (b.getState() == Bundle.ACTIVE)
{
bundleStarted(b);
}
else if (b.getState() == Bundle.STOPPING)
{
bundleStopping(b);
}
}
/**
* Checks if a started bundle have some DependencyManager descriptors
* referenced in the "DependencyManager-Component" OSGi header.
* @param b the started bundle.
*/
void bundleStarted(Bundle b)
{
String descriptorPaths = (String) b.getHeaders().get("DependencyManager-Component");
if (descriptorPaths == null)
{
return;
}
for (String descriptorPath: descriptorPaths.split(","))
{
URL descriptorURL = b.getEntry(descriptorPath);
if (descriptorURL == null)
{
m_log.log(LogService.LOG_ERROR, "Runtime: " +
"DependencyManager component descriptor not found: " + descriptorPath);
continue;
}
loadDescriptor(b, descriptorURL);
}
}
/**
* Unregisters all services for a stopping bundle.
* @param b
*/
@SuppressWarnings("unchecked")
private void bundleStopping(Bundle b)
{
m_log.log(LogService.LOG_INFO, "Runtime: Removing services from stopping bundle: " + b.getSymbolicName());
DependencyManager dm = m_managers.remove(b);
if (dm != null)
{
List<Component> services = new ArrayList(dm.getServices());
for (Component service : services)
{
m_log.log(LogService.LOG_INFO, "Runtime: Removing service: " + service);
dm.remove(service);
}
}
}
/**
* Load a DependencyManager component descriptor from a given bundle.
* @param b
* @param descriptorURL
*/
private void loadDescriptor(Bundle b, URL descriptorURL)
{
m_log.log(LogService.LOG_DEBUG, "Runtime: ++++ Parsing descriptor " + descriptorURL
+ " from bundle " + 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);
m_managers.put(b, dm);
}
catch (Throwable t)
{
m_log.log(LogService.LOG_ERROR, "Runtime: Error while parsing descriptor "
+ descriptorURL + " from bundle " + b.getSymbolicName(), t);
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException ignored)
{
}
}
}
}
}