1
2
3
4
5
6
7
8
9
10 package com.sri.common.controller;
11
12 import com.jcorporate.expresso.core.controller.*;
13 import com.jcorporate.expresso.core.dataobjects.DataFieldMetaData;
14 import com.jcorporate.expresso.core.dataobjects.DataObjectMetaData;
15 import com.jcorporate.expresso.core.db.DBException;
16 import com.jcorporate.expresso.core.dbobj.DBField;
17 import com.jcorporate.expresso.core.dbobj.DBObject;
18 import com.jcorporate.expresso.core.dbobj.RowSecuredDBObject;
19 import com.jcorporate.expresso.core.misc.ConfigManager;
20 import com.jcorporate.expresso.core.registry.RequestRegistry;
21 import com.jcorporate.expresso.core.security.User;
22 import com.jcorporate.expresso.core.security.filters.Filter;
23 import com.jcorporate.expresso.core.security.filters.RawFilter;
24 import com.jcorporate.expresso.services.dbobj.Setup;
25 import com.sri.common.taglib.InputTag;
26 import com.sri.emo.controller.PermissionController;
27 import org.apache.struts.config.ActionConfig;
28 import org.apache.struts.config.ForwardConfig;
29 import org.dom4j.Document;
30 import org.dom4j.DocumentHelper;
31 import org.dom4j.Element;
32 import org.dom4j.io.HTMLWriter;
33 import org.dom4j.io.OutputFormat;
34 import org.dom4j.io.XMLWriter;
35
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38 import java.io.IOException;
39 import java.io.StringWriter;
40 import java.util.ArrayList;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44
45 /***
46 * Superclass for all TI controllers, for common behavior like logging.
47 */
48 public abstract class AbstractDBController extends DBController {
49
50 /***
51 * Footer constant.
52 */
53 public static final String FOOTER = "FOOTER";
54
55 /***
56 * Column Constant.
57 */
58 public static final String COLUMN = "COLUMN";
59
60 /***
61 * Row Block Constant.
62 */
63 public static final String ROW_BLOCK = "rowblock";
64
65 /***
66 * Row Constant.
67 */
68 public static final String ROW = "ROW";
69
70 /***
71 * Constant for warning list location.
72 */
73 public static final String WARNING_LIST = "WARNING_LIST";
74
75 /***
76 * Constant for common footer handler.
77 */
78 public static final String COMMON_FOOTER_HANDLER = "commonFooter";
79
80 /***
81 * Constant for common header handler.
82 */
83 public static final String COMMON_HEADER_HANDLER = "commonHeader";
84
85 /***
86 * Limit number of chars that appear in display for a given field.
87 */
88 public static final int MAX_CHARS_OUTPUT = 75;
89
90 /***
91 * Number of lines for a text area.
92 */
93 public static final int SINGLE_TEXTAREA_NUM_LINES = 6;
94
95 /***
96 * Number of lines for multiple text area.
97 */
98 public static final int MULTIPLE_TEXTAREA_NUM_LINES = 4;
99
100 /***
101 * Number of columns for a test area.
102 */
103 public static final int TEXTAREA_NUM_COLS = 40;
104
105 /***
106 * Max Text Area Length constant.
107 */
108 public static final int MAX_TEXTAREA_LENGTH = 4000;
109
110 /***
111 * Delimiter constant.
112 */
113 public static final String DELIMIT = "|";
114
115 /***
116 * Jsp constant.
117 */
118 public static final String EDIT_GROUP_DISPLAY = "EDIT_GROUP_DISPLAY";
119
120 /***
121 * Parameter to indicate 'embedded mode'. Most likely it will just need
122 * to be passed around to the save states. Necessary for 'view'
123 */
124 public static final String EMBEDDED_MODE = "Embedded";
125
126
127 /***
128 * Key where the RequestContainer is stored in the session.
129 */
130 public static final String REQUEST_CONTAINER = "RequestContainer";
131
132 /***
133 * Key where the session container is stored.
134 */
135 public static final String SESSION_CONTAINER = "SessionContainer";
136
137 /***
138 * Populates DBObject with any parameters which match field names.
139 * Will forward any validation errors to failureState
140 *
141 * @param object the DBObject to check
142 * @param requiredFields an array of strings that are required to check
143 * @param failureState the state to transition to on failure
144 * @param request the <code>ExpressoRequest</code> object
145 * @param response the <code>ExpressoResponse</code> object
146 * @return true if NO errors exist
147 * @throws ControllerException upon error.
148 * @throws DBException upon database metadata access/validation error.
149 */
150 public boolean isValidAndPopulated(final DBObject object,
151 final String[] requiredFields, final String failureState,
152 final ExpressoRequest request, final ExpressoResponse response) throws
153 ControllerException,
154 DBException {
155 ErrorCollection errorCollection = new ErrorCollection();
156
157
158
159 populateDBObject(object, request);
160
161
162 try {
163 for (int i = 0;
164 (requiredFields != null) && (i < requiredFields.length);
165 i++) {
166
167 String value = object.getField(requiredFields[i]);
168
169 if ((value == null) || (value.length() == 0)) {
170
171 String friendlyFieldName = null;
172
173 try {
174 friendlyFieldName = object.getMetaData().getDescription(requiredFields[i]);
175 } catch (Throwable e) {
176 if (getLogger().isDebugEnabled()) {
177 getLogger().debug("TODO: Internationalize strings", e);
178 }
179 /***
180 * @todo ignore internationalization string missing errors for now; add later
181 */
182 }
183
184 if ((friendlyFieldName == null) || (friendlyFieldName.length() == 0)) {
185 friendlyFieldName = requiredFields[i];
186 }
187
188 errorCollection.addError("The " + friendlyFieldName
189 + " field is required. Please complete this field.");
190 }
191 }
192 } catch (DBException e) {
193 throw new ControllerException(e);
194 }
195
196 if (errorCollection.size() > 0) {
197 response.saveErrors(errorCollection);
198 getLogger().debug("There were errors. Redirecting to " + failureState);
199
200 response.setFormCache();
201
202 if (failureState != null) {
203 transition(failureState, request, response);
204 }
205 }
206
207 return errorCollection.size() <= 0;
208 }
209
210 /***
211 * fill in any fields found in request
212 */
213 public static void populateDBObject(DBObject obj, ExpressoRequest request) throws DBException {
214 String oneFieldName = null;
215 Map allParams = request.getAllParameters();
216 for (Iterator fieldIterator = obj.getMetaData().getFieldNamesList().iterator(); fieldIterator.hasNext();) {
217 oneFieldName = (String) fieldIterator.next();
218 DataFieldMetaData fieldMetadata = obj.getMetaData().getFieldMetadata(oneFieldName);
219 if (!fieldMetadata.isReadOnly() && !fieldMetadata.isVirtual() && allParams.containsKey(oneFieldName)) {
220 String val = request.getParameter(oneFieldName);
221 if (fieldMetadata.isQuotedTextType()) {
222 val = val.trim();
223 }
224 obj.setField(oneFieldName, val);
225 }
226 }
227 }
228
229 public static void trimAllTextFields(final DBObject object) throws DBException {
230
231 DataObjectMetaData meta = object.getMetaData();
232 Filter old = object.setFilterClass(new RawFilter());
233
234 for (Iterator iterator = meta.getFieldNamesList().iterator(); iterator.hasNext();) {
235 String fname = (String) iterator.next();
236
237 DBField dbfield = (DBField) meta.getFieldMetadata(fname);
238 if (!dbfield.isVirtual() && !object.isEmpty() && dbfield.isQuotedTextType()) {
239 object.set(fname, object.getField(fname).trim());
240 }
241 }
242
243 object.setFilterClass(old);
244 }
245
246 /***
247 * Make sure output strings are not empty for tables, and insure that
248 * anchor tags are completed.
249 *
250 * @param item the current value for the table.
251 * @return Resulting String.
252 */
253 public static String str(String item) {
254 if ((item == null) || (item.length() == 0)) {
255 item = " ";
256 }
257
258 return item;
259 }
260
261 /***
262 * Override to permit any logged in user.
263 *
264 * @param newState The name of the new state that is being requested
265 * @param myRequest the <code>ExpressoRequest</code> object
266 * @return True if the state is permitted for this user, else false
267 * @throws ControllerException if another undefined error takes place while
268 * checking security.
269 */
270 public synchronized boolean stateAllowed(final String newState, final ExpressoRequest myRequest) throws
271 ControllerException {
272 try {
273 return (!User.isUnknownUser(RequestRegistry.getUser().getLoginName()));
274 } catch (DBException ex) {
275 throw new ControllerException("Error querying login name from User object");
276 }
277 }
278
279 /***
280 * Output XML for given document.
281 *
282 * @param document The XML Document (Dom4J)
283 * @param request The ExpressoRequest object.
284 * @throws IOException upon io error.
285 * @throws ControllerException upon error getting to the ServletResponse.
286 */
287 public void outputXML(final Document document, final ExpressoRequest request)
288 throws IOException, ControllerException {
289 if (document == null) {
290 throw new ControllerException("null document");
291 }
292
293 StringWriter sw = new StringWriter();
294
295
296 OutputFormat format = OutputFormat.createPrettyPrint();
297 XMLWriter writer = new XMLWriter(sw, format);
298
299 try {
300 writer.write(document);
301 } catch (IOException e) {
302 throw new ControllerException(e);
303 }
304
305
306 String mime = "text/xml";
307
308 if (request.getParameter("text") != null) {
309 mime = "text/plain";
310 }
311
312 ServletControllerRequest servletRequest = (ServletControllerRequest) request;
313 HttpServletResponse httpResponse = (HttpServletResponse) servletRequest.getServletResponse();
314
315 httpResponse.setContentType(mime);
316 httpResponse.getWriter().print(sw.toString());
317 }
318
319 /***
320 * Get nicely formatted XML.
321 *
322 * @param elem The XML Element to pretty print.
323 * @return formatting String.
324 * @throws IOException upon printing exception.
325 */
326 public static String getPrettyXML(final Element elem) throws IOException {
327 elem.detach();
328
329 Document document = DocumentHelper.createDocument();
330 document.setRootElement(elem);
331
332 OutputFormat outformat = OutputFormat.createPrettyPrint();
333
334
335 StringWriter sw = new StringWriter();
336 HTMLWriter writer = new HTMLWriter(sw, outformat);
337 writer.write(document);
338 writer.flush();
339
340 return "<pre>" + sw.toString().replaceAll("//<", "<") + "</pre>";
341 }
342
343 /***
344 * Template Method, allowing a subclass to into actions before
345 * any state in this controller is performed.
346 * For example, a common footer for web pages
347 * could be created here, so long as the web pages in question,
348 * with the common footer, were all in the same controller which overrides
349 * this method.
350 *
351 * @param nextState the state to be performed
352 * @param request the request object
353 * @param response the response object
354 * @throws ControllerException upon controller error.
355 */
356 protected void postPerform(final State nextState, final ExpressoRequest request,
357 final ExpressoResponse response) throws ControllerException {
358
359 String header = Setup.getValueUnrequired(RequestRegistry.getDataContext(),
360 COMMON_HEADER_HANDLER);
361
362 if (header != null && !isEmbeddedMode(request)) {
363 IHeaderStateHandler handler = (IHeaderStateHandler) ConfigManager.getControllerFactory()
364 .getController(header);
365 handler.runHeaderState((ControllerRequest) request, (ControllerResponse) response);
366 }
367
368
369 String footer = Setup.getValueUnrequired(request.getDataContext(),
370 COMMON_FOOTER_HANDLER);
371
372 if (footer != null && !isEmbeddedMode(request)) {
373 IFooterStateHandler handler = (IFooterStateHandler) ConfigManager.getControllerFactory()
374 .getController(footer);
375 handler.runFooterState((ControllerRequest) request, (ControllerResponse) response);
376 }
377 }
378
379 /***
380 * Checks the controller request for the 'embedded mode' parameter. Embedded
381 * mode does not display any headers and footers and must be passed to other
382 * links and submit buttons.
383 *
384 * @param request ExpressoRequest the request parameters to query.
385 * @return boolean true if the embedded mode parameter is detected.
386 */
387 public static boolean isEmbeddedMode(final ExpressoRequest request) {
388 return (request.getParameter(EMBEDDED_MODE) != null);
389 }
390
391 /***
392 * Adds the 'embedded mode' parameter to a transition.
393 *
394 * @param transitionToModify Transition
395 * @return Transition
396 */
397 public static Transition addEmbeddedParameter(final Transition transitionToModify) {
398 transitionToModify.addParam(EMBEDDED_MODE, "true");
399 return transitionToModify;
400 }
401
402
403 /***
404 * Add list to request scope.
405 *
406 * @param request the ExpressoRequest object.
407 * @return list of warnings.
408 */
409 protected List addWarningList(final ExpressoRequest request) {
410 List list;
411 list = new ArrayList();
412
413 ServletControllerRequest servletRequest = (ServletControllerRequest) request;
414 HttpServletRequest hreq = (HttpServletRequest) servletRequest.getServletRequest();
415 hreq.setAttribute(WARNING_LIST, list);
416
417 return list;
418 }
419
420 /***
421 * Get list from request scope.
422 *
423 * @param request The ExpressoRequest object.
424 * @return list, or null
425 */
426 protected List getWarningList(final ExpressoRequest request) {
427 try {
428
429 return (List) request.getSession().getAttribute(WARNING_LIST);
430 } catch (ControllerException ex) {
431 this.getLogger().error("Error getting warning list from session", ex);
432 throw new IllegalStateException("Error getting warning list from session:" + ex.getMessage()
433 + ". Stack Trace Logged");
434 }
435 }
436
437 /***
438 * Adds warning to request scope, creating list if necessary.
439 *
440 * @param request The ExpressoRequest object.
441 * @param s the warning string.
442 */
443 public void addWarning(final ExpressoRequest request, final String s) {
444 List list = getWarningList(request);
445
446 if (list == null) {
447 list = addWarningList(request);
448 }
449
450 list.add(s);
451 }
452
453 /***
454 * Get a checkbox with the given name, label, default value, and
455 * optionally select it.
456 *
457 * @param name The input's name.
458 * @param label The Input's label.
459 * @param defVal The default value.
460 * @param isSelected true if the checkbox is selected.
461 * @return and input of type checkbox
462 */
463 public static Input getCheckbox(final String name, final String label, final String defVal,
464 final boolean isSelected) {
465 Input checkbox = new Input(name, label);
466 checkbox.setType(InputTag.TYPE_CHECKBOX);
467 checkbox.setDefaultValue(defVal);
468
469 if (isSelected) {
470 checkbox.setAttribute(Input.SELECTED, "Y");
471 }
472
473 return checkbox;
474 }
475
476 /***
477 * Get a text area with the given name and default value.
478 *
479 * @param name Name for the text area
480 * @param defVal Default value for the text area
481 * @param rows Rows for the text area
482 * @param cols Columns for the text area
483 * @return a text area with specified values
484 */
485 public static Input getTextArea(final String name, final String defVal, final int rows, final int cols) {
486 Input textarea = new Input(name);
487 textarea.setType(InputTag.TYPE_TEXTAREA);
488 if (defVal != null) {
489 textarea.setDefaultValue(defVal);
490 }
491 textarea.setLines(rows);
492 textarea.setDisplayLength(cols);
493 textarea.setMaxLength(MAX_TEXTAREA_LENGTH);
494 return textarea;
495 }
496
497 /***
498 * Get a text area with 4 rows and 40 columns.
499 *
500 * @param name Name for the text area
501 * @param defVal Default value for the text area
502 * @return a text area with 4 lines and 40 columns
503 */
504 public static Input getTextArea(final String name, final String defVal) {
505 return getTextArea(name, defVal, MULTIPLE_TEXTAREA_NUM_LINES, TEXTAREA_NUM_COLS);
506 }
507
508 /***
509 * Look up the forward used for 'state' param, suitable for use with setStyle().
510 *
511 * @param forward default forwarding path to use in case none is found
512 * @param state final state for which forward is looked up
513 * @return forward for given state
514 * @see ExpressoResponse#setStyle(java.lang.String)
515 */
516 protected String getActionForwarding(final String forward, final String state) {
517 ActionConfig mapping = ConfigManager.getActionConfig(getClass()
518 .getName(),
519 state);
520
521 String returnValue = forward;
522
523 if (mapping != null) {
524 ForwardConfig actionforward = mapping.findForwardConfig(state);
525
526 if (actionforward != null) {
527 returnValue = actionforward.getPath();
528 }
529 }
530
531 return returnValue;
532 }
533
534 /***
535 * Ensure a non-empty string (for <td columns), and also truncate as specified
536 * WHILE INSURING that an anchor link (<a ) is not cut in half.
537 * (extend max if neccesary)
538 *
539 * @param item The item to parse
540 * @param max ?
541 * @return the modified string.
542 */
543 public String strTrunc(String item, final int max) {
544 if ((item == null) || (item.length() == 0)) {
545 item = " ";
546 } else {
547 int index = item.lastIndexOf("<a");
548
549 if (index != -1 && item.lastIndexOf("</a>", index) == -1) {
550
551 if (max >= item.length()) {
552
553 if (getLogger().isDebugEnabled()) {
554 getLogger().debug("cannot satisfy anchor line for : '" + item
555 + "' ; returning all that we had.");
556 }
557 } else {
558 item = strTrunc(item, max * 2);
559 }
560 }
561 }
562
563 return item;
564 }
565
566 /***
567 * Retrieve the permission transition for the row secured dbobject.
568 *
569 * @param obj RowSecuredDBObject the object to get the transition for.
570 * @return Transition the transition for permissions.
571 */
572 public Transition getPermsTrans(final RowSecuredDBObject obj) {
573 Transition trans = new Transition("permit", PermissionController.class, PermissionController.PROMPT_EDIT_PERMS);
574 trans.addParam(PermissionController.KEY_FIELD_VALUES, obj.getKey());
575 trans.addParam(PermissionController.OBJ_TYPE, obj.getClass().getName());
576 return trans;
577 }
578
579 /***
580 * Template Method, allowing a subclass to do an action after any state
581 * in this controller is performed.
582 * <p>This implementation initializes any request-level services if needed.
583 *
584 * @param nextState the state to be performed
585 * @param request the request object
586 * @param response the response object
587 * @throws ControllerException
588 */
589 protected void prePerform(final State nextState, final ExpressoRequest request,
590 final ExpressoResponse response) throws ControllerException {
591 this.getSchemaInstance();
592 }
593 }