blob: 90c0b1f83ef0608cc0a634c74cfadf4d8f65dda9 [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.lang.reflect.Method;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import org.apache.felix.dm.Component;
import org.apache.felix.dm.Dependency;
import org.apache.felix.dm.DependencyManager;
import org.apache.felix.dm.runtime.api.ComponentInstance;
import org.osgi.framework.Bundle;
/**
* Implementation for our DM Runtime ComponentInstance.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ComponentInstanceImpl implements ComponentInstance {
/**
* The list of Dependencies which are applied in the Service.
*/
private final MetaData m_srvMeta;
/**
* The list of Dependencies which are applied in the Service.
*/
private final List<MetaData> m_depsMeta;
/**
* The DependencyManager which is used to create Service instances.
*/
private final DependencyManager m_dm;
/**
* The bundle containing the Service annotated with the factory attribute.
*/
private final Bundle m_bundle;
/**
* The component
*/
private final Object m_impl;
/**
* The DM Component used to define the component
*/
private final Component m_component;
public ComponentInstanceImpl(DependencyManager dm, Bundle b, MetaData srvMeta, List<MetaData> depsMeta, Dictionary<String, ?> conf) throws Exception
{
m_bundle = b;
m_dm = dm;
m_srvMeta = srvMeta;
m_depsMeta = depsMeta;
m_component = m_dm.createComponent();
Class<?> implClass = m_bundle.loadClass(m_srvMeta.getString(Params.impl));
Object impl = conf.get(ComponentBuilder.FACTORY_INSTANCE);
if (impl == null) {
String factoryMethod = m_srvMeta.getString(Params.factoryMethod, null);
if (factoryMethod == null) {
impl = implClass.newInstance();
} else {
Method m = implClass.getDeclaredMethod(factoryMethod);
m.setAccessible(true);
impl = m.invoke(null);
}
}
m_impl = impl;
// Invoke "configure" callback
String configure = m_srvMeta.getString(Params.factoryConfigure, null);
if (configure != null) {
invokeConfigure(impl, configure, conf);
}
// Create Service
m_component.setImplementation(impl);
String[] provides = m_srvMeta.getStrings(Params.provides, null);
if (provides != null) {
// Merge service properties with the configuration provided by the factory.
Dictionary<String, ?> serviceProperties = m_srvMeta.getDictionary(Params.properties, null);
serviceProperties = mergeSettings(serviceProperties, conf);
m_component.setInterface(provides, serviceProperties);
}
m_component.setComposition(m_srvMeta.getString(Params.composition, null));
ServiceLifecycleHandler lfcleHandler = new ServiceLifecycleHandler(m_component, m_bundle, m_dm, m_srvMeta, m_depsMeta);
// The dependencies will be plugged by our lifecycle handler.
m_component.setCallbacks(lfcleHandler, "init", "start", "stop", "destroy");
// Adds dependencies (except named dependencies, which are managed by the lifecycle handler).
for (MetaData dependency : m_depsMeta) {
String name = dependency.getString(Params.name, null);
if (name == null) {
DependencyBuilder depBuilder = new DependencyBuilder(dependency);
Log.instance().info("ServiceLifecycleHandler.init: adding dependency %s into service %s", dependency,
m_srvMeta);
Dependency d = depBuilder.build(m_bundle, m_dm);
m_component.add(d);
}
}
// Register the Service instance, and keep track of it.
Log.instance().info("ServiceFactory: created service %s", m_srvMeta);
m_dm.add(m_component);
}
@Override
public void dispose() {
m_dm.remove(m_component);
}
@Override
public void update(Dictionary<String, ?> conf) {
// Reconfigure an already existing Service.
String configure = m_srvMeta.getString(Params.factoryConfigure, null);
if (configure != null) {
Log.instance().info("ServiceFactory: updating service %s", m_impl);
invokeConfigure(m_impl, configure, conf);
}
// Update service properties
String[] provides = m_srvMeta.getStrings(Params.provides, null);
if (provides != null) {
Dictionary<String, ?> serviceProperties = m_srvMeta.getDictionary(Params.properties, null);
serviceProperties = mergeSettings(serviceProperties, conf);
m_component.setServiceProperties(serviceProperties);
}
}
/**
* Invokes the configure callback method on the service instance implemenatation.
* @param impl
* @param configure
* @param config
*/
private void invokeConfigure(Object impl, String configure, Dictionary<String, ?> config) {
try {
InvocationUtil.invokeCallbackMethod(impl, configure, new Class[][] { { Dictionary.class } },
new Object[][] { { config } });
}
catch (Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException("Could not invoke method " + configure + " on object " + impl, t);
}
}
}
/**
* Merge factory configuration settings with the service properties. The private factory configuration
* settings are ignored. A factory configuration property is private if its name starts with a dot (".").
*
* @param serviceProperties
* @param factoryConfiguration
* @return
*/
private Dictionary<String, Object> mergeSettings(Dictionary<String, ?> serviceProperties,
Dictionary<String, ?> factoryConfiguration)
{
Dictionary<String, Object> props = new Hashtable<>();
if (serviceProperties != null) {
Enumeration<String> keys = serviceProperties.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Object val = serviceProperties.get(key);
props.put(key, val);
}
}
Enumeration<String> keys = factoryConfiguration.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
if (!key.toString().startsWith(".")) {
// public properties are propagated
Object val = factoryConfiguration.get(key);
props.put(key, val);
}
}
return props;
}
}