Package ch.entwine.weblounge.dispatcher.impl

Source Code of ch.entwine.weblounge.dispatcher.impl.WebloungeDispatcherServlet

/*
* Weblounge: Web Content Management System Copyright (c) 2009 The Weblounge
* Team http://entwinemedia.com/weblounge
*
* This program 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 of the License, or (at your option) any
* later version.
*
* This program 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 program; if not, write to the Free Software Foundation Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package ch.entwine.weblounge.dispatcher.impl;

import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;

import ch.entwine.weblounge.cache.CacheService;
import ch.entwine.weblounge.common.impl.request.RequestUtils;
import ch.entwine.weblounge.common.impl.request.WebloungeRequestImpl;
import ch.entwine.weblounge.common.impl.request.WebloungeResponseImpl;
import ch.entwine.weblounge.common.request.RequestListener;
import ch.entwine.weblounge.common.request.ResponseCache;
import ch.entwine.weblounge.common.request.WebloungeRequest;
import ch.entwine.weblounge.common.request.WebloungeResponse;
import ch.entwine.weblounge.common.security.SecurityService;
import ch.entwine.weblounge.common.site.Environment;
import ch.entwine.weblounge.common.site.Site;
import ch.entwine.weblounge.dispatcher.DispatchListener;
import ch.entwine.weblounge.dispatcher.RequestHandler;
import ch.entwine.weblounge.dispatcher.SiteDispatcherService;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* This is the main dispatcher for weblounge, every request starts and ends
* here. Using the <code>HttpServiceTracker</code>, the servlet is registered
* with an instance of the OSGi web service.
* <p>
* The servlet is also where you enable and disable response caching by calling
* <code>setResponseCache()</code> with the appropriate implementation
* reference.
*/
public final class WebloungeDispatcherServlet extends HttpServlet {

  /** Serial version uid */
  private static final long serialVersionUID = 8939686825567275614L;

  /** Logger */
  private static final Logger logger = LoggerFactory.getLogger(WebloungeDispatcherServlet.class);

  /** Value of the X-Powered-By header */
  private static final String POWERED_BY = "Weblounge Content Management System";

  /** Response default encoding */
  private static final String DEFAULT_RESPONSE_ENCODING = "UTF-8";

  /** The environment */
  private Environment environment = Environment.Production;

  /** The sites that are online */
  private transient SiteDispatcherService sites = null;

  /** The security service */
  private SecurityService securityService = null;

  /** List of request listeners */
  private List<RequestListener> requestListeners = null;

  /** List of dispatcher listeners */
  private List<DispatchListener> dispatcher = null;

  /** List of dispatcher listeners */
  private Set<RequestHandler> requestHandler = null;

  /** List with well known urls and files */
  private static List<String> wellknownFiles = new ArrayList<String>();

  /** List of offline sites that have already issued a warning once */
  private final Set<String> missingRepositoryWarnings = new TreeSet<String>();

  /** List of missing sites that have already issued a warning once */
  private final Set<String> missingSiteWarnings = new TreeSet<String>();

  /** The response caches */
  private Map<String, ResponseCache> caches = null;

  /** Name of this Weblounge instance */
  private String instanceName = null;

  static {
    wellknownFiles.add("/favicon.ico");
    wellknownFiles.add("/robots.txt");
  }

  /**
   * Creates a new instance of the weblounge dispatcher servlet.
   *
   * @param environment
   *          the environment
   */
  WebloungeDispatcherServlet(Environment environment) {
    this.environment = environment;
    requestListeners = new CopyOnWriteArrayList<RequestListener>();
    dispatcher = new CopyOnWriteArrayList<DispatchListener>();
    requestHandler = new TreeSet<RequestHandler>(new Comparator<RequestHandler>() {
      public int compare(RequestHandler handler1, RequestHandler handler2) {
        int compare = Integer.valueOf(handler2.getPriority()).compareTo(Integer.valueOf(handler1.getPriority()));
        // FIXME if 0 is returned the request handler will not be added?!
        if (compare == 0)
          return 1;
        return compare;
      }
    });
    caches = new HashMap<String, ResponseCache>();
  }

  /**
   * Sets the name of this Weblounge instance or <code>null</code> if no
   * instance name was configured.
   *
   * @param instanceName
   *          name of the instance
   */
  public void setName(String instanceName) {
    this.instanceName = instanceName;
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    super.doGet(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doDelete(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    super.doDelete(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doHead(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doHead(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    super.doHead(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doOptions(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doOptions(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    super.doOptions(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    super.doPost(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doPut(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    super.doPut(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#doTrace(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void doTrace(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    super.doTrace(request, response);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest)
   */
  @Override
  protected long getLastModified(HttpServletRequest req) {
    return super.getLastModified(req);
  }

  /**
   * {@inheritDoc}
   *
   * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse)
   */
  @Override
  protected void service(HttpServletRequest httpRequest,
      HttpServletResponse httpResponse) throws ServletException, IOException {

    // Return the instance name if available
    if (instanceName != null) {
      httpResponse.addHeader("X-Weblounge-Instance", instanceName);
    }

    if (sites == null) {
      httpResponse.sendError(SC_SERVICE_UNAVAILABLE);
      return;
    }

    logger.debug("Serving {}", httpRequest.getRequestURI());

    // Get the site dispatcher
    Site site = securityService.getSite();
    if (site == null) {
      site = getSiteByRequest(httpRequest);
      securityService.setSite(site);
    }

    boolean isSpecialRequest = StringUtils.isNotBlank(httpRequest.getHeader("X-Weblounge-Special"));

    // See if a site dispatcher was found, and if so, if it's enabled
    if (site == null) {
      String serverName = httpRequest.getScheme() + "://" + httpRequest.getServerName();
      if (httpRequest.getServerPort() != 80)
        serverName += ":" + httpRequest.getServerPort();
      if (!wellknownFiles.contains(httpRequest.getRequestURI()) && !missingSiteWarnings.contains(serverName)) {
        missingSiteWarnings.add(serverName);
        logger.warn("No site found to handle {}", serverName);
      }
      httpResponse.sendError(SC_NOT_FOUND);
      return;
    } else if (!site.isOnline() && !isSpecialRequest) {
      if (site.getContentRepository() == null) {
        if (!missingRepositoryWarnings.contains(site.getIdentifier())) {
          logger.warn("No content repository connected to site '{}'", site);
          missingRepositoryWarnings.add(site.getIdentifier());
        } else {
          logger.debug("No content repository connected to site '{}'", site);
        }
      } else {
        logger.debug("Ignoring request for disabled site '{}'", site);
      }
      httpResponse.sendError(SC_SERVICE_UNAVAILABLE);
      return;
    }

    // Make sure the response is buffered
    httpResponse = new BufferedHttpServletResponse(httpResponse);

    // Get the servlet that is responsible for the site's content
    Servlet siteServlet = sites.getSiteServlet(site);

    // Get the response cache, if available
    ResponseCache cache = caches.get(site.getIdentifier());

    // Wrap for caching
    if (cache != null) {
      httpResponse = cache.createCacheableResponse(httpRequest, httpResponse);
    }

    // Wrap request and response
    WebloungeRequestImpl request = new WebloungeRequestImpl(httpRequest, siteServlet, environment);
    WebloungeResponseImpl response = new WebloungeResponseImpl(httpResponse);

    // Configure request and response objects
    request.init(site);
    response.setRequest(request);
    response.setResponseCache(cache);
    response.setCharacterEncoding(DEFAULT_RESPONSE_ENCODING);
    response.setHeader("X-Powered-By", POWERED_BY);
    response.setDateHeader("Date", Calendar.getInstance().getTimeInMillis());

    // Notify listeners about starting request
    fireRequestStarted(request, response, site);

    boolean requestServed = false;

    // Ask the registered request handler if they are willing to handle
    // the request.
    try {
      securityService.setSite(site);
      request.setUser(securityService.getUser());
      for (RequestHandler handler : requestHandler) {
        try {
          logger.trace("Asking {} to serve {}", handler, request);
          if (handler.service(request, response)) {
            requestServed = true;
            logger.debug("{} served request {}", handler, request);
            if (response.hasError()) {
              logger.debug("Request processing failed on {}", request);
              fireRequestFailed(request, response, site);
            } else {
              fireRequestDelivered(request, response, site);
            }
            return;
          }
        } catch (Throwable t) {
          response.invalidate();
          String params = RequestUtils.dumpParameters(request);
          if (t.getCause() != null) {
            t = t.getCause();
          }
          logger.error("Request handler '{}' failed to handle {} {}", new Object[] {
              handler,
              request,
              params });
          logger.error(t.getMessage(), t);
          DispatchUtils.sendInternalError(t.getMessage(), request, response);
          break;
        }
      }
    } finally {
      securityService.setSite(null);
      if (requestServed) {
        response.endResponse();
        response.flushBuffer();
        logger.debug("Finished processing of {}", httpRequest.getRequestURI());
      } else {
        logger.debug("No handler found for {}", request);
        DispatchUtils.sendNotFound(request, response);
        if (cache != null)
          cache.invalidate(response);
        fireRequestFailed(request, response, site);
      }
    }
  }

  /**
   * Returns the site that is being targeted by <code>request</code>.
   *
   * @param request
   *          the http request
   * @return the target site or <code>null</code>
   */
  private Site getSiteByRequest(HttpServletRequest request) {
    if (sites == null)
      return null;
    Site site = sites.findSiteByRequest(request);
    return site;
  }

  /**
   * Adds <code>listener</code> to the list of request listeners if it has not
   * already been registered. The listener is called a few times during the life
   * cycle of a request.
   *
   * @param listener
   *          the lister
   */
  void addRequestListener(RequestListener listener) {
    if (!requestListeners.contains(listener)) {
      requestListeners.add(listener);
    }
  }

  /**
   * Removes the listener from the list of request listeners.
   *
   * @param listener
   *          the listener to remove
   */
  void removeRequestListener(RequestListener listener) {
    requestListeners.remove(listener);
  }

  /**
   * Adds <code>listener</code> to the list of dispatch listeners if it has not
   * already been registered. The listener is called every time the current
   * request is internally being included or forwarded using the
   * <code>include</code> or <code>forward</code> method of this class.
   *
   * @param listener
   *          the lister
   */
  void addDispatchListener(DispatchListener listener) {
    if (!dispatcher.contains(listener)) {
      dispatcher.add(listener);
    }
  }

  /**
   * Removes the listener from the list of request listeners.
   *
   * @param listener
   *          the listener to remove
   */
  void removeDispatchListener(DispatchListener listener) {
    dispatcher.remove(listener);
  }

  /**
   * Adds <code>handler</code> to the list of request handler if it has not
   * already been registered. The handler is called every time a request enters
   * the system.
   *
   * @param handler
   *          the request handler
   */
  void addRequestHandler(RequestHandler handler) {
    requestHandler.add(handler);
    requestHandler.size();
  }

  /**
   * Removes the request handler from the list of handlers.
   *
   * @param handler
   *          the request handler to remove
   */
  void removeRequestHandler(RequestHandler handler) {
    requestHandler.remove(handler);
  }

  /**
   * Registers the response cache with the main dispatcher servlet.
   *
   * @param cache
   *          the response cache
   */
  void addResponseCache(CacheService cache) {
    caches.put(cache.getIdentifier(), cache);
    logger.info("Response caching activated for site '{}'", cache.getIdentifier());
  }

  /**
   * Removes the response cache from the main dispatcher servlet.
   *
   * @param cache
   *          the response cache
   */
  void removeResponseCache(CacheService cache) {
    caches.remove(cache.getIdentifier());
    logger.info("Response caching deactivated for site '{}'", cache.getIdentifier());
  }

  /**
   * Method to fire a <code>startRequest()</code> message to all registered
   * <code>RequestListeners</code>.
   *
   * @param request
   *          the starting request
   * @param response
   *          the servlet response
   * @param site
   *          the target site
   */
  protected void fireRequestStarted(WebloungeRequest request,
      WebloungeResponse response, Site site) {
    for (int i = 0; i < requestListeners.size(); i++) {
      RequestListener listener = requestListeners.get(i);
      listener.requestStarted(request, response);
    }
  }

  /**
   * Method to fire a <code>requestDelivered()</code> message to all registered
   * <code>RequestListeners</code>.
   *
   * @param request
   *          the delivered request
   * @param response
   *          the servlet response
   * @param site
   *          the target site
   */
  protected void fireRequestDelivered(WebloungeRequest request,
      WebloungeResponse response, Site site) {
    for (int i = 0; i < requestListeners.size(); i++) {
      RequestListener listener = requestListeners.get(i);
      listener.requestDelivered(request, response);
    }
  }

  /**
   * Method to fire a <code>requestFailed()</code> message to all registered
   * <code>RequestListeners</code>.
   *
   * @param request
   *          the failing request
   * @param response
   *          the servlet response
   * @param site
   *          the target site
   */
  protected void fireRequestFailed(WebloungeRequest request,
      WebloungeResponse response, Site site) {
    WebloungeResponseImpl resp = ((WebloungeResponseImpl) response);
    for (int i = 0; i < requestListeners.size(); i++) {
      RequestListener listener = requestListeners.get(i);
      listener.requestFailed(request, response, resp.getStatus());
    }
    if (site != null)
      site.requestFailed(request, response, resp.getStatus());
  }

  /**
   * Sets the environment that is used to determine the correct context for
   * requests.
   *
   * @param environment
   *          the environment
   */
  public void setEnvironment(Environment environment) {
    this.environment = environment;
  }

  /**
   * Sets the site locator.
   *
   * @param siteDispatcher
   *          the site locator
   */
  void setSiteDispatcher(SiteDispatcherService siteDispatcher) {
    this.sites = siteDispatcher;
  }

  /**
   * Removes the site locator.
   *
   * @param siteDispatcher
   *          the site locator
   */
  void removeSiteDispatcher(SiteDispatcherService siteDispatcher) {
    this.sites = null;
  }

  /**
   * Sets the security service.
   *
   * @param securityService
   *          the security service
   */
  void setSecurityService(SecurityService securityService) {
    this.securityService = securityService;
  }

  /**
   * {@inheritDoc}
   *
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return "Weblounge dispatcher servlet";
  }

}
TOP

Related Classes of ch.entwine.weblounge.dispatcher.impl.WebloungeDispatcherServlet

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.