413018 ServletContext.addListener() should throw IllegalArgumentException if arg is not correct type of listener

This commit is contained in:
Jan Bartel 2013-08-02 12:26:54 +10:00
parent 89b33f5b88
commit 47a8809ec7
8 changed files with 173 additions and 22 deletions

View File

@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContextListener;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.webapp.WebAppContext;
@ -101,11 +102,12 @@ public class ContainerInitializer
for (String s : _applicableTypeNames)
classes.add(Loader.loadClass(context.getClass(), s));
}
context.getServletContext().setExtendedListenerTypes(true);
_target.onStartup(classes, context.getServletContext());
}
finally
{
{
context.getServletContext().setExtendedListenerTypes(false);
Thread.currentThread().setContextClassLoader(oldLoader);
}
}

View File

@ -40,6 +40,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
@ -61,6 +62,9 @@ import javax.servlet.SessionTrackingMode;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.ClassLoaderDump;
@ -103,11 +107,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
public static int SERVLET_MAJOR_VERSION=3;
public static int SERVLET_MINOR_VERSION=0;
public static final Class[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
ServletContextAttributeListener.class,
ServletRequestListener.class,
ServletRequestAttributeListener.class};
public static final int DEFAULT_LISTENER_TYPE_INDEX = 1;
public static final int EXTENDED_LISTENER_TYPE_INDEX = 0;
final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
private static final Logger LOG = Log.getLogger(ContextHandler.class);
private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
@ -1721,6 +1731,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
public class Context extends NoContext
{
protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
protected boolean _extendedListenerTypes = false;
/* ------------------------------------------------------------ */
protected Context()
@ -2125,6 +2137,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
try
{
Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
checkListener(clazz);
addListener(clazz);
}
catch (ClassNotFoundException e)
@ -2138,6 +2151,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
if (!_enabled)
throw new UnsupportedOperationException();
checkListener(t.getClass());
ContextHandler.this.addEventListener(t);
ContextHandler.this.addProgrammaticListener(t);
}
@ -2148,6 +2164,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (!_enabled)
throw new UnsupportedOperationException();
checkListener(listenerClass);
try
{
EventListener e = createListener(listenerClass);
@ -2173,6 +2191,34 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
}
}
public void checkListener (Class<? extends EventListener> listener) throws IllegalStateException
{
boolean ok = false;
int startIndex = (isExtendedListenerTypes()?EXTENDED_LISTENER_TYPE_INDEX:DEFAULT_LISTENER_TYPE_INDEX);
for (int i=startIndex;i<SERVLET_LISTENER_TYPES.length;i++)
{
if (SERVLET_LISTENER_TYPES[i].isAssignableFrom(listener))
{
ok = true;
break;
}
}
if (!ok)
throw new IllegalArgumentException("Inappropriate listener class "+listener.getName());
}
public void setExtendedListenerTypes (boolean extended)
{
_extendedListenerTypes = extended;
}
public boolean isExtendedListenerTypes()
{
return _extendedListenerTypes;
}
@Override
public ClassLoader getClassLoader()
{
@ -2210,7 +2256,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
{
return _enabled;
}
public <T> T createInstance (Class<T> clazz) throws Exception
{

View File

@ -29,6 +29,9 @@ import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.server.Request;
@ -46,6 +49,11 @@ public class SessionHandler extends ScopedHandler
final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public final static EnumSet<SessionTrackingMode> DEFAULT_TRACKING = EnumSet.of(SessionTrackingMode.COOKIE,SessionTrackingMode.URL);
public static final Class[] SESSION_LISTENER_TYPES = new Class[] {HttpSessionAttributeListener.class,
HttpSessionIdListener.class,
HttpSessionListener.class};
/* -------------------------------------------------------------- */

View File

@ -95,6 +95,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private String[] __dftProtectedTargets = {"/web-inf", "/meta-inf"};
public static String[] DEFAULT_CONFIGURATION_CLASSES =
{
"org.eclipse.jetty.webapp.WebInfConfiguration",
@ -1402,6 +1403,32 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
/* ------------------------------------------------------------ */
public class Context extends ServletContextHandler.Context
{
/* ------------------------------------------------------------ */
@Override
public void checkListener(Class<? extends EventListener> listener) throws IllegalStateException
{
try
{
super.checkListener(listener);
}
catch (IllegalArgumentException e)
{
//not one of the standard servlet listeners, check our extended session listener types
boolean ok = false;
for (Class l:SessionHandler.SESSION_LISTENER_TYPES)
{
if (l.isAssignableFrom(listener))
{
ok = true;
break;
}
}
if (!ok)
throw new IllegalArgumentException("Inappropriate listener type "+listener.getName());
}
}
/* ------------------------------------------------------------ */
@Override
public URL getResource(String path) throws MalformedURLException
@ -1448,7 +1475,6 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
}
}
}
/* ------------------------------------------------------------ */

View File

@ -197,14 +197,14 @@ public class AnnotationTest extends HttpServlet
out.println("<pre>");
out.println("initParams={@WebInitParam(name=\"fromAnnotation\", value=\"xyz\")}");
out.println("</pre>");
out.println("<br/><b>Result: "+("xyz".equals(config.getInitParameter("fromAnnotation"))? "<span class=\"pass\">PASS": "<span class=\"fail\">FAIL")+"</span>");
out.println("<p><b>Result: "+("xyz".equals(config.getInitParameter("fromAnnotation"))? "<span class=\"pass\">PASS": "<span class=\"fail\">FAIL")+"</span></p>");
out.println("<h2>Init Params from web-fragment</h2>");
out.println("<pre>");
out.println("extra1=123, extra2=345");
out.println("</pre>");
boolean fragInitParamResult = "123".equals(config.getInitParameter("extra1")) && "345".equals(config.getInitParameter("extra2"));
out.println("<br/><b>Result: "+(fragInitParamResult? "<span class=\"pass\">PASS": "<span class=\"fail\">FAIL")+"</span>");
out.println("<p><b>Result: "+(fragInitParamResult? "<span class=\"pass\">PASS": "<span class=\"fail\">FAIL")+"</span></p>");
__HandlesTypes = Arrays.asList( "javax.servlet.GenericServlet",
@ -221,7 +221,7 @@ public class AnnotationTest extends HttpServlet
out.println("<pre>");
out.println("@HandlesTypes({javax.servlet.Servlet.class, Foo.class})");
out.println("</pre>");
out.print("<br/><b>Result: ");
out.print("<p><b>Result: ");
List<Class> classes = (List<Class>)config.getServletContext().getAttribute("com.acme.Foo");
List<String> classNames = new ArrayList<String>();
if (classes != null)
@ -241,19 +241,28 @@ public class AnnotationTest extends HttpServlet
}
else
out.print("<br/><span class=\"fail\">FAIL</span> (No such attribute com.acme.Foo)");
out.println("</b>");
out.println("</b></p>");
out.println("<h2>Complete Servlet Registration</h2>");
Boolean complete = (Boolean)config.getServletContext().getAttribute("com.acme.AnnotationTest.complete");
out.println("<br/><b>Result: "+(complete.booleanValue()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b>");
out.println("<p><b>Result: "+(complete.booleanValue()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b></p>");
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()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b>");
out.println("<p><b>Result: "+(programmaticListener.booleanValue()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b></p>");
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()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b>");
out.println("<p><b>Result: "+(programmaticListenerPrevention.booleanValue()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b></p>");
out.println("<h2>ServletContextListener Registration Prevented from ServletContextListener</h2>");
Boolean webListenerPrevention = (Boolean)config.getServletContext().getAttribute("com.acme.AnnotationTest.sclFromSclRegoTest");
out.println("<p><b>Result: "+(webListenerPrevention.booleanValue()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b></p>");
out.println("<h2>Invalid Type for Listener Detection</h2>");
Boolean badListener = (Boolean)config.getServletContext().getAttribute("com.acme.AnnotationTest.invalidListenerRegoTest");
out.println("<p><b>Result: "+(badListener.booleanValue()?"<span class=\"pass\">PASS":"<span class=\"fail\">FAIL")+"</span></b></p>");
out.println("<h2>@PostConstruct Callback</h2>");
out.println("<pre>");
@ -261,7 +270,7 @@ public class AnnotationTest extends HttpServlet
out.println("private void myPostConstructMethod ()");
out.println("{}");
out.println("</pre>");
out.println("<br/><b>Result: "+postConstructResult+"</b>");
out.println("<p><b>Result: "+postConstructResult+"</b></p>");
out.println("<h2>@Resource Injection for DataSource</h2>");
@ -272,8 +281,8 @@ public class AnnotationTest extends HttpServlet
out.println("myDS=ds;");
out.println("}");
out.println("</pre>");
out.println("<br/><b>Result: "+dsResult+"</b>");
out.println("<br/><b>JNDI Lookup Result: "+dsLookupResult+"</b>");
out.println("<p><b>Result: "+dsResult+"</b>");
out.println("<br/><b>JNDI Lookup Result: "+dsLookupResult+"</b></p>");
out.println("<h2>@Resource Injection for env-entry </h2>");
@ -283,19 +292,20 @@ public class AnnotationTest extends HttpServlet
out.println("@Resource(name=\"minAmount\")");
out.println("private Double minAmount;");
out.println("</pre>");
out.println("<br/><b>Result: "+envResult+": "+(maxAmount.compareTo(new Double(55))==0?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
out.println("<p><b>Result: "+envResult+": "+(maxAmount.compareTo(new Double(55))==0?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
out.println("<br/><b>JNDI Lookup Result: "+envLookupResult+"</b>");
out.println("<br/><b>Result: "+envResult2+": "+(minAmount.compareTo(new Double("0.99"))==0?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
out.println("<br/><b>JNDI Lookup Result: "+envLookupResult2+"</b>");
out.println("<br/><b>Result: "+envResult3+": "+(avgAmount.compareTo(new Double("1.25"))==0?" <span class=\"pass\">PASS":" <span class=\"fail\">FAIL")+"</span></b>");
out.println("<br/><b>JNDI Lookup Result: "+envLookupResult3+"</b>");
out.println("<br/><b>JNDI Lookup Result: "+envLookupResult3+"</b></p>");
out.println("<h2>@Resource Injection for UserTransaction </h2>");
out.println("<pre>");
out.println("@Resource(mappedName=\"UserTransaction\")");
out.println("private UserTransaction myUserTransaction;");
out.println("</pre>");
out.println("<br/><b>Result: "+txResult+"</b>");
out.println("<br/><b>JNDI Lookup Result: "+txLookupResult+"</b>");
out.println("<p><b>Result: "+txResult+"</b>");
out.println("<br/><b>JNDI Lookup Result: "+txLookupResult+"</b></p>");
out.println("</body>");
out.println("</html>");

View File

@ -17,6 +17,8 @@
//
package com.acme;
import java.util.EventListener;
import javax.annotation.Resource;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
@ -38,6 +40,26 @@ import javax.servlet.http.HttpSessionListener;
@WebListener
public class TestListener implements HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener
{
public class NaughtyServletContextListener implements ServletContextListener
{
@Override
public void contextInitialized(ServletContextEvent sce)
{
throw new IllegalStateException("Should not call NaughtServletContextListener.contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
throw new IllegalStateException("Should not call NaughtServletContextListener.contextDestroyed");
}
}
public class InvalidListener implements EventListener
{
}
@Resource(mappedName="maxAmount")
private Double maxAmount;
@ -69,7 +91,36 @@ public class TestListener implements HttpSessionListener, HttpSessionAttributeL
public void contextInitialized(ServletContextEvent sce)
{
//System.err.println("contextInitialized, maxAmount injected as "+maxAmount);
//Can't add a ServletContextListener from a ServletContextListener even if it is declared in web.xml
try
{
sce.getServletContext().addListener(new NaughtyServletContextListener());
sce.getServletContext().setAttribute("com.acme.AnnotationTest.sclFromSclRegoTest", Boolean.FALSE);
}
catch (IllegalArgumentException e)
{
sce.getServletContext().setAttribute("com.acme.AnnotationTest.sclFromSclRegoTest", Boolean.TRUE);
}
catch (Exception e)
{
sce.getServletContext().setAttribute("com.acme.AnnotationTest.sclFromSclRegoTest", Boolean.FALSE);
}
//Can't add an EventListener not part of the specified list for addListener()
try
{
sce.getServletContext().addListener(new InvalidListener());
sce.getServletContext().setAttribute("com.acme.AnnotationTest.invalidListenerRegoTest", Boolean.FALSE);
}
catch (IllegalArgumentException e)
{
sce.getServletContext().setAttribute("com.acme.AnnotationTest.invalidListenerRegoTest", Boolean.TRUE);
}
catch (Exception e)
{
sce.getServletContext().setAttribute("com.acme.AnnotationTest.invalidListenerRegoTest", Boolean.FALSE);
}
}
public void contextDestroyed(ServletContextEvent sce)

View File

@ -1,6 +1,6 @@
body {color: #2E2E2E; font-family:sans-serif; font-size:90%;}
h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;}
h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em;}
h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em; margin-top:2em;}
h3 {font-size:100%; letter-spacing: 0.1em;}
span.pass { color: green; }

View File

@ -0,0 +1,7 @@
body {color: #2E2E2E; font-family:sans-serif; font-size:90%;}
h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;}
h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em; margin-top:2em}
h3 {font-size:100%; letter-spacing: 0.1em;}
span.pass { color: green; }
span.fail { color:red; }