/*
* 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.tuscany.sca.implementation.osgi.xml;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI;
import static javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import org.apache.tuscany.sca.assembly.AssemblyFactory;
import org.apache.tuscany.sca.assembly.ComponentProperty;
import org.apache.tuscany.sca.assembly.ComponentType;
import org.apache.tuscany.sca.assembly.Property;
import org.apache.tuscany.sca.assembly.Reference;
import org.apache.tuscany.sca.assembly.Service;
import org.apache.tuscany.sca.assembly.builder.impl.ProblemImpl;
import org.apache.tuscany.sca.assembly.xml.Constants;
import org.apache.tuscany.sca.contribution.ModelFactoryExtensionPoint;
import org.apache.tuscany.sca.contribution.osgi.BundleReference;
import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
import org.apache.tuscany.sca.contribution.resolver.ClassReference;
import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
import org.apache.tuscany.sca.contribution.service.ContributionReadException;
import org.apache.tuscany.sca.contribution.service.ContributionResolveException;
import org.apache.tuscany.sca.contribution.service.ContributionWriteException;
import org.apache.tuscany.sca.databinding.impl.SimpleTypeMapperImpl;
import org.apache.tuscany.sca.implementation.osgi.impl.OSGiImplementationImpl;
import org.apache.tuscany.sca.interfacedef.Interface;
import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException;
import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract;
import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory;
import org.apache.tuscany.sca.monitor.Monitor;
import org.apache.tuscany.sca.monitor.Problem;
import org.apache.tuscany.sca.monitor.Problem.Severity;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
*
* Process an element in a component definition. An instance of
* OSGiImplementation is created.
* Also associates the component type file with the implementation.
*
* @version $Rev$ $Date$
*/
public class OSGiImplementationProcessor implements StAXArtifactProcessor {
public static final QName IMPLEMENTATION_OSGI = new QName(Constants.SCA10_TUSCANY_NS, "implementation.osgi");
private static final String BUNDLE_SYMBOLICNAME= "bundleSymbolicName";
private static final String BUNDLE_VERSION = "bundleVersion";
private static final String CLASSES = "classes";
private static final String IMPORTS = "imports";
private static final QName PROPERTIES_QNAME = new QName(Constants.SCA10_TUSCANY_NS, "properties");
private static final QName PROPERTY_QNAME = new QName(Constants.SCA10_TUSCANY_NS, "property");
private JavaInterfaceFactory javaInterfaceFactory;
private AssemblyFactory assemblyFactory;
private ModelFactoryExtensionPoint modelFactories;
private Monitor monitor;
private static final DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
static {
domFactory.setNamespaceAware(true);
}
public OSGiImplementationProcessor(ModelFactoryExtensionPoint modelFactories, Monitor monitor) {
this.monitor = monitor;
this.modelFactories = modelFactories;
this.assemblyFactory = modelFactories.getFactory(AssemblyFactory.class);
this.javaInterfaceFactory = modelFactories.getFactory(JavaInterfaceFactory.class);
}
/**
* Report a exception.
*
* @param problems
* @param message
* @param model
*/
private void error(String message, Object model, Exception ex) {
if (monitor != null) {
Problem problem = new ProblemImpl(this.getClass().getName(), "impl-osgi-validation-messages", Severity.ERROR, model, message, ex);
monitor.problem(problem);
}
}
/**
* Report a error.
*
* @param problems
* @param message
* @param model
*/
private void error(String message, Object model, Object... messageParameters) {
if (monitor != null) {
Problem problem = new ProblemImpl(this.getClass().getName(), "impl-osgi-validation-messages", Severity.ERROR, model, message, (Object[])messageParameters);
monitor.problem(problem);
}
}
public QName getArtifactType() {
return IMPLEMENTATION_OSGI;
}
public Class getModelType() {
return OSGiImplementationImpl.class;
}
private String[] tokenize(String str) {
StringTokenizer tokenizer = new StringTokenizer(str);
String[] tokens = new String[tokenizer.countTokens()];
for (int i= 0; i < tokens.length; i++) {
tokens[i] = tokenizer.nextToken();
}
return tokens;
}
public OSGiImplementationImpl read(XMLStreamReader reader) throws ContributionReadException, XMLStreamException {
assert IMPLEMENTATION_OSGI.equals(reader.getName());
String bundleSymbolicName = reader.getAttributeValue(null, BUNDLE_SYMBOLICNAME);
String bundleVersion = reader.getAttributeValue(null, BUNDLE_VERSION);
String imports = reader.getAttributeValue(null, IMPORTS);
String[] importList;
if (imports != null)
importList = tokenize(imports);
else
importList = new String[0];
String classes = reader.getAttributeValue(null, CLASSES);
String[] classList;
if (classes != null)
classList = tokenize(classes);
else
classList = new String[0];
Hashtable> refProperties =
new Hashtable>();
Hashtable> serviceProperties =
new Hashtable>();
Hashtable> refCallbackProperties =
new Hashtable>();
Hashtable> serviceCallbackProperties =
new Hashtable>();
while (reader.hasNext()) {
int next = reader.next();
if (next == END_ELEMENT && IMPLEMENTATION_OSGI.equals(reader.getName())) {
break;
}
else if (next == START_ELEMENT && PROPERTIES_QNAME.equals(reader.getName())) {
// FIXME: This is temporary code which allows reference and service properties used
// for filtering OSGi services to be specified in
// This should really be provided in the component type file since these
// properties are associated with an implementation rather than a configured
// instance of an implementation.
String refName = reader.getAttributeValue(null, "reference");
String serviceName = reader.getAttributeValue(null, "service");
String refCallbackName = reader.getAttributeValue(null, "referenceCallback");
String serviceCallbackName = reader.getAttributeValue(null, "serviceCallback");
List props = readProperties(reader);
if (refName != null)
refProperties.put(refName, props);
else if (serviceName != null)
serviceProperties.put(serviceName, props);
else if (refCallbackName != null)
refCallbackProperties.put(refCallbackName, props);
else if (serviceCallbackName != null)
serviceCallbackProperties.put(serviceCallbackName, props);
else {
error("PropertyShouldSpecifySR", reader);
//throw new ContributionReadException("Properties in implementation.osgi should specify service or reference");
}
}
}
OSGiImplementationImpl implementation = new OSGiImplementationImpl(
modelFactories,
bundleSymbolicName,
bundleVersion,
importList,
classList,
refProperties,
serviceProperties);
implementation.setCallbackProperties(refCallbackProperties, serviceCallbackProperties);
implementation.setUnresolved(true);
return implementation;
}
public void resolve(OSGiImplementationImpl impl, ModelResolver resolver) throws ContributionResolveException {
try {
if (impl == null || !impl.isUnresolved())
return;
impl.setUnresolved(false);
BundleReference bundleReference = new BundleReference(impl.getBundleSymbolicName(), impl.getBundleVersion());
BundleReference resolvedBundle = resolver.resolveModel(BundleReference.class, bundleReference);
Bundle bundle = (Bundle)resolvedBundle.getBundle();
if (bundle != null) {
impl.setOSGiBundle(bundle);
} else {
error("CouldNotLocateOSGiBundle", impl, impl.getBundleSymbolicName());
//throw new ContributionResolveException("Could not locate OSGi bundle " +
//impl.getBundleSymbolicName());
return;
}
String bundleName = resolvedBundle.getBundleRelativePath();
String ctURI = bundleName.endsWith(".jar") || bundleName.endsWith(".JAR")?
bundleName.substring(0, bundleName.lastIndexOf(".")) : bundleName;
ctURI = ctURI.replaceAll("\\.", "/");
ctURI = ctURI + ".componentType";
ComponentType componentType = assemblyFactory.createComponentType();
componentType.setURI(ctURI);
componentType.setUnresolved(true);
componentType = resolver.resolveModel(ComponentType.class, componentType);
if (componentType.isUnresolved()) {
error("MissingComponentTypeFile", impl, ctURI);
//throw new ContributionResolveException("missing .componentType side file " + ctURI);
return;
}
List services = componentType.getServices();
for (Service service : services) {
Interface interfaze = service.getInterfaceContract().getInterface();
if (interfaze instanceof JavaInterface) {
JavaInterface javaInterface = (JavaInterface)interfaze;
if (javaInterface.getJavaClass() == null) {
javaInterface.setJavaClass(getJavaClass(resolver, javaInterface.getName()));
}
Class> callback = null;
if (service.getInterfaceContract().getCallbackInterface() instanceof JavaInterface) {
JavaInterface callbackInterface = (JavaInterface)service.getInterfaceContract().getCallbackInterface();
if (callbackInterface.getJavaClass() == null) {
callbackInterface.setJavaClass(getJavaClass(resolver, callbackInterface.getName()));
}
callback = callbackInterface.getJavaClass();
}
Service serv = createService(service, javaInterface.getJavaClass(), callback);
impl.getServices().add(serv);
}
}
List references = componentType.getReferences();
for (Reference reference : references) {
Interface interfaze = reference.getInterfaceContract().getInterface();
if (interfaze instanceof JavaInterface) {
JavaInterface javaInterface = (JavaInterface)interfaze;
if (javaInterface.getJavaClass() == null) {
javaInterface.setJavaClass(getJavaClass(resolver, javaInterface.getName()));
}
Reference ref = createReference(reference, javaInterface.getJavaClass());
impl.getReferences().add(ref);
}
else
impl.getReferences().add(reference);
}
List properties = componentType.getProperties();
for (Property property : properties) {
impl.getProperties().add(property);
}
impl.setConstrainingType(componentType.getConstrainingType());
} catch (InvalidInterfaceException e) {
ContributionResolveException ce = new ContributionResolveException(e);
error("ContributionResolveException", resolver, ce);
//throw ce;
}
}
private Class getJavaClass(ModelResolver resolver, String className) {
ClassReference ref = new ClassReference(className);
ref = resolver.resolveModel(ClassReference.class, ref);
return ref.getJavaClass();
}
private Service createService(Service serv, Class> interfaze, Class> callbackInterfaze) throws InvalidInterfaceException {
Service service = assemblyFactory.createService();
JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
service.setInterfaceContract(interfaceContract);
// create a relative URI
service.setName(serv.getName());
JavaInterface callInterface = javaInterfaceFactory.createJavaInterface(interfaze);
service.getInterfaceContract().setInterface(callInterface);
if (callbackInterfaze != null) {
JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(callbackInterfaze);
service.getInterfaceContract().setCallbackInterface(callbackInterface);
}
else if (callInterface.getCallbackClass() != null) {
JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(callInterface.getCallbackClass());
service.getInterfaceContract().setCallbackInterface(callbackInterface);
}
return service;
}
private Reference createReference(Reference ref, Class> clazz) throws InvalidInterfaceException {
org.apache.tuscany.sca.assembly.Reference reference = assemblyFactory.createReference();
JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
reference.setInterfaceContract(interfaceContract);
reference.setName(ref.getName());
reference.setMultiplicity(ref.getMultiplicity());
JavaInterface callInterface = javaInterfaceFactory.createJavaInterface(clazz);
reference.getInterfaceContract().setInterface(callInterface);
if (callInterface.getCallbackClass() != null) {
JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(callInterface.getCallbackClass());
reference.getInterfaceContract().setCallbackInterface(callbackInterface);
}
return reference;
}
public void write(OSGiImplementationImpl model, XMLStreamWriter outputSource) throws ContributionWriteException, XMLStreamException {
//FIXME Implement this method
}
private QName getQNameValue(XMLStreamReader reader, String value) {
if (value != null) {
int index = value.indexOf(':');
String prefix = index == -1 ? "" : value.substring(0, index);
String localName = index == -1 ? value : value.substring(index + 1);
String ns = reader.getNamespaceContext().getNamespaceURI(prefix);
if (ns == null) {
ns = "";
}
return new QName(ns, localName, prefix);
} else {
return null;
}
}
private void declareNamespace(Element element, String prefix, String ns) {
String qname = null;
if ("".equals(prefix)) {
qname = "xmlns";
} else {
qname = "xmlns:" + prefix;
}
Node node = element;
boolean declared = false;
while (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
NamedNodeMap attrs = node.getAttributes();
if (attrs == null) {
break;
}
Node attr = attrs.getNamedItem(qname);
if (attr != null) {
declared = ns.equals(attr.getNodeValue());
break;
}
node = node.getParentNode();
}
if (!declared) {
org.w3c.dom.Attr attr = element.getOwnerDocument().createAttributeNS(XMLNS_ATTRIBUTE_NS_URI, qname);
attr.setValue(ns);
element.setAttributeNodeNS(attr);
}
}
private Element createElement(Document document, QName name) {
String prefix = name.getPrefix();
String qname = (prefix != null && prefix.length() > 0) ? prefix + ":" + name.getLocalPart() : name
.getLocalPart();
return document.createElementNS(name.getNamespaceURI(), qname);
}
private void loadElement(XMLStreamReader reader, Element root) throws XMLStreamException {
Document document = root.getOwnerDocument();
Node current = root;
while (true) {
switch (reader.next()) {
case XMLStreamConstants.START_ELEMENT:
QName name = reader.getName();
Element child = createElement(document, name);
// push the new element and make it the current one
current.appendChild(child);
current = child;
declareNamespace(child, name.getPrefix(), name.getNamespaceURI());
int count = reader.getNamespaceCount();
for (int i = 0; i < count; i++) {
String prefix = reader.getNamespacePrefix(i);
String ns = reader.getNamespaceURI(i);
declareNamespace(child, prefix, ns);
}
// add the attributes for this element
count = reader.getAttributeCount();
for (int i = 0; i < count; i++) {
String ns = reader.getAttributeNamespace(i);
String prefix = reader.getAttributePrefix(i);
String localPart = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
child.setAttributeNS(ns, localPart, value);
declareNamespace(child, prefix, ns);
}
break;
case XMLStreamConstants.CDATA:
current.appendChild(document.createCDATASection(reader.getText()));
break;
case XMLStreamConstants.CHARACTERS:
current.appendChild(document.createTextNode(reader.getText()));
break;
case XMLStreamConstants.END_ELEMENT:
// if we are back at the root then we are done
if (current == root) {
return;
}
// pop the element off the stack
current = current.getParentNode();
}
}
}
private Document readPropertyValue(XMLStreamReader reader, QName type)
throws XMLStreamException, ParserConfigurationException {
Document doc = domFactory.newDocumentBuilder().newDocument();
// root element has no namespace and local name "value"
Element root = doc.createElementNS(null, "value");
if (type != null) {
org.w3c.dom.Attr xsi = doc.createAttributeNS(XMLNS_ATTRIBUTE_NS_URI, "xmlns:xsi");
xsi.setValue(W3C_XML_SCHEMA_INSTANCE_NS_URI);
root.setAttributeNodeNS(xsi);
String prefix = type.getPrefix();
if (prefix == null || prefix.length() == 0) {
prefix = "ns";
}
declareNamespace(root, prefix, type.getNamespaceURI());
org.w3c.dom.Attr xsiType = doc.createAttributeNS(W3C_XML_SCHEMA_INSTANCE_NS_URI, "xsi:type");
xsiType.setValue(prefix + ":" + type.getLocalPart());
root.setAttributeNodeNS(xsiType);
}
doc.appendChild(root);
loadElement(reader, root);
return doc;
}
private void readProperty(ComponentProperty prop, XMLStreamReader reader)
throws XMLStreamException, ContributionReadException {
prop.setName(reader.getAttributeValue(null, "name"));
String xsdType = reader.getAttributeValue(null, "type");
if (xsdType != null)
prop.setXSDType(getQNameValue(reader, xsdType));
else
prop.setXSDType(SimpleTypeMapperImpl.XSD_STRING);
try {
Document value = readPropertyValue(reader, prop.getXSDType());
prop.setValue(value);
} catch (ParserConfigurationException e) {
ContributionReadException ce = new ContributionReadException(e);
error("ContributionReadException", reader, ce);
throw ce;
}
}
private List readProperties(XMLStreamReader reader)
throws XMLStreamException, ContributionReadException {
List properties = new ArrayList();
while (reader.hasNext()) {
int next = reader.next();
if (next == END_ELEMENT && PROPERTIES_QNAME.equals(reader.getName())) {
break;
}
else if (next == START_ELEMENT && PROPERTY_QNAME.equals(reader.getName())) {
ComponentProperty componentProperty = assemblyFactory.createComponentProperty();
readProperty(componentProperty, reader);
properties.add(componentProperty);
}
}
return properties;
}
}