You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2007/10/19 15:13:26 UTC
svn commit: r586423 - in
/incubator/sling/whiteboard/microsling/src/main/java/org/apache/sling/microsling/helpers/servlets:
AbstractSlingServlet.java SlingAllMethodsServlet.java
SlingSafeMethodsServlet.java
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();
+ }
+ }
+ }
+
+}