/* * Copyright 2013 Brian Rosenberger (Brutex Network) * * Licensed 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 net.brutex.xservices.ws.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import javax.jws.WebService; import javax.ws.rs.NotAuthorizedException; import javax.xml.namespace.QName; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import net.brutex.xservices.security.DirectoryPermission; import net.brutex.xservices.types.AttributeType; import net.brutex.xservices.types.NamespaceListType; import net.brutex.xservices.types.NamespaceType; import net.brutex.xservices.types.StringSplitType; import net.brutex.xservices.types.ant.FileResource; import net.brutex.xservices.ws.XServicesFault; import net.brutex.xservices.ws.XmlService; import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMCloneOptions; import org.apache.axiom.om.OMComment; import org.apache.axiom.om.OMContainer; import org.apache.axiom.om.OMDocument; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNode; import org.apache.axiom.om.OMProcessingInstruction; import org.apache.axiom.om.OMText; import org.apache.axiom.om.OMXMLBuilderFactory; import org.apache.axiom.om.xpath.AXIOMXPath; import org.apache.log4j.Logger; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.jaxen.JaxenException; import org.jaxen.SimpleNamespaceContext; /** * @author Brian Rosenberger, bru(at)brutex.de * */ @WebService(targetNamespace = "http://ws.xservices.brutex.net", endpointInterface = "net.brutex.xservices.ws.XmlService", serviceName = "XmlService") public class XmlServiceImpl implements XmlService { final Logger logger = Logger.getLogger(XmlServiceImpl.class); public String insertNodesFromFile(FileResource res, NamespaceListType nsList, String xpath, String xmlFragment) throws XServicesFault { try { AXIOMXPath axp = new AXIOMXPath(xpath); InputStream is = res.getAntResource(null).getInputStream(); OMDocument sourcedoc = OMXMLBuilderFactory.createOMBuilder(is) .getDocument(); OMDocument fragdoc = null; if ((xmlFragment != null) && (new String(xmlFragment).length() > 0)) { fragdoc = OMXMLBuilderFactory.createOMBuilder( new StringReader("" + xmlFragment + "")) .getDocument(); } else { throw new XServicesFault("No xmldata to insert."); } // Initialize XPath context SimpleNamespaceContext context = createContext(nsList); axp.setNamespaceContext(context); axp.addNamespaces(fragdoc.getOMDocumentElement()); OMDocument document = insertNodes(sourcedoc, axp, fragdoc); StringWriter sw = new StringWriter(); XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter writer = xof.createXMLStreamWriter(sw); document.serialize(writer); this.logger.trace(sw.getBuffer().toString()); return sw.getBuffer().toString(); } catch (JaxenException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (IOException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (XMLStreamException e) { e.printStackTrace(); throw new XServicesFault(e); } } public String replaceNodesFromFile(FileResource res, NamespaceListType nsList, String xpath, String xmlFragment) throws XServicesFault { try { AXIOMXPath axp = new AXIOMXPath(xpath); InputStream is = res.getAntResource(null).getInputStream(); OMDocument sourcedoc = OMXMLBuilderFactory.createOMBuilder(is).getDocument(); OMDocument fragdoc = null; if ((xmlFragment != null) && (new String(xmlFragment).length() > 0)) { fragdoc = OMXMLBuilderFactory.createOMBuilder( new StringReader("" + xmlFragment + "")) .getDocument(); } else { throw new XServicesFault("No xmldata to insert."); } // Initialize XPath context SimpleNamespaceContext context = createContext(nsList); axp.setNamespaceContext(context); axp.addNamespaces(fragdoc.getOMDocumentElement()); OMDocument document = replaceNodes(sourcedoc, axp, fragdoc); StringWriter sw = new StringWriter(); XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter writer = xof.createXMLStreamWriter(sw); document.serialize(writer); this.logger.trace(sw.getBuffer().toString()); return sw.getBuffer().toString(); } catch (JaxenException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (XMLStreamException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (IOException e) { e.printStackTrace(); throw new XServicesFault(e); } } public String replaceNodes(String source, String encoding, NamespaceListType nsList, String xpath, String xmlFragment) throws XServicesFault { encoding = validateEncoding(encoding); try { AXIOMXPath axp = new AXIOMXPath(xpath); InputStream is = new ByteArrayInputStream(source.getBytes(encoding)); OMDocument sourcedoc = OMXMLBuilderFactory.createOMBuilder(is) .getDocument(); OMDocument fragdoc = null; if ((xmlFragment != null) && (new String(xmlFragment).length() > 0)) { fragdoc = OMXMLBuilderFactory.createOMBuilder( new StringReader("" + xmlFragment + "")) .getDocument(); } else { throw new XServicesFault("No xmldata to insert."); } // Initialize XPath context SimpleNamespaceContext context = createContext(nsList); axp.setNamespaceContext(context); axp.addNamespaces(fragdoc.getOMDocumentElement()); OMDocument document = replaceNodes(sourcedoc, axp, fragdoc); StringWriter sw = new StringWriter(); XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter writer = xof.createXMLStreamWriter(sw); document.serialize(writer); this.logger.trace(sw.getBuffer().toString()); return sw.getBuffer().toString(); } catch (JaxenException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (XMLStreamException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (UnsupportedEncodingException e) { throw new XServicesFault(e); } } @RequiresPermissions("insertNodes") public String insertNodes(String source, String encoding, NamespaceListType nsList, String xpath, String xmlFragment) throws XServicesFault { encoding = validateEncoding(encoding); try { AXIOMXPath axp = new AXIOMXPath(xpath); InputStream is = new ByteArrayInputStream(source.getBytes(encoding)); OMDocument sourcedoc = OMXMLBuilderFactory.createOMBuilder(is) .getDocument(); OMDocument fragdoc = null; if ((xmlFragment != null) && (new String(xmlFragment).length() > 0)) { fragdoc = OMXMLBuilderFactory.createOMBuilder( new StringReader("" + xmlFragment + "")) .getDocument(); } else { throw new XServicesFault("No xmldata to insert."); } // Initialize XPath context SimpleNamespaceContext context = createContext(nsList); axp.setNamespaceContext(context); axp.addNamespaces(fragdoc.getOMDocumentElement()); OMDocument document = insertNodes(sourcedoc, axp, fragdoc); StringWriter sw = new StringWriter(); XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter writer = xof.createXMLStreamWriter(sw); document.serialize(writer); this.logger.trace(sw.getBuffer().toString()); return sw.getBuffer().toString(); } catch (JaxenException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (XMLStreamException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (UnsupportedEncodingException e) { throw new XServicesFault(e); } } public String wrapInCDATA(String data) throws XServicesFault { String result =""; String[] tokens = data.split("\\]\\]>", -1); for(int i=0; i"; return result; } public StringSplitType selectXPath(String source, String encoding, NamespaceListType nsList, String xpath) throws XServicesFault { encoding = validateEncoding(encoding); try { StringSplitType rarray = new StringSplitType(); AXIOMXPath axp = new AXIOMXPath(xpath); InputStream is = new ByteArrayInputStream(source.getBytes(encoding)); OMDocument sourcedoc = OMXMLBuilderFactory.createOMBuilder(is).getDocument(); // Initialize XPath context SimpleNamespaceContext context = createContext(nsList); axp.setNamespaceContext(context); List results = axp.selectNodes(sourcedoc); for(Object o : results) { String text = null; if(o instanceof OMNode) { switch (((OMNode)o).getType()) { case OMNode.TEXT_NODE: text = ((OMText)o).getText(); break; case OMNode.COMMENT_NODE: text = ((OMComment)o).getValue(); break; case OMNode.PI_NODE: text = ((OMProcessingInstruction)o).getValue(); break; default: StringWriter sw = new StringWriter(); XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter writer = xof.createXMLStreamWriter(sw); ((OMNode)o).serialize(writer); writer.flush(); text = sw.toString(); } } else if(o instanceof OMAttribute) { text = ((OMAttribute)o).getAttributeValue(); } else { text = String.valueOf(o); } rarray.addStringMatch(text); } return rarray; } catch (JaxenException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (XMLStreamException e) { // TODO Auto-generated catch block throw new XServicesFault(e.getMessage()); } catch (UnsupportedEncodingException e) { throw new XServicesFault(e); } } public String setAttribute(String source, String encoding, NamespaceListType nsList, String xpath, AttributeType attr) throws XServicesFault { encoding = validateEncoding(encoding); try { StringSplitType rarray = new StringSplitType(); AXIOMXPath axp = new AXIOMXPath(xpath); InputStream is = new ByteArrayInputStream(source.getBytes(encoding)); OMDocument sourcedoc = OMXMLBuilderFactory.createOMBuilder(is).getDocument(); OMFactory fac = OMAbstractFactory.getOMFactory(); // Initialize XPath context SimpleNamespaceContext context = createContext(nsList); axp.setNamespaceContext(context); List results = axp.selectNodes(sourcedoc); for(Object o : results) { String text = null; if(o instanceof OMNode) { switch (((OMNode)o).getType()) { case OMNode.ELEMENT_NODE: OMElement node = ((OMElement)o); if(attr.value == null) { node.removeAttribute( node.getAttribute(new QName(attr.name))); } else { node.addAttribute(attr.name, attr.value, node.getNamespace()); } break; default: throw new XServicesFault("XPath expression did not match an element node."); } } else { throw new XServicesFault("XPath expression did not match a node."); } } StringWriter sw = new StringWriter(); XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter writer = xof.createXMLStreamWriter(sw); sourcedoc.serialize(writer); writer.flush(); return sw.toString(); } catch (JaxenException e) { e.printStackTrace(); throw new XServicesFault(e); } catch (XMLStreamException e) { // TODO Auto-generated catch block throw new XServicesFault(e.getMessage()); } catch (UnsupportedEncodingException e) { throw new XServicesFault(e); } } private OMDocument insertNodes(OMDocument xmldocument, AXIOMXPath axp,OMDocument xmlfragment) throws XServicesFault { List olist = null; try { olist = axp.selectNodes(xmldocument.getOMDocumentElement()); this.logger.debug("XPath '" + axp.toString() + "' has " + olist.size() + " matches."); this.logger.trace("XPath root expression is: '" + axp.debug() + "'."); } catch (JaxenException e) { throw new XServicesFault(e.getMessage(), e); } if (olist.size() == 0) throw new XServicesFault(Messages.getString("XmlService.no_match", new Object[] { axp.toString() })); // Prepare children to insert xmlfragment.build(); // Determine what has been matched OMContainer match = null; for (Object o : olist) { Iterator children = xmlfragment.getOMDocumentElement() .getChildren(); if ((o instanceof OMNode)) { OMNode node = (OMNode) o; switch (node.getType()) { case OMNode.ELEMENT_NODE: if ((o instanceof OMElement)) match = (OMElement) o; if ((o instanceof OMDocument)) match = ((OMDocument) o).getOMDocumentElement(); this.logger.debug(Messages.getString("XmlService.8")); break; case OMNode.TEXT_NODE: match = ((OMText) o).getParent(); this.logger.debug(Messages.getString("XmlService.9")); break; case OMNode.COMMENT_NODE: // match = node.getParent(); match = (OMContainer) node; this.logger.debug(Messages.getString("XmlService.10")); break; default: this.logger.error("XPath matched " + o.getClass().getCanonicalName() + " Node Type:" + node.getType()); this.logger.error(Messages.getString("XmlService.11")); throw new XServicesFault( Messages.getString("XmlService.12")); } } else { this.logger.error("XPath matched " + o.getClass().getCanonicalName()); this.logger.error(Messages.getString("XmlService.11")); throw new XServicesFault(Messages.getString("XmlService.12")); } while (children.hasNext()) { OMNode container = (OMNode) children.next(); match.addChild((OMNode) container.clone(new OMCloneOptions())); } } xmldocument.build(); return xmldocument; } private OMDocument replaceNodes(OMDocument xmldocument, AXIOMXPath axp, OMDocument xmlfragment) throws XServicesFault { List olist = null; try { olist = axp.selectNodes(xmldocument.getOMDocumentElement()); this.logger.debug("XPath '" + axp.toString() + "' has " + olist.size() + " matches."); this.logger.trace("XPath root expression is: '" + axp.debug() + "'."); } catch (JaxenException e) { throw new XServicesFault(e.getMessage(), e); } if (olist.size() == 0) throw new XServicesFault(Messages.getString("XmlService.no_match", new Object[] { axp.toString() })); // Prepare children to insert xmlfragment.build(); // Determine what has been matched OMNode match = null; for (Object o : olist) { Iterator children = xmlfragment.getOMDocumentElement() .getChildren(); if ((o instanceof OMNode)) { OMNode node = (OMNode) o; switch (node.getType()) { case OMNode.ELEMENT_NODE: if ((o instanceof OMElement)) match = (OMElement) o; if ((o instanceof OMDocument)) match = ((OMDocument) o).getOMDocumentElement(); this.logger.debug(Messages.getString("XmlService.8")); break; default: this.logger.error("XPath matched " + o.getClass().getCanonicalName() + " Node Type:" + node.getType()); this.logger.error(Messages.getString("XmlService.11")); throw new XServicesFault( Messages.getString("XmlService.12")); } } else { this.logger.error("XPath matched " + o.getClass().getCanonicalName()); this.logger.error(Messages.getString("XmlService.11")); throw new XServicesFault(Messages.getString("XmlService.12")); } while (children.hasNext()) { OMNode container = (OMNode) children.next(); match.insertSiblingBefore((OMNode) container.clone(new OMCloneOptions())); } match.detach(); } xmldocument.build(); return xmldocument; } private SimpleNamespaceContext createContext(NamespaceListType nsList) { // Initialize XPath context SimpleNamespaceContext context = new SimpleNamespaceContext(); if(nsList != null) { for (NamespaceType ns : nsList.getNamespaces()) { context.addNamespace(ns.getPrefix(), ns.getUri().toString()); this.logger.debug(Messages.getString("XmlService.0") + ns.getPrefix() + "=\"" + ns.getUri().toString() + "\"'"); } } else { logger.debug("No namespaces defined."); } return context; } private String validateEncoding(String encoding) throws XServicesFault { if(encoding == null || encoding.equals("")) { encoding=Charset.defaultCharset().displayName(); } try { Charset.isSupported(encoding); } catch (IllegalCharsetNameException e) { throw new XServicesFault("Endcoding '"+encoding+"' is not supported by this JRE."); } logger.debug("Setting source xml string encoding to '"+encoding+"'"); return encoding; } }