Skip to content

Commit

Permalink
Merge pull request #5273 from eclipse/jetty-9.4.x-5032-minimal-extens…
Browse files Browse the repository at this point in the history
…ible-web-apps

Issue #5032 - Minimal Extensible Web App changes
  • Loading branch information
joakime committed Sep 17, 2020
2 parents c6ed603 + 59976dc commit 0db3663
Show file tree
Hide file tree
Showing 9 changed files with 606 additions and 52 deletions.
Expand Up @@ -19,6 +19,7 @@
package org.eclipse.jetty.servlet;

import java.io.IOException;
import java.util.function.BiFunction;
import javax.servlet.ServletContext;
import javax.servlet.UnavailableException;

Expand Down Expand Up @@ -185,6 +186,47 @@ public synchronized boolean isInstance()
return _instance != null;
}

/**
* Wrap component using component specific Wrapper Function beans.
*
* @param component the component to optionally wrap
* @param wrapperFunctionType the bean class type to look for in the {@link ServletContextHandler}
* @param function the BiFunction to execute for each {@code wrapperFunctionType} Bean found (passing in the component and component type)
* @param <W> the "wrapper function" implementation. (eg: {@code ServletHolder.WrapperFunction} or {@code FilterHolder.WrapperFunction}, etc)
* @return the component that has passed through all Wrapper Function beans found.
*/
protected <W> T wrap(final T component, final Class<W> wrapperFunctionType, final BiFunction<W, T, T> function)
{
T ret = component;
ServletContextHandler contextHandler = getServletHandler().getServletContextHandler();
if (contextHandler == null)
{
ContextHandler.Context context = ContextHandler.getCurrentContext();
contextHandler = (ServletContextHandler)(context == null ? null : context.getContextHandler());
}

if (contextHandler != null)
{
for (W wrapperFunction : contextHandler.getBeans(wrapperFunctionType))
{
ret = function.apply(wrapperFunction, ret);
}
}
return ret;
}

protected T unwrap(final T component)
{
T ret = component;

while (ret instanceof Wrapped)
{
// noinspection unchecked,rawtypes
ret = (T)((Wrapped)ret).getWrapped();
}
return ret;
}

@Override
public void dump(Appendable out, String indent) throws IOException
{
Expand All @@ -196,4 +238,9 @@ public String dump()
{
return Dumpable.dump(this);
}

interface Wrapped<C>
{
C getWrapped();
}
}
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, FilterHolder.WrapFunction.class, FilterHolder.WrapFunction::wrapFilter);
_config = new Config();
if (LOG.isDebugEnabled())
LOG.debug("Filter.init {}", _filter);
Expand Down Expand Up @@ -156,13 +161,19 @@ 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;

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

// destroy the wrapped filter, in case there is special behaviour
filter.destroy();
}

public synchronized void setFilter(Filter filter)
Expand All @@ -175,6 +186,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 +282,69 @@ 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 {@code ServletContextHandler} or {@code 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 WrapFunction
{
/**
* Optionally wrap the Filter.
*
* @param filter the Filter being passed in.
* @return the Filter (extend from {@link FilterHolder.Wrapper} if you do wrap the Filter)
*/
Filter wrapFilter(Filter filter);
}

public static class Wrapper implements Filter, Wrapped<Filter>
{
private final Filter _filter;

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

@Override
public Filter getWrapped()
{
return _filter;
}

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

@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, WrapFunction.class, WrapFunction::wrapEventListener);
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 @@ -131,4 +132,46 @@ public String toString()
{
return super.toString() + ": " + getClassName();
}

/**
* Experimental Wrapper mechanism for Servlet EventListeners.
* <p>
* Beans in {@code ServletContextHandler} or {@code WebAppContext} that implement this interface
* will be called to optionally wrap any newly created Servlet EventListeners before
* they are used for the first time.
* </p>
*/
public interface WrapFunction
{
/**
* Optionally wrap the Servlet EventListener.
*
* @param listener the Servlet EventListener being passed in.
* @return the Servlet EventListener (extend from {@link ListenerHolder.Wrapper}
* if you do wrap the Servlet EventListener)
*/
EventListener wrapEventListener(EventListener listener);
}

public static class Wrapper implements EventListener, Wrapped<EventListener>
{
final EventListener _listener;

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

@Override
public EventListener getWrapped()
{
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 0db3663

Please sign in to comment.