/* Copyright (c) 2001 - 2011 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.monitor;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.geoserver.filters.GeoServerFilter;
import org.geoserver.monitor.RequestData.Status;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.GeoServerExtensions;
import org.geotools.util.logging.Logging;
public class MonitorFilter implements GeoServerFilter {
static Logger LOGGER = Logging.getLogger("org.geoserver.monitor");
Monitor monitor;
MonitorRequestFilter requestFilter;
ExecutorService postProcessExecutor;
public MonitorFilter(Monitor monitor, MonitorRequestFilter requestFilter) {
this.monitor = monitor;
this.requestFilter = requestFilter;
postProcessExecutor = Executors.newFixedThreadPool(2);
if (monitor.isEnabled()) {
LOGGER.info("Monitor extension enabled");
}
else {
String msg ="Monitor extension disabled";
if (monitor.getConfig().getError() != null) {
msg += ": " + monitor.getConfig().getError().getLocalizedMessage();
}
LOGGER.info(msg);
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//check if enabled, and ignore non http requests
if (!monitor.isEnabled() || !(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (requestFilter.filter(req)) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(req.getRequestURI() + " was filtered from monitoring");
}
//don't monitor this request
chain.doFilter(request, response);
return;
}
//start a new request
RequestData data = monitor.start();
data.setStartTime(new Date());
//fill in the initial data
data.setPath(req.getServletPath() + req.getPathInfo());
if (req.getQueryString() != null) {
data.setQueryString(URLDecoder.decode(req.getQueryString(), "UTF-8"));
}
data.setHttpMethod(req.getMethod());
data.setBodyContentLength(req.getContentLength());
data.setBodyContentType(req.getContentType());
String serverName = System.getProperty("http.serverName");
if (serverName == null) {
serverName = req.getServerName();
}
data.setHost(serverName);
data.setInternalHost(InternalHostname.get());
data.setRemoteAddr(getRemoteAddr(req));
data.setStatus(Status.RUNNING);
if (SecurityContextHolder.getContext() != null
&& SecurityContextHolder.getContext().getAuthentication() != null) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth.getPrincipal() != null && auth.getPrincipal() instanceof User) {
data.setRemoteUser(((User)auth.getPrincipal()).getUsername());
}
}
//wrap the request and response
request = new MonitorServletRequest(req, monitor.getConfig().getMaxBodySize());
response = new MonitorServletResponse(resp);
monitor.update();
//execute the request
Throwable error = null;
try {
chain.doFilter(request, response);
}
catch(Throwable t) {
error = t;
}
data = monitor.current();
data.setBody(((MonitorServletRequest)request).getBodyContent());
data.setBodyContentLength(((MonitorServletRequest)request).getBytesRead());
data.setResponseContentType(response.getContentType());
data.setResponseLength(((MonitorServletResponse)response).getContentLength());
data.setResponseStatus(((MonitorServletResponse)response).getStatus());
if (error != null) {
data.setStatus(Status.FAILED);
data.setErrorMessage(error.getLocalizedMessage());
data.setError(error);
}
if (data.getStatus() != Status.FAILED) {
data.setStatus(Status.FINISHED);
}
data.setEndTime(new Date());
data.setTotalTime(data.getEndTime().getTime() - data.getStartTime().getTime());
monitor.update();
data = monitor.current();
monitor.complete();
//post processing
postProcessExecutor.execute(new PostProcessTask(monitor, data, req, resp));
if (error != null) {
if (error instanceof RuntimeException) {
throw (RuntimeException)error;
}
else {
throw new RuntimeException(error);
}
}
}
public void destroy() {
postProcessExecutor.shutdown();
monitor.dispose();
}
String getRemoteAddr(HttpServletRequest req) {
String forwardedFor = req.getHeader("X-Forwarded-For");
if (forwardedFor != null) {
String[] ips = forwardedFor.split(", ");
return ips[0];
} else {
return req.getRemoteAddr();
}
}
static class PostProcessTask implements Runnable {
Monitor monitor;
RequestData data;
HttpServletRequest request;
HttpServletResponse response;
PostProcessTask(Monitor monitor, RequestData data, HttpServletRequest request, HttpServletResponse response) {
this.monitor = monitor;
this.data = data;
this.request = request;
this.response = response;
}
public void run() {
try {
List<RequestPostProcessor> pp = new ArrayList();
pp.add(new ReverseDNSPostProcessor());
pp.addAll(GeoServerExtensions.extensions(RequestPostProcessor.class));
for (RequestPostProcessor p : pp) {
try {
p.run(data, request, response);
}
catch(Exception e) {
LOGGER.log(Level.WARNING, "Post process task failed", e);
}
}
monitor.postProcessed(data);
}
finally {
monitor = null;
data = null;
request = null;
response = null;
}
}
}
}