Example usage for javax.servlet.http HttpServletRequestWrapper HttpServletRequestWrapper

List of usage examples for javax.servlet.http HttpServletRequestWrapper HttpServletRequestWrapper

Introduction

In this page you can find the example usage for javax.servlet.http HttpServletRequestWrapper HttpServletRequestWrapper.

Prototype

public HttpServletRequestWrapper(HttpServletRequest request) 

Source Link

Document

Constructs a request object wrapping the given request.

Usage

From source file:org.cloudfoundry.identity.uaa.authentication.AuthzAuthenticationFilter.java

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;

    Map<String, String> loginInfo = getCredentials(req);

    boolean buggyVmcAcceptHeader = false;

    try {//  www  . j a v a  2s  . com
        if (loginInfo.isEmpty()) {
            throw new BadCredentialsException("Request does not contain credentials.");
        } else {
            logger.debug("Located credentials in request, with keys: " + loginInfo.keySet());
            if (methods != null && !methods.contains(req.getMethod().toUpperCase())) {
                throw new BadCredentialsException("Credentials must be sent by (one of methods): " + methods);
            }
            Authentication result = authenticationManager
                    .authenticate(new AuthzAuthenticationRequest(loginInfo, new UaaAuthenticationDetails(req)));
            SecurityContextHolder.getContext().setAuthentication(result);
            ofNullable(successHandler).ifPresent(s -> s.setSavedAccountOptionCookie(req, res, result));
        }
    } catch (AuthenticationException e) {
        logger.debug("Authentication failed");

        String acceptHeaderValue = req.getHeader("accept");
        String clientId = req.getParameter("client_id");
        if ("*/*; q=0.5, application/xml".equals(acceptHeaderValue) && "vmc".equals(clientId)) {
            buggyVmcAcceptHeader = true;
        }

        if (buggyVmcAcceptHeader) {
            HttpServletRequest jsonAcceptingRequest = new HttpServletRequestWrapper(req) {

                @SuppressWarnings("unchecked")
                @Override
                public Enumeration<String> getHeaders(String name) {
                    if ("accept".equalsIgnoreCase(name)) {
                        return new JsonInjectedEnumeration(
                                ((HttpServletRequest) getRequest()).getHeaders(name));
                    } else {
                        return ((HttpServletRequest) getRequest()).getHeaders(name);
                    }
                }

                @Override
                public String getHeader(String name) {
                    if (name.equalsIgnoreCase("accept")) {
                        return "application/json";
                    } else {
                        return ((HttpServletRequest) getRequest()).getHeader(name);
                    }
                }
            };

            authenticationEntryPoint.commence(jsonAcceptingRequest, res, e);
        } else {
            authenticationEntryPoint.commence(req, res, e);
        }
        return;
    }

    chain.doFilter(request, response);
}

From source file:org.codehaus.waffle.io.RequestFileUploader.java

/**
 * Creates RequestFileUploader/*from  w w  w .  j a v  a 2 s . c  o m*/
 * 
 * @param request the HttpServletRequest
 * @param itemFactory the FileItemFactory
 */
public RequestFileUploader(HttpServletRequest request, FileItemFactory itemFactory) {
    this.request = new HttpServletRequestWrapper(request);
    this.itemFactory = itemFactory;
}

From source file:org.codice.ddf.security.idp.client.AssertionConsumerService.java

private boolean login(org.opensaml.saml.saml2.core.Response samlResponse) {
    if (!request.isSecure()) {
        return false;
    }/*from   ww  w .j  a  v  a  2 s .co  m*/
    Map<String, Cookie> cookieMap = HttpUtils.getCookieMap(request);
    if (cookieMap.containsKey("JSESSIONID")) {
        sessionFactory.getOrCreateSession(request).invalidate();
    }
    String assertionValue = DOM2Writer.nodeToString(samlResponse.getAssertions().get(0).getDOM());

    String encodedAssertion;
    try {
        encodedAssertion = RestSecurity.deflateAndBase64Encode(assertionValue);
    } catch (IOException e) {
        LOGGER.warn("Unable to deflate and encode assertion.", e);
        return false;
    }

    final String authHeader = RestSecurity.SAML_HEADER_PREFIX + encodedAssertion;

    HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request) {
        @Override
        public String getHeader(String name) {
            if (RestSecurity.AUTH_HEADER.equals(name)) {
                return authHeader;
            }
            return super.getHeader(name);
        }

        @Override
        public Object getAttribute(String name) {
            if (ContextPolicy.ACTIVE_REALM.equals(name)) {
                return "idp";
            }
            return super.getAttribute(name);
        }
    };

    SAMLAssertionHandler samlAssertionHandler = new SAMLAssertionHandler();

    LOGGER.trace("Processing SAML assertion with SAML Handler.");
    HandlerResult samlResult = samlAssertionHandler.getNormalizedToken(wrappedRequest, null, null, false);

    if (samlResult.getStatus() != HandlerResult.Status.COMPLETED) {
        LOGGER.debug("Failed to handle SAML assertion.");
        return false;
    }

    request.setAttribute(WebSSOFilter.DDF_AUTHENTICATION_TOKEN, samlResult);
    request.removeAttribute(ContextPolicy.NO_AUTH_POLICY);

    try {
        LOGGER.trace("Trying to login with provided SAML assertion.");
        loginFilter.doFilter(wrappedRequest, null, (servletRequest, servletResponse) -> {
        });
    } catch (IOException | ServletException e) {
        LOGGER.debug("Failed to apply login filter to SAML assertion", e);
        return false;
    }

    return true;
}

From source file:org.codice.ddf.security.idp.client.IdpHandler.java

/**
 * Handler implementing SAML 2.0 IdP authentication. Supports HTTP-Redirect and HTTP-POST bindings.
 *
 * @param request  http request to obtain attributes from and to pass into any local filter chains required
 * @param response http response to return http responses or redirects
 * @param chain    original filter chain (should not be called from your handler)
 * @param resolve  flag with true implying that credentials should be obtained, false implying return if no credentials are found.
 * @return result of handling this request - status and optional tokens
 * @throws ServletException/*from  w w  w .ja v  a 2s.  c  o m*/
 */
@Override
public HandlerResult getNormalizedToken(ServletRequest request, ServletResponse response, FilterChain chain,
        boolean resolve) throws ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(httpRequest) {
        @Override
        public Object getAttribute(String name) {
            if (ContextPolicy.ACTIVE_REALM.equals(name)) {
                return "idp";
            }
            return super.getAttribute(name);
        }
    };

    SAMLAssertionHandler samlAssertionHandler = new SAMLAssertionHandler();
    samlAssertionHandler.setSessionFactory(sessionFactory);

    LOGGER.trace("Processing SAML assertion with SAML Handler.");
    HandlerResult samlResult = samlAssertionHandler.getNormalizedToken(wrappedRequest, null, null, false);

    if (samlResult != null && samlResult.getStatus() == HandlerResult.Status.COMPLETED) {
        return samlResult;
    }

    HandlerResult handlerResult = new HandlerResult(HandlerResult.Status.REDIRECTED, null);
    handlerResult.setSource("idp-" + SOURCE);

    String path = httpRequest.getServletPath();
    LOGGER.debug("Doing IdP authentication and authorization for path {}", path);

    // Default to HTTP-Redirect if binding is null
    if (idpMetadata.getSingleSignOnBinding() == null
            || idpMetadata.getSingleSignOnBinding().endsWith("Redirect")) {
        doHttpRedirectBinding((HttpServletRequest) request, (HttpServletResponse) response);
    } else {
        doHttpPostBinding((HttpServletRequest) request, (HttpServletResponse) response);
    }

    return handlerResult;
}

From source file:org.debux.webmotion.server.WebMotionServer.java

/**
 * Static resources management//from  w ww.  j  ava 2s  .  c o  m
 */
protected void doResource(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    // Suppress the static in path
    HttpServletRequest requestWrapper = new HttpServletRequestWrapper(request) {
        @Override
        public String getServletPath() {
            String servletPath = super.getServletPath();
            if (servletPath != null) {
                return servletPath.replaceFirst(PATH_STATIC, "");
            }
            return null;
        }

        @Override
        public String getPathInfo() {
            String pathInfo = super.getPathInfo();
            if (pathInfo != null) {
                return pathInfo.replaceFirst(PATH_STATIC, "");
            }
            return null;
        }

        @Override
        public String getRequestURI() {
            String requestURI = super.getRequestURI();
            if (requestURI != null) {
                return requestURI.replaceFirst(PATH_STATIC, "");
            }
            return null;
        }
    };

    // Dispatch on default servlet
    ServletContext servletContext = request.getServletContext();
    RequestDispatcher dispatcher = servletContext.getNamedDispatcher("default");

    DispatcherType dispatcherType = request.getDispatcherType();
    if (dispatcherType == DispatcherType.INCLUDE) {
        dispatcher.include(requestWrapper, response);
    } else {
        dispatcher.forward(requestWrapper, response);
    }
}

From source file:org.eclipse.equinox.http.servlet.tests.ServletTest.java

public void basicFilterTest22(String servlet1Pattern, String servlet2Pattern, String filterPattern,
        String expected, String[] dispatchers) throws Exception {
    final AtomicReference<HttpServletRequestWrapper> httpServletRequestWrapper = new AtomicReference<HttpServletRequestWrapper>();
    final AtomicReference<HttpServletResponseWrapper> httpServletResponseWrapper = new AtomicReference<HttpServletResponseWrapper>();

    Servlet servlet1 = new BaseServlet() {
        private static final long serialVersionUID = 1L;

        @Override/*from  w ww  . jav a  2  s .c  o m*/
        protected void service(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            request.getRequestDispatcher("index.jsp").forward(request, response);
        }
    };

    Servlet servlet2 = new BaseServlet("a") {
        private static final long serialVersionUID = 1L;

        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {

            if ((httpServletRequestWrapper.get() != null) && !request.equals(httpServletRequestWrapper.get())) {
                throw new ServletException("not the same request");
            }
            if ((httpServletResponseWrapper.get() != null)
                    && !response.equals(httpServletResponseWrapper.get())) {
                throw new ServletException("not the same response");
            }

            response.getWriter().print(content);
        }

    };

    Filter filter = new TestFilter() {

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {

            response.getWriter().write('b');

            httpServletRequestWrapper.set(new HttpServletRequestWrapper((HttpServletRequest) request));
            httpServletResponseWrapper.set(new HttpServletResponseWrapper((HttpServletResponse) response));

            chain.doFilter(httpServletRequestWrapper.get(), httpServletResponseWrapper.get());

            response.getWriter().write('b');
        }

    };

    Dictionary<String, Object> props = new Hashtable<String, Object>();
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, servlet1Pattern);
    registrations.add(getBundleContext().registerService(Servlet.class, servlet1, props));

    props = new Hashtable<String, Object>();
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, servlet2Pattern);
    registrations.add(getBundleContext().registerService(Servlet.class, servlet2, props));

    props = new Hashtable<String, Object>();
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, "F22");
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER, dispatchers);
    props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, filterPattern);
    registrations.add(getBundleContext().registerService(Filter.class, filter, props));

    String response = requestAdvisor.request("f22/a");

    Assert.assertEquals(expected, response);
}

From source file:org.jahia.bin.Logout.java

protected void doRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String redirect = request.getParameter("redirect");
    if (redirect == null) {
        redirect = request.getHeader("referer");
        if (StringUtils.isNotEmpty(redirect) && Login.isAuthorizedRedirect(request, redirect, false)) {
            redirect = redirect.startsWith("http://") ? StringUtils.substringAfter(redirect, "http://")
                    : StringUtils.substringAfter(redirect, "https://");
            redirect = redirect.contains("/") ? "/" + StringUtils.substringAfter(redirect, "/") : null;
        } else {/*from   ww  w  .  ja  v a2s .co m*/
            redirect = null;
        }
    } else if (!Login.isAuthorizedRedirect(request, redirect, false)) {
        redirect = null;
    }
    if (StringUtils.isNotBlank(redirect)) {
        try {
            final String r = redirect;
            HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
                @Override
                public String getRequestURI() {
                    return r;
                }

                @Override
                public String getPathInfo() {
                    if (r.startsWith(getContextPath() + "/cms/")) {
                        return StringUtils.substringAfter(r, getContextPath() + "/cms");
                    }
                    return null;
                }
            };

            if (urlRewriteService.prepareInbound(wrapper, response)) {
                RewrittenUrl restored = urlRewriteService.rewriteInbound(wrapper, response);
                if (restored != null) {
                    redirect = request.getContextPath() + restored.getTarget();
                }
            }
        } catch (Exception e) {
            logger.error("Cannot rewrite redirection url", e);
        }

        String prefix = request.getContextPath() + "/cms/";
        if (redirect.startsWith(prefix)) {
            String url = "/" + StringUtils.substringAfter(redirect, prefix);
            String hash = StringUtils.substringAfterLast(url, "#");
            url = StringUtils.substringBefore(url,
                    ";" + SettingsBean.getInstance().getJsessionIdParameterName());
            url = StringUtils.substringBefore(url, "?");
            url = StringUtils.substringBefore(url, "#");
            if (hash != null && hash.startsWith("/sites/") && url.contains("/sites/")) {
                url = StringUtils.substringBefore(url, "/sites/") + StringUtils.substringBefore(hash, ":")
                        + ".html";
            }

            List<String> urls = new ArrayList<String>();
            urls.add(url);
            if (url.startsWith("/edit/")) {
                url = "/render/" + StringUtils.substringAfter(url, "/edit/");
                urls.add(url);
            } else if (url.startsWith("/editframe/default/")) {
                url = "/render/live/" + StringUtils.substringAfter(url, "/editframe/default/");
                urls.add(url);
            } else if (url.startsWith("/contribute/")) {
                url = "/render/" + StringUtils.substringAfter(url, "/contribute/");
                urls.add(url);
            } else if (url.startsWith("/contributeframe/default/")) {
                url = "/render/live/" + StringUtils.substringAfter(url, "/contributeframe/default/");
                urls.add(url);
            }
            if (url.startsWith("/render/default/")) {
                url = "/render/live/" + StringUtils.substringAfter(url, "/render/default/");
                urls.add(url);
            }
            for (String currentUrl : urls) {
                try {
                    URLResolver r = urlResolverFactory.createURLResolver(currentUrl, request.getServerName(),
                            request);
                    if (r.getPath().startsWith("/sites/")) {
                        JCRNodeWrapper n = r.getNode();
                        // test that we do not get the site node, in that case, redirect to homepage
                        if (n.isNodeType("jnt:virtualsite")) {
                            n = ((JCRSiteNode) n).getHome();
                        }
                        if (n == null) {
                            // this can occur if the homepage of the site is not set
                            redirect = request.getContextPath() + "/";
                        } else {
                            redirect = prefix + r.getServletPart() + "/" + r.getWorkspace() + "/"
                                    + resolveLanguage(request, n.getResolveSite()) + n.getPath() + ".html";
                        }
                    } else {
                        redirect = request.getContextPath() + "/";
                    }
                    redirect = urlRewriteService.rewriteOutbound(redirect, request, response);
                    response.sendRedirect(response.encodeRedirectURL(redirect));
                    return;
                } catch (Exception e) {
                    logger.debug("Cannot redirect to " + currentUrl, e);
                }
            }
            response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + "/"));
            return;
        }
    }

    response.sendRedirect(response
            .encodeRedirectURL(StringUtils.isNotEmpty(redirect) ? redirect : request.getContextPath() + "/"));
}

From source file:org.jahia.services.render.webflow.WebflowDispatcherScript.java

/**
 * Execute the script and return the result as a string
 *
 * @param resource resource to display//from   www.j  av a  2s.co  m
 * @param context
 * @return the rendered resource
 * @throws org.jahia.services.render.RenderException
 *
 */
public String execute(Resource resource, RenderContext context) throws RenderException {
    final View view = getView();
    if (view == null) {
        throw new RenderException("View not found for : " + resource);
    } else {
        if (logger.isDebugEnabled()) {
            logger.debug("View '" + view + "' resolved for resource: " + resource);
        }
    }

    String identifier;
    try {
        identifier = resource.getNode().getIdentifier();
    } catch (RepositoryException e) {
        throw new RenderException(e);
    }
    String identifierNoDashes = StringUtils.replace(identifier, "-", "_");
    if (!view.getKey().equals("default")) {
        identifierNoDashes += "__" + view.getKey();
    }

    HttpServletRequest request;
    HttpServletResponse response = context.getResponse();

    @SuppressWarnings("unchecked")
    final Map<String, List<String>> parameters = (Map<String, List<String>>) context.getRequest()
            .getAttribute("actionParameters");

    if (xssFilteringEnabled && parameters != null) {
        final Map<String, String[]> m = Maps.transformEntries(parameters,
                new Maps.EntryTransformer<String, List<String>, String[]>() {
                    @Override
                    public String[] transformEntry(@Nullable String key, @Nullable List<String> value) {
                        return value != null ? value.toArray(new String[value.size()]) : null;
                    }
                });
        request = new WebflowHttpServletRequestWrapper(context.getRequest(), m, identifierNoDashes);
    } else {
        request = new WebflowHttpServletRequestWrapper(context.getRequest(),
                new HashMap<>(context.getRequest().getParameterMap()), identifierNoDashes);
    }

    String s = (String) request.getSession().getAttribute("webflowResponse" + identifierNoDashes);
    if (s != null) {
        request.getSession().removeAttribute("webflowResponse" + identifierNoDashes);
        return s;
    }

    // skip aggregation for potentials fragments under the webflow
    boolean aggregationSkippedForWebflow = false;
    if (!AggregateFilter.skipAggregation(context.getRequest())) {
        aggregationSkippedForWebflow = true;
        context.getRequest().setAttribute(AggregateFilter.SKIP_AGGREGATION, true);
    }

    flowPath = MODULE_PREFIX_PATTERN.matcher(view.getPath()).replaceFirst("");

    RequestDispatcher rd = request.getRequestDispatcher("/flow/" + flowPath);

    Object oldModule = request.getAttribute("currentModule");
    request.setAttribute("currentModule", view.getModule());

    if (logger.isDebugEnabled()) {
        dumpRequestAttributes(request);
    }

    StringResponseWrapper responseWrapper = new StringResponseWrapper(response);

    try {
        FlowDefinitionRegistry reg = ((FlowDefinitionRegistry) view.getModule().getContext()
                .getBean("jahiaFlowRegistry"));
        final GenericApplicationContext applicationContext = (GenericApplicationContext) reg
                .getFlowDefinition(flowPath).getApplicationContext();
        applicationContext.setClassLoader(view.getModule().getClassLoader());
        applicationContext.setResourceLoader(new ResourceLoader() {
            @Override
            public org.springframework.core.io.Resource getResource(String location) {
                return applicationContext.getParent().getResource("/" + flowPath + "/" + location);
            }

            @Override
            public ClassLoader getClassLoader() {
                return view.getModule().getClassLoader();
            }
        });

        rd.include(request, responseWrapper);

        String redirect = responseWrapper.getRedirect();
        if (redirect != null && context.getRedirect() == null) {
            // if we have an absolute redirect, set it and move on
            if (redirect.startsWith("http:/") || redirect.startsWith("https://")) {
                context.setRedirect(responseWrapper.getRedirect());
            } else {
                while (redirect != null) {
                    final String qs = StringUtils.substringAfter(responseWrapper.getRedirect(), "?");
                    final Map<String, String[]> params = new HashMap<String, String[]>();
                    if (!StringUtils.isEmpty(qs)) {
                        params.put("webflowexecution" + identifierNoDashes, new String[] { StringUtils
                                .substringAfterLast(qs, "webflowexecution" + identifierNoDashes + "=") });
                    }
                    HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request) {
                        @Override
                        public String getMethod() {
                            return "GET";
                        }

                        @SuppressWarnings("rawtypes")
                        @Override
                        public Map getParameterMap() {
                            return params;
                        }

                        @Override
                        public String getParameter(String name) {
                            return params.containsKey(name) ? params.get(name)[0] : null;
                        }

                        @Override
                        public Enumeration getParameterNames() {
                            return new Vector(params.keySet()).elements();
                        }

                        @Override
                        public String[] getParameterValues(String name) {
                            return params.get(name);
                        }

                        @Override
                        public Object getAttribute(String name) {
                            if (WebUtils.FORWARD_QUERY_STRING_ATTRIBUTE.equals(name)) {
                                return qs;
                            }
                            return super.getAttribute(name);
                        }

                        @Override
                        public String getQueryString() {
                            return qs;
                        }
                    };
                    rd = requestWrapper.getRequestDispatcher("/flow/" + flowPath + "?" + qs);
                    responseWrapper = new StringResponseWrapper(response);
                    rd.include(requestWrapper, responseWrapper);

                    String oldRedirect = redirect;
                    redirect = responseWrapper.getRedirect();
                    if (redirect != null) {
                        // if we have an absolute redirect, exit the loop
                        if (redirect.startsWith("http://") || redirect.startsWith("https://")) {
                            context.setRedirect(redirect);
                            break;
                        }
                    } else if (request.getMethod().equals("POST")) {
                        // set the redirect to the last non-null one
                        request.getSession().setAttribute("webflowResponse" + identifierNoDashes,
                                responseWrapper.getString());
                        context.setRedirect(oldRedirect);
                    }
                }
            }
        }
    } catch (ServletException e) {
        throw new RenderException(e.getRootCause() != null ? e.getRootCause() : e);
    } catch (IOException e) {
        throw new RenderException(e);
    } finally {
        request.setAttribute("currentModule", oldModule);
    }
    try {
        if (aggregationSkippedForWebflow) {
            request.removeAttribute(AggregateFilter.SKIP_AGGREGATION);
        }
        return responseWrapper.getString();
    } catch (IOException e) {
        throw new RenderException(e);
    }
}

From source file:org.kuali.hr.TestAutoLoginFilter.java

/** {@inheritDoc} */
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    String username = StringUtils.isBlank(OVERRIDE_ID) ? filterConfig.getInitParameter(USER_PARAM_NAME)
            : OVERRIDE_ID;/*from   ww  w .  j a va 2 s .  co  m*/
    if (username == null) {
        throw new IllegalStateException("the " + USER_PARAM_NAME + " param is not set");
    }

    chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
        @Override
        public String getRemoteUser() {
            String username = TestAutoLoginFilter.OVERRIDE_ID;
            if (StringUtils.isBlank(username)) {
                username = TestAutoLoginFilter.this.filterConfig.getInitParameter(USER_PARAM_NAME);
            }
            return username;
        }
    }, response);
}

From source file:org.kuali.kpme.core.KPMELoginFilter.java

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest hsRequest = (HttpServletRequest) request;
    if (isTestMode()) {
        hsRequest = new HttpServletRequestWrapper(hsRequest) {
            public String getRemoteUser() {
                return TEST_ID;
            }/*  ww  w .  j a  v a 2  s.  c o  m*/
        };
        chain.doFilter(hsRequest, response);
    } else {
        applyRedirectHeader(request, response);
        dummyLoginFilter.doFilter(request, response, chain);
    }
}