/* | |
* 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.service.provides; | |
import java.io.IOException; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.apache.felix.ipojo.Factory; | |
import org.apache.felix.ipojo.manipulation.Manipulator; | |
import org.apache.felix.ipojo.metadata.Attribute; | |
import org.apache.felix.ipojo.metadata.Element; | |
import org.osgi.framework.BundleContext; | |
import org.osgi.framework.InvalidSyntaxException; | |
import org.osgi.framework.ServiceReference; | |
/** | |
* Check and build a composition, i.e. a POJO containing the composition. | |
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> | |
*/ | |
public class CompositionMetadata { | |
/** | |
* Implemented composition. | |
*/ | |
private SpecificationMetadata m_specification; | |
/** | |
* Name of the composition. | |
*/ | |
private String m_name; | |
/** | |
* Bundle Context. | |
*/ | |
private BundleContext m_context; | |
/** | |
* Manipulation Metadata. | |
*/ | |
private Element m_manipulation; | |
/** | |
* Reference on the handler. | |
*/ | |
private ProvidedServiceHandler m_handler; | |
/** | |
* List of Mappings. | |
*/ | |
private List m_mappings = new ArrayList(); | |
/** | |
* Constructor. | |
* @param context : bundle context | |
* @param description : 'provides' element | |
* @param psh : parent handler | |
* @param name : name of the composition. | |
*/ | |
public CompositionMetadata(BundleContext context, Element description, ProvidedServiceHandler psh, String name) { | |
m_context = context; | |
m_handler = psh; | |
// Get the composition name | |
m_name = description.getAttribute("specification") + name; | |
// Get implemented service specification | |
String spec = description.getAttribute("specification"); | |
m_specification = new SpecificationMetadata(spec, m_context, false, false, m_handler); | |
Element[] mappings = description.getElements("delegation"); | |
for (int i = 0; mappings != null && i < mappings.length; i++) { | |
String methodName = mappings[i].getAttribute("method"); | |
MethodMetadata method = m_specification.getMethodByName(methodName); | |
if (method == null) { | |
m_handler.error("The method " + methodName + " does not exist in the specicifation " + spec); | |
return; | |
} | |
if (mappings[i].getAttribute("policy").equalsIgnoreCase("All")) { | |
method.setAllPolicy(); | |
} | |
} | |
} | |
protected BundleContext getBundleContext() { | |
return m_context; | |
} | |
public String getName() { | |
return m_name; | |
} | |
public SpecificationMetadata getSpecificationMetadata() { | |
return m_specification; | |
} | |
/** | |
* Build Available Mappings. | |
* @throws CompositionException : a factory is not available, the composition cannot be checked. | |
*/ | |
private void buildAvailableMappingList() throws CompositionException { | |
int index = 0; | |
for (int i = 0; i < m_handler.getInstanceType().size(); i++) { | |
String type = (String) m_handler.getInstanceType().get(i); | |
try { | |
ServiceReference[] refs = m_context.getServiceReferences(Factory.class.getName(), "(factory.name=" + type + ")"); | |
if (refs == null) { | |
m_handler.error("The factory " + type + " is not available, cannot check the composition"); | |
throw new CompositionException("The factory " + type + " needs to be available to check the composition"); | |
} else { | |
String className = (String) refs[0].getProperty("component.class"); | |
Class impl = m_context.getBundle().loadClass(className); | |
SpecificationMetadata spec = new SpecificationMetadata(impl, type, m_handler); | |
FieldMetadata field = new FieldMetadata(spec); | |
field.setName("_field" + index); | |
Mapping map = new Mapping(spec, field); | |
m_mappings.add(map); | |
index++; | |
} | |
} catch (InvalidSyntaxException e) { | |
m_handler.error("A LDAP filter is not valid : " + e.getMessage()); | |
} catch (ClassNotFoundException e) { | |
m_handler.error("The implementation class of a component cannot be loaded : " + e.getMessage()); | |
} | |
} | |
for (int i = 0; i < m_handler.getSpecifications().size(); i++) { | |
SpecificationMetadata spec = (SpecificationMetadata) m_handler.getSpecifications().get(i); | |
FieldMetadata field = new FieldMetadata(spec); | |
field.setName("_field" + index); | |
if (spec.isOptional()) { | |
field.setOptional(true); | |
} | |
if (spec.isAggregate()) { | |
field.setAggregate(true); | |
} | |
Mapping map = new Mapping(spec, field); | |
m_mappings.add(map); | |
index++; | |
} | |
} | |
/** | |
* Build the delegation mapping. | |
* @throws CompositionException : occurs when the mapping cannot be inferred correctly | |
*/ | |
protected void buildMapping() throws CompositionException { | |
buildAvailableMappingList(); | |
// Dependency closure is OK, now look for method delegation | |
Map/* <MethodMetadata, Mapping> */svcMethods = new HashMap(); | |
Map/* <MethodMetadata, Mapping> */instMethods = new HashMap(); | |
for (int i = 0; i < m_mappings.size(); i++) { | |
Mapping map = (Mapping) m_mappings.get(i); | |
SpecificationMetadata spec = map.getSpecification(); | |
for (int j = 0; j < spec.getMethods().size(); j++) { | |
MethodMetadata method = (MethodMetadata) spec.getMethods().get(j); | |
if (spec.isInterface()) { | |
svcMethods.put(method, map); | |
} else { | |
instMethods.put(method, map); | |
} | |
} | |
} | |
// For each needed method, search if available and store the mapping | |
for (int j = 0; j < m_specification.getMethods().size(); j++) { | |
MethodMetadata method = (MethodMetadata) m_specification.getMethods().get(j); | |
Set keys = instMethods.keySet(); // Look first in methods contained in the glue code. | |
Iterator iterator = keys.iterator(); | |
boolean found = false; | |
while (iterator.hasNext() & !found) { | |
MethodMetadata met = (MethodMetadata) iterator.next(); | |
if (met.equals(method)) { | |
found = true; | |
FieldMetadata field = ((Mapping) instMethods.get(met)).getField(); | |
field.setUseful(true); | |
method.setDelegation(field); | |
} | |
} | |
if (!found) { // If not found looks inside method contained in services. | |
keys = svcMethods.keySet(); // Look first in methods contained in the glue code | |
iterator = keys.iterator(); | |
while (!found && iterator.hasNext()) { | |
MethodMetadata met = (MethodMetadata) iterator.next(); | |
if (met.equals(method)) { | |
found = true; | |
FieldMetadata field = ((Mapping) svcMethods.get(met)).getField(); | |
field.setUseful(true); | |
method.setDelegation(field); | |
// Test optional | |
if (field.isOptional() && !method.throwsUnsupportedOperationException()) { | |
m_handler.warn("The method " + method.getMethod().getName() + " could not be provided correctly : the specification " + field.getSpecification().getName() + " is optional"); | |
} | |
} | |
} | |
} | |
if (!found) { throw new CompositionException("Inconsistent composition - the method " + method.getMethod() + " could not be delegated"); } | |
} | |
} | |
/** | |
* Build a service implementation. | |
* @return the byte[] of the POJO. | |
*/ | |
protected byte[] buildPOJO() { | |
Class clazz = null; | |
try { | |
clazz = getBundleContext().getBundle().loadClass(m_specification.getName()); | |
} catch (ClassNotFoundException e1) { | |
// The class has already be loaded. | |
return null; | |
} | |
byte[] pojo = POJOWriter.dump(clazz, m_name, getFieldList(), getMethodList(), m_handler); | |
Manipulator manipulator = new Manipulator(); | |
try { | |
byte[] newclazz = manipulator.manipulate(pojo); | |
m_manipulation = manipulator.getManipulationMetadata(); | |
return newclazz; | |
} catch (IOException e) { | |
m_handler.error("An error occurs during the composite implementation creation : " + e.getMessage(), e); | |
} | |
return null; | |
} | |
/** | |
* Build service implementation metadata. | |
* @param name : name of the future instance (used to avoid cycle) | |
* @return Component Type metadata. | |
*/ | |
protected Element buildMetadata(String name) { | |
Element elem = new Element("component", ""); | |
Attribute className = new Attribute("classname", m_name); | |
Attribute factory = new Attribute("public", "false"); | |
elem.addAttribute(className); | |
elem.addAttribute(factory); | |
// Add architecture for debug | |
elem.addAttribute(new Attribute("architecture", "true")); | |
// Provides | |
Element provides = new Element("provides", ""); | |
provides.addAttribute(new Attribute("specification", m_specification.getName())); | |
elem.addElement(provides); | |
// Dependencies | |
List fields = getFieldList(); | |
for (int i = 0; i < fields.size(); i++) { | |
FieldMetadata field = (FieldMetadata) fields.get(i); | |
if (field.isUseful() && field.getSpecification().isInterface()) { | |
Element dep = new Element("requires", ""); | |
dep.addAttribute(new Attribute("field", field.getName())); | |
dep.addAttribute(new Attribute("scope", "composite")); | |
dep.addAttribute(new Attribute("proxy", "false")); | |
if (field.getSpecification().isOptional()) { | |
dep.addAttribute(new Attribute("optional", "true")); | |
} | |
dep.addAttribute(new Attribute("filter", "(!(instance.name=" + name + "))")); | |
elem.addElement(dep); | |
} | |
} | |
Element properties = new Element("properties", ""); | |
for (int i = 0; i < fields.size(); i++) { | |
FieldMetadata field = (FieldMetadata) fields.get(i); | |
if (field.isUseful() && !field.getSpecification().isInterface()) { | |
Element prop = new Element("Property", ""); | |
prop.addAttribute(new Attribute("field", field.getName())); | |
properties.addElement(prop); | |
} | |
} | |
if (properties.getElements().length != 0) { | |
elem.addElement(properties); | |
} | |
// Insert information to metadata | |
elem.addElement(m_manipulation); | |
return elem; | |
} | |
/** | |
* Get the field list to use for the delegation. | |
* @return the field list. | |
*/ | |
public List getFieldList() { | |
List list = new ArrayList(); | |
for (int i = 0; i < m_mappings.size(); i++) { | |
Mapping map = (Mapping) m_mappings.get(i); | |
list.add(map.getField()); | |
} | |
return list; | |
} | |
/** | |
* Get the method list contained in the implemented specification. | |
* @return the List of implemented method. | |
*/ | |
private List getMethodList() { | |
return m_specification.getMethods(); | |
} | |
/** | |
* Store links between Field and pointed Specification. | |
*/ | |
private class Mapping { | |
/** | |
* Specification. | |
*/ | |
private SpecificationMetadata m_spec; | |
/** | |
* Field. | |
*/ | |
private FieldMetadata m_field; | |
/** | |
* Constructor. | |
* @param spec : specification metadata. | |
* @param field : the field. | |
*/ | |
public Mapping(SpecificationMetadata spec, FieldMetadata field) { | |
m_spec = spec; | |
m_field = field; | |
} | |
public SpecificationMetadata getSpecification() { | |
return m_spec; | |
} | |
public FieldMetadata getField() { | |
return m_field; | |
} | |
} | |
} |