/* | |
* 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; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Dictionary; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Properties; | |
import org.apache.felix.ipojo.architecture.ComponentDescription; | |
import org.apache.felix.ipojo.metadata.Element; | |
import org.apache.felix.ipojo.util.Logger; | |
import org.apache.felix.ipojo.util.Tracker; | |
import org.osgi.framework.BundleContext; | |
import org.osgi.framework.Constants; | |
import org.osgi.framework.InvalidSyntaxException; | |
import org.osgi.framework.ServiceReference; | |
import org.osgi.framework.ServiceRegistration; | |
/** | |
* The component factory manages component instance objects. This management | |
* consist in creating and managing component instance build with the component | |
* factory. This class could export Factory and ManagedServiceFactory services. | |
* | |
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> | |
*/ | |
public class CompositeFactory extends ComponentFactory implements Factory { | |
/** | |
* Service Registration of this factory (Factory & ManagedServiceFactory). | |
*/ | |
private ServiceRegistration m_sr; | |
/** | |
* Create a composite factory. | |
* @param bc : bundle context | |
* @param cm : metadata of the component to create | |
*/ | |
public CompositeFactory(BundleContext bc, Element cm) { | |
super(bc, cm); | |
} | |
/** | |
* Check if the metadata are well formed. | |
* @param cm : metadata | |
* @return true if the metadata are correct. | |
* @see org.apache.felix.ipojo.ComponentFactory#check(org.apache.felix.ipojo.metadata.Element) | |
*/ | |
public boolean check(Element cm) { | |
// Get the name | |
if (cm.containsAttribute("name")) { | |
m_factoryName = cm.getAttribute("name"); | |
return true; | |
} else { | |
System.err.println("A composite needs a name"); | |
return false; | |
} | |
} | |
/** | |
* Check if the given handler identifier can be fulfilled by the given service reference. | |
* @param hi : handler identifier. | |
* @param ref : service reference. | |
* @return true if the service reference can fulfill the given handler identifier | |
* @see org.apache.felix.ipojo.ComponentFactory#match(org.apache.felix.ipojo.ComponentFactory.HandlerIdentifier, org.osgi.framework.ServiceReference) | |
*/ | |
public boolean match(HandlerIdentifier hi, ServiceReference ref) { | |
String name = (String) ref.getProperty(Handler.HANDLER_NAME_PROPERTY); | |
String ns = (String) ref.getProperty(Handler.HANDLER_NAMESPACE_PROPERTY); | |
String type = (String) ref.getProperty(Handler.HANDLER_TYPE_PROPERTY); | |
if ("composite".equals(type)) { | |
if (IPojoConfiguration.IPOJO_NAMESPACE.equals(ns)) { | |
ns = ""; | |
} | |
return name.equals(hi.getName()) && ns.equals(hi.getNamespace()); | |
} else { | |
return false; | |
} | |
} | |
/** | |
* Compute required handlers. | |
*/ | |
protected void computeRequiredHandlers() { | |
Element[] elems = m_componentMetadata.getElements(); | |
for (int i = 0; i < elems.length; i++) { | |
Element current = elems[i]; | |
HandlerIdentifier hi = new HandlerIdentifier(current.getName(), current.getNameSpace()); | |
if (! m_handlerIdentifiers.contains(hi)) { m_handlerIdentifiers.add(hi); } | |
} | |
// Add architecture if needed | |
if (m_componentMetadata.containsAttribute("architecture") && m_componentMetadata.getAttribute("architecture").equalsIgnoreCase("true")) { | |
HandlerIdentifier hi = new HandlerIdentifier("architecture", ""); | |
if (! m_handlerIdentifiers.contains(hi)) { m_handlerIdentifiers.add(hi); } | |
} | |
} | |
/** | |
* Stop all the instance managers. | |
*/ | |
public synchronized void stop() { | |
m_tracker.close(); | |
final Collection col = m_componentInstances.values(); | |
final Iterator it = col.iterator(); | |
while (it.hasNext()) { | |
CompositeManager ci = (CompositeManager) it.next(); | |
if (ci.getState() != ComponentInstance.DISPOSED) { | |
ci.kill(); | |
} | |
m_instancesName.remove(ci.getInstanceName()); | |
} | |
m_componentInstances.clear(); | |
if (m_sr != null) { | |
m_sr.unregister(); | |
m_sr = null; | |
} | |
m_tracker = null; | |
m_componentDesc = null; | |
m_state = INVALID; | |
} | |
/** | |
* Start all the instance managers. | |
*/ | |
public synchronized void start() { | |
if (m_componentDesc != null) { // Already started. | |
return; | |
} | |
try { | |
String filter = "(&(" + Constants.OBJECTCLASS + "=" + Factory.class.getName() + ")" | |
+ "(" + Handler.HANDLER_NAME_PROPERTY + "=*)" + "(" + Handler.HANDLER_NAMESPACE_PROPERTY + "=*)" /* Look only for handlers */ | |
+ "(" + Handler.HANDLER_TYPE_PROPERTY + "=" + CompositeHandler.HANDLER_TYPE + ")" | |
+ "(factory.state=1)" | |
+ ")"; | |
m_tracker = new Tracker(m_context, m_context.createFilter(filter), this); | |
m_tracker.open(); | |
} catch (InvalidSyntaxException e) { | |
m_logger.log(Logger.ERROR, "A factory filter is not valid: " + e.getMessage()); | |
return; | |
} | |
computeFactoryState(); | |
// Check if the factory should be exposed | |
if (m_isPublic) { | |
// Exposition of the factory service | |
m_sr = m_context.registerService(new String[] { Factory.class.getName() }, this, getProperties()); | |
} | |
} | |
/** | |
* Compute factory service properties. | |
* @return the factory service properties | |
* @see org.apache.felix.ipojo.ComponentFactory#getProperties() | |
*/ | |
protected Properties getProperties() { | |
final Properties props = new Properties(); | |
if (m_componentDesc != null) { | |
props.put("component.providedServiceSpecifications", m_componentDesc.getprovidedServiceSpecification()); | |
props.put("component.properties", m_componentDesc.getProperties()); | |
props.put("component.description", m_componentDesc); | |
props.put("component.desc", m_componentDesc.toString()); | |
} | |
// Add factory state | |
props.put("factory.state", "" + m_state); | |
props.put("factory.name", m_factoryName); | |
return props; | |
} | |
/** | |
* Create an instance. The given configuration needs to contain the 'name' | |
* property. | |
* @param configuration : configuration of the created instance. | |
* @return the created component instance. | |
* @throws UnacceptableConfiguration : occurs if the given configuration is | |
* not consistent with the component type of this factory. | |
* @throws MissingHandlerException : occurs if an handler is unavailable when the instance is created. | |
* @throws ConfigurationException : occurs if an error occurs during the instance configuration. | |
* @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary) | |
*/ | |
public synchronized ComponentInstance createComponentInstance(Dictionary configuration) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException { | |
return createComponentInstance(configuration, null); | |
} | |
/** | |
* Create an instance. The given configuration needs to contain the 'name' | |
* property. | |
* @param configuration : configuration of the created instance. | |
* @param serviceContext : the service context to push for this instance. | |
* @return the created component instance. | |
* @throws UnacceptableConfiguration : occurs if the given configuration is | |
* not consistent with the component type of this factory. | |
* @throws MissingHandlerException : occurs when an handler is unavailable when creating the instance. | |
* @throws ConfigurationException : occurs when the instance configuration failed. | |
* @see org.apache.felix.ipojo.Factory#createComponentInstance(java.util.Dictionary) | |
*/ | |
public synchronized ComponentInstance createComponentInstance(Dictionary configuration, ServiceContext serviceContext) throws UnacceptableConfiguration, MissingHandlerException, ConfigurationException { | |
if (m_state == INVALID) { | |
throw new MissingHandlerException(getMissingHandlers()); | |
} | |
if (configuration == null) { | |
configuration = new Properties(); | |
} | |
try { | |
checkAcceptability(configuration); | |
} catch (UnacceptableConfiguration e) { | |
m_logger.log(Logger.ERROR, "The configuration is not acceptable : " + e.getMessage()); | |
throw new UnacceptableConfiguration("The configuration " + configuration + " is not acceptable for " + m_factoryName + ": " + e.getMessage()); | |
} | |
String in = null; | |
if (configuration.get("name") != null) { | |
in = (String) configuration.get("name"); | |
} else { | |
in = generateName(); | |
configuration.put("name", in); | |
} | |
if (m_instancesName.contains(in)) { | |
throw new UnacceptableConfiguration("Name already used : " + in + "(" + m_instancesName + ")"); | |
} else { | |
m_instancesName.add(in); | |
} | |
BundleContext context = null; | |
if (serviceContext == null) { | |
context = new IPojoContext(m_context); | |
} else { | |
context = new IPojoContext(m_context, serviceContext); | |
} | |
ComponentInstance instance = null; | |
List handlers = new ArrayList(); | |
for (int i = 0; i < m_handlerIdentifiers.size(); i++) { | |
HandlerIdentifier hi = (HandlerIdentifier) m_handlerIdentifiers.get(i); | |
handlers.add(getHandlerInstance(hi, serviceContext)); | |
} | |
final CompositeManager inst = new CompositeManager(this, context, (HandlerManager[]) handlers.toArray(new HandlerManager[0])); | |
inst.configure(m_componentMetadata, configuration); | |
instance = inst; | |
m_componentInstances.put(in, instance); | |
instance.start(); | |
return instance; | |
} | |
/** | |
* Reconfigure an existing instance. | |
* @param properties : the new configuration to push. | |
* @throws UnacceptableConfiguration : occurs if the new configuration is | |
* not consistent with the component type. | |
* @throws MissingHandlerException : occurs when an handler is unavailable when creating the instance. | |
* @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary) | |
*/ | |
public synchronized void reconfigure(Dictionary properties) throws UnacceptableConfiguration, MissingHandlerException { | |
if (properties == null || properties.get("name") == null) { | |
throw new UnacceptableConfiguration("The configuration does not contains the \"name\" property"); | |
} | |
final String name = (String) properties.get("name"); | |
ComponentInstance cm = (CompositeManager) m_componentInstances.get(name); | |
if (cm == null) { | |
return; // The instance does not exist. | |
} | |
cm.reconfigure(properties); // re-configure the component | |
} | |
/** | |
* Compute factory state. | |
*/ | |
protected void computeFactoryState() { | |
boolean isValid = true; | |
for (int i = 0; isValid && i < m_handlerIdentifiers.size(); i++) { | |
HandlerIdentifier hi = (HandlerIdentifier) m_handlerIdentifiers.get(i); | |
isValid = hi.getReference() != null; | |
} | |
if (isValid && m_componentDesc == null) { | |
try { | |
computeDescription(); | |
} catch (org.apache.felix.ipojo.ConfigurationException e) { | |
m_logger.log(Logger.ERROR, "The component type metadata are not correct : " + e.getMessage()); | |
stop(); | |
return; | |
} | |
} | |
if (isValid && m_state == INVALID) { | |
m_state = VALID; | |
if (m_sr != null) { | |
m_sr.setProperties(getProperties()); | |
} | |
for (int i = 0; i < m_listeners.size(); i++) { | |
((FactoryStateListener) m_listeners.get(i)).stateChanged(this, VALID); | |
} | |
return; | |
} | |
if (!isValid && m_state == VALID) { | |
m_state = INVALID; | |
final Collection col = m_componentInstances.values(); | |
final Iterator it = col.iterator(); | |
while (it.hasNext()) { | |
CompositeManager ci = (CompositeManager) it.next(); | |
ci.kill(); | |
m_instancesName.remove(ci.getInstanceName()); | |
} | |
m_componentInstances.clear(); | |
if (m_sr != null) { | |
m_sr.setProperties(getProperties()); | |
} | |
for (int i = 0; i < m_listeners.size(); i++) { | |
((FactoryStateListener) m_listeners.get(i)).stateChanged(this, INVALID); | |
} | |
return; | |
} | |
} | |
/** | |
* Compute component type description. | |
* @throws ConfigurationException : if one handler has rejected the configuration. | |
* @see org.apache.felix.ipojo.ComponentFactory#computeDescription() | |
*/ | |
public void computeDescription() throws ConfigurationException { | |
List l = new ArrayList(); | |
for (int i = 0; i < m_handlerIdentifiers.size(); i++) { | |
HandlerIdentifier hi = (HandlerIdentifier) m_handlerIdentifiers.get(i); | |
l.add(hi.getFullName()); | |
} | |
m_componentDesc = new ComponentDescription(getName(), "composite", m_state, l, getMissingHandlers(), m_context.getBundle().getBundleId()); | |
for (int i = 0; i < m_handlerIdentifiers.size(); i++) { | |
HandlerIdentifier hi = (HandlerIdentifier) m_handlerIdentifiers.get(i); | |
HandlerManager hm = getHandlerInstance(hi, null); | |
hm.getHandler(); | |
Handler ch = hm.getHandler(); | |
ch.initializeComponentFactory(m_componentDesc, m_componentMetadata); | |
((Pojo) ch).getComponentInstance().dispose(); | |
} | |
} | |
/** | |
* Get a composite handler object for the given handler identifier. | |
* @param handler : the handler identifier. | |
* @param sc : the service context in which creating the handler. | |
* @return the composite handler object or null if not found. | |
*/ | |
private HandlerManager getHandlerInstance(HandlerIdentifier handler, ServiceContext sc) { | |
Factory factory = (Factory) m_context.getService(handler.getReference()); | |
try { | |
return (HandlerManager) factory.createComponentInstance(null, sc); | |
} catch (MissingHandlerException e) { | |
m_logger.log(Logger.ERROR, "The creation of the composite handler " + handler.getFullName() + " has failed: " + e.getMessage()); | |
return null; | |
} catch (UnacceptableConfiguration e) { | |
m_logger.log(Logger.ERROR, "The creation of the composite handler " + handler.getFullName() + " has failed (UnacceptableConfiguration): " + e.getMessage()); | |
return null; | |
} catch (ConfigurationException e) { | |
m_logger.log(Logger.ERROR, "The creation of the composite handler " + handler.getFullName() + " has failed (ConfigurationException): " + e.getMessage()); | |
return null; | |
} | |
} | |
} |