/*
* Copyright 2013 <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ocpsoft.rewrite.servlet;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.ocpsoft.common.pattern.WeightedComparator;
import org.ocpsoft.common.services.ServiceLoader;
import org.ocpsoft.common.spi.ServiceEnricher;
import org.ocpsoft.common.util.Iterators;
import org.ocpsoft.logging.Logger;
import org.ocpsoft.logging.Logger.Level;
import org.ocpsoft.rewrite.AbstractRewrite;
import org.ocpsoft.rewrite.Version;
import org.ocpsoft.rewrite.config.ConfigurationProvider;
import org.ocpsoft.rewrite.el.spi.ExpressionLanguageProvider;
import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.servlet.event.BaseRewrite;
import org.ocpsoft.rewrite.servlet.event.InboundServletRewrite;
import org.ocpsoft.rewrite.servlet.http.HttpRewriteLifecycleContext;
import org.ocpsoft.rewrite.servlet.impl.HttpRewriteContextImpl;
import org.ocpsoft.rewrite.servlet.spi.ContextListener;
import org.ocpsoft.rewrite.servlet.spi.InboundRewriteProducer;
import org.ocpsoft.rewrite.servlet.spi.OutboundRewriteProducer;
import org.ocpsoft.rewrite.servlet.spi.RequestCycleWrapper;
import org.ocpsoft.rewrite.servlet.spi.RequestListener;
import org.ocpsoft.rewrite.servlet.spi.RequestParameterProvider;
import org.ocpsoft.rewrite.servlet.spi.RewriteLifecycleListener;
import org.ocpsoft.rewrite.servlet.spi.RewriteResultHandler;
import org.ocpsoft.rewrite.spi.ConfigurationCacheProvider;
import org.ocpsoft.rewrite.spi.InvocationResultHandler;
import org.ocpsoft.rewrite.spi.RewriteProvider;
import org.ocpsoft.rewrite.util.ServiceLogger;
/**
* {@link Filter} responsible for handling all inbound {@link org.ocpsoft.rewrite.event.Rewrite} events.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class RewriteFilter implements Filter
{
private static Logger log = Logger.getLogger(RewriteFilter.class);
private static String FILTER_COUNT_KEY = RewriteFilter.class.getName() + "_FILTER_COUNT";
private List<RewriteLifecycleListener<Rewrite>> listeners;
private List<RequestCycleWrapper<ServletRequest, ServletResponse>> wrappers;
private List<RewriteProvider<ServletContext, Rewrite>> providers;
private List<RewriteResultHandler> resultHandlers;
private List<InboundRewriteProducer<ServletRequest, ServletResponse>> inbound;
private List<OutboundRewriteProducer<ServletRequest, ServletResponse, Object>> outbound;
private ServletContext servletContext;
@Override
@SuppressWarnings("unchecked")
public void init(final FilterConfig filterConfig) throws ServletException
{
if (log.isInfoEnabled())
log.info("RewriteFilter starting up...");
servletContext = filterConfig.getServletContext();
listeners = Iterators.asList(ServiceLoader.load(RewriteLifecycleListener.class));
wrappers = Iterators.asList(ServiceLoader.load(RequestCycleWrapper.class));
providers = Iterators.asList(ServiceLoader.load(RewriteProvider.class));
resultHandlers = Iterators.asList(ServiceLoader.load(RewriteResultHandler.class));
inbound = Iterators.asList(ServiceLoader.load(InboundRewriteProducer.class));
outbound = Iterators.asList(ServiceLoader.load(OutboundRewriteProducer.class));
Collections.sort(listeners, new WeightedComparator());
Collections.sort(wrappers, new WeightedComparator());
Collections.sort(providers, new WeightedComparator());
Collections.sort(resultHandlers, new WeightedComparator());
Collections.sort(inbound, new WeightedComparator());
Collections.sort(outbound, new WeightedComparator());
ServiceLogger.logLoadedServices(log, RewriteLifecycleListener.class, listeners);
ServiceLogger.logLoadedServices(log, RequestCycleWrapper.class, wrappers);
ServiceLogger.logLoadedServices(log, RewriteProvider.class, providers);
ServiceLogger.logLoadedServices(log, RewriteResultHandler.class, resultHandlers);
ServiceLogger.logLoadedServices(log, InboundRewriteProducer.class, inbound);
ServiceLogger.logLoadedServices(log, OutboundRewriteProducer.class, outbound);
/*
* Log more services for debug purposes only.
*/
ServiceLogger.logLoadedServices(log, ContextListener.class,
Iterators.asList(ServiceLoader.load(ContextListener.class)));
ServiceLogger.logLoadedServices(log, RequestListener.class,
Iterators.asList(ServiceLoader.load(RequestListener.class)));
ServiceLogger.logLoadedServices(log, RequestParameterProvider.class,
Iterators.asList(ServiceLoader.load(RequestParameterProvider.class)));
ServiceLogger.logLoadedServices(log, ExpressionLanguageProvider.class,
Iterators.asList(ServiceLoader.load(ExpressionLanguageProvider.class)));
ServiceLogger.logLoadedServices(log, InvocationResultHandler.class,
Iterators.asList(ServiceLoader.load(InvocationResultHandler.class)));
ServiceLogger.logLoadedServices(log, ServiceEnricher.class,
Iterators.asList(ServiceLoader.load(ServiceEnricher.class)));
/*
* Load ConfigurationProviders and ConfigurationCacheProviders here solely so that we
* can see registered implementations at boot time.
*/
ServiceLogger.logLoadedServices(log, ConfigurationCacheProvider.class,
Iterators.asList(ServiceLoader.load(ConfigurationCacheProvider.class)));
List<ConfigurationProvider<?>> configurations = Iterators.asList(ServiceLoader
.load(ConfigurationProvider.class));
ServiceLogger.logLoadedServices(log, ConfigurationProvider.class, configurations);
for (RewriteProvider<ServletContext, Rewrite> provider : providers) {
if (provider instanceof ServletRewriteProvider)
((ServletRewriteProvider<?>) provider).init(servletContext);
}
if ((configurations == null) || configurations.isEmpty())
{
if (log.isWarnEnabled())
log.warn("No ConfigurationProviders were registered: " +
"Rewrite will not be enabled on this application. " +
"Did you forget to create a '/META-INF/services/" + ConfigurationProvider.class.getName() +
" file containing the fully qualified name of your provider implementation?");
}
if (log.isInfoEnabled())
log.info(Version.getFullName() + " initialized.");
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException
{
InboundServletRewrite<ServletRequest, ServletResponse> event = createRewriteEvent(request,
response);
if (event == null)
{
if (log.isWarnEnabled())
log.warn("No Rewrite event was produced - RewriteFilter disabled on this request.");
chain.doFilter(request, response);
}
else
{
incrementFilterCount(request);
if (request.getAttribute(RewriteLifecycleContext.LIFECYCLE_CONTEXT_KEY) == null)
{
HttpRewriteLifecycleContext context = new HttpRewriteContextImpl(inbound, outbound, listeners,
resultHandlers, wrappers, providers);
request.setAttribute(RewriteLifecycleContext.LIFECYCLE_CONTEXT_KEY, context);
}
for (RewriteLifecycleListener<Rewrite> listener : listeners)
{
if (listener.handles(event))
listener.beforeInboundLifecycle(event);
}
for (RequestCycleWrapper<ServletRequest, ServletResponse> wrapper : wrappers)
{
if (wrapper.handles(event))
{
event.setRequest(wrapper.wrapRequest(event.getRequest(), event.getResponse(), servletContext));
event.setResponse(wrapper.wrapResponse(event.getRequest(), event.getResponse(), servletContext));
}
}
try {
rewrite(event);
}
catch (ServletException e) {
if (getFilterCount(request) == 1)
AbstractRewrite.logEvaluatedRules(event, Level.ERROR);
decrementFilterCount(request);
throw e;
}
catch (RuntimeException e) {
if (getFilterCount(request) == 1)
AbstractRewrite.logEvaluatedRules(event, Level.ERROR);
decrementFilterCount(request);
throw e;
}
if (!event.getFlow().is(BaseRewrite.ServletRewriteFlow.ABORT_REQUEST))
{
if (log.isDebugEnabled())
log.debug("RewriteFilter passing control of request to underlying application.");
if (response.isCommitted() && log.isWarnEnabled())
log.warn("Response has already been committed, and further write operations are not permitted. "
+ "This may result in an IllegalStateException being triggered by the underlying application. To avoid this situation, "
+ "consider adding a Rule `.when(Direction.isInbound().and(Response.isCommitted())).perform(Lifecycle.abort())`, or "
+ "figure out where the response is being incorrectly committed and correct the bug in the offending code.");
chain.doFilter(event.getRequest(), event.getResponse());
if (log.isDebugEnabled())
log.debug("Control of request returned to RewriteFilter.");
}
for (RewriteLifecycleListener<Rewrite> listener : listeners)
{
if (listener.handles(event))
listener.afterInboundLifecycle(event);
}
if (getFilterCount(request) == 1)
AbstractRewrite.logEvaluatedRules(event, Level.DEBUG);
decrementFilterCount(request);
}
}
public InboundServletRewrite<ServletRequest, ServletResponse> createRewriteEvent(final ServletRequest request,
final ServletResponse response)
{
for (InboundRewriteProducer<ServletRequest, ServletResponse> producer : inbound)
{
InboundServletRewrite<ServletRequest, ServletResponse> event = producer
.createInboundRewrite(request, response, servletContext);
if (event != null)
return event;
}
return null;
}
private void rewrite(final InboundServletRewrite<ServletRequest, ServletResponse> event)
throws ServletException, IOException
{
int listenerCount = listeners.size();
for (int i = 0; i < listenerCount; i++)
{
RewriteLifecycleListener<Rewrite> listener = listeners.get(i);
if (listener.handles(event))
listener.beforeInboundRewrite(event);
}
int providerCount = providers.size();
for (int i = 0; i < providerCount; i++)
{
RewriteProvider<ServletContext, Rewrite> provider = providers.get(i);
if (provider.handles(event))
{
provider.rewrite(event);
if (event.getFlow().is(BaseRewrite.ServletRewriteFlow.HANDLED))
{
if (log.isDebugEnabled())
log.debug("Event flow marked as HANDLED. No further processing will occur.");
break;
}
}
}
for (int i = 0; i < listenerCount; i++)
{
RewriteLifecycleListener<Rewrite> listener = listeners.get(i);
if (listener.handles(event))
listener.afterInboundRewrite(event);
}
int handlerCount = resultHandlers.size();
for (int i = 0; i < handlerCount; i++)
{
if (resultHandlers.get(i).handles(event))
resultHandlers.get(i).handleResult(event);
}
}
@Override
public void destroy()
{
log.info("RewriteFilter shutting down...");
for (RewriteProvider<ServletContext, Rewrite> provider : providers) {
if (provider instanceof ServletRewriteProvider)
((ServletRewriteProvider<?>) provider).shutdown(servletContext);
}
log.info("RewriteFilter deactivated.");
}
private int getFilterCount(ServletRequest request)
{
return (Integer) request.getAttribute(FILTER_COUNT_KEY);
}
private void decrementFilterCount(ServletRequest request)
{
Integer count = (Integer) request.getAttribute(FILTER_COUNT_KEY);
if (count != null)
{
count--;
}
request.setAttribute(FILTER_COUNT_KEY, count);
}
private void incrementFilterCount(ServletRequest request)
{
Integer count = (Integer) request.getAttribute(FILTER_COUNT_KEY);
if (count == null)
count = 1;
else
count++;
request.setAttribute(FILTER_COUNT_KEY, count);
}
}