| /* |
| * 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.ArrayList; |
| import java.util.List; |
| |
| 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.ExternalAttachment; |
| 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<Composite> 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() && definitions.getExternalAttachments().isEmpty()) ) { |
| return composite; |
| } |
| |
| |
| Document document = null; |
| |
| for (PolicySet ps : definitions.getPolicySets()) { |
| XPathExpression exp = ps.getAttachToXPathExpression(); |
| if ( exp != null ) { |
| if ( document == null ) { |
| document = saveAsDOM(composite); |
| } |
| NodeList nodes = (NodeList) exp.evaluate(document, XPathConstants.NODESET); |
| attachPolicySetToNodes(composite, monitor, nodes, ps); |
| } |
| } |
| |
| for ( ExternalAttachment ea : definitions.getExternalAttachments() ) { |
| XPathExpression exp = ea.getAttachToXPathExpression(); |
| if ( exp != null ) { |
| if ( document == null ) { |
| document = saveAsDOM(composite); |
| } |
| NodeList nodes = (NodeList) exp.evaluate(document, XPathConstants.NODESET); |
| for ( PolicySet ps : ea.getPolicySets() ) { |
| attachPolicySetToNodes(composite, monitor, nodes, ps); |
| } |
| } |
| } |
| |
| // Recursively apply the xpath against the composites referenced by <implementation.composite> |
| // If the composite or component has policy sets attached, we have to ignore policy sets |
| // attached to the inner composite. |
| if ( composite.getPolicySets().isEmpty() ) { |
| for (Component component : composite.getComponents()) { |
| if ( component.getPolicySets().isEmpty() ) { |
| Implementation impl = component.getImplementation(); |
| if (impl instanceof Composite) { |
| Composite patched = applyXPath((Composite)impl, definitions, monitor); |
| if (patched != impl) { |
| component.setImplementation(patched); |
| } |
| } |
| } |
| } |
| } |
| |
| return composite; |
| } finally { |
| monitor.popContext(); |
| } |
| } |
| |
| private void attachPolicySetToNodes(Composite composite, |
| Monitor monitor, NodeList nodes, PolicySet ps) { |
| |
| for (int i = 0; i < nodes.getLength(); i++) { |
| Node node = nodes.item(i); |
| |
| if ( isAttachedToProperty(node) ) { |
| Monitor.error(monitor, |
| this, |
| BUILDER_VALIDATION_BUNDLE, |
| "PolicyAttachedToProperty", |
| ps.getName().toString()); |
| } |
| |
| |
| // The node can be a component, service, reference or binding |
| String index = getStructuralURI(node); |
| PolicySubject subject = lookup(composite, index); |
| if (subject != null) { |
| ps.setIsExternalAttachment(true); |
| // Remove any PolicySets with the same name that may have been added |
| List<PolicySet> subjectPSCopy = new ArrayList<PolicySet>(subject.getPolicySets()); |
| for ( PolicySet existingPS : subjectPSCopy ) { |
| if ( existingPS.getName().equals(ps.getName()) ) { |
| subject.getPolicySets().remove(existingPS); |
| } |
| } |
| 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); |
| } |
| |
| } |
| } |
| |
| /** |
| * 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 |
| * @param node |
| * @return |
| */ |
| private boolean isAttachedToProperty(Node node) { |
| |
| Node testNode = node; |
| while (testNode != null){ |
| if ((node.getNodeType() == Node.ELEMENT_NODE) && |
| (node.getLocalName().equals("property"))){ |
| return true; |
| } |
| testNode = testNode.getParentNode(); |
| } |
| return false; |
| } |
| |
| 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("<!-- DOM to which XPath will be applies is -->\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 if ( new QName(Base.SCA11_NS, "composite").equals(name)) { |
| return ""; |
| } 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()"; |
| } else if (localName.startsWith("interface.")) { |
| 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 + "#" + contractName + "#interface()"; //(" + contractName + "/" + interfaceName + ")" |
| } |
| } |
| } |
| 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; |
| } else if ( structuralURI.equals("")) { |
| return composite; |
| } |
| int index = structuralURI.indexOf('#'); |
| String componentURI = structuralURI; |
| String service = null; |
| String reference = null; |
| String binding = null; |
| boolean isInterface = false; |
| 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; |
| } else if ( prefix.indexOf("#interface") != -1 ) { |
| service = prefix.substring(0, prefix.indexOf("#interface")); |
| isInterface = true; |
| } |
| } |
| } |
| } |
| for (Component component : composite.getComponents()) { |
| if (component.getURI().equals(componentURI)) { |
| if (service != null) { |
| ComponentService componentService = component.getService(service); |
| if ( isInterface ) { |
| return componentService.getInterfaceContract().getInterface(); |
| } else 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; |
| } |
| } |