/* * 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.builder.impl; import java.io.IOException; import java.io.StringWriter; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import org.apache.tuscany.sca.assembly.Base; import org.apache.tuscany.sca.assembly.Binding; import org.apache.tuscany.sca.assembly.Component; import org.apache.tuscany.sca.assembly.ComponentReference; import org.apache.tuscany.sca.assembly.ComponentService; import org.apache.tuscany.sca.assembly.Composite; import org.apache.tuscany.sca.assembly.Contract; import org.apache.tuscany.sca.assembly.Implementation; import org.apache.tuscany.sca.assembly.builder.BuilderContext; import org.apache.tuscany.sca.assembly.builder.CompositeBuilder; import org.apache.tuscany.sca.assembly.builder.CompositeBuilderException; import org.apache.tuscany.sca.common.xml.dom.DOMHelper; import org.apache.tuscany.sca.common.xml.stax.StAXHelper; import org.apache.tuscany.sca.contribution.processor.ContributionWriteException; import org.apache.tuscany.sca.contribution.processor.ProcessorContext; import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor; import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint; import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.definitions.Definitions; import org.apache.tuscany.sca.monitor.Monitor; import org.apache.tuscany.sca.policy.PolicySet; import org.apache.tuscany.sca.policy.PolicySubject; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * A builder that attaches policy sets to the domain composite using the xpath defined by * the attachTo attribute. It first creates a DOM model for the composite so that the xpath * expression can be evaluated. For the nodes selected by the xpath, caluclate the element * URI and add the policy set into the composite model * * @version $Rev$ $Date$ */ public class PolicyAttachmentBuilderImpl implements CompositeBuilder { protected static final String BUILDER_VALIDATION_BUNDLE = "org.apache.tuscany.sca.builder.builder-validation-messages"; protected StAXHelper staxHelper; protected DOMHelper domHelper; protected ExtensionPointRegistry registry; protected StAXArtifactProcessor processor; public PolicyAttachmentBuilderImpl(ExtensionPointRegistry registry) { this.registry = registry; domHelper = DOMHelper.getInstance(registry); staxHelper = StAXHelper.getInstance(registry); StAXArtifactProcessorExtensionPoint processors = registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class); processor = processors.getProcessor(Composite.class); } public String getID() { return "org.apache.tuscany.sca.policy.builder.PolicyAttachmentBuilder"; } public Composite build(Composite composite, BuilderContext context) throws CompositeBuilderException { try { Composite patched = applyXPath(composite, context.getDefinitions(), context.getMonitor()); return patched; } catch (Exception e) { throw new CompositeBuilderException(e); } } /** * Apply the attachTo XPath against the composite model * @param composite The orginal composite * @param definitions SCA definitions that contain the policy sets * @param monitor The monitor * @return A reloaded composite * @throws Exception */ private Composite applyXPath(Composite composite, Definitions definitions, Monitor monitor) throws Exception { monitor.pushContext("Composite: " + composite.getName().toString()); try { if (definitions == null || definitions.getPolicySets().isEmpty()) { return composite; } // Recursively apply the xpath against the composites referenced by for (Component component : composite.getComponents()) { Implementation impl = component.getImplementation(); if (impl instanceof Composite) { Composite patched = applyXPath((Composite)impl, definitions, monitor); if (patched != impl) { component.setImplementation(patched); } } } Document document = null; for (PolicySet ps : definitions.getPolicySets()) { // First calculate the applicable nodes Set applicableNodes = null; /* XPathExpression appliesTo = ps.getAppliesToXPathExpression(); if (appliesTo != null) { applicableNodes = new HashSet(); NodeList nodes = (NodeList)appliesTo.evaluate(document, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { applicableNodes.add(nodes.item(i)); } } */ XPathExpression exp = ps.getAttachToXPathExpression(); if (exp != null) { if (document == null) { document = saveAsDOM(composite); } NodeList nodes = (NodeList)exp.evaluate(document, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); // POL_40002 - you can't attach a policy to a property node // or one of it's children // walk backwards up the node tree looking for an element called property // and raise an error if we find one Node testNode = node; while (testNode != null){ if ((node.getNodeType() == Node.ELEMENT_NODE) && (node.getLocalName().equals("property"))){ Monitor.error(monitor, this, BUILDER_VALIDATION_BUNDLE, "PolicyAttachedToProperty", ps.getName().toString()); break; } testNode = testNode.getParentNode(); } if (applicableNodes == null || applicableNodes.contains(node)) { // The node can be a component, service, reference or binding String index = getStructuralURI(node); PolicySubject subject = lookup(composite, index); if (subject != null) { subject.getPolicySets().add(ps); } else { // raise a warning that the XPath node didn't match a node in the // models Monitor.warning(monitor, this, BUILDER_VALIDATION_BUNDLE, "PolicyDOMModelMissmatch", ps.getName().toString(), index); } } } } } return composite; } finally { monitor.popContext(); } } protected Document saveAsDOM(Composite composite) throws XMLStreamException, ContributionWriteException, IOException, SAXException { // First write the composite into a DOM document so that we can apply the xpath StringWriter sw = new StringWriter(); XMLStreamWriter writer = staxHelper.createXMLStreamWriter(sw); // Write the composite into a DOM document processor.write(composite, writer, new ProcessorContext(registry)); writer.close(); Document document = domHelper.load(sw.toString()); // Debugging //System.out.println("\n" + sw.toString()); return document; } private static final QName COMPONENT = new QName(Base.SCA11_NS, "component"); private static final QName SERVICE = new QName(Base.SCA11_NS, "service"); private static final QName REFERENCE = new QName(Base.SCA11_NS, "reference"); protected static String getStructuralURI(Node node) { if (node != null) { QName name = new QName(node.getNamespaceURI(), node.getLocalName()); if (COMPONENT.equals(name)) { Element element = (Element)node; return element.getAttributeNS(null, "uri"); } else if (SERVICE.equals(name)) { Element component = (Element)node.getParentNode(); String uri = component.getAttributeNS(null, "uri"); String service = ((Element)node).getAttributeNS(null, "name"); return uri + "#service(" + service + ")"; } else if (REFERENCE.equals(name)) { Element component = (Element)node.getParentNode(); String uri = component.getAttributeNS(null, "uri"); String reference = ((Element)node).getAttributeNS(null, "name"); return uri + "#reference(" + reference + ")"; } else { String localName = node.getLocalName(); if (localName.startsWith("binding.")) { String bindingName = ((Element)node).getAttributeNS(null, "name"); Element contract = (Element)node.getParentNode(); String contractName = contract.getAttributeNS(null, "name"); Element component = (Element)node.getParentNode().getParentNode(); String uri = component.getAttributeNS(null, "uri"); return uri + "#" + contract.getLocalName() + "(" + contractName + "/" + bindingName + ")"; } else if (localName.startsWith("implementation.")) { Element component = (Element)node.getParentNode(); String uri = component.getAttributeNS(null, "uri"); return uri + "#implementation()"; } } } return null; } protected Binding getBinding(Contract contract, String name) { for (Binding binding : contract.getBindings()) { if (name.equals(binding.getName())) { return binding; } } return null; } protected PolicySubject lookup(Composite composite, String structuralURI) { if (structuralURI == null) { return null; } int index = structuralURI.indexOf('#'); String componentURI = structuralURI; String service = null; String reference = null; String binding = null; boolean impl = false; if (index != -1) { componentURI = structuralURI.substring(0, index); String fragment = structuralURI.substring(index + 1); int begin = fragment.indexOf('('); int end = fragment.indexOf(')'); if (begin != -1 && end != -1) { String path = fragment.substring(begin + 1, end).trim(); String prefix = fragment.substring(0, begin).trim(); if (prefix.equals("implementation")) { impl = true; } else { int pos = path.indexOf('/'); if (pos != -1) { binding = path.substring(pos + 1); path = path.substring(0, pos); if ("service-binding".equals(prefix)) { service = path; } else if ("reference-binding".equals(prefix)) { reference = path; } } if ("service".equals(prefix)) { service = path; } else if ("reference".equals(prefix)) { reference = path; } } } } for (Component component : composite.getComponents()) { if (component.getURI().equals(componentURI)) { if (service != null) { ComponentService componentService = component.getService(service); if (binding != null) { Binding b = getBinding(componentService, binding); if (b instanceof PolicySubject) { return (PolicySubject)b; } } else { return componentService; } } else if (reference != null) { ComponentReference componentReference = component.getReference(reference); if (binding != null) { Binding b = getBinding(componentReference, binding); if (b instanceof PolicySubject) { return (PolicySubject)b; } } else { return componentReference; } } else if (impl) { return component.getImplementation(); } return component; } else if (structuralURI.startsWith(component.getURI() + "/")) { Implementation implementation = component.getImplementation(); if (implementation instanceof Composite) { return lookup((Composite)implementation, structuralURI); } else { return null; } } } return null; } }