Skip to content

Commit

Permalink
Issue #5032 - Minimal Extensible Web App changes
Browse files Browse the repository at this point in the history
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
  • Loading branch information
joakime committed Sep 15, 2020
1 parent 3849a91 commit 30caaba
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 30 deletions.
Expand Up @@ -24,12 +24,16 @@
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.Dumpable;
Expand Down Expand Up @@ -128,6 +132,7 @@ public void initialize() throws Exception
throw ex;
}
}
_filter = wrap(_filter);
_config = new Config();
if (LOG.isDebugEnabled())
LOG.debug("Filter.init {}", _filter);
Expand Down Expand Up @@ -156,13 +161,41 @@ public void doStop()

@Override
public void destroyInstance(Object o)
throws Exception
{
if (o == null)
return;
Filter f = (Filter)o;
f.destroy();
getServletHandler().destroyFilter(f);

Filter filter = (Filter)o;
// destroy the wrapped filter, in case there is special behaviour
filter.destroy();
// need to use the unwrapped filter because lifecycle callbacks such as
// postconstruct and predestroy are based off the classname and the wrapper
// classes are unknown outside the ServletHolder
getServletHandler().destroyFilter(unwrap(filter));
}

private Filter wrap(final Filter filter)
{
Filter ret = filter;
ServletContextHandler contextHandler = getServletHandler().getServletContextHandler();
if (contextHandler != null)
{
for (FilterHolder.WrapperFunction wrapperFunction : contextHandler.getBeans(FilterHolder.WrapperFunction.class))
{
ret = wrapperFunction.wrapFilter(ret);
}
}
return ret;
}

private Filter unwrap(Filter filter)
{
Filter unwrapped = filter;
while (FilterHolder.WrapperFilter.class.isAssignableFrom(unwrapped.getClass()))
{
unwrapped = ((FilterHolder.WrapperFilter)unwrapped).getWrappedFilter();
}
return unwrapped;
}

public synchronized void setFilter(Filter filter)
Expand All @@ -175,6 +208,13 @@ public Filter getFilter()
return _filter;
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
_filter.doFilter(request, response, chain);
}

@Override
public void dump(Appendable out, String indent) throws IOException
{
Expand Down Expand Up @@ -264,11 +304,62 @@ public Collection<String> getUrlPatternMappings()

class Config extends HolderConfig implements FilterConfig
{

@Override
public String getFilterName()
{
return getName();
}
}

/**
* Experimental Wrapper mechanism for Filter objects.
* <p>
* Beans in ServletContextHandler or WebAppContext that implement this interface
* will be called to optionally wrap any newly created Filters
* (before their {@link Filter#init(FilterConfig)} method is called)
* </p>
*/
public interface WrapperFunction
{
Filter wrapFilter(Filter filter);
}

public static class WrapperFilter implements Filter
{
private final Filter _filter;

public WrapperFilter(Filter filter)
{
_filter = Objects.requireNonNull(filter, "Filter cannot be null");
}

@Override
public void init(FilterConfig filterConfig) throws ServletException
{
_filter.init(filterConfig);
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
_filter.doFilter(request, response, chain);
}

@Override
public void destroy()
{
_filter.destroy();
}

public Filter getWrappedFilter()
{
return _filter;
}

@Override
public String toString()
{
return String.format("%s:%s", this.getClass().getSimpleName(), _filter.toString());
}
}
}
Expand Up @@ -102,6 +102,7 @@ public void doStart() throws Exception
throw ex;
}
}
_listener = wrap(_listener);
contextHandler.addEventListener(_listener);
}
}
Expand All @@ -117,7 +118,7 @@ public void doStop() throws Exception
ContextHandler contextHandler = ContextHandler.getCurrentContext().getContextHandler();
if (contextHandler != null)
contextHandler.removeEventListener(_listener);
getServletHandler().destroyListener(_listener);
getServletHandler().destroyListener(unwrap(_listener));
}
finally
{
Expand All @@ -126,9 +127,67 @@ public void doStop() throws Exception
}
}

private EventListener wrap(final EventListener listener)
{
EventListener ret = listener;
ServletContextHandler contextHandler = getServletHandler().getServletContextHandler();
if (contextHandler != null)
{
for (ListenerHolder.WrapperFunction wrapperFunction : contextHandler.getBeans(ListenerHolder.WrapperFunction.class))
{
ret = wrapperFunction.wrapEventListener(ret);
}
}
return ret;
}

private static EventListener unwrap(EventListener listener)
{
EventListener unwrapped = listener;
while (ListenerHolder.WrapperEventListener.class.isAssignableFrom(unwrapped.getClass()))
{
unwrapped = ((ListenerHolder.WrapperEventListener)unwrapped).getWrappedListener();
}
return unwrapped;
}

@Override
public String toString()
{
return super.toString() + ": " + getClassName();
}

/**
* Experimental Wrapper mechanism for Servlet Listeners.
* <p>
* Beans in ServletContextHandler or WebAppContext that implement this interface
* will be called to optionally wrap any newly created ServletListeners before
* they are used for the first time.
* </p>
*/
public interface WrapperFunction
{
EventListener wrapEventListener(EventListener listener);
}

public static class WrapperEventListener implements EventListener
{
final EventListener _listener;

public WrapperEventListener(EventListener listener)
{
_listener = listener;
}

public EventListener getWrappedListener()
{
return _listener;
}

@Override
public String toString()
{
return String.format("%s:%s", this.getClass().getSimpleName(), _listener.toString());
}
}
}
Expand Up @@ -395,6 +395,11 @@ public ServletContext getServletContext()
return _servletContext;
}

public ServletContextHandler getServletContextHandler()
{
return _contextHandler;
}

@ManagedAttribute(value = "mappings of servlets", readonly = true)
public ServletMapping[] getServletMappings()
{
Expand Down Expand Up @@ -1617,7 +1622,6 @@ public void doFilter(ServletRequest request, ServletResponse response)
{
if (LOG.isDebugEnabled())
LOG.debug("call filter {}", _filterHolder);
Filter filter = _filterHolder.getFilter();

//if the request already does not support async, then the setting for the filter
//is irrelevant. However if the request supports async but this filter does not
Expand All @@ -1627,15 +1631,15 @@ public void doFilter(ServletRequest request, ServletResponse response)
try
{
baseRequest.setAsyncSupported(false, _filterHolder.toString());
filter.doFilter(request, response, _next);
_filterHolder.doFilter(request, response, _next);
}
finally
{
baseRequest.setAsyncSupported(true, null);
}
}
else
filter.doFilter(request, response, _next);
_filterHolder.doFilter(request, response, _next);

return;
}
Expand Down Expand Up @@ -1690,7 +1694,6 @@ public void doFilter(ServletRequest request, ServletResponse response)
FilterHolder holder = _chain.get(_filter++);
if (LOG.isDebugEnabled())
LOG.debug("call filter " + holder);
Filter filter = holder.getFilter();

//if the request already does not support async, then the setting for the filter
//is irrelevant. However if the request supports async but this filter does not
Expand All @@ -1700,15 +1703,15 @@ public void doFilter(ServletRequest request, ServletResponse response)
try
{
_baseRequest.setAsyncSupported(false, holder.toString());
filter.doFilter(request, response, this);
holder.doFilter(request, response, this);
}
finally
{
_baseRequest.setAsyncSupported(true, null);
}
}
else
filter.doFilter(request, response, this);
holder.doFilter(request, response, this);

return;
}
Expand Down

0 comments on commit 30caaba

Please sign in to comment.