2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shale.remoting.impl;
19
20 import java.io.IOException;
21 import javax.faces.context.FacesContext;
22 import javax.faces.el.MethodBinding;
23 import javax.servlet.ServletContext;
24 import javax.servlet.http.HttpServletResponse;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.shale.remoting.Constants;
28 import org.apache.shale.remoting.impl.FilteringProcessor;
29 import org.apache.shale.remoting.Processor;
30
31 /***
32 * <p>Implementation of {@link Processor} which maps a resource identifier
33 * to a method binding expression, then delegates the creation of the current
34 * response to the execution of that method. The precise details of how a
35 * resource identifier gets mapped are encapsulated in the <code>mapResourceId</code>
36 * method, which may be specialized as desired in a subclass.</p>
37 */
38 public class MethodBindingProcessor extends FilteringProcessor {
39
40
41
42
43
44
45
46
47
48 /***
49 * <p><code>Log</code> instance for this class.</p>
50 */
51 private transient Log log = null;
52
53
54
55
56
57 /***
58 * <p>Force our default excludes list to be included.</p>
59 *
60 * @param excludes Application specified excludes list
61 */
62 public void setExcludes(String excludes) {
63
64 if ((excludes != null) && (excludes.length() > 0)) {
65 super.setExcludes(Constants.DYNAMIC_RESOURCES_EXCLUDES_DEFAULT
66 + "," + excludes);
67 } else {
68 super.setExcludes(Constants.DYNAMIC_RESOURCES_EXCLUDES_DEFAULT);
69 }
70
71 }
72
73
74
75
76
77 /***
78 * <p>Convert the specified resource identifier into a method binding
79 * expression, and delegate creation of the response to a call to the
80 * identified method. Call <code>FacesContext.responseComplete()</code>
81 * to tell JavaServer Faces that the entire response has already been
82 * created.</p>
83 *
84 * @param context <code>FacesContext</code> for the current request
85 * @param resourceId Resource identifier used to select the appropriate response
86 * (this will generally be a context relative path starting with "/")
87 *
88 * @exception IllegalArgumentException if the view identifier is not
89 * well formed (starting with a '/' character)
90 * @exception IOException if an input/output error occurs
91 * @exception NullPointerException if <code>viewId</code> is <code>null</code>
92 */
93 public void process(FacesContext context, String resourceId) throws IOException {
94
95
96
97 if (context.getResponseComplete()) {
98 return;
99 }
100
101
102 if (!accept(resourceId)) {
103 if (log().isTraceEnabled()) {
104 log().trace("Resource id '" + resourceId
105 + "' rejected by include/exclude rules");
106 }
107
108
109
110 sendNotFound(context, resourceId);
111 context.responseComplete();
112 return;
113 }
114
115
116 MethodBinding mb = mapResourceId(context, resourceId);
117 if (log().isDebugEnabled()) {
118 log().debug("Translated resource id '" + resourceId
119 + "' to method binding expression '"
120 + mb.getExpressionString() + "'");
121 }
122 mb.invoke(context, new Object[] { });
123
124
125 context.responseComplete();
126
127 }
128
129
130
131
132
133 /***
134 * <p>Map the specified resource identifier into a corresponding
135 * <code>MethodBinding</code> which identifies the method which will be
136 * called to produce this response.</p>
137 *
138 * <p>The default algorithm performs this conversion as follows:</p>
139 * <ul>
140 * <li>Strip any leading slash character.</li>
141 * <li>Convert embedded slash characters to periods.</li>
142 * <li>Surround the result with "#{" and "}" delimiters.</li>
143 * <li>Ask JavaServer Faces to create a method binding, using this
144 * expression, for a method that takes no parameters.</li>
145 * </ul>
146 *
147 * @param context <code>FacesContext</code> for the current request
148 * @param resourceId Resource identifier to be mapped
149 */
150 protected MethodBinding mapResourceId(FacesContext context, String resourceId) {
151
152
153 if (resourceId.startsWith("/")) {
154 resourceId = resourceId.substring(1);
155 }
156
157
158
159 resourceId = resourceId.replace('/', '.');
160
161
162 resourceId = "#{" + resourceId + "}";
163
164
165 return context.getApplication().createMethodBinding(resourceId,
166 new Class[] { });
167
168 }
169
170
171
172
173
174
175 /***
176 * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
177 */
178 private Log log() {
179
180 if (this.log == null) {
181 log = LogFactory.getLog(MethodBindingProcessor.class);
182 }
183 return log;
184
185 }
186
187
188 /***
189 * <p>Send a "not found" HTTP response, if possible. Otherwise, throw an
190 * <code>IllegalArgumentException</code> that will ripple out.</p>
191 *
192 * @param context <code>FacesContext</code> for the current request
193 * @param resourceId Resource identifier of the resource that was not found
194 *
195 * @exception IllegalArgumentException if we cannot send an HTTP response
196 * @exception IOException if an input/output error occurs
197 *
198 * @since 1.0.4
199 */
200 private void sendNotFound(FacesContext context, String resourceId) throws IOException {
201
202 if (servletRequest(context)) {
203 HttpServletResponse response = (HttpServletResponse)
204 context.getExternalContext().getResponse();
205 response.sendError(HttpServletResponse.SC_NOT_FOUND, resourceId);
206 } else {
207 throw new IllegalArgumentException(resourceId);
208 }
209
210 }
211
212
213 /***
214 * <p>Return <code>true</code> if we are processing a servlet request (as
215 * opposed to a portlet request).</p>
216 *
217 * @param context <code>FacesContext</code> for the current request
218 *
219 * @since 1.0.4
220 */
221 private boolean servletRequest(FacesContext context) {
222
223 return context.getExternalContext().getContext() instanceof ServletContext;
224
225 }
226
227
228 }