blob: 696217cf12666e63e83e7a26171456aefb711c59 [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.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.getMessage());
}
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 update the validaity 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:
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);
}
}
}
}