001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.ajax;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.InputStreamReader;
022: import java.io.Reader;
023: import java.io.Writer;
024: import java.io.OutputStreamWriter;
025: import java.io.StringWriter;
026: import java.util.HashMap;
027: import java.util.Map;
028:
029: import javax.servlet.ServletOutputStream;
030: import javax.servlet.http.HttpServletResponse;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.apache.jetspeed.layout.impl.Constants;
035: import org.apache.jetspeed.request.RequestContext;
036: import org.apache.velocity.VelocityContext;
037: import org.apache.velocity.app.VelocityEngine;
038: import org.apache.velocity.context.Context;
039: import org.apache.velocity.tools.generic.EscapeTool;
040:
041: /**
042: *
043: * Provides a generic way to handle a Ajax request/response. Useful for AJAX since
044: * the processing can be broken down into actions and builders
045: */
046: public class AjaxRequestServiceImpl implements AjaxRequestService {
047:
048: protected static final String CONTENT_TYPE = "text/xml";
049:
050: protected static final String AJAX_PROCESSOR = "AJAX processor";
051:
052: protected static final String DEFAULT_ERROR = "<js><status>failure</status><action>unknown</action></js>";
053:
054: // Name of the parameter that will be used to lookup the
055: // command object. Default is action.
056: //
057: // Sample URL:
058: // http://localhost/js?pipeline=layout&action=move
059: //
060: // In this case the parameter "action" is used to find "move"
061: // "move" will be used as the key to lookup the object that will
062: // handle the command
063: protected static final String URL_PARAMETER_NAME = "action";
064:
065: /** Logger */
066: protected Log log = LogFactory.getLog(AjaxRequestServiceImpl.class);
067:
068: // Objects that are available to execution. These objects must
069: // implement either the Action interface or the Builder interface
070: // or both.
071: // If the Action interface is implemented, then the run method is called
072: // If the Build interface is implemented, then the build methods are called
073: protected Map objects;
074:
075: // Used to create the response XML
076: protected VelocityEngine velocityEngine = null;
077:
078: // Parameter on the URL that will be used to lookup the object
079: protected String urlParameterName = URL_PARAMETER_NAME;
080:
081: // Default Action if no action specified
082: protected String defaultAction = "getpage";
083:
084: // Handy Velocity Escape Tool (is threadsafe)
085: protected EscapeTool velocityEscTool = null;
086:
087: // Spring can be used to inject this information
088: public AjaxRequestServiceImpl(Map objects,
089: VelocityEngine velocityEngine) {
090: this .objects = objects;
091: this .velocityEngine = velocityEngine;
092: this .velocityEscTool = new EscapeTool();
093: }
094:
095: // Spring can be used to inject this information
096: public AjaxRequestServiceImpl(Map objects,
097: VelocityEngine velocityEngine, String urlParameterName) {
098: this .objects = objects;
099: this .velocityEngine = velocityEngine;
100: this .urlParameterName = urlParameterName;
101: this .velocityEscTool = new EscapeTool();
102: }
103:
104: // This is the entry point for this service
105: public void process(RequestContext requestContext)
106: throws AJAXException {
107: // Lookup the object that is to be used
108: String objectKey = requestContext
109: .getRequestParameter(urlParameterName);
110: if (objectKey == null) {
111: objectKey = defaultAction;
112: }
113: // Get the object associated with this key
114: Object object = objects.get(objectKey);
115: if (object != null) {
116: Map resultMap = new HashMap();
117:
118: boolean success = true;
119: try {
120: // Check to see if this object implements the action
121: // interface
122: if (object instanceof AjaxAction) {
123: success = processAction((AjaxAction) object,
124: requestContext, resultMap);
125: }
126: } catch (Exception e) {
127: success = false;
128: }
129:
130: try {
131: // Check to see if this object implements the builder
132: // interface
133: if (object instanceof AjaxBuilder) {
134: processBuilder((AjaxBuilder) object, resultMap,
135: requestContext, success);
136: }
137: } catch (Exception e) {
138: // The builder failed, return an error response
139: buildError(requestContext);
140: }
141: } else {
142: // Log an informational message
143: log.debug("could not find the object named:" + objectKey);
144:
145: // Return an error response
146: buildError(requestContext);
147: }
148: }
149:
150: // Process the action if provided
151: protected boolean processAction(AjaxAction action,
152: RequestContext requestContext, Map resultMap)
153: throws Exception {
154: return action.run(requestContext, resultMap);
155: }
156:
157: // Process the builder if provided
158: protected void processBuilder(AjaxBuilder builder, Map inputMap,
159: RequestContext requestContext, boolean actionSuccessFlag) {
160: // Response will always be text/xml
161: String format = requestContext.getRequestParameter("format");
162: if (format == null) {
163: requestContext.getResponse().setContentType(CONTENT_TYPE);
164: } else {
165: if (format.equals("json")) {
166: requestContext.getResponse()
167: .setContentType("text/json");
168: }
169: }
170:
171: try {
172: // Ask the builder to construct the context
173: // Add the input map to the velocity context
174:
175: boolean result = true;
176:
177: if (actionSuccessFlag == true) {
178: result = builder.buildContext(requestContext, inputMap);
179: } else {
180: result = builder.buildErrorContext(requestContext,
181: inputMap);
182: }
183:
184: Context context = new VelocityContext(inputMap);
185: context.put("esc", this .velocityEscTool);
186:
187: // Check to see if we have a valid context
188: if (result) {
189: // Get the name of the template from the builder
190: String templateName = null;
191:
192: if (actionSuccessFlag == true) {
193: templateName = builder.getTemplate();
194: } else {
195: templateName = builder.getErrorTemplate();
196: }
197:
198: // Get a reader to the velocity template
199: final InputStream templateStream = this .getClass()
200: .getClassLoader().getResourceAsStream(
201: templateName);
202:
203: Reader template = new InputStreamReader(templateStream);
204:
205: // The results of the velocity template will be stored here
206: StringWriter stringWriter = new StringWriter();
207:
208: // Run the velocity template
209: velocityEngine.evaluate(context, stringWriter,
210: AJAX_PROCESSOR, template);
211:
212: // Get the results from the velocity processing
213: String buffer = stringWriter.getBuffer().toString();
214:
215: //log.debug("output from AjaxService:" + buffer);
216:
217: // Put the response XML on the response object
218: HttpServletResponse response = requestContext
219: .getResponse();
220: ServletOutputStream sos = response.getOutputStream();
221:
222: Writer writer = new OutputStreamWriter(sos, "UTF-8");
223: writer.write(buffer);
224: writer.flush();
225: } else {
226: log.error("could not create builder context");
227: buildError(requestContext);
228: }
229: } catch (Exception e) {
230: log.error("builder failed", e);
231: inputMap.put(Constants.REASON, e.toString());
232:
233: buildError(requestContext);
234: }
235: }
236:
237: // This is the last chance to handle an error to send back to the client
238: // Send back a generic response. Subclasses may want to override this
239: // method
240: protected void buildError(RequestContext requestContext) {
241: try {
242: requestContext.getResponse().getOutputStream().print(
243: DEFAULT_ERROR);
244: } catch (IOException e) {
245: // Not much can be done here, an exception while handling an exception
246: log.error(
247: "exception while trying to build an error message",
248: e);
249: }
250: }
251:
252: /**
253: * @return Returns the objects.
254: */
255: public Map getActionMap() {
256: return objects;
257: }
258:
259: }
|