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.ModuleDescriptorProvider; 033 import org.apache.hivemind.Registry; 034 import org.apache.hivemind.impl.DefaultClassResolver; 035 import org.apache.hivemind.impl.RegistryBuilder; 036 import org.apache.hivemind.impl.XmlModuleDescriptorProvider; 037 import org.apache.hivemind.util.ContextResource; 038 039 /** 040 * Servlet filter that constructs the Registry at startup. It ensures that each request is properly 041 * terminated with a call to 042 * {@link org.apache.hivemind.service.ThreadEventNotifier#fireThreadCleanup()}. It also makes the 043 * Registry available during the request by storing it as a request attribute. 044 * 045 * @author Howard Lewis Ship 046 */ 047 public class HiveMindFilter implements Filter 048 { 049 private static final Log LOG = LogFactory.getLog(HiveMindFilter.class); 050 051 /** 052 * Request attribute key that stores the Registry. 053 */ 054 055 static final String REQUEST_KEY = "org.apache.hivemind.RequestRegistry"; 056 057 static final String REBUILD_REQUEST_KEY = "org.apache.hivemind.RebuildRegistry"; 058 059 /** @since 1.1 */ 060 static final String HIVE_MODULE_XML = "/WEB-INF/hivemodule.xml"; 061 062 private FilterConfig _filterConfig; 063 064 private Registry _registry; 065 066 /** 067 * Constructs a {@link Registry} and stores it into the <code>ServletContext</code>. Any 068 * exception throws is logged. 069 */ 070 public void init(FilterConfig config) throws ServletException 071 { 072 _filterConfig = config; 073 074 initializeRegistry(); 075 076 } 077 078 private void initializeRegistry() 079 { 080 long startTime = System.currentTimeMillis(); 081 082 LOG.info(ServletMessages.filterInit()); 083 084 try 085 { 086 _registry = constructRegistry(_filterConfig); 087 088 LOG.info(ServletMessages.constructedRegistry(_registry, System.currentTimeMillis() 089 - startTime)); 090 } 091 catch (Exception ex) 092 { 093 LOG.error(ex.getMessage(), ex); 094 } 095 } 096 097 /** 098 * Invoked from {@link #init(FilterConfig)} to actually construct the Registry. Subclasses may 099 * override if they have specific initialization needs, or have nonstandard rules for finding 100 * HiveMind module deployment descriptors. 101 */ 102 protected Registry constructRegistry(FilterConfig config) 103 { 104 RegistryBuilder builder = new RegistryBuilder(); 105 106 ClassResolver resolver = new DefaultClassResolver(); 107 108 builder.addModuleDescriptorProvider(getModuleDescriptorProvider(resolver)); 109 110 addWebInfDescriptor(config.getServletContext(), resolver, builder); 111 112 return builder.constructRegistry(getRegistryLocale()); 113 } 114 115 /** 116 * Invoked from {@link #constructRegistry(FilterConfig)} to add WEB-INF/hivemodule.xml to 117 * the registry, if it exists. 118 * 119 * @since 1.1 120 */ 121 122 protected void addWebInfDescriptor(ServletContext context, ClassResolver resolver, 123 RegistryBuilder builder) 124 { 125 ContextResource r = new ContextResource(context, HIVE_MODULE_XML); 126 127 if (r.getResourceURL() != null) 128 { 129 ModuleDescriptorProvider provider = new XmlModuleDescriptorProvider(resolver, r); 130 131 builder.addModuleDescriptorProvider(provider); 132 } 133 } 134 135 /** 136 * Returns the default Locale. Subclasses may override to select a particular locale for the 137 * Registry. 138 */ 139 protected Locale getRegistryLocale() 140 { 141 return Locale.getDefault(); 142 } 143 144 /** 145 * Returns the {@link ModuleDescriptorProvider} to be used to construct the Registry. This 146 * implementation returns the default {@link XmlModuleDescriptorProvider}. May be overridden by 147 * subclasses. 148 * 149 * @since 1.1 150 */ 151 protected ModuleDescriptorProvider getModuleDescriptorProvider(ClassResolver resolver) 152 { 153 return new XmlModuleDescriptorProvider(resolver); 154 } 155 156 /** 157 * Passes the request to the filter chain, but then invokes {@link Registry#cleanupThread()} 158 * (from a finally block). 159 */ 160 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 161 throws IOException, ServletException 162 { 163 try 164 { 165 // I believe the _registry will only be null in a couple of test situations. 166 167 if (_registry != null) 168 _registry.setupThread(); 169 170 request.setAttribute(REQUEST_KEY, _registry); 171 172 chain.doFilter(request, response); 173 } 174 finally 175 { 176 cleanupThread(); 177 178 checkRegistryRebuild(request); 179 } 180 } 181 182 private synchronized void checkRegistryRebuild(ServletRequest request) 183 { 184 if (request.getAttribute(REBUILD_REQUEST_KEY) == null) 185 return; 186 187 Registry oldRegistry = _registry; 188 189 // Replace the old Registry with a new one. All other threads, but this 190 // one, will begin using the new Registry. Hopefully, we didn't get 191 // rebuild requests on multiple threads. 192 193 initializeRegistry(); 194 195 // Shutdown the old Registry. Perhaps we should sleep for a moment, first, 196 // to help ensure that other threads have "cleared out". If not, we'll see some 197 // instability at the instant we shutdown (i.e., all the proxies will get disabled). 198 // Alternately, we should create a WeakReference based monitor that shuts down the 199 // old registry when it is no longer used by any other threads. For the moment, 200 // this functionality is limited to development-time only (not production), so it isn't 201 // urgent. 202 203 oldRegistry.shutdown(); 204 } 205 206 /** 207 * Cleanup the thread, ignoring any exceptions that may be thrown. 208 */ 209 private void cleanupThread() 210 { 211 try 212 { 213 _registry.cleanupThread(); 214 } 215 catch (Exception ex) 216 { 217 LOG.error(ServletMessages.filterCleanupError(ex), ex); 218 } 219 } 220 221 /** 222 * Invokes {@link Registry#shutdown()}. 223 */ 224 public void destroy() 225 { 226 if (_registry != null) 227 _registry.shutdown(); 228 229 _filterConfig = null; 230 } 231 232 /** 233 * Returns the {@link Registry} that was stored as a request attribute inside method 234 * {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}. 235 */ 236 public static Registry getRegistry(HttpServletRequest request) 237 { 238 return (Registry) request.getAttribute(REQUEST_KEY); 239 } 240 241 /** 242 * Sets a flag in the request that will cause the current Registry to be shutdown and replaced 243 * with a new Registry (at the end of the current request). 244 */ 245 public static void rebuildRegistry(HttpServletRequest request) 246 { 247 request.setAttribute(REBUILD_REQUEST_KEY, Boolean.TRUE); 248 } 249 250 }