You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/08/04 16:18:06 UTC
svn commit: r800814 -
/incubator/pivot/trunk/web/src/org/apache/pivot/web/server/QueryServlet.java
Author: tvolkert
Date: Tue Aug 4 14:18:05 2009
New Revision: 800814
URL: http://svn.apache.org/viewvc?rev=800814&view=rev
Log:
PIVOT-172 :: Change QueryServlet to use ThreadLocal storage where appropriate
Modified:
incubator/pivot/trunk/web/src/org/apache/pivot/web/server/QueryServlet.java
Modified: incubator/pivot/trunk/web/src/org/apache/pivot/web/server/QueryServlet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/web/src/org/apache/pivot/web/server/QueryServlet.java?rev=800814&r1=800813&r2=800814&view=diff
==============================================================================
--- incubator/pivot/trunk/web/src/org/apache/pivot/web/server/QueryServlet.java (original)
+++ incubator/pivot/trunk/web/src/org/apache/pivot/web/server/QueryServlet.java Tue Aug 4 14:18:05 2009
@@ -84,33 +84,102 @@
}
private boolean authenticationRequired = false;
- private Credentials credentials = null;
-
- private String hostname;
- private String contextPath;
- private String queryPath;
- private int port;
- private boolean secure;
- private Method method;
-
private boolean determineContentLength = false;
- private QueryDictionary parameters = new QueryDictionary();
- private QueryDictionary requestHeaders = new QueryDictionary();
- private QueryDictionary responseHeaders = new QueryDictionary();
-
private Serializer<?> serializer = new JSONSerializer();
+ private transient ThreadLocal<Credentials> credentials = new ThreadLocal<Credentials>();
+
+ private transient ThreadLocal<String> hostname = new ThreadLocal<String>();
+ private transient ThreadLocal<String> contextPath = new ThreadLocal<String>();
+ private transient ThreadLocal<String> queryPath = new ThreadLocal<String>();
+ private transient ThreadLocal<Integer> port = new ThreadLocal<Integer>();
+ private transient ThreadLocal<Boolean> secure = new ThreadLocal<Boolean>();
+ private transient ThreadLocal<Method> method = new ThreadLocal<Method>();
+
+ private transient ThreadLocal<QueryDictionary> parameters = new ThreadLocal<QueryDictionary>();
+ private transient ThreadLocal<QueryDictionary> requestHeaders = new ThreadLocal<QueryDictionary>();
+ private transient ThreadLocal<QueryDictionary> responseHeaders = new ThreadLocal<QueryDictionary>();
+
private static final String BASIC_AUTHENTICATION_TAG = "Basic";
private static final String HTTP_PROTOCOL = "http";
private static final String HTTPS_PROTOCOL = "https";
private static final String URL_ENCODING = "UTF-8";
/**
+ * Tells whether this servlet is configured to always determine the content
+ * length of outgoing responses and set the <tt>Content-Length</tt> HTTP
+ * response header accordingly. If this flag is <tt>false</tt>, it is up to
+ * the servlet's discretion as to when to set the <tt>Content-Length</tt>
+ * header (it will do so if it is trivially easy). If this is set to
+ * <tt>true</tt>, it will force the servlet to always set the header, but
+ * doing so will incur a performance penalty, as the servlet will be unable
+ * to stream the response directly to the HTTP output stream as it gets
+ * serialized.
+ */
+ public boolean isDetermineContentLength() {
+ return determineContentLength;
+ }
+
+ /**
+ * Sets the value of the <tt>determineContentLength</tt> flag.
+ *
+ * @see #isDetermineContentLength()
+ */
+ public void setDetermineContentLength(boolean determineContentLength) {
+ this.determineContentLength = determineContentLength;
+ }
+
+ /**
+ * Tells whether or not this servlet will require authentication data. If
+ * set to <tt>true</tt>, and un-authenticated requests are received, the
+ * servlet will automatically respond with a request for authentication.
+ */
+ public boolean isAuthenticationRequired() {
+ return authenticationRequired;
+ }
+
+ /**
+ * Sets whether or not this servlet will require authentication data. If
+ * set to <tt>true</tt>, and un-authenticated requests are received, the
+ * servlet will automatically respond with a request for authentication.
+ */
+ public void setAuthenticationRequired(boolean authenticationRequired) {
+ this.authenticationRequired = authenticationRequired;
+
+ if (!authenticationRequired) {
+ credentials = null;
+ }
+ }
+
+ /**
+ * Returns the serializer used to stream the value passed to or from the
+ * web query. By default, an instance of {@link JSONSerializer} is used.
+ */
+ public Serializer<?> getSerializer() {
+ return serializer;
+ }
+
+ /**
+ * Sets the serializer used to stream the value passed to or from the
+ * web query.
+ *
+ * @param serializer
+ * The serializer (must be non-null).
+ */
+ public void setSerializer(Serializer<?> serializer) {
+ if (serializer == null) {
+ throw new IllegalArgumentException("serializer is null.");
+ }
+
+ this.serializer = serializer;
+ }
+
+ /**
* Gets the host name that was requested.
*/
public String getHostname() {
- return hostname;
+ return hostname.get();
}
/**
@@ -120,7 +189,7 @@
* servlets in the default (root) context, this method returns "".
*/
public String getContextPath() {
- return contextPath;
+ return contextPath.get();
}
/**
@@ -130,7 +199,7 @@
* path.
*/
public String getQueryPath() {
- return queryPath;
+ return queryPath.get();
}
/**
@@ -138,14 +207,14 @@
* the request was received.
*/
public int getPort() {
- return port;
+ return port.get();
}
/**
* Tells whether the request has been ecrypted over HTTPS.
*/
public boolean isSecure() {
- return secure;
+ return secure.get();
}
/**
@@ -159,53 +228,7 @@
* Gets the HTTP method with which the current request was made.
*/
public Method getMethod() {
- return method;
- }
-
- /**
- * Tells whether this servlet is configured to always determine the content
- * length of outgoing responses and set the <tt>Content-Length</tt> HTTP
- * response header accordingly. If this flag is <tt>false</tt>, it is up to
- * the servlet's discretion as to when to set the <tt>Content-Length</tt>
- * header (it will do so if it is trivially easy). If this is set to
- * <tt>true</tt>, it will force the servlet to always set the header, but
- * doing so will incur a performance penalty, as the servlet will be unable
- * to stream the response directly to the HTTP output stream as it gets
- * serialized.
- */
- public boolean isDetermineContentLength() {
- return determineContentLength;
- }
-
- /**
- * Sets the value of the <tt>determineContentLength</tt> flag.
- *
- * @see #isDetermineContentLength()
- */
- public void setDetermineContentLength(boolean determineContentLength) {
- this.determineContentLength = determineContentLength;
- }
-
- /**
- * Tells whether or not this servlet will require authentication data. If
- * set to <tt>true</tt>, and un-authenticated requests are received, the
- * servlet will automatically respond with a request for authentication.
- */
- public boolean isAuthenticationRequired() {
- return authenticationRequired;
- }
-
- /**
- * Sets whether or not this servlet will require authentication data. If
- * set to <tt>true</tt>, and un-authenticated requests are received, the
- * servlet will automatically respond with a request for authentication.
- */
- public void setAuthenticationRequired(boolean authenticationRequired) {
- this.authenticationRequired = authenticationRequired;
-
- if (!authenticationRequired) {
- credentials = null;
- }
+ return method.get();
}
/**
@@ -214,7 +237,7 @@
* flag is set to <tt>true</tt>.
*/
public Credentials getCredentials() {
- return credentials;
+ return credentials.get();
}
/**
@@ -222,7 +245,7 @@
* passed in the HTTP request query string.
*/
public QueryDictionary getParameters() {
- return parameters;
+ return parameters.get();
}
/**
@@ -230,7 +253,7 @@
* request headers.
*/
public QueryDictionary getRequestHeaders() {
- return requestHeaders;
+ return requestHeaders.get();
}
/**
@@ -238,55 +261,89 @@
* response headers that will be sent back to the client.
*/
public QueryDictionary getResponseHeaders() {
- return responseHeaders;
- }
-
- /**
- * Returns the serializer used to stream the value passed to or from the
- * web query. By default, an instance of {@link JSONSerializer} is used.
- */
- public Serializer<?> getSerializer() {
- return serializer;
- }
-
- /**
- * Sets the serializer used to stream the value passed to or from the
- * web query.
- *
- * @param serializer
- * The serializer (must be non-null).
- */
- public void setSerializer(Serializer<?> serializer) {
- if (serializer == null) {
- throw new IllegalArgumentException("serializer is null.");
- }
-
- this.serializer = serializer;
+ return responseHeaders.get();
}
/**
+ * Called when an HTTP GET is received. This base method throws
+ * <tt>UnsupportedOperationException</tt>, which will cause an HTTP 405
+ * (method not allowed) to be sent in the response. Subclasses should
+ * override this method if they wish to support GET requests.
+ * <p>
+ * Request parameters, and request/response headers are available to
+ * subclasses via the corresponding query dictionary.
*
+ * @return
+ * The object that was retrieved via the GET request. This object will be
+ * serialized by this servlet's serializer before being included in the
+ * HTTP response
+ *
+ * @see #getParameters()
+ * @see #getRequestHeaders()
+ * @see #getResponseHeaders()
*/
protected Object doGet() throws ServletException {
throw new UnsupportedOperationException();
}
/**
+ * Called when an HTTP POST is received. This base method throws
+ * <tt>UnsupportedOperationException</tt>, which will cause an HTTP 405
+ * (method not allowed) to be sent in the response. Subclasses should
+ * override this method if they wish to support POST requests.
+ * <p>
+ * Request parameters, and request/response headers are available to
+ * subclasses via the corresponding query dictionary.
*
+ * @param value
+ * The object that is being posted by the client. This object will have
+ * been de-serialized from within the request by this servlet's serializer
+ *
+ * @return
+ * The URL identifying the location of the object that was posted. The
+ * semantics of this URL are up to the subclass to define
+ *
+ * @see #getParameters()
+ * @see #getRequestHeaders()
+ * @see #getResponseHeaders()
*/
protected URL doPost(Object value) throws ServletException {
throw new UnsupportedOperationException();
}
/**
+ * Called when an HTTP PUT is received. This base method throws
+ * <tt>UnsupportedOperationException</tt>, which will cause an HTTP 405
+ * (method not allowed) to be sent in the response. Subclasses should
+ * override this method if they wish to support PUT requests.
+ * <p>
+ * Request parameters, and request/response headers are available to
+ * subclasses via the corresponding query dictionary.
*
+ * @param value
+ * The object that is being updated by the client. This object will have
+ * been de-serialized from within the request by this servlet's serializer
+ *
+ * @see #getParameters()
+ * @see #getRequestHeaders()
+ * @see #getResponseHeaders()
*/
protected void doPut(Object value) throws ServletException {
throw new UnsupportedOperationException();
}
/**
+ * Called when an HTTP DELETE is received. This base method throws
+ * <tt>UnsupportedOperationException</tt>, which will cause an HTTP 405
+ * (method not allowed) to be sent in the response. Subclasses should
+ * override this method if they wish to support DELETE requests.
+ * <p>
+ * Request parameters, and request/response headers are available to
+ * subclasses via the corresponding query dictionary.
*
+ * @see #getParameters()
+ * @see #getRequestHeaders()
+ * @see #getResponseHeaders()
*/
protected void doDelete() throws ServletException {
throw new UnsupportedOperationException();
@@ -299,7 +356,7 @@
* wishing to authorize the authenticated user credentials may override
* this method to perform that authorization. On the other hand, the
* <tt>authorize</tt> method of <tt>QueryServlet</tt> does nothing, so
- * subclasses that wish to authenticate the request but not authorization
+ * subclasses that wish to authenticate the request but not authorize
* it may simply not override this method.
* <p>
* This method is guaranteed to be called after the arguments and request
@@ -316,92 +373,104 @@
@SuppressWarnings("unchecked")
protected void service(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
- boolean proceed = true;
-
- if (authenticationRequired) {
- String authorization = request.getHeader("Authorization");
+ try {
+ parameters.set(new QueryDictionary());
+ requestHeaders.set(new QueryDictionary());
+ responseHeaders.set(new QueryDictionary());
- if (authorization == null) {
- proceed = false;
- doUnauthorized(request, response);
- } else {
- String encodedCredentials = authorization.substring
- (BASIC_AUTHENTICATION_TAG.length() + 1);
- String decodedCredentials = new String(Base64.decode(encodedCredentials));
- String[] credentialsPair = decodedCredentials.split(":");
+ boolean proceed = true;
- String username = credentialsPair.length > 0 ? credentialsPair[0] : "";
- String password = credentialsPair.length > 1 ? credentialsPair[1] : "";
+ if (authenticationRequired) {
+ String authorization = request.getHeader("Authorization");
- if (credentials == null) {
- credentials = new Credentials(username, password);
+ if (authorization == null) {
+ proceed = false;
+ doUnauthorized(request, response);
} else {
- credentials.username = username;
- credentials.password = password;
- }
- }
- }
+ String encodedCredentials = authorization.substring
+ (BASIC_AUTHENTICATION_TAG.length() + 1);
+ String decodedCredentials = new String(Base64.decode(encodedCredentials));
+ String[] credentialsPair = decodedCredentials.split(":");
- if (proceed) {
- // Extract our location context
- try {
- URL url = new URL(request.getRequestURL().toString());
-
- hostname = url.getHost();
- contextPath = request.getContextPath();
- queryPath = request.getRequestURI();
- port = request.getLocalPort();
- secure = url.getProtocol().equalsIgnoreCase(HTTPS_PROTOCOL);
- method = Method.valueOf(request.getMethod().toUpperCase());
+ String username = credentialsPair.length > 0 ? credentialsPair[0] : "";
+ String password = credentialsPair.length > 1 ? credentialsPair[1] : "";
- if (queryPath.startsWith(contextPath)) {
- queryPath = queryPath.substring(contextPath.length());
+ credentials.set(new Credentials(username, password));
}
- } catch (MalformedURLException ex) {
- throw new ServletException(ex);
}
- // Clear out any remnants of the previous service
- parameters.clear();
- requestHeaders.clear();
- responseHeaders.clear();
-
- // Copy the query string into our arguments dictionary
- String queryString = request.getQueryString();
- if (queryString != null) {
- String[] pairs = queryString.split("&");
+ if (proceed) {
+ // Extract our location context
+ try {
+ URL url = new URL(request.getRequestURL().toString());
+ String requestURI = request.getRequestURI();
+ String requestContext = request.getContextPath();
+
+ hostname.set(url.getHost());
+ contextPath.set(requestContext);
+ queryPath.set(requestURI);
+ port.set(request.getLocalPort());
+ secure.set(url.getProtocol().equalsIgnoreCase(HTTPS_PROTOCOL));
+ method.set(Method.valueOf(request.getMethod().toUpperCase()));
+
+ if (requestURI.startsWith(requestContext)) {
+ queryPath.set(requestURI.substring(requestContext.length()));
+ }
+ } catch (MalformedURLException exception) {
+ throw new ServletException(exception);
+ }
+
+ // Copy the query string into our arguments dictionary
+ String queryString = request.getQueryString();
+ if (queryString != null) {
+ QueryDictionary parametersDictionary = parameters.get();
+ String[] pairs = queryString.split("&");
- for (int i = 0, n = pairs.length; i < n; i++) {
- String[] pair = pairs[i].split("=");
+ for (int i = 0, n = pairs.length; i < n; i++) {
+ String[] pair = pairs[i].split("=");
- String key = URLDecoder.decode(pair[0], URL_ENCODING);
- String value = URLDecoder.decode((pair.length > 1) ? pair[1] : "", URL_ENCODING);
+ String key = URLDecoder.decode(pair[0], URL_ENCODING);
+ String value = URLDecoder.decode((pair.length > 1) ? pair[1] : "", URL_ENCODING);
- parameters.add(key, value);
+ parametersDictionary.add(key, value);
+ }
}
- }
- // Copy the request headers into our request properties dictionary
- Enumeration<String> headerNames = request.getHeaderNames();
- while (headerNames.hasMoreElements()) {
- String headerName = headerNames.nextElement();
- String headerValue = request.getHeader(headerName);
+ // Copy the request headers into our request properties dictionary
+ QueryDictionary requestHeaderDictionary = requestHeaders.get();
+ Enumeration<String> headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ String headerValue = request.getHeader(headerName);
- requestHeaders.add(headerName, headerValue);
- }
+ requestHeaderDictionary.add(headerName, headerValue);
+ }
- if (authenticationRequired) {
- try {
- authorize();
- } catch (LoginException ex) {
- proceed = false;
- doForbidden(request, response);
+ if (authenticationRequired) {
+ try {
+ authorize();
+ } catch (LoginException exception) {
+ proceed = false;
+ doForbidden(request, response);
+ }
}
}
- }
- if (proceed) {
- super.service(request, response);
+ if (proceed) {
+ super.service(request, response);
+ }
+ } finally {
+ // Clean up our thread local variables
+ credentials.remove();
+ hostname.remove();
+ contextPath.remove();
+ queryPath.remove();
+ port.remove();
+ secure.remove();
+ method.remove();
+ parameters.remove();
+ requestHeaders.remove();
+ responseHeaders.remove();
}
}
@@ -451,10 +520,10 @@
} else {
serializer.writeObject(result, responseOutputStream);
}
- } catch (UnsupportedOperationException ex) {
+ } catch (UnsupportedOperationException exception) {
doMethodNotAllowed(response);
- } catch (SerializationException ex) {
- throw new ServletException(ex);
+ } catch (SerializationException exception) {
+ throw new ServletException(exception);
}
}
@@ -471,10 +540,10 @@
response.setHeader("Location", url.toString());
response.setContentLength(0);
response.flushBuffer();
- } catch (UnsupportedOperationException ex) {
+ } catch (UnsupportedOperationException exception) {
doMethodNotAllowed(response);
- } catch (SerializationException ex) {
- throw new ServletException(ex);
+ } catch (SerializationException exception) {
+ throw new ServletException(exception);
}
}
@@ -490,10 +559,10 @@
setResponseHeaders(response);
response.setContentLength(0);
response.flushBuffer();
- } catch (UnsupportedOperationException ex) {
+ } catch (UnsupportedOperationException exception) {
doMethodNotAllowed(response);
- } catch (SerializationException ex) {
- throw new ServletException(ex);
+ } catch (SerializationException exception) {
+ throw new ServletException(exception);
}
}
@@ -507,7 +576,7 @@
setResponseHeaders(response);
response.setContentLength(0);
response.flushBuffer();
- } catch (UnsupportedOperationException ex) {
+ } catch (UnsupportedOperationException exception) {
doMethodNotAllowed(response);
}
}
@@ -565,9 +634,11 @@
*
*/
private void setResponseHeaders(HttpServletResponse response) {
- for (String key : responseHeaders) {
- for (int i = 0, n = responseHeaders.getLength(key); i < n; i++) {
- response.addHeader(key, responseHeaders.get(key, i));
+ QueryDictionary responseHeaderDictionary = responseHeaders.get();
+
+ for (String key : responseHeaderDictionary) {
+ for (int i = 0, n = responseHeaderDictionary.getLength(key); i < n; i++) {
+ response.addHeader(key, responseHeaderDictionary.get(key, i));
}
}
}