Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure request is set for ContextScopeListener in EE10 #11518

Merged
merged 4 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,12 @@ public void run(Runnable runnable, Request request)
@Override
public void execute(Runnable runnable)
{
getServer().getContext().execute(() -> run(runnable));
execute(runnable, null);
}

public void execute(Runnable runnable, Request request)
{
getServer().getContext().execute(() -> run(runnable, request));
}

protected DecoratedObjectFactory getDecoratedObjectFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.thread.Scheduler;

public class AsyncContextEvent extends AsyncEvent implements Runnable
{
private final ServletContext _servletContext;
private final ContextHandler.ScopedContext _context;
private final ServletContextHandler.ServletScopedContext _context;
private final AsyncContextState _asyncContext;
private final HttpURI _baseURI;
private final ServletChannelState _state;
Expand All @@ -35,7 +34,7 @@ public class AsyncContextEvent extends AsyncEvent implements Runnable
private volatile Scheduler.Task _timeoutTask;
private Throwable _throwable;

public AsyncContextEvent(ContextHandler.ScopedContext context, AsyncContextState asyncContext, ServletChannelState state, ServletRequest request, ServletResponse response)
public AsyncContextEvent(ServletContextHandler.ServletScopedContext context, AsyncContextState asyncContext, ServletChannelState state, ServletRequest request, ServletResponse response)
{
super(null, request, response, null);
_context = context;
Expand Down Expand Up @@ -76,7 +75,7 @@ public ServletContext getServletContext()
return _dispatchContext == null ? _servletContext : _dispatchContext;
}

public ContextHandler.ScopedContext getContext()
public ServletContextHandler.ServletScopedContext getContext()
{
return _context;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public void start(final Runnable task)
{
task.run();
}
});
}, _state.getServletChannel().getRequest());
}

public void reset()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,11 @@ protected void execute(Runnable task)
_context.execute(task);
}

protected void execute(Runnable task, Request request)
{
_context.execute(task, request);
}

/**
* If a write or similar operation to this channel fails,
* then this method should be called.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ public void upgrade()

protected void scheduleDispatch()
{
_servletChannel.execute(_servletChannel::handle);
_servletChannel.execute(_servletChannel::handle, _servletChannel.getRequest());
}

protected void cancelTimeout()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.ee10.servlet;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

import jakarta.servlet.AsyncContext;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class ContextScopeListenerTest
{
private Server _server;
private ServerConnector _connector;
private HttpClient _client;
private final List<String> _history = new CopyOnWriteArrayList<>();
private ServletContextHandler _contextHandler;

@BeforeEach
public void before() throws Exception
{
_server = new Server();
_connector = new ServerConnector(_server);
_server.addConnector(_connector);

_contextHandler = new ServletContextHandler();
_server.setHandler(_contextHandler);
_server.start();

_client = new HttpClient();
_client.start();
}

@AfterEach
public void after() throws Exception
{
_client.stop();
_server.stop();
}

@Test
public void testAsyncServlet() throws Exception
{
_contextHandler.addServlet(new ServletHolder(new HttpServlet()
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
{
if (req.getDispatcherType() == DispatcherType.ASYNC)
{
_history.add("asyncDispatch");
return;
}

_history.add("doGet");
AsyncContext asyncContext = req.startAsync();
asyncContext.start(() ->
{
_history.add("asyncRunnable");
asyncContext.dispatch("/dispatch");
});
}
}), "/");

_contextHandler.addEventListener(new ContextHandler.ContextScopeListener()
{
// Use a lock to prevent the async thread running the listener concurrently.
private final ReentrantLock _lock = new ReentrantLock();

@Override
public void enterScope(Context context, Request request)
{
_lock.lock();
String pathInContext = (request == null) ? "null" : Request.getPathInContext(request);
_history.add("enterScope " + pathInContext);
}

@Override
public void exitScope(Context context, Request request)
{
String pathInContext = (request == null) ? "null" : Request.getPathInContext(request);
_history.add("exitScope " + pathInContext);
_lock.unlock();
}
});

URI uri = URI.create("http://localhost:" + _connector.getLocalPort() + "/initialPath");
ContentResponse response = _client.GET(uri);
assertThat(response.getStatus(), equalTo(HttpStatus.OK_200));
assertHistory(
"enterScope /initialPath",
"doGet",
"exitScope /initialPath",
"enterScope /initialPath",
"asyncRunnable",
"exitScope /initialPath",
"enterScope /initialPath",
"asyncDispatch",
"exitScope /initialPath"
);
}

private void assertHistory(String... values)
{
assertThat(_history, equalTo(Arrays.asList(values)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.ee9.servlet;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

import jakarta.servlet.AsyncContext;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.URIUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

public class ContextScopeListenerTest
{
private Server _server;
private ServerConnector _connector;
private HttpClient _client;
private final List<String> _history = new CopyOnWriteArrayList<>();
private ServletContextHandler _contextHandler;

@BeforeEach
public void before() throws Exception
{
_server = new Server();
_connector = new ServerConnector(_server);
_server.addConnector(_connector);

_contextHandler = new ServletContextHandler();
_server.setHandler(_contextHandler);
_server.start();

_client = new HttpClient();
_client.start();
}

@AfterEach
public void after() throws Exception
{
_client.stop();
_server.stop();
}

@Test
public void testAsyncServlet() throws Exception
{
_contextHandler.addServlet(new ServletHolder(new HttpServlet()
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
{
if (req.getDispatcherType() == DispatcherType.ASYNC)
{
_history.add("asyncDispatch");
return;
}

_history.add("doGet");
AsyncContext asyncContext = req.startAsync();
asyncContext.start(() ->
{
_history.add("asyncRunnable");
asyncContext.dispatch("/dispatch");
});
}
}), "/");

_contextHandler.addEventListener(new ContextHandler.ContextScopeListener()
{
// Use a lock to prevent the async thread running the listener concurrently.
private final ReentrantLock _lock = new ReentrantLock();

@Override
public void enterScope(ContextHandler.APIContext context, org.eclipse.jetty.ee9.nested.Request request, Object reason)
{
_lock.lock();
String pathInContext = (request == null) ? "null" : URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
_history.add("enterScope " + pathInContext);
}

@Override
public void exitScope(ContextHandler.APIContext context, org.eclipse.jetty.ee9.nested.Request request)
{
String pathInContext = (request == null) ? "null" : URIUtil.addPaths(request.getServletPath(), request.getPathInfo());
_history.add("exitScope " + pathInContext);
_lock.unlock();
}
});

URI uri = URI.create("http://localhost:" + _connector.getLocalPort() + "/initialPath");
ContentResponse response = _client.GET(uri);
assertThat(response.getStatus(), equalTo(HttpStatus.OK_200));
assertHistory(
"enterScope /initialPath",
"doGet",
"exitScope /initialPath",
"enterScope /initialPath",
"asyncRunnable",
"exitScope /initialPath",
"enterScope /dispatch",
"asyncDispatch",
"exitScope /dispatch",
"enterScope /dispatch",
"exitScope /dispatch"
);
}

private void assertHistory(String... values)
{
assertThat(_history, equalTo(Arrays.asList(values)));
}
}
Loading