Merge branch 'master' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project

Conflicts:
	jetty-server/src/main/config/etc/jetty.xml
This commit is contained in:
Greg Wilkins 2013-03-02 09:32:57 +11:00
commit 6478306812
23 changed files with 389 additions and 126 deletions

View File

@ -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
{
@ -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();

View File

@ -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);
}

View File

@ -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);
@ -131,10 +132,12 @@ public class ServletContainerInitializerListener implements ServletContextListen
}
/**
* @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()
{
}

View File

@ -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;
}
}

View File

@ -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
{
@Mock
private EndPoint _endPointMock;
private WriteFlusher _flusher;
private final AtomicBoolean _flushIncomplete = new AtomicBoolean(false);
private final ExecutorService executor = Executors.newFixedThreadPool(16);
@Mock
private EndPoint _endPointMock;
private WriteFlusher _flusher;
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;

View File

@ -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)

View File

@ -55,7 +55,7 @@
<!-- =========================================================== -->
<Call name="addBean">
<Arg>
<New class="org.eclipse.jetty.util.thread.TimerScheduler"/>
<New class="org.eclipse.jetty.util.thread.ScheduledExecutorScheduler"/>
</Arg>
</Call>

View File

@ -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.
@ -156,7 +156,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co
/**
* @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();

View File

@ -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;
/* ------------------------------------------------------------ */
@ -343,7 +343,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
}
private static class LRMScheduler extends TimerScheduler
private static class LRMScheduler extends ScheduledExecutorScheduler
{
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -73,6 +73,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
@ -109,6 +115,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>

View File

@ -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

View File

@ -111,6 +111,13 @@ public class MuxChannel implements LogicalConnection, IncomingFrames, SuspendTok
return null;
}
@Override
public long getMaxIdleTimeout()
{
// TODO Auto-generated method stub
return 0;
}
@Override
public WebSocketPolicy getPolicy()
{
@ -205,6 +212,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)
{

View File

@ -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;
@ -568,6 +574,12 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
super.setInputBufferSize(inputBufferSize);
}
@Override
public void setMaxIdleTimeout(long ms)
{
getEndPoint().setIdleTimeout(ms);
}
@Override
public void setSession(WebSocketSession session)
{

View File

@ -91,6 +91,13 @@ public class LocalWebSocketConnection implements LogicalConnection, IncomingFram
return null;
}
@Override
public long getMaxIdleTimeout()
{
// TODO Auto-generated method stub
return 0;
}
@Override
public WebSocketPolicy getPolicy()
{
@ -148,6 +155,13 @@ public class LocalWebSocketConnection implements LogicalConnection, IncomingFram
{
}
@Override
public void setMaxIdleTimeout(long ms)
{
// TODO Auto-generated method stub
}
@Override
public void setNextIncomingFrames(IncomingFrames incoming)
{

View File

@ -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;

View File

@ -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
* {
* &#064;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);
* }
* }

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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");