public List<ServiceEndpoint> getEndpoints(Service service, String protocol) throws DaoException {
   List<ServiceEndpoint> eps = new ArrayList<ServiceEndpoint>();
   for (ServiceEndpoint e : service.getServiceEndpoints()) {
     String p = e.getProtocolId();
     if (p != null) {
       if (e.getProtocolId().equals(protocol)) {
         eps.add(e);
       }
     } else {
       if (protocol.equals(Protocols.DEFAULT)) {
         eps.add(e);
       }
     }
   }
   return eps;
 }
  @Override
  public void invokeRequest(
      ServletContext servletContext,
      HttpServletRequest request,
      HttpServletResponse response,
      ServiceContext serviceContext,
      DaoContext daoContext,
      Service service,
      List<ServiceEndpoint> endpoints,
      String additionalUrlPart,
      Map<String, String> headers,
      byte[] input)
      throws DaoException, TooManyCallNestException, NoValidEndpointsException,
          ProcessFailedException, IOException {
    String serviceId = service.getServiceId();
    StringBuilder faults = new StringBuilder();
    URL url;
    ServiceEndpoint endpoint = endpoints.get(0);

    if (additionalUrlPart != null) {
      try {
        url = URLUtil.mergePath(endpoint.getUrl(), additionalUrlPart);
      } catch (MalformedURLException e) {
        logger.warning(
            "failed to create url: "
                + endpoint.getUrl().toString()
                + "  param: "
                + additionalUrlPart);
        url = endpoint.getUrl();
      }
    } else {
      url = endpoint.getUrl();
    }

    int timeoutMillis =
        new HttpServletRequestParameterContext(request).getInteger("langrid.timeout", 0);
    if (timeoutMillis == 0) {
      timeoutMillis = service.getTimeoutMillis();
      if (timeoutMillis == 0) {
        ServiceContainerType ct = service.getContainerType();
        if (ct == null || ct.equals(ATOMIC)) {
          timeoutMillis = atomicReadTimeout;
        } else if (ct.equals(COMPOSITE)) {
          timeoutMillis = compositeReadTimeout;
        }
      }
    }

    if (timeoutEnabled && timeoutMillis > 0) {
      final ServletRequest wrequest = createServletRequest(request, headers, input);
      final RequestDispatcher rd = servletContext.getRequestDispatcher(url.getPath());
      final HttpServletResponse res = response;
      final Holder<Exception> uncaughtException = new Holder<Exception>();
      Thread thread =
          new Thread(
              new Runnable() {
                @Override
                public void run() {
                  try {
                    rd.forward(wrequest, res);
                  } catch (ServletException e) {
                    uncaughtException.set(e);
                  } catch (IOException e) {
                    uncaughtException.set(e);
                  }
                }
              });
      thread.start();
      try {
        thread.join(timeoutMillis);
      } catch (InterruptedException e) {
      }
      if (thread.isAlive()) {
        // thread.stop();
        response.setStatus(HttpServletResponse.SC_REQUEST_TIMEOUT);
      } else {
        Exception e = uncaughtException.get();
        if (e != null) {
          logger.log(Level.WARNING, "invocation failed. sid: " + serviceId + ", url: " + url, e);
          faults.append("invocation failed with exception: ");
          faults.append(e.toString());
          throw new ProcessFailedException(faults.toString());
        }
      }
    } else {
      try {
        servletContext
            .getRequestDispatcher(url.getPath())
            .forward(createServletRequest(request, headers, input), response);
        return;
      } catch (ServletException e) {
        logger.log(Level.WARNING, "invocation failed. sid: " + serviceId + ", url: " + url, e);
        faults.append("invocation failed with exception: ");
        faults.append(e.toString());
      } catch (IOException e) {
        logger.log(Level.WARNING, "invocation failed. sid: " + serviceId + ", url: " + url, e);
        faults.append("invocation failed with exception: ");
        faults.append(e.toString());
      }
      throw new ProcessFailedException(faults.toString());
    }
  }