001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.hivemind.servlet; 016 017 import java.io.IOException; 018 import java.util.Locale; 019 020 import javax.servlet.Filter; 021 import javax.servlet.FilterChain; 022 import javax.servlet.FilterConfig; 023 import javax.servlet.ServletContext; 024 import javax.servlet.ServletException; 025 import javax.servlet.ServletRequest; 026 import javax.servlet.ServletResponse; 027 import javax.servlet.http.HttpServletRequest; 028 029 import org.apache.commons.logging.Log; 030 import org.apache.commons.logging.LogFactory; 031 import org.apache.hivemind.ClassResolver; 032 import org.apache.hivemind.Registry; 033 import org.apache.hivemind.impl.DefaultClassResolver; 034 import org.apache.hivemind.impl.RegistryBuilder; 035 036 /** 037 * Servlet filter that constructs the Registry at startup. It ensures that each request is properly 038 * terminated with a call to 039 * {@link org.apache.hivemind.service.ThreadEventNotifier#fireThreadCleanup()}. It also makes the 040 * Registry available during the request by storing it as a request attribute. 041 * Autodetects hivemind modules by calling {@link RegistryBuilder#autoDetectModules()}. 042 * 043 * Specializations can override the {@link #addWebInfDescriptor(ServletContext, ClassResolver, RegistryBuilder)} method. 044 * to add custom modules. 045 * 046 * @author Howard Lewis Ship 047 */ 048 public class AutoloadingHiveMindFilter implements Filter 049 { 050 private static final Log LOG = LogFactory.getLog(AutoloadingHiveMindFilter.class); 051 052 /** 053 * Request attribute key that stores the Registry. 054 */ 055 056 static final String REQUEST_KEY = "org.apache.hivemind.RequestRegistry"; 057 058 static final String REBUILD_REQUEST_KEY = "org.apache.hivemind.RebuildRegistry"; 059 060 private FilterConfig _filterConfig; 061 062 private Registry _registry; 063 064 /** 065 * Constructs a {@link Registry} and stores it into the <code>ServletContext</code>. Any 066 * exception throws is logged. 067 */ 068 public void init(FilterConfig config) throws ServletException 069 { 070 _filterConfig = config; 071 072 initializeRegistry(); 073 074 } 075 076 private void initializeRegistry() 077 { 078 long startTime = System.currentTimeMillis(); 079 080 LOG.info(ServletMessages.filterInit()); 081 082 try 083 { 084 _registry = constructRegistry(_filterConfig); 085 086 LOG.info(ServletMessages.constructedRegistry(_registry, System.currentTimeMillis() 087 - startTime)); 088 } 089 catch (Exception ex) 090 { 091 LOG.error(ex.getMessage(), ex); 092 } 093 } 094 095 /** 096 * Invoked from {@link #init(FilterConfig)} to actually construct the Registry. Subclasses may 097 * override if they have specific initialization needs, or have nonstandard rules for finding 098 * HiveMind module deployment descriptors. 099 */ 100 protected Registry constructRegistry(FilterConfig config) 101 { 102 RegistryBuilder builder = new RegistryBuilder(); 103 104 ClassResolver resolver = new DefaultClassResolver(); 105 106 addDefaultModules(builder, resolver); 107 108 addWebInfDescriptor(config.getServletContext(), resolver, builder); 109 110 return builder.constructRegistry(getRegistryLocale()); 111 } 112 113 /** 114 * Invoked from {@link #constructRegistry(FilterConfig)} to add an web specific 115 * application descriptor if one exists. 116 * 117 */ 118 protected void addWebInfDescriptor(ServletContext context, ClassResolver resolver, 119 RegistryBuilder builder) 120 { 121 } 122 123 /** 124 * Returns the default Locale. Subclasses may override to select a particular locale for the 125 * Registry. 126 */ 127 protected Locale getRegistryLocale() 128 { 129 return Locale.getDefault(); 130 } 131 132 protected void addDefaultModules(RegistryBuilder builder, ClassResolver resolver) 133 { 134 builder.autoDetectModules(); 135 } 136 137 /** 138 * Passes the request to the filter chain, but then invokes {@link Registry#cleanupThread()} 139 * (from a finally block). 140 */ 141 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 142 throws IOException, ServletException 143 { 144 try 145 { 146 // I believe the _registry will only be null in a couple of test situations. 147 148 if (_registry != null) 149 _registry.setupThread(); 150 151 request.setAttribute(REQUEST_KEY, _registry); 152 153 chain.doFilter(request, response); 154 } 155 finally 156 { 157 cleanupThread(); 158 159 checkRegistryRebuild(request); 160 } 161 } 162 163 private synchronized void checkRegistryRebuild(ServletRequest request) 164 { 165 if (request.getAttribute(REBUILD_REQUEST_KEY) == null) 166 return; 167 168 Registry oldRegistry = _registry; 169 170 // Replace the old Registry with a new one. All other threads, but this 171 // one, will begin using the new Registry. Hopefully, we didn't get 172 // rebuild requests on multiple threads. 173 174 initializeRegistry(); 175 176 // Shutdown the old Registry. Perhaps we should sleep for a moment, first, 177 // to help ensure that other threads have "cleared out". If not, we'll see some 178 // instability at the instant we shutdown (i.e., all the proxies will get disabled). 179 // Alternately, we should create a WeakReference based monitor that shuts down the 180 // old registry when it is no longer used by any other threads. For the moment, 181 // this functionality is limited to development-time only (not production), so it isn't 182 // urgent. 183 184 oldRegistry.shutdown(); 185 } 186 187 /** 188 * Cleanup the thread, ignoring any exceptions that may be thrown. 189 */ 190 private void cleanupThread() 191 { 192 try 193 { 194 _registry.cleanupThread(); 195 } 196 catch (Exception ex) 197 { 198 LOG.error(ServletMessages.filterCleanupError(ex), ex); 199 } 200 } 201 202 /** 203 * Invokes {@link Registry#shutdown()}. 204 */ 205 public void destroy() 206 { 207 if (_registry != null) 208 _registry.shutdown(); 209 210 _filterConfig = null; 211 } 212 213 /** 214 * Returns the {@link Registry} that was stored as a request attribute inside method 215 * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}. 216 */ 217 public static Registry getRegistry(HttpServletRequest request) 218 { 219 return (Registry) request.getAttribute(REQUEST_KEY); 220 } 221 222 /** 223 * Sets a flag in the request that will cause the current Registry to be shutdown and replaced 224 * with a new Registry (at the end of the current request). 225 */ 226 public static void rebuildRegistry(HttpServletRequest request) 227 { 228 request.setAttribute(REBUILD_REQUEST_KEY, Boolean.TRUE); 229 } 230 231 }