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.FacesException;
22 import javax.faces.context.FacesContext;
23 import javax.servlet.ServletContext;
24 import javax.servlet.http.HttpServletResponse;
25 import org.apache.commons.chain.Catalog;
26 import org.apache.commons.chain.CatalogFactory;
27 import org.apache.commons.chain.Command;
28 import org.apache.commons.chain.Context;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.shale.remoting.Processor;
32
33 /***
34 * <p>Implementation of {@link Processor} which maps a resource identifier
35 * to the name of a <a href="http://jakarta.apache.org/commons/chain">Commons
36 * Chain</a> command or chain, in an appropriate catalog. The command or chain
37 * that is executed is passed an appropriate <code>Context</code> object, and
38 * it will also have access to the current JavaServer Faces state by calling
39 * <code>FacesContext.getCurrentInstance()</code>.</p>
40 */
41 public class ChainProcessor implements Processor {
42
43
44
45
46
47
48
49
50
51 /***
52 * <p>The <code>Log</code> instance for this class.</p>
53 */
54 private transient Log log = null;
55
56
57
58
59
60 /***
61 * <p>Map the specified resource identifier to an appropriate Commons
62 * Chain command or chain, in an appropriate catalog. Construct an
63 * appropriate <code>Context</code> object, and execute the specified
64 * command or chain, to which we delegate responsibility for creating
65 * the response for the current request. Call
66 * <code>FacesContext.responseComplete()</code> to tell JavaServer Faces
67 * that the entire response has already been created.</p>
68 *
69 * @param context <code>FacesContext</code> for the current request
70 * @param resourceId Resource identifier used to select the appropriate response
71 * (this will generally be a context relative path starting with "/")
72 *
73 * @exception IOException if an input/output error occurs
74 * @exception NullPointerException if <code>viewId</code> is <code>null</code>
75 */
76 public void process(FacesContext context, String resourceId) throws IOException {
77
78 if (log().isDebugEnabled()) {
79 log().debug("Translated resource id '" + resourceId + "' to catalog '"
80 + mapCatalog(context, resourceId) + "' and command '"
81 + mapCommand(context, resourceId) + "'");
82 }
83
84
85 String catalogName = mapCatalog(context, resourceId);
86 Catalog catalog = CatalogFactory.getInstance().getCatalog(catalogName);
87 if (catalog == null) {
88 if (log().isErrorEnabled()) {
89 log().error("Cannot find catalog '" + catalogName + "' for resource '"
90 + resourceId + "'");
91 }
92 sendNotFound(context, resourceId);
93 context.responseComplete();
94 return;
95 }
96
97
98 String commandName = mapCommand(context, resourceId);
99 Command command = catalog.getCommand(commandName);
100 if (command == null) {
101 if (log().isErrorEnabled()) {
102 log().error("Cannot find command '" + commandName + "' in catalog '"
103 + catalogName + "' for resource '" + resourceId + "'");
104 }
105 sendNotFound(context, resourceId);
106 context.responseComplete();
107 return;
108 }
109
110
111 try {
112 command.execute(createContext(context, resourceId));
113 } catch (Exception e) {
114 if (log().isErrorEnabled()) {
115 log().error("Exception executing command '" + commandName
116 + "' from catalog '" + catalogName + "' for resource '"
117 + resourceId + "'", e);
118 }
119 sendServerError(context, resourceId, e);
120 }
121
122
123 context.responseComplete();
124
125 }
126
127
128
129
130
131 /***
132 * <p>Create and return an appropriate <code>Context</code> instance to be
133 * passed to the command or chain that is executed.</p>
134 *
135 * <p>The default algorithm constructs and returns an instance of
136 * {@link ChainContext} that wraps the specified <code>FacesContext</code>.</p>
137 *
138 * @param context <code>FacesContext</code> for the current request
139 * @param resourceId Resource identifier to be mapped
140 */
141 protected Context createContext(FacesContext context, String resourceId) {
142
143 return new ChainContext(context);
144
145 }
146
147
148 /***
149 * <p>Map the specified resource identifier to the name of a Commons Chain
150 * <code>Catalog</code> from which the command or chain instance will be
151 * acquired.</p>
152 *
153 * <p>The default implementation returns <code>remoting</code>
154 * unconditionally.</p>
155 *
156 * @param context <code>FacesContext</code> for the current request
157 * @param resourceId Resource identifier to be mapped
158 */
159 protected String mapCatalog(FacesContext context, String resourceId) {
160
161 return "remoting";
162
163 }
164
165
166 /***
167 * <p>Map the specified resource identifier to the name of a Commons Chain
168 * <code>Command</code> or <code>Chain</code>, which will be acquired from
169 * a mapped <code>Catalog</code>.</p>
170 *
171 * <p>The default algorithm performs this conversion as follows:</p>
172 * <ul>
173 * <li>Strip any leading slash character.</li>
174 * <li>Convert embedded slash characters to periods.</li>
175 * </ul>
176 *
177 * @param context <code>FacesContext</code> for the current request
178 * @param resourceId Resource identifier to be mapped
179 */
180 protected String mapCommand(FacesContext context, String resourceId) {
181
182
183 if (resourceId.startsWith("/")) {
184 resourceId = resourceId.substring(1);
185 }
186
187
188 resourceId = resourceId.replace('/', '.');
189
190
191 return resourceId;
192
193 }
194
195
196 /***
197 * <p>Send a "not found" HTTP response, if possible. Otherwise, throw an
198 * <code>IllegalArgumentException</code> that will ripple out.</p>
199 *
200 * @param context <code>FacesContext</code> for the current request
201 * @param resourceId Resource identifier of the resource that was not found
202 *
203 * @exception IllegalArgumentException if we cannot send an HTTP response
204 * @exception IOException if an input/output error occurs
205 */
206 protected void sendNotFound(FacesContext context, String resourceId) throws IOException {
207
208 if (servletRequest(context)) {
209 HttpServletResponse response = (HttpServletResponse)
210 context.getExternalContext().getResponse();
211 response.sendError(HttpServletResponse.SC_NOT_FOUND, resourceId);
212 } else {
213 throw new IllegalArgumentException(resourceId);
214 }
215
216 }
217
218
219 /***
220 * <p>Send a "server error" HTTP response, if possible. Otherwise, throw a
221 * <code>FacesException</code> that will ripple out.</p>
222 *
223 * @param context <code>FacesContext</code> for the current request
224 * @param resourceId Resource identifier of the resource that was not found
225 * @param e Server exception to be reported
226 *
227 * @exception FacesException if we cannot send an HTTP response
228 * @exception IOException if an input/output error occurs
229 */
230 protected void sendServerError(FacesContext context, String resourceId,
231 Exception e) throws IOException {
232
233 if (servletRequest(context)) {
234 HttpServletResponse response = (HttpServletResponse)
235 context.getExternalContext().getResponse();
236 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, resourceId);
237 } else {
238 throw new FacesException(resourceId);
239 }
240
241 }
242
243
244 /***
245 * <p>Return <code>true</code> if we are processing a servlet request (as
246 * opposed to a portlet request).</p>
247 *
248 * @param context <code>FacesContext</code> for the current request
249 */
250 protected boolean servletRequest(FacesContext context) {
251
252 return context.getExternalContext().getContext() instanceof ServletContext;
253
254 }
255
256
257
258
259
260 /***
261 * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
262 */
263 private Log log() {
264
265 if (this.log == null) {
266 log = LogFactory.getLog(ChainProcessor.class);
267 }
268 return log;
269
270 }
271
272
273 }