/* * Copyright 2004-2010 the Seasar Foundation and the Others. * * 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.slim3.controller; import java.io.IOException; 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 javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slim3.controller.router.Router; import org.slim3.controller.router.RouterFactory; import org.slim3.util.AppEngineUtil; import org.slim3.util.Cleaner; import org.slim3.util.RequestLocator; import org.slim3.util.RequestUtil; import org.slim3.util.ResponseLocator; import org.slim3.util.ServletContextLocator; import org.slim3.util.StringUtil; /** * A filter for HOT reloading. * * @author higa * @since 1.0.0 * */ public class HotReloadingFilter implements Filter { /** * The logger. */ // private static final Logger logger = // Logger.getLogger(HotReloadingFilter.class.getName()); /** * The servlet context. */ protected ServletContext servletContext; /** * Whether this filter supports hot reloading. */ protected boolean hotReloading = false; /** * The root package name. */ protected String rootPackageName; /** * The cool package name. */ protected String coolPackageName; /** * Constructor. */ public HotReloadingFilter() { } public void init(FilterConfig config) throws ServletException { initServletContext(config); initHotReloading(); initRootPackageName(); initCoolPackageName(); } /** * Initializes the servlet context. * * @param config * the filter configuration. */ protected void initServletContext(FilterConfig config) { servletContext = config.getServletContext(); } /** * Initializes the HOT reloading setting. */ protected void initHotReloading() { if (AppEngineUtil.isDevelopment()) { if ("false".equalsIgnoreCase(System .getProperty(ControllerConstants.HOT_RELOADING_KEY))) { hotReloading = false; } else { hotReloading = true; } } else { hotReloading = false; } if (hotReloading) { System.setSecurityManager(null); ServletContextLocator.set(new HotServletContextWrapper( servletContext)); } if (AppEngineUtil.isDevelopment()) { System.out.println("Slim3 HOT reloading:" + hotReloading); } } /** * Initializes the root package name. */ protected void initRootPackageName() { rootPackageName = servletContext .getInitParameter(ControllerConstants.ROOT_PACKAGE_KEY); if (StringUtil.isEmpty(rootPackageName)) { throw new IllegalStateException("The context-param(" + ControllerConstants.ROOT_PACKAGE_KEY + ") is not found in web.xml."); } } /** * Initializes the cool package name. */ protected void initCoolPackageName() { coolPackageName = servletContext .getInitParameter(ControllerConstants.COOL_PACKAGE_KEY); if (StringUtil.isEmpty(coolPackageName)) { coolPackageName = ControllerConstants.DEFAULT_COOL_PACKAGE; } } public void destroy() { Cleaner.cleanAll(); if (hotReloading) { ServletContextLocator.set(null); } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { doFilter( (HttpServletRequest) request, (HttpServletResponse) response, chain); } /** * Executes filtering process. * * @param request * the request * @param response * the response * @param chain * the filter chain * @throws IOException * if {@link IOException} is encountered * @throws ServletException * if {@link ServletException} is encountered */ protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (hotReloading) { String path = RequestUtil.getPath(request); Router router = RouterFactory.getRouter(); if (!router.isStatic(path)) { ClassLoader previousLoader = Thread.currentThread().getContextClassLoader(); if (!(previousLoader instanceof HotReloadingClassLoader)) { doHotReloading(request, response, chain, previousLoader); return; } } } chain.doFilter(request, response); } /** * Executes filtering process. * * @param request * the request * @param response * the response * @param chain * the filter chain * @param previousLoader * the previous class loader * @throws IOException * if {@link IOException} is encountered * @throws ServletException * if {@link ServletException} is encountered */ protected synchronized void doHotReloading(HttpServletRequest request, HttpServletResponse response, FilterChain chain, ClassLoader previousLoader) throws IOException, ServletException { Thread.currentThread().setContextClassLoader( new HotReloadingClassLoader( previousLoader, rootPackageName, coolPackageName)); request = new HotHttpServletRequestWrapper(request); RequestLocator.set(request); ResponseLocator.set(response); try { chain.doFilter(request, response); } catch (LinkageError e) { String msg = e.getMessage(); if (msg != null && msg.indexOf("loader constraint violation") >= 0) { throw createHotReloadingRuntimeException(e); } throw e; } catch (ClassCastException e) { throw createHotReloadingRuntimeException(e); } finally { Cleaner.cleanAll(); Thread.currentThread().setContextClassLoader(previousLoader); RequestLocator.set(null); ResponseLocator.set(null); } } /** * Creates {@link HotReloadingRuntimeException}. * * @param cause * the cause * @return {@link HotReloadingRuntimeException} */ protected HotReloadingRuntimeException createHotReloadingRuntimeException( Throwable cause) { return new HotReloadingRuntimeException( "If you use MemcacheService or JCache, use org.slim3.memcache.Memcache instead of it.\n" + "Or if a COOL class wants to access a HOT reloaded class, use CoolBridge.\n" + "COOL classes means classes located on \"" + rootPackageName + "." + coolPackageName + "\" package or classes which Servlet Container manages like Servlets.\n" + "HOT reloaded classes means classes located on \"" + rootPackageName + "\" package except \"" + rootPackageName + "." + coolPackageName + "\" package.\n" + "See http://sites.google.com/site/slim3appengine/hot-reloading", cause); } }