package fr.imag.adele.apam.declarations.encoding.capability; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.felix.bundlerepository.Capability; import org.apache.felix.bundlerepository.Property; import org.apache.felix.bundlerepository.Requirement; import fr.imag.adele.apam.CST; import fr.imag.adele.apam.declarations.AtomicImplementationDeclaration; import fr.imag.adele.apam.declarations.ComponentDeclaration; import fr.imag.adele.apam.declarations.ComponentKind; import fr.imag.adele.apam.declarations.CompositeDeclaration; import fr.imag.adele.apam.declarations.PropertyDefinition; import fr.imag.adele.apam.declarations.RelationDeclaration; import fr.imag.adele.apam.declarations.encoding.Encoder; import fr.imag.adele.apam.declarations.encoding.ipojo.ComponentParser; import fr.imag.adele.apam.declarations.references.ResolvableReference; import fr.imag.adele.apam.declarations.references.components.ComponentReference; import fr.imag.adele.apam.declarations.references.components.VersionedReference; import fr.imag.adele.apam.declarations.references.resources.InterfaceReference; import fr.imag.adele.apam.declarations.references.resources.MessageReference; import fr.imag.adele.apam.declarations.references.resources.PackageReference; import fr.imag.adele.apam.declarations.references.resources.UnknownReference; import fr.imag.adele.apam.declarations.repository.acr.ApamComponentRepository; import fr.imag.adele.apam.declarations.tools.Reporter; import fr.imag.adele.apam.declarations.tools.Reporter.Severity; import fr.imag.adele.apam.util.Util; /** * Encodes a component declaration as a capability in an OSGi Bundle Repository. * * This is the format used by the Apam Component Repository at build time, and the OBR * manager at runtime. * * @author vega * */ public class CapabilityEncoder implements Encoder<Capability> { private Reporter reporter; private ComponentDeclaration component; private Builder result; @Override public Capability encode(ComponentDeclaration component, Reporter reporter) { this.reporter = reporter; this.component = component; info("encoding ACR capability for component : " + component.getName() + " of type " + component.getKind()); this.result = builder(CST.CAPABILITY_COMPONENT); /* * Name and kind */ property(CST.NAME,component.getName()); property(CST.COMPONENT_TYPE, kind(component.getKind())); /* * Group reference * * TODO We should unify the group references for all kind of components */ VersionedReference<?> group = component.getGroupVersioned(); if(group != null) { switch(group.getComponent().getKind()) { case SPECIFICATION: property(CST.PROVIDE_SPECIFICATION,group.getName()); break; case IMPLEMENTATION: property(CST.IMPLNAME,group.getName()); break; case INSTANCE: case COMPONENT: default: break; } if (group.getRange() != null) { property(CST.REQUIRE_VERSION,group.getRange()); } } /* * Specific properties depending on the kind of component */ if (component instanceof AtomicImplementationDeclaration) { AtomicImplementationDeclaration atomicComponent = (AtomicImplementationDeclaration) component; property(CST.PROVIDE_CLASSNAME, atomicComponent.getClassName()); } if (component instanceof CompositeDeclaration) { property(CST.APAM_COMPOSITE,flag(true)); CompositeDeclaration composite = (CompositeDeclaration) component; if (composite.getMainComponent() != null) { property(CST.APAM_MAIN_COMPONENT, composite.getMainComponent().getName()); } } /* * Predefined properties */ if(component.isDefinedSingleton()) { property(CST.SINGLETON,flag(component.isSingleton())); } if(component.isDefinedShared()) { property(CST.SHARED,flag(component.isShared())); } if(component.isDefinedInstantiable()) { property(CST.INSTANTIABLE,flag(component.isInstantiable())); } /* * provided resources */ provided(); /* * relations */ relations(); /* * property definitions and values */ properties(); return result(); } /** * Encode the provided resources of the component */ private void provided() { Set<InterfaceReference> interfaces = component.getProvidedResources(InterfaceReference.class); if (!interfaces.isEmpty()) { property(CST.PROVIDE_INTERFACES,Util.list(interfaces)); } Set<MessageReference> messages = component.getProvidedResources(MessageReference.class); if (!messages.isEmpty()) { property(CST.PROVIDE_MESSAGES, Util.list(messages)); } } /** * Encodes the properties and its definitions */ private void properties() { for (PropertyDefinition definition : component.getPropertyDefinitions()) { property(CST.DEFINITION_PREFIX + definition.getName(), definition.getType(), definition.getDefaultValue()); } for (Map.Entry<String, String> property : component.getProperties().entrySet()) { /* * Try to get the exact type and map it to types supported by OBR */ PropertyDefinition definition = component.getPropertyDefinition(property.getKey()); if (definition == null) { property(property.getKey(),property.getValue()); } else { property(property.getKey(),type(definition.getType()),property.getValue()); } } } /** * encodes the type of a property as an OBR type. * * Return null if no exact type can be inferred */ private String type(String type) { if (type.equalsIgnoreCase("version")) return Property.VERSION; return null; } /** * Encodes the relations */ private void relations() { for (RelationDeclaration relation : component.getRelations()) { property(CST.RELATION_PREFIX+relation.getIdentifier(),target(relation)); } } /** * encodes the target of a relation */ private String target(RelationDeclaration relation) { ResolvableReference target = relation.getTarget(); String encodedTarget = null; if (target instanceof InterfaceReference) { encodedTarget = "{"+ComponentParser.INTERFACE+"}"+target.getName(); } else if (target instanceof MessageReference) { encodedTarget = "{"+ComponentParser.MESSAGE+"}"+target.getName(); } else if (target instanceof PackageReference) { encodedTarget = "{"+ComponentParser.PACKAGE+"}"+target.getName(); } else if (target instanceof UnknownReference && target.as(InterfaceReference.class) != null) { encodedTarget = "{"+ComponentParser.INTERFACE+"}"; } else if (target instanceof UnknownReference && target.as(MessageReference.class) != null) { encodedTarget ="{"+ComponentParser.MESSAGE+"}"; } else if (target instanceof ComponentReference) { encodedTarget = target.getName(); } else { error("unknown kind of target for relation "+relation); } return encodedTarget; } /** * Free references to all intermediate objects (to allow reuse of this encoder) and * returns the final result */ private Capability result() { this.component = null; this.reporter = null; Capability result = this.result.build(); this.result = null; return result; } /** * Add a new property to the result capability */ private void property(String name, String type, String value) { result = result.property(name, type, value); } /** * Add a new untyped property to the result capability */ private void property(String name, String value) { property(name,null,value); } /** * Convert a boolean value to its serialization */ private static String flag(boolean flag) { return Boolean.toString(flag); } /** * Convert a component kind to its internal representation */ private static String kind(ComponentKind componentKind) { switch (componentKind) { case SPECIFICATION: return CST.SPECIFICATION; case IMPLEMENTATION: return CST.IMPLEMENTATION; case INSTANCE: return CST.INSTANCE; default: return null; } } private final void error(String message) { reporter.report(Severity.ERROR, message); } private final void info(String message) { reporter.report(Severity.INFO, message); } /** * A simple builder to construct an OBR capability * * @author vega * */ public static class Builder { private String name; private List<Property> properties; private Builder() { this.properties = new ArrayList<Property>(); } public Builder name(String name) { this.name = name; return this; } public Builder property(String name, String type, String value) { this.properties.add(new SerializationProperty(name,type,value)); return this; } public Builder property(String name, String value) { return property(name,null,value); } public Capability build() { return new SerializationCapability(name,properties); } } /** * Get a new capability builder */ public static Builder builder(String name) { return new Builder().name(name); } /** * Internal read only capability representation * * TODO We should reuse the felix OBR implementation, but there is currently no public * API to create capabilities with typed properties * * @author vega * */ private static class SerializationCapability implements Capability { private final String name; private final Property[] properties; public SerializationCapability(String name, List<Property> properties) { this.name = name; this.properties = properties.toArray(new Property[properties.size()]);; } @Override public String getName() { return name; } @Override public Property[] getProperties() { return properties; } @Override public Map<String,?> getPropertiesAsMap() { throw new UnsupportedOperationException("This is capability implementation is intended only for serialization"); } } /** * Internal read only property representation * * TODO We should reuse the felix OBR implementation, but there is currently no public * API to create typed properties * * @author vega * */ private static class SerializationProperty implements Property { private final String name; private final String type; private final String value; public SerializationProperty(String name, String type, String value) { this.name = name; this.type = type; this.value = value; } @Override public String getName() { return name; } @Override public String getType() { return type; } @Override public String getValue() { return value; } @Override public Object getConvertedValue() { throw new UnsupportedOperationException("This property implementation is intended only for serialization"); } } /** * Creates a new requirement to represent a component reference */ public static Requirement requirement(VersionedReference<?> reference) { return new ComponentRequirement(reference); } /** * A requirement inferred from a referenced component version * * @author vega * */ private static class ComponentRequirement implements Requirement { private final VersionedReference<?> reference; private final String filter; public ComponentRequirement(VersionedReference<?> reference) { this.reference = reference; String filter = null; try { filter = ApamComponentRepository.filter(reference); } catch(Exception parseException) { } this.filter = filter; } @Override public String getName() { return CST.CAPABILITY_COMPONENT; } @Override public String getFilter() { return filter; } @Override public boolean isMultiple() { return false; } @Override public boolean isOptional() { return false; } @Override public boolean isExtend() { return false; } @Override public String getComment() { return "required component : "+reference.getName()+" version "+ reference.getRange(); } @Override public boolean isSatisfied(Capability capability) { throw new UnsupportedOperationException("This requirement implementation is intended only for serialization"); } } }