| 
68
 | 
     1 /*
 | 
| 
 | 
     2 Copyright (c) 2008  Franklin Schmidt <fschmidt@gmail.com>
 | 
| 
 | 
     3 
 | 
| 
 | 
     4 Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
| 
 | 
     5 of this software and associated documentation files (the "Software"), to deal
 | 
| 
 | 
     6 in the Software without restriction, including without limitation the rights
 | 
| 
 | 
     7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
| 
 | 
     8 copies of the Software, and to permit persons to whom the Software is
 | 
| 
 | 
     9 furnished to do so, subject to the following conditions:
 | 
| 
 | 
    10 
 | 
| 
 | 
    11 The above copyright notice and this permission notice shall be included in
 | 
| 
 | 
    12 all copies or substantial portions of the Software.
 | 
| 
 | 
    13 
 | 
| 
 | 
    14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
| 
 | 
    15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
| 
 | 
    16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
| 
 | 
    17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
| 
 | 
    18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
| 
 | 
    19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
| 
 | 
    20 THE SOFTWARE.
 | 
| 
 | 
    21 */
 | 
| 
 | 
    22 
 | 
| 
 | 
    23 package fschmidt.util.servlet;
 | 
| 
 | 
    24 
 | 
| 
 | 
    25 import fschmidt.util.java.ProcUtils;
 | 
| 
 | 
    26 import fschmidt.util.java.SimpleClassLoader;
 | 
| 
 | 
    27 import org.slf4j.Logger;
 | 
| 
 | 
    28 import org.slf4j.LoggerFactory;
 | 
| 
 | 
    29 
 | 
| 
 | 
    30 import javax.servlet.ServletContext;
 | 
| 
 | 
    31 import javax.servlet.ServletException;
 | 
| 
 | 
    32 import javax.servlet.ServletOutputStream;
 | 
| 
 | 
    33 import javax.servlet.http.HttpServlet;
 | 
| 
 | 
    34 import javax.servlet.http.HttpServletRequest;
 | 
| 
 | 
    35 import javax.servlet.http.HttpServletResponse;
 | 
| 
 | 
    36 import javax.servlet.http.HttpServletResponseWrapper;
 | 
| 
 | 
    37 import java.io.File;
 | 
| 
 | 
    38 import java.io.IOException;
 | 
| 
 | 
    39 import java.io.PrintWriter;
 | 
| 
 | 
    40 import java.net.URL;
 | 
| 
 | 
    41 import java.util.Arrays;
 | 
| 
 | 
    42 import java.util.Collection;
 | 
| 
 | 
    43 import java.util.Collections;
 | 
| 
 | 
    44 import java.util.HashMap;
 | 
| 
 | 
    45 import java.util.HashSet;
 | 
| 
 | 
    46 import java.util.LinkedHashMap;
 | 
| 
 | 
    47 import java.util.LinkedList;
 | 
| 
 | 
    48 import java.util.Map;
 | 
| 
 | 
    49 import java.util.Set;
 | 
| 
 | 
    50 
 | 
| 
 | 
    51 
 | 
| 
 | 
    52 public final class JtpContextServlet extends HttpServlet implements JtpContext {
 | 
| 
 | 
    53 	private static final Logger logger = LoggerFactory.getLogger(JtpContextServlet.class);
 | 
| 
 | 
    54 
 | 
| 
 | 
    55 	private static final Set<String> allowedMethods = new HashSet<String>(Arrays.asList(
 | 
| 
 | 
    56 		"GET", "POST", "HEAD"
 | 
| 
 | 
    57 	));
 | 
| 
 | 
    58 	private String base;
 | 
| 
 | 
    59 	private boolean reload = false;
 | 
| 
 | 
    60 	private boolean recompile = false;
 | 
| 
 | 
    61 	private SimpleClassLoader.Filter filter = null;
 | 
| 
 | 
    62 	private ClassLoader cl = null;
 | 
| 
 | 
    63 	private Map<String,HttpServlet> map = new HashMap<String,HttpServlet>();
 | 
| 
 | 
    64 	private long clTime;
 | 
| 
 | 
    65 	private Object lock = new Object();
 | 
| 
 | 
    66 	private HttpCache httpCache;
 | 
| 
 | 
    67 	private boolean isCaching;
 | 
| 
 | 
    68 	private String characterEncoding;
 | 
| 
 | 
    69 	private Map<String, String> customHeaders = new HashMap<String, String>();
 | 
| 
 | 
    70 	private UrlMapper urlMapper = new UrlMapper() {
 | 
| 
 | 
    71 		public UrlMapping getUrlMapping(HttpServletRequest request) {
 | 
| 
 | 
    72 			return null;
 | 
| 
 | 
    73 		}
 | 
| 
 | 
    74 	};
 | 
| 
 | 
    75 	private Set<String> errorCache = null;
 | 
| 
 | 
    76 	private Collection<String> ipList = null;
 | 
| 
 | 
    77 	private static final String authKeyAttr = "authKey";
 | 
| 
 | 
    78 	private static final String[] noModifyingEvents = new String[]{"_"};
 | 
| 
 | 
    79 
 | 
| 
 | 
    80 	public void setUrlMapper(UrlMapper urlMapper) {
 | 
| 
 | 
    81 		this.urlMapper = urlMapper;
 | 
| 
 | 
    82 	}
 | 
| 
 | 
    83 
 | 
| 
 | 
    84 	public HttpCache getHttpCache() {
 | 
| 
 | 
    85 		return httpCache;
 | 
| 
 | 
    86 	}
 | 
| 
 | 
    87 
 | 
| 
 | 
    88 	public void setHttpCache(HttpCache httpCache) {
 | 
| 
 | 
    89 		this.httpCache = httpCache;
 | 
| 
 | 
    90 	}
 | 
| 
 | 
    91 
 | 
| 
 | 
    92 	public void addCustomHeader(String key, String value) {
 | 
| 
 | 
    93 		this.customHeaders.put(key, value);
 | 
| 
 | 
    94 	}
 | 
| 
 | 
    95 
 | 
| 
 | 
    96 	public void unloadServlets() {
 | 
| 
 | 
    97 		if( !reload )
 | 
| 
 | 
    98 			throw new UnsupportedOperationException("'reload' must be set");
 | 
| 
 | 
    99 		synchronized(lock) {
 | 
| 
 | 
   100 			cl = new SimpleClassLoader(filter);
 | 
| 
 | 
   101 			map = new HashMap<String,HttpServlet>();
 | 
| 
 | 
   102 			clTime = System.currentTimeMillis();
 | 
| 
 | 
   103 		}
 | 
| 
 | 
   104 	}
 | 
| 
 | 
   105 
 | 
| 
 | 
   106 	public void setBase(String base) {
 | 
| 
 | 
   107 		if( base==null )
 | 
| 
 | 
   108 			throw new NullPointerException();
 | 
| 
 | 
   109 		this.base = base;
 | 
| 
 | 
   110 	}
 | 
| 
 | 
   111 
 | 
| 
 | 
   112 	public void init()
 | 
| 
 | 
   113 		throws ServletException
 | 
| 
 | 
   114 	{
 | 
| 
 | 
   115 		ServletContext context = getServletContext();
 | 
| 
 | 
   116 		String newBase = getInitParameter("base");
 | 
| 
 | 
   117 		if( newBase != null )
 | 
| 
 | 
   118 			setBase(newBase);
 | 
| 
 | 
   119 		recompile = Boolean.valueOf(getInitParameter("recompile"));
 | 
| 
 | 
   120 		reload = recompile || Boolean.valueOf(getInitParameter("reload"));
 | 
| 
 | 
   121 		if( reload ) {
 | 
| 
 | 
   122 			filter = new SimpleClassLoader.Filter(){
 | 
| 
 | 
   123 				final String s = base + ".";
 | 
| 
 | 
   124 				public boolean load(String className)  {
 | 
| 
 | 
   125 					return className.startsWith(s);
 | 
| 
 | 
   126 				}
 | 
| 
 | 
   127 			};
 | 
| 
 | 
   128 			unloadServlets();
 | 
| 
 | 
   129 		}
 | 
| 
 | 
   130 		context.setAttribute(JtpContext.attrName,this);
 | 
| 
 | 
   131 		String servletS = getInitParameter("servlet");
 | 
| 
 | 
   132 		if( servletS != null ) {
 | 
| 
 | 
   133 			throw new RuntimeException("the 'servlet' init parameter is no longer supported");
 | 
| 
 | 
   134 		}
 | 
| 
 | 
   135 		isCaching = "true".equalsIgnoreCase(getInitParameter("cache"));
 | 
| 
 | 
   136 		if( isCaching ) {
 | 
| 
 | 
   137 			if( httpCache==null ) {
 | 
| 
 | 
   138 				logger.error("can't set init parameter 'cache' to true without httpCache");
 | 
| 
 | 
   139 				System.exit(-1);
 | 
| 
 | 
   140 			}
 | 
| 
 | 
   141 			logger.info("cache");
 | 
| 
 | 
   142 		}
 | 
| 
 | 
   143 		characterEncoding = getInitParameter("characterEncoding");
 | 
| 
 | 
   144 		{
 | 
| 
 | 
   145 			String s = getInitParameter("timeLimit");
 | 
| 
 | 
   146 			if( s != null )
 | 
| 
 | 
   147 				timeLimit = Long.parseLong(s);
 | 
| 
 | 
   148 		}
 | 
| 
 | 
   149 		{
 | 
| 
 | 
   150 			String s = getInitParameter("errorCacheSize");
 | 
| 
 | 
   151 			if( s != null ) {
 | 
| 
 | 
   152 				final int errorCacheSize = Integer.parseInt(s);
 | 
| 
 | 
   153 				errorCache = Collections.synchronizedSet(Collections.newSetFromMap(new LinkedHashMap<String,Boolean>(){
 | 
| 
 | 
   154 					protected boolean removeEldestEntry(Map.Entry eldest) {
 | 
| 
 | 
   155 						return size() > errorCacheSize;
 | 
| 
 | 
   156 					}
 | 
| 
 | 
   157 				}));
 | 
| 
 | 
   158 			}
 | 
| 
 | 
   159 		}
 | 
| 
 | 
   160 		{
 | 
| 
 | 
   161 			String s = getInitParameter("ipListSize");
 | 
| 
 | 
   162 			if( s != null ) {
 | 
| 
 | 
   163 				final int ipListSize = Integer.parseInt(s);
 | 
| 
 | 
   164 				ipList = Collections.synchronizedList(new LinkedList<String>() {
 | 
| 
 | 
   165 					public boolean add(String s) {
 | 
| 
 | 
   166 						if( contains(s) )
 | 
| 
 | 
   167 							return false;
 | 
| 
 | 
   168 						super.add(s);
 | 
| 
 | 
   169 						if( size() > ipListSize )
 | 
| 
 | 
   170 							removeFirst();
 | 
| 
 | 
   171 						return true;
 | 
| 
 | 
   172 					}
 | 
| 
 | 
   173 				});
 | 
| 
 | 
   174 			}
 | 
| 
 | 
   175 		}
 | 
| 
 | 
   176 	}
 | 
| 
 | 
   177 
 | 
| 
 | 
   178 	private boolean isInErrorCache(String s) {
 | 
| 
 | 
   179 		return errorCache==null || !errorCache.add(s);
 | 
| 
 | 
   180 	}
 | 
| 
 | 
   181 
 | 
| 
 | 
   182 	private boolean isInIpList(String ip) {
 | 
| 
 | 
   183 		return ipList!=null && !ipList.add(ip);
 | 
| 
 | 
   184 	}
 | 
| 
 | 
   185 
 | 
| 
 | 
   186 	public static interface DestroyListener {
 | 
| 
 | 
   187 		public void destroyed();
 | 
| 
 | 
   188 	}
 | 
| 
 | 
   189 
 | 
| 
 | 
   190 	private DestroyListener destroyListener = null;
 | 
| 
 | 
   191 
 | 
| 
 | 
   192 	public void addDestroyListener(DestroyListener dl) {
 | 
| 
 | 
   193 		synchronized(lock) {
 | 
| 
 | 
   194 			if( destroyListener!=null )
 | 
| 
 | 
   195 				throw new RuntimeException("only one DestroyListener allowed");
 | 
| 
 | 
   196 			destroyListener = dl;
 | 
| 
 | 
   197 		}
 | 
| 
 | 
   198 	}
 | 
| 
 | 
   199 
 | 
| 
 | 
   200 	public void destroy() {
 | 
| 
 | 
   201 		synchronized(lock) {
 | 
| 
 | 
   202 			if( destroyListener != null )
 | 
| 
 | 
   203 				destroyListener.destroyed();
 | 
| 
 | 
   204 		}
 | 
| 
 | 
   205 	}
 | 
| 
 | 
   206 
 | 
| 
 | 
   207 	public static final class RequestAndResponse {
 | 
| 
 | 
   208 		public final HttpServletRequest request;
 | 
| 
 | 
   209 		public final HttpServletResponse response;
 | 
| 
 | 
   210 
 | 
| 
 | 
   211 		public RequestAndResponse(HttpServletRequest request,HttpServletResponse response) {
 | 
| 
 | 
   212 			this.request = request;
 | 
| 
 | 
   213 			this.response = response;
 | 
| 
 | 
   214 		}
 | 
| 
 | 
   215 	}
 | 
| 
 | 
   216 
 | 
| 
 | 
   217 	public static interface CustomWrappers {
 | 
| 
 | 
   218 		public RequestAndResponse wrap(HttpServletRequest request, HttpServletResponse response);
 | 
| 
 | 
   219 	}
 | 
| 
 | 
   220 
 | 
| 
 | 
   221 	private CustomWrappers customWrappers;
 | 
| 
 | 
   222 
 | 
| 
 | 
   223 	public void setCustomWrappers(CustomWrappers customWrappers) {
 | 
| 
 | 
   224 		this.customWrappers = customWrappers;
 | 
| 
 | 
   225 	}
 | 
| 
 | 
   226 
 | 
| 
 | 
   227 	private static String hideNull(String s) {
 | 
| 
 | 
   228 		return s==null ? "" : s;
 | 
| 
 | 
   229 	}
 | 
| 
 | 
   230 
 | 
| 
 | 
   231 	private String getServletPath(HttpServletRequest request) {
 | 
| 
 | 
   232 		return request.getServletPath() + hideNull(request.getPathInfo());
 | 
| 
 | 
   233 	}
 | 
| 
 | 
   234 
 | 
| 
 | 
   235 	protected void service(HttpServletRequest request,HttpServletResponse response)
 | 
| 
 | 
   236 		throws ServletException, IOException
 | 
| 
 | 
   237 	{
 | 
| 
 | 
   238 		final TimeLimit tl = startTimeLimit(request);
 | 
| 
 | 
   239 		response = new HttpServletResponseWrapper(response) {
 | 
| 
 | 
   240 			PrintWriter writer = null;
 | 
| 
 | 
   241 			ServletOutputStream out = null;
 | 
| 
 | 
   242 
 | 
| 
 | 
   243 			public PrintWriter getWriter()
 | 
| 
 | 
   244 				throws java.io.IOException
 | 
| 
 | 
   245 			{
 | 
| 
 | 
   246 				if( writer==null ) {
 | 
| 
 | 
   247 					writer = new PrintWriter(super.getWriter()) {
 | 
| 
 | 
   248 						public void write(String s,int off,int len) {
 | 
| 
 | 
   249 							long t = System.currentTimeMillis();
 | 
| 
 | 
   250 							super.write(s,off,len);
 | 
| 
 | 
   251 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   252 						}
 | 
| 
 | 
   253 						public void write(char[] buf,int off,int len) {
 | 
| 
 | 
   254 							long t = System.currentTimeMillis();
 | 
| 
 | 
   255 							super.write(buf,off,len);
 | 
| 
 | 
   256 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   257 						}
 | 
| 
 | 
   258 						public void write(int c) {
 | 
| 
 | 
   259 							long t = System.currentTimeMillis();
 | 
| 
 | 
   260 							super.write(c);
 | 
| 
 | 
   261 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   262 						}
 | 
| 
 | 
   263 						public void flush() {
 | 
| 
 | 
   264 							long t = System.currentTimeMillis();
 | 
| 
 | 
   265 							super.flush();
 | 
| 
 | 
   266 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   267 						}
 | 
| 
 | 
   268 						public void println() {
 | 
| 
 | 
   269 							long t = System.currentTimeMillis();
 | 
| 
 | 
   270 							super.println();
 | 
| 
 | 
   271 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   272 						}
 | 
| 
 | 
   273 					};
 | 
| 
 | 
   274 				}
 | 
| 
 | 
   275 				return writer;
 | 
| 
 | 
   276 			}
 | 
| 
 | 
   277 
 | 
| 
 | 
   278 			public ServletOutputStream getOutputStream()
 | 
| 
 | 
   279 				throws java.io.IOException
 | 
| 
 | 
   280 			{
 | 
| 
 | 
   281 				if( out==null ) {
 | 
| 
 | 
   282 					final ServletOutputStream sos = super.getOutputStream();
 | 
| 
 | 
   283 					out = new ServletOutputStream() {
 | 
| 
 | 
   284 						public void write(byte[] b,int off,int len) throws IOException {
 | 
| 
 | 
   285 							long t = System.currentTimeMillis();
 | 
| 
 | 
   286 							sos.write(b,off,len);
 | 
| 
 | 
   287 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   288 						}
 | 
| 
 | 
   289 						public void write(byte[] b) throws IOException {
 | 
| 
 | 
   290 							long t = System.currentTimeMillis();
 | 
| 
 | 
   291 							sos.write(b);
 | 
| 
 | 
   292 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   293 						}
 | 
| 
 | 
   294 						public void write(int c) throws IOException {
 | 
| 
 | 
   295 							long t = System.currentTimeMillis();
 | 
| 
 | 
   296 							sos.write(c);
 | 
| 
 | 
   297 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   298 						}
 | 
| 
 | 
   299 						public void flush() throws IOException {
 | 
| 
 | 
   300 							long t = System.currentTimeMillis();
 | 
| 
 | 
   301 							sos.flush();
 | 
| 
 | 
   302 							tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   303 						}
 | 
| 
 | 
   304 					};
 | 
| 
 | 
   305 				}
 | 
| 
 | 
   306 				return out;
 | 
| 
 | 
   307 			}
 | 
| 
 | 
   308 
 | 
| 
 | 
   309 			public void sendError(int sc) throws IOException {
 | 
| 
 | 
   310 				long t = System.currentTimeMillis();
 | 
| 
 | 
   311 				super.sendError(sc);
 | 
| 
 | 
   312 				tl.ioTime += System.currentTimeMillis() - t;
 | 
| 
 | 
   313 			}
 | 
| 
 | 
   314 
 | 
| 
 | 
   315 			public void sendRedirect(String location) throws IOException {
 | 
| 
 | 
   316 				if( containsHeader("Expires") )
 | 
| 
 | 
   317 					setHeader("Expires",null);
 | 
| 
 | 
   318 				if( containsHeader("Last-Modified") )
 | 
| 
 | 
   319 					setHeader("Last-Modified",null);
 | 
| 
 | 
   320 				if( containsHeader("Etag") )
 | 
| 
 | 
   321 					setHeader("Etag",null);
 | 
| 
 | 
   322 				if( containsHeader("Cache-Control") )
 | 
| 
 | 
   323 					setHeader("Cache-Control",null);
 | 
| 
 | 
   324 				if( containsHeader("Content-Type") )
 | 
| 
 | 
   325 					setHeader("Content-Type",null);
 | 
| 
 | 
   326 				if( containsHeader("Content-Length") )
 | 
| 
 | 
   327 //					setHeader("Content-Length",null);
 | 
| 
 | 
   328 					setContentLength(0);
 | 
| 
 | 
   329 				super.sendRedirect(location);
 | 
| 
 | 
   330 			}
 | 
| 
 | 
   331 		};
 | 
| 
 | 
   332 		service2(request,response);
 | 
| 
 | 
   333 		checkTimeLimit(request);
 | 
| 
 | 
   334 	}
 | 
| 
 | 
   335 
 | 
| 
 | 
   336 	private void service2(HttpServletRequest request, HttpServletResponse response)
 | 
| 
 | 
   337 		throws ServletException, IOException
 | 
| 
 | 
   338 	{
 | 
| 
 | 
   339 		if( !allowedMethods.contains(request.getMethod()) ) {
 | 
| 
 | 
   340 			response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
 | 
| 
 | 
   341 			return;
 | 
| 
 | 
   342 		}
 | 
| 
 | 
   343 //		String contextPath = request.getContextPath();
 | 
| 
 | 
   344 //		String contextUrl = ServletUtils.getContextURL(request);
 | 
| 
 | 
   345 
 | 
| 
 | 
   346 		// First we set the character encoding because any manipulation
 | 
| 
 | 
   347 		// of request parameters will break without this.
 | 
| 
 | 
   348 		response.setHeader("Content-Type","text/html; charset=utf-8");  // default, servlet can override
 | 
| 
 | 
   349 		if( characterEncoding != null ) {
 | 
| 
 | 
   350 			response.setCharacterEncoding(characterEncoding);
 | 
| 
 | 
   351 			request.setCharacterEncoding(characterEncoding);
 | 
| 
 | 
   352 		}
 | 
| 
 | 
   353 
 | 
| 
 | 
   354 		HttpServlet servlet;
 | 
| 
 | 
   355 		String path = getServletPath(request);
 | 
| 
 | 
   356 
 | 
| 
 | 
   357 		UrlMapping urlMapping = urlMapper.getUrlMapping(request);
 | 
| 
 | 
   358 		if( urlMapping != null ) {
 | 
| 
 | 
   359 			try {
 | 
| 
 | 
   360 				servlet = getServletFromClass(urlMapping.servletClass.getName());
 | 
| 
 | 
   361 			} catch(ClassNotFoundException e) {
 | 
| 
 | 
   362 				throw new RuntimeException(e);
 | 
| 
 | 
   363 			}
 | 
| 
 | 
   364 			final Map params = urlMapping.parameterMap;
 | 
| 
 | 
   365 			request = new BetterRequestWrapper(request) {
 | 
| 
 | 
   366 				public Map getParameterMap() {
 | 
| 
 | 
   367 					return params;
 | 
| 
 | 
   368 				}
 | 
| 
 | 
   369 			};
 | 
| 
 | 
   370 		} else {
 | 
| 
 | 
   371 			try {
 | 
| 
 | 
   372 				servlet = getServlet(path);
 | 
| 
 | 
   373 			} catch(ClassNotFoundException e) {
 | 
| 
 | 
   374 				response.sendError(HttpServletResponse.SC_NOT_FOUND);
 | 
| 
 | 
   375 				String agent = request.getHeader("user-agent");
 | 
| 
 | 
   376 				String referer = request.getHeader("referer");
 | 
| 
 | 
   377 				String remote = request.getRemoteAddr();
 | 
| 
 | 
   378 				String msg = request.getRequestURL()+" referer="+referer+" user-agent="+agent+" remote="+remote;
 | 
| 
 | 
   379 				if( referer==null ) {
 | 
| 
 | 
   380 					logger.info(msg,e);
 | 
| 
 | 
   381 				} else {
 | 
| 
 | 
   382 					logger.warn(msg,e);
 | 
| 
 | 
   383 				}
 | 
| 
 | 
   384 				return;
 | 
| 
 | 
   385 			}
 | 
| 
 | 
   386 		}
 | 
| 
 | 
   387 
 | 
| 
 | 
   388 		// Custom headers
 | 
| 
 | 
   389 		addCustomHeaders(response);
 | 
| 
 | 
   390 
 | 
| 
 | 
   391 		AuthorizingServlet auth = servlet instanceof AuthorizingServlet ? (AuthorizingServlet)servlet : null;
 | 
| 
 | 
   392 		if( isCaching ) {
 | 
| 
 | 
   393 			String etagS = request.getHeader("If-None-Match");
 | 
| 
 | 
   394 			if( etagS != null ) {
 | 
| 
 | 
   395 				String prevEtag = null;
 | 
| 
 | 
   396 				for( String etag : etagS.split("\\s*,\\s*") ) {
 | 
| 
 | 
   397 					if( etag.equals(prevEtag) )
 | 
| 
 | 
   398 						continue;
 | 
| 
 | 
   399 					prevEtag = etag;
 | 
| 
 | 
   400 					if( etag.length()>=2 && etag.charAt(0)=='"' && etag.charAt(etag.length()-1)=='"' )
 | 
| 
 | 
   401 						etag = etag.substring(1,etag.length()-1);
 | 
| 
 | 
   402 					String authKey = null;
 | 
| 
 | 
   403 					if( etag.length()>=2 && etag.charAt(0)=='[' ) {
 | 
| 
 | 
   404 						int i = etag.indexOf(']');
 | 
| 
 | 
   405 						if( i > 0 ) {
 | 
| 
 | 
   406 							if( auth != null )
 | 
| 
 | 
   407 								authKey = etag.substring(1,i);
 | 
| 
 | 
   408 							etag = etag.substring(i+1);
 | 
| 
 | 
   409 						}
 | 
| 
 | 
   410 					}
 | 
| 
 | 
   411 					String[] events = etag.split("~");
 | 
| 
 | 
   412 					long lastModified = getLastModified(events);
 | 
| 
 | 
   413 					try {
 | 
| 
 | 
   414 						if( lastModified <= request.getDateHeader("If-Modified-Since") ) {
 | 
| 
 | 
   415 							if( authKey==null || authorize(auth,authKey,request,response) )
 | 
| 
 | 
   416 								response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
 | 
| 
 | 
   417 							return;
 | 
| 
 | 
   418 						}
 | 
| 
 | 
   419 					} catch(RuntimeException e) {
 | 
| 
 | 
   420 						handleException(request,e);
 | 
| 
 | 
   421 					}
 | 
| 
 | 
   422 				}
 | 
| 
 | 
   423 			}
 | 
| 
 | 
   424 		}
 | 
| 
 | 
   425 		String authKey = auth==null ? null : getAuthorizationKey(auth,request);
 | 
| 
 | 
   426 		if( authKey != null ) {
 | 
| 
 | 
   427 			if( !authorize(auth,authKey,request,response) )
 | 
| 
 | 
   428 				return;
 | 
| 
 | 
   429 			request.setAttribute(authKeyAttr,authKey);
 | 
| 
 | 
   430 		}
 | 
| 
 | 
   431 
 | 
| 
 | 
   432 		if( servlet instanceof CanonicalUrl ) {
 | 
| 
 | 
   433 			CanonicalUrl srv = (CanonicalUrl)servlet;
 | 
| 
 | 
   434 			StringBuffer currentUrl = request.getRequestURL();
 | 
| 
 | 
   435 			int i = currentUrl.indexOf(";");
 | 
| 
 | 
   436 			if( i != -1 )
 | 
| 
 | 
   437 				currentUrl.setLength(i);
 | 
| 
 | 
   438 			String query = request.getQueryString();
 | 
| 
 | 
   439 			if( query != null )
 | 
| 
 | 
   440 				currentUrl.append( '?' ).append( query );
 | 
| 
 | 
   441 			try {
 | 
| 
 | 
   442 				String canonicalUrl = srv.getCanonicalUrl(request);
 | 
| 
 | 
   443 				if( canonicalUrl != null && !currentUrl.toString().equals(canonicalUrl) ) {
 | 
| 
 | 
   444 					response.setHeader("Location",canonicalUrl);
 | 
| 
 | 
   445 					response.sendError( HttpServletResponse.SC_MOVED_PERMANENTLY );
 | 
| 
 | 
   446 					return;
 | 
| 
 | 
   447 				}
 | 
| 
 | 
   448 			} catch(RuntimeException e) {
 | 
| 
 | 
   449 				logger.warn("couldn't get canonical url",e);
 | 
| 
 | 
   450 			}
 | 
| 
 | 
   451 		}
 | 
| 
 | 
   452 
 | 
| 
 | 
   453 		request.setAttribute("servlet",servlet);
 | 
| 
 | 
   454 
 | 
| 
 | 
   455 		try {
 | 
| 
 | 
   456 			if (customWrappers != null) {
 | 
| 
 | 
   457 				RequestAndResponse rr = customWrappers.wrap(request, response);
 | 
| 
 | 
   458 				request = rr.request;
 | 
| 
 | 
   459 				response = rr.response;
 | 
| 
 | 
   460 			}
 | 
| 
 | 
   461 			servlet.service(request,response);
 | 
| 
 | 
   462 		} catch(RuntimeException e) {
 | 
| 
 | 
   463 			handleException(request,e);
 | 
| 
 | 
   464 		} catch(ServletException e) {
 | 
| 
 | 
   465 			handleException(request,e);
 | 
| 
 | 
   466 		}
 | 
| 
 | 
   467 	}
 | 
| 
 | 
   468 
 | 
| 
 | 
   469 	public void setEtag( HttpServletRequest request, HttpServletResponse response, String... modifyingEvents ) {
 | 
| 
 | 
   470 		if( modifyingEvents.length == 0 )
 | 
| 
 | 
   471 			modifyingEvents = noModifyingEvents;
 | 
| 
 | 
   472 		StringBuilder buf = new StringBuilder();
 | 
| 
 | 
   473 		String authKey = (String)request.getAttribute(authKeyAttr);
 | 
| 
 | 
   474 		if( authKey != null )
 | 
| 
 | 
   475 			buf.append( '[' ).append( authKey).append( ']' );
 | 
| 
 | 
   476 		buf.append( modifyingEvents[0] );
 | 
| 
 | 
   477 		for( int i=1; i<modifyingEvents.length; i++ ) {
 | 
| 
 | 
   478 			buf.append( '~' ).append( modifyingEvents[i] );
 | 
| 
 | 
   479 		}
 | 
| 
 | 
   480 		response.setHeader("Etag",buf.toString());
 | 
| 
 | 
   481 		long lastModified = getLastModified(modifyingEvents);
 | 
| 
 | 
   482 		response.setDateHeader("Last-Modified",lastModified);
 | 
| 
 | 
   483 		response.setHeader("Cache-Control","max-age=0");
 | 
| 
 | 
   484 	}
 | 
| 
 | 
   485 
 | 
| 
 | 
   486 	private boolean authorize(AuthorizingServlet auth,String authKey,HttpServletRequest request,HttpServletResponse response)
 | 
| 
 | 
   487 		throws IOException, ServletException
 | 
| 
 | 
   488 	{
 | 
| 
 | 
   489 		try {
 | 
| 
 | 
   490 			if (customWrappers != null) {
 | 
| 
 | 
   491 				RequestAndResponse rr = customWrappers.wrap(request, response);
 | 
| 
 | 
   492 				request = rr.request;
 | 
| 
 | 
   493 				response = rr.response;
 | 
| 
 | 
   494 			}
 | 
| 
 | 
   495 			return auth.authorize(authKey,request,response);
 | 
| 
 | 
   496 		} catch(RuntimeException e) {
 | 
| 
 | 
   497 			handleException(request,e);
 | 
| 
 | 
   498 		} catch(ServletException e) {
 | 
| 
 | 
   499 			handleException(request,e);
 | 
| 
 | 
   500 		}
 | 
| 
 | 
   501 		throw new RuntimeException("never");
 | 
| 
 | 
   502 	}
 | 
| 
 | 
   503 
 | 
| 
 | 
   504 	private String getAuthorizationKey(AuthorizingServlet auth,HttpServletRequest request)
 | 
| 
 | 
   505 		throws ServletException
 | 
| 
 | 
   506 	{
 | 
| 
 | 
   507 		try {
 | 
| 
 | 
   508 			return auth.getAuthorizationKey(request);
 | 
| 
 | 
   509 		} catch(RuntimeException e) {
 | 
| 
 | 
   510 			handleException(request,e);
 | 
| 
 | 
   511 		} catch(ServletException e) {
 | 
| 
 | 
   512 			handleException(request,e);
 | 
| 
 | 
   513 		}
 | 
| 
 | 
   514 		return null;  // never gets here
 | 
| 
 | 
   515 	}
 | 
| 
 | 
   516 
 | 
| 
 | 
   517 	private long getLastModified(String[] modifyingEvents) {
 | 
| 
 | 
   518 		long[] lastModifieds = httpCache.lastModifieds(modifyingEvents);
 | 
| 
 | 
   519 		long lastModified = lastModifieds[0];
 | 
| 
 | 
   520 		for( int i=1; i<lastModifieds.length; i++ ) {
 | 
| 
 | 
   521 			long lm = lastModifieds[i];
 | 
| 
 | 
   522 			if( lastModified < lm )
 | 
| 
 | 
   523 				lastModified = lm;
 | 
| 
 | 
   524 		}
 | 
| 
 | 
   525 		return lastModified;
 | 
| 
 | 
   526 	}
 | 
| 
 | 
   527 
 | 
| 
 | 
   528 	/** Adds all custom headers to the response object. */
 | 
| 
 | 
   529 	private void addCustomHeaders(HttpServletResponse response) {
 | 
| 
 | 
   530 		Set<Map.Entry<String, String>> entries = this.customHeaders.entrySet();
 | 
| 
 | 
   531 		for (Map.Entry<String, String> entry : entries) {
 | 
| 
 | 
   532 			response.setHeader(entry.getKey(), entry.getValue());
 | 
| 
 | 
   533 		}
 | 
| 
 | 
   534 	}
 | 
| 
 | 
   535 
 | 
| 
 | 
   536 	private void handleException(HttpServletRequest request,RuntimeException e)
 | 
| 
 | 
   537 		throws ServletException
 | 
| 
 | 
   538 	{
 | 
| 
 | 
   539 		JtpRuntimeException rte;
 | 
| 
 | 
   540 		try {
 | 
| 
 | 
   541 			String agent = request.getHeader("user-agent");
 | 
| 
 | 
   542 			if( agent == null )
 | 
| 
 | 
   543 				throw new JtpServletException(request,"null agent",e);
 | 
| 
 | 
   544 			if (!isValidAgent(agent))
 | 
| 
 | 
   545 				throw new JtpServletException(request, "bad agent " + agent, e);
 | 
| 
 | 
   546 			String remote = request.getRemoteAddr();
 | 
| 
 | 
   547 			String referer = request.getHeader("referer");
 | 
| 
 | 
   548 			StringBuilder buf = new StringBuilder()
 | 
| 
 | 
   549 				.append( "method=" ).append( request.getMethod() )
 | 
| 
 | 
   550 				.append( " user-agent=" ).append( agent )
 | 
| 
 | 
   551 				.append( " referer=" ).append( referer )
 | 
| 
 | 
   552 				.append( " remote=" ).append( remote )
 | 
| 
 | 
   553 			;
 | 
| 
 | 
   554 			String etag = request.getHeader("If-None-Match");
 | 
| 
 | 
   555 			if( etag != null )
 | 
| 
 | 
   556 				buf.append( " etag=[" ).append( etag ).append( "]" );
 | 
| 
 | 
   557 			if( referer==null || isInIpList(remote) )
 | 
| 
 | 
   558 				throw new JtpServletException(request,buf.toString(),e);
 | 
| 
 | 
   559 			rte = new JtpRuntimeException(request,buf.toString(),e);
 | 
| 
 | 
   560 		} catch(RuntimeException e2) {
 | 
| 
 | 
   561 			logger.error("failed to handle",e);
 | 
| 
 | 
   562 			throw e2;
 | 
| 
 | 
   563 		}
 | 
| 
 | 
   564 		throw rte;
 | 
| 
 | 
   565 	}
 | 
| 
 | 
   566 
 | 
| 
 | 
   567 	private static void handleException(HttpServletRequest request,ServletException e)
 | 
| 
 | 
   568 		throws ServletException
 | 
| 
 | 
   569 	{
 | 
| 
 | 
   570 		String agent = request.getHeader("user-agent");
 | 
| 
 | 
   571 		throw new JtpServletException(request,"user-agent="+agent+" method="+request.getMethod()+" referer="+request.getHeader("referer"),e);
 | 
| 
 | 
   572 	}
 | 
| 
 | 
   573 
 | 
| 
 | 
   574 	private static class JtpRuntimeException extends RuntimeException {
 | 
| 
 | 
   575 		private JtpRuntimeException(HttpServletRequest request,String msg,RuntimeException e) {
 | 
| 
 | 
   576 			super("url="+getCurrentURL(request)+"  "+msg,e);
 | 
| 
 | 
   577 		}
 | 
| 
 | 
   578 	}
 | 
| 
 | 
   579 
 | 
| 
 | 
   580 	private static class JtpServletException extends ServletException {
 | 
| 
 | 
   581 		private JtpServletException(HttpServletRequest request,String msg,Exception e) {
 | 
| 
 | 
   582 			super("url="+getCurrentURL(request)+"  "+msg,e);
 | 
| 
 | 
   583 		}
 | 
| 
 | 
   584 	}
 | 
| 
 | 
   585 
 | 
| 
 | 
   586 	// work-around jetty bug
 | 
| 
 | 
   587 	private static String getCurrentURL(HttpServletRequest request) {
 | 
| 
 | 
   588 		try {
 | 
| 
 | 
   589 			return ServletUtils.getCurrentURL(request);
 | 
| 
 | 
   590 		} catch(RuntimeException e) {
 | 
| 
 | 
   591 			logger.warn("jetty screwed up",e);
 | 
| 
 | 
   592 			return "[failed]";
 | 
| 
 | 
   593 		}
 | 
| 
 | 
   594 	}
 | 
| 
 | 
   595 
 | 
| 
 | 
   596 	private static boolean isValidAgent(String agent) {
 | 
| 
 | 
   597 		if (agent == null)
 | 
| 
 | 
   598 			return false;
 | 
| 
 | 
   599 		for (String badAgent : badAgents) {
 | 
| 
 | 
   600 			if (agent.indexOf(badAgent) >= 0)
 | 
| 
 | 
   601 				return false;
 | 
| 
 | 
   602 		}
 | 
| 
 | 
   603 		return true;
 | 
| 
 | 
   604 	}
 | 
| 
 | 
   605 
 | 
| 
 | 
   606 	private static final String[] badAgents = new String[]{
 | 
| 
 | 
   607 		"MJ12bot",
 | 
| 
 | 
   608 		"WISEnutbot",
 | 
| 
 | 
   609 		"Win98",  // not worth handling these
 | 
| 
 | 
   610 		"Windows 98",
 | 
| 
 | 
   611 		"Windows 95",
 | 
| 
 | 
   612 		"RixBot",
 | 
| 
 | 
   613 		"User-Agent",  // from corrupt header
 | 
| 
 | 
   614 		"Firefox/0",  // ancient version of Firefox
 | 
| 
 | 
   615 		"Firefox/2.",  // ancient version of Firefox
 | 
| 
 | 
   616 		"Firefox/3.",  // ancient version of Firefox
 | 
| 
 | 
   617 		"Opera 7.",  // ancient version of Opera
 | 
| 
 | 
   618 		"Opera/7.",
 | 
| 
 | 
   619 		"Opera 8.",
 | 
| 
 | 
   620 		"Opera/8.",
 | 
| 
 | 
   621 		"Opera/9.",
 | 
| 
 | 
   622 		"TwitterFeed 3",
 | 
| 
 | 
   623 		"NAVER Blog Rssbot",
 | 
| 
 | 
   624 		"AOL 9.0",
 | 
| 
 | 
   625 		"rssreader@newstin.com",
 | 
| 
 | 
   626 		"PHPCrawl",
 | 
| 
 | 
   627 		"MSIE 2.",
 | 
| 
 | 
   628 		"MSIE 4.",
 | 
| 
 | 
   629 		"MSIE 5.",
 | 
| 
 | 
   630 		"MSIE 6.",
 | 
| 
 | 
   631 		"MSIE 7.0",
 | 
| 
 | 
   632 		"Mozilla/0.",
 | 
| 
 | 
   633 		"Mozilla/2.0",
 | 
| 
 | 
   634 		"Mozilla/3.0",
 | 
| 
 | 
   635 		"Mozilla/4.6",
 | 
| 
 | 
   636 		"Mozilla/4.7",
 | 
| 
 | 
   637 		"RSSIncludeBot/1.0", // cause exceptions in xml feeds
 | 
| 
 | 
   638 		"Powermarks",
 | 
| 
 | 
   639 		"GenwiFeeder",
 | 
| 
 | 
   640 		"Akregator",
 | 
| 
 | 
   641 		"ia_archiver",
 | 
| 
 | 
   642 		"Atomic_Email_Hunter",
 | 
| 
 | 
   643 		"Yahoo! Slurp",
 | 
| 
 | 
   644 		"Python-urllib",
 | 
| 
 | 
   645 		"BlackBerry",
 | 
| 
 | 
   646 		"SimplePie", // Feeds parser
 | 
| 
 | 
   647 		"www.webintegration.at", // crazy bot
 | 
| 
 | 
   648 		"www.run4dom.com", // crazy bot
 | 
| 
 | 
   649 		"zia-httpmirror",
 | 
| 
 | 
   650 		"POE-Component-Client-HTTP",
 | 
| 
 | 
   651 		"anonymous",
 | 
| 
 | 
   652 		"Sosospider",
 | 
| 
 | 
   653 		"Java/1.6",
 | 
| 
 | 
   654 		"Shareaza",
 | 
| 
 | 
   655 		"Jakarta Commons-HttpClient",
 | 
| 
 | 
   656 		"Apache-HttpClient",
 | 
| 
 | 
   657 		"Baiduspider",
 | 
| 
 | 
   658 		"bingbot",
 | 
| 
 | 
   659 		"MLBot", // www.metadatalabs.com/mlbot
 | 
| 
 | 
   660 		"www.vbseo.com",
 | 
| 
 | 
   661 		"yacybot", // yacy.net/bot.html
 | 
| 
 | 
   662 		"SearchBot"
 | 
| 
 | 
   663 	};
 | 
| 
 | 
   664 
 | 
| 
 | 
   665 	private static boolean isBot(String agent) {
 | 
| 
 | 
   666 		if (agent == null)
 | 
| 
 | 
   667 			return false;
 | 
| 
 | 
   668 		for (String bot : bots) {
 | 
| 
 | 
   669 			if (agent.indexOf(bot) >= 0)
 | 
| 
 | 
   670 				return true;
 | 
| 
 | 
   671 		}
 | 
| 
 | 
   672 		return false;
 | 
| 
 | 
   673 	}
 | 
| 
 | 
   674 
 | 
| 
 | 
   675 	private static final String[] bots = new String[]{
 | 
| 
 | 
   676 		"Googlebot"
 | 
| 
 | 
   677 	};
 | 
| 
 | 
   678 
 | 
| 
 | 
   679 	private HttpServlet getServlet(String path)
 | 
| 
 | 
   680 		throws ServletException, ClassNotFoundException, IOException
 | 
| 
 | 
   681 	{
 | 
| 
 | 
   682 		int i = path.lastIndexOf('.');
 | 
| 
 | 
   683 		if( i == -1 )
 | 
| 
 | 
   684 			throw new ClassNotFoundException(path);
 | 
| 
 | 
   685 		return getServletFromClass(
 | 
| 
 | 
   686 			base + path.substring(0,i).replace('/','.')
 | 
| 
 | 
   687 		);
 | 
| 
 | 
   688 	}
 | 
| 
 | 
   689 
 | 
| 
 | 
   690 	private HttpServlet getServletFromClass(String cls)
 | 
| 
 | 
   691 		throws ClassNotFoundException
 | 
| 
 | 
   692 	{
 | 
| 
 | 
   693 		synchronized(lock) {
 | 
| 
 | 
   694 			if( reload && hasChanged(cls) ) {
 | 
| 
 | 
   695 				unloadServlets();
 | 
| 
 | 
   696 			}
 | 
| 
 | 
   697 			HttpServlet srv = map.get(cls);
 | 
| 
 | 
   698 			if( srv==null ) {
 | 
| 
 | 
   699 				try {
 | 
| 
 | 
   700 					Class clas = reload ? cl.loadClass(cls) : Class.forName(cls);
 | 
| 
 | 
   701 					srv = (HttpServlet)clas.newInstance();
 | 
| 
 | 
   702 				} catch(IllegalAccessException e) {
 | 
| 
 | 
   703 					throw new RuntimeException(e);
 | 
| 
 | 
   704 				} catch(InstantiationException e) {
 | 
| 
 | 
   705 					throw new RuntimeException(e);
 | 
| 
 | 
   706 				}
 | 
| 
 | 
   707 				try {
 | 
| 
 | 
   708 					srv.init(this);
 | 
| 
 | 
   709 				} catch(ServletException e) {
 | 
| 
 | 
   710 					throw new RuntimeException(e);
 | 
| 
 | 
   711 				}
 | 
| 
 | 
   712 				map.put(cls,srv);
 | 
| 
 | 
   713 			}
 | 
| 
 | 
   714 			return srv;
 | 
| 
 | 
   715 		}
 | 
| 
 | 
   716 	}
 | 
| 
 | 
   717 
 | 
| 
 | 
   718 	private boolean hasChanged(String cls) {
 | 
| 
 | 
   719 		try {
 | 
| 
 | 
   720 			URL url = cl.getResource( SimpleClassLoader.classToResource(cls) );
 | 
| 
 | 
   721 			if( url==null )
 | 
| 
 | 
   722 				return true;
 | 
| 
 | 
   723 			File file = new File(url.getPath());
 | 
| 
 | 
   724 			if( recompile ) {
 | 
| 
 | 
   725 				String path = file.toString();
 | 
| 
 | 
   726 				if( !path.endsWith(".class") )
 | 
| 
 | 
   727 					throw new RuntimeException();
 | 
| 
 | 
   728 				File dir = file.getParentFile();
 | 
| 
 | 
   729 				String base = path.substring(0,path.length()-6);
 | 
| 
 | 
   730 				File source = new File( base + ".jtp" );
 | 
| 
 | 
   731 				if( source.lastModified() > clTime ) {
 | 
| 
 | 
   732 					Process proc = Runtime.getRuntime().exec(new String[]{
 | 
| 
 | 
   733 						"java", "fschmidt.tools.Jtp", source.getName()
 | 
| 
 | 
   734 					},null,dir);
 | 
| 
 | 
   735 					ProcUtils.checkProc(proc);
 | 
| 
 | 
   736 				}
 | 
| 
 | 
   737 				source = new File( base + ".java" );
 | 
| 
 | 
   738 				if( source.lastModified() > clTime ) {
 | 
| 
 | 
   739 					Process proc = Runtime.getRuntime().exec(new String[]{
 | 
| 
 | 
   740 						"javac", "-g", source.getName()
 | 
| 
 | 
   741 					},null,dir);
 | 
| 
 | 
   742 					ProcUtils.checkProc(proc);
 | 
| 
 | 
   743 				}
 | 
| 
 | 
   744 			}
 | 
| 
 | 
   745 			return file.lastModified() > clTime;
 | 
| 
 | 
   746 		} catch(IOException e) {
 | 
| 
 | 
   747 			throw new RuntimeException(e);
 | 
| 
 | 
   748 		}
 | 
| 
 | 
   749 	}
 | 
| 
 | 
   750 
 | 
| 
 | 
   751 
 | 
| 
 | 
   752 	private long timeLimit = 0;
 | 
| 
 | 
   753 	private static final String timeLimitAttr = "time-limit";
 | 
| 
 | 
   754 
 | 
| 
 | 
   755 	private static class TimeLimit {
 | 
| 
 | 
   756 		long timeLimit;
 | 
| 
 | 
   757 		final long startTime = System.currentTimeMillis();
 | 
| 
 | 
   758 		long ioTime = 0L;
 | 
| 
 | 
   759 
 | 
| 
 | 
   760 		TimeLimit(long timeLimit) {
 | 
| 
 | 
   761 			this.timeLimit = timeLimit;
 | 
| 
 | 
   762 		}
 | 
| 
 | 
   763 	}
 | 
| 
 | 
   764 
 | 
| 
 | 
   765 	public long getTimeLimit() {
 | 
| 
 | 
   766 		return timeLimit;
 | 
| 
 | 
   767 	}
 | 
| 
 | 
   768 
 | 
| 
 | 
   769 	public void setTimeLimit(long timeLimit) {
 | 
| 
 | 
   770 		this.timeLimit = timeLimit;
 | 
| 
 | 
   771 	}
 | 
| 
 | 
   772 
 | 
| 
 | 
   773 	private TimeLimit startTimeLimit(HttpServletRequest request) {
 | 
| 
 | 
   774 		TimeLimit tl = new TimeLimit(timeLimit);
 | 
| 
 | 
   775 		request.setAttribute( timeLimitAttr, tl );
 | 
| 
 | 
   776 		return tl;
 | 
| 
 | 
   777 	}
 | 
| 
 | 
   778 
 | 
| 
 | 
   779 	public void setTimeLimit(HttpServletRequest request,long timeLimit) {
 | 
| 
 | 
   780 		TimeLimit tl = (TimeLimit)request.getAttribute(timeLimitAttr);
 | 
| 
 | 
   781 		tl.timeLimit = timeLimit;
 | 
| 
 | 
   782 	}
 | 
| 
 | 
   783 
 | 
| 
 | 
   784 	private void checkTimeLimit(HttpServletRequest request) {
 | 
| 
 | 
   785 		TimeLimit tl = (TimeLimit)request.getAttribute(timeLimitAttr);
 | 
| 
 | 
   786 		if( tl.timeLimit == 0L )
 | 
| 
 | 
   787 			return;
 | 
| 
 | 
   788         long time = System.currentTimeMillis() - tl.startTime - tl.ioTime;
 | 
| 
 | 
   789         if( time > tl.timeLimit ) {
 | 
| 
 | 
   790 			float free = Runtime.getRuntime().freeMemory();
 | 
| 
 | 
   791 			float total = Runtime.getRuntime().totalMemory();
 | 
| 
 | 
   792 			float used = (total - free) * 100f;
 | 
| 
 | 
   793 			logger.error(ServletUtils.getCurrentURL(request,100) + " took " + time + " ms | " + String.format("%.1f",used/total) + '%');
 | 
| 
 | 
   794 /*
 | 
| 
 | 
   795 			Scheduler scheduler = TheScheduler.get();
 | 
| 
 | 
   796 			if( scheduler instanceof ProfilingScheduler ) {
 | 
| 
 | 
   797 				ProfilingScheduler profilingScheduler = (ProfilingScheduler)scheduler;
 | 
| 
 | 
   798 				if( profilingScheduler.getMode()==ProfilingScheduler.Mode.FOREGROUND ) {
 | 
| 
 | 
   799 					profilingScheduler.captureCPUSnapshot();
 | 
| 
 | 
   800 				}
 | 
| 
 | 
   801 			}
 | 
| 
 | 
   802 */
 | 
| 
 | 
   803 		}
 | 
| 
 | 
   804 	}
 | 
| 
 | 
   805 
 | 
| 
 | 
   806 }
 |