/**
* Copyright OPS4J
*
* 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.ops4j.pax.wicket.internal.servlet;
import static java.lang.reflect.Proxy.newProxyInstance;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.ops4j.pax.wicket.api.SuperFilter;
import org.ops4j.pax.wicket.internal.DefaultConfigurableFilterConfig;
import org.ops4j.pax.wicket.internal.PaxWicketApplicationFactory;
import org.ops4j.pax.wicket.internal.filter.FilterDelegator;
import org.ops4j.pax.wicket.internal.filter.PAXWicketFilterChain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Intercepts calls to a servlet before they are given to an underlying servlet, the initilization of
* {@link org.ops4j.pax.wicket.api.SuperFilter}s is done here
*
* @author nmw
* @version $Id: $Id
*/
public class ServletCallInterceptor implements Servlet {
private static final Logger LOG = LoggerFactory.getLogger(ServletCallInterceptor.class);
private static final Class<?>[] REQUEST_INTERFACES = new Class[]{
HttpServletRequest.class
};
private final PaxWicketApplicationFactory applicationFactory;
private final Servlet delegateServlet;
private Filter[] superFilter;
/**
* <p>Constructor for ServletCallInterceptor.</p>
*
* @param applicationFactory a {@link org.ops4j.pax.wicket.internal.PaxWicketApplicationFactory} object.
* @param delegateServlet a {@link javax.servlet.Servlet} object.
*/
public ServletCallInterceptor(PaxWicketApplicationFactory applicationFactory, Servlet delegateServlet) {
this.applicationFactory = applicationFactory;
this.delegateServlet = delegateServlet;
}
/** {@inheritDoc} */
public void init(ServletConfig config) throws ServletException {
LOG.info("Init servlet...");
// We must init the superfilters first...
List<SuperFilter> superFilterList = applicationFactory.getSuperFilterList();
Filter[] superFilter = new Filter[superFilterList.size()];
if (superFilter.length > 0) {
LOG.info("Init {} superfilters...", superFilter.length);
for (int i = 0; i < superFilter.length; i++) {
SuperFilter annotation = superFilterList.get(i);
Class<? extends Filter> filterClass = annotation.filter();
String initParameterPath = annotation.initParameter();
// Create a new instance...
try {
superFilter[i] = filterClass.newInstance();
} catch (InstantiationException e) {
throw new ServletException("can't instantiate Filter " + filterClass, e);
} catch (IllegalAccessException e) {
throw new ServletException("can't access Filter " + filterClass + " constructor", e);
}
// init it ...
DefaultConfigurableFilterConfig filterConfig =
new DefaultConfigurableFilterConfig(filterClass.getName() + "-" + i, config);
if (initParameterPath != null && initParameterPath.length() > 0) {
// Fetch all classlaoder...
ClassLoader appLoader = applicationFactory.getWebApplicationFactory().getClass()
.getClassLoader();
ClassLoader filterLoader = filterClass.getClassLoader();
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
// read the properties
Properties properties =
readProperties(initParameterPath, appLoader, filterLoader, contextClassLoader);
// create filter config
Set<String> propertyNames = properties.stringPropertyNames();
for (String property : propertyNames) {
filterConfig.putInitParameter(property, properties.getProperty(property));
}
}
if (LOG.isInfoEnabled()) {
StringBuilder sb = new StringBuilder();
Enumeration<?> parameterNames = filterConfig.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
sb.append("\r\n\t");
Object object = parameterNames.nextElement();
sb.append(object);
sb.append(": ");
sb.append(filterConfig.getInitParameter(object.toString()));
}
LOG.info("Init super filter {}/{} (class = {}) init parameter = {}", new Object[]{ i + 1,
superFilter.length, filterClass, sb });
}
superFilter[i].init(filterConfig);
}
}
this.superFilter = superFilter;
// now init the delegate
delegateServlet.init(config);
}
/**
* @param initParameterPath
* @param classLoader
* @return
* @throws ServletException
*/
private Properties readProperties(String initParameterPath, ClassLoader... classLoader) throws ServletException {
Properties properties = new Properties();
for (ClassLoader loader : classLoader) {
InputStream stream = loader.getResourceAsStream(initParameterPath);
if (stream != null) {
try {
properties.load(stream);
} catch (IOException e) {
throw new ServletException("can't load init parameter '" + initParameterPath + "'", e);
}
}
}
return properties;
}
/**
* <p>getServletConfig.</p>
*
* @return a {@link javax.servlet.ServletConfig} object.
*/
public ServletConfig getServletConfig() {
return delegateServlet.getServletConfig();
}
/** {@inheritDoc} */
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
// Check if we should replace this request
if (req instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
if (!isProxied(httpServletRequest)) {
req = newProxyRequest(httpServletRequest);
}
}
// Start the filter process...
FilterDelegator filterDelegator = applicationFactory.getFilterDelegator();
if (filterDelegator != null) {
filterDelegator.doFilter(superFilter, req, res);
} else if (superFilter.length > 0) {
FilterChain chain = new PAXWicketFilterChain(Arrays.asList(superFilter), delegateServlet);
chain.doFilter(req, res);
} else {
delegateServlet.service(req, res);
}
}
/**
* Check if this request is already proxied by pax wicket
*
* @param httpServletRequest
* @return true if the instance is a proxy and invocationhandler is a {@link ServletRequestInvocationHandler}
*/
private boolean isProxied(HttpServletRequest httpServletRequest) {
if (Proxy.isProxyClass(httpServletRequest.getClass())) {
InvocationHandler handler = Proxy.getInvocationHandler(httpServletRequest);
return handler instanceof ServletRequestInvocationHandler;
}
return false;
}
/**
* <p>getServletInfo.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getServletInfo() {
return delegateServlet.getServletInfo();
}
/**
* <p>destroy.</p>
*/
public void destroy() {
// destroy all filter first...
for (Filter filter : superFilter) {
try {
filter.destroy();
} catch (RuntimeException e) {
LOG.warn("destroying a filter failed", e);
}
}
// destroy the delegate...
delegateServlet.destroy();
}
private HttpServletRequest newProxyRequest(HttpServletRequest request) {
ClassLoader loader = getClass().getClassLoader();
ServletRequestInvocationHandler ih =
new ServletRequestInvocationHandler(request, applicationFactory.getMountPoint());
return (HttpServletRequest) newProxyInstance(loader, REQUEST_INTERFACES, ih);
}
}