All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xlightweb.HttpRequestHandler Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright (c) xlightweb.org, 2008 - 2010. All rights reserved.
 *
 *  This library 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.1 of the License, or (at your option) any later version.
 *
 *  This library 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 library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
 * The latest copy of this software may be found on http://www.xlightweb.org/
 */
package org.xlightweb;

import java.io.IOException;

import java.lang.reflect.Method;
import java.net.ConnectException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xsocket.Execution;
import org.xsocket.connection.ConnectionUtils;


 

/**
 * Provides an abstract class to be subclassed to implement a 
 * {@link IHttpRequestHandler}. A subclass of HttpRequestHandlerAdapter 
 * must override at least one method, usually one of these:
 *
 * 
    *
  • doGet, if the implementation supports HTTP GET requests *
  • doPost, for HTTP POST requests *
  • doPut, for HTTP PUT requests *
  • doDelete, for HTTP DELETE requests *
* * If the method for a dedicated HTTP method is not sub classes, * a 405 error will be returned.

* * Example: *
 * class MyRequestHandler extends HttpRequestHandler {
 * 
 *    @Override
 *    protected void doGet(IHttpExchange exchange) throws IOException, BadMessageException {
 *       exchange.send(new HttpResponse(200, "text/plain", "GET called"));
 *    }
 *    
 *    @Override
 *    protected void doPost(IHttpExchange exchange) throws IOException, BadMessageException {
 *       exchange.send(new HttpResponse(200, "text/plain", "POST called"));
 *    }
 * }
 * 
* * For more examples see {@link IHttpRequestHandler} * * * @author [email protected] */ public class HttpRequestHandler implements IHttpRequestHandler { private static final Logger LOG = Logger.getLogger(HttpRequestHandler.class.getName()); @SuppressWarnings("unchecked") private static final Map handlerInfoCache = ConnectionUtils.newMapCache(25); private final HandlerInfo handlerInfo; public HttpRequestHandler() { handlerInfo = getHandlerInfo(getClass()); } @SuppressWarnings("unchecked") private static HandlerInfo getHandlerInfo(Class clazz) { HandlerInfo handlerInfo = handlerInfoCache.get(clazz); if (handlerInfo == null) { handlerInfo = new HandlerInfo(clazz); handlerInfoCache.put(clazz, handlerInfo); } return handlerInfo; } /** * {@inheritDoc} */ @Execution(Execution.NONTHREADED) @InvokeOn(InvokeOn.HEADER_RECEIVED) public final void onRequest(final IHttpExchange exchange) throws IOException { String method = exchange.getRequest().getMethod(); if (method.equals(IHttpRequest.GET_METHOD)) { if (handlerInfo.isDoGetInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoGet(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoGet(exchange); } } else if (method.equals(IHttpRequest.POST_METHOD)) { if (handlerInfo.isDoPostInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoPost(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoPost(exchange); } } else if (method.equals(IHttpRequest.PUT_METHOD)) { if (handlerInfo.isDoPutInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoPut(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoPut(exchange); } } else if (method.equals(IHttpRequest.DELETE_METHOD)) { if (handlerInfo.isDoDeleteInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoDelete(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoDelete(exchange); } } else if (method.equals(IHttpRequest.HEAD_METHOD)) { if (handlerInfo.isDoHeadInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoHead(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoHead(exchange); } } else if (method.equals(IHttpRequest.OPTIONS_METHOD)) { if (handlerInfo.isDoOptionsInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoOptions(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoOptions(exchange); } } else if (method.equals(IHttpRequest.TRACE_METHOD)) { if (handlerInfo.isDoTraceInvokeOnMessage() && exchange.getRequest().hasBody()) { IBodyCompleteListener cl = new IBodyCompleteListener() { public void onComplete() throws IOException { callDoTrace(exchange); } }; NonBlockingBodyDataSource ds = exchange.getRequest().getNonBlockingBody(); ds.addCompleteListener(cl); ds.addDestroyListener(new DestroyListener(exchange)); } else { callDoTrace(exchange); } } else { exchange.forward(exchange.getRequest()); } } private static final class DestroyListener implements IBodyDestroyListener { private final IHttpExchange exchange; public DestroyListener(IHttpExchange exchange) { this.exchange = exchange; } public void onDestroyed() throws IOException { exchange.destroy(); } } private void callDoGet(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoGetMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doGet(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doGet()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doGet(exchange); } } private void callDoPost(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoPostMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doPost(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doPost()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doPost(exchange); } } private void callDoPut(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoPutMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doPut(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doPut()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doPut(exchange); } } private void callDoDelete(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoDeleteMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doDelete(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doDelete()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doDelete(exchange); } } private void callDoHead(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoHeadMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doHead(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doHead()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doHead(exchange); } } private void callDoOptions(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoOptionsMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doOptions(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doOptions()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doOptions(exchange); } } private void callDoTrace(final IHttpExchange exchange) throws IOException { if (handlerInfo.isDoTraceMultithreaded()) { Runnable task = new Runnable() { public void run() { try { doTrace(exchange); } catch (IOException ioe) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("error occured by performing doTrace()"); } if (exchange.getConnection().isOpen()) { exchange.sendError(ioe); } } } }; ((AbstractHttpConnection) exchange.getConnection()).getExecutor().processMultithreaded(task); } else { doTrace(exchange); } } /** * call back which will be called, if a GET request is received * *

Overriding this method to support a GET request also * automatically supports an HTTP HEAD request. A HEAD * request is a GET request that returns no body in the * response, only the request header fields.

* *

The GET method should also be idempotent, meaning * that it can be safely repeated.

* * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doGet(IHttpExchange exchange) throws IOException, BadMessageException { exchange.forward(exchange.getRequest()); } /** * call back which will be called, if a POST request is received * *

This method does not need to be either safe or idempotent. * Operations requested through POST can have side effects

* * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doPost(IHttpExchange exchange) throws IOException, BadMessageException { exchange.forward(exchange.getRequest()); } /** * call back which will be called, if a PUT request is received * *

This method does not need to be either safe or idempotent. * Operations that doPut performs can have side * effects

* * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doPut(IHttpExchange exchange) throws IOException, BadMessageException { exchange.forward(exchange.getRequest()); } /** * call back which will be called, if a DELETE request is received * *

This method does not need to be either safe * or idempotent. Operations requested through * DELETE can have side effects

* * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doDelete(IHttpExchange exchange) throws IOException, BadMessageException { exchange.forward(exchange.getRequest()); } /** * call back which will be called, if a HEAD request is received * *

The client sends a HEAD request when it wants * to see only the headers of a response, such as * Content-Type or Content-Length.

* * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doHead(IHttpExchange exchange) throws IOException, BadMessageException { doGet(new HeadMethodHttpExchange(exchange)); } /** * call back which will be called, if a OPTIONS request is received * * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doOptions(IHttpExchange exchange) throws IOException, BadMessageException { exchange.forward(exchange.getRequest()); } /** * call back which will be called, if a TRACE request is received * * * @param exchange the exchange * @throws IOException if an exception occurred. By throwing this exception an error http response message * will be sent by xSocket, if one or more requests are unanswered. The underlying * connection will be closed * @throws BadMessageException By throwing this exception an error http response message will be sent by xSocket, * which contains the exception message. The underlying connection will be closed */ public void doTrace(IHttpExchange exchange) throws IOException, BadMessageException { exchange.forward(exchange.getRequest()); } private static final class HeadMethodHttpExchange implements IHttpExchange { private IHttpExchange delegate = null; public HeadMethodHttpExchange(IHttpExchange delegate) { this.delegate = delegate; } public void destroy() { delegate.destroy(); } public BodyDataSink forward(IHttpRequestHeader requestHeader, IHttpResponseHandler responseHandler) throws IOException, ConnectException, IllegalStateException { return delegate.forward(requestHeader, responseHandler); } public BodyDataSink forward(IHttpRequestHeader requestHeader) throws IOException, ConnectException, IllegalStateException { return delegate.forward(requestHeader); } public BodyDataSink forward(IHttpRequestHeader requestHeader, int contentLength) throws IOException, ConnectException, IllegalStateException { return delegate.forward(requestHeader, contentLength); } public BodyDataSink forward(IHttpRequestHeader requestHeader, int contentLength, IHttpResponseHandler responseHandler) throws IOException, ConnectException, IllegalStateException { return delegate.forward(requestHeader, contentLength, responseHandler); } public void forward(IHttpRequest request) throws IOException, ConnectException, IllegalStateException { delegate.forward(request); } public void forward(IHttpRequest request, IHttpResponseHandler responseHandler) throws IOException, ConnectException, IllegalStateException { delegate.forward(request, responseHandler); } public boolean sendContinue() throws IOException { return delegate.sendContinue(); } public boolean sendContinueIfRequested() throws IOException { return delegate.sendContinueIfRequested(); } public IHttpConnection getConnection() { return delegate.getConnection(); } public IHttpRequest getRequest() { return delegate.getRequest(); } public IHttpSession getSession(boolean create) { return delegate.getSession(create); } public String encodeURL(String url) { return delegate.encodeURL(url); } public BodyDataSink send(final IHttpResponseHeader header) throws IOException, IllegalStateException { final BodyDataSink bodyDataSink = newEmtpyBodyDataSink(); IBodyCloseListener closeListener = new IBodyCloseListener() { @Execution(Execution.NONTHREADED) public void onClose() throws IOException { delegate.send(new HttpResponse(header)); } }; bodyDataSink.addCloseListener(closeListener); return bodyDataSink; } public BodyDataSink send(IHttpResponseHeader header, int contentLength) throws IOException, IllegalStateException { return send(header); } public void sendRedirect(String location) throws IllegalStateException { delegate.sendRedirect(location); } public void send(IHttpResponse response) throws IOException, IllegalStateException { delegate.send(new HttpResponse(response.getResponseHeader())); } public void sendError(int errorCode, String msg) throws IllegalStateException { delegate.sendError(errorCode, msg); } public void sendError(int errorCode) throws IllegalStateException { delegate.sendError(errorCode); } public void sendError(Exception e) throws IllegalStateException { delegate.sendError(e); } private BodyDataSink newEmtpyBodyDataSink() throws IOException { return null; } } private static final class HandlerInfo { private boolean isDoGetMultithreaded = true; private boolean isDoGetInvokeOnMessage = false; private boolean isDoPostMultithreaded = true; private boolean isDoPostInvokeOnMessage = false; private boolean isDoPutMultithreaded = true; private boolean isDoPutInvokeOnMessage = false; private boolean isDoHeadMultithreaded = true; private boolean isDoHeadInvokeOnMessage = false; private boolean isDoOptionsMultithreaded = true; private boolean isDoOptionsInvokeOnMessage = false; private boolean isDoTraceMultithreaded = true; private boolean isDoTraceInvokeOnMessage = false; private boolean isDoDeleteMultithreaded = true; private boolean isDoDeleteInvokeOnMessage = false; public HandlerInfo(Class clazz) { boolean isMultithtreadedDefault = (DEFAULT_EXECUTION_MODE == Execution.MULTITHREADED); boolean isInvokeOnMessageDefault = (DEFAULT_INVOKE_ON_MODE == InvokeOn.MESSAGE_RECEIVED); isDoDeleteMultithreaded = isMethodMultithreaded(clazz, "doDelete", isMultithtreadedDefault); isDoDeleteInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doDelete", isInvokeOnMessageDefault); isDoGetMultithreaded = isMethodMultithreaded(clazz, "doGet", isMultithtreadedDefault); isDoGetInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doGet", isInvokeOnMessageDefault); isDoPostMultithreaded = isMethodMultithreaded(clazz, "doPost", isMultithtreadedDefault); isDoPostInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doPost", isInvokeOnMessageDefault); isDoPutMultithreaded = isMethodMultithreaded(clazz, "doPut", isMultithtreadedDefault); isDoPutInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doPut", isInvokeOnMessageDefault); isDoTraceMultithreaded = isMethodMultithreaded(clazz, "doTrace", isMultithtreadedDefault); isDoTraceInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doTrace", isInvokeOnMessageDefault); isDoOptionsMultithreaded = isMethodMultithreaded(clazz, "doOptions", isMultithtreadedDefault); isDoOptionsInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doOptions", isInvokeOnMessageDefault); isDoHeadMultithreaded = isMethodMultithreaded(clazz, "doHead", isMultithtreadedDefault); isDoHeadInvokeOnMessage = isOnRequestInvokeOnMessageReceived(clazz, "doHead", isInvokeOnMessageDefault); } private boolean isMethodMultithreaded(Class clazz, String methodname, boolean dflt) { Execution execution = clazz.getAnnotation(Execution.class); if (execution != null) { return (execution.value() == Execution.MULTITHREADED); } try { Method meth = clazz.getMethod(methodname, new Class[] { IHttpExchange.class }); execution = meth.getAnnotation(Execution.class); if (execution != null) { return (execution.value() == Execution.MULTITHREADED); } } catch (NoSuchMethodException nsme) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("shouldn't occure because body handler has to have such a method " + nsme.toString()); } } return dflt; } private boolean isOnRequestInvokeOnMessageReceived(Class clazz, String methodname, boolean dflt) { InvokeOn invokeOn = clazz.getAnnotation(InvokeOn.class); if (invokeOn != null) { return (invokeOn.value() == Execution.MULTITHREADED); } try { Method meth = clazz.getMethod(methodname, new Class[] { IHttpExchange.class }); invokeOn = meth.getAnnotation(InvokeOn.class); if (invokeOn != null) { return (invokeOn.value() == Execution.MULTITHREADED); } } catch (NoSuchMethodException nsme) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("shouldn't occure because response handler has to have such a method " + nsme.toString()); } } return dflt; } public boolean isDoGetMultithreaded() { return isDoGetMultithreaded; } public boolean isDoGetInvokeOnMessage() { return isDoGetInvokeOnMessage; } public boolean isDoPostMultithreaded() { return isDoPostMultithreaded; } public boolean isDoPostInvokeOnMessage() { return isDoPostInvokeOnMessage; } public boolean isDoPutMultithreaded() { return isDoPutMultithreaded; } public boolean isDoPutInvokeOnMessage() { return isDoPutInvokeOnMessage; } public boolean isDoHeadMultithreaded() { return isDoHeadMultithreaded; } public boolean isDoHeadInvokeOnMessage() { return isDoHeadInvokeOnMessage; } public boolean isDoOptionsMultithreaded() { return isDoOptionsMultithreaded; } public boolean isDoOptionsInvokeOnMessage() { return isDoOptionsInvokeOnMessage; } public boolean isDoTraceMultithreaded() { return isDoTraceMultithreaded; } public boolean isDoTraceInvokeOnMessage() { return isDoTraceInvokeOnMessage; } public boolean isDoDeleteMultithreaded() { return isDoDeleteMultithreaded; } public boolean isDoDeleteInvokeOnMessage() { return isDoDeleteInvokeOnMessage; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy