/** create and submit the portlet content rendering job to the thread pool */
  protected IPortletRenderExecutionWorker startPortletRenderInternal(
      IPortletWindowId portletWindowId, HttpServletRequest request, HttpServletResponse response) {
    // first check to see if there is a Throwable in the session for this IPortletWindowId
    final Map<IPortletWindowId, Exception> portletFailureMap = getPortletErrorMap(request);
    final Exception cause = portletFailureMap.remove(portletWindowId);

    final IPortletRenderExecutionWorker portletRenderExecutionWorker;
    if (null != cause) {
      // previous action failed, dispatch to errorPortlet immediately
      portletRenderExecutionWorker =
          this.portletWorkerFactory.createFailureWorker(request, response, portletWindowId, cause);
    } else {
      IPortletWindow portletWindow =
          portletWindowRegistry.getPortletWindow(request, portletWindowId);
      IPortletDefinition portletDef = portletWindow.getPortletEntity().getPortletDefinition();
      if (portletDef.getLifecycleState().equals(PortletLifecycleState.MAINTENANCE)) {
        // Prevent the portlet from rendering;  replace with a helpful "Out of Service" message
        portletRenderExecutionWorker =
            this.portletWorkerFactory.createFailureWorker(
                request, response, portletWindowId, new MaintenanceModeException());
      } else {
        // Happy path
        portletRenderExecutionWorker =
            this.portletWorkerFactory.createRenderWorker(request, response, portletWindowId);
      }
    }

    portletRenderExecutionWorker.submit();

    final Map<IPortletWindowId, IPortletRenderExecutionWorker> portletRenderingMap =
        this.getPortletRenderingMap(request);
    portletRenderingMap.put(portletWindowId, portletRenderExecutionWorker);

    return portletRenderExecutionWorker;
  }
  /** Checks to see if a worker has been retrieved (not orphaned) and if it is complete. */
  protected void checkWorkerCompletion(
      HttpServletRequest request, IPortletRenderExecutionWorker portletRenderExecutionWorker) {
    if (!portletRenderExecutionWorker.isRetrieved()) {
      final IPortletWindowId portletWindowId = portletRenderExecutionWorker.getPortletWindowId();
      final IPortletWindow portletWindow =
          this.portletWindowRegistry.getPortletWindow(request, portletWindowId);
      this.logger.warn(
          "Portlet worker started but never retrieved for {}, worker {}."
              + " If random portlet fnames it may be users switching tabs before page is done rendering"
              + " (would see separate log message with java.net.SocketException on socket write)."
              + " If repeatedly occurring with one portlet fname your theme layout xsl may not be including"
              + " a portlet present in your layout xml files (see"
              + " http://jasig.275507.n4.nabble.com/Portlet-worker-started-but-never-retrieved-td4580698.html)",
          portletWindow,
          portletRenderExecutionWorker);

      try {
        portletRenderExecutionWorker.get(0);
      } catch (Exception e) {
        // Ignore exception here, we just want to get this worker to complete
      }
    }

    if (!portletRenderExecutionWorker.isComplete()) {
      cancelWorker(request, portletRenderExecutionWorker);
    }
  }
 /**
  * Returns the PortletRenderResult waiting up to the portlet's timeout
  *
  * @return The PortletRenderResult from the portlet's execution
  * @throws TimeoutException If the portlet's timeout was hit before a result was returned
  * @throws Exception The exception thrown by the portlet during execution
  */
 protected PortletRenderResult getPortletRenderResult(
     IPortletWindowId portletWindowId, HttpServletRequest request, HttpServletResponse response)
     throws Exception {
   final IPortletRenderExecutionWorker tracker =
       getRenderedPortletBodyWorker(portletWindowId, request, response);
   final long timeout = getPortletRenderTimeout(portletWindowId, request);
   return tracker.get(timeout);
 }
  /**
   * create and submit the portlet header rendering job to the thread pool
   *
   * @param portletWindowId
   * @param request
   * @param response
   * @return
   */
  protected IPortletRenderExecutionWorker startPortletHeaderRenderInternal(
      IPortletWindowId portletWindowId, HttpServletRequest request, HttpServletResponse response) {
    IPortletRenderExecutionWorker portletHeaderRenderWorker =
        this.portletWorkerFactory.createRenderHeaderWorker(request, response, portletWindowId);
    portletHeaderRenderWorker.submit();

    final Map<IPortletWindowId, IPortletRenderExecutionWorker> portletHeaderRenderingMap =
        this.getPortletHeaderRenderingMap(request);
    portletHeaderRenderingMap.put(portletWindowId, portletHeaderRenderWorker);

    return portletHeaderRenderWorker;
  }
  /* (non-Javadoc)
   * @see org.apereo.portal.portlet.rendering.IPortletExecutionManager#getPortletHeadOutput(org.apereo.portal.portlet.om.IPortletWindowId, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  @Override
  public String getPortletHeadOutput(
      IPortletWindowId portletWindowId, HttpServletRequest request, HttpServletResponse response) {
    if (doesPortletNeedHeaderWorker(portletWindowId, request)) {
      final IPortletRenderExecutionWorker tracker =
          getRenderedPortletHeaderWorker(portletWindowId, request, response);
      final long timeout = getPortletRenderTimeout(portletWindowId, request);
      try {
        final String output = tracker.getOutput(timeout);
        return output == null ? "" : output;
      } catch (Exception e) {
        logger.error("failed to render header output for " + portletWindowId, e);
        return "";
      }
    }

    logger.debug(portletWindowId + " does not produce output for header");
    return "";
  }
  /* (non-Javadoc)
   * @see org.apereo.portal.portlet.rendering.IPortletExecutionManager#getPortletOutput(org.apereo.portal.portlet.om.IPortletWindowId, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
   */
  @Override
  public String getPortletOutput(
      IPortletWindowId portletWindowId, HttpServletRequest request, HttpServletResponse response) {
    final IPortletRenderExecutionWorker tracker =
        getRenderedPortletBodyWorker(portletWindowId, request, response);
    final long timeout = getPortletRenderTimeout(portletWindowId, request);

    try {
      final String output = tracker.getOutput(timeout);
      return output == null ? "" : output;
    } catch (Exception e) {
      final IPortletFailureExecutionWorker failureWorker =
          this.portletWorkerFactory.createFailureWorker(request, response, portletWindowId, e);
      // TODO publish portlet error event?
      try {
        failureWorker.submit();
        return failureWorker.getOutput(timeout);
      } catch (Exception e1) {
        logger.error("Failed to render error portlet for: " + portletWindowId, e1);
        return "Error Portlet Unavailable. Please contact your portal administrators.";
      }
    }
  }