@Override
  public DelegationActionResponse doAction(
      ActionRequest actionRequest,
      ActionResponse actionResponse,
      DelegationRequest delegationRequest)
      throws IOException {
    final HttpServletRequest request = this.portalRequestUtils.getPortletHttpRequest(actionRequest);
    final HttpServletResponse response =
        this.portalRequestUtils.getOriginalPortalResponse(actionRequest);

    // Sanity check that the dispatch is being called by the same user it was created for
    final IPerson person = this.personManager.getPerson(request);
    if (this.userId != person.getID()) {
      throw new IllegalStateException(
          "This dispatcher was created for userId "
              + this.userId
              + " but is being executed for userId "
              + person.getID());
    }

    this.setupDelegateRequestInfo(request, delegationRequest);

    final IPortletWindowId portletWindowId = this.portletWindow.getPortletWindowId();
    try {
      // TODO canRender permission checks!
      this.portletRenderer.doAction(portletWindowId, request, response);
    } catch (RuntimeException e) {
      this.logger.error("Failed to execute action on delegate", e);
      throw e;
    }

    // Get the portal URL builders for this request and check if a redirect was sent
    final IPortalActionUrlBuilder portalActionUrlBuilder =
        this.portalUrlProvider.getPortalActionUrlBuilder(request);
    final String redirectLocation = portalActionUrlBuilder.getRedirectLocation();
    if (redirectLocation != null) {
      final String renderUrlParamName = portalActionUrlBuilder.getRenderUrlParamName();

      // clear out the redirect from the delegate, leave it up to the parent if the redirect should
      // happen
      portalActionUrlBuilder.setRedirectLocation(null, null);

      return new DelegationActionResponse(
          this.getDelegateState(), redirectLocation, renderUrlParamName);
    }

    // No redirect so get the portlet's url builder and copy the state-changing data into the
    // delegate response
    final IPortletUrlBuilder portletUrlBuilder =
        portalActionUrlBuilder.getPortletUrlBuilder(portletWindowId);

    final WindowState windowState = portletUrlBuilder.getWindowState();
    final PortletMode portletMode = portletUrlBuilder.getPortletMode();
    final Map<String, String[]> parameters = portletUrlBuilder.getParameters();

    return new DelegationActionResponse(
        this.getDelegateState(), portletMode, windowState, parameters);
  }
  protected String getUrlString(IRedirectionUrl url, HttpServletRequest request) {

    if (url instanceof ExternalRedirectionUrl) {
      ExternalRedirectionUrl externalUrl = (ExternalRedirectionUrl) url;
      StringBuffer urlStr = new StringBuffer();
      urlStr.append(externalUrl.getUrl());

      try {

        // add any additional parameters
        String separator = "?";
        for (Map.Entry<String, String[]> param : externalUrl.getAdditionalParameters().entrySet()) {
          for (String value : param.getValue()) {
            urlStr.append(separator);
            urlStr.append(param.getKey());
            urlStr.append("=");
            urlStr.append(URLEncoder.encode(value, "UTF-8"));
            separator = "&";
          }
        }

        // add any dynamic parameters
        for (Map.Entry<String, String> param : externalUrl.getDynamicParameters().entrySet()) {
          String[] values = request.getParameterValues(param.getKey());
          if (values != null) {
            for (String value : values) {
              urlStr.append(separator);
              urlStr.append(param.getValue());
              urlStr.append("=");
              urlStr.append(URLEncoder.encode(value, "UTF-8"));
              separator = "&";
            }
          }
        }
        return urlStr.toString();

      } catch (UnsupportedEncodingException ex) {
        log.error("Unable to encode URL parameter for external service redirect", ex);
        return null;
      }

    } else {

      PortletRedirectionUrl portletUrl = (PortletRedirectionUrl) url;

      // create the base URL for the portlet
      final IPortletWindow portletWindow =
          this.portletWindowRegistry.getOrCreateDefaultPortletWindowByFname(
              request, portletUrl.getFname());
      final IPortalUrlBuilder portalUrlBuilder =
          this.portalUrlProvider.getPortalUrlBuilderByPortletWindow(
              request, portletWindow.getPortletWindowId(), portletUrl.getType());
      final IPortletUrlBuilder portletUrlBuilder = portalUrlBuilder.getTargetedPortletUrlBuilder();
      portletUrlBuilder.setPortletMode(portletUrl.getMode());
      portletUrlBuilder.setWindowState(WindowState.MAXIMIZED);

      // for each of the defined additional parameters, add a matching
      // parameter to the portlet URL
      for (Map.Entry<String, String[]> param : portletUrl.getAdditionalParameters().entrySet()) {
        portletUrlBuilder.addParameter(param.getKey(), param.getValue());
      }

      // for each of the defined dynamic parameters, add a parameter if
      // the value submitted to this service was non-null
      for (Map.Entry<String, String> param : portletUrl.getDynamicParameters().entrySet()) {
        String[] values = request.getParameterValues(param.getKey());
        if (values != null) {
          portletUrlBuilder.addParameter(param.getValue(), values);
        }
      }

      return portalUrlBuilder.getUrlString();
    }
  }