Package org.xlightweb

Source Code of org.xlightweb.HttpUtils$HttpConnectionHandlerInfo

/*
*  Copyright (c) xlightweb.org, 2008 - 2009. 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.net.URLDecoder;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;


import org.xlightweb.AbstractHttpConnection.IMultimodeExecutor;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.ILifeCycle;
import org.xsocket.Resource;
import org.xsocket.SerializedTaskQueue;
import org.xsocket.connection.ConnectionUtils;
import org.xsocket.connection.IServer;
import org.xsocket.connection.IWriteCompletionHandler;



/**
* A HTTP utility class
*
* @author grro@xlightweb.org
*/
public final class HttpUtils {
 
  static final int MAX_HEADER_SIZE = 8192;
 
  static final byte CR = 13;
  static final byte LF = 10;
  static final byte SPACE = 32;
  static final byte HTAB = 9;
  static final byte AND = 38;
  static final byte SLASH = 47;
  static final byte COLON = 58;
  static final byte EQUALS = 61;
  static final byte QUESTION_MARK = 63;
 
  private static final Logger LOG = Logger.getLogger(HttpUtils.class.getName());
 
 
  private static final Executor DEFAULT_WORKERPOOL = Executors.newCachedThreadPool();
 
    private static final String DATE_TIME_PATTERN_1 = "yyyy-MM-dd'T'HH:mm:ss";
    private static final String DATE_TIME_PATTERN_2 = "yyyyMMdd'T'HHmmssz";
    private static final String DATE_TIME_PATTERN_3 = "yyyy-MM-dd'T'HH:mm:ss.S";

    private static final String RFC1123_TIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz";
    private static final String RFC1036_TIME_PATTERN = "EEEE, dd-MMM-yy HH:mm:ss zzz";
   
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> bodyDataExecutionModeCache = ConnectionUtils.newMapCache(25);
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> bodyCompleteListenerExecutionModeCache = ConnectionUtils.newMapCache(25);

  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> requestTimeoutHandlerExecutionModeCache = ConnectionUtils.newMapCache(25);

    @SuppressWarnings("unchecked")
    private static final Map<Class, CompletionHandlerInfo> completionHandlerInfoCache = ConnectionUtils.newMapCache(25);
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, Boolean> bodyCloseListenerExecutionModeCache = ConnectionUtils.newMapCache(25);

  @SuppressWarnings("unchecked")
  private static final Map<Class, String[]> mappingCache = ConnectionUtils.newMapCache(25);

 
  @SuppressWarnings("unchecked")
  private static final Map<Class, RequestHandlerInfo> httpRequestHandlerInfoCache = ConnectionUtils.newMapCache(25);
  private static RequestHandlerInfo emptyHttpRequestHandlerInfo;

  @SuppressWarnings("unchecked")
  private static final Map<Class, PartHandlerInfo> partHandlerInfoCache = ConnectionUtils.newMapCache(25);
  private static PartHandlerInfo emptyPartHandlerInfo;
 
  @SuppressWarnings("unchecked")
  private static final Map<Class, ResponseHandlerInfo> httpResponseHandlerInfoCache = ConnectionUtils.newMapCache(25);
  private static ResponseHandlerInfo emptyResponseHandlerInfo;

 
  @SuppressWarnings("unchecked")
  private static final Map<Class, HttpConnectionHandlerInfo> httpConnectionHandlerInfoCache = ConnectionUtils.newMapCache(25);
  private static final HttpConnectionHandlerInfo EMPTY_HTTP_CONNECTION_HANDLER_INFO = new HttpConnectionHandlerInfo(null);
 
 
  private static String implementationVersion;
  private static String implementationDate;
  private static String xSocketImplementationVersion;
 
 
  private static Map<String, String> mimeTypeMap;

  private static boolean showDetailedError;

  private static Boolean isConnectHandlerWarningIsSuppressed = null;
 
 
  static {
    showDetailedError = Boolean.parseBoolean(System.getProperty(IHttpExchange.SHOW_DETAILED_ERROR_KEY, IHttpExchange.SHOW_DETAILED_ERROR_DEFAULT));
  }

  private static String MESSAGE_DEFAULT_ENCODING = System.getProperty(IHttpMessage.DEFAULT_ENCODING_KEY, IHttpMessage.DEFAULT_ENCODING);
 

  private static final char[] ISO_8859_1_Array = new char[256];

  static {
    for (int i = 0; i < ISO_8859_1_Array.length; i++) {
      try {
        ISO_8859_1_Array[i] = new String(new byte[] { (byte) i }, "ISO-8859-1").charAt(0);
      } catch (UnsupportedEncodingException use) {
        throw new RuntimeException(use);
      }
    }
  }
 

  private static final byte base64[] = {
              (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D',
              (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H'
              (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
              (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P'
              (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
              (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X'
              (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b',
              (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',      
              (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
              (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',      
              (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
              (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',      
              (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
              (byte) '0', (byte) '1', (byte) '2', (byte) '3',      
              (byte) '4', (byte) '5', (byte) '6', (byte) '7',
              (byte) '8', (byte) '9', (byte) '+', (byte) '/',      
              (byte) '='                         
  };

  private HttpUtils() { }
   
 
 
  /**
   * returns a ISO_8859_1 byte array
   *
   * @return a ISO_8859_1 byte array
   */
  static char[] getISO_8859_1_Array() {
    return ISO_8859_1_Array;
  }
 
 
  /**
   * returns true, if detailed error messages should been shown. See {@link IHttpExchange#SHOW_DETAILED_ERROR_KEY}
   *
   * @return true, if detailed error messages should been shown
   */
  public static boolean isShowDetailedError() {
    return showDetailedError;
  }


  /**
   * clears the internal caches
   *
   * <br/><br/><b>This is a xSocket internal method and subject to change</b>
   */
  public static void clearCaches() {
    bodyDataExecutionModeCache.clear();
    mappingCache.clear();
    bodyCompleteListenerExecutionModeCache.clear();;
    requestTimeoutHandlerExecutionModeCache.clear();
    completionHandlerInfoCache.clear();
    bodyCloseListenerExecutionModeCache.clear();
    httpRequestHandlerInfoCache.clear();
    partHandlerInfoCache.clear();
    httpResponseHandlerInfoCache.clear();
    httpConnectionHandlerInfoCache.clear();
  }

 
  /**
   * returns the reason text
   *
   * @param statusCode the status code
   * @return the reason text
   */
  public static String getReason(int statusCode) {
    switch (statusCode) {
    case 200:
      return "OK";
     
    case 201:
      return "Created";

        case 202:
            return "Accepted";
     
        case 301:
            return "Moved Permanently";
           
        case 302:
            return "Not Found";
           
        case 303:
            return "See other";
           
    case 304:
      return "Not Modifed";

    case 401:
      return "Unauthorized";

    case 404:
      return "Not found";
     
    case 500:
      return "Internal Server Error";
     
    case 501:
      return "Not Implemented";

    case 502:
      return "Bad Gateway";

    case 503:
      return "Service Unavailable";
     
    case 504:
      return "Gateway Timeout";
   
    case 505:
      return "HTTP Version Not Supported";

     
    default:
      return " ";
    }
  }

 
 

 

  /**
   * encodes the given byte array
   *
   * @param bytes   byte array to encode
   * @return the encoded byte array
   * @throws IOException if an exception occurs
   */
  public static byte[] encodeBase64(byte bytes[]) throws IOException {
    ByteArrayInputStream in = new ByteArrayInputStream(bytes);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
   
    byte buffer[] = new byte[1024];
    int got = -1;
    int off = 0;
    int count = 0;
   
    while ((got = in.read(buffer, off, 1024 - off)) > 0) {
     
      if (got >= 3) {
        got += off;
        off  = 0;
       
              while (off + 3 <= got) {
                  int c1 = (buffer[off] & 0xfc) >> 2;
                  int c2 = ((buffer[off]&0x3) << 4) | ((buffer[off+1]&0xf0) >>> 4);
                  int c3 = ((buffer[off+1] & 0x0f) << 2) | ((buffer[off+2] & 0xc0) >>> 6);
                  int c4 = buffer[off+2] & 0x3f;
                 
                  switch (count) {
                      case 73:
                         out.write(base64[c1]);
                         out.write(base64[c2]);
                         out.write(base64[c3]);
                         out.write ('\n') ;
                         out.write(base64[c4]);
                         count = 1 ;
                         break ;
                        
                       case 74:
                         out.write(base64[c1]);
                         out.write(base64[c2]);
                         out.write ('\n') ;
                         out.write(base64[c3]);
                         out.write(base64[c4]) ;
                         count = 2 ;
                         break ;
                        
                       case 75:
                         out.write(base64[c1]);
                         out.write ('\n') ;
                         out.write(base64[c2]);
                         out.write(base64[c3]);
                         out.write(base64[c4]) ;
                         count = 3 ;
                         break ;
                        
                       case 76:
                         out.write('\n') ;
                         out.write(base64[c1]);
                         out.write(base64[c2]);
                         out.write(base64[c3]);
                         out.write(base64[c4]);
                         count = 4;
                         break;
                        
                       default:
                         out.write(base64[c1]);
                         out.write(base64[c2]);
                         out.write(base64[c3]);
                         out.write(base64[c4]);
                         count += 4;
                         break;
                        
                  }
                  off += 3;
              }
             
              for ( int i = 0 ; i < 3 ;i++) {
                  buffer[i] = (i < got-off) ? buffer[off+i] : ((byte) 0);
              }
              off = got-off ;
             
           } else {
              off += got;
           }
    }


    switch (off) {
      case 1:
        out.write(base64[(buffer[0] & 0xfc) >> 2]);
              out.write(base64[((buffer[0]&0x3) << 4) | ((buffer[1]&0xf0) >>> 4)]);
              out.write('=');
              out.write('=');
              break ;
             
          case 2:
              out.write(base64[(buffer[0] & 0xfc) >> 2]);
              out.write(base64[((buffer[0]&0x3) << 4) | ((buffer[1]&0xf0) >>> 4)]);
              out.write(base64[((buffer[1] & 0x0f) << 2) | ((buffer[2] & 0xc0) >>> 6)]);
              out.write('=');
    }
 
    return out.toByteArray();
  }
 


  /**
   * decodes the given byte array
   *
   * @param bytes   byte array to decode
   * @return the decoded byte array
   * @throws IOException if an exception occurs
   */
  public static byte[] dencodeBase64(byte bytes[]) throws IOException {
    ByteArrayInputStream in = new ByteArrayInputStream(bytes);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
   
    byte buffer[] = new byte[1024];
    byte chunk[] = new byte[4];
    int got = -1;
    int ready = 0;

    fill : while ((got = in.read(buffer)) > 0) {
      int skiped = 0;
      while (skiped < got) {
        while (ready < 4) {
          if (skiped >= got) {
            continue fill;
          }

          int ch = buffer[skiped++];

          if ((ch >= 'A') && (ch <= 'Z')) {
            ch = ch - 'A';
          } else if ((ch >= 'a') && (ch <= 'z')) {
            ch =  ch - 'a' + 26;
          } else if ((ch >= '0') && (ch <= '9')) {
            ch = ch - '0' + 52;
          } else {
            switch (ch) {
              case '=':
                ch = 65;
                break;
             
              case '+':
                ch = 62;
                break;
             
              case '/':
                ch = 63;
                break;
               
              default:
                ch = -1;
            }
          }
         
          if (ch >= 0) {
            chunk[ready++] = (byte) ch;
          }
        }
       
        if (chunk[2] == 65) {
          out.write(((chunk[0] & 0x3f) << 2) | ((chunk[1] & 0x30) >>> 4));
          out.flush();
          return out.toByteArray();
         
        } else if (chunk[3] == 65) {
          out.write(((chunk[0] & 0x3f) << 2) | ((chunk[1] & 0x30) >>> 4));
          out.write(((chunk[1] & 0x0f) << 4) | ((chunk[2] & 0x3c) >>> 2));
          out.flush();
          return out.toByteArray();
         
        } else {
          out.write(((chunk[0] & 0x3f) << 2) | ((chunk[1] & 0x30) >>> 4));
          out.write(((chunk[1] & 0x0f) << 4) | ((chunk[2] & 0x3c) >>> 2));
          out.write(((chunk[2] & 0x03) << 6) | (chunk[3] & 0x3f));
         
        }
        ready = 0;
      }
    }
    if (ready != 0) {
      throw new IOException("invalid length");
    }
   
    out.flush();
    return out.toByteArray();
  }
 
 
  static String getMessageDefaultEncdoing() {
      return MESSAGE_DEFAULT_ENCODING;
  }
   
 
  /**
   * validate, based on a leading int length field. The length field will be removed
   *
   * @param connection     the connection
   * @return the length
   * @throws IOException if an exception occurs
   * @throws BufferUnderflowException if not enough data is available
   */
  public static int validateSufficientDatasizeByIntLengthField(NonBlockingBodyDataSource stream) throws IOException, BufferUnderflowException {
    return validateSufficientDatasizeByIntLengthField(stream, true) ;
  }
   
 
  /**
   * validate, based on a leading int length field, that enough data (getNumberOfAvailableBytes() >= length) is available. If not,
   * an BufferUnderflowException will been thrown.
   *
   * @param connection         the connection
   * @param removeLengthField  true, if length field should be removed
   * @return the length
   * @throws IOException if an exception occurs
   * @throws BufferUnderflowException if not enough data is available
   */
  public static int validateSufficientDatasizeByIntLengthField(NonBlockingBodyDataSource stream, boolean removeLengthField) throws IOException, BufferUnderflowException {

    stream.resetToReadMark();
    stream.markReadPosition();
   
    // check if enough data is available
    int length = stream.readInt();
    if (stream.available() < length) {
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("insufficient data. require " + length + " got "  + stream.available());
      }
      throw new BufferUnderflowException();
 
    } else {
      // ...yes, remove mark
      if  (!removeLengthField) {
        stream.resetToReadMark();
      }
      stream.removeReadMark();
      return length;
    }
  } 
 
 
 
  /**
   * get the mime type file to extension map
   *
   * @return the mime type file to extension map
   */
  public synchronized static Map<String, String> getMimeTypeMapping() {
   
    if (mimeTypeMap == null) {
     
      Map<String, String> map = new HashMap<String, String>();
      mimeTypeMap = Collections.unmodifiableMap(map);
     
      InputStreamReader isr = null;
      LineNumberReader lnr = null;
      try {
        isr = new InputStreamReader(HttpUtils.class.getResourceAsStream("/org/xlightweb/mime.types"));
        if (isr != null) {
          lnr = new LineNumberReader(isr);
          String line = null;
          while (true) {
            line = lnr.readLine();
            if (line != null) {
              line = line.trim();
              if (!line.startsWith("#")) {
                StringTokenizer st = new StringTokenizer(line);
                if (st.hasMoreTokens()) {
                  String mimeType = st.nextToken();
                  while (st.hasMoreTokens()) {
                    String extension = st.nextToken();
                    map.put(extension, mimeType);
                     
                    if (LOG.isLoggable(Level.FINER)) {
                      LOG.finer("mapping " + extension + " -> " + mimeType + " added");
                    }
                  }
                } else {
                  if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("line " + line + "ignored");
                 
                }
              }
            } else {
              break;
            }
          }
   
          lnr.close();
        }
       
      } catch (Exception ioe) {
          // eat and log exception
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("could not read mime.types. reason: " + ioe.toString());
        }       
       
      } finally {
        try {
          if (lnr != null) {
            lnr.close();
          }
           
          if (isr != null) {
            isr.close();
          }
        } catch (IOException ioe) {
          if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("exception occured by closing mime.types file stream " + ioe.toString());
          }
        }
      }

     
     
    }
   
    return mimeTypeMap;
  }

 
 

  /**
   * injects a server field
   *
   * @param handler   the handler
   * @param server    the server to inject
   */
  static void injectServerField(Object handler, IServer server) {
    Field[] fields = handler.getClass().getDeclaredFields();
    for (Field field : fields) {
      if (field.isAnnotationPresent(Resource.class)) {
        Resource res = field.getAnnotation(Resource.class);
        if ((field.getType() == IServer.class) || (res.type() == IServer.class)) {
          field.setAccessible(true);
          try {
            field.set(handler, server);
          } catch (IllegalAccessException iae) {
            LOG.warning("could not set HandlerContext for attribute " + field.getName() + ". Reason " + iae.toString());
          }
        }
      }
    }
  }





  static boolean isContentTypeFormUrlencoded(IHttpMessage message) {
   
    if (!message.hasBody()) {
      return false;
    }
   
    String contentType = message.getContentType();
    if ((contentType != null) && (contentType.startsWith("application/x-www-form-urlencoded"))) {
      return true;
    }
   
    return false;
  }
 
 
  static boolean isPrintable(IHttpMessage message) {
    if (!message.hasBody()) {
      return true;
    }
   
    String contentType = message.getContentType();
    if (contentType == null) {
      return true;
    }
   
    contentType = contentType.toLowerCase();
    if (contentType.startsWith("text") ||
      contentType.startsWith("application/json") ||
      contentType.startsWith("application/x-www-form-urlencoded")) {
      return true;
    }
   
    return false;
  }
 
 
 
  /**
   * returns the encoding type of content typed FORM URL encoded message
   *
   * @param message  the message
   * @return the encoding type
   */
  static String getContentTypedFormUrlencodedEncodingType(IHttpMessage message) {
   
    String contentType = message.getContentType();
    if ((contentType != null) && (contentType.startsWith("application/x-www-form-urlencoded"))) {
      String[] parts = contentType.split(";");
      if (parts.length > 1) {
        for(int i = 1; i < parts.length; i++) {
          String[] kvp = parts[i].split("=");
          if (kvp[0].trim().toUpperCase().equals("CHARSET")) {
            return kvp[1].trim();
          }
        }
      }
    }
   
    return HttpUtils.getMessageDefaultEncdoing();
  }
 


 
  /**
   * creates a FORM URL encoded body message wrapper
   *
   * @param request  the request
   * @return a FORM URL encoded body message wrapper
   * @throws IOException  if an exception occurs
   */
  static IHttpRequest newFormEncodedRequestWrapper(IHttpRequest request) throws IOException {
    if (request instanceof HttpRequestWrapper) {
      return request;
    }
   
    return new HttpRequestWrapper(new FormEncodedRequestHeaderWrapper(request), request);
  }
 
 
 

  static String[] retrieveMappings(IHttpRequestHandler handler) {
    String[] mappings = mappingCache.get(handler.getClass());
   
    if (mappings == null) {
          Mapping mappingAnnotation = handler.getClass().getAnnotation(Mapping.class);
          if (mappingAnnotation != null) {
            mappings = mappingAnnotation.value();
            mappingCache.put(handler.getClass(), mappings);
          }
    }

    return mappings;
 
 
   
    static CompletionHandlerInfo getCompletionHandlerInfo(IWriteCompletionHandler handler) {
        CompletionHandlerInfo completionHandlerInfo = completionHandlerInfoCache.get(handler.getClass());

        if (completionHandlerInfo == null) {
            completionHandlerInfo = new CompletionHandlerInfo(handler);
            completionHandlerInfoCache.put(handler.getClass(), completionHandlerInfo);
        }

        return completionHandlerInfo;
   

    /**
     * returns the part handler info
     *
     * @param partHandler the part handler
     * @return the part handler info
     */
    @SuppressWarnings("unchecked")
    static PartHandlerInfo getPartHandlerInfo(IPartHandler partHandler) {
        if (partHandler == null) {
            if (emptyPartHandlerInfo == null) {
                emptyPartHandlerInfo = new PartHandlerInfo(null);
            }
            return emptyPartHandlerInfo;
        }

        PartHandlerInfo partHandlerInfo = partHandlerInfoCache.get(partHandler.getClass());

        if (partHandlerInfo == null) {
            partHandlerInfo = new PartHandlerInfo((Class<IPartHandler>) partHandler.getClass());
            partHandlerInfoCache.put(partHandler.getClass(), partHandlerInfo);
        }

        return partHandlerInfo;
    }

 
  /**
   * returns the response handler info
   *
   * @param httpResponseHandler  the response handler
   * @return the response handler info
   */
  @SuppressWarnings("unchecked")
  private static ResponseHandlerInfo getHttpResponseHandlerInfo(IHttpResponseHandler httpResponseHandler) {
    if (httpResponseHandler == null) {
      if (emptyResponseHandlerInfo == null) {
        emptyResponseHandlerInfo = new ResponseHandlerInfo(null);
      }
      return emptyResponseHandlerInfo;
    }

    ResponseHandlerInfo httpResponseHandlerInfo = httpResponseHandlerInfoCache.get(httpResponseHandler.getClass());

    if (httpResponseHandlerInfo == null) {
      httpResponseHandlerInfo = new ResponseHandlerInfo((Class<IHttpResponseHandler>) httpResponseHandler.getClass());
      httpResponseHandlerInfoCache.put(httpResponseHandler.getClass(), httpResponseHandlerInfo);
    }

    return httpResponseHandlerInfo;
  }
 

  /**
   * returns the connection handler info
   *
   * @param httpConnectionHandler  the connection handler
   * @return the connection handler info
   */
  @SuppressWarnings("unchecked")
  static HttpConnectionHandlerInfo getHttpConnectionHandlerInfo(IHttpConnectionHandler httpConnectionHandler) {
    if (httpConnectionHandler == null) {
      return EMPTY_HTTP_CONNECTION_HANDLER_INFO;
    }

    HttpConnectionHandlerInfo httpConnectionHandlerInfo = httpConnectionHandlerInfoCache.get(httpConnectionHandler.getClass());

    if (httpConnectionHandlerInfo == null) {
      httpConnectionHandlerInfo = new HttpConnectionHandlerInfo((Class<IHttpConnectionHandler>) httpConnectionHandler.getClass());
      httpConnectionHandlerInfoCache.put(httpConnectionHandler.getClass(), httpConnectionHandlerInfo);
    }

    return httpConnectionHandlerInfo;
  }

 


  /**
   * <b>This is a xSocket internal method and subject to change</b>
   */
  @SuppressWarnings("unchecked")
    static RequestHandlerInfo getRequestHandlerInfo(IHttpRequestHandler requestHandler) {
      if (requestHandler == null) {
            if (emptyHttpRequestHandlerInfo == null) {
                emptyHttpRequestHandlerInfo = new RequestHandlerInfo(null);
            }
            return emptyHttpRequestHandlerInfo;
        }

        RequestHandlerInfo requestHandlerInfo = httpRequestHandlerInfoCache.get(requestHandler.getClass());

        // info found in cache?
        if (requestHandlerInfo != null) {
            return requestHandlerInfo;
        
        // .. no, it is not in cache
        } else {
            // create a new one
            requestHandlerInfo = new RequestHandlerInfo((Class<IHttpRequestHandler>) requestHandler.getClass());
            httpRequestHandlerInfoCache.put(requestHandler.getClass(), requestHandlerInfo);
        }

        return requestHandlerInfo;
  }

 
  /**
   * <b>This is a xSocket internal method and subject to change</b>
   */
  static ResponseHandlerInfo getResponseHandlerInfo(IHttpResponseHandler responseHandler) {
    return getHttpResponseHandlerInfo(responseHandler);
  }

 
 
 


 
  static String parseEncoding(String contentType) {

    // contains charset encoding?
        int pos = contentType.indexOf(';');
        if (pos > 0) {
          int pos2 = contentType.indexOf("charset=", pos + 1);
          if (pos2 > 0) {
            String encoding = contentType.substring(pos2 + "charset=".length(), contentType.length()).trim();
            if(encoding.indexOf(';') != -1) {
                encoding = encoding.substring(0, encoding.indexOf(';'));
            }
            return encoding;
          }
        }
       
        return null;
  }

 
  static String parseEncoding(String contentType, String dflt) {
      String encoding = parseEncoding(contentType);
     
      if (encoding == null) {
          return dflt;
      } else {
          return encoding;
      }
    }
 
 

  static boolean isBodyCompleteListenerMutlithreaded(IBodyCompleteListener completeListener) {
    Boolean isMutlithreaded = bodyCompleteListenerExecutionModeCache.get(completeListener.getClass());
    
    if (isMutlithreaded == null) {
      int mode = IBodyDataHandler.DEFAULT_EXECUTION_MODE;
     
      Execution execution = completeListener.getClass().getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
      }
     
      try {
        Method meth = completeListener.getClass().getMethod("onComplete", new Class[] { });
        execution = meth.getAnnotation(Execution.class);
        if (execution != null) {
          mode = execution.value();
        }
       
      } catch (NoSuchMethodException nsme) {
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("shouldn't occure because body handler has to have such a method " + nsme.toString());
        }
      }
     
      isMutlithreaded = (mode == Execution.MULTITHREADED);
     
      bodyCompleteListenerExecutionModeCache.put(completeListener.getClass(), isMutlithreaded);
    }

    return isMutlithreaded;
  }
 
 
 
  /**
   * Copies a request. Only complete received message are supported
   * 
   * @param request   the request
   * @return the copy
   * @throws IOException if an exception occurs
   */
  public static IHttpRequest copy(IHttpRequest request) throws IOException {

    if (request.hasBody()) {
      if (!request.getNonBlockingBody().isCompleteReceived()) {
        throw new IOException("copy is only supported for complete received messages (hint: uses @InvokeOn(InvokeOn.MESSAGE_RECEIVED) annotation)");
      }
 
      return new HttpRequest(request.getRequestHeader().copy(), request.getNonBlockingBody().copyContent());
     
    } else {
      return new HttpRequest(request.getRequestHeader().copy());     
    }
  }
 
 

  /**
   * Copies a response. Only complete received message are supported
   * 
   * @param response  the response
   * @return the copy
   * @throws IOException if an exception occurs
   */
  public static IHttpResponse copy(IHttpResponse response) throws IOException {

    if (response.hasBody()) {
      if (!response.getNonBlockingBody().isCompleteReceived()) {
        throw new IOException("copy is only supported for complete received messages (hint: uses @InvokeOn(InvokeOn.MESSAGE_RECEIVED) annotation)");
      }
 
      return new HttpResponse(response.getResponseHeader().copy(), response.getNonBlockingBody().copyContent());
     
    } else {
      return new HttpResponse(response.getResponseHeader().copy());
    }
  }
 
 
  static int computeRemaining(ByteBuffer[] bufs) {
    int remaining = 0;
   
    if (bufs == null) {
        return 0;
    }
   
    for (ByteBuffer byteBuffer : bufs) {
        if (byteBuffer != null) {
            remaining += byteBuffer.remaining();
        }
    }
   
    return remaining;
  }



  static ByteBuffer merge(ByteBuffer[] buffers) {

    if (buffers.length == 1) {
      return buffers[0];
     
    } else if (buffers.length == 0) {
      return ByteBuffer.allocate(0);
     
    } else {
      int size = 0;
      for (ByteBuffer byteBuffer : buffers) {
        if (byteBuffer != null) {
          size += byteBuffer.remaining();
        }
      }
     
      ByteBuffer buffer = ByteBuffer.allocate(size);
      for (ByteBuffer byteBuffer : buffers) {
        if (byteBuffer != null) {
          int pos = byteBuffer.position();
          int limit = byteBuffer.limit();
          buffer.put(byteBuffer);
         
          byteBuffer.position(pos);
          byteBuffer.limit(limit);
        }
      }
     
      buffer.flip();
      return buffer;
    }
  }

 

  static ByteBuffer[] merge(ByteBuffer[] buffers, ByteBuffer tailBuffer) {

    if (tailBuffer.remaining() == 0) {
      return buffers;
    }
   
    if ((buffers == null) || (buffers.length == 0)) {
      return new ByteBuffer[] { tailBuffer };
     
    } else {
      ByteBuffer[] result = new ByteBuffer[buffers.length + 1];
      System.arraycopy(buffers, 0, result, 0, buffers.length);
      result[buffers.length] = tailBuffer;
     
      return result;
    }
  }
 

 

  static boolean isEmpty(ByteBuffer[] buffer) {
    if (buffer == null) {
      return true;
    }
   
   
    for (ByteBuffer byteBuffer : buffer) {
      if (byteBuffer.hasRemaining()) {
        return false;
      }
    }
   
    return true;
  }

 
 
  static ByteBuffer merge(byte[] bytes, ByteBuffer tailBuffer) {
    int remaining = bytes.length;
    int tailRemaining = tailBuffer.remaining();
   
    // inefficient implementation. improve this
    byte[] data  = new byte[remaining + tailRemaining];
    System.arraycopy(bytes, 0, data, 0, remaining);
    System.arraycopy(DataConverter.toBytes(tailBuffer), 0, data, remaining, tailRemaining);
   
    return DataConverter.toByteBuffer(data);
  }

 

  static ByteBuffer[] merge(ByteBuffer[] buffers, ByteBuffer[] tailBuffers) {
   
    if ((buffers == null) || (buffers.length == 0)) {
      return tailBuffers;
     
    } else {
      ByteBuffer[] result = new ByteBuffer[buffers.length + tailBuffers.length];
      System.arraycopy(buffers, 0, result, 0, buffers.length);
      System.arraycopy(tailBuffers, 0, result, buffers.length, tailBuffers.length);
     
      return result;
    }
  }
 
 
  static ByteBuffer[] compact(ByteBuffer[] buffers) {
    ByteBuffer[] result = null;
   
    if (buffers != null) {
      if (buffers.length == 1) {
        if (buffers[0] == null) {
          return null;
        } else {
          if (buffers[0].remaining() == 0) {
            return null;
          } else {
            return buffers;
          }
        }
      }
     
      for (ByteBuffer buffer : buffers) {
        if (buffer != null) {
          result = merge(result, buffer);
        }
      }
    }
   
    return result;
  }
 
 
  /**
   * copies the given buffer
   *
   * @param buffer  the buffer to copy
   * @return the copy
   */
  static ByteBuffer copy(ByteBuffer buffer) {
    if (buffer == null) {
      return null;
    }
   
    return ByteBuffer.wrap(DataConverter.toBytes(buffer));
  }
 
 
  /**
   * wraps a exception with an io exception
   *
   * @param t   the exception
   * @return the ioe exception
   */
  public static IOException toIOException(Throwable t) {
      if (t instanceof IOException) {
          return (IOException) t;
      } else {
          IOException ioe = new IOException(t.getClass().getName() + ": " + t.toString());
          ioe.setStackTrace(t.getStackTrace());
          return ioe;
      }
  }
 
 
  /**
   * compresses a body (using the GZIP compression)
   *
   * @param dataSource   the body to compress
   * @return the compressed body
   * @throws IOException if an excption occurs
   */
  public static BlockingBodyDataSource compress(BlockingBodyDataSource dataSource) throws IOException {
      byte[] compressedData = compress(dataSource.readBytes());
      InMemoryBodyDataSource compressedDataSource = new InMemoryBodyDataSource(dataSource.getUnderliyingBodyDataSource().getEncoding(),
                                                                               dataSource.getUnderliyingBodyDataSource().getExecutor(),
                                                                               compressedData);
      return new BlockingBodyDataSource(compressedDataSource);
    }
 
 
 
  /**
     * compresses a byte array (using the GZIP compression)
     *
     * @param dataSource   the byte array to compress
     * @return the compressed byte array
     * @throws IOException if an excption occurs
     */
  static byte[] compress(byte[] data) throws IOException {
      ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
      GZIPOutputStream out = new GZIPOutputStream(bos);
     
        out.write(data);
        out.close();
        return bos.toByteArray();
    }
   

  static ByteBuffer[] readFile(File file) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        FileChannel fc = raf.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate((int) fc.size());
        fc.read(buffer);
        fc.close();
        raf.close();
        buffer.flip();
       
        return new ByteBuffer[] { buffer };
    }
 
 
    /**
     * decompresses a body (using the GZIP decompression)
     *
     * @param dataSource   the body to decompress
     * @return the decompressed body
     * @throws IOException if an excption occurs
     */
    public static BlockingBodyDataSource decompress(BlockingBodyDataSource dataSource) throws IOException {
       
        byte[] decompressedData = decompress(dataSource.readBytes());
       
        InMemoryBodyDataSource decompressedDataSource = new InMemoryBodyDataSource(dataSource.getUnderliyingBodyDataSource().getEncoding(),
                dataSource.getUnderliyingBodyDataSource().getExecutor(),
                decompressedData);

        return new BlockingBodyDataSource(decompressedDataSource);
    }
   
   
   
    /**
     * decompresses a byte array (using the GZIP decompression)
     *
     * @param compressedData
     * @return the deconmpressed byte array
     * @throws IOException
     */
    static byte[] decompress(byte[] compressedData) throws IOException {
       
        ByteArrayInputStream bis = new ByteArrayInputStream(compressedData);
        GZIPInputStream in = new GZIPInputStream(bis);
       
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
       
        byte[] transferBuffer = new byte[4096];
        int read = 0;
        do {
            read = in.read(transferBuffer);
            if (read != -1) {
                bos.write(transferBuffer, 0, read);
            }
        } while (read != -1);
        in.close();
               
        in.close();
        bos.close();
       
        return bos.toByteArray();
    }
   
   
 
    /**
     * waits until the complete message is received
     *
     *
     * @param messag            the message
     * @param timeoutMillis     the max wait time
     * @throws SocketTimeoutException  is the max wait time is reached
     * @throws IOException
     */
    public static void waitUntilComplete(final IHttpMessage message, int timeoutMillis) throws SocketTimeoutException, IOException {
       
        final AtomicBoolean isComplete = new AtomicBoolean(false);
       
        if (message.hasBody()) {
           
            if (message.getNonBlockingBody().isComplete()) {
                return;
               
            } else {
                long start = System.currentTimeMillis();

                IBodyCompleteListener cl = new IBodyCompleteListener() {
   
                    public void onComplete() throws IOException {
                        synchronized (message) {
                            isComplete.set(true);
                            message.notifyAll();
                        }
                    }
                };
                message.getNonBlockingBody().addCompleteListener(cl);
               

                do {
                    synchronized (message) {
                        if (!isComplete.get()) {
                            try {
                                message.wait(timeoutMillis);
                            } catch (InterruptedException ignore) { }
                           
                            if ((start - System.currentTimeMillis()) > timeoutMillis) {
                                throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(timeoutMillis) + " reached");
                            }
                        }
                    }
                   
                } while (!isComplete.get());
            }
            
        } else {
            return;
        }
    }
 
 
 
  public static String toDateTimeString(Date date) {
      SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_1);
      formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
      return formatter.format(date);
  }
 
 
  public static Date parseDateTimeString(String dateTimeString) throws java.text.ParseException {

      try {
            SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_1);
            formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
            return formatter.parse(dateTimeString);

        } catch (java.text.ParseException pe) {
           
            try {
                SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_2);
                formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
                return formatter.parse(dateTimeString);
               
            } catch (java.text.ParseException pe2) {
                SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN_3);
                formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
                return formatter.parse(dateTimeString);
            }
        }

    }
 
 
  public static String toRFC1123DateString(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat(RFC1123_TIME_PATTERN, Locale.US);
        formatter.setTimeZone(TimeZone.getTimeZone("GMT"));

        String dateString = formatter.format(date);
        return dateString;
    }
   
 
  public static Date parseHttpDateString(String dateString) {

      try {
          SimpleDateFormat dateParser = new SimpleDateFormat(RFC1123_TIME_PATTERN, Locale.US);
          dateParser.setTimeZone(TimeZone.getTimeZone("GMT"));
          return dateParser.parse(dateString);
         
      } catch (ParseException pe) {

          try {
              SimpleDateFormat dateParser = new SimpleDateFormat(RFC1036_TIME_PATTERN, Locale.US);
              dateParser.setTimeZone(TimeZone.getTimeZone("GMT"));
                return dateParser.parse(dateString);
            } catch (ParseException pe2) {
                return null;
            }
      }

     
  }
 
  /**
   * (deep) copy of the byte buffer array
   *  
   * @param buffers the byte buffer array
   * @return the copy
   */
  static ByteBuffer[] copy(ByteBuffer[] buffers) {
    if (buffers == null) {
      return null;
    }
   
    ByteBuffer[] copy = new ByteBuffer[buffers.length];
    for (int i = 0; i < copy.length; i++) {
      copy[i] = copy(buffers[i]);
    }
    return copy;
  }
 
 
 
  static IMultimodeExecutor newMultimodeExecutor() {
        return newMultimodeExecutor(DEFAULT_WORKERPOOL);
    }
 
 
  static IMultimodeExecutor newMultimodeExecutor(Executor workerpool) {
      return new MultimodeExecutor(workerpool);
  }
 
    
    private static final class MultimodeExecutor implements IMultimodeExecutor  {
       
        private final SerializedTaskQueue taskQueue = new SerializedTaskQueue();

        private Executor workerpool = null;
       
        public MultimodeExecutor(Executor workerpool) {
            this.workerpool = workerpool;
        }

        public void processMultithreaded(Runnable task) {
            taskQueue.performMultiThreaded(task, workerpool);
        }

        public void processNonthreaded(Runnable task) {
            taskQueue.performNonThreaded(task, workerpool);
        }
       
        public Executor getWorkerpool() {
            return workerpool;
        }
    }
 
 
  /**
   * returns the execution mode for request timeout handler
   *
   * @param requestTimeoutHandler  the request timeout handler
   * @return the execution mode
   */
  static boolean isRequestTimeoutHandlerMultithreaded(Class<IHttpRequestTimeoutHandler> clazz) {
    Boolean isMultithreaded = requestTimeoutHandlerExecutionModeCache.get(clazz);
    
    if (isMultithreaded == null) {
      int mode = IHttpRequestTimeoutHandler.DEFAULT_EXECUTION_MODE;
     
      Execution execution = clazz.getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
      }
     
      try {
        Method meth = clazz.getMethod("onRequestTimeout", new Class[] { IHttpConnection.class });
        execution = meth.getAnnotation(Execution.class);
        if (execution != null) {
          mode = execution.value();
        }
       
      } catch (NoSuchMethodException nsme) {
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("shouldn't occure because request timeout handlerr has to have such a method " + nsme.toString());
        }
      }
     
      isMultithreaded = (mode == Execution.MULTITHREADED);
     
      bodyCompleteListenerExecutionModeCache.put(clazz, isMultithreaded);
    }

    return isMultithreaded;
  }
 
 

 
  static Map<String, List<String>> parseParamters(String txt, String encoding) {
    Map<String, List<String>> result = new HashMap<String, List<String>>();
   
    try {
      String[] params = txt.split("&");
      for (String param : params) {
        String[] kv = param.split("=");
        if (kv.length > 1) {
          String name = URLDecoder.decode(kv[0], encoding);
          List<String> values = result.get(name);
          if (values == null) {
            values = new ArrayList<String>();
            result.put(name, values);
          }
       
          values.add(URLDecoder.decode(kv[1], encoding));
        }
      }
     
      return result;
   
    } catch (UnsupportedEncodingException use) {
      throw new RuntimeException(use.toString());
    }
  }
 
 
  /**
   * get the implementation version
   *
   * @return the implementation version
   */
  public static String getImplementationVersion() {
   
    if (implementationVersion == null) {
      readVersionFile();
    }
   
    return implementationVersion;
  }
 
 
    
    /**
     * get the xSocket implementation version
     *
     * @return the xSocket implementation version
     */
    static String getXSocketImplementationVersion() {
       
        if (xSocketImplementationVersion == null) {
            readVersionFile();
        }
       
        return xSocketImplementationVersion;
    }

   
    /**
     * <br/><br/><b>This is a xSocket internal method and subject to change</b>
     */
    public static long parseLong(String longString, long dflt) {
        if (longString == null) {
            return dflt;
        }
       
        try {
            return Long.parseLong(longString);
        } catch (NumberFormatException nfe) {
            return dflt;
        }
    }
   
 
  /**
   * get the implementation date
   *
   * @return the implementation date
   */
  public static String getImplementationDate() {
   
    if (implementationDate== null) {
      readVersionFile();
    }
   
    return implementationDate;
  }

 
  private static void readVersionFile() {
   
    implementationVersion = "<unknown>";
    implementationDate = "<unknown>";
     
    InputStreamReader isr = null;
    LineNumberReader lnr = null;
   
    try {
      isr = new InputStreamReader(HttpUtils.class.getResourceAsStream("/org/xlightweb/version.txt"));
      if (isr != null) {
        lnr = new LineNumberReader(isr);
        String line = null;
   
        do {
          line = lnr.readLine();
          if (line != null) {
            if (line.startsWith("Implementation-Version=")) {
              implementationVersion = line.substring("Implementation-Version=".length(), line.length()).trim();
             
            } else if (line.startsWith("Implementation-Date=")) {
              implementationDate = line.substring("Implementation-Date=".length(), line.length()).trim();
             
            } else if (line.startsWith("Dependency.xSocket.Implementation-Version=")) {
                            xSocketImplementationVersion = line.substring("Dependency.xSocket.Implementation-Version=".length(), line.length()).trim();
                        }
          }
        } while (line != null);
       
        lnr.close();
      }
    } catch (Exception ioe) {
       
            implementationDate = "<unknown>";
            implementationVersion  = "<unknown>";
            xSocketImplementationVersion  = "<unknown>";
                   
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("could not read version file. reason: " + ioe.toString());
      }       
     
    } finally {
      try {
        if (lnr != null) {
          lnr.close();
        }
         
        if (isr != null) {
          isr.close();
        }
      } catch (IOException ioe) {
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("exception occured by closing version.txt file stream " + ioe.toString());
        }
      }
    }
  }
 
 
  /**
   * returns if handler is multi threaded
   *
   * <br/><br/><b>This is a xLightweb internal method and subject to change</b>
   *
   * @param clazz   the handler class
   * @param dflt    the default value
   * @return true, if multi threaded
   */
    static boolean isHandlerMultithreaded(Class<? extends Object> clazz, boolean dflt) {
        Execution execution = clazz.getAnnotation(Execution.class);
        if (execution != null) {
            if(execution.value() == Execution.NONTHREADED) {
                return false;
               
            } else {
                return true;
            }

        } else {
            return dflt;
        }
    }
   

    /**
     * returns if the handler method is multi threaded
     *
   * <br/><br/><b>This is a xSocket internal method and subject to change</b>
     *
     * @param clazz       the handler class
     * @param methodname  the method name
     * @param dflt        the default value
     * @param paramClass  the method parameter classes
     * @return true, if multi threaded
     */
    @SuppressWarnings("unchecked")
    public static boolean isMethodMultithreaded(Class clazz, String methodname, boolean dflt, Class... paramClass) {
        try {
            Method meth = clazz.getMethod(methodname, paramClass);
            Execution execution = meth.getAnnotation(Execution.class);
            if (execution != null) {
                if(execution.value() == Execution.NONTHREADED) {
                    return false;
                } else {
                    return true;
                }
            } else {
                return dflt;
            }
           
        } catch (NoSuchMethodException nsme) {
            return dflt;
        }
    }
   
   
  
   
  static boolean isSynchronizedOnSession(Class<? extends Object> clazz, boolean dflt) {
   
    SynchronizedOn synchronizedOn = clazz.getAnnotation(SynchronizedOn.class);
      if (synchronizedOn != null) {
        if(synchronizedOn.value() == SynchronizedOn.SESSION) {
          return true;
                 
        } else {
          return false;
        }

      } else {
        return dflt;
      }
  }
 
 
  /**
   * returns if the connect handler warning is suppressed
   *
     * <br/><br/><b>This is a xSocket internal method and subject to change</b> 
   *
   * @return true, if the connect handler warning is suppressed
   */
  public static boolean isConnectHandlerWarningIsSuppressed() {
      if (isConnectHandlerWarningIsSuppressed == null) {
          isConnectHandlerWarningIsSuppressed = Boolean.parseBoolean(System.getProperty("org.xlightweb.httpConnectHandler.suppresswarning", "false"));
      }
     
      return isConnectHandlerWarningIsSuppressed;
  }
  
  @SuppressWarnings("unchecked")
  static boolean isSynchronizedOnSession(Class clazz, String methodname, boolean dflt, Class... paramClass) {
    try {
            Method meth = clazz.getMethod(methodname, paramClass);
            SynchronizedOn synchronizedOn = meth.getAnnotation(SynchronizedOn.class);
            if (synchronizedOn != null) {
                if(synchronizedOn.value() == SynchronizedOn.SESSION) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return dflt;
            }
           
        } catch (NoSuchMethodException nsme) {
            return dflt;
        }
  }

 
    static boolean isInvokeOnMessageReceived(Class<? extends Object> clazz, boolean dflt) {
       InvokeOn invokeOn = clazz.getAnnotation(InvokeOn.class);
         if (invokeOn != null) {
             if(invokeOn.value() == InvokeOn.MESSAGE_RECEIVED) {
                 return true;
                
             } else {
                 return false;
             }

         } else {
             return dflt;
         }
  }
   

    @SuppressWarnings("unchecked")
  static boolean isInvokeOnMessageReceived(Class clazz, String methodname, boolean dflt, Class... paramClass) {
      try {
            Method meth = clazz.getMethod(methodname, paramClass);
            InvokeOn invokeOn = meth.getAnnotation(InvokeOn.class);
            if (invokeOn != null) {
                if(invokeOn.value() == InvokeOn.MESSAGE_RECEIVED) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return dflt;
            }
           
        } catch (NoSuchMethodException nsme) {
            return dflt;
        }
  }
   
  
 
  static class HttpConnectionHandlerInfo {
   
    private boolean isConnectHandler = false;
    private boolean isConnectHandlerMultithreaded = true;
   
    private boolean isDisconnectHandler = false;
    private boolean isDisconnectHandlerMultithreaded = true;
   
 
    @SuppressWarnings("unchecked")
    public HttpConnectionHandlerInfo(Class clazz) {

      if (clazz == null) {
        return;
      }
     
     
      if (IHttpConnectHandler.class.isAssignableFrom(clazz)) {
        isConnectHandler = true;
        isConnectHandlerMultithreaded = isOnConnectMultithreaded(clazz);
      }

      if (IHttpDisconnectHandler.class.isAssignableFrom(clazz)) {
        isDisconnectHandler = true;
        isDisconnectHandlerMultithreaded = isOnDisconnectMultithreaded(clazz);
      }
    }



    static boolean isOnConnectMultithreaded(Class<IHttpRequestHandler> serverHandlerClass) {
      int mode = IHttpRequestHandler.DEFAULT_EXECUTION_MODE;

      Execution execution = serverHandlerClass.getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
        return (mode == Execution.MULTITHREADED);
      }

      try {
        Method meth = serverHandlerClass.getMethod("onConnect", new Class[] { IHttpConnection.class });
        execution = meth.getAnnotation(Execution.class);
        if (execution != null) {
          mode = execution.value();
        }

      } 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 (mode == Execution.MULTITHREADED);
    }


    static boolean isOnDisconnectMultithreaded(Class<IHttpRequestHandler> serverHandlerClass) {
      int mode = IHttpRequestHandler.DEFAULT_EXECUTION_MODE;

      Execution execution = serverHandlerClass.getAnnotation(Execution.class);
      if (execution != null) {
        mode = execution.value();
        return (mode == Execution.MULTITHREADED);
      }

      try {
        Method meth = serverHandlerClass.getMethod("onDisconnect", new Class[] { IHttpConnection.class });
        execution = meth.getAnnotation(Execution.class);
        if (execution != null) {
          mode = execution.value();
        }

      } 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 (mode == Execution.MULTITHREADED);
    }

    public boolean isConnectHandler() {
      return isConnectHandler;
    }

    public boolean isConnectHandlerMultithreaded() {
      return isConnectHandlerMultithreaded;
    }

    public boolean isDisconnectHandler() {
      return isDisconnectHandler;
    }

    public boolean isDisconnectHandlerMultithreaded() {
      return isDisconnectHandlerMultithreaded;
    }
  }
 
 
 

 
  private static final class FormEncodedRequestHeaderWrapper extends HttpRequestHeaderWrapper {
   
    private static final Boolean NULL_BOOLEAN = null;
   
    private final IHttpRequest request;
    private final Map<String, List<String>> bodyParamsMap = new HashMap<String, List<String>>();
   
    private boolean isBodyParsed = false;

   
   
    FormEncodedRequestHeaderWrapper(IHttpRequest request) throws IOException {
      super(request.getRequestHeader());
      this.request = request;
    }
     
   

    public Integer getIntParameter(String name) {
      String s = getParameter(name);
      if (s != null) {
        return Integer.parseInt(s);
      } else {
        return null;
      }
    }
   


    public int getIntParameter(String name, int defaultVal) {
      String s = getParameter(name);
      if (s != null) {
        try {
          return Integer.parseInt(s);
        } catch (Exception e) {
          return defaultVal;
        }
      } else {
        return defaultVal;
      }
    }
   

    public Long getLongParameter(String name) {
      String s = getParameter(name);
      if (s != null) {
        return Long.parseLong(s);
      } else {
        return null;
      }
    }
   

    public long getLongParameter(String name, long defaultVal) {
      String s = getParameter(name);
      if (s != null) {
        try {
          return Long.parseLong(s);
        } catch (Exception e) {
          return defaultVal;
        }
      } else {
        return defaultVal;
      }
    }


    public Double getDoubleParameter(String name) {
      String s = getParameter(name);
      if (s != null) {
        return Double.parseDouble(s);
      } else {
        return null;
      }
    }
   

    public double getDoubleParameter(String name, double defaultVal) {
      String s = getParameter(name);
      if (s != null) {
        try {
          return Double.parseDouble(s);
        } catch (Exception e) {
          return defaultVal;
        }
      } else {
        return defaultVal;
      }
    }
   
   
    public Float getFloatParameter(String name) {
      String s = getParameter(name);
      if (s != null) {
        return Float.parseFloat(s);
      } else {
        return null;
      }
    }
   
   
    public float getFloatParameter(String name, float defaultVal) {
      String s = getParameter(name);
      if (s != null) {
        try {
          return Float.parseFloat(s);
        } catch (Exception e) {
          return defaultVal;
        }
      } else {
        return defaultVal;
      }
    }
   
    public Boolean getBooleanParameter(String name) {
      String s = getParameter(name);
      if (s != null) {
        return Boolean.parseBoolean(s);
      } else {
        return NULL_BOOLEAN;
      }
    }
   
   
   
    public boolean getBooleanParameter(String name, boolean defaultVal) {
      String s = getParameter(name);
      if (s != null) {
        try {
          return Boolean.parseBoolean(s);
        } catch (Exception e) {
          return defaultVal;
        }
      } else {
        return defaultVal;
      }
    }
       

   
        private void parseBodyIfNecessary() {
           
            if (!isBodyParsed) {
                isBodyParsed = true;
                try {
                    if (HttpUtils.isContentTypeFormUrlencoded(request) && request.hasBody()) {
                        bodyParamsMap.putAll(parseParamters(request.getNonBlockingBody().toString(), HttpUtils.getContentTypedFormUrlencodedEncodingType(request)));
                    } else {
                        if (LOG.isLoggable(Level.FINE)) {
                            LOG.fine("warning request does not contain a FORM-URLENCODED body: " + request);
                        }
                    }
                } catch (IOException ioe) {
                    throw new RuntimeException(ioe);
                }
            }
        }
       


    public String getParameter(String name) {
        parseBodyIfNecessary();
       
      if (bodyParamsMap.containsKey(name)) {
        return bodyParamsMap.get(name).get(0);
      }
     
      return getWrappedRequestHeader().getParameter(name);
    }
   
   
   
    public String[] getParameterValues(String name) {
        parseBodyIfNecessary();
       
      ArrayList<String> result = new ArrayList<String>();
     
      if (bodyParamsMap.containsKey(name)) {
        result.addAll(bodyParamsMap.get(name));
      }
     
      String[] v = getWrappedRequestHeader().getParameterValues(name);
      result.addAll(Arrays.asList(v));
     
      return result.toArray(new String[result.size()]);
    }
   
   
    public void setParameter(String parameterName, String parameterValue) {
        parseBodyIfNecessary();
       
      if (bodyParamsMap.containsKey(parameterName)) {
        throw new RuntimeException("parameter is contained in body and can not be modified");
      }
     
      getWrappedRequestHeader().setParameter(parameterName, parameterValue);
    }

   
    public Set<String> getParameterNameSet() {
        parseBodyIfNecessary();
       
      Set<String> result = new HashSet<String>();
     
      result.addAll(getWrappedRequestHeader().getParameterNameSet());
      result.addAll(bodyParamsMap.keySet());
     
      return result;
    }
   
    @SuppressWarnings("unchecked")
    public Enumeration getParameterNames() {
      return Collections.enumeration(getParameterNameSet());
    }
  }

    static final class CompletionHandlerInfo {
       
        private boolean isUnsynchronized = false;
        private boolean isOnWrittenMultithreaded = false;
        private boolean isOnExceptionMultithreaded = false;

        public CompletionHandlerInfo(IWriteCompletionHandler handler) {
           
            boolean isHandlerMultithreaded = isHandlerMultithreaded(handler.getClass(), true);
           
            isOnWrittenMultithreaded = isMethodMultithreaded(handler.getClass(), "onWritten", isHandlerMultithreaded, int.class);
            isOnExceptionMultithreaded = isMethodMultithreaded(handler.getClass(), "onException", isHandlerMultithreaded, IOException.class);
           
            isUnsynchronized = (handler instanceof IUnsynchronized);
        }

       
        public boolean isUnsynchronized() {
            return isUnsynchronized;
        }

        public boolean isOnWrittenMultithreaded() {
            return isOnWrittenMultithreaded;
        }

        public boolean isOnExceptionMutlithreaded() {
            return isOnExceptionMultithreaded;
        }
    } 
   
    /**
   * @deprecated
   */
  static interface IBufferSink {
   
    /**
     * add data
     *
     * @param buffer data
     * @throws IOException if an exception occurs
     */
    void add(ByteBuffer buffer) throws IOException;
  }
 
 
  static final class RequestHandlerInfo  {
     
      private static final Logger LOG = Logger.getLogger(RequestHandlerInfo.class.getName());
     
      private static final List<String> SYSTEM_HANDLERS = Arrays.asList(new String[] { "org.xlightweb.client.CookieHandler",
                                                                                       "org.xlightweb.client.CacheHandler",
                                                                                       "org.xlightweb.client.AutoRedirectHandler",
                                                                                       "org.xlightweb.client.ProxyHandler" });
     
      private String clazzName;
      private boolean isUnsynchronized = false;
      private boolean isRequestHandlerSynchronizedOnSession = false;
      private boolean isRequestHandlerInvokeOnMessageReceived = false;
      private boolean isRequestHandlerMultithreaded = true;

      private boolean isLifeCycle = false;
     
      private boolean isRequestTimoutHandler = false;
      private boolean isRequestTimoutHandlerMultithreaded = true;

     
     

      @SuppressWarnings("unchecked")
      RequestHandlerInfo(Class clazz) {
          if (clazz == null) {
              return;
          }
         
          clazzName = clazz.getName();         
          isLifeCycle = ILifeCycle.class.isAssignableFrom(clazz);
         

          if (IHttpRequestHandler.class.isAssignableFrom(clazz)) {
              isRequestHandlerMultithreaded = isOnRequestMultithreaded((Class<IHttpRequestHandler>) clazz);
              isRequestHandlerInvokeOnMessageReceived = isOnRequestInvokeOnMessageReceived((Class<IHttpRequestHandler>) clazz);
              isRequestHandlerSynchronizedOnSession = isOnSession((Class<IHttpRequestHandler>) clazz);
             
              if (isRequestHandlerSynchronizedOnSession && !isRequestHandlerMultithreaded) {
                  LOG.warning("request handler " + clazz.getName() + " is annotated as session scope and non-threaded. " +
                              "Session scope have to be multi-threaded. Updating execution mode");
                  isRequestHandlerMultithreaded = true;
              }
          }
         
          if (IHttpRequestTimeoutHandler.class.isAssignableFrom(clazz)) {
              isRequestTimoutHandler = true;
              isRequestTimoutHandlerMultithreaded = HttpUtils.isRequestTimeoutHandlerMultithreaded((Class<IHttpRequestTimeoutHandler>) clazz);
          }
         
          isUnsynchronized = IUnsynchronized.class.isAssignableFrom(clazz) || SYSTEM_HANDLERS.contains(clazz.getName());
         
      }


      static boolean isOnRequestMultithreaded(Class<IHttpRequestHandler> serverHandlerClass) {
          boolean isMultithreaded = HttpUtils.isHandlerMultithreaded(serverHandlerClass, (IHttpRequestHandler.DEFAULT_EXECUTION_MODE == Execution.MULTITHREADED));
          return HttpUtils.isMethodMultithreaded(serverHandlerClass, "onRequest", isMultithreaded, IHttpExchange.class);
      }

     
      static boolean isOnRequestInvokeOnMessageReceived(Class<IHttpRequestHandler> handlerClass) {
          boolean invokeOnMessageReceived = HttpUtils.isInvokeOnMessageReceived(handlerClass, (IHttpRequestHandler.DEFAULT_INVOKE_ON_MODE == InvokeOn.MESSAGE_RECEIVED));
          return HttpUtils.isInvokeOnMessageReceived(handlerClass, "onRequest", invokeOnMessageReceived, IHttpExchange.class);   
      }

     
      static boolean isOnSession(Class<IHttpRequestHandler> handlerClass) {
          boolean isSynchronizedOnSession = HttpUtils.isSynchronizedOnSession(handlerClass, (IHttpRequestHandler.DEFAULT_SYNCHRONIZED_ON_MODE == SynchronizedOn.SESSION));
          return HttpUtils.isSynchronizedOnSession(handlerClass, "onRequest", isSynchronizedOnSession, IHttpExchange.class);
      }

     
     
      public boolean isLifeCycle() {
          return isLifeCycle;
      }
     
      public boolean isRequestHandlerInvokeOnMessageReceived() {
          return  isRequestHandlerInvokeOnMessageReceived;
      }

     
      public boolean isRequestHandlerSynchronizedOnSession() {
          return isRequestHandlerSynchronizedOnSession;
      }

      public boolean isRequestHandlerMultithreaded() {
          return isRequestHandlerMultithreaded;
      }
     
      public boolean isRequestTimeoutHandler() {
          return isRequestTimoutHandler;
      }
     
      public boolean isRequestTimeoutHandlerMultithreaded() {
          return isRequestTimoutHandlerMultithreaded;
      }
     
      public boolean isUnsynchronized() {
          return isUnsynchronized;
      }
     
      @Override
      public String toString() {
          StringBuilder sb = new StringBuilder(super.toString() + " (");
          sb.append("classname=" + clazzName + " isLifeCycle=" + isLifeCycle + " " +
                    "isUnsynchronized=" + isUnsynchronized + " " +
                    "isRequestHandlerMultithreaded=" + isRequestHandlerMultithreaded + " " +
                    "isRequestHandlerInvokeOnMessageReceived=" + isRequestHandlerInvokeOnMessageReceived + " " +
                    "isRequestHandlerSynchronizedOnSession=" + isRequestHandlerSynchronizedOnSession + " " +
                    "isRequestTimoutHandler=" + isRequestTimoutHandler + " " +
                    "isRequestTimoutHandlerMultithreaded=" + isRequestTimoutHandlerMultithreaded + ")");
          return sb.toString();
      }
  }
 

}
TOP

Related Classes of org.xlightweb.HttpUtils$HttpConnectionHandlerInfo

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