blob: 31356c2c3759f916063d6145299e4cc9b60dffdf [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.Dictionary;
import java.util.HashMap;
import java.util.List;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.dependencies.BundleDependency;
import org.apache.felix.dm.dependencies.ConfigurationDependency;
import org.apache.felix.dm.dependencies.Dependency;
import org.apache.felix.dm.dependencies.ResourceDependency;
import org.apache.felix.dm.dependencies.ServiceDependency;
import org.apache.felix.dm.dependencies.TemporalServiceDependency;
import org.apache.felix.dm.service.Service;
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 ComponentManager implements SynchronousBundleListener
{
private HashMap<Bundle, List<Service>> m_services = new HashMap<Bundle, List<Service>>();
private LogService m_logService; // Injected
private BundleContext m_bctx; // Injected
/**
* 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()
{
for (Bundle b : m_bctx.getBundles())
{
if (b.getState() == Bundle.ACTIVE)
{
bundleChanged(new BundleEvent(Bundle.ACTIVE, b));
}
}
m_bctx.addBundleListener(this);
}
/**
* Stops our service. We'll stop all activated DependencyManager services.
*/
protected void stop()
{
for (List<Service> services : m_services.values())
{
for (Service service : services)
{
service.stop();
}
}
m_services.clear();
}
/**
* 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)
{
bundleStopped(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_logService.log(LogService.LOG_ERROR,
"DependencyManager component descriptor not found: " + descriptorPath);
continue;
}
loadDescriptor(b, descriptorURL);
}
}
/**
* Load a DependencyManager component descriptor from a given bundle.
* @param b
* @param descriptorURL
*/
private void loadDescriptor(Bundle b, URL descriptorURL)
{
m_logService.log(LogService.LOG_DEBUG, "Parsing descriptor " + descriptorURL
+ " from bundle " + b.getSymbolicName());
BufferedReader in = null;
try
{
DescriptorParser parser = new DescriptorParser(m_logService);
in = new BufferedReader(new InputStreamReader(descriptorURL.openStream()));
DependencyManager dm = new DependencyManager(b.getBundleContext());
Service service = null;
String line;
while ((line = in.readLine()) != null)
{
switch (parser.parse(line))
{
case Service:
service = createService(b, dm, parser);
break;
case AspectService:
service = createAspectService(b, dm, parser);
break;
case AdapterService:
service = createAdapterService(b, dm, parser);
break;
case BundleAdapterService:
service = createBundleAdapterService(b, dm, parser);
break;
case ResourceAdapterService:
service = createResourceAdapterService(b, dm, parser);
break;
case ServiceDependency:
checkServiceParsed(service);
service.add(createServiceDependency(b, dm, parser, false));
break;
case TemporalServiceDependency:
checkServiceParsed(service);
service.add(createServiceDependency(b, dm, parser, true));
break;
case ConfigurationDependency:
checkServiceParsed(service);
service.add(createConfigurationDependency(b, dm, parser));
break;
case BundleDependency:
checkServiceParsed(service);
service.add(createBundleDependency(b, dm, parser));
break;
case ResourceDependency:
checkServiceParsed(service);
service.add(createResourceDependency(b, dm, parser));
break;
}
}
List<Service> services = m_services.get(b);
if (services == null)
{
services = new ArrayList<Service>();
m_services.put(b, services);
}
services.add(service);
dm.add(service);
}
catch (Throwable t)
{
m_logService.log(LogService.LOG_ERROR, "Error while parsing descriptor "
+ descriptorURL + " from bundle " + b.getSymbolicName(), t);
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException ignored)
{
}
}
}
}
/**
* Unregisters all services for a stopping bundle.
* @param b
*/
private void bundleStopped(Bundle b)
{
m_logService.log(LogService.LOG_INFO, "bundle stopped: " + b.getSymbolicName());
List<Service> services = m_services.remove(b);
if (services != null)
{
for (Service s : services)
{
m_logService.log(LogService.LOG_INFO, "stopping service " + s);
s.stop();
}
}
}
/**
* Check if we have already parsed the Service entry from a given DM component descriptor file.
* Each descriptor must start with a Service definition entry.
* @param service the parsed service
* @throws IllegalArgumentException if the service has not been parsed.
*/
private void checkServiceParsed(Service service)
{
if (service == null)
{
throw new IllegalArgumentException("Service not declared in the first descriptor line");
}
}
/**
* Creates a Service that we parsed from a component descriptor entry.
* @param b the bundle from where the service is started
* @param dm the DependencyManager framework
* @param parser the parser that just parsed the descriptor "Service" entry
* @return the created DependencyManager Service
* @throws ClassNotFoundException if the service implementation could not be instantiated
*/
private Service createService(Bundle b, DependencyManager dm, DescriptorParser parser)
throws ClassNotFoundException
{
Service service = dm.createService();
// Get factory parameters.
String factory = parser.getString(DescriptorParam.factory, null);
String factoryMethod = parser.getString(DescriptorParam.factoryMethod, "create");
if (factory == null)
{
// Set service impl
String impl = parser.getString(DescriptorParam.impl);
service.setImplementation(b.loadClass(impl));
}
else
{
// Set service factory
Class<?> factoryClass = b.loadClass(factory);
service.setFactory(factoryClass, factoryMethod);
}
// Set service callbacks
setCommonServiceParams(service, parser);
// Set service interface with associated service properties
Dictionary<String, String> serviceProperties = parser.getDictionary(
DescriptorParam.properties, null);
String[] provides = parser.getStrings(DescriptorParam.provide, null);
if (provides != null)
{
service.setInterface(provides, serviceProperties);
}
return service;
}
/**
* Set common Service parameters, if provided from our Component descriptor
* @param service
* @param parser
*/
private void setCommonServiceParams(Service service, DescriptorParser parser)
{
String init = parser.getString(DescriptorParam.init, null);
String start = parser.getString(DescriptorParam.start, null);
String stop = parser.getString(DescriptorParam.stop, null);
String destroy = parser.getString(DescriptorParam.destroy, null);
service.setCallbacks(init, start, stop, destroy);
String composition = parser.getString(DescriptorParam.composition, null);
if (composition != null)
{
service.setComposition(composition);
}
}
/**
* Creates an Aspect Service.
* @param b
* @param dm
* @param parser
* @return
*/
private Service createAspectService(Bundle b, DependencyManager dm, DescriptorParser parser)
throws ClassNotFoundException
{
Service service = null;
Class<?> serviceInterface = b.loadClass(parser.getString(DescriptorParam.service));
String serviceFilter = parser.getString(DescriptorParam.filter, null);
Dictionary<String, String> aspectProperties = parser.getDictionary(DescriptorParam.properties, null);
int ranking = parser.getInt(DescriptorParam.ranking, 1);
String factory = parser.getString(DescriptorParam.factory, null);
if (factory == null)
{
String implClass = parser.getString(DescriptorParam.impl);
Object impl = b.loadClass(implClass);
service = dm.createAspectService(serviceInterface, serviceFilter, ranking, impl, aspectProperties);
}
else
{
String factoryMethod = parser.getString(DescriptorParam.factoryMethod, "create");
Class<?> factoryClass = b.loadClass(factory);
service = dm.createAspectService(serviceInterface, serviceFilter, ranking, factoryClass, factoryMethod, aspectProperties);
}
setCommonServiceParams(service, parser);
return service;
}
/**
* Creates an Adapter Service.
* @param b
* @param dm
* @param parser
* @return
*/
private Service createAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
throws ClassNotFoundException
{
Class<?> adapterImpl = b.loadClass(parser.getString(DescriptorParam.impl));
Class<?> adapterService = b.loadClass(parser.getString(DescriptorParam.adapterService));
Dictionary<String, String> adapterProperties = parser.getDictionary(DescriptorParam.adapterProperties, null);
Class<?> adapteeService = b.loadClass(parser.getString(DescriptorParam.adapteeService));
String adapteeFilter = parser.getString(DescriptorParam.adapteeFilter, null);
Service service = dm.createAdapterService(adapteeService, adapteeFilter, adapterService.getName(), adapterImpl, adapterProperties);
setCommonServiceParams(service, parser);
return service;
}
/**
* Creates a Bundle Adapter Service.
* @param b
* @param dm
* @param parser
* @return
*/
private Service createBundleAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
throws ClassNotFoundException
{
int stateMask = parser.getInt(DescriptorParam.stateMask, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
String filter = parser.getString(DescriptorParam.filter, null);
Class<?> adapterImpl = b.loadClass(parser.getString(DescriptorParam.impl));
String service = parser.getString(DescriptorParam.service);
Dictionary<String, String> properties = parser.getDictionary(DescriptorParam.properties, null);
boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
Service srv = dm.createBundleAdapterService(stateMask, filter, adapterImpl, service, properties, propagate);
setCommonServiceParams(srv, parser);
return srv;
}
/**
* Creates a Resource Adapter Service.
* @param b
* @param dm
* @param parser
* @return
*/
private Service createResourceAdapterService(Bundle b, DependencyManager dm, DescriptorParser parser)
throws ClassNotFoundException
{
String filter = parser.getString(DescriptorParam.filter, null);
Class<?> impl = b.loadClass(parser.getString(DescriptorParam.impl));
String service = parser.getString(DescriptorParam.service);
Class<?> serviceClass = b.loadClass(service);
Dictionary<String, String> properties = parser.getDictionary(DescriptorParam.properties, null);
boolean propagate = "true".equals(parser.getString(DescriptorParam.propagate, "false"));
Service srv = dm.createResourceAdapterService(filter, null, serviceClass.getName(), properties, impl, propagate);
setCommonServiceParams(srv, parser);
return srv;
}
/**
* Creates a ServiceDependency that we parsed from a component descriptor "ServiceDependency" entry.
* @param b
* @param dm
* @param parser
* @param temporal true if this dependency is a temporal one, false if not.
* @return
* @throws ClassNotFoundException
*/
private ServiceDependency createServiceDependency(Bundle b, DependencyManager dm,
DescriptorParser parser, boolean temporal) throws ClassNotFoundException
{
ServiceDependency sd = temporal ? dm.createTemporalServiceDependency()
: dm.createServiceDependency();
// Set service with eventual service filter
String service = parser.getString(DescriptorParam.service);
Class serviceClass = b.loadClass(service);
String serviceFilter = parser.getString(DescriptorParam.filter, null);
sd.setService(serviceClass, serviceFilter);
// Set default service impl
String defaultServiceImpl = parser.getString(DescriptorParam.defaultImpl, null);
if (defaultServiceImpl != null)
{
Class defaultServiceImplClass = b.loadClass(defaultServiceImpl);
sd.setDefaultImplementation(defaultServiceImplClass);
}
// Set bind/unbind/rebind
String added = parser.getString(DescriptorParam.added, null);
String changed = temporal ? null : parser.getString(DescriptorParam.changed, null);
String removed = temporal ? null : parser.getString(DescriptorParam.removed, null);
sd.setCallbacks(added, changed, removed);
// Set AutoConfig
String autoConfigField = parser.getString(DescriptorParam.autoConfig, null);
if (autoConfigField != null)
{
sd.setAutoConfig(autoConfigField);
}
// Do specific parsing for temporal service dependency
if (temporal)
{
// Set the timeout value for a temporal service dependency
String timeout = parser.getString(DescriptorParam.timeout, null);
if (timeout != null)
{
((TemporalServiceDependency) sd).setTimeout(Long.parseLong(timeout));
}
// Set required flag (always true for a temporal dependency)
sd.setRequired(true);
} else {
// for ServiceDependency, get required flag.
String required = parser.getString(DescriptorParam.required, "true");
sd.setRequired("true".equals(required));
}
return sd;
}
/**
* Creates a ConfigurationDependency that we parsed from a component descriptor entry.
* @param b
* @param dm
* @param parser
* @return
*/
private Dependency createConfigurationDependency(Bundle b, DependencyManager dm,
DescriptorParser parser)
{
ConfigurationDependency cd = dm.createConfigurationDependency();
String pid = parser.getString(DescriptorParam.pid);
if (pid == null)
{
throw new IllegalArgumentException(
"pid attribute not provided in ConfigurationDependency declaration");
}
cd.setPid(pid);
String propagate = parser.getString(DescriptorParam.propagate, "false");
cd.setPropagate("true".equals(propagate));
String callback = parser.getString(DescriptorParam.updated, "updated");
cd.setCallback(callback);
return cd;
}
/**
* Creates a BundleDependency that we parsed from a component descriptor entry.
* @param b
* @param dm
* @param parser
* @return
*/
private Dependency createBundleDependency(Bundle b, DependencyManager dm,
DescriptorParser parser)
{
BundleDependency bd = dm.createBundleDependency();
// Set add/changed/removed
String added = parser.getString(DescriptorParam.added, null);
String changed = parser.getString(DescriptorParam.changed, null);
String removed = parser.getString(DescriptorParam.removed, null);
bd.setCallbacks(added, changed, removed);
// required
bd.setRequired("true".equals(parser.getString(DescriptorParam.required, "true")));
// filter
String filter = parser.getString(DescriptorParam.filter, null);
if (filter != null)
{
bd.setFilter(filter);
}
// stateMask
int stateMask = parser.getInt(DescriptorParam.stateMask, -1);
if (stateMask != -1)
{
bd.setStateMask(stateMask);
}
// propagate
bd.setPropagate("true".equals(parser.getString(DescriptorParam.propagate, "false")));
return bd;
}
private Dependency createResourceDependency(Bundle b, DependencyManager dm,
DescriptorParser parser)
{
ResourceDependency rd = dm.createResourceDependency();
// Set add/changed/removed
String added = parser.getString(DescriptorParam.added, null);
String changed = parser.getString(DescriptorParam.changed, null);
String removed = parser.getString(DescriptorParam.removed, null);
rd.setCallbacks(added, changed, removed);
// required
rd.setRequired("true".equals(parser.getString(DescriptorParam.required, "true")));
// filter
String filter = parser.getString(DescriptorParam.filter, null);
if (filter != null)
{
rd.setFilter(filter);
}
// propagate
rd.setPropagate("true".equals(parser.getString(DescriptorParam.propagate, "false")));
return rd;
}
}