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.layout.impl;
18  
19  import java.io.InputStream;
20  import java.io.InputStreamReader;
21  import java.io.Reader;
22  import java.io.StringWriter;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.StringTokenizer;
28  
29  import org.apache.jetspeed.ajax.AJAXException;
30  import org.apache.jetspeed.ajax.AjaxAction;
31  import org.apache.jetspeed.ajax.AjaxBuilder;
32  import org.apache.jetspeed.ajax.AjaxRequestService;
33  import org.apache.jetspeed.layout.PortletActionSecurityBehavior;
34  import org.apache.jetspeed.page.PageManager;
35  import org.apache.jetspeed.request.JetspeedRequestContext;
36  import org.apache.jetspeed.request.RequestContext;
37  import org.apache.velocity.VelocityContext;
38  import org.apache.velocity.app.VelocityEngine;
39  import org.apache.velocity.context.Context;
40  import org.springframework.beans.BeansException;
41  import org.springframework.beans.factory.BeanFactory;
42  import org.springframework.beans.factory.BeanFactoryAware;
43  
44  /***
45   * 
46   * @author David Gurney
47   * 
48   * The purpose of this object is to run several AJAX actions and aggregate the
49   * results into a single response. This is useful when the client needs to make
50   * more than one call as the result of a single user action.
51   * 
52   * The sample request URL is shown below:
53   * 
54   * http://host:port/ajaxapi?action=multiple&commands=(action;name,value;name,value)(action;name,value)
55   * 
56   * The constructor accepts a map of the actions that are available to be run.
57   * The name,value pairs are parameter values needed by the action. The actions
58   * are run in the order that they are found on the URL string
59   * 
60   */
61  public class MultipleAction extends BasePortletAction implements AjaxAction,
62          AjaxBuilder, BeanFactoryAware
63  {
64  
65      protected static final String ALL_RESULTS = "results";
66  
67      protected static final String BUILD_RESULTS = "buildresults";
68  
69      protected static final String MULTIPLE_ACTION_PROCESSOR = "Multiple Action Processor";
70  
71      protected static final String COMMANDS = "commands";
72  
73      protected static final String COMMAND_TOKEN = ")";
74  
75      protected static final String PARAM_TOKEN = ";";
76  
77      protected static final String VALUE_TOKEN = ",";
78  
79      protected Map actionMap = null;
80  
81      protected VelocityEngine m_oVelocityEngine = null;
82  
83      public MultipleAction(AjaxRequestService requestService, String p_sTemplate,
84              String p_sErrorTemplate, PageManager p_oPageManager,
85              PortletActionSecurityBehavior p_oSecurityBehavior,
86              VelocityEngine p_oVelocityEngine)
87      {
88          super(p_sTemplate, p_sErrorTemplate, p_oPageManager,
89                  p_oSecurityBehavior);
90          actionMap = requestService.getActionMap();
91          m_oVelocityEngine = p_oVelocityEngine;
92      }
93  
94      public void setBeanFactory(BeanFactory beanFactory) throws BeansException
95      {
96          // get the proxied object for this, and put it in the map to avoid circular dep
97          Object proxy = beanFactory.getBean("AjaxMultipleAction");
98          actionMap.put("multiple", proxy);        
99      }
100     
101     public boolean run(RequestContext p_oRequestContext, Map p_oResultMap)
102             throws AJAXException
103     {
104         boolean a_bReturnSuccess = true;
105         List a_oResultArray = new ArrayList();
106 
107         p_oResultMap.put(ACTION, "multiple");
108         p_oResultMap.put(STATUS, "success");
109 
110         // Get the command string
111         String a_sCommands = p_oRequestContext.getRequestParameter(COMMANDS);
112         if (a_sCommands == null || a_sCommands.length() <= 0)
113         {
114             buildErrorContext(p_oRequestContext, p_oResultMap);
115             p_oResultMap.put(STATUS, "failure");
116             p_oResultMap.put(REASON, "command parameters not found");
117 
118             throw new AJAXException("command parameters not found");
119         }
120 
121         // Tokenize the commands into single commands
122         StringTokenizer a_oCommandTok = new StringTokenizer(a_sCommands,
123                 COMMAND_TOKEN);
124 
125         // Process each command
126         while (a_oCommandTok.hasMoreTokens())
127         {
128             // Get the token
129             String a_sCommand = a_oCommandTok.nextToken();
130 
131             // Strip off the opening (
132             a_sCommand = a_sCommand.substring(1);
133 
134             // Tokenize the single commands into parameters
135             StringTokenizer a_oParamTok = new StringTokenizer(a_sCommand,
136                     PARAM_TOKEN);
137             if (a_oParamTok == null || a_oParamTok.hasMoreTokens() == false)
138             {
139                 buildErrorContext(p_oRequestContext, p_oResultMap);
140                 p_oResultMap.put(STATUS, "failure");
141                 p_oResultMap.put(REASON, "incorrect url request");
142 
143                 throw new AJAXException("incorrect url request");
144             }
145 
146             // Get the action - which is the first item in the list
147             String a_sAction = a_oParamTok.nextToken();
148 
149             // Lookup the action from the action map
150             Object a_oActionObject = actionMap.get(a_sAction);
151             if (a_oActionObject == null
152                     && !(a_oActionObject instanceof AjaxAction))
153             {
154                 buildErrorContext(p_oRequestContext, p_oResultMap);
155                 p_oResultMap.put(REASON, "unknown action requested==>"
156                         + a_sAction);
157 
158                 throw new AJAXException("unknown action requested==>"
159                         + a_sAction);
160             }
161 
162             AjaxAction a_oAction = (AjaxAction) a_oActionObject;
163 
164             JetspeedRequestContext a_oJetspeedRequestContext = (JetspeedRequestContext) p_oRequestContext;
165 
166             // Process each parameter for this action
167             while (a_oParamTok.hasMoreTokens())
168             {
169                 String a_sName = a_oParamTok.nextToken(VALUE_TOKEN);
170                 // Strip of the leading ; if present
171                 if (a_sName.indexOf(';') >= 0)
172                 {
173                     a_sName = a_sName.substring(1);
174                 }
175 
176                 String a_sValue = a_oParamTok.nextToken();
177 
178                 // Put the parameters on the request context
179                 a_oJetspeedRequestContext.setAttribute(a_sName, a_sValue);
180             }
181 
182             // Invoke the action
183             Map a_oResultMap = new HashMap();
184             boolean a_bSuccess;
185 
186             try
187             {
188                 a_bSuccess = a_oAction.runBatch(a_oJetspeedRequestContext,
189                         a_oResultMap);
190             } catch (Exception e)
191             {
192                 // Move the reason into the return map
193                 p_oResultMap.put(REASON, a_oResultMap.get(REASON));
194 
195                 throw new AJAXException(e);
196             }
197 
198             // Check for success
199             if (a_bSuccess)
200             {
201                 // Invoke the builder for this action if possible
202                 if (a_oAction instanceof AjaxBuilder)
203                 {
204                     processBuilder((AjaxBuilder) a_oAction, a_oResultMap,
205                             p_oRequestContext, a_bSuccess);
206                 }
207 
208                 // Get the build results
209                 String a_sBuildResults = (String) a_oResultMap
210                         .get(BUILD_RESULTS);
211 
212                 // Look for an xml tag and strip it off
213                 int a_iStartIndex = a_sBuildResults.indexOf("<?xml");
214                 if (a_iStartIndex >= 0)
215                 {
216                     // Look for the end of the tag
217                     int a_iEndIndex = a_sBuildResults.indexOf(">",
218                             a_iStartIndex);
219                     if (a_iEndIndex >= 0)
220                     {
221                         String a_sStart = a_sBuildResults.substring(0,
222                                 a_iStartIndex);
223                         String a_sEnd = a_sBuildResults.substring(
224                                 a_iEndIndex + 1, a_sBuildResults.length());
225                         a_sBuildResults = a_sStart + a_sEnd;
226                     }
227                 }
228 
229                 if (a_sBuildResults != null)
230                 {
231                     // Save the results
232                     a_oResultArray.add(a_sBuildResults);
233                 }
234             } else
235             {
236                 // Move the reason into the return map
237                 p_oResultMap.put(REASON, a_oResultMap.get(REASON));
238 
239                 // Exit the loop
240                 a_bReturnSuccess = false;
241                 break;
242             }
243         }
244 
245         // Save the results for later building into the response
246         p_oResultMap.put(ALL_RESULTS, a_oResultArray);
247 
248         return a_bReturnSuccess;
249     }
250 
251     // Process the builder if provided
252     protected void processBuilder(AjaxBuilder p_oBuilder, Map p_oInputMap,
253             RequestContext p_oRequestContext, boolean p_oActionSuccessFlag)
254     {
255         try
256         {
257             // Ask the builder to construct the context
258             // Add the input map to the velocity context
259             boolean result = true;
260 
261             if (p_oActionSuccessFlag == true)
262             {
263                 result = p_oBuilder
264                         .buildContext(p_oRequestContext, p_oInputMap);
265             } else
266             {
267                 result = p_oBuilder.buildErrorContext(p_oRequestContext,
268                         p_oInputMap);
269             }
270 
271             Context a_oContext = new VelocityContext(p_oInputMap);
272 
273             // Check to see if we have a valid context
274             if (result)
275             {
276                 // Get the name of the template from the builder
277                 String a_sTemplateName = null;
278 
279                 if (p_oActionSuccessFlag == true)
280                 {
281                     a_sTemplateName = p_oBuilder.getTemplate();
282                 } else
283                 {
284                     a_sTemplateName = p_oBuilder.getErrorTemplate();
285                 }
286 
287                 // Get a reader to the velocity template
288                 final InputStream a_oTemplateStream = this.getClass()
289                         .getClassLoader().getResourceAsStream(a_sTemplateName);
290 
291                 Reader a_oTemplate = new InputStreamReader(a_oTemplateStream);
292 
293                 // The results of the velocity template will be stored here
294                 StringWriter a_oStringWriter = new StringWriter();
295 
296                 // Run the velocity template
297                 m_oVelocityEngine.evaluate(a_oContext, a_oStringWriter,
298                         MULTIPLE_ACTION_PROCESSOR, a_oTemplate);
299 
300                 // Get the results from the velocity processing
301                 String a_sResults = a_oStringWriter.getBuffer().toString();
302 
303                 // Save the results on the input map
304                 p_oInputMap.put(BUILD_RESULTS, a_sResults);
305             } else
306             {
307                 log.error("could not create builder context");
308             }
309         } catch (Exception e)
310         {
311             log.error("builder failed", e);
312             p_oInputMap.put(Constants.REASON, e.toString());
313         }
314     }
315 
316     public boolean buildContext(RequestContext p_oRequestContext,
317             Map p_oInputMap)
318     {
319         boolean a_bResults = true;
320 
321         a_bResults = super.buildContext(p_oRequestContext, p_oInputMap);
322 
323         return a_bResults;
324     }
325 
326 }