View Javadoc

1   /* ===================================================================
2    * Copyright 2002-04 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  package com.sri.emo.dbobj;
11  
12  import com.jcorporate.expresso.core.controller.Controller;
13  import com.jcorporate.expresso.core.controller.ControllerException;
14  import com.jcorporate.expresso.core.controller.Transition;
15  import com.jcorporate.expresso.core.dataobjects.DataObject;
16  import com.jcorporate.expresso.core.dataobjects.DataObjectFactory;
17  import com.jcorporate.expresso.core.dataobjects.jdbc.JDBCDataObject;
18  import com.jcorporate.expresso.core.db.DBConnection;
19  import com.jcorporate.expresso.core.db.DBException;
20  import com.jcorporate.expresso.core.db.exception.DBRecordNotFoundException;
21  import com.jcorporate.expresso.core.dbobj.DBField;
22  import com.jcorporate.expresso.core.dbobj.RowSecuredDBObject;
23  import com.jcorporate.expresso.core.dbobj.ValidValue;
24  import com.jcorporate.expresso.core.misc.ConfigManager;
25  import com.jcorporate.expresso.core.security.filters.AllowedHtmlPlusURLFilter;
26  import com.jcorporate.expresso.core.security.filters.Filter;
27  import com.jcorporate.expresso.core.security.filters.RawFilter;
28  import com.sri.emo.controller.SelectionWizardManager;
29  import com.sri.emo.wizard.expressoimpl.WizardController;
30  import com.sri.emo.wizard.persistence.AdditionalInfo;
31  import com.sri.emo.wizard.selection.management.PromptStepType;
32  
33  import java.util.ArrayList;
34  import java.util.List;
35  
36  /***
37   * Definition of an Emo Wizard.
38   * <p>DataObjects provide the low-level Object Relational
39   * mapping between these objects
40   * and a JDBC backend.</p>
41   */
42  public class WizDefinition extends RowSecuredDBObject implements IViewable {
43  
44  
45  	/***
46  	 * 
47  	 */
48  	private static final long serialVersionUID = 1L;
49  
50  	/***
51       * Public constant for access to field
52       * &quot;Item ID&quot;.
53       */
54      public static final String FLD_ID = "Id";
55  
56      /***
57       * Public constant for access to field
58       * &quot;Name&quot;.
59       */
60      public static final String FLD_NAME = "Name";
61  
62      /***
63       * Public constant for access to field
64       * &quot;wizard class&quot;.
65       */
66      public static final String FLD_WIZARD = "Wizard";
67  
68      /***
69       * Public constant for access to field
70       * &quot;Factory&quot;.
71       */
72      public static final String FLD_FACTORY = "Factory";
73  
74      /***
75       * Public constant for access to field
76       * &quot;Bean&quot;.
77       */
78      public static final String FLD_SUMMARY = "Summary";
79  
80      /***
81       * Table name.
82       */
83      public static final String TABLE_NAME = "EMOWIZDEF";
84  
85      /***
86       * Name of the controller.
87       */
88      public static final String FLD_CONTROLLER = "Controller";
89  
90  
91      /***
92       * The classname of the contorller for adding.
93       */
94      public static final String FLD_EDITOR_CONTROLLER = "EditorController";
95  
96  
97      /***
98       * Field that is the classname of any 1-1 mapping database object
99       * that provides additional information for this wizard.
100      */
101     public static final String FLD_ADDITIONAL_INFO = "AdditionalInfo";
102 
103     /***
104      * The default controller for editing a wizard.
105      */
106     public static final String DEFAULT_EDITING_CONTROLLER = SelectionWizardManager.class.getName();
107 
108 
109     /***
110      * For many scenarios, it is worthwhile to bind an instance of the peer data
111      * object here.  Examples are for add and update items, etc.
112      */
113     private JDBCDataObject peerDataObject = null;
114 
115     /***
116      * Creates an instance of WizDefinition.
117      * before using.
118      *
119      * @throws DBException upon initialization exception.
120      * @see com.jcorporate.expresso.core.dbobj.SecuredDBObject#SecuredDBObject
121      */
122     public WizDefinition() throws DBException {
123     }
124 
125     /***
126      * Creates an instance of WizDefinition that uses
127      * an already grabbed DBConnection object.  This is for use
128      * inside transactions.  <p>Example: <code><pre>
129      * DBConnection oneConnection = DBConnectionPool
130      * .getInstance("default").getConnection();
131      * oneConnection.setAutoCommit(false);
132      * WizDefinition myObj = new WizDefinition(oneConnection);
133      * //Set fields here....
134      * myObj.add();
135      * myObj.clear();
136      * //more set fields
137      * myObj.add();
138      * //Commit the transaction
139      * oneConnection.commit();
140      * oneConnection.release();
141      * </pre></code>
142      * </p>
143      *
144      * @param dbConnection com.jcorporate.expresso.core.db.DBConnection
145      * @param uid          the user id
146      * @throws DBException upon construction error
147      */
148     public WizDefinition(final DBConnection dbConnection, final int uid) throws DBException {
149         super(dbConnection, uid);
150     }
151 
152     /* WizDefinition(DBConnection) */
153 
154 
155     public WizDefinition(final DBConnection connection) throws DBException {
156         super(connection);
157     }
158 
159 
160     /***
161      * Returns the managing contorller
162      *
163      * @return Controller
164      * @throws DBException
165      * @throws ControllerException
166      */
167     public Controller getManagingController() throws DBException, ControllerException {
168         String controllerClassName = this.getField(FLD_EDITOR_CONTROLLER);
169         if (controllerClassName == null || controllerClassName.length() == 0) {
170             controllerClassName = DEFAULT_EDITING_CONTROLLER;
171         }
172 
173         return ConfigManager.getControllerFactory().getController(controllerClassName);
174     }
175 
176 
177     /***
178      * Retrieve the controller to 'run' the wizard.
179      *
180      * @return Controller
181      * @throws DBException
182      * @throws ControllerException
183      */
184     public Controller getRunningController() throws DBException, ControllerException {
185         String controllerClassName = this.getField(FLD_WIZARD);
186         if (controllerClassName == null || controllerClassName.length() == 0) {
187             controllerClassName = DEFAULT_EDITING_CONTROLLER;
188         }
189 
190         return ConfigManager.getControllerFactory().getController(controllerClassName);
191 
192     }
193 
194     /***
195      * Retrieves all page definitions associated with this wizard definition.
196      * <em>Only useful for Selection Wizards</em>
197      *
198      * @return List of WizStep objects.
199      * @throws DBException upon error.
200      * @todo Refactor Selection wizard specifics to a separate table.
201      */
202     public List getPageDefinitions() throws DBException {
203         String id = getId();
204         if (id == null || id.length() == 0) {
205             throw new IllegalStateException("Must call 'setField(FLD_ID)' before calling "
206                     + "getPageDefinitions()");
207         }
208         WizStep steps = new WizStep();
209 
210         steps.setWizId(getId());
211 
212         return (List) steps.searchAndRetrieveList(WizStep.FLD_ORDER);
213     }
214 
215     /***
216      * One time intiialization of all the field types.  Set all specifications
217      * here such as field names, table name, friendly name of the data object,
218      * characterset, etc.
219      *
220      * @throws DBException upon error
221      */
222     protected void setupFields() throws DBException {
223         if (getLogger().isDebugEnabled()) {
224             getLogger().debug("Initializing field members");
225         }
226 
227         super.setupFields();
228         setTargetTable(TABLE_NAME);
229 
230         setDescription("Wizard Definition");
231         setCharset("ISO-8859-1");
232 
233         addField(FLD_ID, "auto-inc", 0, false, "Item ID");
234         addKey(FLD_ID);
235 
236         addField(FLD_NAME, DBField.VARCHAR_TYPE, 254, false, "Name");
237         addField(FLD_WIZARD, DBField.VARCHAR_TYPE, 254, false, "Wizard");
238         addField(FLD_FACTORY, DBField.VARCHAR_TYPE, 254, false, "Factory");
239         addField(FLD_CONTROLLER, DBField.VARCHAR_TYPE, 254, true, "Wizard Controller");
240         addField(FLD_EDITOR_CONTROLLER, DBField.VARCHAR_TYPE, 254, true, "Wizard Editor Controller");
241         addField(FLD_ADDITIONAL_INFO, DBField.VARCHAR_TYPE, 254, true, "Additional Info Database Object Class");
242 
243         addField(FLD_SUMMARY, DBField.TEXT_TYPE, 0, true, "Summary");
244 
245         // field will have URLs rendered as <a> tags
246         DBField fieldMeta = (DBField) getMetaData().getFieldMetadata(FLD_SUMMARY);
247         fieldMeta.setFilterClass(AllowedHtmlPlusURLFilter.class);
248 
249         addDetail(WizStep.class.getName(), FLD_ID,
250                 WizStep.FLD_WIZID);
251 
252         setDefaultValue(FLD_CONTROLLER,
253                 com.sri.emo.wizard.selection.WizardAction.class.getName());
254 
255         setDefaultValue(FLD_WIZARD,
256                 com.sri.emo.wizard.selection.EmoSelectionWizard
257                         .class.getName());
258 
259         setDefaultValue(FLD_FACTORY,
260                 com.sri.emo.wizard.selection.EmoSelectionWizardFactory
261                         .class.getName());
262 
263         addDetail(WizStep.class.getName(),
264                 FLD_ID, WizStep.FLD_WIZID);
265     }
266 
267     /***
268      * Default class for additional information.
269      *
270      * @param newClass Class
271      * @throws DBException
272      */
273     public void setAdditionalInfo(Class newClass) throws DBException {
274         if (newClass == null) {
275             this.setField(FLD_ADDITIONAL_INFO, (String) null);
276         } else {
277             this.setField(FLD_ADDITIONAL_INFO, newClass.getName());
278         }
279     }
280 
281     /***
282      * Retrieves the additional information class.
283      *
284      * @return Class or null if no additional information is set.
285      * @throws DBException
286      */
287     public Class getAdditionalInfoClass() throws DBException {
288         String classValue = this.getField(FLD_ADDITIONAL_INFO);
289         if (classValue == null || classValue.length() == 0) {
290             return null;
291         } else {
292             try {
293                 return Thread.currentThread().getContextClassLoader().loadClass(classValue);
294             } catch (ClassNotFoundException ex) {
295                 throw new DBException("Unable to locate class: " + classValue, ex);
296             }
297         }
298     }
299 
300     /***
301      * Sets a new object to the additional info placeholder.
302      *
303      * @param replacementInfo DataObject
304      */
305     public void setAdditionalInfo(final JDBCDataObject replacementInfo) {
306         peerDataObject = replacementInfo;
307     }
308 
309     /***
310      * Retrieves an additional information object associated with this class.
311      *
312      * @return DataObject or null if there is no info class associated with this
313      *         wizard instance.
314      * @throws DBException               upon general database error.
315      * @throws DBRecordNotFoundException if unable to locate the additional info.
316      */
317     public JDBCDataObject getAdditionalInfo() throws DBException, DBRecordNotFoundException {
318         if (peerDataObject != null) {
319             return peerDataObject;
320         }
321 
322         Class infoClass = getAdditionalInfoClass();
323         if (infoClass == null) {
324             return null;
325         }
326 
327         JDBCDataObject resultObject = (JDBCDataObject) DataObjectFactory.createDataObject(infoClass,
328                 this.getDataContext(),
329                 this.getRequestingUser());
330 
331         if (!this.getStatus().equals(STATUS_NEW) && this.getId() != null) {
332             String idField = ((AdditionalInfo) resultObject).getIdField();
333             resultObject.set(idField, this.getId());
334 
335             if (!resultObject.find()) {
336                 throw new DBRecordNotFoundException("Unable to find additional info class of: " + infoClass.getName()
337                         + " with key of: " + this.getId());
338             }
339 
340         }
341 
342         //We have to save this instance since when we add, we have
343         //to update the peer key as well.
344         this.peerDataObject = resultObject;
345 
346         return peerDataObject;
347     }
348 
349 
350     /***
351      * Override of standard checkfield to verify class name and factory name.
352      *
353      * @param fieldName  the name of the field to check.
354      * @param fieldValue the value of the field to check.
355      * @throws DBException upon validation error.
356      */
357     public synchronized void checkField(final String fieldName,
358                                         final String fieldValue) throws com.jcorporate.expresso.core.db.DBException {
359         if (FLD_WIZARD.equals(fieldName)) {
360             validateClass(fieldName, fieldValue, com.sri.emo.wizard
361                     .Wizard.class);
362         } else if (FLD_FACTORY.equals(fieldName)) {
363             validateClass(fieldName, fieldValue, com.sri.emo.wizard
364                     .WizardFactory.class);
365         } else {
366             super.checkField(fieldName, fieldValue);
367         }
368     }
369 
370     /***
371      * Validates a class by (1) name, and (2) makes sure it derives
372      * from the specified class.
373      *
374      * @param className          the name of the class.
375      * @param requiredSuperclass the required superclass.
376      * @param fieldName          the name of the field we're validating.
377      * @throws DBException upon validation error.
378      */
379     protected void validateClass(final String fieldName,
380                                  final String className,
381                                  final Class requiredSuperclass) throws DBException {
382         if (className == null || className.length() == 0) {
383             throw new DBException("Must have a value set for "
384                     + getMetaData()
385                     .getFieldMetadata(fieldName)
386                     .getDescription());
387         }
388 
389         try {
390             Class c = Class.forName(className,
391                     false,
392                     Thread.currentThread()
393                             .getContextClassLoader());
394             if (!requiredSuperclass.isAssignableFrom(c)) {
395                 throw new DBException("Class " + className +
396                         " must be derived from: "
397                         + requiredSuperclass.toString()
398                         + "for field: "
399                         + getMetaData()
400                         .getFieldMetadata(fieldName)
401                         .getDescription());
402             }
403         } catch (ClassNotFoundException ex) {
404             throw new DBException("Unable to find class: " + className);
405         }
406 
407     }
408 
409     /***
410      * Retrieve a list of pertinent decision steps for the current wizard.
411      *
412      * @return List a list of WizStep instances.
413      * @throws DBException upon error.
414      *                     <em>Only useful for Selection Wizards</em>
415      * @todo Refactor Selection wizard specifics to a separate table.
416      */
417     public List getPertinentDecisionSteps() throws
418             DBException {
419         List allSteps = getPageDefinitions();
420         if (allSteps.size() == 0) {
421             //No steps defined.
422             return new ArrayList();
423         }
424 
425         //Guess at size - 1 because usually the first step is
426         //a welcome screen.
427         ArrayList pertinentSteps = new ArrayList(allSteps.size() - 1);
428         for (int i = 0; i < allSteps.size(); i++) {
429             WizStep oneStep = (WizStep) allSteps.get(i);
430             int stepType = oneStep.getStepType();
431             switch (stepType) {
432                 case PromptStepType.INSTANCE_PICKLIST:
433                 case PromptStepType.CUSTOM_PICKLIST:
434                 case PromptStepType.MODEL_PICKLIST:
435                     ValidValue[] vv = oneStep.getMenuItems();
436                     if (vv != null && vv.length > 0) {
437                         pertinentSteps.add(oneStep);
438                     }
439                     break;
440 
441                 case PromptStepType.INSTRUCTIONS_ONLY:
442 
443                     //Instructions only isn't pertinent
444                     continue;
445 
446                 case PromptStepType.TEXT_ENTRY:
447 
448                     //Text Entry isn't pertinent
449                     continue;
450 
451                 default:
452                     throw new IllegalStateException("InvalidInput Value: " + WizStep.FLD_STEP_TYPE + "=" + stepType);
453             }
454         }
455         return pertinentSteps;
456     }
457 
458 
459     /***
460      * Provide a transition for viewing this object, suitable for creating an
461      * HTTP link.
462      *
463      * @return transtion for viewing, including label for name of object; never null
464      * @throws DBException upon error.
465      */
466     public Transition getViewTrans() throws DBException {
467         Transition trans = new Transition(getWizName(), SelectionWizardManager.class,
468                 SelectionWizardManager.STATE_PROMPT_EDIT);
469         trans.addParam(WizardController.WIZ_PARAMETER_ID, getId());
470         return trans;
471     }
472 
473     /***
474      * Get the Wizard id.
475      *
476      * @return java.lang.String
477      * @throws DBException upon getField error
478      */
479     public String getId() throws DBException {
480         return getField(FLD_ID);
481     }
482 
483     /***
484      * Get the Wizard name.
485      *
486      * @return java.lang.String
487      * @throws DBException upon getField error
488      */
489     public String getWizName() throws DBException {
490         return getField(FLD_NAME);
491     }
492 
493     public void setId(String id) throws DBException {
494         setField(FLD_ID, id);
495     }
496 
497     public String getWizardClass() throws DBException {
498         return getField(FLD_WIZARD);
499     }
500 
501     public String getSummary() throws DBException {
502         return getField(FLD_SUMMARY);
503     }
504 
505     public String getController() throws DBException {
506         return getField(FLD_CONTROLLER);
507     }
508 
509     public void setId(int id) throws DBException {
510         setField(FLD_ID, id);
511     }
512 
513     public String getSummaryRaw() throws DBException {
514         Filter old = setFilterClass(new RawFilter());
515         String raw = getSummary();
516         setFilterClass(old);
517 
518         return raw;
519     }
520 
521 
522     /***
523      * Override of delete that deletes 'additional info' 1-1 mappings as well.
524      *
525      * @throws DBException
526      */
527     public void delete(final boolean deleteDetails) throws DBException {
528         DataObject additionalInfo = null;
529         String errorMessage = null;
530         try {
531             additionalInfo = this.getAdditionalInfo();
532             if (additionalInfo != null) {
533                 additionalInfo.delete();
534 
535             }
536         } catch (DBException ex) {
537             errorMessage = "Error deleting wizard definition additional info." + ((additionalInfo != null)
538                     ? " additional info class: " + additionalInfo.getClass().getName() : "");
539             this.getLogger().error(errorMessage, ex);
540             throw new DBException(errorMessage, ex);
541         }
542 
543         super.delete(deleteDetails);
544     }
545 
546     /***
547      * Adds the record to the defined data source and adds the additional
548      * info dataobjects bound to this object as well.
549      *
550      * @throws DBException upon error.
551      */
552     public void add() throws DBException {
553         //Add me first.
554         super.add();
555 
556         //Add additional info.
557         JDBCDataObject additionalInfo = null;
558         String errorMessage = null;
559         try {
560             additionalInfo = this.getAdditionalInfo();
561             if (additionalInfo != null) {
562                 if (!(additionalInfo instanceof AdditionalInfo)) {
563                     throw new DBException("Class " + additionalInfo.getClass().getName() + " must implement interface "
564                             + AdditionalInfo.class.getName());
565                 }
566 
567                 additionalInfo.set(((AdditionalInfo) additionalInfo).getIdField(), this.get(FLD_ID));
568                 additionalInfo.setConnection(this.getLocalConnection());
569 
570                 additionalInfo.add();
571             }
572         } catch (DBException ex) {
573             errorMessage = "Error adding wizard definition additional info." + ((additionalInfo != null)
574                     ? " additional info class: " + additionalInfo.getClass().getName() : "");
575             this.getLogger().error(errorMessage, ex);
576             throw new DBException(errorMessage, ex);
577         }
578 
579     }
580 
581     /***
582      * Updates this object and any additional info objects bound to this
583      * wizard.
584      *
585      * @throws DBException upon error.
586      */
587     public void update() throws DBException {
588         //Update myself first.
589         super.update();
590 
591         //Update additional info.
592         DataObject additionalInfo = null;
593         String errorMessage = null;
594         try {
595             additionalInfo = this.getAdditionalInfo();
596             if (additionalInfo != null) {
597                 additionalInfo.update();
598             }
599         } catch (DBException ex) {
600             errorMessage = "Error updating wizard definition additional info." + ((additionalInfo != null)
601                     ? " additional info class: " + additionalInfo.getClass().getName() : "");
602             this.getLogger().error(errorMessage, ex);
603             throw new DBException(errorMessage, ex);
604         }
605     }
606 
607     /***
608      * Clears all field data as well as additional info.
609      *
610      * @throws DBException upon error.
611      */
612     public void clear() throws DBException {
613         this.peerDataObject = null;
614         super.clear();
615     }
616     
617     
618     public String toString() {
619     	return "Wizard Definition Id: " + getKey();
620 	}
621     
622 }