incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r586423 - in /incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets: AbstractSlingServlet.java SlingAllMethodsServlet.java SlingSafeMethodsServlet.java
Date Fri, 19 Oct 2007 13:13:26 GMT
Author: fmeschbe
Date: Fri Oct 19 06:13:26 2007
New Revision: 586423

URL: http://svn.apache.org/viewvc?rev=586423&view=rev
Log:
SLING-67 Provide extensible default Servlets

Added:
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingAllMethodsServlet.java
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java
Removed:
    incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/AbstractSlingServlet.java

Added: incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingAllMethodsServlet.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingAllMethodsServlet.java?rev=586423&view=auto
==============================================================================
--- incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingAllMethodsServlet.java
(added)
+++ incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingAllMethodsServlet.java
Fri Oct 19 06:13:26 2007
@@ -0,0 +1,205 @@
+/*
+ * 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.sling.microsling.helpers.servlets;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.microsling.helpers.constants.HttpConstants;
+
+
+/**
+ * Helper base class for data modifying Servlets used in Sling. This class
+ * extends the {@link SlingSafeMethodsServlet} by support for the <em>POST</em>,
+ * <em>PUT</em> and <em>DELETE</em> methods.
+ *
+ * @see SlingSafeMethodsServlet for more information on supporting more HTTP methods
+ */
+public class SlingAllMethodsServlet extends SlingSafeMethodsServlet {
+
+    /**
+     * Called by the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+     * handle an HTTP <em>POST</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>POST</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doPost(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+     * handle an HTTP <em>PUT</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>PUT</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doPut(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+     * handle an HTTP <em>DELETE</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>DELETE</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doDelete(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Tries to handle the request by calling a Java method implemented for the
+     * respective HTTP request method.
+     * <p>
+     * This implementation first calls the base class implementation and only if
+     * the base class cannot dispatch will try to dispatch the supported methods
+     * <em>POST</em>, <em>PUT</em> and <em>DELETE</em>
and returns
+     * <code>true</code> if any of these methods is requested. Otherwise
+     * <code>false</code> is just returned.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @return <code>true</code> if the requested method (<code>request.getMethod()</code>)
+     *         is known. Otherwise <code>false</code> is returned.
+     * @throws ServletException Forwarded from any of the dispatched methods
+     * @throws IOException Forwarded from any of the dispatched methods
+     */
+    protected boolean mayService(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // assume the method is known for now
+        if (super.mayService(request, response)) {
+            return true;
+        }
+
+        // assume the method is known for now
+        boolean methodKnown = true;
+
+        String method = request.getMethod().toUpperCase();
+        if (HttpConstants.METHOD_POST.equals(method)) {
+            doPost(request, response);
+        } else if (HttpConstants.METHOD_PUT.equals(method)) {
+            doPut(request, response);
+        } else if (HttpConstants.METHOD_DELETE.equals(method)) {
+            doDelete(request, response);
+        } else {
+            // actually we do not know the method
+            methodKnown = false;
+        }
+
+        // return whether we actually knew the request method or not
+        return methodKnown;
+    }
+
+    /**
+     * Helper method called by
+     * {@link #doOptions(HttpServletRequest, HttpServletResponse)} to calculate
+     * the value of the <em>Allow</em> header sent as the response to the HTTP
+     * <em>OPTIONS</em> request.
+     * <p>
+     * This implementation overwrites the base class implementation adding
+     * support for the <em>POST</em>, <em>PUT</em> and <em>DELETE</em>
+     * methods in addition to the methods returned by the base class
+     * implementation.
+     *
+     * @param declaredMethods The public and protected methods declared in the
+     *            extension of this class.
+     * @return A <code>StringBuffer</code> containing the list of HTTP methods
+     *         supported.
+     */
+    protected StringBuffer getAllowedRequestMethods(
+            Map<String, Method> declaredMethods) {
+        StringBuffer allowBuf = super.getAllowedRequestMethods(declaredMethods);
+
+        // add more method names depending on the methods found
+        String className = SlingAllMethodsServlet.class.getName();
+        if (isMethodValid(declaredMethods.get("doPost"), className)) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_POST);
+
+        } else if (isMethodValid(declaredMethods.get("doPut"), className)) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_PUT);
+
+        } else if (isMethodValid(declaredMethods.get("doDelete"), className)) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_DELETE);
+        }
+
+        return allowBuf;
+    }
+
+    /**
+     * Returns <code>true</code> if <code>method</code> is not
+     * <code>null</code> and the method is not defined in the class named by
+     * <code>className</code>.
+     * <p>
+     * This method may be used to make sure a method is actually overwritten and
+     * not just the default implementation.
+     *
+     * @param method The Method to check
+     * @param className The name of class assumed to contained the initial
+     *            declaration of the method.
+     * @return <code>true</code> if <code>method</code> is not
+     *         <code>null</code> and the methods declaring class is not the
+     *         given class.
+     */
+    protected boolean isMethodValid(Method method, String className) {
+        return method != null && !method.getClass().getName().equals(className);
+    }
+}

Added: incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java
URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java?rev=586423&view=auto
==============================================================================
--- incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java
(added)
+++ incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets/SlingSafeMethodsServlet.java
Fri Oct 19 06:13:26 2007
@@ -0,0 +1,538 @@
+/*
+ * 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.sling.microsling.helpers.servlets;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.sling.microsling.api.exceptions.HttpStatusCodeException;
+import org.apache.sling.microsling.helpers.constants.HttpConstants;
+
+/**
+ * Helper base class for read-only Servlets used in Sling. This base class is
+ * actually just a better implementation of the Servlet API <em>HttpServlet</em>
+ * class which accounts for extensibility. So extensions of this class have
+ * great control over what methods to overwrite.
+ * <p>
+ * If any of the default HTTP methods is to be implemented just overwrite the
+ * respective doXXX method. If additional methods should be supported implement
+ * appropriate doXXX methods and overwrite the
+ * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+ * dispatch to the doXXX methods as appropriate and overwrite the
+ * {@link #getAllowedRequestMethods(Set)} to add the new method names.
+ * <p>
+ * Please note, that this base class is intended for applications where data is
+ * only read. As such, this servlet by itself does not support the <em>POST</em>,
+ * <em>PUT</em> and <em>DELETE</em> methods. Extensions of this class
should
+ * either overwrite any of the doXXX methods of this class or add support for
+ * other read-only methods only. Applications wishing to support data
+ * modification should rather use or extend the {@link SlingAllMethodsServlet}
+ * which also contains support for the <em>POST</em>, <em>PUT</em>
and
+ * <em>DELETE</em> methods. This latter class should also be overwritten to
+ * add support for HTTP methods modifying data.
+ *
+ * @see SlingAllMethodsServlet
+ */
+public class SlingSafeMethodsServlet extends GenericServlet {
+
+    /**
+     * Handles the <em>HEAD</em> method.
+     * <p>
+     * This base implementation just calls the
+     * {@link #doGet(HttpServletRequest, HttpServletResponse)} method dropping
+     * the output. Implementations of this class may overwrite this method if
+     * they have a more performing implementation. Otherwise, they may just keep
+     * this base implementation.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response which only gets the headers set
+     * @throws ServletException Forwarded from the
+     *             {@link #doGet(HttpServletRequest, HttpServletResponse)}
+     *             method called by this implementation.
+     * @throws IOException Forwarded from the
+     *             {@link #doGet(HttpServletRequest, HttpServletResponse)}
+     *             method called by this implementation.
+     */
+    protected void doHead(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // the null-output wrapper
+        NoBodyResponse wrappedResponse = new NoBodyResponse(response);
+
+        // do a normal get request, dropping the output
+        doGet(request, wrappedResponse);
+
+        // ensure the content length is set as gathered by the null-output
+        wrappedResponse.setContentLength();
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method to
+     * handle an HTTP <em>GET</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>GET</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doGet(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Handles the <em>OPTIONS</em> method by setting the HTTP
+     * <code>Allow</code> header on the response depending on the methods
+     * declared in this class.
+     * <p>
+     * Extensions of this class should generally not overwrite this method but
+     * rather the {@link #getAllowedRequestMethods(Map)} method. This method
+     * gathers all declared public and protected methods for the concrete class
+     * (upto but not including this class) and calls the
+     * {@link #getAllowedRequestMethods(Map)} method with the methods gathered.
+     * The returned value is then used as the value of the <code>Allow</code>
+     * header set.
+     *
+     * @param request The HTTP request object. Not used.
+     * @param response The HTTP response object on which the header is set.
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException Not thrown by this implementation.
+     */
+    protected void doOptions(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        Map<String, Method> methods = getAllDeclaredMethods(this.getClass());
+        StringBuffer allowBuf = getAllowedRequestMethods(methods);
+        response.setHeader("Allow", allowBuf.toString());
+    }
+
+    /**
+     * Handles the <em>TRACE</em> method by just returning the list of all
+     * header values in the response body.
+     * <p>
+     * Extensions of this class do not generally need to overwrite this method
+     * as it contains all there is to be done to the <em>TRACE</em> method.
+     *
+     * @param request The HTTP request whose headers are returned.
+     * @param response The HTTP response into which the request headers are
+     *            written.
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException May be thrown if there is an problem sending back the
+     *             request headers in the response stream.
+     */
+    protected void doTrace(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        String CRLF = "\r\n";
+
+        StringBuffer responseString = new StringBuffer();
+        responseString.append("TRACE ").append(request.getRequestURI());
+        responseString.append(' ').append(request.getProtocol());
+
+        Enumeration<?> reqHeaderEnum = request.getHeaderNames();
+        while (reqHeaderEnum.hasMoreElements()) {
+            String headerName = (String) reqHeaderEnum.nextElement();
+
+            Enumeration<?> reqHeaderValEnum = request.getHeaders(headerName);
+            while (reqHeaderValEnum.hasMoreElements()) {
+                responseString.append(CRLF);
+                responseString.append(headerName).append(": ");
+                responseString.append(reqHeaderValEnum.nextElement());
+            }
+        }
+
+        responseString.append(CRLF);
+
+        String charset = "UTF-8";
+        byte[] rawResponse = responseString.toString().getBytes(charset);
+        int responseLength = rawResponse.length;
+
+        response.setContentType("message/http");
+        response.setCharacterEncoding(charset);
+        response.setContentLength(responseLength);
+
+        ServletOutputStream out = response.getOutputStream();
+        out.write(rawResponse);
+    }
+
+    /**
+     * Called by the {@link #service(HttpServletRequest, HttpServletResponse)}
+     * method to handle a request for an HTTP method, which is not known and
+     * handled by this class or its extension.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * This method should be overwritten with great care. It is better to
+     * overwrite the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method and
+     * add support for any extension HTTP methods through an additional doXXX
+     * method.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException This implementation throws a
+     *             {@link HttpStatusCodeException} exception with the
+     *             appropriate status code and message.
+     */
+    protected void doGeneric(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+        handleMethodNotImplemented(request);
+    }
+
+    /**
+     * Tries to handle the request by calling a Java method implemented for the
+     * respective HTTP request method.
+     * <p>
+     * This base class implentation dispatches the <em>HEAD</em>,
+     * <em>GET</em>, <em>OPTIONS</em> and <em>TRACE</em>
to the
+     * respective <em>doXXX</em> methods and returns <code>true</code>
if
+     * any of these methods is requested. Otherwise <code>false</code> is just
+     * returned.
+     * <p>
+     * Implementations of this class may overwrite this method but should first
+     * call this base implementation and in case <code>false</code> is
+     * returned add handling for any other method and of course return whether
+     * the requested method was known or not.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @return <code>true</code> if the requested method (<code>request.getMethod()</code>)
+     *         is known. Otherwise <code>false</code> is returned.
+     * @throws ServletException Forwarded from any of the dispatched methods
+     * @throws IOException Forwarded from any of the dispatched methods
+     */
+    protected boolean mayService(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // assume the method is known for now
+        boolean methodKnown = true;
+
+        String method = request.getMethod().toUpperCase();
+        if (HttpConstants.METHOD_HEAD.equals(method)) {
+            doHead(request, response);
+        } else if (HttpConstants.METHOD_GET.equals(method)) {
+            doGet(request, response);
+        } else if (HttpConstants.METHOD_OPTIONS.equals(method)) {
+            doOptions(request, response);
+        } else if (HttpConstants.METHOD_TRACE.equals(method)) {
+            doTrace(request, response);
+        } else {
+            // actually we do not know the method
+            methodKnown = false;
+        }
+
+        // return whether we actually knew the request method or not
+        return methodKnown;
+    }
+
+    /**
+     * Helper method which causes an appropriate HTTP response to be sent for an
+     * unhandled HTTP request method. In case of HTTP/1.1 a 405 status code
+     * (Method Not Allowed) is returned, otherwise a 400 status (Bad Request) is
+     * returned.
+     *
+     * @param request The HTTP request from which the method and protocol values
+     *            are extracted to build the appropriate message.
+     * @throws HttpStatusCodeException Always thrown by this method containing
+     *             the appropriate status code and message.
+     */
+    protected void handleMethodNotImplemented(HttpServletRequest request)
+            throws HttpStatusCodeException {
+        String protocol = request.getProtocol();
+        String msg = "Method " + request.getMethod() + " not supported";
+
+        // for HTTP/1.1 use 405 Method Not Allowed
+        if (protocol.endsWith("1.1")) {
+            throw new HttpStatusCodeException(
+                HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+        }
+
+        // otherwise use 400 Bad Request
+        throw new HttpStatusCodeException(HttpServletResponse.SC_BAD_REQUEST,
+            msg);
+    }
+
+    /**
+     * Called by the {@link #service(ServletRequest, ServletResponse)} method to
+     * handle the HTTP request. This implementation calls the
+     * {@link #mayService(HttpServletRequest, HttpServletResponse)} method and
+     * depedending on its return value call the
+     * {@link #doGeneric(HttpServletRequest, HttpServletResponse)} method. If
+     * the {@link #mayService(HttpServletRequest, HttpServletResponse)} method
+     * can handle the request, the
+     * {@link #doGeneric(HttpServletRequest, HttpServletResponse)} method is not
+     * called otherwise it is called.
+     * <p>
+     * Implementations of this class should not generally overwrite this method.
+     * Rather the {@link #mayService(HttpServletRequest, HttpServletResponse)}
+     * method should be overwritten to add support for more HTTP methods.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Forwarded from the
+     *             {@link #mayService(HttpServletRequest, HttpServletResponse)}
+     *             or
+     *             {@link #doGeneric(HttpServletRequest, HttpServletResponse)}
+     *             methods.
+     * @throws IOException Forwarded from the
+     *             {@link #mayService(HttpServletRequest, HttpServletResponse)}
+     *             or
+     *             {@link #doGeneric(HttpServletRequest, HttpServletResponse)}
+     *             methods.
+     */
+    protected void service(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException, IOException {
+
+        // first try to handle the request by the known methods
+        boolean methodKnown = mayService(request, response);
+
+        // otherwise try to handle it through generic means
+        if (!methodKnown) {
+            doGeneric(request, response);
+        }
+    }
+
+    /**
+     * Forwards the request to the
+     * {@link #service(HttpServletRequest, HttpServletResponse)} method if the
+     * request is a HTTP request.
+     * <p>
+     * Implementations of this class will not generally overwrite this method.
+     *
+     * @param req The Servlet request
+     * @param res The Servlet response
+     * @throws ServletException If the request is not a HTTP request or
+     *             forwarded from the
+     *             {@link #service(HttpServletRequest, HttpServletResponse)}
+     *             called.
+     * @throws IOException Forwarded from the
+     *             {@link #service(HttpServletRequest, HttpServletResponse)}
+     *             called.
+     */
+    public void service(ServletRequest req, ServletResponse res)
+            throws ServletException, IOException {
+
+        try {
+            HttpServletRequest request = (HttpServletRequest) req;
+            HttpServletResponse response = (HttpServletResponse) res;
+
+            service(request, response);
+
+        } catch (ClassCastException cce) {
+            throw new ServletException("Not a HTTP request/response");
+        }
+
+    }
+
+    /**
+     * Helper method called by
+     * {@link #doOptions(HttpServletRequest, HttpServletResponse)} to calculate
+     * the value of the <em>Allow</em> header sent as the response to the HTTP
+     * <em>OPTIONS</em> request.
+     * <p>
+     * This base class implementation checks whether any doXXX methods exist for
+     * <em>GET</em> and <em>HEAD</em> and returns the list of methods
+     * supported found. The list returned always includes the HTTP
+     * <em>OPTIONS</em> and <em>TRACE</em> methods.
+     * <p>
+     * Implementations of this class may overwrite this method check for more
+     * methods supported by the extension (generally the same list as used in
+     * the {@link #mayService(HttpServletRequest, HttpServletResponse)} method).
+     * This base class implementation should always be called to make sure the
+     * default HTTP methods are included in the list.
+     *
+     * @param declaredMethods The public and protected methods declared in the
+     *            extension of this class.
+     * @return A <code>StringBuffer</code> containing the list of HTTP methods
+     *         supported.
+     */
+    protected StringBuffer getAllowedRequestMethods(
+            Map<String, Method> declaredMethods) {
+        StringBuffer allowBuf = new StringBuffer();
+
+        // OPTIONS and TRACE are always supported by this servlet
+        allowBuf.append(HttpConstants.METHOD_OPTIONS);
+        allowBuf.append(", ").append(HttpConstants.METHOD_TRACE);
+
+        // add more method names depending on the methods found
+        if (declaredMethods.containsKey("doHead")
+            && !declaredMethods.containsKey("doGet")) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_HEAD);
+
+        } else if (declaredMethods.containsKey("doGet")) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_GET);
+            allowBuf.append(", ").append(HttpConstants.METHOD_HEAD);
+
+        }
+
+        return allowBuf;
+    }
+
+    /**
+     * Returns a map of methods declared by the class indexed by method name.
+     * This method is called by the
+     * {@link #doOptions(HttpServletRequest, HttpServletResponse)} method to
+     * find the methods to be checked by the
+     * {@link #getAllowedRequestMethods(Map)} method. Note, that only extension
+     * classes of this class are considered to be sure to not account for the
+     * default implementations of the doXXX methods in this class.
+     *
+     * @param c The <code>Class</code> to get the declared methods from
+     * @return The Map of methods considered for support checking.
+     */
+    private Map<String, Method> getAllDeclaredMethods(Class<?> c) {
+        // stop (and do not include) the AbstractSlingServletClass
+        if (c == null || c.getName().equals(SlingSafeMethodsServlet.class.getName())) {
+            return new HashMap<String, Method>();
+        }
+
+        // get the declared methods from the base class
+        Map<String, Method> methodSet = getAllDeclaredMethods(c.getSuperclass());
+
+        // add declared methods of c (maybe overwrite base class methods)
+        Method[] declaredMethods = c.getDeclaredMethods();
+        for (Method method : declaredMethods) {
+            // only consider public and protected methods
+            if (Modifier.isProtected(method.getModifiers())
+                || Modifier.isPublic(method.getModifiers())) {
+                methodSet.put(method.getName(), method);
+            }
+        }
+
+        return methodSet;
+    }
+
+    /**
+     * A response that includes no body, for use in (dumb) "HEAD" support. This
+     * just swallows that body, counting the bytes in order to set the content
+     * length appropriately.
+     */
+    private class NoBodyResponse extends HttpServletResponseWrapper {
+
+        /** The byte sink and counter */
+        private NoBodyOutputStream noBody;
+
+        /** Optional writer around the byte sink */
+        private PrintWriter writer;
+
+        /** Whether the request processor set the content length itself or not. */
+        private boolean didSetContentLength;
+
+        NoBodyResponse(HttpServletResponse wrappedResponse) {
+            super(wrappedResponse);
+            noBody = new NoBodyOutputStream();
+        }
+
+        /**
+         * Called at the end of request processing to ensure the content length
+         * is set. If the processor already set the length, this method does not
+         * do anything. Otherwise the number of bytes written through the
+         * null-output is set on the response.
+         */
+        void setContentLength() {
+            if (!didSetContentLength) {
+                setContentLength(noBody.getContentLength());
+            }
+        }
+
+        /**
+         * Overwrite this to prevent setting the content length at the end of
+         * the request through {@link #setContentLength()}
+         */
+        public void setContentLength(int len) {
+            super.setContentLength(len);
+            didSetContentLength = true;
+        }
+
+        /**
+         * Just return the null output stream and don't check whether a writer
+         * has already been acquired.
+         */
+        public ServletOutputStream getOutputStream() {
+            return noBody;
+        }
+
+        /**
+         * Just return the writer to the null output stream and don't check
+         * whether an output stram has already been acquired.
+         */
+        public PrintWriter getWriter() throws UnsupportedEncodingException {
+            if (writer == null) {
+                OutputStreamWriter w;
+
+                w = new OutputStreamWriter(noBody, getCharacterEncoding());
+                writer = new PrintWriter(w);
+            }
+            return writer;
+        }
+    }
+
+    /**
+     * Simple ServletOutputStream which just does not write but counts the bytes
+     * written through it. This class is used by the NoBodyResponse.
+     */
+    private class NoBodyOutputStream extends ServletOutputStream {
+
+        private int contentLength = 0;
+
+        /**
+         * @return the number of bytes "written" through this stream
+         */
+        int getContentLength() {
+            return contentLength;
+        }
+
+        public void write(int b) {
+            contentLength++;
+        }
+
+        public void write(byte buf[], int offset, int len) {
+            if (len >= 0) {
+                contentLength += len;
+            } else {
+                throw new IndexOutOfBoundsException();
+            }
+        }
+    }
+
+}



Mime
View raw message