Package org.xlightweb

Source Code of org.xlightweb.Context$BodyCompleteListener

/*
*  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.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xlightweb.HttpUtils.RequestHandlerInfo;
import org.xsocket.Execution;
import org.xsocket.ILifeCycle;




/**
* By using the <ocde>Context</code> specific request handlers can be assigned
* via a url-pattern to a set of URLs (Routing). The url-pattern syntax is equals
* to the Servlet API. A request handler will be assigned to a url by using a Context. <br>
*
* Typically, this approach is required, if static resources have to be
* supported as well as dynamic resources. See example:
*
* <pre>
*  Context ctx = new Context("");
*  ctx.addHandler("/site/*", new FileServiceRequestHandler(basePath, true));  
*  ctx.addHandler("/rpc/*", new MyBusinessHandler());
*  ctx.addHandler(new MappingAnnotatedHandler()); 
*  Server server = new HttpServer(8080, ctx);
*  server.start();
* </pre> 
*
* @author grro@xlightweb.org
*/
@org.xsocket.Execution(Execution.MULTITHREADED)
public class Context implements IHttpRequestHandler, IHttpRequestTimeoutHandler, ILifeCycle, Cloneable  {

  private static final Logger LOG = Logger.getLogger(Context.class.getName());
 
 
  private final List<IHttpRequestHandler> handlers = new ArrayList<IHttpRequestHandler>();
 
  private final List<ILifeCycle> lifeCycleChain = new ArrayList<ILifeCycle>();
 
  private final List<IHolder> holders = new ArrayList<IHolder>();
  private final HolderCache holderCache = new HolderCache(40);
 
  private boolean isOnRequestTimeoutPathMultithreaded = false;
  private final List<IHttpRequestTimeoutHandler> requestTimeoutHandlerChain = new ArrayList<IHttpRequestTimeoutHandler>();

  private final String contextPath;
 
 
 
  /**
   * constructor
   *
   * @param contextPath   the context path
   */
  public Context(String contextPath) {
    this.contextPath = contextPath;
  }

 
  /**
   * constructor
   *
   * @param contextPath  the context path
   * @param handlers     handler map
   */
  public Context(String contextPath, Map<String, IHttpRequestHandler> handlers) {
    this.contextPath = contextPath;
   
    for (Entry<String, IHttpRequestHandler> entry : handlers.entrySet()) {
      addHandler(entry.getKey(), entry.getValue());
    }
  }

 
  /**
   * constructor
   *
   * @param parentContext   the parent context
   * @param contextPath     the context path
   */
  public Context(Context parentContext, String contextPath) {
    this.contextPath = contextPath;
   
    parentContext.addContext(this);
  }
 
 
  private void addContext(Context ctx) {
    holders.add(new ContextHolder(ctx));
    sortHolderList();
  }
 
 
  /**
   * adds an annotated handler to the current context
   *
   * @param requestHandler   the annotated handler (supported: {@link IHttpRequestHandler}, {@link ILifeCycle})
   */
  public void addHandler(IHttpRequestHandler requestHandler) {
    String[] mappings = HttpUtils.retrieveMappings(requestHandler);
    if (mappings == null) {
      throw new RuntimeException("handler mapping is not annotated (hint: use @Mapping()");
    }
   
    for (String mapping : mappings) {
      addHandler(mapping, requestHandler);
    }
  }
 
 
 
 
  /**
   * adds a handler to the current context
   *
   * @param pattern          the pattern
   * @param requestHandler   the handler (supported: {@link IHttpRequestHandler}, {@link ILifeCycle})
   */
  public void addHandler(String pattern, IHttpRequestHandler requestHandler) {
   
      if (!HttpUtils.isConnectHandlerWarningIsSuppressed() && (requestHandler instanceof IHttpConnectHandler)) {
          LOG.warning("only IHttpRequestHandler is supported. The onConnect(...) method will not be called. (suppress this warning by setting system property org.xlightweb.httpConnectHandler.suppresswarning=true)");
      }
     
    RequestHandlerHolder holder = new RequestHandlerHolder(pattern, requestHandler, HttpUtils.getRequestHandlerInfo(requestHandler));
   
    for (IHolder hld : holders) {
      if (hld.getPattern().equalsIgnoreCase(holder.getPattern())) {
        holders.remove(hld);
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("handler already exists for pattern " + pattern + " Replacing existing holder");
        }
        break;
      }
    }
   
    holders.add(holder);
    sortHolderList();
   
    handlers.add(requestHandler);   
    computePath();
  }
 
 
  private void computePath() {
    holderCache.clear();   
    lifeCycleChain.clear();
   
    requestTimeoutHandlerChain.clear();
    isOnRequestTimeoutPathMultithreaded = false;
   
   
    for (IHttpRequestHandler handler : handlers) {

      if (ILifeCycle.class.isAssignableFrom(handler.getClass())) {
        lifeCycleChain.add((ILifeCycle) handler);
      }
     
      RequestHandlerInfo requestHandlerInfo = HttpUtils.getRequestHandlerInfo(handler);
      if (requestHandlerInfo.isRequestTimeoutHandler()) {
        requestTimeoutHandlerChain.add((IHttpRequestTimeoutHandler) handler);
        isOnRequestTimeoutPathMultithreaded = isOnRequestTimeoutPathMultithreaded || requestHandlerInfo.isRequestTimeoutHandlerMultithreaded();
      }
    }   
  }
  
 
 
  private void sortHolderList() {
    Comparator<IHolder> comparator = new Comparator<IHolder>() {

      public int compare(IHolder o1, IHolder o2) {
        return (0 - o1.getPattern().compareTo(o2.getPattern()));
      }
    };

    Collections.sort(holders, comparator);
  }
 

  /**
   * {@inheritDoc}
   */
  public void onInit() {
    for (IHolder holder : holders) {
      holder.onInit();
    }
  }

 
  /**
   * {@inheritDoc}
   */
  public void onDestroy() throws IOException {
    for (IHolder holder : holders) {
      holder.onDestroy();
    }   
  }
 
 
  /**
   * returns the context path
   *
   * @return the context path
   */
  public String getContextPath() {
    return contextPath;
  }

 
  /**
   * returns the mappings
   *
   * @return the mappings
   */
  List<String> getMapping() {
    List<String> result = new ArrayList<String>();
   
    for (IHolder holder : holders) {
      result.add("[" + holder.getPattern() + "] -> " + holder.getTarget());
    }
   
    Collections.sort(result);
   
    return result;
  }
 
 
  /**
   * returns the handlers
   *
   * @return the handlers
   */
  public List<IHttpRequestHandler> getHandlers() {
    List<IHttpRequestHandler> result = new ArrayList<IHttpRequestHandler>();
   
    for (IHolder holder : holders) {
      result.add(holder.getTarget());
    }
   
    return result;
  }
 
 
  /**
   * returns the current size
   *
   * @return the current site
   */
  public int size() {
    return holders.size();
  }
 

 
 
 
  /**
   * {@inheritDoc}
   */
  public void onRequest(IHttpExchange exchange) throws IOException {
   
    String path = exchange.getRequest().getRequestURI();
   
    if (path.startsWith(contextPath)) {
      path = path.substring(contextPath.length(), path.length());
     
      onRequest(path, exchange, contextPath);
     
    } else {
      sendNotFoundError(exchange);
    }
  } 
 



  private void onRequest(String path, IHttpExchange exchange, String totalContextPath) throws IOException {
   
    if (holderCache.containsKey(path)){
      holderCache.get(path).onRequest(path, exchange, totalContextPath);
      return;
    }
     
    for (IHolder holder : holders) {
      if (holder.match(path)) {
        holderCache.put(path, holder);
        holder.onRequest(path, exchange, totalContextPath);
        return;
      }
    }
   
    sendNotFoundError(exchange);
 
 
  private void sendNotFoundError(IHttpExchange exchange) {
    if (HttpUtils.isShowDetailedError()) {
     
      StringBuilder sb = new StringBuilder("Not found\r\n\r\nsupported context:\r\n");
      for (IHolder holder : holders) {
        sb.append("<a href=\"" + holder.getPattern() + "\">" + holder.getPattern() + "</a><br>");
      }

      exchange.sendError(404, sb.toString());
    } else {
      exchange.sendError(404);
    }
  }
 
   
  /**
   * {@inheritDoc}
   */
  public boolean onRequestTimeout(IHttpConnection connection) throws IOException {

    if (requestTimeoutHandlerChain.isEmpty()) {
      if (LOG.isLoggable(Level.FINER)) {
        LOG.finer("no request timeout handler set. ignore callback");
      }
     
      return false;
    }
   
        for (IHttpRequestTimeoutHandler requestTimeoutHandler : requestTimeoutHandlerChain) {
            boolean result = requestTimeoutHandler.onRequestTimeout(connection);
            if (result == true) {
                return true;
            }
        }  
       
        return false;
  }
 

 
 
   
 
 
  /**
   * {@inheritDoc}
   */
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Context copy = (Context) super.clone();
    return copy;
  }
 

  /**
   * copies the context
   * @return the copy
   */
  Context copy() {
    try {
      return (Context) clone();
    } catch (CloneNotSupportedException cnse) {
      throw new RuntimeException(cnse.toString());
    }
  }
 
 
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder("\"" + contextPath + "\"->{");
    for (IHolder holder  : holders) {
      sb.append(holder + " ");
    }
    return sb.toString().trim() + "}";
  }
 
 
  private interface IHolder {
       
    public void onInit();
   
    public void onDestroy() throws IOException;
   
    public void onRequest(String path, IHttpExchange exchange, String totalContextPath) throws IOException;
   
    public boolean match(String requestedRessource);
   
    public String getPattern();
   
    public IHttpRequestHandler getTarget();
  }
 
 
  private static final class ContextHolder implements IHolder {

    private Context context = null;
   
    ContextHolder(Context context) {
      this.context = context.copy();
    }
   
    public void onInit() {
      context.onInit();
    }
   
    public void onDestroy() throws IOException {
      context.onDestroy();
    }
   
   
    @Execution(Execution.NONTHREADED)
    public void onRequest(String path, IHttpExchange exchange, String totalContextPath) throws IOException {
      path = path.substring(context.contextPath.length(), path.length());
      context.onRequest(path, exchange, totalContextPath + context.contextPath);
    }
   
    public boolean match(String requestedRessource) {
      return requestedRessource.startsWith(context.contextPath) || requestedRessource.equals(context.contextPath);
    }
   
    public String getPattern() {
      return context.contextPath;
    }
   
    public IHttpRequestHandler getTarget() {
      return context;
    }
       
    @Override
    public String toString() {
      return context.toString();
    }
  } 

 
 
 
  private static final class RequestHandlerHolder implements IHolder {

    private String path = null;
    private String pattern = null;
    private boolean isWildcardPath = false;
    private boolean isWildcardPathExt = false;
   
    private IHttpRequestHandler handler = null;
    private RequestHandlerInfo handlerInfo = null;

      
    RequestHandlerHolder(String pattern, IHttpRequestHandler handler, RequestHandlerInfo handlerInfo) {
      this.handler = handler;
      this.handlerInfo = handlerInfo;
      this.pattern = pattern;
      this.path = pattern;
     
      if (pattern.endsWith("/*")) {
        isWildcardPath = true;
        path = pattern.substring(0, pattern.indexOf("/*"));
         
      } else if (pattern.startsWith("*")) {
        path = pattern.substring(1, pattern.length());
        isWildcardPathExt = true;

      } else {
        isWildcardPath = false;
        path = pattern;
      }
    }
   
   
    public void onInit() {
      if (handlerInfo.isLifeCycle()) {
        ((ILifeCycle) handler).onInit();
      }
    }
   
    public void onDestroy() throws IOException {
      if (handlerInfo.isLifeCycle()) {
        ((ILifeCycle) handler).onDestroy();
      }
    }
   
   
    @Execution(Execution.NONTHREADED)
    public void onRequest(String reqPath, IHttpExchange exchange, String totalContextPath) throws IOException {
     
      IHttpRequest request = exchange.getRequest();
      if (request == null) {
        exchange.destroy();
        return;
      }
     
      request.setContextPath(totalContextPath);
      request.setRequestHandlerPath(path);

     
      // mode MESSAGE_RECEIVED     
      if (handlerInfo.isRequestHandlerInvokeOnMessageReceived()) {
        if (request.hasBody()) {
           
          IBodyCompleteListener completeListener = new BodyCompleteListener(exchange, this);
          request.getNonBlockingBody().addCompleteListener(completeListener);

        } else {
          performOnRequest(exchange);
        }
       
      // mode HEADER_RECEIVED     
      } else {
        performOnRequest(exchange);
      }
    }
   
   
    private void performOnRequest(IHttpExchange exchange)  {
        try {
            handler.onRequest(exchange);
        } catch (Exception e) {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("error occured by calling on request " + handler + " " + e.toString());
            }
            throw new RuntimeException(e);
        }
    }
   
   
    public boolean match(String requestedRessource) {
       
      if (isWildcardPath) {
        return requestedRessource.startsWith(path) || requestedRessource.equals(path);
         
      } else if (isWildcardPathExt) {
        return requestedRessource.endsWith(path);
         
      } else {
        return requestedRessource.equals(path);
      }
    }
   
   
    public String getPattern() {
      return pattern;
    }
   
    public IHttpRequestHandler getTarget() {
      return handler;
    }
   
    @Override
    public String toString() {
      return "\"" + pattern + "\"->" + handler.getClass().getSimpleName();
    }
  }
 
 
  private static final class BodyCompleteListener implements IBodyCompleteListener {
   
    private IHttpExchange exchange = null;
    private RequestHandlerHolder requestHandlerHolder = null;
   
    public BodyCompleteListener(IHttpExchange exchange, RequestHandlerHolder requestHandlerHolder) {
      this.exchange = exchange;
      this.requestHandlerHolder = requestHandlerHolder;
    }
   
    @Execution(Execution.NONTHREADED)
    public void onComplete() {
      requestHandlerHolder.performOnRequest(exchange);
    }
  }
 
 
  private static final class HolderCache extends LinkedHashMap<String, IHolder> {
       
        private static final long serialVersionUID = 4513864504007457500L;
       
        private int maxSize = 0;
       
        HolderCache(int maxSize) {
            this.maxSize = maxSize;
        }
       
        @Override
        protected boolean removeEldestEntry(Entry<String, IHolder> eldest) {
            return size() > maxSize;
        }  
    }
}
TOP

Related Classes of org.xlightweb.Context$BodyCompleteListener

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.