View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.ajax;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.InputStreamReader;
22  import java.io.Reader;
23  import java.io.Writer;
24  import java.io.OutputStreamWriter;
25  import java.io.StringWriter;
26  import java.util.HashMap;
27  import java.util.Map;
28  
29  import javax.servlet.ServletOutputStream;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.jetspeed.layout.impl.Constants;
35  import org.apache.jetspeed.request.RequestContext;
36  import org.apache.velocity.VelocityContext;
37  import org.apache.velocity.app.VelocityEngine;
38  import org.apache.velocity.context.Context;
39  import org.apache.velocity.tools.generic.EscapeTool;
40  
41  /***
42   * 
43   * Provides a generic way to handle a Ajax request/response. Useful for AJAX since
44   * the processing can be broken down into actions and builders
45   */
46  public class AjaxRequestServiceImpl implements AjaxRequestService
47  {
48  
49      protected static final String CONTENT_TYPE = "text/xml";
50  
51      protected static final String AJAX_PROCESSOR = "AJAX processor";
52  
53      protected static final String DEFAULT_ERROR = "<js><status>failure</status><action>unknown</action></js>";
54  
55      // Name of the parameter that will be used to lookup the
56      // command object. Default is action.
57      //
58      // Sample URL:
59      // http://localhost/js?pipeline=layout&action=move
60      //
61      // In this case the parameter "action" is used to find "move"
62      // "move" will be used as the key to lookup the object that will
63      // handle the command
64      protected static final String URL_PARAMETER_NAME = "action";
65  
66      /*** Logger */
67      protected Log log = LogFactory.getLog(AjaxRequestServiceImpl.class);
68  
69      // Objects that are available to execution. These objects must
70      // implement either the Action interface or the Builder interface
71      // or both.
72      // If the Action interface is implemented, then the run method is called
73      // If the Build interface is implemented, then the build methods are called
74      protected Map objects;
75  
76      // Used to create the response XML
77      protected VelocityEngine velocityEngine = null;
78  
79      // Parameter on the URL that will be used to lookup the object
80      protected String urlParameterName = URL_PARAMETER_NAME;
81  
82      // Default Action if no action specified
83      protected String defaultAction = "getpage";
84      
85      // Handy Velocity Escape Tool (is threadsafe)
86      protected EscapeTool velocityEscTool = null;
87      
88      // Spring can be used to inject this information
89      public AjaxRequestServiceImpl(Map objects, VelocityEngine velocityEngine)
90      {
91          this.objects = objects;
92          this.velocityEngine = velocityEngine;
93          this.velocityEscTool = new EscapeTool();
94      }
95  
96      // Spring can be used to inject this information
97      public AjaxRequestServiceImpl(Map objects, VelocityEngine velocityEngine,
98              String urlParameterName)
99      {
100         this.objects = objects;
101         this.velocityEngine = velocityEngine;
102         this.urlParameterName = urlParameterName;
103         this.velocityEscTool = new EscapeTool();
104     }
105     
106     // This is the entry point for this service
107     public void process(RequestContext requestContext) throws AJAXException
108     {        
109         // Lookup the object that is to be used
110         String objectKey = requestContext.getRequestParameter(urlParameterName);        
111         if (objectKey == null)
112         {
113             objectKey = defaultAction;
114         }
115         // Get the object associated with this key
116         Object object = objects.get(objectKey);
117         if (object != null)
118         {
119             Map resultMap = new HashMap();
120 
121             boolean success = true;
122             try
123             {
124                 // Check to see if this object implements the action
125                 // interface
126                 if (object instanceof AjaxAction)
127                 {
128                     success = processAction((AjaxAction) object,
129                             requestContext, resultMap);
130                 }
131             } catch (Exception e)
132             {
133                 success = false;
134             }
135 
136             try
137             {
138                 // Check to see if this object implements the builder
139                 // interface
140                 if (object instanceof AjaxBuilder)
141                 {
142                     processBuilder((AjaxBuilder) object, resultMap,
143                             requestContext, success);
144                 }
145             } catch (Exception e)
146             {
147                 // The builder failed, return an error response
148                 buildError(requestContext);
149             }
150         } else
151         {
152             // Log an informational message
153             log.debug("could not find the object named:" + objectKey);
154 
155             // Return an error response
156             buildError(requestContext);
157         }
158     }
159 
160     // Process the action if provided
161     protected boolean processAction(AjaxAction action,
162             RequestContext requestContext, Map resultMap)
163             throws Exception
164     {
165         return action.run(requestContext, resultMap);
166     }
167 
168     // Process the builder if provided
169     protected void processBuilder(AjaxBuilder builder, Map inputMap,
170             RequestContext requestContext, boolean actionSuccessFlag)
171     {
172         // Response will always be text/xml
173         String format = requestContext.getRequestParameter("format");
174         if (format == null)
175         {
176             requestContext.getResponse().setContentType(CONTENT_TYPE);
177         }
178         else
179         {
180             if (format.equals("json"))
181             {
182                 requestContext.getResponse().setContentType("text/json");
183             }
184         }
185 
186         try
187         {
188             // Ask the builder to construct the context
189             // Add the input map to the velocity context
190             
191             boolean result = true;
192 
193             if (actionSuccessFlag == true)
194             {
195                 result = builder.buildContext(requestContext, inputMap);
196             }
197             else
198             {
199                 result = builder.buildErrorContext(requestContext, inputMap);
200             }
201             
202             Context context = new VelocityContext(inputMap);
203             context.put("esc", this.velocityEscTool);
204             
205             // Check to see if we have a valid context
206             if (result)
207             {
208                 // Get the name of the template from the builder
209                 String templateName = null;
210 
211                 if (actionSuccessFlag == true)
212                 {
213                     templateName = builder.getTemplate();
214                 } 
215                 else
216                 {
217                     templateName = builder.getErrorTemplate();
218                 }
219 
220                 // Get a reader to the velocity template
221                 final InputStream templateStream = this.getClass()
222                         .getClassLoader().getResourceAsStream(templateName);
223 
224                 Reader template = new InputStreamReader(templateStream);
225 
226                 // The results of the velocity template will be stored here
227                 StringWriter stringWriter = new StringWriter();
228 
229                 // Run the velocity template
230                 velocityEngine.evaluate(context, stringWriter,
231                         AJAX_PROCESSOR, template);
232 
233                 // Get the results from the velocity processing
234                 String buffer = stringWriter.getBuffer().toString();
235 
236                 //log.debug("output from AjaxService:" + buffer);
237 
238                 // Put the response XML on the response object
239                 HttpServletResponse response = requestContext.getResponse();
240                 ServletOutputStream sos = response.getOutputStream();
241                 
242                 Writer writer = new OutputStreamWriter(sos, "UTF-8");
243                 writer.write(buffer);
244                 writer.flush();
245             } 
246             else
247             {
248                 log.error("could not create builder context");
249                 buildError(requestContext);
250             }
251         } catch (Exception e)
252         {
253             log.error("builder failed", e);
254             inputMap.put(Constants.REASON, e.toString());
255             
256             buildError(requestContext);
257         }
258     }
259 
260     // This is the last chance to handle an error to send back to the client
261     // Send back a generic response.  Subclasses may want to override this 
262     // method
263     protected void buildError(RequestContext requestContext)
264     {
265         try
266         {
267             requestContext.getResponse().getOutputStream().print(DEFAULT_ERROR);
268         } 
269         catch (IOException e)
270         {
271             // Not much can be done here, an exception while handling an exception
272             log.error("exception while trying to build an error message", e);
273         }
274     }
275     
276     /***
277      * @return Returns the objects.
278      */
279     public Map getActionMap()
280     {
281         return objects;
282     }
283 
284 }