Subversion Repositories XServices

Rev

Rev 158 | Rev 185 | Go to most recent revision | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *   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("<XS>" + xmlFragment + "</XS>"))
                                                .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("<XS>" + xmlFragment + "</XS>"))
                                                        .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("<XS>" + xmlFragment + "</XS>"))
                                                .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("<XS>" + xmlFragment + "</XS>"))
                                                .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<tokens.length; i++) {
                        result +=  tokens[i];
                        if (i+1 < tokens.length ) result += "]]]]><![CDATA[>";
                }
                
                result = "<![CDATA[" + result + "]]>";
                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;
        }
        

}