/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jetspeed.layout.impl;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.jetspeed.ajax.AJAXException;
import org.apache.jetspeed.ajax.AjaxAction;
import org.apache.jetspeed.ajax.AjaxBuilder;
import org.apache.jetspeed.ajax.AjaxRequestService;
import org.apache.jetspeed.layout.PortletActionSecurityBehavior;
import org.apache.jetspeed.page.PageManager;
import org.apache.jetspeed.request.JetspeedRequestContext;
import org.apache.jetspeed.request.RequestContext;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
/**
*
* @author David Gurney
*
* The purpose of this object is to run several AJAX actions and aggregate the
* results into a single response. This is useful when the client needs to make
* more than one call as the result of a single user action.
*
* The sample request URL is shown below:
*
* http://host:port/ajaxapi?action=multiple&commands=(action;name,value;name,value)(action;name,value)
*
* The constructor accepts a map of the actions that are available to be run.
* The name,value pairs are parameter values needed by the action. The actions
* are run in the order that they are found on the URL string
*
*/
public class MultipleAction extends BasePortletAction implements AjaxAction,
AjaxBuilder, BeanFactoryAware
{
protected static final String ALL_RESULTS = "results";
protected static final String BUILD_RESULTS = "buildresults";
protected static final String MULTIPLE_ACTION_PROCESSOR = "Multiple Action Processor";
protected static final String COMMANDS = "commands";
protected static final String COMMAND_TOKEN = ")";
protected static final String PARAM_TOKEN = ";";
protected static final String VALUE_TOKEN = ",";
protected Map actionMap = null;
protected VelocityEngine m_oVelocityEngine = null;
public MultipleAction(AjaxRequestService requestService, String p_sTemplate,
String p_sErrorTemplate, PageManager p_oPageManager,
PortletActionSecurityBehavior p_oSecurityBehavior,
VelocityEngine p_oVelocityEngine)
{
super(p_sTemplate, p_sErrorTemplate, p_oPageManager,
p_oSecurityBehavior);
actionMap = requestService.getActionMap();
m_oVelocityEngine = p_oVelocityEngine;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException
{
// get the proxied object for this, and put it in the map to avoid circular dep
Object proxy = beanFactory.getBean("AjaxMultipleAction");
actionMap.put("multiple", proxy);
}
public boolean run(RequestContext p_oRequestContext, Map p_oResultMap)
throws AJAXException
{
boolean a_bReturnSuccess = true;
List a_oResultArray = new ArrayList();
p_oResultMap.put(ACTION, "multiple");
p_oResultMap.put(STATUS, "success");
// Get the command string
String a_sCommands = p_oRequestContext.getRequestParameter(COMMANDS);
if (a_sCommands == null || a_sCommands.length() <= 0)
{
buildErrorContext(p_oRequestContext, p_oResultMap);
p_oResultMap.put(STATUS, "failure");
p_oResultMap.put(REASON, "command parameters not found");
throw new AJAXException("command parameters not found");
}
// Tokenize the commands into single commands
StringTokenizer a_oCommandTok = new StringTokenizer(a_sCommands,
COMMAND_TOKEN);
// Process each command
while (a_oCommandTok.hasMoreTokens())
{
// Get the token
String a_sCommand = a_oCommandTok.nextToken();
// Strip off the opening (
a_sCommand = a_sCommand.substring(1);
// Tokenize the single commands into parameters
StringTokenizer a_oParamTok = new StringTokenizer(a_sCommand,
PARAM_TOKEN);
if (a_oParamTok == null || a_oParamTok.hasMoreTokens() == false)
{
buildErrorContext(p_oRequestContext, p_oResultMap);
p_oResultMap.put(STATUS, "failure");
p_oResultMap.put(REASON, "incorrect url request");
throw new AJAXException("incorrect url request");
}
// Get the action - which is the first item in the list
String a_sAction = a_oParamTok.nextToken();
// Lookup the action from the action map
Object a_oActionObject = actionMap.get(a_sAction);
if (a_oActionObject == null
&& !(a_oActionObject instanceof AjaxAction))
{
buildErrorContext(p_oRequestContext, p_oResultMap);
p_oResultMap.put(REASON, "unknown action requested==>"
+ a_sAction);
throw new AJAXException("unknown action requested==>"
+ a_sAction);
}
AjaxAction a_oAction = (AjaxAction) a_oActionObject;
JetspeedRequestContext a_oJetspeedRequestContext = (JetspeedRequestContext) p_oRequestContext;
// Process each parameter for this action
while (a_oParamTok.hasMoreTokens())
{
String a_sName = a_oParamTok.nextToken(VALUE_TOKEN);
// Strip of the leading ; if present
if (a_sName.indexOf(';') >= 0)
{
a_sName = a_sName.substring(1);
}
String a_sValue = a_oParamTok.nextToken();
// Put the parameters on the request context
a_oJetspeedRequestContext.setAttribute(a_sName, a_sValue);
}
// Invoke the action
Map a_oResultMap = new HashMap();
boolean a_bSuccess;
try
{
a_bSuccess = a_oAction.runBatch(a_oJetspeedRequestContext,
a_oResultMap);
} catch (Exception e)
{
// Move the reason into the return map
p_oResultMap.put(REASON, a_oResultMap.get(REASON));
throw new AJAXException(e);
}
// Check for success
if (a_bSuccess)
{
// Invoke the builder for this action if possible
if (a_oAction instanceof AjaxBuilder)
{
processBuilder((AjaxBuilder) a_oAction, a_oResultMap,
p_oRequestContext, a_bSuccess);
}
// Get the build results
String a_sBuildResults = (String) a_oResultMap
.get(BUILD_RESULTS);
// Look for an xml tag and strip it off
int a_iStartIndex = a_sBuildResults.indexOf("<?xml");
if (a_iStartIndex >= 0)
{
// Look for the end of the tag
int a_iEndIndex = a_sBuildResults.indexOf(">",
a_iStartIndex);
if (a_iEndIndex >= 0)
{
String a_sStart = a_sBuildResults.substring(0,
a_iStartIndex);
String a_sEnd = a_sBuildResults.substring(
a_iEndIndex + 1, a_sBuildResults.length());
a_sBuildResults = a_sStart + a_sEnd;
}
}
if (a_sBuildResults != null)
{
// Save the results
a_oResultArray.add(a_sBuildResults);
}
} else
{
// Move the reason into the return map
p_oResultMap.put(REASON, a_oResultMap.get(REASON));
// Exit the loop
a_bReturnSuccess = false;
break;
}
}
// Save the results for later building into the response
p_oResultMap.put(ALL_RESULTS, a_oResultArray);
return a_bReturnSuccess;
}
// Process the builder if provided
protected void processBuilder(AjaxBuilder p_oBuilder, Map p_oInputMap,
RequestContext p_oRequestContext, boolean p_oActionSuccessFlag)
{
try
{
// Ask the builder to construct the context
// Add the input map to the velocity context
boolean result = true;
if (p_oActionSuccessFlag == true)
{
result = p_oBuilder
.buildContext(p_oRequestContext, p_oInputMap);
} else
{
result = p_oBuilder.buildErrorContext(p_oRequestContext,
p_oInputMap);
}
Context a_oContext = new VelocityContext(p_oInputMap);
// Check to see if we have a valid context
if (result)
{
// Get the name of the template from the builder
String a_sTemplateName = null;
if (p_oActionSuccessFlag == true)
{
a_sTemplateName = p_oBuilder.getTemplate();
} else
{
a_sTemplateName = p_oBuilder.getErrorTemplate();
}
// Get a reader to the velocity template
final InputStream a_oTemplateStream = this.getClass()
.getClassLoader().getResourceAsStream(a_sTemplateName);
Reader a_oTemplate = new InputStreamReader(a_oTemplateStream);
// The results of the velocity template will be stored here
StringWriter a_oStringWriter = new StringWriter();
// Run the velocity template
m_oVelocityEngine.evaluate(a_oContext, a_oStringWriter,
MULTIPLE_ACTION_PROCESSOR, a_oTemplate);
// Get the results from the velocity processing
String a_sResults = a_oStringWriter.getBuffer().toString();
// Save the results on the input map
p_oInputMap.put(BUILD_RESULTS, a_sResults);
} else
{
log.error("could not create builder context");
}
} catch (Exception e)
{
log.error("builder failed", e);
p_oInputMap.put(Constants.REASON, e.toString());
}
}
public boolean buildContext(RequestContext p_oRequestContext,
Map p_oInputMap)
{
boolean a_bResults = true;
a_bResults = super.buildContext(p_oRequestContext, p_oInputMap);
return a_bResults;
}
}