Package ro.isdc.wro.model.resource.locator.support

Source Code of ro.isdc.wro.model.resource.locator.support.DispatcherStreamLocator

/*
* Copyright (c) 2008. All rights reserved.
*/
package ro.isdc.wro.model.resource.locator.support;

import static org.apache.commons.lang3.Validate.notNull;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.config.jmx.WroConfiguration;
import ro.isdc.wro.http.support.RedirectedStreamServletResponseWrapper;
import ro.isdc.wro.model.resource.locator.UriLocator;
import ro.isdc.wro.model.resource.locator.UrlUriLocator;
import ro.isdc.wro.util.WroUtil;


/**
* Responsible to locate a context relative resource. Attempts to locate the resource using {@link RequestDispatcher} .
* If the dispatcher fails, it will fallback resource retrieval to a http call using {@link UrlUriLocator}.
*
* @author Alex Objelean
*/
public class DispatcherStreamLocator {
  private static final Logger LOG = LoggerFactory.getLogger(DispatcherStreamLocator.class);
  /**
   * Attribute indicating that the request is included from within a wro request cycle. This is required to prevent
   * {@link StackOverflowError}.
   *
   * @VisibleForTesting
   */
  public static final String ATTRIBUTE_INCLUDED_BY_DISPATCHER = DispatcherStreamLocator.class.getName()
      + ".included_with_dispatcher";
  private int timeout = WroConfiguration.DEFAULT_CONNECTION_TIMEOUT;

  /**
   * /** When using JBoss Portal and it has some funny quirks...actually a portal application have several small web
   * application behind it. So when it intercepts a requests for portal then it start bombing the the application behind
   * the portal with multiple threads (web requests) that are combined with threads for wro4j.
   *
   * @param location
   *          a path to a request relative resource to locate.
   * @return a valid stream for required location. This method will never return a null.
   * @throws IOException
   *           if the stream cannot be located at the specified location.
   */
  public InputStream getInputStream(final HttpServletRequest request, final HttpServletResponse response,
      final String location)
      throws IOException {
    if (request == null || response == null || location == null) {
      throw new IOException("Cannot get stream for location: " + location
          + " because either request, response or location is not available");
    }

    // where to write the bytes of the stream
    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    boolean warnOnEmptyStream = false;
    try {
      final RequestDispatcher dispatcher = request.getRequestDispatcher(location);
      if (dispatcher != null) {
        // Wrap request
        final ServletRequest servletRequest = getWrappedServletRequest(request, location);
        // Wrap response
        final ServletResponse servletResponse = new RedirectedStreamServletResponseWrapper(os, response);
        LOG.debug("dispatching request to location: {}", location);
        // use dispatcher
        dispatcher.include(servletRequest, servletResponse);
        warnOnEmptyStream = true;
        // force flushing - the content will be written to BytArrayOutputStream.
        // Otherwise exactly 32K of data will be written.
        servletResponse.getWriter().flush();
        os.close();
      }
    } catch (final Exception e) {
      LOG.debug("Could not dispatch request for location {}", location);
      // Not only servletException can be thrown, also dispatch.include can throw NPE when the scheduler runs outside
      // of the request cycle, thus connection is unavailable. This is caused mostly when invalid resources are
      // included.
      return locateExternal(request, location);
    }
    try {
      // fallback to external resource locator if the dispatcher is empty
      if (os.size() == 0) {
        return locateExternal(request, location);
      }
    } finally {
      if (warnOnEmptyStream && os.size() == 0) {
        LOG.debug("Wrong or empty resource with location: {}", location);
      }
    }
    return new ByteArrayInputStream(os.toByteArray());
  }

  public InputStream locateExternal(final HttpServletRequest request, final String location)
      throws IOException {
    final String servletContextPath = request.getRequestURL().toString().replace(request.getServletPath(), "");
    final String absolutePath = servletContextPath + location;
    LOG.debug("locateExternalUri: {}", absolutePath);
    return createExternalResourceLocator().locate(absolutePath);
  }

  /**
   * @return the {@link UriLocator} responsible for locating resources when dispatcher fails. No wildcard handling is
   *         required.
   * @VisibleForTesting
   */
  UriLocator createExternalResourceLocator() {
    final UrlUriLocator locator = new UrlUriLocator();
    locator.setEnableWildcards(false);
    locator.setTimeout(timeout);
    return locator;
  }

  /**
   * Build a wrapped servlet request which will be used for dispatching.
   */
  private ServletRequest getWrappedServletRequest(final HttpServletRequest request, final String location) {
    final HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) {
      @Override
      public String getRequestURI() {
        return getContextPath() + location;
      }

      @Override
      public String getPathInfo() {
        return WroUtil.getPathInfoFromLocation(this, location);
      }

      @Override
      public String getServletPath() {
        return WroUtil.getServletPathFromLocation(this, location);
      }
    };
    // add an attribute to mark this request as included from wro
    wrappedRequest.setAttribute(ATTRIBUTE_INCLUDED_BY_DISPATCHER, Boolean.TRUE);
    return wrappedRequest;
  }

  /**
   * @return true if the request is included from within wro request cycle.
   */
  public static boolean isIncludedRequest(final HttpServletRequest request) {
    notNull(request);
    return request.getAttribute(ATTRIBUTE_INCLUDED_BY_DISPATCHER) != null;
  }

  /**
   * @param timeout
   *          time (in millis) used when an external url is requested for both: connection & read.
   */
  public void setTimeout(final int timeout) {
    this.timeout = timeout;
  }

  /**
   * @return the configured timeout for this instance of locator.
   */
  public int getTimeout() {
    return timeout;
  }
}
TOP

Related Classes of ro.isdc.wro.model.resource.locator.support.DispatcherStreamLocator

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.