001: package org.enhydra.util.chiba;
002:
003: import java.io.File;
004: import java.io.UnsupportedEncodingException;
005: import java.util.Enumeration;
006: import java.util.HashMap;
007: import java.util.Iterator;
008: import java.util.List;
009: import java.util.Map;
010:
011: import javax.servlet.http.HttpServletRequest;
012:
013: import org.apache.commons.fileupload.DiskFileUpload;
014: import org.apache.commons.fileupload.FileItem;
015: import org.apache.commons.fileupload.FileUpload;
016: import org.apache.commons.fileupload.FileUploadException;
017: import org.chiba.adapter.ChibaEvent;
018: import org.chiba.xml.xforms.ChibaBean;
019: import org.chiba.xml.xforms.config.Config;
020: import org.chiba.xml.xforms.events.XFormsEventFactory;
021: import org.chiba.xml.xforms.exception.XFormsException;
022: import org.chiba.xml.xforms.ui.Repeat;
023:
024: import com.lutris.appserver.server.httpPresentation.HttpPresentationComms;
025:
026: /**
027: * Default implementation for handling http servlet requests.
028: *
029: * @author joern turner
030: * @version $Id: HttpRequestHandler.java,v 1.3 2007-10-19 10:05:39 sinisa Exp $
031: */
032: public class HttpRequestHandler {
033: public static final String DATA_PREFIX_PROPERTY = "chiba.web.dataPrefix";
034:
035: public static final String TRIGGER_PREFIX_PROPERTY = "chiba.web.triggerPrefix";
036:
037: public static final String SELECTOR_PREFIX_PROPERTY = "chiba.web.selectorPrefix";
038:
039: public static final String REMOVE_UPLOAD_PREFIX_PROPERTY = "chiba.web.removeUploadPrefix";
040:
041: public static final String DATA_PREFIX_DEFAULT = "d_";
042:
043: public static final String TRIGGER_PREFIX_DEFAULT = "t_";
044:
045: public static final String SELECTOR_PREFIX_DEFAULT = "s_";
046:
047: public static final String REMOVE_UPLOAD_PREFIX_DEFAULT = "ru_";
048:
049: private static final boolean debug = true;
050:
051: private ChibaBean chibaBean;
052:
053: private String dataPrefix;
054:
055: private String selectorPrefix;
056:
057: private String triggerPrefix;
058:
059: private String removeUploadPrefix;
060:
061: private String uploadRoot;
062:
063: public HttpRequestHandler(ChibaBean chibaBean) {
064: this .chibaBean = chibaBean;
065: }
066:
067: /**
068: * executes this handler.
069: *
070: * @throws XFormsException
071: */
072:
073: public void execute(ChibaEvent event) throws XFormsException {
074: //HttpServletRequest request = (HttpServletRequest) this.chibaBean.getContext().get(ServletAdapter.HTTP_SERVLET_REQUEST);
075: HttpPresentationComms comms = (HttpPresentationComms) event
076: .getContextInfo();
077:
078: String contextRoot = comms.request.getHttpServletRequest()
079: .getSession().getServletContext().getRealPath("");
080: if (contextRoot == null) {
081: contextRoot = comms.request.getHttpServletRequest()
082: .getSession().getServletContext().getRealPath(".");
083: }
084:
085: String uploadDir = (String) this .chibaBean.getContext().get(
086: ServletAdapter.HTTP_UPLOAD_DIR);
087:
088: if (uploadDir == null) {
089: uploadDir = "";
090: }
091:
092: this .uploadRoot = new File(contextRoot, uploadDir)
093: .getAbsolutePath();
094:
095: handleRequest(comms);
096: }
097:
098: /**
099: * checks whether we have multipart or urlencoded request and processes it accordingly. After updating
100: * the data, a recalculate, revalidate refresh sequence is fired and the found trigger is executed.
101: *
102: * @param request Servlet request
103: * @throws org.chiba.xml.xforms.exception.XFormsException
104: * todo: implement action block behavior
105: */
106: protected void handleRequest(HttpPresentationComms comms)
107: throws XFormsException {
108: String trigger = null;
109:
110: HttpServletRequest request = comms.request
111: .getHttpServletRequest();
112:
113: // Check that we have a file upload request
114: boolean isMultipart = FileUpload.isMultipartContent(request);
115: if (debug) {
116: System.err.println("request isMultipart: " + isMultipart);
117: System.err.println("base URI: "
118: + this .chibaBean.getBaseURI());
119: System.err.println("user agent: "
120: + request.getHeader("User-Agent"));
121: }
122:
123: if (isMultipart) {
124: trigger = processMultiPartRequest(request, trigger);
125: } else {
126: trigger = processUrlencodedRequest(request, trigger);
127: }
128:
129: // finally activate trigger if any
130: if (trigger != null) {
131: this .chibaBean.dispatch(trigger,
132: XFormsEventFactory.DOM_ACTIVATE);
133: }
134: }
135:
136: /**
137: * @param request Servlet request
138: * @param trigger Trigger control
139: * @return the calculated trigger
140: * @throws XFormsException If an error occurs
141: */
142: protected String processMultiPartRequest(
143: HttpServletRequest request, String trigger)
144: throws XFormsException {
145: DiskFileUpload upload = new DiskFileUpload();
146:
147: String encoding = request.getCharacterEncoding();
148: if (encoding == null) {
149: encoding = "ISO-8859-1";
150: }
151:
152: upload.setRepositoryPath(this .uploadRoot);
153:
154: if (debug) {
155: System.err.println("root dir for uploads: "
156: + this .uploadRoot);
157: }
158:
159: List items;
160: try {
161: items = upload.parseRequest(request);
162: } catch (FileUploadException fue) {
163: throw new XFormsException(fue);
164: }
165:
166: Map formFields = new HashMap();
167: Iterator iter = items.iterator();
168: while (iter.hasNext()) {
169: FileItem item = (FileItem) iter.next();
170: String itemName = item.getName();
171: String fieldName = item.getFieldName();
172: String id = fieldName.substring(Config.getInstance()
173: .getProperty("chiba.web.dataPrefix").length());
174:
175: if (debug) {
176: System.err.println("Multipart item name is: "
177: + itemName + " and fieldname is: " + fieldName
178: + " and id is: " + id);
179: System.err.println("Is formfield: "
180: + item.isFormField());
181: }
182:
183: if (item.isFormField()) {
184:
185: // check for upload-remove action
186: if (fieldName.startsWith(getRemoveUploadPrefix())) {
187: id = fieldName.substring(getRemoveUploadPrefix()
188: .length());
189: // if data is null, file will be removed ...
190: // TODO: remove the file from the disk as well
191: chibaBean.updateControlValue(id, "", "", null);
192: continue;
193: }
194:
195: // It's a field name, it means that we got a non-file
196: // form field. Upload is not required. We must treat it as we
197: // do in processUrlencodedRequest()
198: processMultipartParam(formFields, fieldName, item,
199: encoding);
200: } else {
201:
202: String uniqueFilename = new File(
203: getUniqueParameterName("file"), new File(
204: itemName).getName()).getPath();
205:
206: File savedFile = new File(this .uploadRoot,
207: uniqueFilename);
208:
209: byte[] data = null;
210:
211: data = processMultiPartFile(item, id, savedFile,
212: encoding, data);
213:
214: // if data is null, file will be removed ...
215: // TODO: remove the file from the disk as well
216: chibaBean.updateControlValue(id, item.getContentType(),
217: itemName, data);
218: }
219:
220: // handle regular fields
221: if (formFields.size() > 0) {
222:
223: Iterator it = formFields.keySet().iterator();
224: while (it.hasNext()) {
225:
226: fieldName = (String) it.next();
227: String[] values = (String[]) formFields
228: .get(fieldName);
229:
230: // [1] handle data
231: handleData(fieldName, values);
232:
233: // [2] handle selector
234: handleSelector(fieldName, values[0]);
235:
236: // [3] handle trigger
237: trigger = handleTrigger(trigger, fieldName);
238: }
239: }
240: }
241:
242: return trigger;
243: }
244:
245: protected String processUrlencodedRequest(
246: HttpServletRequest request, String trigger)
247: throws XFormsException {
248: // iterate request parameters
249: Enumeration names = request.getParameterNames();
250: while (names.hasMoreElements()) {
251: String paramName = names.nextElement().toString();
252: String[] values = request.getParameterValues(paramName);
253:
254: if (debug) {
255: System.err.println(this + " parameter-name: "
256: + paramName);
257: for (int i = 0; i < values.length; i++) {
258: System.err.println(this + " value: " + values[i]);
259: }
260: }
261:
262: // [1] handle data
263: handleData(paramName, values);
264:
265: // [2] handle selector
266: handleSelector(paramName, values[0]);
267:
268: // [3] handle trigger
269: trigger = handleTrigger(trigger, paramName);
270: }
271: return trigger;
272: }
273:
274: /**
275: * @param name
276: * @throws XFormsException
277: */
278: protected void handleData(String name, String[] values)
279: throws XFormsException {
280: if (name.startsWith(getDataPrefix())) {
281: String id = name.substring(getDataPrefix().length());
282:
283: // assemble new control value
284: String newValue;
285:
286: if (values.length > 1) {
287: StringBuffer buffer = new StringBuffer(values[0]);
288:
289: for (int i = 1; i < values.length; i++) {
290: buffer.append(" ").append(values[i]);
291: }
292:
293: newValue = trim(buffer.toString());
294: } else {
295: newValue = trim(values[0]);
296: }
297:
298: this .chibaBean.updateControlValue(id, newValue);
299: }
300: }
301:
302: /**
303: * patch to handle linefeed duplication in text areas with some browsers.
304: *
305: * @param value the value where line breaks will be trimmed
306: * @return returns a cleaned up version of the value
307: */
308:
309: protected String trim(String value) {
310: if (value != null && value.length() > 0) {
311: value = value.replaceAll("\r\n", "\r");
312: value = value.trim();
313: }
314: return value;
315: }
316:
317: /**
318: * @param name
319: * @throws XFormsException
320: */
321: protected void handleSelector(String name, String value)
322: throws XFormsException {
323: if (name.startsWith(getSelectorPrefix())) {
324: int separator = value.lastIndexOf(':');
325:
326: String id = value.substring(0, separator);
327: int index = Integer.valueOf(value.substring(separator + 1))
328: .intValue();
329:
330: Repeat repeat = (Repeat) this .chibaBean.lookup(id);
331: repeat.setIndex(index);
332: }
333: }
334:
335: protected String handleTrigger(String trigger, String name) {
336: if ((trigger == null) && name.startsWith(getTriggerPrefix())) {
337: /*String parameter = name;
338: int x = parameter.lastIndexOf(".x");
339: int y = parameter.lastIndexOf(".y");
340:
341: if (x > -1) {
342: parameter = parameter.substring(0, x);
343: }
344:
345: if (y > -1) {
346: parameter = parameter.substring(0, y);
347: }
348: */
349: // keep trigger id
350: trigger = name.substring(getTriggerPrefix().length());
351: }
352: return trigger;
353: }
354:
355: private byte[] processMultiPartFile(FileItem item, String id,
356: File savedFile, String encoding, byte[] data)
357: throws XFormsException {
358: // some data uploaded ...
359: if (item.getSize() > 0) {
360:
361: if (chibaBean.storesExternalData(id)) {
362:
363: // store data to file and create URI
364: try {
365: savedFile.getParentFile().mkdir();
366: item.write(savedFile);
367: } catch (Exception e) {
368: throw new XFormsException(e);
369: }
370: // content is URI in this case
371: try {
372: data = savedFile.toURI().toString().getBytes(
373: encoding);
374: } catch (UnsupportedEncodingException e) {
375: throw new XFormsException(e);
376: }
377:
378: } else {
379: // content is the data
380: data = item.get();
381: }
382: }
383: return data;
384: }
385:
386: private void processMultipartParam(Map formFields,
387: String fieldName, FileItem item, String encoding)
388: throws XFormsException {
389: String values[] = (String[]) formFields.get(fieldName);
390: String formFieldValue = null;
391: try {
392: formFieldValue = item.getString(encoding);
393: } catch (UnsupportedEncodingException e) {
394: throw new XFormsException(e.getMessage(), e);
395: }
396:
397: if (values == null) {
398: formFields.put(fieldName, new String[] { formFieldValue });
399: } else {
400: // not very effective, but not many duplicate values
401: // expected either ...
402: String[] tmp = new String[values.length + 1];
403: System.arraycopy(values, 0, tmp, 0, values.length);
404: tmp[values.length] = formFieldValue;
405: formFields.put(fieldName, tmp);
406: }
407: }
408:
409: /**
410: * returns the prefix which is used to identify trigger parameters.
411: *
412: * @return the prefix which is used to identify trigger parameters
413: */
414: protected final String getTriggerPrefix() {
415: if (this .triggerPrefix == null) {
416: try {
417: this .triggerPrefix = Config.getInstance()
418: .getProperty(TRIGGER_PREFIX_PROPERTY,
419: TRIGGER_PREFIX_DEFAULT);
420: } catch (Exception e) {
421: this .triggerPrefix = TRIGGER_PREFIX_DEFAULT;
422: }
423: }
424:
425: return this .triggerPrefix;
426: }
427:
428: protected final String getDataPrefix() {
429: if (this .dataPrefix == null) {
430: try {
431: this .dataPrefix = Config.getInstance().getProperty(
432: DATA_PREFIX_PROPERTY, DATA_PREFIX_DEFAULT);
433: } catch (Exception e) {
434: this .dataPrefix = DATA_PREFIX_DEFAULT;
435: }
436: }
437:
438: return this .dataPrefix;
439: }
440:
441: protected final String getRemoveUploadPrefix() {
442: if (this .removeUploadPrefix == null) {
443: try {
444: this .removeUploadPrefix = Config.getInstance()
445: .getProperty(REMOVE_UPLOAD_PREFIX_PROPERTY,
446: REMOVE_UPLOAD_PREFIX_DEFAULT);
447: } catch (Exception e) {
448: this .removeUploadPrefix = REMOVE_UPLOAD_PREFIX_DEFAULT;
449: }
450: }
451:
452: return this .removeUploadPrefix;
453: }
454:
455: private String getUniqueParameterName(String prefix) {
456: return prefix
457: + Integer.toHexString((int) (Math.random() * 10000));
458: }
459:
460: /**
461: * returns the configured prefix which identifies 'selector' parameters. These are used to transport
462: * the state of repeat indices via http.
463: *
464: * @return the prefix for selector parameters from the configuration
465: */
466: public final String getSelectorPrefix() {
467: if (this .selectorPrefix == null) {
468: try {
469: this .selectorPrefix = Config.getInstance().getProperty(
470: SELECTOR_PREFIX_PROPERTY,
471: SELECTOR_PREFIX_DEFAULT);
472: } catch (Exception e) {
473: this .selectorPrefix = SELECTOR_PREFIX_DEFAULT;
474: }
475: }
476:
477: return this .selectorPrefix;
478: }
479:
480: /**
481: * Get the value of chibaBean.
482: *
483: * @return the value of chibaBean
484: */
485: public ChibaBean getChibaBean() {
486: return this .chibaBean;
487: }
488:
489: }
490:
491: // end of class
|