diff options
author | Greg Wilkins | 2013-03-01 22:32:57 +0000 |
---|---|---|
committer | Greg Wilkins | 2013-03-01 22:32:57 +0000 |
commit | 64783068123ad4bc262cf1f594462c833a6d3e50 (patch) | |
tree | 281e6017d8726c17bd034e7b76a54be1264ca8c9 | |
parent | 41952ebf1db6f5b7809c30dfc5be40c4892146d4 (diff) | |
parent | 79630de1f194e9edb0d1807b7d1841fbc5acd7ee (diff) | |
download | org.eclipse.jetty.project-64783068123ad4bc262cf1f594462c833a6d3e50.tar.gz org.eclipse.jetty.project-64783068123ad4bc262cf1f594462c833a6d3e50.tar.xz org.eclipse.jetty.project-64783068123ad4bc262cf1f594462c833a6d3e50.zip |
Merge branch 'master' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project
Conflicts:
jetty-server/src/main/config/etc/jetty.xml
23 files changed, 388 insertions, 125 deletions
diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 181e6c5c93..ec27c98fed 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -25,7 +25,6 @@ import org.eclipse.jetty.deploy.PropertiesConfigurationManager; import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; -import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -42,7 +41,7 @@ import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.TimerScheduler; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; public class LikeJettyXml { @@ -51,7 +50,7 @@ public class LikeJettyXml String jetty_home = System.getProperty("jetty.home","../../jetty-distribution/target/distribution"); System.setProperty("jetty.home",jetty_home); - + // === jetty.xml === // Setup Threadpool QueuedThreadPool threadPool = new QueuedThreadPool(); @@ -61,7 +60,7 @@ public class LikeJettyXml Server server = new Server(threadPool); // Scheduler - server.addBean(new TimerScheduler()); + server.addBean(new ScheduledExecutorScheduler()); // HTTP Configuration HttpConfiguration http_config = new HttpConfiguration(); @@ -73,7 +72,7 @@ public class LikeJettyXml http_config.setSendServerVersion(true); http_config.setSendDateHeader(false); // httpConfig.addCustomizer(new ForwardedRequestCustomizer()); - + // Handler Structure HandlerCollection handlers = new HandlerCollection(); ContextHandlerCollection contexts = new ContextHandlerCollection(); @@ -84,21 +83,21 @@ public class LikeJettyXml server.setDumpAfterStart(false); server.setDumpBeforeStop(false); server.setStopAtShutdown(true); - - // === jetty-jmx.xml === + + // === jetty-jmx.xml === MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); server.addBean(mbContainer); - - // === jetty-http.xml === + + // === jetty-http.xml === ServerConnector http = new ServerConnector(server,new HttpConnectionFactory(http_config)); http.setPort(8080); http.setIdleTimeout(30000); server.addConnector(http); - - // === jetty-https.xml === + + // === jetty-https.xml === // SSL Context Factory SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore"); @@ -114,11 +113,11 @@ public class LikeJettyXml "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); - + // SSL HTTP Configuration HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); - + // SSL Connector ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory,"http/1.1"), @@ -141,15 +140,15 @@ public class LikeJettyXml deployer.addAppProvider(webapp_provider); server.addBean(deployer); - - // === jetty-stats.xml === + + // === jetty-stats.xml === StatisticsHandler stats = new StatisticsHandler(); stats.setHandler(server.getHandler()); server.setHandler(stats); - // === jetty-requestlog.xml === + // === jetty-requestlog.xml === NCSARequestLog requestLog = new NCSARequestLog(); requestLog.setFilename(jetty_home + "/logs/jetty-yyyy_mm_dd.log"); requestLog.setFilenameDateFormat("yyyy_MM_dd"); @@ -163,7 +162,7 @@ public class LikeJettyXml handlers.addHandler(requestLogHandler); - // === jetty-lowresources.xml === + // === jetty-lowresources.xml === LowResourceMonitor lowResourcesMonitor=new LowResourceMonitor(server); lowResourcesMonitor.setPeriod(1000); lowResourcesMonitor.setLowResourcesIdleTimeout(200); @@ -173,8 +172,8 @@ public class LikeJettyXml lowResourcesMonitor.setMaxLowResourcesTime(5000); server.addBean(lowResourcesMonitor); - - // === test-realm.xml === + + // === test-realm.xml === HashLoginService login = new HashLoginService(); login.setName("Test Realm"); login.setConfig(jetty_home + "/etc/realm.properties"); @@ -182,7 +181,7 @@ public class LikeJettyXml server.addBean(login); - // Start the server + // Start the server server.start(); server.join(); } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java index a726705f55..2f79754951 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.annotations; import java.net.URI; import java.util.ArrayList; +import java.util.EventListener; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; @@ -29,6 +30,7 @@ import javax.servlet.annotation.HandlesTypes; import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler; import org.eclipse.jetty.plus.annotation.ContainerInitializer; +import org.eclipse.jetty.util.ArrayUtil; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -221,10 +223,10 @@ public class AnnotationConfiguration extends AbstractConfiguration - //add a listener which will call the servletcontainerinitializers when appropriate + //add a bean which will call the servletcontainerinitializers when appropriate ServletContainerInitializerListener listener = new ServletContainerInitializerListener(); listener.setWebAppContext(context); - context.addEventListener(listener); + context.addBean(listener, true); } diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java index e36bc272d1..b5ced5b558 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java @@ -22,11 +22,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - +import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.plus.annotation.ContainerInitializer; import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; @@ -36,7 +35,7 @@ import org.eclipse.jetty.webapp.WebAppContext; * * */ -public class ServletContainerInitializerListener implements ServletContextListener +public class ServletContainerInitializerListener extends AbstractLifeCycle { private static final Logger LOG = Log.getLogger(ServletContainerInitializerListener.class); protected WebAppContext _context = null; @@ -47,10 +46,12 @@ public class ServletContainerInitializerListener implements ServletContextListen _context = context; } - /** - * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) + + /** + * Call the doStart method of the ServletContainerInitializers + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() */ - public void contextInitialized(ServletContextEvent sce) + public void doStart() { List<ContainerInitializer> initializers = (List<ContainerInitializer>)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); MultiMap classMap = (MultiMap)_context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); @@ -129,12 +130,14 @@ public class ServletContainerInitializerListener implements ServletContextListen addInheritedTypes (classMap, initializer, implementsOrExtends); } } - - - /** - * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) + + + + /** + * Nothing to do for ServletContainerInitializers on stop + * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() */ - public void contextDestroyed(ServletContextEvent sce) + public void doStop() { } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java index c43a41c4f1..8b3def6744 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java @@ -45,6 +45,7 @@ abstract public class WriteFlusher { private static final Logger LOG = Log.getLogger(WriteFlusher.class); private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false + private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[0]; private static final EnumMap<StateType, Set<StateType>> __stateTransitions = new EnumMap<>(StateType.class); private static final State __IDLE = new IdleState(); private static final State __WRITING = new WritingState(); @@ -243,7 +244,7 @@ abstract public class WriteFlusher private PendingState(ByteBuffer[] buffers, Callback callback) { super(StateType.PENDING); - _buffers = buffers; + _buffers = compact(buffers); _callback = callback; } @@ -263,6 +264,44 @@ abstract public class WriteFlusher if (_callback!=null) _callback.succeeded(); } + + /** + * Compacting the buffers is needed because the semantic of WriteFlusher is + * to write the buffers and if the caller sees that the buffer is consumed, + * then it can recycle it. + * If we do not compact, then it is possible that we store a consumed buffer, + * which is then recycled and refilled; when the WriteFlusher is invoked to + * complete the write, it will write the refilled bytes, garbling the content. + * + * @param buffers the buffers to compact + * @return the compacted buffers + */ + private ByteBuffer[] compact(ByteBuffer[] buffers) + { + int length = buffers.length; + + // Just one element, no need to compact + if (length < 2) + return buffers; + + // How many still have content ? + int consumed = 0; + while (consumed < length && BufferUtil.isEmpty(buffers[consumed])) + ++consumed; + + // All of them still have content, no need to compact + if (consumed == 0) + return buffers; + + // None has content, return empty + if (consumed == length) + return EMPTY_BUFFERS; + + int newLength = length - consumed; + ByteBuffer[] result = new ByteBuffer[newLength]; + System.arraycopy(buffers, consumed, result, 0, newLength); + return result; + } } /** @@ -306,7 +345,7 @@ abstract public class WriteFlusher if (updateState(__WRITING,pending)) onIncompleteFlushed(); else - fail(new PendingState(buffers, callback)); + fail(pending); return; } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java index b1eeadc478..85fe4f5057 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/WriteFlusherTest.java @@ -18,19 +18,11 @@ package org.eclipse.jetty.io; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.when; - import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.WritePendingException; import java.security.SecureRandom; +import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -54,16 +46,23 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + @RunWith(MockitoJUnitRunner.class) public class WriteFlusherTest { + private final AtomicBoolean _flushIncomplete = new AtomicBoolean(false); + private final ExecutorService executor = Executors.newFixedThreadPool(16); @Mock private EndPoint _endPointMock; - private WriteFlusher _flusher; - - private final AtomicBoolean _flushIncomplete = new AtomicBoolean(false); - private final ExecutorService executor = Executors.newFixedThreadPool(16); private ByteArrayEndPoint _endp; @Before @@ -400,6 +399,34 @@ public class WriteFlusherTest assertThat("callback completed", callback.isDone(), is(true)); } + @Test + public void testPendingWriteDoesNotStoreConsumedBuffers() throws Exception + { + int toWrite = _endp.getOutput().capacity(); + byte[] chunk1 = new byte[toWrite / 2]; + Arrays.fill(chunk1, (byte)1); + ByteBuffer buffer1 = ByteBuffer.wrap(chunk1); + byte[] chunk2 = new byte[toWrite]; + Arrays.fill(chunk1, (byte)2); + ByteBuffer buffer2 = ByteBuffer.wrap(chunk2); + + _flusher.write(new Callback.Adapter(), buffer1, buffer2); + assertTrue(_flushIncomplete.get()); + assertFalse(buffer1.hasRemaining()); + + // Reuse buffer1 + buffer1.clear(); + Arrays.fill(chunk1, (byte)3); + int remaining1 = buffer1.remaining(); + + // Complete the write + _endp.takeOutput(); + _flusher.completeWrite(); + + // Make sure buffer1 is unchanged + assertEquals(remaining1, buffer1.remaining()); + } + private class ExposingStateCallback extends FutureCallback { private boolean failed = false; diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java index 1d32c66442..2e0760f4bd 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java @@ -28,7 +28,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; - import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -52,8 +51,8 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; -import org.eclipse.jetty.util.thread.TimerScheduler; /** * <p>Implementation of a {@link Handler} that supports HTTP CONNECT.</p> @@ -163,7 +162,7 @@ public class ConnectHandler extends HandlerWrapper } if (scheduler == null) { - setScheduler(new TimerScheduler()); + setScheduler(new ScheduledExecutorScheduler()); addBean(getScheduler()); } if (bufferPool == null) diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml index 954ba71173..dd78c54004 100644 --- a/jetty-server/src/main/config/etc/jetty.xml +++ b/jetty-server/src/main/config/etc/jetty.xml @@ -14,7 +14,7 @@ <!-- java -jar start.jar -? --> <!-- =============================================================== --> -<!-- =============================================================== --> +<!-- =============================================================== --> <!-- Configure a Jetty Server instance with an ID "Server" --> <!-- Other configuration files may also configure the "Server" --> <!-- ID, in which case they are adding configuration to the same --> @@ -49,13 +49,13 @@ <Set name="detailedDump">false</Set> </New> </Arg> - + <!-- =========================================================== --> <!-- Add shared Scheduler instance --> <!-- =========================================================== --> <Call name="addBean"> <Arg> - <New class="org.eclipse.jetty.util.thread.TimerScheduler"/> + <New class="org.eclipse.jetty.util.thread.ScheduledExecutorScheduler"/> </Arg> </Call> @@ -83,14 +83,14 @@ <Set name="sendServerVersion">true</Set> <Set name="sendDateHeader">false</Set> <Set name="headerCacheSize">512</Set> - - <!-- Uncomment to enable handling of X-Forwarded- style headers + + <!-- Uncomment to enable handling of X-Forwarded- style headers <Call name="addCustomizer"> <Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg> </Call> --> </New> - + <!-- =========================================================== --> <!-- Set the default handler structure for the Server --> @@ -100,7 +100,7 @@ <!-- DefaultHandler, which handles any requests not handled by --> <!-- the context handlers. --> <!-- Other handlers may be added to the "Handlers" collection, --> - <!-- for example the jetty-requestlog.xml file adds the --> + <!-- for example the jetty-requestlog.xml file adds the --> <!-- RequestLogHandler after the default handler --> <!-- =========================================================== --> <Set name="handler"> diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index ca40a390df..bcc13aa3cd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -48,8 +48,8 @@ import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; -import org.eclipse.jetty.util.thread.TimerScheduler; /** * <p>An abstract implementation of {@link Connector} that provides a {@link ConnectionFactory} mechanism @@ -63,7 +63,7 @@ import org.eclipse.jetty.util.thread.TimerScheduler; * </li> * <li>The {@link Scheduler} service is used to monitor the idle timeouts of all connections and is also made available * to the connections to time such things as asynchronous request timeouts. The default is to use a new - * {@link TimerScheduler} instance. + * {@link ScheduledExecutorScheduler} instance. * </li> * <li>The {@link ByteBufferPool} service is made available to all connections to be used to acquire and release * {@link ByteBuffer} instances from a pool. The default is to use a new {@link ArrayByteBufferPool} instance. @@ -151,12 +151,12 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co private long _idleTimeout = 30000; private String _defaultProtocol; private ConnectionFactory _defaultConnectionFactory; - + /** * @param server The server this connector will be added to. Must not be null. * @param executor An executor for this connector or null to use the servers executor - * @param scheduler A scheduler for this connector or null to either a {@link Scheduler} set as a server bean or if none set, then a new {@link TimerScheduler} instance. + * @param scheduler A scheduler for this connector or null to either a {@link Scheduler} set as a server bean or if none set, then a new {@link ScheduledExecutorScheduler} instance. * @param pool A buffer pool for this connector or null to either a {@link ByteBufferPool} set as a server bean or none set, the new {@link ArrayByteBufferPool} instance. * @param acceptors the number of acceptor threads to use, or 0 for a default value. * @param factories The Connection Factories to use. @@ -173,7 +173,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co _executor=executor!=null?executor:_server.getThreadPool(); if (scheduler==null) scheduler=_server.getBean(Scheduler.class); - _scheduler=scheduler!=null?scheduler:new TimerScheduler(); + _scheduler=scheduler!=null?scheduler:new ScheduledExecutorScheduler(); if (pool==null) pool=_server.getBean(ByteBufferPool.class); _byteBufferPool = pool!=null?pool:new ArrayByteBufferPool(); @@ -479,9 +479,9 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co } } - - - + + + // protected void connectionOpened(Connection connection) // { // _stats.connectionOpened(); @@ -512,7 +512,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co { _endpoints.add(endp); } - + protected void onEndPointClosed(EndPoint endp) { _endpoints.remove(endp); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java index d4a1dbb029..3749e04057 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/LowResourceMonitor.java @@ -32,9 +32,9 @@ import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.ThreadPool; -import org.eclipse.jetty.util.thread.TimerScheduler; /* ------------------------------------------------------------ */ @@ -42,7 +42,7 @@ import org.eclipse.jetty.util.thread.TimerScheduler; * <p>An instance of this class will monitor all the connectors of a server (or a set of connectors * configured with {@link #setMonitoredConnectors(Collection)}) for a low resources state. * Low resources can be detected by:<ul> - * <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is + * <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is * an instance of {@link ThreadPool} and {@link #setMonitorThreads(boolean)} is true.<li> * <li>If {@link #setMaxMemory(long)} is non zero then low resources is detected if the JVMs * {@link Runtime} instance has {@link Runtime#totalMemory()} minus {@link Runtime#freeMemory()} @@ -51,11 +51,11 @@ import org.eclipse.jetty.util.thread.TimerScheduler; * of connections exceeds {@link #getMaxConnections()}</li> * </ul> * </p> - * <p>Once low resources state is detected, the cause is logged and all existing connections returned - * by {@link Connector#getConnectedEndPoints()} have {@link EndPoint#setIdleTimeout(long)} set - * to {@link #getLowResourcesIdleTimeout()}. New connections are not affected, however if the low - * resources state persists for more than {@link #getMaxLowResourcesTime()}, then the - * {@link #getLowResourcesIdleTimeout()} to all connections again. Once the low resources state is + * <p>Once low resources state is detected, the cause is logged and all existing connections returned + * by {@link Connector#getConnectedEndPoints()} have {@link EndPoint#setIdleTimeout(long)} set + * to {@link #getLowResourcesIdleTimeout()}. New connections are not affected, however if the low + * resources state persists for more than {@link #getMaxLowResourcesTime()}, then the + * {@link #getLowResourcesIdleTimeout()} to all connections again. Once the low resources state is * cleared, the idle timeout is reset to the connector default given by {@link Connector#getIdleTimeout()}. * </p> */ @@ -76,44 +76,44 @@ public class LowResourceMonitor extends AbstractLifeCycle private String _cause; private String _reasons; private long _lowStarted; - - + + private final Runnable _monitor = new Runnable() { @Override public void run() - { + { if (isRunning()) { monitor(); _scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS); } - } + } }; - + public LowResourceMonitor(@Name("server") Server server) { _server=server; } - + @ManagedAttribute("Are the monitored connectors low on resources?") public boolean isLowOnResources() { return _low.get(); } - + @ManagedAttribute("The reason(s) the monitored connectors are low on resources") public String getLowResourcesReasons() { return _reasons; } - + @ManagedAttribute("Get the timestamp in ms since epoch that low resources state started") public long getLowResourcesStarted() { return _lowStarted; } - + @ManagedAttribute("The monitored connectors. If null then all server connectors are monitored") public Collection<Connector> getMonitoredConnectors() { @@ -140,7 +140,7 @@ public class LowResourceMonitor extends AbstractLifeCycle } /** - * @param periodMS The period in ms to monitor for low resources + * @param periodMS The period in ms to monitor for low resources */ public void setPeriod(int periodMS) { @@ -154,8 +154,8 @@ public class LowResourceMonitor extends AbstractLifeCycle } /** - * @param monitorThreads If true, check connectors executors to see if they are - * {@link ThreadPool} instances that are low on threads. + * @param monitorThreads If true, check connectors executors to see if they are + * {@link ThreadPool} instances that are low on threads. */ public void setMonitorThreads(boolean monitorThreads) { @@ -222,14 +222,14 @@ public class LowResourceMonitor extends AbstractLifeCycle protected void doStart() throws Exception { _scheduler = _server.getBean(Scheduler.class); - + if (_scheduler==null) { _scheduler=new LRMScheduler(); _scheduler.start(); } super.doStart(); - + _scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS); } @@ -240,24 +240,24 @@ public class LowResourceMonitor extends AbstractLifeCycle _scheduler.stop(); super.doStop(); } - + protected Connector[] getMonitoredOrServerConnectors() { if (_monitoredConnectors!=null && _monitoredConnectors.length>0) return _monitoredConnectors; return _server.getConnectors(); } - + protected void monitor() { String reasons=null; String cause=""; int connections=0; - + for(Connector connector : getMonitoredOrServerConnectors()) { connections+=connector.getConnectedEndPoints().size(); - + Executor executor = connector.getExecutor(); if (executor instanceof ThreadPool) { @@ -269,21 +269,21 @@ public class LowResourceMonitor extends AbstractLifeCycle } } } - + if (_maxConnections>0 && connections>_maxConnections) { reasons=low(reasons,"Max Connections exceeded: "+connections+">"+_maxConnections); cause+="C"; } - + long memory=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory(); if (_maxMemory>0 && memory>_maxMemory) { reasons=low(reasons,"Max memory exceeded: "+memory+">"+_maxMemory); cause+="M"; } - - + + if (reasons!=null) { // Log the reasons if there is any change in the cause @@ -292,7 +292,7 @@ public class LowResourceMonitor extends AbstractLifeCycle LOG.warn("Low Resources: {}",reasons); _cause=cause; } - + // Enter low resources state? if (_low.compareAndSet(false,true)) { @@ -300,12 +300,12 @@ public class LowResourceMonitor extends AbstractLifeCycle _lowStarted=System.currentTimeMillis(); setLowResources(); } - + // Too long in low resources state? if (_maxLowResourcesTime>0 && (System.currentTimeMillis()-_lowStarted)>_maxLowResourcesTime) setLowResources(); } - else + else { if (_low.compareAndSet(true,false)) { @@ -316,7 +316,7 @@ public class LowResourceMonitor extends AbstractLifeCycle } } } - + protected void setLowResources() { for(Connector connector : getMonitoredOrServerConnectors()) @@ -325,7 +325,7 @@ public class LowResourceMonitor extends AbstractLifeCycle endPoint.setIdleTimeout(_lowResourcesIdleTimeout); } } - + protected void clearLowResources() { for(Connector connector : getMonitoredOrServerConnectors()) @@ -334,16 +334,16 @@ public class LowResourceMonitor extends AbstractLifeCycle endPoint.setIdleTimeout(connector.getIdleTimeout()); } } - + private String low(String reasons, String newReason) { if (reasons==null) return newReason; return reasons+", "+newReason; } - - - private static class LRMScheduler extends TimerScheduler + + + private static class LRMScheduler extends ScheduledExecutorScheduler { } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index abd240ddcf..cb2d54fe2b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -2097,6 +2097,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu if (!_enabled) throw new UnsupportedOperationException(); ContextHandler.this.addEventListener(t); + ContextHandler.this.addProgrammaticListener(t); } @Override diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java index 4bb4113cb5..a73a07b9bf 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java @@ -995,7 +995,7 @@ public class DoSFilter implements Filter * @see #addWhitelistAddress(List, String) */ @ManagedOperation("removes an IP address that will not be rate limited") - public boolean removeWhitelistAddress(String address) + public boolean removeWhitelistAddress(@Name("address") String address) { return _whitelist.remove(address); } diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java index 8970dcafab..2a23c5f2c1 100644 --- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java +++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java @@ -30,7 +30,6 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; import java.util.concurrent.Future; - import javax.net.ssl.SSLEngine; import org.eclipse.jetty.io.ByteBufferPool; @@ -50,8 +49,8 @@ import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; -import org.eclipse.jetty.util.thread.TimerScheduler; public class SPDYClient { @@ -202,7 +201,7 @@ public class SPDYClient addBean(executor); if (scheduler == null) - scheduler = new TimerScheduler(); + scheduler = new ScheduledExecutorScheduler(); this.scheduler = scheduler; addBean(scheduler); diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java index 1bc7366ee5..dd6431d0c9 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/LogicalConnection.java @@ -74,6 +74,12 @@ public interface LogicalConnection extends OutgoingFrames, SuspendToken InetSocketAddress getLocalAddress(); /** + * Set the maximum number of milliseconds of idleness before the connection is closed/disconnected, (ie no frames are either sent or received) + * @return the idle timeout in milliseconds + */ + long getMaxIdleTimeout(); + + /** * The policy that the connection is running under. * @return the policy for the connection */ @@ -110,6 +116,14 @@ public interface LogicalConnection extends OutgoingFrames, SuspendToken boolean isReading(); /** + * Set the maximum number of milliseconds of idleness before the connection is closed/disconnected, (ie no frames are either sent or received) + * + * @param ms + * the number of milliseconds of idle timeout + */ + void setMaxIdleTimeout(long ms); + + /** * Set where the connection should send the incoming frames to. * <p> * Often this is from the Parser to the start of the extension stack, and eventually on to the session. diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index 0d648c6ada..cb80189b5f 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -59,7 +59,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc private ExtensionFactory extensionFactory; private long maximumMessageSize; private String protocolVersion; - private long timeout; private Map<String, String[]> parameterMap = new HashMap<>(); private WebSocketRemoteEndpoint remote; private IncomingFrames incomingHandler; @@ -165,12 +164,12 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc } /** - * The idle timeout in seconds + * The idle timeout in milliseconds */ @Override public long getIdleTimeout() { - return timeout; + return connection.getMaxIdleTimeout(); } @ManagedAttribute(readonly = true) @@ -320,12 +319,12 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc } /** - * Set the timeout in seconds + * Set the timeout in milliseconds */ @Override - public void setIdleTimeout(long seconds) + public void setIdleTimeout(long ms) { - this.timeout = seconds; + connection.setMaxIdleTimeout(ms); } @Override diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/mux/MuxChannel.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/mux/MuxChannel.java index 3e56bbd56f..b8bd82508f 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/mux/MuxChannel.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/extensions/mux/MuxChannel.java @@ -112,6 +112,13 @@ public class MuxChannel implements LogicalConnection, IncomingFrames, SuspendTok } @Override + public long getMaxIdleTimeout() + { + // TODO Auto-generated method stub + return 0; + } + + @Override public WebSocketPolicy getPolicy() { return policy; @@ -206,6 +213,13 @@ public class MuxChannel implements LogicalConnection, IncomingFrames, SuspendTok } @Override + public void setMaxIdleTimeout(long ms) + { + // TODO Auto-generated method stub + + } + + @Override public void setNextIncomingFrames(IncomingFrames incoming) { this.incoming = incoming; diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java index 289be61b17..12091c194c 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java @@ -347,6 +347,12 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp return ioState; } + @Override + public long getMaxIdleTimeout() + { + return getEndPoint().getIdleTimeout(); + } + public Parser getParser() { return parser; @@ -569,6 +575,12 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp } @Override + public void setMaxIdleTimeout(long ms) + { + getEndPoint().setIdleTimeout(ms); + } + + @Override public void setSession(WebSocketSession session) { this.session = session; diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java index 7099af3ac4..5387af30d0 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java @@ -92,6 +92,13 @@ public class LocalWebSocketConnection implements LogicalConnection, IncomingFram } @Override + public long getMaxIdleTimeout() + { + // TODO Auto-generated method stub + return 0; + } + + @Override public WebSocketPolicy getPolicy() { return policy; @@ -149,6 +156,13 @@ public class LocalWebSocketConnection implements LogicalConnection, IncomingFram } @Override + public void setMaxIdleTimeout(long ms) + { + // TODO Auto-generated method stub + + } + + @Override public void setNextIncomingFrames(IncomingFrames incoming) { this.incoming = incoming; diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java index e5f75af903..edffd2f172 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java @@ -27,7 +27,6 @@ import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -39,8 +38,8 @@ import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; -import org.eclipse.jetty.util.thread.TimerScheduler; import org.eclipse.jetty.websocket.api.UpgradeRequest; import org.eclipse.jetty.websocket.api.UpgradeResponse; import org.eclipse.jetty.websocket.api.WebSocketException; @@ -83,7 +82,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc /** * Have the factory maintain 1 and only 1 scheduler. All connections share this scheduler. */ - private final Scheduler scheduler = new TimerScheduler(); + private final Scheduler scheduler = new ScheduledExecutorScheduler(); private final String supportedVersions; private final WebSocketPolicy basePolicy; private final EventDriverFactory eventDriverFactory; @@ -308,7 +307,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc /* * (non-Javadoc) - * + * * @see org.eclipse.jetty.websocket.server.WebSocketServletFactory#register(java.lang.Class) */ @Override @@ -349,7 +348,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc * <p> * This method will not normally return, but will instead throw a UpgradeConnectionException, to exit HTTP handling and initiate WebSocket handling of the * connection. - * + * * @param request * The request to upgrade * @param response diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java index 355accfbae..609a812625 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketServlet.java @@ -42,15 +42,17 @@ import org.eclipse.jetty.websocket.api.annotations.WebSocket; * <pre> * package my.example; * - * import javax.servlet.http.HttpServletRequest; - * import org.eclipse.jetty.websocket.WebSocket; - * import org.eclipse.jetty.websocket.server.WebSocketServlet; + * import org.eclipse.jetty.websocket.servlet.WebSocketServlet; + * import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; * * public class MyEchoServlet extends WebSocketServlet * { * @Override - * public void registerWebSockets(WebSocketServerFactory factory) + * public void configure(WebSocketServletFactory factory) * { + * // set a 10 second idle timeout + * factory.getPolicy().setIdleTimeout(10000); + * // register my socket * factory.register(MyEchoSocket.class); * } * } diff --git a/jetty-websocket/websocket-servlet/src/test/java/examples/MyExampleServlet.java b/jetty-websocket/websocket-servlet/src/test/java/examples/MyExampleServlet.java new file mode 100644 index 0000000000..b81559a754 --- /dev/null +++ b/jetty-websocket/websocket-servlet/src/test/java/examples/MyExampleServlet.java @@ -0,0 +1,35 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 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 examples; + +import org.eclipse.jetty.websocket.servlet.WebSocketServlet; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; + +@SuppressWarnings("serial") +public class MyExampleServlet extends WebSocketServlet +{ + @Override + public void configure(WebSocketServletFactory factory) + { + // set a 10 second timeout + factory.getPolicy().setIdleTimeout(10000); + // register my socket + factory.register(MyExampleSocket.class); + } +} diff --git a/jetty-websocket/websocket-servlet/src/test/java/examples/MyExampleSocket.java b/jetty-websocket/websocket-servlet/src/test/java/examples/MyExampleSocket.java new file mode 100644 index 0000000000..cffefaa6c8 --- /dev/null +++ b/jetty-websocket/websocket-servlet/src/test/java/examples/MyExampleSocket.java @@ -0,0 +1,34 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 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 examples; + +import org.eclipse.jetty.websocket.api.WebSocketAdapter; + +/** + * Example WebSocket, simple echo + */ +public class MyExampleSocket extends WebSocketAdapter +{ + @Override + public void onWebSocketText(String message) + { + // Echo message back, asynchronously + getSession().getRemote().sendStringByFuture(message); + } +} diff --git a/tests/test-webapps/test-servlet-spec/test-container-initializer/src/main/java/com/acme/FooInitializer.java b/tests/test-webapps/test-servlet-spec/test-container-initializer/src/main/java/com/acme/FooInitializer.java index 1f1a46699a..c07863ed46 100644 --- a/tests/test-webapps/test-servlet-spec/test-container-initializer/src/main/java/com/acme/FooInitializer.java +++ b/tests/test-webapps/test-servlet-spec/test-container-initializer/src/main/java/com/acme/FooInitializer.java @@ -23,17 +23,80 @@ import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; import javax.servlet.ServletRegistration; import javax.servlet.annotation.HandlesTypes; @HandlesTypes ({javax.servlet.Servlet.class, Foo.class}) public class FooInitializer implements ServletContainerInitializer { + public static class BarListener implements ServletContextListener + { + + /** + * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) + */ + @Override + public void contextInitialized(ServletContextEvent sce) + { + throw new IllegalStateException("BAR LISTENER CALLED!"); + } + + /** + * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) + */ + @Override + public void contextDestroyed(ServletContextEvent sce) + { + + } + + } + public static class FooListener implements ServletContextListener + { + + /** + * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) + */ + @Override + public void contextInitialized(ServletContextEvent sce) + { + //Can add a ServletContextListener from a ServletContainerInitializer + sce.getServletContext().setAttribute("com.acme.AnnotationTest.listenerTest", Boolean.TRUE); + + //Can't add a ServletContextListener from a ServletContextListener + try + { + sce.getServletContext().addListener(new BarListener()); + sce.getServletContext().setAttribute("com.acme.AnnotationTest.listenerRegoTest", Boolean.FALSE); + } + catch (UnsupportedOperationException e) + { + sce.getServletContext().setAttribute("com.acme.AnnotationTest.listenerRegoTest", Boolean.TRUE); + } + catch (Exception e) + { + sce.getServletContext().setAttribute("com.acme.AnnotationTest.listenerRegoTest", Boolean.FALSE); + } + } + + /** + * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) + */ + @Override + public void contextDestroyed(ServletContextEvent sce) + { + + } + + } public void onStartup(Set<Class<?>> classes, ServletContext context) { context.setAttribute("com.acme.Foo", new ArrayList<Class>(classes)); ServletRegistration.Dynamic reg = context.addServlet("AnnotationTest", "com.acme.AnnotationTest"); context.setAttribute("com.acme.AnnotationTest.complete", (reg == null)); + context.addListener(new FooListener()); } } diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/AnnotationTest.java b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/AnnotationTest.java index f6b319a983..b3eecea853 100644 --- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/AnnotationTest.java +++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/AnnotationTest.java @@ -245,6 +245,14 @@ public class AnnotationTest extends HttpServlet Boolean complete = (Boolean)config.getServletContext().getAttribute("com.acme.AnnotationTest.complete"); out.println("<br/><b>Result: "+(complete.booleanValue()?"PASS":"FAIL")+"</b>"); + out.println("<h2>ServletContextListener Programmatic Registration from ServletContainerInitializer</h2>"); + Boolean programmaticListener = (Boolean)config.getServletContext().getAttribute("com.acme.AnnotationTest.listenerTest"); + out.println("<br/><b>Result: "+(programmaticListener.booleanValue()?"PASS":"FAIL")+"</b>"); + + out.println("<h2>ServletContextListener Programmatic Registration Prevented from ServletContextListener</h2>"); + Boolean programmaticListenerPrevention = (Boolean)config.getServletContext().getAttribute("com.acme.AnnotationTest.listenerRegoTest"); + out.println("<br/><b>Result: "+(programmaticListenerPrevention.booleanValue()?"PASS":"FAIL")+"</b>"); + out.println("<h2>@PostConstruct Callback</h2>"); out.println("<pre>"); out.println("@PostConstruct"); |