001/*
002 *  jDTAUS Core RI Servlet Container
003 *  Copyright (C) 2005 Christian Schulte
004 *  <cs@schulte.it>
005 *
006 *  This library is free software; you can redistribute it and/or
007 *  modify it under the terms of the GNU Lesser General Public
008 *  License as published by the Free Software Foundation; either
009 *  version 2.1 of the License, or any later version.
010 *
011 *  This library is distributed in the hope that it will be useful,
012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 *  Lesser General Public License for more details.
015 *
016 *  You should have received a copy of the GNU Lesser General Public
017 *  License along with this library; if not, write to the Free Software
018 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
019 *
020 */
021package org.jdtaus.core.container.ri.servlet;
022
023import java.io.IOException;
024import java.lang.reflect.InvocationTargetException;
025import java.lang.reflect.Method;
026import java.util.Locale;
027import javax.servlet.Filter;
028import javax.servlet.FilterChain;
029import javax.servlet.FilterConfig;
030import javax.servlet.ServletContext;
031import javax.servlet.ServletException;
032import javax.servlet.ServletRequest;
033import javax.servlet.ServletResponse;
034import javax.servlet.http.HttpServletRequest;
035import javax.servlet.http.HttpSession;
036import org.jdtaus.core.container.ContainerFactory;
037import org.jdtaus.core.container.Context;
038import org.jdtaus.core.container.ContextFactory;
039import org.jdtaus.core.container.ModelFactory;
040
041/**
042 * jDTAUS webapp integration filter.
043 * <p>This filter needs to be setup in each application's {@code web.xml}
044 * descriptor like:<pre>
045 * &lt;filter&gt;
046 *   &lt;filter-name&gt;jDTAUS Integration Filter&lt;/filter-name&gt;
047 *   &lt;filter-class&gt;org.jdtaus.core.container.ri.servlet.ServletFilter&lt;/filter-class&gt;
048 * &lt;/filter&gt;</pre></p>
049 *
050 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
051 * @version $JDTAUS: ServletFilter.java 8743 2012-10-07 03:06:20Z schulte $
052 *
053 * @see #init(FilterConfig)
054 */
055public class ServletFilter implements Filter
056{
057    //--ServletFilter-----------------------------------------------------------
058
059    /** Holds thread local {@code ServletContext}s. */
060    private static final ThreadLocal CONTEXTS = new ThreadLocal();
061
062    /** Holds thread local {@code HttpSession}s. */
063    private static final ThreadLocal SESSIONS = new ThreadLocal();
064
065    /** Holds thread local {@code Locale}s. */
066    private static final ThreadLocal LOCALES = new ThreadLocal();
067
068    /** Holds the {@code ServletContext} the filter got initialized with. */
069    private ServletContext servletContext;
070
071    /**
072     * Gets the thread local servlet context.
073     *
074     * @return the thread local servlet context.
075     *
076     * @throws ContextLostException if no servlet context is bound to the
077     * current thread of execution.
078     */
079    public static ServletContext getServletContext()
080    {
081        final ServletContext context = (ServletContext) CONTEXTS.get();
082
083        if ( context == null )
084        {
085            throw new ContextLostException( getLocale(),
086                                            Thread.currentThread() );
087
088        }
089
090        return context;
091    }
092
093    /**
094     * Gets the thread local HTTP session.
095     *
096     * @return the thread local HTTP session.
097     *
098     * @throws SessionLostException if no session is bound to the current
099     * thread of execution or if the requesting {@code ServletRequest} is not
100     * an instance of {@code HttpServletRequest}.
101     */
102    public static HttpSession getHttpSession()
103    {
104        final HttpSession session = (HttpSession) SESSIONS.get();
105
106        if ( session == null )
107        {
108            throw new SessionLostException( getLocale(),
109                                            Thread.currentThread() );
110
111        }
112
113        return session;
114    }
115
116    /**
117     * Gets the thread local locale.
118     *
119     * @return the thread local locale.
120     */
121    public static Locale getLocale()
122    {
123        final Locale locale = (Locale) LOCALES.get();
124        return locale == null ? Locale.getDefault() : locale;
125    }
126
127    //-----------------------------------------------------------ServletFilter--
128    //--Filter------------------------------------------------------------------
129
130    /**
131     * Initializes the filter by configuring {@code ContainerFactory},
132     * {@code ModelFactory} and {@code ContextFactory} to use the
133     * {@code ServletContextFactories} implementation.
134     *
135     * @see ServletContextFactories
136     */
137    public void init( final FilterConfig filterConfig ) throws ServletException
138    {
139        this.servletContext = filterConfig.getServletContext();
140
141        // Initialize system properties if nothing is provided.
142        if ( System.getProperty( ContainerFactory.class.getName() ) == null )
143        {
144            System.setProperty( ContainerFactory.class.getName(),
145                                ServletContextFactories.class.getName() );
146
147            filterConfig.getServletContext().log( this.getSystemPropertyMessage(
148                Locale.getDefault(), ContainerFactory.class.getName(),
149                ServletContextFactories.class.getName() ) );
150
151        }
152        if ( System.getProperty( ContextFactory.class.getName() ) == null )
153        {
154            System.setProperty( ContextFactory.class.getName(),
155                                ServletContextFactories.class.getName() );
156
157            filterConfig.getServletContext().log( this.getSystemPropertyMessage(
158                Locale.getDefault(), ContextFactory.class.getName(),
159                ServletContextFactories.class.getName() ) );
160
161        }
162        if ( System.getProperty( ModelFactory.class.getName() ) == null )
163        {
164            System.setProperty( ModelFactory.class.getName(),
165                                ServletContextFactories.class.getName() );
166
167            filterConfig.getServletContext().log( this.getSystemPropertyMessage(
168                Locale.getDefault(), ModelFactory.class.getName(),
169                ServletContextFactories.class.getName() ) );
170
171        }
172        if ( System.getProperty( Context.class.getName() ) == null )
173        {
174            System.setProperty( Context.class.getName(),
175                                HttpSessionContext.class.getName() );
176
177            filterConfig.getServletContext().log( this.getSystemPropertyMessage(
178                Locale.getDefault(), Context.class.getName(),
179                ServletContextFactories.class.getName() ) );
180
181        }
182    }
183
184    /**
185     * Attaches the request's {@code HttpSession} with corresponding
186     * {@code ServletContext} to the current thread of execution.
187     *
188     * @see #getServletContext()
189     * @see #getHttpSession()
190     */
191    public void doFilter( final ServletRequest req, final ServletResponse rsp,
192                          final FilterChain chain )
193        throws IOException, ServletException
194    {
195        try
196        {
197            if ( req instanceof HttpServletRequest )
198            {
199                final HttpServletRequest request = (HttpServletRequest) req;
200                final HttpSession session = request.getSession( true );
201
202                CONTEXTS.set( session.getServletContext() );
203                SESSIONS.set( session );
204            }
205            else
206            {
207                CONTEXTS.set( this.servletContext );
208                SESSIONS.set( null );
209            }
210
211            LOCALES.set( req.getLocale() );
212            chain.doFilter( req, rsp );
213        }
214        finally
215        {
216            this.removeThreadLocal( CONTEXTS );
217            this.removeThreadLocal( SESSIONS );
218            this.removeThreadLocal( LOCALES );
219        }
220    }
221
222    public void destroy()
223    {
224        this.servletContext = null;
225    }
226
227    //------------------------------------------------------------------Filter--
228    //--ServletFilter-----------------------------------------------------------
229
230    /** Creates a new {@code ServletFilter} instance. */
231    public ServletFilter()
232    {
233        super();
234    }
235
236    private void removeThreadLocal( final ThreadLocal threadLocal )
237    {
238        try
239        {
240            // Try remove method introduced in JDK 1.5.
241            final Method removeMethod = threadLocal.getClass().
242                getDeclaredMethod( "remove", new Class[ 0 ] );
243
244            removeMethod.invoke( threadLocal, null );
245        }
246        catch ( final IllegalAccessException e )
247        {
248            threadLocal.set( null );
249            this.servletContext.log( e.getMessage(), e );
250        }
251        catch ( final IllegalArgumentException e )
252        {
253            threadLocal.set( null );
254            this.servletContext.log( e.getMessage(), e );
255        }
256        catch ( final InvocationTargetException e )
257        {
258            threadLocal.set( null );
259            this.servletContext.log( e.getMessage(), e );
260        }
261        catch ( final NoSuchMethodException e )
262        {
263            threadLocal.set( null );
264        }
265    }
266
267    //-----------------------------------------------------------ServletFilter--
268    //--Messages----------------------------------------------------------------
269
270// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
271    // This section is managed by jdtaus-container-mojo.
272
273    /**
274     * Gets the text of message <code>systemProperty</code>.
275     * <blockquote><pre>System Eigenschaft {0} auf {1} gesetzt.</pre></blockquote>
276     * <blockquote><pre>System property {0} set to {1}.</pre></blockquote>
277     *
278     * @param locale The locale of the message instance to return.
279     * @param propertyName Name of the updated system property.
280     * @param value Value the system property got updated to.
281     *
282     * @return Information about an updated system property.
283     */
284    private String getSystemPropertyMessage( final Locale locale,
285            final java.lang.String propertyName,
286            final java.lang.String value )
287    {
288        return ContainerFactory.getContainer().
289            getMessage( this, "systemProperty", locale,
290                new Object[]
291                {
292                    propertyName,
293                    value
294                });
295
296    }
297
298// </editor-fold>//GEN-END:jdtausMessages
299
300    //----------------------------------------------------------------Messages--
301}