Package org.emrys.webosgi.core.runtime

Source Code of org.emrys.webosgi.core.runtime.OSGiWebContainerHelper$IServletFilter

package org.emrys.webosgi.core.runtime;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponseWrapper;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.emrys.webosgi.core.handlers.CustomizeFiltersHandler;
import org.emrys.webosgi.core.handlers.CustomizeServletHandler;
import org.emrys.webosgi.core.handlers.IFwkHandlerChain;
import org.emrys.webosgi.core.handlers.IFwkRequestHandler;
import org.emrys.webosgi.core.handlers.RequestHandlerChain;
import org.emrys.webosgi.core.handlers.RequestPathAdjustHandler;
import org.emrys.webosgi.core.jeeres.AbstMultiInstUrlMapObject;
import org.emrys.webosgi.core.jeeres.ClonedExecutableServletObject;
import org.emrys.webosgi.core.jeeres.FilterDelegate;
import org.emrys.webosgi.core.jeeres.ListenerInfo;
import org.emrys.webosgi.core.jeeres.ServletDelegate;
import org.emrys.webosgi.core.jeewrappers.BundledHttpServletRequestWrapper;
import org.emrys.webosgi.core.jeewrappers.HttpSessionWrapper;
import org.emrys.webosgi.core.service.IWABServletContext;
import org.osgi.framework.Bundle;

/**
* The {@link OSGiWebContainer}'s helper class to provide some utilitary method
* and make the {@link OSGiWebContainer} clear and easy to read. This class has
* been defined as packaged scope using only. Other package shoule not use it.
*
* @author Leo Chang
*/
public class OSGiWebContainerHelper {
  private final static String REQ_V_MAPPED_SERVLET_PREFIX = "REQ_V_MAPPED_SERVLET_PREFIX";
  private final OSGiWebContainer webContainer;
  /**
   * the sorter map for SerlvetDelegate and FilterDelegate.
   */
  private final Map<Class<?>, URLMapObjectSorter<?>> sorterMap = new HashMap<Class<?>, URLMapObjectSorter<?>>();
  // Buffered global data.
  private Collection<ServletDelegate> normalServlets;
  private Collection<FilterDelegate> allFilters;
  private RequestHandlerChain requestHandlerChain;

  public OSGiWebContainerHelper(OSGiWebContainer jeeContainer) {
    this.webContainer = jeeContainer;
    this.normalServlets = new HashSet<ServletDelegate>();
    this.allFilters = new HashSet<FilterDelegate>();
  }

  /**
   * Refresh the buffered Data.
   */
  public void refresh() throws Exception {
    // Find all serlvets
    normalServlets = findURLParttensObjects(null, ServletDelegate.class,
        null);
    // collect all fitlers.
    getAllBufferedFilters(true);

    // sort normal serlvets and all fitlers and buffered them.S
    sortURLPatternsExeObjs(ServletDelegate.class, true);
    sortURLPatternsExeObjs(FilterDelegate.class, true);
  }

  /**
   * Get all buffered {@link FilterDelegate}
   *
   * @param update
   * @return
   */
  public Collection<FilterDelegate> getAllBufferedFilters(boolean update) {
    if (allFilters == null || update) {
      allFilters = findURLParttensObjects(null, FilterDelegate.class,
          new IServletFilter() {
            public boolean intrest(AbstMultiInstUrlMapObject info) {
              return true;
            }
          });
    }
    return allFilters;
  }

  /**
   * Switch request's current bundle to the filter's bundle. Because
   * {@link BundledHttpServletRequestWrapper#getSession()} should return
   * different {@link HttpSessionWrapper} according to the current bundle in
   * {@link BundledHttpServletRequestWrapper}
   *
   * @param bundle
   *            current bundle to be set into BundledHttpServletRequestWrapper
   * @see <li>{@link BundledHttpServletRequestWrapper#getSession()}</li> <li>
   *      {@link #doServletMapFilter(ServletDelegate, BundledHttpServletRequestWrapper, ServletResponse, List)}
   *      </li> <li>
   *      {@link #doUrlMapFilter(BundledHttpServletRequestWrapper, HttpServletResponseWrapper, List, boolean)}
   *      </li> <li>
   *      {@link #doServletService(BundledHttpServletRequestWrapper, HttpServletResponseWrapper)}
   *      </li>
   */
  public void switchReqBundleContext(Bundle bundle) {
    BundledHttpServletRequestWrapper req = (BundledHttpServletRequestWrapper) webContainer
        .getReqThreadVariants().get(OSGiWebContainer.THREAD_V_REQUEST);
    req.setBundle(bundle);
  }

  /**
   * @param httpReq
   * @param sortedSerlvetCopys
   *            All sorted Serlvet Copys for all URL Pattern of all Serlvet.
   * @return the serlvet copy matched the current request's path.
   * @throws IllegalAccessException
   * @throws InstantiationException
   */
  public ClonedExecutableServletObject<ServletDelegate> chooseDelegateServlet(
      BundledHttpServletRequestWrapper httpReqWrapper,
      List<ClonedExecutableServletObject<ServletDelegate>> sortedSerlvetCopys)
      throws Exception {
    String originalServletPath = httpReqWrapper.getServletPath();
    webContainer.getReqThreadVariants().put(REQ_V_MAPPED_SERVLET_PREFIX,
        null);

    ClonedExecutableServletObject<ServletDelegate> servletDelegateCopy = calculateServlet(
        originalServletPath, sortedSerlvetCopys);
    if (servletDelegateCopy == null)
      return null;

    IWABServletContext ctx = servletDelegateCopy.getOriginalObj()
        .getBundleContext();

    // switchReqBundleContext(ctx.getBundle());
    String servletPrefix = (String) webContainer.getReqThreadVariants()
        .get(REQ_V_MAPPED_SERVLET_PREFIX);

    String newServletPath = servletPrefix;
    if (servletPrefix.startsWith(ctx.getWABContextPath()))
      newServletPath = new Path(servletPrefix).removeFirstSegments(1)
          .makeAbsolute().toPortableString();

    if (newServletPath.equals("/"))
      newServletPath = null;

    httpReqWrapper.setServletPath(newServletPath);
    String newPathInfo = originalServletPath.replace(servletPrefix, "");
    // Tomcat6's behavior is to take the empty pathInfo as null.
    if (newPathInfo.length() == 0)
      newPathInfo = null;
    httpReqWrapper.setPathInfo(newPathInfo);

    return servletDelegateCopy;
  }

  /**
   * To match a givan servlet path with the all servlet copys list. If a copy
   * of one Servlet has been executed, other copy will be skiped.
   *
   * @param servletPath
   *            the current http request's serlvet path to match.
   * @param servletCopys
   *            all servlet copys for each url pattern.
   * @return
   */
  private ClonedExecutableServletObject<ServletDelegate> calculateServlet(
      String servletPath,
      List<ClonedExecutableServletObject<ServletDelegate>> servletCopys) {
    for (ClonedExecutableServletObject<ServletDelegate> clonedUrlPatternInst : servletCopys) {
      // if one copy of a serlvet has been executed in this request
      // thread, skip it.
      // But this is not the fact for servlet, because serlvet will only
      // be called once in request thread, not like flter.
      /*
       * if (clonedUrlPatternInst.isExecuted()) continue;
       */

      String expr = clonedUrlPatternInst.getOriginalObj()
          .getURLPatterns()[clonedUrlPatternInst.getId()];
      StringBuffer sb = new StringBuffer();

      if (checkPathInfoWithExpr(servletPath, expr, sb, false)) {
        String servletPrefix = sb.toString();
        webContainer.getReqThreadVariants().put(
            REQ_V_MAPPED_SERVLET_PREFIX, servletPrefix);
        return clonedUrlPatternInst;
      }
    }
    return null;
  }

  /**
   * For Servlet and Filter, they may has mutiple URL pattern mapping. To sort
   * the execute order by their url patterns, we cloned many object of the
   * servlet or filter according their url patterns. These cloned object will
   * be sort into a list to be execute, but a servlet or filter can only be
   * executed once. This method will sort all cloned object for servlet or
   * filter into a list. The result will be buffered only update need.
   *
   * @param <T>
   *            the type to sort.
   * @param type
   *            the type to sort.
   * @param forceUpdate
   *            whether to update the buffer.
   * @return the sorted cloned object for servlet or filter.
   */
  public <T extends AbstMultiInstUrlMapObject> List<ClonedExecutableServletObject<T>> sortURLPatternsExeObjs(
      Class<T> type, boolean forceUpdate) {
    URLMapObjectSorter<?> sorter = sorterMap.get(type);
    if (!sorterMap.containsKey(type)) {
      sorter = new URLMapObjectSorter<T>();
      sorterMap.put(type, sorter);
    }

    Collection<? extends AbstMultiInstUrlMapObject> multiInstObjs = null;
    if (type.equals(ServletDelegate.class))
      multiInstObjs = normalServlets;
    if (type.equals(FilterDelegate.class))
      multiInstObjs = allFilters;

    // Filter not need to sort by the url pattern, just according to the map
    // element define order.
    List<?> result = sorter.sort(multiInstObjs, !FilterDelegate.class
        .isAssignableFrom(type), forceUpdate);

    // Leo hates the java template class and method programming!!!
    List<ClonedExecutableServletObject<T>> tmpList = new ArrayList<ClonedExecutableServletObject<T>>();
    tmpList
        .addAll((Collection<? extends ClonedExecutableServletObject<T>>) result);
    return tmpList;
  }

  interface IServletFilter {
    boolean intrest(AbstMultiInstUrlMapObject info);
  }

  /**
   * Find all ServletDelegate or FilterDelegate from given Serlvet Context, if
   * no context given, all contexts will be searched.
   *
   * @param ctx
   *            the servlet context to search from.
   * @param filter
   *            the filter to decide which serlvet is interesting.
   * @return the desiring servlet.
   */
  public <T extends AbstMultiInstUrlMapObject> List<T> findURLParttensObjects(
      ServletContext ctx, Class<T> type, IServletFilter filter) {
    List<T> result = new ArrayList<T>();
    if (ctx instanceof IWABServletContext) {
      IWABServletContext octx = (IWABServletContext) ctx;
      Collection<? extends AbstMultiInstUrlMapObject> servlets = null;
      if (type.equals(ServletDelegate.class))
        servlets = octx.getServletsInfo();
      if (type.equals(FilterDelegate.class))
        servlets = octx.getFilters();
      for (AbstMultiInstUrlMapObject info : servlets) {
        if (filter == null || filter.intrest(info))
          result.add((T) info);
      }
    } else {
      // Collect all ServletContextListener here.
      Set<IWABServletContext> set = webContainer
          .getAllBundledServletContext();
      synchronized (set) {
        for (IWABServletContext c : set) {
          result.addAll(findURLParttensObjects(c, type, filter));
        }
      }
    }
    return result;
  }

  /**
   * Find all servlet listener of give type from the {@link WabServletContext}
   * . If null servlet context given, all servlet contexts' will be find and
   * returned.
   *
   * @param ctx
   *            the servlet context from which to search the listeners.
   * @return the listeners of the given type
   */
  public <T extends EventListener> List<T> findListeners(ServletContext ctx,
      Class<T> type) {
    List<T> result = new ArrayList<T>();
    if (ctx instanceof IWABServletContext) {
      IWABServletContext octx = (IWABServletContext) ctx;
      Collection<ListenerInfo> listeners = octx.getListeners();
      ListenerInfo[] listenerArray = listeners
          .toArray(new ListenerInfo[listeners.size()]);
      for (ListenerInfo info : listenerArray) {
        try {
          EventListener l = info.getListener();
          if (type.isInstance(l)) {
            result.add((T) l);
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    } else {
      // Collect all ServletContextListener here.
      Set<IWABServletContext> set = webContainer
          .getAllBundledServletContext();
      for (IWABServletContext c : set) {
        result.addAll(findListeners(c, type));
      }
    }
    return result;
  }

  /**
   * Check if the given filter need to be invoked according the given servlet
   * request. This method will match the current path of request to the map
   * url pattern of the filter.
   *
   * @param request
   *            the current servlet request.
   * @param String
   *            the filter's URL Pattern.
   * @return the matching result.
   */
  public boolean checkNeedFilter(HttpServletRequest request,
      String filterURLPattern) {
    String servletPath = request.getServletPath();
    if (checkPathInfoWithExpr(servletPath, filterURLPattern, null, false)) {
      return true;
    }
    return false;
  }

  /**
   * Check if the given request path matched the RegExp pattern, and the
   * matched content will be returned in matchResult argument if it isn't
   * null.
   *
   * @param pathInfo
   *            the path info to check with the regular express.
   * @param exprs
   *            the regular express string used to match the path info .
   * @param matchResult
   *            can be null. To get the matched path content. this content may
   *            be regard as the servet path.
   * @param mutipleExpr
   *            whether the exprs string is mutiple expreses splited by
   *            ';'(defined in {@link FilterDelegate.MULTI_MAP_SEG_SEPERATOR}
   *            ).
   * @return whether the matched result is true.
   */
  public boolean checkPathInfoWithExpr(String pathInfo, String exprs,
      StringBuffer matchResult, boolean mutipleExpr) {
    String[] exprsArray = null;
    if (mutipleExpr)
      exprsArray = exprs.split(FilterDelegate.MULTI_MAP_SEG_SEPERATOR);
    else
      exprsArray = new String[] { exprs };

    for (String expr : exprsArray) {
      // Need to check out if expr has some invalid chars like # @ ^,etc?
      // No, it's not affect any thing and not our responsibility.

      // if "/*" map, return true immediately, and let the whole path info
      // as matched servlet path.
      if (expr.equals("/*")) {
        /*
         * if (matchResult != null) matchResult.append(pathInfo);
         */
        return true;
      }

      // Developer may append a unnecessary '/' at the end of exact path
      // map, remove it.
      if (expr.endsWith("/"))
        expr = expr.substring(0, expr.length() - 1);

      int lastSegIndex = expr.lastIndexOf('/');
      String preSegs = lastSegIndex > 0 ? expr.substring(0, lastSegIndex)
          : "";
      // If the prefix segments contains '*' or ".", then the express is
      // invalid, skip it.
      if (preSegs.indexOf('*') != -1 || preSegs.indexOf('.') != -1)
        continue;

      String lastSeg = lastSegIndex > -1 ? expr.substring(lastSegIndex)
          : "/" + expr;

      // 3 types of servlet or filter url parttern.

      // Type 1: extension map like *.jsp, *.do
      // Regard the /xxx/*.jsp or /*.jsp as extension map, not wildcard
      // path map.
      boolean isExtMap = lastSeg.startsWith("/*.")
          && lastSeg.length() > 3;

      // Type 2: exact path map like /abc, /abc/ef, /abc/ef.g (without
      // containing '*').
      boolean isExactPathMap = lastSeg.indexOf('*') == -1;

      // Type 3: wildcard path map like /xxx/*, /*
      boolean isWildcardPathMap = lastSeg.equals("/*");

      // Remove / at last index.
      if (pathInfo.endsWith("/"))
        pathInfo = pathInfo.substring(0, pathInfo.length() - 1);

      // Build the new pattern express according to different map type.
      String newExpr = preSegs;
      if (isExtMap) {
        // Replace RegExp in last segment if any.
        lastSeg = lastSeg.replaceAll("\\.", "\\\\.");
        lastSeg = lastSeg.replaceAll("\\*", "\\\\S*");
        // match the start of given path and group an empty servlet
        // path.
        newExpr = "^(" + newExpr + lastSeg + ")" + "$";
      } else if (isExactPathMap) {
        // match the start and the end of given path
        newExpr = "^(" + newExpr + lastSeg + ")$";
      } else if (isWildcardPathMap) {
        lastSeg = lastSeg.replaceAll("\\*", "\\\\S*");
        newExpr = "^(" + newExpr + ")" + lastSeg;
      } else
        continue;

      Pattern p = Pattern.compile(newExpr);
      Matcher m = p.matcher(pathInfo);
      if (m.find()) {
        String s = m.group(1);
        // Get the grouped matched content as servlet path if the
        // invoking method need(then matchResult!=null).
        if (matchResult != null)
          matchResult.append(s);
        return true;
      }
    }

    return false;
  }

  /**
   * Check out whether a given web resource path is a extant directory. This
   * method mainly be used to redirect the client request to a new path
   * appended with "/".
   *
   * @param bundleServletCtx
   *            the current servlet context root.
   * @param path
   *            resource path relative to the current servlet context root.
   * @return true if this path indicating a extant resource directory.
   */
  public boolean checkIsResourceDir(IWABServletContext bundleServletCtx,
      IPath path) {
    // FIXME: need to buffer result to optimize performance.
    try {
      String pathStr = path.toPortableString();
      URL url = bundleServletCtx.getResource(pathStr);
      // If some web bundle without solid resources root, it may return
      // null for the root resource.
      if ((pathStr.length() == 0 || pathStr.equals("/")) && url == null)
        return true;

      // Judge the url is local directory of web resource.
      if (url != null && url.toExternalForm().endsWith("/"))
        return true;
    } catch (MalformedURLException e) {
      // e.printStackTrace();
    }
    return false;
  }

  /**
   * Try to find a welcome page for a given directory path if configured in
   * web.xml according to the Java EE standard.
   *
   * @param bundleServletCtx
   * @param path
   *            the current resource direcory path.
   * @return the welcome page name.
   */
  public String tryRedirectToWelcomPage(IWABServletContext bundleServletCtx,
      IPath path) {
    // Check if this request path if a directory need to try to redirect to
    // welcome page.
    if (!checkIsResourceDir(bundleServletCtx, path))
      return null;

    // FIXME: need to buffer result to optimize performance.
    List<String> welcomePages = bundleServletCtx.getWelcomePages();
    if (welcomePages == null || welcomePages.size() == 0)
      return null;
    String resPath = path.toPortableString();
    if (resPath.length() == 0)
      resPath = "/";
    Set subResPaths = bundleServletCtx.getResourcePaths(resPath);
    if (subResPaths != null) {
      for (String welcomePage : welcomePages) {
        for (Iterator it = subResPaths.iterator(); it.hasNext();) {
          String p = (String) it.next();
          // remove the prefix "/" form path name to match welcome
          // page
          // setting.
          if (welcomePage.equals(new Path(p).removeFirstSegments(
              path.segmentCount()).makeRelative()
              .toPortableString()))
            return welcomePage;
        }
      }
    }
    // If not found any existant welcome page resource, modify the current
    // servlet path as the first specified welcome page.
    return welcomePages.get(0);
  }

  public IFwkHandlerChain constructHandlerChain(boolean refresh) {
    if (refresh)
      requestHandlerChain = null;

    if (requestHandlerChain == null) {
      synchronized (this) {
        if (requestHandlerChain == null) {
          RequestHandlerChain chain = new RequestHandlerChain();
          chain
              .addHandler(new RequestPathAdjustHandler(
                  webContainer));
          chain.addHandler(new CustomizeFiltersHandler(webContainer));
          chain.addHandler(new CustomizeServletHandler(webContainer));

          Set<IFwkRequestHandler> regiteredReqHandlers = webContainer.reqHandlers;
          if (regiteredReqHandlers != null)
            chain.addHandlers(regiteredReqHandlers);
          requestHandlerChain = chain;
        }
      }
    }
    return requestHandlerChain;
  }

  public boolean forwardToErrPage(ServletRequest req, ServletResponse res,
      Throwable t) {
    return false;
  }
}
TOP

Related Classes of org.emrys.webosgi.core.runtime.OSGiWebContainerHelper$IServletFilter

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.