| /* |
| * 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.List; |
| import java.util.Properties; |
| |
| import org.apache.felix.ipojo.ComponentInstance; |
| import org.apache.felix.ipojo.CompositeHandler; |
| 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.metadata.Element; |
| import org.apache.felix.ipojo.parser.ParseException; |
| import org.apache.felix.ipojo.util.Logger; |
| |
| /** |
| * 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; |
| |
| /** |
| * Is the handler valid ? |
| * (Lifecycle controller) |
| */ |
| private boolean m_isValid = false; |
| |
| /** |
| * Available factories. |
| */ |
| private Factory[] m_factories; |
| |
| |
| /** |
| * 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. |
| */ |
| Dictionary getConfiguration() { |
| return m_configuration; |
| } |
| |
| /** |
| * Return the used factory name. |
| * @return the factory name |
| */ |
| String getFactory() { |
| return m_factoryName; |
| } |
| |
| String getNeededFactoryName() { |
| return m_desiredFactory; |
| } |
| |
| /** |
| * Return the created instance. |
| * @return the instance (or null if no instance are created). |
| */ |
| ComponentInstance getInstance() { |
| return m_instance; |
| } |
| |
| /** |
| * Set the factory name. |
| * |
| * @param name : the factory name. |
| */ |
| void setFactory(String name) { |
| m_factoryName = name; |
| } |
| |
| /** |
| * Set the instance object. |
| * |
| * @param instance : the instance |
| */ |
| 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) { |
| log(Logger.ERROR, "A factory is available for the configuration but the configuration is not acceptable", e); |
| } catch (MissingHandlerException e) { |
| log(Logger.ERROR, "The instance creation has failed, at least one handler is missing", e); |
| } catch (ConfigurationException e) { |
| log(Logger.ERROR, "The instance creation has failed, an error during the configuration has occured", e); |
| } |
| } |
| |
| /** |
| * A new valid factory appears. |
| * @param f : factory. |
| */ |
| public void bindFactory(Factory f) { |
| boolean implicated = false; |
| String factName = f.getName(); |
| String className = f.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(f, m_configurations[i]); |
| implicated = true; |
| } |
| } |
| if (implicated && ! m_isValid) { |
| checkValidity(); |
| } |
| } |
| |
| /** |
| * An existing factory disappears or becomes invalid. |
| * @param f : factory |
| */ |
| public void unbindFactory(Factory f) { |
| boolean implicated = false; |
| for (int i = 0; i < m_configurations.length; i++) { |
| if (m_configurations[i].getInstance() != null && m_configurations[i].getFactory().equals(f.getName())) { |
| m_configurations[i].setInstance(null); |
| m_configurations[i].setFactory(null); |
| implicated = true; |
| } |
| } |
| if (implicated && m_isValid) { |
| 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.CompositeHandler#configure(org.apache.felix.ipojo.CompositeManager, org.apache.felix.ipojo.metadata.Element, java.util.Dictionary) |
| */ |
| public void configure(Element metadata, Dictionary configuration) throws ConfigurationException { |
| m_scope = getCompositeManager().getServiceContext(); |
| Element[] instances = metadata.getElements("instance"); |
| m_configurations = new ManagedConfiguration[instances.length]; |
| for (int i = 0; i < instances.length; i++) { |
| Dictionary conf = null; |
| try { |
| conf = parseInstance(instances[i]); |
| } catch (ParseException e) { |
| log(Logger.ERROR, "An instance cannot be parsed correctly", e); |
| throw new ConfigurationException("An instance cannot be parsed correctly : " + e.getMessage()); |
| } |
| m_configurations[i] = new ManagedConfiguration(conf); |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| private Dictionary parseInstance(Element instance) throws ParseException { |
| Dictionary dict = new Properties(); |
| String name = instance.getAttribute("name"); |
| if (name != null) { |
| dict.put("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); |
| } |
| |
| for (int i = 0; i < instance.getElements("property").length; i++) { |
| parseProperty(instance.getElements("property")[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 |
| */ |
| private 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) { |
| dict.put(name, value); |
| } else { |
| // Recursive case |
| // Check if there is 'property' element |
| Element[] subProps = prop.getElements("property"); |
| if (subProps.length == 0) { 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); |
| } |
| } |
| } |
| |
| /** |
| * Start method. |
| * @see org.apache.felix.ipojo.CompositeHandler#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 update the m_validity field. |
| * @return the new validity. |
| */ |
| private boolean checkValidity() { |
| for (int i = 0; i < m_configurations.length; i++) { |
| if (m_configurations[i].getInstance() == null || m_configurations[i].getInstance().getState() != ComponentInstance.VALID) { |
| m_isValid = false; |
| return false; |
| } |
| } |
| m_isValid = true; |
| return 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: |
| case ComponentInstance.STOPPED: |
| break; // Should not happen |
| case ComponentInstance.VALID: |
| if (!m_isValid) { |
| checkValidity(); |
| } |
| break; |
| case ComponentInstance.INVALID: |
| if (m_isValid) { |
| 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()) && m_configurations[i].getInstance().getState() == ComponentInstance.VALID) { |
| return ((InstanceManager) m_configurations[i].getInstance()).getPojoObject(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Return the handler description, i.e. the state of created instances. |
| * @return the handler description. |
| * @see org.apache.felix.ipojo.CompositeHandler#getDescription() |
| */ |
| public HandlerDescription getDescription() { |
| List l = new ArrayList(); |
| for (int i = 0; i < m_configurations.length; i++) { |
| l.add(m_configurations[i]); |
| } |
| return new InstanceHandlerDescription(this, l); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| } |