/* * 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; } }