/* * 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.ipojo.composite.instance; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.List; import java.util.Properties; import org.apache.felix.ipojo.ComponentInstance; import org.apache.felix.ipojo.ConfigurationException; import org.apache.felix.ipojo.Factory; import org.apache.felix.ipojo.InstanceManager; import org.apache.felix.ipojo.InstanceStateListener; import org.apache.felix.ipojo.MissingHandlerException; import org.apache.felix.ipojo.ServiceContext; import org.apache.felix.ipojo.UnacceptableConfiguration; import org.apache.felix.ipojo.architecture.HandlerDescription; import org.apache.felix.ipojo.composite.CompositeHandler; import org.apache.felix.ipojo.metadata.Element; import org.apache.felix.ipojo.parser.ParseException; /** * Composite Instance Handler. * This handler allows creating an instance inside a composite. * This instance is determine by its type and a configuration. * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class InstanceHandler extends CompositeHandler implements InstanceStateListener { /** * Internal context. */ private ServiceContext m_scope; /** * Available factories. */ private Factory[] m_factories; /** * Handler description. */ private InstanceHandlerDescription m_description; /** * This structure aims to manage a configuration. It stores all necessary * information to create an instance and to track the factory. */ class ManagedConfiguration { /** * Configuration of the instance to create. */ private Dictionary m_configuration; /** * Factory name. */ private String m_factoryName; /** * Created instance. */ private ComponentInstance m_instance; /** * Desired Factory (can be the classname). */ private String m_desiredFactory; /** * Constructor. * * @param conf : the configuration to create. */ ManagedConfiguration(Dictionary conf) { m_configuration = conf; m_desiredFactory = (String) conf.get("component"); } /** * Return the managed configuration. * @return the configuration. */ protected Dictionary getConfiguration() { return m_configuration; } /** * Return the used factory name. * @return the factory name */ protected String getFactory() { return m_factoryName; } protected String getNeededFactoryName() { return m_desiredFactory; } /** * Return the created instance. * @return the instance (or null if no instance are created). */ protected ComponentInstance getInstance() { return m_instance; } /** * Set the factory name. * * @param name : the factory name. */ protected void setFactory(String name) { m_factoryName = name; } /** * Set the instance object. * * @param instance : the instance */ protected void setInstance(ComponentInstance instance) { m_instance = instance; } } /** * Configurations to create and maintains. */ private ManagedConfiguration[] m_configurations = new ManagedConfiguration[0]; /** * Create an instance using the given factory and the given configuration. * * @param fact : the factory name to used. * @param config : the configuration. */ private void createInstance(Factory fact, ManagedConfiguration config) { Dictionary conf = config.getConfiguration(); try { config.setInstance(fact.createComponentInstance(conf, m_scope)); config.setFactory(fact.getName()); config.getInstance().addInstanceStateListener(this); } catch (UnacceptableConfiguration e) { error("A factory is available for the configuration but the configuration is not acceptable", e); } catch (MissingHandlerException e) { error("The instance creation has failed, at least one handler is missing", e); } catch (ConfigurationException e) { error("The instance creation has failed, an error during the configuration has occured", e); } } /** * A new valid factory appears. * @param factory : factory. */ public void bindFactory(Factory factory) { boolean implicated = false; String factName = factory.getName(); String className = factory.getComponentDescription().getClassName(); for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) { createInstance(factory, m_configurations[i]); implicated = true; } } if (implicated && ! getValidity()) { checkValidity(); } } /** * An existing factory disappears or becomes invalid. * @param factory : factory */ public void unbindFactory(Factory factory) { boolean implicated = false; for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() != null && m_configurations[i].getFactory().equals(factory.getName())) { m_configurations[i].setInstance(null); m_configurations[i].setFactory(null); implicated = true; } } if (implicated && getValidity()) { checkValidity(); } } /** * Stop all created instances. */ public synchronized void stop() { for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() != null) { m_configurations[i].getInstance().removeInstanceStateListener(this); if (m_configurations[i].getInstance().getState() != ComponentInstance.DISPOSED) { m_configurations[i].getInstance().dispose(); } } m_configurations[i].setInstance(null); m_configurations[i].setFactory(null); } m_configurations = new ManagedConfiguration[0]; } /** * Configure method. * @param metadata : component type metadata. * @param configuration : instance configuration. * @throws ConfigurationException : occurs an instance cannot be parsed correctly. * @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.metadata.Element, java.util.Dictionary) */ public void configure(Element metadata, Dictionary configuration) throws ConfigurationException { m_scope = getCompositeManager().getServiceContext(); // Prepare the configuration to append. Properties toAppend = new Properties(); Enumeration keys = configuration.keys(); while(keys.hasMoreElements()) { String key = (String) keys.nextElement(); if (! (key.equals("instance.name") || key.equals("component"))) { // Remove instance.name and component toAppend.put(key, configuration.get(key)); } } Element[] instances = metadata.getElements("instance"); m_configurations = new ManagedConfiguration[instances.length]; for (int i = 0; i < instances.length; i++) { Properties conf = null; try { conf = parseInstance(instances[i]); } catch (ParseException e) { error("An instance cannot be parsed correctly", e); throw new ConfigurationException("An instance cannot be parsed correctly", e); } Properties instanceConfiguration = new Properties(); instanceConfiguration.putAll(conf); instanceConfiguration.putAll(toAppend); m_configurations[i] = new ManagedConfiguration(instanceConfiguration); } m_description = new InstanceHandlerDescription(this, m_configurations); } /** * Parse an Element to get a dictionary. * @param instance : the Element describing an instance. * @return : the resulting dictionary * @throws ParseException : occurs when a configuration cannot be parse correctly. */ public static Properties parseInstance(Element instance) throws ParseException { Properties dict = new Properties(); String name = instance.getAttribute("name"); if (name != null) { dict.put("instance.name", name); } String comp = instance.getAttribute("component"); if (comp == null) { throw new ParseException("An instance does not have the 'component' attribute"); } else { dict.put("component", comp); } Element[] props = instance.getElements("property"); for (int i = 0; props != null && i < props.length; i++) { parseProperty(props[i], dict); } return dict; } /** * Parse a property. * @param prop : the current element to parse * @param dict : the dictionary to populate * @throws ParseException : occurs if the property cannot be parsed correctly */ public static void parseProperty(Element prop, Dictionary dict) throws ParseException { // Check that the property has a name String name = prop.getAttribute("name"); String value = prop.getAttribute("value"); if (name == null) { throw new ParseException("A property does not have the 'name' attribute"); } // Final case : the property element has a 'value' attribute if (value == null) { // Recursive case // Check if there is 'property' element Element[] subProps = prop.getElements("property"); if (subProps == null) { throw new ParseException("A complex property must have at least one 'property' sub-element"); } Dictionary dict2 = new Properties(); for (int i = 0; i < subProps.length; i++) { parseProperty(subProps[i], dict2); dict.put(prop.getAttribute("name"), dict2); } } else { dict.put(name, value); } } /** * Start method. * @see org.apache.felix.ipojo.Handler#start() */ public void start() { for (int j = 0; j < m_factories.length; j++) { String factName = m_factories[j].getName(); String className = m_factories[j].getClassName(); for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() == null && (m_configurations[i].getNeededFactoryName().equals(factName) || m_configurations[i].getNeededFactoryName().equals(className))) { createInstance(m_factories[j], m_configurations[i]); } } } checkValidity(); } /** * Check handler validity. * The method updates the validity of the handler. */ private void checkValidity() { for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() == null || m_configurations[i].getInstance().getState() != ComponentInstance.VALID) { setValidity(false); return; } } setValidity(true); } /** * Instance state listener. * This method listens when managed instance states change. * @param instance : instance * @param newState : the now state of the given instance * @see org.apache.felix.ipojo.InstanceStateListener#stateChanged(org.apache.felix.ipojo.ComponentInstance, int) */ public void stateChanged(ComponentInstance instance, int newState) { switch (newState) { case ComponentInstance.DISPOSED: break; // Should not happen case ComponentInstance.STOPPED: break; // Should not happen case ComponentInstance.VALID: if (!getValidity()) { checkValidity(); } break; case ComponentInstance.INVALID: if (getValidity()) { checkValidity(); } break; default: break; } } /** * Method returning an instance object of the given component type. * This method must be called only on 'primitive' type. * @param type : type. * @return an instance object or null if not found. */ public Object getObjectFromInstance(String type) { for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() != null && type.equals(m_configurations[i].getFactory())) { if (m_configurations[i].getInstance().getState() == ComponentInstance.VALID) { return ((InstanceManager) m_configurations[i].getInstance()).getPojoObject(); } else { error("An object cannot be get from the instance of the type " + type + ": invalid instance" + m_configurations[i].getInstance().getInstanceDescription().getDescription()); return null; } } } return null; } /** * Return the handler description, i.e. the state of created instances. * @return the handler description. * @see org.apache.felix.ipojo.Handler#getDescription() */ public HandlerDescription getDescription() { return m_description; } /** * Get the list of used component type. * @return the list containing the used component type */ public List getUsedType() { List result = new ArrayList(); for (int i = 0; i < m_configurations.length; i++) { result.add(m_configurations[i].getConfiguration().get("component")); } return result; } /** * The composite is reconfigured, we check if we have a property to change. * We reconfigure all contained instances. * @param configuration the new instance configuration */ public void reconfigure(Dictionary configuration) { for (int i = 0; i < m_configurations.length; i++) { if (m_configurations[i].getInstance() != null) { info("Reconfiguring " + m_configurations[i].getInstance().getInstanceName()); m_configurations[i].getInstance().reconfigure(configuration); } } } }