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         * &nbsp; (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    }