1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.myfaces.trinidadinternal.webapp;
20
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.Map;
24
25 import javax.faces.context.ExternalContext;
26 import javax.faces.context.FacesContext;
27 import javax.servlet.Filter;
28 import javax.servlet.FilterChain;
29 import javax.servlet.FilterConfig;
30 import javax.servlet.ServletContext;
31 import javax.servlet.ServletException;
32 import javax.servlet.ServletRequest;
33 import javax.servlet.ServletResponse;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36
37 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
38 import org.apache.myfaces.trinidad.util.ClassLoaderUtils;
39 import org.apache.myfaces.trinidadinternal.config.GlobalConfiguratorImpl;
40 import org.apache.myfaces.trinidadinternal.config.dispatch.DispatchResponseConfiguratorImpl;
41 import org.apache.myfaces.trinidadinternal.config.dispatch.DispatchServletResponse;
42 import org.apache.myfaces.trinidadinternal.config.upload.FileUploadConfiguratorImpl;
43 import org.apache.myfaces.trinidadinternal.config.upload.UploadRequestWrapper;
44 import org.apache.myfaces.trinidadinternal.config.xmlHttp.XmlHttpConfigurator;
45 import org.apache.myfaces.trinidadinternal.context.RequestContextImpl;
46 import org.apache.myfaces.trinidadinternal.context.external.ServletExternalContext;
47 import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderKit;
48
49 /**
50 * Actual implementation of the Trinidad servlet filter.
51 * <p>
52 * @version $Name: $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/webapp/AdfFacesFilterImpl.java#0 $) $Date: 10-nov-2005.18:48:59 $
53 * @todo Allow configuration of the maximum allowed number of bytes in
54 * an entire request
55 */
56 public class TrinidadFilterImpl implements Filter
57 {
58 static public void verifyFilterIsInstalled(FacesContext context)
59 {
60 Object isInstalled =
61 context.getExternalContext().getRequestMap().get(_FILTER_EXECUTED_KEY);
62 if (!Boolean.TRUE.equals(isInstalled))
63 {
64 _LOG.warning("REQUIRED_TRINIDADFILTER_NOT_INSTALLED");
65
66 }
67 }
68
69 static public FacesContext getPseudoFacesContext()
70 {
71 return _PSEUDO_FACES_CONTEXT.get();
72 }
73
74 /**
75 * Returns true if the filter is in the middle of executing the
76 * "return from dialog"
77 */
78 static public boolean isExecutingDialogReturn(FacesContext context)
79 {
80 return Boolean.TRUE.equals(
81 context.getExternalContext().getRequestMap().get(_IS_RETURNING_KEY));
82 }
83
84 public void init(FilterConfig filterConfig) throws ServletException
85 {
86 _servletContext = filterConfig.getServletContext();
87
88 //There is some functionality that still might require servlet-only filter services.
89 _filters = ClassLoaderUtils.getServices(TrinidadFilterImpl.class.getName());
90 for(Filter f:_filters)
91 {
92 f.init(filterConfig);
93 }
94 }
95
96 public void destroy()
97 {
98 //Destroy filter services
99 for(Filter f:_filters)
100 {
101 f.destroy();
102 }
103
104 _filters = null;
105 }
106
107
108 @SuppressWarnings("unchecked")
109 public void doFilter(
110 ServletRequest request,
111 ServletResponse response,
112 FilterChain chain) throws IOException, ServletException
113 {
114 //Execute the filter services
115 if (!_filters.isEmpty())
116 chain = new FilterListChain(_filters, chain);
117
118 // Set a flag so that we can detect if the filter has been
119 // properly installed.
120 request.setAttribute(_FILTER_EXECUTED_KEY, Boolean.TRUE);
121
122 ExternalContext externalContext = new ServletExternalContext(_servletContext, request, response);
123 GlobalConfiguratorImpl config = GlobalConfiguratorImpl.getInstance();
124 config.beginRequest(externalContext);
125
126 //To maintain backward compatibilty, wrap the request at the filter level
127 Map<String, String[]> addedParams = FileUploadConfiguratorImpl.getAddedParameters(externalContext);
128
129 if(addedParams != null)
130 {
131 FileUploadConfiguratorImpl.apply(externalContext);
132 request = new UploadRequestWrapper((HttpServletRequest)request, addedParams);
133 }
134
135 try
136 {
137
138 _doFilterImpl(request, response, chain);
139 }
140 // For PPR errors, handle the request specially
141 catch (Throwable t)
142 {
143 boolean isPartialRequest;
144 if (addedParams != null)
145 {
146 isPartialRequest = CoreRenderKit.isPartialRequest(addedParams);
147 }
148 else
149 {
150 isPartialRequest = CoreRenderKit.isPartialRequest(externalContext);
151 }
152
153 if (isPartialRequest)
154 {
155 XmlHttpConfigurator.handleError(externalContext, t);
156 }
157 else
158 {
159 // For non-partial requests, just re-throw. It is not
160 // our responsibility to catch these
161 if (t instanceof RuntimeException)
162 throw ((RuntimeException) t);
163 if (t instanceof Error)
164 throw ((Error) t);
165 if (t instanceof IOException)
166 throw ((IOException) t);
167 if (t instanceof ServletException)
168 throw ((ServletException) t);
169
170 // Should always be one of those four types to have
171 // gotten here.
172 _LOG.severe(t);
173 }
174
175 }
176 finally
177 {
178 config.endRequest(externalContext);
179 }
180 }
181
182
183 @SuppressWarnings("unchecked")
184 private void _doFilterImpl(
185 ServletRequest request,
186 ServletResponse response,
187 FilterChain chain) throws IOException, ServletException
188 {
189 // -= Scott O'Bryan =-
190 // Added for backward compatibility
191 ExternalContext ec = new ServletExternalContext(_servletContext, request, response);
192 HttpServletResponse dispatch = new DispatchServletResponse(ec);
193 DispatchResponseConfiguratorImpl.apply(ec);
194
195 _invokeDoFilter(request, dispatch, chain);
196
197 // If there are existing "launchParameters", then that means
198 // we've returned from a "launch", and we need to re-execute the
199 // faces lifecycle. ViewHandlerImpl will be responsible for ensuring
200 // that we re-execute the lifecycle on the correct page.
201 // -= Simon Lessard =-
202 // FIXME: Using <String, String[]> for now to accomodate ReplaceParametersRequestWrapper.
203 // However, the Servlet specification suggest <String, Object> so this
204 // could lead to some nasty problems one day. Especially if JEE spec includes
205 // generics for its Servlet API soon.
206 //
207 // -= Scott O'Bryan =-
208 // TODO: The following should be made available to the Portal. This is not trivial
209 // because this just re-invokes the filter chain with a new set of parameters.
210 // In the portal environment, this must rerun the portlet without the use of
211 // filters until Portlet 2.0.
212 Map<String, String[]> launchParameters = (Map<String, String[]>)
213 request.getAttribute(RequestContextImpl.LAUNCH_PARAMETERS);
214 if (launchParameters != null)
215 {
216 request.removeAttribute(RequestContextImpl.LAUNCH_PARAMETERS);
217 request.setAttribute(_IS_RETURNING_KEY, Boolean.TRUE);
218 request = new ReplaceParametersRequestWrapper(
219 (HttpServletRequest) request, launchParameters);
220 _invokeDoFilter(request, dispatch, chain);
221 request.removeAttribute(_IS_RETURNING_KEY);
222 }
223 }
224
225 private void _invokeDoFilter(
226 ServletRequest request,
227 ServletResponse response,
228 FilterChain chain) throws IOException, ServletException
229 {
230 // Set up a PseudoFacesContext with the actual request and response
231 // so that RequestContext can be more functional in the interval
232 // between now and when the FacesServlet starts.
233 PseudoFacesContext pfc = new PseudoFacesContext(
234 new ServletExternalContext(_servletContext, request, response));
235 _PSEUDO_FACES_CONTEXT.set(pfc);
236 try
237 {
238 chain.doFilter(request, response);
239 }
240 finally
241 {
242 _PSEUDO_FACES_CONTEXT.remove();
243 }
244 }
245
246
247 private static final class FilterListChain implements FilterChain
248 {
249 private final List<Filter> _filters;
250 private final FilterChain _last;
251 private final int _index;
252
253 public FilterListChain(List<Filter> filters, FilterChain last)
254 {
255 this(filters, last, 0);
256 }
257
258 private FilterListChain(List<Filter> filters, FilterChain last, int index)
259 {
260 assert index < filters.size();
261 _filters = filters;
262 _last = last;
263 _index = index;
264 }
265
266 public void doFilter(ServletRequest request, ServletResponse response)
267 throws IOException, ServletException
268 {
269 int nextIndex = _index+1;
270 final FilterChain next;
271 // if there are more filters to chain, then keep using
272 // FilterListChain; otherwise, just use the last chain:
273 if (nextIndex < _filters.size())
274 next = new FilterListChain(_filters, _last, nextIndex);
275 else
276 next = _last;
277
278 _filters.get(_index).doFilter(request, response, next);
279 }
280 }
281
282 private ServletContext _servletContext;
283 private List<Filter> _filters = null;
284
285 private static final String _IS_RETURNING_KEY =
286 "org.apache.myfaces.trinidadinternal.webapp.AdfacesFilterImpl.IS_RETURNING";
287 private static final String _FILTER_EXECUTED_KEY =
288 "org.apache.myfaces.trinidadinternal.webapp.AdfacesFilterImpl.EXECUTED";
289
290 private static ThreadLocal<PseudoFacesContext> _PSEUDO_FACES_CONTEXT =
291 new ThreadLocal<PseudoFacesContext>()
292 {
293 @Override
294 protected PseudoFacesContext initialValue() { return null; }
295 };
296
297 private static final TrinidadLogger _LOG =
298 TrinidadLogger.createTrinidadLogger(TrinidadFilterImpl.class);
299 }