1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.continuation;
20
21import java.io.IOException;
22
23import javax.servlet.Filter;
24import javax.servlet.FilterChain;
25import javax.servlet.FilterConfig;
26import javax.servlet.ServletContext;
27import javax.servlet.ServletException;
28import javax.servlet.ServletRequest;
29import javax.servlet.ServletResponse;
30
31
32
33/* ------------------------------------------------------------ */
34/**
35 * <p>ContinuationFilter must be applied to servlet paths that make use of
36 * the asynchronous features provided by {@link Continuation} APIs, but that
37 * are deployed in servlet containers that are neither Jetty (>= 7) nor a
38 * compliant Servlet 3.0 container.</p>
39 * <p>The following init parameters may be used to configure the filter (these are mostly for testing):</p>
40 * <dl>
41 * <dt>debug</dt><dd>Boolean controlling debug output</dd>
42 * <dt>jetty6</dt><dd>Boolean to force use of Jetty 6 continuations</dd>
43 * <dt>faux</dt><dd>Boolean to force use of faux continuations</dd>
44 * </dl>
45 * <p>If the servlet container is not Jetty (either 6 or 7) nor a Servlet 3
46 * container, then "faux" continuations will be used.</p>
47 * <p>Faux continuations will just put the thread that called {@link Continuation#suspend()}
48 * in wait, and will notify that thread when {@link Continuation#resume()} or
49 * {@link Continuation#complete()} is called.</p>
50 * <p>Faux continuations are not threadless continuations (they are "faux" - fake - for this reason)
51 * and as such they will scale less than proper continuations.</p>
52 */
53public class ContinuationFilter implements Filter
54{
55    static boolean _initialized;
56    static boolean __debug; // shared debug status
57    private boolean _faux;
58    private boolean _jetty6;
59    private boolean _filtered;
60    ServletContext _context;
61    private boolean _debug;
62
63    public void init(FilterConfig filterConfig) throws ServletException
64    {
65        boolean jetty_7_or_greater="org.eclipse.jetty.servlet".equals(filterConfig.getClass().getPackage().getName());
66        _context = filterConfig.getServletContext();
67
68        String param=filterConfig.getInitParameter("debug");
69        _debug=param!=null&&Boolean.parseBoolean(param);
70        if (_debug)
71            __debug=true;
72
73        param=filterConfig.getInitParameter("jetty6");
74        if (param==null)
75            param=filterConfig.getInitParameter("partial");
76        if (param!=null)
77            _jetty6=Boolean.parseBoolean(param);
78        else
79            _jetty6=ContinuationSupport.__jetty6 && !jetty_7_or_greater;
80
81        param=filterConfig.getInitParameter("faux");
82        if (param!=null)
83            _faux=Boolean.parseBoolean(param);
84        else
85            _faux=!(jetty_7_or_greater || _jetty6 || _context.getMajorVersion()>=3);
86
87        _filtered=_faux||_jetty6;
88        if (_debug)
89            _context.log("ContinuationFilter "+
90                    " jetty="+jetty_7_or_greater+
91                    " jetty6="+_jetty6+
92                    " faux="+_faux+
93                    " filtered="+_filtered+
94                    " servlet3="+ContinuationSupport.__servlet3);
95        _initialized=true;
96    }
97
98    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
99    {
100        if (_filtered)
101        {
102            Continuation c = (Continuation) request.getAttribute(Continuation.ATTRIBUTE);
103            FilteredContinuation fc;
104            if (_faux && (c==null || !(c instanceof FauxContinuation)))
105            {
106                fc = new FauxContinuation(request);
107                request.setAttribute(Continuation.ATTRIBUTE,fc);
108            }
109            else
110                fc=(FilteredContinuation)c;
111
112            boolean complete=false;
113            while (!complete)
114            {
115                try
116                {
117                    if (fc==null || (fc).enter(response))
118                        chain.doFilter(request,response);
119                }
120                catch (ContinuationThrowable e)
121                {
122                    debug("faux",e);
123                }
124                finally
125                {
126                    if (fc==null)
127                        fc = (FilteredContinuation) request.getAttribute(Continuation.ATTRIBUTE);
128
129                    complete=fc==null || (fc).exit();
130                }
131            }
132        }
133        else
134        {
135            try
136            {
137                chain.doFilter(request,response);
138            }
139            catch (ContinuationThrowable e)
140            {
141                debug("caught",e);
142            }
143        }
144    }
145
146    private void debug(String string)
147    {
148        if (_debug)
149        {
150            _context.log(string);
151        }
152    }
153
154    private void debug(String string, Throwable th)
155    {
156        if (_debug)
157        {
158            if (th instanceof ContinuationThrowable)
159                _context.log(string+":"+th);
160            else
161                _context.log(string,th);
162        }
163    }
164
165    public void destroy()
166    {
167    }
168
169    public interface FilteredContinuation extends Continuation
170    {
171        boolean enter(ServletResponse response);
172        boolean exit();
173    }
174}
175