Merged branch 'jetty-10.0.x' into 'jetty-10.0.x-3951-http2_demand'.

This commit is contained in:
Simone Bordet 2019-11-13 15:12:40 +01:00
commit 6577a8b9dc
73 changed files with 638 additions and 405 deletions

View File

@ -27,7 +27,6 @@ import java.security.CodeSource;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -615,9 +614,9 @@ public class AntWebAppContext extends WebAppContext
TaskLog.logWithTimestamp("Stopping web application " + this);
Thread.currentThread().sleep(500L);
super.doStop();
//remove all filters, servlets and listeners. They will be recreated
//either via application of a context xml file or web.xml or annotation or servlet api
setEventListeners(new EventListener[0]);
// remove all filters and servlets. They will be recreated
// either via application of a context xml file or web.xml or annotation or servlet api.
// Event listeners are reset in ContextHandler.doStop()
getServletHandler().setFilters(new FilterHolder[0]);
getServletHandler().setFilterMappings(new FilterMapping[0]);
getServletHandler().setServlets(new ServletHolder[0]);

View File

@ -22,6 +22,7 @@ import java.io.EOFException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.HttpReceiver;
import org.eclipse.jetty.client.HttpResponse;
@ -50,8 +51,13 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
super(channel);
HttpClient httpClient = channel.getHttpDestination().getHttpClient();
parser = new HttpParser(this, -1, httpClient.getHttpCompliance());
parser.setHeaderCacheSize(((HttpClientTransportOverHTTP)httpClient.getTransport()).getHeaderCacheSize());
parser.setHeaderCacheCaseSensitive(((HttpClientTransportOverHTTP)httpClient.getTransport()).isHeaderCacheCaseSensitive());
HttpClientTransport transport = httpClient.getTransport();
if (transport instanceof HttpClientTransportOverHTTP)
{
HttpClientTransportOverHTTP httpTransport = (HttpClientTransportOverHTTP)transport;
parser.setHeaderCacheSize(httpTransport.getHeaderCacheSize());
parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive());
}
}
@Override

View File

@ -39,7 +39,6 @@ public class HttpClientJMXTest
String name = "foo";
HttpClient httpClient = new HttpClient();
httpClient.setName(name);
httpClient.start();
try
{
@ -47,6 +46,7 @@ public class HttpClientJMXTest
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
// Adding MBeanContainer as a bean will trigger the registration of MBeans.
httpClient.addBean(mbeanContainer);
httpClient.start();
String domain = HttpClient.class.getPackage().getName();
ObjectName pattern = new ObjectName(domain + ":type=" + HttpClient.class.getSimpleName().toLowerCase(Locale.ENGLISH) + ",*");

View File

@ -6,7 +6,7 @@
<!-- ================================================================ -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.setuid.SetUIDListener">
<Set name="startServerAsPrivileged"><Property name="jetty.setuid.startServerAsPrivileged" default="false"/></Set>

View File

@ -5,7 +5,7 @@
<!-- Mixin the Start FileNoticeLifeCycleListener -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.util.component.FileNoticeLifeCycleListener">
<Arg><Property name="jetty.state" default="./jetty.state"/></Arg>

View File

@ -73,7 +73,7 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
final HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, executor, endPoint,
parser, session, client.getInputBufferSize(), promise, listener);
connection.addListener(connectionListener);
connection.addEventListener(connectionListener);
return customize(connection, context);
}

View File

@ -261,7 +261,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne
endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener);
connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers());
connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers());
connection.addListener(sessionContainer);
connection.addEventListener(sessionContainer);
return configure(connection, connector, endPoint);
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.io;
import java.util.EventListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
@ -57,13 +58,14 @@ public abstract class AbstractConnection implements Connection
}
@Override
public void addListener(Listener listener)
public void addEventListener(EventListener listener)
{
_listeners.add(listener);
if (listener instanceof Listener)
_listeners.add((Listener)listener);
}
@Override
public void removeListener(Listener listener)
public void removeEventListener(EventListener listener)
{
_listeners.remove(listener);
}

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.io;
import java.io.IOException;
import java.util.EventListener;
import java.util.List;
import java.util.Map;
@ -43,7 +44,7 @@ public interface ClientConnectionFactory
{
ContainerLifeCycle client = (ContainerLifeCycle)context.get(CLIENT_CONTEXT_KEY);
if (client != null)
client.getBeans(Connection.Listener.class).forEach(connection::addListener);
client.getBeans(EventListener.class).forEach(connection::addEventListener);
return connection;
}

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.io;
import java.io.Closeable;
import java.nio.ByteBuffer;
import java.util.EventListener;
import org.eclipse.jetty.util.component.Container;
@ -38,14 +39,14 @@ public interface Connection extends Closeable
*
* @param listener the listener to add
*/
public void addListener(Listener listener);
public void addEventListener(EventListener listener);
/**
* <p>Removes a listener of connection events.</p>
*
* @param listener the listener to remove
*/
public void removeListener(Listener listener);
public void removeEventListener(EventListener listener);
/**
* <p>Callback method invoked when this connection is opened.</p>
@ -133,7 +134,7 @@ public interface Connection extends Closeable
* the Connector or ConnectionFactory are added as listeners to all new connections
* </p>
*/
public interface Listener
public interface Listener extends EventListener
{
public void onOpened(Connection connection);

View File

@ -38,6 +38,7 @@ import java.util.function.IntUnaryOperator;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
@ -389,31 +390,37 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
*/
public abstract Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) throws IOException;
public void addEventListener(EventListener listener)
/**
* @param listener An EventListener
* @see AcceptListener
* @see Container#addEventListener(EventListener)
*/
@Override
public boolean addEventListener(EventListener listener)
{
if (isRunning())
throw new IllegalStateException(this.toString());
if (listener instanceof AcceptListener)
addAcceptListener(AcceptListener.class.cast(listener));
if (super.addEventListener(listener))
{
if (listener instanceof AcceptListener)
_acceptListeners.add((AcceptListener)listener);
return true;
}
return false;
}
public void removeEventListener(EventListener listener)
@Override
public boolean removeEventListener(EventListener listener)
{
if (isRunning())
throw new IllegalStateException(this.toString());
if (listener instanceof AcceptListener)
removeAcceptListener(AcceptListener.class.cast(listener));
}
public void addAcceptListener(AcceptListener listener)
{
if (!_acceptListeners.contains(listener))
_acceptListeners.add(listener);
}
public void removeAcceptListener(AcceptListener listener)
{
_acceptListeners.remove(listener);
if (super.removeEventListener(listener))
{
if (listener instanceof AcceptListener)
_acceptListeners.remove(listener);
return true;
}
return false;
}
protected void onAccepting(SelectableChannel channel)
@ -461,12 +468,16 @@ public abstract class SelectorManager extends ContainerLifeCycle implements Dump
}
}
public interface SelectorManagerListener extends EventListener
{
}
/**
* <p>A listener for accept events.</p>
* <p>This listener is called from either the selector or acceptor thread
* and implementations must be non blocking and fast.</p>
*/
public interface AcceptListener extends EventListener
public interface AcceptListener extends SelectorManagerListener
{
/**
* Called immediately after a new SelectableChannel is accepted, but

View File

@ -571,7 +571,8 @@ public abstract class WriteFlusher
}
/**
* <p>A listener of {@link WriteFlusher} events.</p>
* <p>A listener of {@link WriteFlusher} events.
* If implemented by a Connection class, the {@link #onFlushed(long)} event will be delivered to it.</p>
*/
public interface Listener
{

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -30,7 +30,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
</New>

View File

@ -3,7 +3,7 @@
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Ref id="httpConnector">
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -24,7 +24,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerConnectorListener">
<Set name="fileName"><Property name="jetty.port.file" default="port.txt"/></Set>

View File

@ -23,7 +23,6 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -356,11 +355,9 @@ public class JettyWebAppContext extends WebAppContext
super.doStop();
// remove all listeners, servlets and filters. This is because we will
// re-apply
// any context xml file, which means they would potentially be added
// multiple times.
setEventListeners(new EventListener[0]);
// remove all servlets and filters. This is because we will
// re-appy any context xml file, which means they would potentially be
// added multiple times.
getServletHandler().setFilters(new FilterHolder[0]);
getServletHandler().setFilterMappings(new FilterMapping[0]);
getServletHandler().setServlets(new ServletHolder[0]);

View File

@ -2,7 +2,7 @@
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.maven.plugin.ServerListener">
<Set name="tokenFile"><Property name="jetty.token.file"/></Set>

View File

@ -46,7 +46,7 @@ public class Activator implements BundleActivator
{
//For test purposes, use a random port
Server server = new Server(0);
server.getConnectors()[0].addLifeCycleListener(new AbstractLifeCycleListener()
server.getConnectors()[0].addEventListener(new AbstractLifeCycleListener()
{
/**

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.context.service.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.webapp.service.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.annotations.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.bundle.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.javax.websocket.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.jsp.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.websocket.port</Set>

View File

@ -31,7 +31,7 @@
</Item>
</Array>
</Arg>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">foo.foo</Set>

View File

@ -16,7 +16,7 @@
</New>
</Arg>
</Call>
<Call name="addLifeCycleListener">
<Call name="addEventListener">
<Arg>
<New class="org.eclipse.jetty.osgi.boot.utils.ServerConnectorListener">
<Set name="sysPropertyName">boot.https.port</Set>

View File

@ -125,6 +125,31 @@ public class SpnegoAuthenticatorTest
assertEquals(HttpHeader.NEGOTIATE.asString(), res.getHeader(HttpHeader.WWW_AUTHENTICATE.asString()));
assertEquals(HttpServletResponse.SC_UNAUTHORIZED, res.getStatus());
}
class MockConnector extends AbstractConnector
{
public MockConnector()
{
super(new Server() , null, null, null, 0);
}
@Override
protected void accept(int acceptorID) throws IOException, InterruptedException
{
}
@Override
public Object getTransport()
{
return null;
}
@Override
public String dumpSelf()
{
return null;
}
}
}
class MockConnector extends AbstractConnector

View File

@ -23,7 +23,6 @@ import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@ -32,17 +31,7 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
/**
* <p>Provides the common handling for {@link ConnectionFactory} implementations including:</p>
* <ul>
* <li>Protocol identification</li>
* <li>Configuration of new Connections:
* <ul>
* <li>Setting inputbuffer size</li>
* <li>Calling {@link Connection#addListener(Connection.Listener)} for all
* Connection.Listener instances found as beans on the {@link Connector}
* and this {@link ConnectionFactory}</li>
* </ul>
* </ul>
* <p>Provides the common handling for {@link ConnectionFactory} implementations.</p>
*/
@ManagedObject
public abstract class AbstractConnectionFactory extends ContainerLifeCycle implements ConnectionFactory
@ -92,19 +81,10 @@ public abstract class AbstractConnectionFactory extends ContainerLifeCycle imple
connection.setInputBufferSize(getInputBufferSize());
// Add Connection.Listeners from Connector
if (connector instanceof ContainerLifeCycle)
{
ContainerLifeCycle aggregate = (ContainerLifeCycle)connector;
for (Connection.Listener listener : aggregate.getBeans(Connection.Listener.class))
{
connection.addListener(listener);
}
}
connector.getEventListeners().forEach(connection::addEventListener);
// Add Connection.Listeners from this factory
for (Connection.Listener listener : getBeans(Connection.Listener.class))
{
connection.addListener(listener);
}
getEventListeners().forEach(connection::addEventListener);
return connection;
}

View File

@ -104,7 +104,6 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
_state = new HttpChannelState(this);
_request = new Request(this, newHttpInput(_state));
_response = new Response(this, newHttpOutput());
_executor = connector.getServer().getThreadPool();
_requestLog = connector.getServer().getRequestLog();
_combinedListener = (connector instanceof AbstractConnector)
@ -1224,15 +1223,16 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
public void succeeded()
{
_written += _length;
if (_complete)
_response.getHttpOutput().closed();
super.succeeded();
if (_commit)
_combinedListener.onResponseCommit(_request);
if (_length > 0)
_combinedListener.onResponseContent(_request, _content);
if (_complete && _state.completeResponse())
{
_response.getHttpOutput().closed();
_combinedListener.onResponseEnd(_request);
}
super.succeeded();
}
@Override

View File

@ -386,22 +386,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
if (!_state.compareAndSet(state, State.CLOSED))
break;
// Just make sure write and output stream really are closed
try
{
_channel.getResponse().closeOutput();
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug(x);
abort(x);
}
finally
{
releaseBuffer();
}
// Return even if an exception is thrown by closeOutput().
releaseBuffer();
return;
}
}

View File

@ -223,10 +223,8 @@ public class ServerConnector extends AbstractNetworkConnector
@Override
protected void doStart() throws Exception
{
for (EventListener l : getBeans(EventListener.class))
{
for (EventListener l : getBeans(SelectorManager.SelectorManagerListener.class))
_manager.addEventListener(l);
}
super.doStart();

View File

@ -198,7 +198,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
private boolean _compactPath = false;
private boolean _usingSecurityManager = System.getSecurityManager() != null;
private final List<EventListener> _eventListeners = new CopyOnWriteArrayList<>();
private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>();
private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>();
private final List<ServletContextListener> _destroySerletContextListeners = new ArrayList<>();
@ -206,7 +205,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
private final List<ServletRequestListener> _servletRequestListeners = new CopyOnWriteArrayList<>();
private final List<ServletRequestAttributeListener> _servletRequestAttributeListeners = new CopyOnWriteArrayList<>();
private final List<ContextScopeListener> _contextListeners = new CopyOnWriteArrayList<>();
private final List<EventListener> _durableListeners = new CopyOnWriteArrayList<>();
private final Set<EventListener> _durableListeners = new HashSet<>();
private String[] _protectedTargets;
private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
@ -260,7 +259,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
dumpObjects(out, indent,
new ClassLoaderDump(getClassLoader()),
new DumpableCollection("eventListeners " + this, _eventListeners),
new DumpableCollection("handler attributes " + this, ((AttributesMap)getAttributes()).getAttributeEntrySet()),
new DumpableCollection("context attributes " + this, ((Context)getServletContext()).getAttributeEntrySet()),
new DumpableCollection("initparams " + this, getInitParams().entrySet()));
@ -606,101 +604,68 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return _displayName;
}
public EventListener[] getEventListeners()
{
return _eventListeners.toArray(new EventListener[_eventListeners.size()]);
}
/**
* Set the context event listeners.
*
* @param eventListeners the event listeners
* @see ServletContextListener
* @see ServletContextAttributeListener
* @see ServletRequestListener
* @see ServletRequestAttributeListener
*/
public void setEventListeners(EventListener[] eventListeners)
{
_contextListeners.clear();
_servletContextListeners.clear();
_servletContextAttributeListeners.clear();
_servletRequestListeners.clear();
_servletRequestAttributeListeners.clear();
_eventListeners.clear();
if (eventListeners != null)
for (EventListener listener : eventListeners)
{
addEventListener(listener);
}
}
/**
* Add a context event listeners.
*
* @param listener the event listener to add
* @return true if the listener was added
* @see ContextScopeListener
* @see ServletContextListener
* @see ServletContextAttributeListener
* @see ServletRequestListener
* @see ServletRequestAttributeListener
*/
public void addEventListener(EventListener listener)
@Override
public boolean addEventListener(EventListener listener)
{
_eventListeners.add(listener);
if (!(isStarted() || isStarting()))
if (super.addEventListener(listener))
{
_durableListeners.add(listener);
if (listener instanceof ContextScopeListener)
{
_contextListeners.add((ContextScopeListener)listener);
if (__context.get() != null)
((ContextScopeListener)listener).enterScope(__context.get(), null, "Listener registered");
}
if (listener instanceof ServletContextListener)
_servletContextListeners.add((ServletContextListener)listener);
if (listener instanceof ServletContextAttributeListener)
_servletContextAttributeListeners.add((ServletContextAttributeListener)listener);
if (listener instanceof ServletRequestListener)
_servletRequestListeners.add((ServletRequestListener)listener);
if (listener instanceof ServletRequestAttributeListener)
_servletRequestAttributeListeners.add((ServletRequestAttributeListener)listener);
return true;
}
if (listener instanceof ContextScopeListener)
{
_contextListeners.add((ContextScopeListener)listener);
if (__context.get() != null)
((ContextScopeListener)listener).enterScope(__context.get(), null, "Listener registered");
}
if (listener instanceof ServletContextListener)
_servletContextListeners.add((ServletContextListener)listener);
if (listener instanceof ServletContextAttributeListener)
_servletContextAttributeListeners.add((ServletContextAttributeListener)listener);
if (listener instanceof ServletRequestListener)
_servletRequestListeners.add((ServletRequestListener)listener);
if (listener instanceof ServletRequestAttributeListener)
_servletRequestAttributeListeners.add((ServletRequestAttributeListener)listener);
return false;
}
/**
* Remove a context event listeners.
*
* @param listener the event listener to remove
* @see ServletContextListener
* @see ServletContextAttributeListener
* @see ServletRequestListener
* @see ServletRequestAttributeListener
*/
public void removeEventListener(EventListener listener)
@Override
public boolean removeEventListener(EventListener listener)
{
_eventListeners.remove(listener);
if (super.removeEventListener(listener))
{
if (listener instanceof ContextScopeListener)
_contextListeners.remove(listener);
if (listener instanceof ContextScopeListener)
_contextListeners.remove(listener);
if (listener instanceof ServletContextListener)
_servletContextListeners.remove(listener);
if (listener instanceof ServletContextListener)
_servletContextListeners.remove(listener);
if (listener instanceof ServletContextAttributeListener)
_servletContextAttributeListeners.remove(listener);
if (listener instanceof ServletContextAttributeListener)
_servletContextAttributeListeners.remove(listener);
if (listener instanceof ServletRequestListener)
_servletRequestListeners.remove(listener);
if (listener instanceof ServletRequestListener)
_servletRequestListeners.remove(listener);
if (listener instanceof ServletRequestAttributeListener)
_servletRequestAttributeListeners.remove(listener);
if (listener instanceof ServletRequestAttributeListener)
_servletRequestAttributeListeners.remove(listener);
return true;
}
return false;
}
/**
@ -720,7 +685,11 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
public boolean isDurableListener(EventListener listener)
{
return _durableListeners.contains(listener);
// The durable listeners are those set when the context is started
if (isStarted())
return _durableListeners.contains(listener);
// If we are not yet started then all set listeners are durable
return getEventListeners().contains(listener);
}
/**
@ -800,6 +769,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (_mimeTypes == null)
_mimeTypes = new MimeTypes();
_durableListeners.addAll(getEventListeners());
try
{
// Set the classloader, context and enter scope
@ -975,7 +946,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
stopContext();
// retain only durable listeners
setEventListeners(_durableListeners.toArray(new EventListener[_durableListeners.size()]));
setEventListeners(_durableListeners);
_durableListeners.clear();
if (_errorHandler != null)

View File

@ -261,33 +261,26 @@ public class SessionHandler extends ScopedHandler
* Individual SessionManagers implementations may accept arbitrary listener types,
* but they are expected to at least handle HttpSessionActivationListener,
* HttpSessionAttributeListener, HttpSessionBindingListener and HttpSessionListener.
* @return true if the listener was added
* @see #removeEventListener(EventListener)
* @see HttpSessionAttributeListener
* @see HttpSessionListener
* @see HttpSessionIdListener
*/
public void addEventListener(EventListener listener)
@Override
public boolean addEventListener(EventListener listener)
{
if (listener instanceof HttpSessionAttributeListener)
_sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
if (listener instanceof HttpSessionListener)
_sessionListeners.add((HttpSessionListener)listener);
if (listener instanceof HttpSessionIdListener)
_sessionIdListeners.add((HttpSessionIdListener)listener);
addBean(listener, false);
}
/**
* Removes all event listeners for session-related events.
*
* @see #removeEventListener(EventListener)
*/
public void clearEventListeners()
{
for (EventListener e : getBeans(EventListener.class))
if (super.addEventListener(listener))
{
removeBean(e);
if (listener instanceof HttpSessionAttributeListener)
_sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
if (listener instanceof HttpSessionListener)
_sessionListeners.add((HttpSessionListener)listener);
if (listener instanceof HttpSessionIdListener)
_sessionIdListeners.add((HttpSessionIdListener)listener);
return true;
}
_sessionAttributeListeners.clear();
_sessionListeners.clear();
_sessionIdListeners.clear();
return false;
}
/**
@ -785,21 +778,20 @@ public class SessionHandler extends ScopedHandler
}
}
/**
* Removes an event listener for for session-related events.
*
* @param listener the session event listener to remove
* @see #addEventListener(EventListener)
*/
public void removeEventListener(EventListener listener)
@Override
public boolean removeEventListener(EventListener listener)
{
if (listener instanceof HttpSessionAttributeListener)
_sessionAttributeListeners.remove(listener);
if (listener instanceof HttpSessionListener)
_sessionListeners.remove(listener);
if (listener instanceof HttpSessionIdListener)
_sessionIdListeners.remove(listener);
removeBean(listener);
if (super.removeEventListener(listener))
{
if (listener instanceof HttpSessionAttributeListener)
_sessionAttributeListeners.remove(listener);
if (listener instanceof HttpSessionListener)
_sessionListeners.remove(listener);
if (listener instanceof HttpSessionIdListener)
_sessionIdListeners.remove(listener);
return true;
}
return false;
}
/**

View File

@ -0,0 +1,112 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.junit.jupiter.api.BeforeEach;
/**
* Extended Server Tester.
*/
public class DelayedServerTest extends HttpServerTestBase
{
@BeforeEach
public void init() throws Exception
{
startServer(new ServerConnector(_server, new HttpConnectionFactory()
{
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
return configure(new DelayedHttpConnection(getHttpConfiguration(), connector, endPoint), connector, endPoint);
}
}));
}
private static class DelayedHttpConnection extends HttpConnection
{
public DelayedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
{
super(config, connector, endPoint, false);
}
@Override
public void send(MetaData.Request request, MetaData.Response response, ByteBuffer content, boolean lastContent, Callback callback)
{
DelayedCallback delay = new DelayedCallback(callback, getServer().getThreadPool());
super.send(request, response, content, lastContent, delay);
}
}
private static class DelayedCallback extends Callback.Nested
{
final ThreadPool pool;
public DelayedCallback(Callback callback, ThreadPool threadPool)
{
super(callback);
pool = threadPool;
}
@Override
public void succeeded()
{
pool.execute(()->
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
}
finally
{
super.succeeded();
}
});
}
@Override
public void failed(Throwable x)
{
pool.execute(()->
{
try
{
Thread.sleep(20);
}
catch (InterruptedException e)
{
}
finally
{
super.failed(x);
}
});
}
}
}

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
@ -73,11 +72,6 @@ public class ExtendedServerTest extends HttpServerTestBase
{
private volatile long _lastSelected;
public ExtendedEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler)
{
super(channel, selector, key, scheduler);
}
public ExtendedEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler)
{
super(channel, selector, key, scheduler);

View File

@ -28,10 +28,12 @@ import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.Exchanger;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
@ -42,6 +44,8 @@ import org.eclipse.jetty.http.tools.HttpTester;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.AbstractLogger;
import org.eclipse.jetty.util.log.Log;
@ -1816,4 +1820,53 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
assertThat(client.getInputStream().read(), is(-1));
}
}
@Test
public void testSendAsyncContent() throws Exception
{
int size = 64 * 1024;
configureServer(new SendAsyncContentHandler(size));
try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()))
{
OutputStream os = client.getOutputStream();
os.write(("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n").getBytes(StandardCharsets.ISO_8859_1));
os.flush();
HttpTester.Response response = HttpTester.parseResponse(client.getInputStream());
assertThat(response.getStatus(), is(200));
assertThat(response.getContentBytes().length, is(size));
// Try again to check previous request completed OK
os.write(("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n").getBytes(StandardCharsets.ISO_8859_1));
os.flush();
response = HttpTester.parseResponse(client.getInputStream());
assertThat(response.getStatus(), is(200));
assertThat(response.getContentBytes().length, is(size));
}
}
private class SendAsyncContentHandler extends AbstractHandler
{
final ByteBuffer content;
public SendAsyncContentHandler(int size)
{
content = BufferUtil.allocate(size);
Arrays.fill(content.array(),0,size,(byte)'X');
content.position(0);
content.limit(size);
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setStatus(200);
response.setContentType("application/unknown");
response.setContentLength(content.remaining());
AsyncContext async = request.startAsync();
((HttpOutput)response.getOutputStream()).sendContent(content.slice(), Callback.from(async::complete));
}
}
}

View File

@ -70,7 +70,7 @@ public class ShutdownHandlerTest
start(null);
CountDownLatch stopLatch = new CountDownLatch(1);
server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener()
server.addEventListener(new AbstractLifeCycle.AbstractLifeCycleListener()
{
@Override
public void lifeCycleStopped(LifeCycle event)

View File

@ -61,6 +61,7 @@ public class ListenerHolder extends BaseHolder<EventListener>
* just like ServletHolder and FilterHolder,
* the listener will not be introspected for
* annotations like Resource etc.
* @param listener The listener instance
*/
public void setListener(EventListener listener)
{

View File

@ -196,24 +196,33 @@ public class ServletContextHandler extends ContextHandler
/**
* Add EventListener
* Adds an EventListener to the list. @see org.eclipse.jetty.server.handler.ContextHandler#addEventListener().
* Also adds any listeners that are session related to the SessionHandler.
*
* @param listener the listener to add
* @return true if the listener was added
* @see HttpSessionAttributeListener
* @see HttpSessionActivationListener
* @see HttpSessionBindingListener
* @see HttpSessionListener
* @see HttpSessionIdListener
* @see ContextHandler#addEventListener(EventListener)
*/
@Override
public void addEventListener(EventListener listener)
public boolean addEventListener(EventListener listener)
{
super.addEventListener(listener);
if ((listener instanceof HttpSessionActivationListener) ||
(listener instanceof HttpSessionAttributeListener) ||
(listener instanceof HttpSessionBindingListener) ||
(listener instanceof HttpSessionListener) ||
(listener instanceof HttpSessionIdListener))
if (super.addEventListener(listener))
{
if (_sessionHandler != null)
_sessionHandler.addEventListener(listener);
if ((listener instanceof HttpSessionActivationListener) ||
(listener instanceof HttpSessionAttributeListener) ||
(listener instanceof HttpSessionBindingListener) ||
(listener instanceof HttpSessionListener) ||
(listener instanceof HttpSessionIdListener))
{
if (_sessionHandler != null)
_sessionHandler.addEventListener(listener);
}
return true;
}
return false;
}
@Override

View File

@ -670,7 +670,7 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
//Register a listener to delete tmp files that are created as a result of this
//servlet calling Request.getPart() or Request.getParts()
ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
if (!Arrays.asList(ch.getEventListeners()).contains(MultiPartCleanerListener.INSTANCE))
if (!ch.getEventListeners().contains(MultiPartCleanerListener.INSTANCE))
ch.addEventListener(MultiPartCleanerListener.INSTANCE);
}
}

View File

@ -29,7 +29,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@ -462,11 +461,9 @@ public class ServletContextHandlerTest
assertTrue(root.isProgrammaticListener(l.getListener()));
}
}
EventListener[] listeners = root.getEventListeners();
assertNotNull(listeners);
List<String> listenerClassNames = new ArrayList<>();
for (EventListener l : listeners)
for (EventListener l : root.getEventListeners())
listenerClassNames.add(l.getClass().getName());
assertTrue(listenerClassNames.contains("org.eclipse.jetty.servlet.ServletContextHandlerTest$MySCAListener"));

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.servlet;
import java.io.IOException;
import java.util.EnumSet;
import java.util.EventListener;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.servlet.DispatcherType;
@ -120,9 +121,19 @@ public class ServletLifeCycleTest
"Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener"));
// Listener added before start is not destroyed
EventListener[] listeners = context.getEventListeners();
assertThat(listeners.length, is(1));
assertThat(listeners[0].getClass(), is(TestListener2.class));
List<EventListener> listeners = context.getEventListeners();
assertThat(listeners.size(), is(1));
assertThat(listeners.get(0).getClass(), is(TestListener2.class));
server.start();
context.addEventListener(new EventListener() {});listeners = context.getEventListeners();
listeners = context.getEventListeners();
assertThat(listeners.size(), is(3));
server.stop();
listeners = context.getEventListeners();
assertThat(listeners.size(), is(1));
assertThat(listeners.get(0).getClass(), is(TestListener2.class));
}
public static class TestDecorator implements Decorator

View File

@ -19,10 +19,7 @@
package org.eclipse.jetty.util;
/**
* Interface for 3rd party libraries to decorate recently created objects in Jetty.
* <p>
* Most common use is weld/CDI.
* <p>
* Interface to decorate objects created by the {@link DecoratedObjectFactory}
*/
public interface Decorator
{

View File

@ -18,6 +18,9 @@
package org.eclipse.jetty.util.component;
import java.util.Collection;
import java.util.EventListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.util.Uptime;
@ -49,7 +52,7 @@ public abstract class AbstractLifeCycle implements LifeCycle
public static final String STARTED = State.STARTED.toString();
public static final String STOPPING = State.STOPPING.toString();
private final CopyOnWriteArrayList<LifeCycle.Listener> _listeners = new CopyOnWriteArrayList<LifeCycle.Listener>();
private final List<EventListener> _eventListener = new CopyOnWriteArrayList<>();
private final Object _lock = new Object();
private volatile State _state = State.STOPPED;
private long _stopTimeout = 30000;
@ -186,16 +189,39 @@ public abstract class AbstractLifeCycle implements LifeCycle
return _state == State.FAILED;
}
@Override
public void addLifeCycleListener(LifeCycle.Listener listener)
public List<EventListener> getEventListeners()
{
_listeners.add(listener);
return _eventListener;
}
public void setEventListeners(Collection<EventListener> eventListeners)
{
for (EventListener l : _eventListener)
{
if (!eventListeners.contains(l))
removeEventListener(l);
}
for (EventListener l : eventListeners)
{
if (!_eventListener.contains(l))
addEventListener(l);
}
}
@Override
public void removeLifeCycleListener(LifeCycle.Listener listener)
public boolean addEventListener(EventListener listener)
{
_listeners.remove(listener);
if (_eventListener.contains(listener))
return false;
_eventListener.add(listener);
return true;
}
@Override
public boolean removeEventListener(EventListener listener)
{
return _eventListener.remove(listener);
}
@ManagedAttribute(value = "Lifecycle State for this instance", readonly = true)
@ -226,8 +252,9 @@ public abstract class AbstractLifeCycle implements LifeCycle
_state = State.STARTED;
if (LOG.isDebugEnabled())
LOG.debug("STARTED @{}ms {}", Uptime.getUptime(), this);
for (Listener listener : _listeners)
listener.lifeCycleStarted(this);
for (EventListener listener : _eventListener)
if (listener instanceof Listener)
((Listener)listener).lifeCycleStarted(this);
}
}
@ -236,8 +263,9 @@ public abstract class AbstractLifeCycle implements LifeCycle
if (LOG.isDebugEnabled())
LOG.debug("STARTING {}", this);
_state = State.STARTING;
for (Listener listener : _listeners)
listener.lifeCycleStarting(this);
for (EventListener listener : _eventListener)
if (listener instanceof Listener)
((Listener)listener).lifeCycleStarting(this);
}
private void setStopping()
@ -245,8 +273,9 @@ public abstract class AbstractLifeCycle implements LifeCycle
if (LOG.isDebugEnabled())
LOG.debug("STOPPING {}", this);
_state = State.STOPPING;
for (Listener listener : _listeners)
listener.lifeCycleStopping(this);
for (EventListener listener : _eventListener)
if (listener instanceof Listener)
((Listener)listener).lifeCycleStopping(this);
}
private void setStopped()
@ -256,10 +285,9 @@ public abstract class AbstractLifeCycle implements LifeCycle
_state = State.STOPPED;
if (LOG.isDebugEnabled())
LOG.debug("STOPPED {}", this);
for (Listener listener : _listeners)
{
listener.lifeCycleStopped(this);
}
for (EventListener listener : _eventListener)
if (listener instanceof Listener)
((Listener)listener).lifeCycleStopped(this);
}
}
@ -268,9 +296,10 @@ public abstract class AbstractLifeCycle implements LifeCycle
_state = State.FAILED;
if (LOG.isDebugEnabled())
LOG.warn("FAILED " + this + ": " + th, th);
for (Listener listener : _listeners)
for (EventListener listener : _eventListener)
{
listener.lifeCycleFailure(this, th);
if (listener instanceof Listener)
((Listener)listener).lifeCycleFailure(this, th);
}
}

View File

@ -18,25 +18,28 @@
package org.eclipse.jetty.util.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.List;
/**
* A Container
*/
public interface Container
{
/**
* Add a bean. If the bean is-a {@link Listener}, then also do an implicit {@link #addEventListener(Listener)}.
* Add a bean. If the bean is-a {@link EventListener}, then also do an implicit {@link #addEventListener(EventListener)}.
*
* @param o the bean object to add
* @return true if the bean was added, false if it was already present
*/
public boolean addBean(Object o);
boolean addBean(Object o);
/**
* Adds the given bean, explicitly managing it or not.
*
* If the bean is-a {@link EventListener}, then also do an implicit {@link #addEventListener(EventListener)}.
* @param o The bean object to add
* @param managed whether to managed the lifecycle of the bean
* @return true if the bean was added, false if it was already present
@ -47,7 +50,7 @@ public interface Container
* @return the list of beans known to this aggregate
* @see #getBean(Class)
*/
public Collection<Object> getBeans();
Collection<Object> getBeans();
/**
* @param clazz the class of the beans
@ -56,39 +59,56 @@ public interface Container
* @see #getBeans()
* @see #getContainedBeans(Class)
*/
public <T> Collection<T> getBeans(Class<T> clazz);
<T> Collection<T> getBeans(Class<T> clazz);
/**
* @param clazz the class of the beans
* @param <T> the Bean type
* @return a list of beans of the given class (or subclass), which may be cached/shared.
* @see #getBeans()
* @see #getContainedBeans(Class)
*/
default <T> Collection<T> getCachedBeans(Class<T> clazz)
{
return getBeans(clazz);
}
/**
* @param clazz the class of the bean
* @param <T> the Bean type
* @return the first bean of a specific class (or subclass), or null if no such bean exist
*/
public <T> T getBean(Class<T> clazz);
<T> T getBean(Class<T> clazz);
/**
* Removes the given bean.
* If the bean is-a {@link Listener}, then also do an implicit {@link #removeEventListener(Listener)}.
* If the bean is-a {@link EventListener}, then also do an implicit {@link #removeEventListener(EventListener)}.
*
* @param o the bean to remove
* @return whether the bean was removed
*/
public boolean removeBean(Object o);
boolean removeBean(Object o);
/**
* Add an event listener.
*
* EventListeners added by this method are also added as beans.
* @param listener the listener to add
* @return true if the listener was added
* @see Container.Listener
* @see LifeCycle.Listener
* @see Container#addBean(Object)
*/
public void addEventListener(Listener listener);
boolean addEventListener(EventListener listener);
/**
* Remove an event listener.
*
* @param listener the listener to remove
* @return true if the listener was removed
* @see Container#removeBean(Object)
*/
public void removeEventListener(Listener listener);
boolean removeEventListener(EventListener listener);
/**
* Unmanages a bean already contained by this aggregate, so that it is not started/stopped/destroyed with this
@ -114,12 +134,35 @@ public interface Container
*/
boolean isManaged(Object bean);
/**
* @param clazz the class of the beans
* @param <T> the Bean type
* @return the list of beans of the given class from the entire Container hierarchy
*/
<T> Collection<T> getContainedBeans(Class<T> clazz);
/**
* Get the beans added to the container that are EventListeners.
* This is essentially equivalent to <code>getBeans(EventListener.class);</code>,
* except that: <ul>
* <li>The result may be precomputed, so it can be more efficient</li>
* <li>The result is ordered by the order added.</li>
* <li>The result is immutable.</li>
* </ul>
* @see #getBeans(Class)
* @return An unmodifiable list of EventListener beans
*/
default List<EventListener> getEventListeners()
{
return Collections.unmodifiableList(new ArrayList<>(getBeans(EventListener.class)));
}
/**
* A listener for Container events.
* If an added bean implements this interface it will receive the events
* for this container.
*/
public interface Listener
interface Listener extends EventListener
{
void beanAdded(Container parent, Object child);
@ -131,14 +174,7 @@ public interface Container
* If an added bean implements this interface, then it will
* be added to all contained beans that are themselves Containers
*/
public interface InheritedListener extends Listener
interface InheritedListener extends Listener
{
}
/**
* @param clazz the class of the beans
* @param <T> the Bean type
* @return the list of beans of the given class from the entire Container hierarchy
*/
public <T> Collection<T> getContainedBeans(Class<T> clazz);
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -64,6 +65,10 @@ import org.eclipse.jetty.util.log.Logger;
* If adding a bean that is shared between multiple {@link ContainerLifeCycle} instances, then it should be started
* before being added, so it is unmanaged, or the API must be used to explicitly set it as unmanaged.
* <p>
* All {@link EventListener}s added via {@link #addEventListener(EventListener)} are also added as beans and all beans
* added via an {@link #addBean(Object)} method that are also {@link EventListener}s are added as listeners via a
* call to {@link #addEventListener(EventListener)}.
* <p>
* This class also provides utility methods to dump deep structures of objects.
* In the dump, the following symbols are used to indicate the type of contained object:
* <pre>
@ -340,18 +345,17 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
Bean newBean = new Bean(o);
// if the bean is a Listener
if (o instanceof Container.Listener)
addEventListener((Container.Listener)o);
// Add the bean
_beans.add(newBean);
// Tell existing listeners about the new bean
// Tell any existing listeners about the new bean
for (Container.Listener l : _listeners)
{
l.beanAdded(this, o);
}
// if the bean is an EventListener, then add it. Because we have already added it as a bean above, then
// addBean will not be called back.
if (o instanceof EventListener)
addEventListener((EventListener)o);
try
{
@ -448,27 +452,62 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
}
@Override
public void addEventListener(Container.Listener listener)
public boolean addEventListener(EventListener listener)
{
if (_listeners.contains(listener))
return;
_listeners.add(listener);
// tell it about existing beans
for (Bean b : _beans)
// Has it already been added as a listener?
if (super.addEventListener(listener))
{
listener.beanAdded(this, b._bean);
// If it is not yet a bean,
if (!contains(listener))
// add it as a bean, we will be called back to add it as an event listener, but it will have
// already been added, so we will not enter this branch.
addBean(listener);
// handle inheritance
if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
if (listener instanceof Container.Listener)
{
if (b._bean instanceof ContainerLifeCycle)
((ContainerLifeCycle)b._bean).addBean(listener, false);
else
((Container)b._bean).addBean(listener);
Container.Listener cl = (Container.Listener)listener;
_listeners.add(cl);
// tell it about existing beans
for (Bean b : _beans)
{
cl.beanAdded(this, b._bean);
// handle inheritance
if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
{
if (b._bean instanceof ContainerLifeCycle)
((ContainerLifeCycle)b._bean).addBean(listener, false);
else
((Container)b._bean).addBean(listener);
}
}
}
return true;
}
return false;
}
@Override
public boolean removeEventListener(EventListener listener)
{
if (super.removeEventListener(listener))
{
removeBean(listener);
if (_listeners.remove(listener))
{
// remove existing beans
for (Bean b : _beans)
{
((Container.Listener)listener).beanRemoved(this, b._bean);
if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
((Container)b._bean).removeBean(listener);
}
}
return true;
}
return false;
}
/**
@ -637,8 +676,9 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
l.beanRemoved(this, bean._bean);
}
if (bean._bean instanceof Container.Listener)
removeEventListener((Container.Listener)bean._bean);
// Remove event listeners, checking list here to avoid calling extended removeEventListener if already removed.
if (bean._bean instanceof EventListener && getEventListeners().contains(bean._bean))
removeEventListener((EventListener)bean._bean);
// stop managed beans
if (wasManaged && bean._bean instanceof LifeCycle)
@ -661,22 +701,6 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
return false;
}
@Override
public void removeEventListener(Container.Listener listener)
{
if (_listeners.remove(listener))
{
// remove existing beans
for (Bean b : _beans)
{
listener.beanRemoved(this, b._bean);
if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
((Container)b._bean).removeBean(listener);
}
}
}
@Override
public void setStopTimeout(long stopTimeout)
{

View File

@ -138,9 +138,9 @@ public interface LifeCycle
*/
boolean isFailed();
void addLifeCycleListener(LifeCycle.Listener listener);
boolean addEventListener(EventListener listener);
void removeLifeCycleListener(LifeCycle.Listener listener);
boolean removeEventListener(EventListener listener);
/**
* Listener.

View File

@ -33,7 +33,7 @@ public class StopLifeCycle extends AbstractLifeCycle implements LifeCycle.Listen
public StopLifeCycle(LifeCycle lifecycle)
{
_lifecycle = lifecycle;
addLifeCycleListener(this);
addEventListener(this);
}
@Override

View File

@ -482,6 +482,7 @@ public abstract class Resource implements ResourceFactory, Closeable
* @param parent True if the parent directory should be included
* @param query query params
* @return String of HTML
* @throws IOException on failure to generate a list.
*/
public String getListHTML(String base, boolean parent, String query) throws IOException
{

View File

@ -443,6 +443,11 @@ public class ContainerLifeCycleTest
c0.addBean(inherited);
assertEquals("listener", handled.poll());
assertEquals("added", operation.poll());
assertEquals(c0, parent.poll());
assertEquals(inherited, child.poll());
assertEquals("inherited", handled.poll());
assertEquals("added", operation.poll());
assertEquals(c0, parent.poll());
@ -453,11 +458,6 @@ public class ContainerLifeCycleTest
assertEquals(c0, parent.poll());
assertEquals(listener, child.poll());
assertEquals("listener", handled.poll());
assertEquals("added", operation.poll());
assertEquals(c0, parent.poll());
assertEquals(inherited, child.poll());
assertEquals("inherited", handled.poll());
assertEquals("added", operation.poll());
assertEquals(c0, parent.poll());

View File

@ -161,7 +161,7 @@ public class LifeCycleListenerNestedTest
{
if (child instanceof LifeCycle)
{
((LifeCycle)child).addLifeCycleListener(this);
((LifeCycle)child).addEventListener(this);
}
}
@ -170,7 +170,7 @@ public class LifeCycleListenerNestedTest
{
if (child instanceof LifeCycle)
{
((LifeCycle)child).removeLifeCycleListener(this);
((LifeCycle)child).removeEventListener(this);
}
}
}
@ -185,7 +185,7 @@ public class LifeCycleListenerNestedTest
foo.addBean(barb);
CapturingListener listener = new CapturingListener();
foo.addLifeCycleListener(listener);
foo.addEventListener(listener);
if (workaround)
foo.addEventListener(listener);
@ -216,7 +216,7 @@ public class LifeCycleListenerNestedTest
Foo foo = new Foo();
CapturingListener listener = new CapturingListener();
foo.addLifeCycleListener(listener);
foo.addEventListener(listener);
if (workaround)
foo.addEventListener(listener);
@ -254,7 +254,7 @@ public class LifeCycleListenerNestedTest
Bar barb = new Bar("b");
CapturingListener listener = new CapturingListener();
foo.addLifeCycleListener(listener);
foo.addEventListener(listener);
if (workaround)
foo.addEventListener(listener);

View File

@ -36,7 +36,7 @@ public class LifeCycleListenerTest
{
TestLifeCycle lifecycle = new TestLifeCycle();
TestListener listener = new TestListener();
lifecycle.addLifeCycleListener(listener);
lifecycle.addEventListener(listener);
lifecycle.setCause(cause);
@ -72,7 +72,7 @@ public class LifeCycleListenerTest
{
TestLifeCycle lifecycle = new TestLifeCycle();
TestListener listener = new TestListener();
lifecycle.addLifeCycleListener(listener);
lifecycle.addEventListener(listener);
// need to set the state to something other than stopped or stopping or
// else
@ -116,11 +116,11 @@ public class LifeCycleListenerTest
{
TestLifeCycle lifecycle = new TestLifeCycle();
TestListener listener = new TestListener();
lifecycle.addLifeCycleListener(listener);
lifecycle.addEventListener(listener);
lifecycle.start();
assertTrue(listener.starting, "The starting event didn't occur");
lifecycle.removeLifeCycleListener(listener);
lifecycle.removeEventListener(listener);
lifecycle.stop();
assertFalse(listener.stopping, "The stopping event occurred");
}

View File

@ -556,15 +556,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
}
}
/*
* @see org.eclipse.thread.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
super.doStop();
}
@Override
public void destroy()
{
@ -957,7 +948,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
new DumpableCollection("Configurations " + name, _configurations),
new DumpableCollection("Handler attributes " + name, ((AttributesMap)getAttributes()).getAttributeEntrySet()),
new DumpableCollection("Context attributes " + name, getServletContext().getAttributeEntrySet()),
new DumpableCollection("EventListeners " + this, Arrays.asList(getEventListeners())),
new DumpableCollection("EventListeners " + this, getEventListeners()),
new DumpableCollection("Initparams " + name, getInitParams().entrySet())
);
}
@ -1080,27 +1071,22 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
}
@Override
public void setEventListeners(EventListener[] eventListeners)
public boolean removeEventListener(EventListener listener)
{
if (_sessionHandler != null)
_sessionHandler.clearEventListeners();
super.setEventListeners(eventListeners);
}
@Override
public void removeEventListener(EventListener listener)
{
super.removeEventListener(listener);
if ((listener instanceof HttpSessionActivationListener) ||
(listener instanceof HttpSessionAttributeListener) ||
(listener instanceof HttpSessionBindingListener) ||
(listener instanceof HttpSessionListener) ||
(listener instanceof HttpSessionIdListener))
if (super.removeEventListener(listener))
{
if (_sessionHandler != null)
_sessionHandler.removeEventListener(listener);
if ((listener instanceof HttpSessionActivationListener) ||
(listener instanceof HttpSessionAttributeListener) ||
(listener instanceof HttpSessionBindingListener) ||
(listener instanceof HttpSessionListener) ||
(listener instanceof HttpSessionIdListener))
{
if (_sessionHandler != null)
_sessionHandler.removeEventListener(listener);
}
return true;
}
return false;
}
/**

View File

@ -90,7 +90,7 @@ public class WebAppContextTest
server.setHandler(wac);
wac.addEventListener(new MySessionListener());
Collection<MySessionListener> listeners = wac.getSessionHandler().getBeans(org.eclipse.jetty.webapp.WebAppContextTest.MySessionListener.class);
Collection<MySessionListener> listeners = wac.getSessionHandler().getBeans(MySessionListener.class);
assertNotNull(listeners);
assertEquals(1, listeners.size());
}

View File

@ -99,7 +99,7 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer
WebSocketComponents.ensureWebSocketComponents(servletContext),
coreClientSupplier);
contextHandler.addManaged(container);
contextHandler.addLifeCycleListener(container);
contextHandler.addEventListener(container);
}
// Store a reference to the ServerContainer per - javax.websocket spec 1.0 final - section 6.4: Programmatic Server Deployment
servletContext.setAttribute(JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE, container);

View File

@ -76,7 +76,7 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
WebSocketComponents.ensureWebSocketComponents(servletContext), executor);
servletContext.setAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE, container);
contextHandler.addManaged(container);
contextHandler.addLifeCycleListener(container);
contextHandler.addEventListener(container);
}
return container;
@ -112,7 +112,7 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
{
factory = new JettyServerFrameHandlerFactory(this);
contextHandler.addManaged(factory);
contextHandler.addLifeCycleListener(factory);
contextHandler.addEventListener(factory);
}
frameHandlerFactory = factory;

View File

@ -48,7 +48,6 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.QuotedStringTokenizer;
@ -371,14 +370,8 @@ public abstract class ClientUpgradeRequest extends HttpRequest implements Respon
HttpClient httpClient = wsClient.getHttpClient();
WebSocketConnection wsConnection = newWebSocketConnection(endp, httpClient.getExecutor(), httpClient.getScheduler(), httpClient.getByteBufferPool(), coreSession);
for (Connection.Listener listener : wsClient.getBeans(Connection.Listener.class))
{
wsConnection.addListener(listener);
}
wsClient.getEventListeners().forEach(wsConnection::addEventListener);
coreSession.setWebSocketConnection(wsConnection);
notifyUpgradeListeners((listener) -> listener.onHandshakeResponse(this, response));
// Now swap out the connection

View File

@ -30,7 +30,6 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
@ -211,10 +210,7 @@ public final class RFC6455Handshaker implements Handshaker
if (connection == null)
throw new WebSocketException("not upgraded: no connection");
for (Connection.Listener listener : connector.getBeans(Connection.Listener.class))
{
connection.addListener(listener);
}
connector.getEventListeners().forEach(connection::addEventListener);
coreSession.setWebSocketConnection(connection);

View File

@ -170,5 +170,10 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>

View File

@ -1,4 +1,5 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=INFO
#org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
org.eclipse.jetty.LEVEL=WARN
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.websocket.LEVEL=DEBUG

View File

@ -0,0 +1,19 @@
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/logs/jetty.txt</file>
<append>false</append>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>

View File

@ -26,7 +26,7 @@ import org.eclipse.jetty.server.Request;
public class TestHttpChannelCompleteListener implements Listener
{
private final AtomicReference<CountDownLatch> _exitSynchronizer = new AtomicReference<>();
AtomicReference<CountDownLatch> _exitSynchronizer = new AtomicReference<>();
/**
* @param exitSynchronizer the exitSynchronizer to set