Package org.exist.http.servlets

Source Code of org.exist.http.servlets.XQueryServlet

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-06 Wolfgang M. Meier
*  wolfgang@exist-db.org
*  http://exist.sourceforge.net
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*  $Id$
*/
package org.exist.http.servlets;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.transform.OutputKeys;

import org.apache.log4j.Logger;
import org.exist.EXistException;
import org.exist.http.Descriptor;
import org.exist.security.AuthenticationException;
import org.exist.security.Permission;
import org.exist.security.PermissionDeniedException;
import org.exist.security.Subject;
import org.exist.security.internal.AccountImpl;
import org.exist.security.internal.web.HttpAccount;
import org.exist.security.xacml.AccessContext;
import org.exist.source.FileSource;
import org.exist.source.Source;
import org.exist.source.SourceFactory;
import org.exist.source.StringSource;
import org.exist.storage.DBBroker;
import org.exist.storage.serializers.Serializer;
import org.exist.util.MimeTable;
import org.exist.util.serializer.SAXSerializer;
import org.exist.util.serializer.SerializerPool;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.CompiledXQuery;
import org.exist.xquery.Constants;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQuery;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.functions.request.RequestModule;
import org.exist.xquery.functions.response.ResponseModule;
import org.exist.xquery.functions.session.SessionModule;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.Item;
import org.exist.debuggee.DebuggeeFactory;
import org.exist.dom.XMLUtil;

/**
* Servlet to generate HTML output from an XQuery file.
*
* The servlet responds to an URL pattern as specified in the
* WEB-INF/web.xml configuration file of the application. It will
* interpret the path with which it is called as leading to a valid
* XQuery file. The XQuery file is loaded, compiled and executed.
* Any output of the script is sent back to the client.
*
* The servlet accepts the following initialization parameters in web.xml:
*
* <table border="0">
*   <tr><td>user</td><td>The user identity with which the script is executed.</td></tr>
*   <tr><td>password</td><td>Password for the user.</td></tr>
*   <tr><td>uri</td><td>A valid XML:DB URI leading to the root collection used to
*   process the request.</td></tr>
*   <tr><td>encoding</td><td>The character encoding used for XQuery files.</td></tr>
*   <tr><td>container-encoding</td><td>The character encoding used by the servlet
*   container.</td></tr>
*   <tr><td>form-encoding</td><td>The character encoding used by parameters posted
*   from HTML for
* ms.</td></tr>
* </table>
*
* User identity and password may also be specified through the HTTP session attributes
* "user" and "password". These attributes will overwrite any other settings.
*
* @author Wolfgang Meier (wolfgang@exist-db.org)
*/
public class XQueryServlet extends AbstractExistHttpServlet {
   
    private static final long serialVersionUID = 5266794852401553015L;

    private static final Logger LOG = Logger.getLogger(XQueryServlet.class);

    // Request attributes
    public static final String ATTR_XQUERY_USER = "xquery.user";
    public static final String ATTR_XQUERY_PASSWORD = "xquery.password";
    public static final String ATTR_XQUERY_SOURCE = "xquery.source";
    public static final String ATTR_XQUERY_URL = "xquery.url";
    public static final String ATTR_XQUERY_REPORT_ERRORS = "xquery.report-errors";
    public static final String ATTR_XQUERY_ATTRIBUTE = "xquery.attribute";
    public static final String ATTR_TIMEOUT = "xquery.timeout";
    public static final String ATTR_MAX_NODES = "xquery.max-nodes";
    public static final String ATTR_MODULE_LOAD_PATH = "xquery.module-load-path";

    public final static XmldbURI DEFAULT_URI = XmldbURI.EMBEDDED_SERVER_URI.append(XmldbURI.ROOT_COLLECTION_URI);
    public final static String DEFAULT_CONTENT_TYPE = "text/html";
   
    public final static String DRIVER = "org.exist.xmldb.DatabaseImpl";
   
    private XmldbURI collectionURI = null;
   
    private String encoding = null;
    private String contentType = null;

    @Override
    public Logger getLog() {
        return LOG;
    }

    /* (non-Javadoc)
     * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        final String confCollectionURI = config.getInitParameter("uri");
        if(confCollectionURI == null) {
            collectionURI = DEFAULT_URI;
           
        } else {
            try {
                collectionURI = XmldbURI.xmldbUriFor(confCollectionURI);
               
            } catch (final URISyntaxException e) {
                throw new ServletException("Invalid XmldbURI for parameter 'uri': "+e.getMessage(),e);
            }
        }
       
        encoding = config.getInitParameter("encoding");
        if(encoding == null) {
            encoding = DEFAULT_ENCODING;
        }
        getLog().info("encoding = " + encoding);

        contentType = config.getInitParameter("content-type");
        if(contentType == null) {
            contentType = DEFAULT_CONTENT_TYPE;
        }
    }
   
    /* (non-Javadoc)
     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        process(request, response);
    }
   
    /* (non-Javadoc)
     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
        HttpServletRequest request = null;
       
        //For POST request, If we are logging the requests we must wrap HttpServletRequest in HttpServletRequestWrapper
        //otherwise we cannot access the POST parameters from the content body of the request!!! - deliriumsky
        final Descriptor descriptor = Descriptor.getDescriptorSingleton();
        if(descriptor != null) {
            if(descriptor.allowRequestLogging()) {
                request = new HttpServletRequestWrapper(req, getFormEncoding());
            } else {
                request = req;
            }
           
        } else {
            request = req;
        }
       
        process(request, response);
    }
   
    //-------------------------------
    // doPut and doDelete added by Andrzej Taramina (andrzej@chaeron.com)
    // Date: Sept/05/2007
    //
    // These methods were added so that you can issue an HTTP PUT or DELETE request and have it serviced by an XQuery.
    // NOTE: The XQuery referenced in the target URL of the request will be executed and the PUT/DELETE request will be passed to it
    //
    //-------------------------------
   
     /* (non-Javadoc)
     * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
        HttpServletRequest request = null;
       
        //For POST request, If we are logging the requests we must wrap HttpServletRequest in HttpServletRequestWrapper
        //otherwise we cannot access the POST parameters from the content body of the request!!! - deliriumsky
        final Descriptor descriptor = Descriptor.getDescriptorSingleton();
        if(descriptor != null) {
            if(descriptor.allowRequestLogging()) {
                request = new HttpServletRequestWrapper(req, getFormEncoding());
            } else {
                request = req;
            }
           
        } else {
            request = req;
        }
       
        process(request, response);
    }
   
   
    /* (non-Javadoc)
     * @see javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        process(request, response);
    }
   
   
    /**
     * Processes incoming HTTP requests for XQuery
     */
    protected void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //first, adjust the path
        String path = request.getPathTranslated();
        if(path == null) {
            path = request.getRequestURI().substring(request.getContextPath().length());
            final int p = path.lastIndexOf(';');
            if(p != Constants.STRING_NOT_FOUND)
                {path = path.substring(0, p);}
            path = getServletContext().getRealPath(path);
        }
       
        //second, perform descriptor actions
        final Descriptor descriptor = Descriptor.getDescriptorSingleton();
        if(descriptor != null && !descriptor.requestsFiltered()) {
            //logs the request if specified in the descriptor
            descriptor.doLogRequestInReplayLog(request);
           
            //map's the path if a mapping is specified in the descriptor
            path = descriptor.mapPath(path);
        }
       
       
//        if (request.getCharacterEncoding() == null)
//            try {
//                request.setCharacterEncoding(formEncoding);
//            } catch (IllegalStateException e) {
//            }
        final ServletOutputStream sout = response.getOutputStream();
        final PrintWriter output = new PrintWriter(new OutputStreamWriter(sout, getFormEncoding()));
//        response.setContentType(contentType + "; charset=" + formEncoding);
        response.addHeader( "pragma", "no-cache" );
        response.addHeader( "Cache-Control", "no-cache" );

        String requestPath = request.getRequestURI();
        final int p = requestPath.lastIndexOf("/");
        if(p != Constants.STRING_NOT_FOUND)
            {requestPath = requestPath.substring(0, p);}
       
        String moduleLoadPath;
        final Object loadPathAttrib = request.getAttribute(ATTR_MODULE_LOAD_PATH);
        if (loadPathAttrib != null)
          {moduleLoadPath = getValue(loadPathAttrib);}
        else
          {moduleLoadPath = getServletContext().getRealPath(requestPath.substring(request.getContextPath().length()));}

        Subject user = getDefaultUser();

        // to determine the user, first check the request attribute "xquery.user", then
        // the current session attribute "user"
        final Object userAttrib = request.getAttribute(ATTR_XQUERY_USER);
        final HttpSession session = request.getSession( false );
        if(userAttrib != null || (session != null && request.isRequestedSessionIdValid())) {
            final Object passwdAttrib = request.getAttribute(ATTR_XQUERY_PASSWORD);
            String username;
            String password;
            if (userAttrib != null) {
                username = getValue(userAttrib);
                password = getValue(passwdAttrib);
            } else {
                username = getSessionAttribute(session, "user");
                password = getSessionAttribute(session, "password");
            }
           
            //TODO authentication should use super.authenticate(...) !!!
      try {
        if( username != null && password != null ) {
          Subject newUser = getPool().getSecurityManager().authenticate(username, password);
              if (newUser != null && newUser.isAuthenticated())
                {user = newUser;}
        }
               
      } catch (final AuthenticationException e) {
        getLog().error("User can not be authenticated ("+username+").");
      }
        }
       
        if (user == getDefaultUser()) {
          Subject requestUser = HttpAccount.getUserFromServletRequest(request);
          if (requestUser != null) {
            user = requestUser;
          } else {
            requestUser = getAuthenticator().authenticate(request, response, false);
            if (requestUser != null)
              {user = requestUser;}
          }
        }
       
        Source source = null;
        final Object sourceAttrib = request.getAttribute(ATTR_XQUERY_SOURCE);
        final Object urlAttrib = request.getAttribute(ATTR_XQUERY_URL);
        if (sourceAttrib != null) {
            String s;
            if (sourceAttrib instanceof Item)
                try {
                    s = ((Item) sourceAttrib).getStringValue();
                } catch (final XPathException e) {
                    throw new ServletException("Failed to read XQuery source string from " +
                        "request attribute '" + ATTR_XQUERY_SOURCE + "': " + e.getMessage(), e);
                }
            else
                {s = sourceAttrib.toString();}
           
            source = new StringSource(s);
           
        } else if (urlAttrib != null) {
            DBBroker broker = null;
            try {
              broker = getPool().get(user);
                source = SourceFactory.getSource(broker, moduleLoadPath, urlAttrib.toString(), true);
            } catch (final Exception e) {
                getLog().error(e.getMessage(), e);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                sendError(output, "Error", e.getMessage());
            } finally {
                getPool().release(broker);
            }
           
        } else {
            final File f = new File(path);
            if(!f.canRead()) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                sendError(output, "Cannot read source file", path);
                return;
            }
            source = new FileSource(f, encoding, true);
        }
       
        if (source == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            sendError(output, "Source not found", path);
        }
       
        boolean reportErrors = false;
        final String errorOpt = (String) request.getAttribute(ATTR_XQUERY_REPORT_ERRORS);
        if (errorOpt != null)
            {reportErrors = errorOpt.equalsIgnoreCase("YES");}
       
        //allow source viewing for GET?
        if("GET".equals(request.getMethod().toUpperCase())) {
            String option;
            boolean allowSource = false;
            if((option = request.getParameter("_source")) != null)
                allowSource = "yes".equals(option);
           
            //Should we display the source of the XQuery or execute it
            if(allowSource && descriptor != null) {
                //show the source
               
                //check are we allowed to show the xquery source - descriptor.xml
//                System.out.println("path="+path);
                if(descriptor.allowSource(path)) {
                 
                  try {
            source.validate(user, Permission.READ);
          } catch (final PermissionDeniedException e) {
            if (getDefaultUser().equals(user)) {
              getAuthenticator().sendChallenge(request, response);
            } else {
              response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission to view XQuery source for: " + path + " denied. (no read access)");
            }
            return;
          }
                   
          //Show the source of the XQuery
                    //writeResourceAs(resource, broker, stylesheet, encoding, "text/plain", outputProperties, response);
                    response.setContentType("text/plain; charset=" + getFormEncoding());
                    output.write(source.getContent());
                    output.flush();
                    return;
                } else {
                  
                   response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission to view XQuery source for: " + path + " denied. Must be explicitly defined in descriptor.xml");
                   return;
                }
            }
        }
       
        //-------------------------------
       
//        URI baseUri;
//        try {
//            baseUri = new URI(request.getScheme(),
//                    null/*user info?*/, request.getLocalName(), request.getLocalPort(),
//                    request.getRequestURI(), null, null);
//        } catch(URISyntaxException e) {
//            baseUri = null;
//        }

        final String requestAttr = (String) request.getAttribute(ATTR_XQUERY_ATTRIBUTE);
        DBBroker broker = null;
        try {
          broker = getPool().get(user);
            final XQuery xquery = broker.getXQueryService();
            CompiledXQuery query = xquery.getXQueryPool().borrowCompiledXQuery(broker, source);

            XQueryContext context;
            if (query==null) {
               context = xquery.newContext(AccessContext.REST);
               context.setModuleLoadPath(moduleLoadPath);
               try {
                 query = xquery.compile(context, source);
                  
               } catch (final XPathException ex) {
                  throw new EXistException("Cannot compile xquery: "+ ex.getMessage(), ex);
                 
               } catch (final IOException ex) {
                  throw new EXistException("I/O exception while compiling xquery: " + ex.getMessage() ,ex);
               }
              
            } else {
               context = query.getContext();
               context.setModuleLoadPath(moduleLoadPath);
            }

            final Properties outputProperties = new Properties();
            outputProperties.put("base-uri", collectionURI.toString());
           
            context.declareVariable(RequestModule.PREFIX + ":request", new HttpRequestWrapper(request, getFormEncoding(), getContainerEncoding()));
            context.declareVariable(ResponseModule.PREFIX + ":response", new HttpResponseWrapper(response));
            context.declareVariable(SessionModule.PREFIX + ":session", ( session != null ? new HttpSessionWrapper( session ) : null ) );

            final String timeoutOpt = (String) request.getAttribute(ATTR_TIMEOUT);
            if (timeoutOpt != null) {
                try {
                    final long timeout = Long.parseLong(timeoutOpt);
                    context.getWatchDog().setTimeout(timeout);
                } catch (final NumberFormatException e) {
                    throw new EXistException("Bad timeout option: " + timeoutOpt);
                }
            }

            final String maxNodesOpt = (String) request.getAttribute(ATTR_MAX_NODES);
            if (maxNodesOpt != null) {
                try{
                    final int maxNodes = Integer.parseInt(maxNodesOpt);
                    context.getWatchDog().setMaxNodes(maxNodes);
                } catch (final NumberFormatException e) {
                    throw new EXistException("Bad max-nodes option: " + maxNodesOpt);
                }
            }

            DebuggeeFactory.checkForDebugRequest(request, context);

            Sequence resultSequence;
            try {
                resultSequence = xquery.execute(query, null, outputProperties);
               
            } finally {
                context.runCleanupTasks();
                xquery.getXQueryPool().returnCompiledXQuery(source, query);
            }

            final String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
            if (mediaType != null) {
                if (!response.isCommitted())
                  {if (MimeTable.getInstance().isTextContent(mediaType)) {
                    response.setContentType(mediaType + "; charset=" + getFormEncoding());
                        response.setCharacterEncoding(getFormEncoding());
                    } else
                    response.setContentType(mediaType);}
               
            } else {
              String contentType = this.contentType;
              try {
                  contentType = getServletContext().getMimeType(path);
                  if (contentType == null)
                      {contentType = this.contentType;}
                   
              } catch (final Throwable e) {
                  contentType = this.contentType;
                   
              } finally {
                  if (MimeTable.getInstance().isTextContent(contentType))
                      {contentType += "; charset=" + getFormEncoding();}
                  response.setContentType(contentType );
              }
            }
           
            if (requestAttr != null && (XmldbURI.API_LOCAL.equals(collectionURI.getApiName())) ) {
                request.setAttribute(requestAttr, resultSequence);
               
            } else {
              final Serializer serializer = broker.getSerializer();
              serializer.reset();
           
              final SerializerPool serializerPool = SerializerPool.getInstance();

              final SAXSerializer sax = (SAXSerializer) serializerPool.borrowObject(SAXSerializer.class);
              try {
                sax.setOutput(output, outputProperties);
                serializer.setProperties(outputProperties);
                serializer.setSAXHandlers(sax, sax);
                serializer.toSAX(resultSequence, 1, resultSequence.getItemCount(), false, false);
                   
              } finally {
                serializerPool.returnObject(sax);
              }
            }
           
    } catch (final PermissionDeniedException e) {
      if (getDefaultUser().equals(user)) {
        getAuthenticator().sendChallenge(request, response);
      } else {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "No permission to execute XQuery for: " + path + " denied.");
      }
      return;
          
        } catch (final XPathException e){
           
            final Logger logger = getLog();           
            if(logger.isDebugEnabled()) {
                logger.debug(e.getMessage(),e);
            }         
           
            if (reportErrors)
              {writeError(output, e);}
            else {
              response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
              sendError(output, "Error", e.getMessage());
            }
           
        } catch (final Throwable e){
            getLog().error(e.getMessage(), e);
            if (reportErrors)
              {writeError(output, e);}
            else {
              response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
              sendError(output, "Error", e.getMessage());
            }
           
        } finally {
            getPool().release(broker);
        }

        output.flush();
        output.close();
    }
   
    private String getSessionAttribute(HttpSession session, String attribute) {
        final Object obj = session.getAttribute(attribute);
        return getValue(obj);
    }

    private String getValue(Object obj) {
        if(obj == null)
            {return null;}
       
        if(obj instanceof Sequence)
            try {
                return ((Sequence)obj).getStringValue();
            } catch (final XPathException e) {
                return null;
            }
        return obj.toString();
    }

    private void writeError(PrintWriter out, Throwable e) {
        out.print("<error>");
//        Throwable t = e.getCause();
//        if (t != null)
//            out.print(XMLUtil.encodeAttrMarkup(t.getMessage()));
//        else
        if (e.getMessage() != null)
          {out.print(XMLUtil.encodeAttrMarkup(e.getMessage()));}
        out.println("</error>");
    }

    private void sendError(PrintWriter out, String message, String description) {
        out.print("<html><head>");
        out.print("<title>XQueryServlet Error</title>");
        out.print("<link rel=\"stylesheet\" type=\"text/css\" href=\"error.css\"></link></head>");
        out.println("<body><h1>Error found</h1>");
        out.print("<div class='message'><b>Message: </b>");
        out.print(message);
        out.print("</div><div class='description'>");
        out.print("<pre>");
        out.print(description);
        out.print("</pre>");
        out.print("</div></body></html>");
        out.flush();
    }
}
TOP

Related Classes of org.exist.http.servlets.XQueryServlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.