View Javadoc

1   /*
2    * Copyright 2001,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.scaffold.sql;
18  
19  
20  import java.sql.SQLException;
21  import java.sql.Timestamp;
22  import java.util.Collection;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Properties;
27  
28  import org.apache.commons.beanutils.BeanUtils;
29  import org.apache.commons.scaffold.lang.ParameterException;
30  import org.apache.commons.scaffold.lang.PopulateException;
31  import org.apache.commons.scaffold.lang.PropertiesException;
32  import org.apache.commons.scaffold.lang.ResourceException;
33  import org.apache.commons.scaffold.lang.Tokens;
34  import org.apache.commons.scaffold.lucene.Engine;
35  import org.apache.commons.scaffold.lucene.SearchUtils;
36  import org.apache.commons.scaffold.text.ConvertUtils;
37  import org.apache.commons.scaffold.util.ProcessBeanBase;
38  import org.apache.commons.scaffold.util.ProcessResult;
39  import org.apache.commons.scaffold.util.ProcessResultBase;
40  import org.apache.commons.scaffold.util.StorageBean;
41  
42  // ------------------------------------------------------------------------ 78
43  
44  /**
45   * Implements StorageBean interface using
46   * <code>StatementUtils</code> and <code>ResultSetUtils</code> in
47   * this package. Specialty operations may be added using the same
48   * utilities.
49   * <P>
50   * <B>To use this class, the developer must:</B>
51   * <UL>
52   * <LI>Declare a subclass adding the properties to be stored.
53   * <LI>Provide in a set of standard SQL queries naming the properties
54   * to be stored as columns in the command
55   * <LI>Override <code>getStorageKey</code> to return the property used
56   * as the primary or unique key in the SQL commands.
57   * <LI>Override <code>getParameters</code> to return an array of
58   * properties as needed by object's insert and update SQL commands.
59   * <LI>Override <code>getPrefix</code> to set a unique prefix
60   * for each StorageBean class.
61   * Include the prefix in the resource file, but not in the Java code.
62   * <LI>Provide keys.next and keys.inc SQL commands to obtain a new
63   * primary key or override createKey to use some other method.
64   * </UL>
65   * <P>
66   * <B>To specify a connection pool and load the SQL commands:</B>
67   * <UL>
68   * <LI>Load the <code>ConnectionAdaptor</code> for your database
69   * connection pool
70   * <LI>Load the <code>Properties</code> file with your commands and
71   * pass it to the StorageBeanBase <code>init</code> method.
72   * </UL>
73   * <P>The <code>ConnectionServlet</code> in this package is designed
74   * to load <code>ConnectionAdapters<code> and resource
75   * (<code>Properties</code>) files.
76   * <P>
77   * Regarding the queries:
78   * <UL>
79   * <LI>The SQL commands are given in a standard Properties file
80   * (e.g, user.retrieve=SELECT name,address FROM userTable).
81   * The default runtime location is
82   * [WEB-INF/classes/resources/command.properties].
83   * <LI>See the command.properties.sample file in this package
84   * for a starter file.
85   * <LI>All the subclass share the same Properties file.
86   * The prefix property lets you use the same standard query names for
87   * more than one StorageBean.
88   * <LI>The standard queries are insert, update, retrieve, delete, and
89   * recycle, and restore.
90   * <LI>The insert and update commands should use same columns in same
91   * order.
92   * <LI>The retrieve, delete, recycle, and restore command should use
93   * the storageKey property for selection.
94   * <LI>Hint: If columns cannot follow property naming conventions,
95   * rename column in command
96   * <LI>Hint: You can use the same object for more than one table or
97   * resultset if you like
98   * </UL>
99   *
100  * @todo Refactor recycle,restore,delete to use common util method
101  * @todo Javadocs for new classes
102  * @todo Use reflection to read parameter key and make array from
103  * that.
104  * @todo Check for key.paramlist then paramlist.
105  * @todo Permit each subclass to have its own properties (collection
106  * of properties, like Struts Actions).
107  * @todo Switch to CommandStore (or eliminate that object if not
108  * needed).
109  * @todo Add properties to manage optimistic locking
110  * @todo Change from BeanUtil.populate to copyProperties in 1.1
111  * version.
112  * @author Ted Husted
113  * @author OK State DEQ
114  * @version $Revision: 155464 $ $Date: 2005-02-26 13:26:54 +0000 (Sat, 26 Feb 2005) $
115  */
116 public class StorageBeanBase extends ProcessBeanBase implements StorageBean {
117 
118 
119     /**
120      * Convenience method to check for null, empty String.
121      */
122     protected boolean blank(String s) {
123         return ConvertUtils.blank(s);
124     }
125 
126     /**
127      * Convenience method to check for null, empty String,
128      * or "0" String.
129      */
130     protected boolean blankValue(String s) {
131         return ConvertUtils.blankValue(s);
132     }
133 
134     /**
135      * Returns true if null or zero.
136      */
137     public static boolean blank(Number key) {
138       return ConvertUtils.blank(key);
139     }
140 
141 
142 
143 // ------------------------------------------------------------------ Commands
144 // TODO: Refactor this to use a CommandStore
145 
146 
147     /**
148      * [:TODO: Javadoc]
149      */
150     public final static String like(String parameter) {
151         return StatementUtils.like(parameter);
152     }
153 
154 
155     /**
156      * [:TODO: Javadoc]
157      */
158     public final static String DOT = ".";
159 
160 
161     /**
162      * Our command string properties.
163      * Can be loaded from an external properties file at startup.
164      */
165     protected static Properties commands;
166 
167 
168     /**
169      * Retrieve command from <code>commands</code> Properties
170      */
171     protected String lookup(String key)
172         throws PropertiesException {
173 
174         if (null==commands) throw new PropertiesException(
175             PropertiesException.MESSAGE + key);
176 
177         String command = null;
178         String prefix = getPrefix();
179         if (null!=prefix) {
180             StringBuffer sb = new StringBuffer(prefix);
181             sb.append(DOT);
182             sb.append(key);
183             command = sb.toString();
184         }
185         else command = key;
186 
187         return commands.getProperty(command);
188     }
189 
190 
191     /**
192      * Retrieve command from <code>commands</code> Properties
193      */
194     protected String lookupRoot(String key)
195         throws PropertiesException {
196 
197         if (null==commands) throw new PropertiesException(
198             PropertiesException.MESSAGE + key);
199 
200         return commands.getProperty(key);
201 
202     }
203 
204 
205     /**
206      * Set the Properties file to be used for SQL commands.
207      * This can be called by main or in a servlet.init
208      * method at startup. If called more than once, the
209      * new commands are added to the existing store.
210      * If  keys clash,the last one wins.
211      */
212     public static final void init(Properties _commands) {
213 
214         if (null==commands) {
215             commands = _commands;
216         }
217         else {
218             commands.putAll(_commands);
219         }
220     }
221 
222 
223 // ----------------------------------------------------------- Base Properties
224 
225     /**
226      * [:TODO: Javadoc]
227      */
228     public final static String INSERT = "insert";
229 
230 
231     /**
232      * [:TODO: Javadoc]
233      */
234     public final static String UPDATE = "update";
235 
236 
237     /**
238      * [:TODO: Javadoc]
239      */
240     public final static String RETRIEVE = "retrieve";
241 
242 
243     /**
244      * [:TODO: Javadoc] purge
245      */
246     public final static String DELETE = "delete";
247 
248 
249     /**
250      * [:TODO: Javadoc]
251      */
252     public final static String RESTORE = "restore";
253 
254 
255     /**
256      * [:TODO: Javadoc] delete
257      */
258     public final static String RECYCLE = "recycle";
259 
260 
261     /**
262      * [:TODO: Javadoc]
263      */
264     public final static String TABLE = "table";
265 
266 
267     /**
268      * [:TODO: Javadoc]
269      */
270     public final static String PARAMS = DOT + "params";
271 
272 
273     /**
274      * A unique identifier for this StorageBean's
275      * commands.
276      */
277     private String prefix = null;
278 
279 
280      // See interface for Javadoc
281     public String getPrefix() {
282         return prefix;
283     }
284 
285 
286      // See interface for Javadoc
287     public void setPrefix(String prefix) {
288         this.prefix = prefix;
289     }
290 
291 
292     /**
293      * The array of parameters required by the
294      * insert and update commands for this object.
295      */
296     private Object[] parameters = null;
297 
298 
299     /**
300      * Return the runtime parameters for the given command.
301      *
302      * The comma-delimited list of parameter properties can be provided
303      * as a ${command}.params element in the command resource. 
304      * <code>getParameters</code> will automatically retrieve the *.params element 
305      * and use it to create an array of the runtime values for each parameter. 
306      * The sequence of runtime values can then be merged with a prepared 
307      * statement (the command).
308      * Since the parameters are inserted into the command by position, 
309      * the column names and parameter names do not need to match. 
310      * @exception ResourceException if the command resource or command cannot 
311      * be found.
312      */
313     public Object[] getParameters(String command) throws ResourceException {
314         
315         if (null==command) {
316             if ((null==parameters) && (null!=paramList)) {
317                 return paramList.toArray();
318             }       
319             return parameters;
320         }
321         
322         // Lookup parameters for command ( * + .params)        
323         String paramToken = command + PARAMS;        
324         String params = lookup(paramToken);
325 
326         if (null==params) { 
327             throw new PropertiesException(
328                 PropertiesException.MESSAGE + paramToken);
329         } 
330         else {
331             String[] tokens = ConvertUtils.tokensToArray(params,PARAM_SEP);
332             Object[] values = new Object[tokens.length];
333             try {
334                 for (int i=0; i<values.length; i++) {
335                     values[i] = BeanUtils.getProperty(this,tokens[i]);
336                 }
337             }
338             catch (Exception e) {
339                 throw new PropertiesException(
340                     PropertiesException.MESSAGE + params);
341             }
342             
343             return values;
344         }       
345     }
346 
347 
348      // See interface for Javadoc
349     public void setParameters(Object[] parameters) {
350         this.parameters = parameters;
351     }
352 
353 
354     /**
355      * The list of parameters required by the
356      * insert and update commands for this object.
357      */
358     private List paramList = null;
359 
360 
361     /**
362      * Return the vparamList or parameters field. 
363      * If both are null, return an empty list.
364      */
365     private List paramListDefault() throws ResourceException {
366         
367         List list = paramList;
368         if (null==list) { 
369                 // Start with empty list
370             list = new java.util.ArrayList();
371                 // Add array parameters, if any
372             Object[] array = getParameters(null);
373             if (null!=array) {
374                 for (int i =0; i<array.length; i++) {
375                     list.add(array[i]);
376                 }
377             }
378         }
379         return list;
380 
381     }
382 
383 
384     /**
385      * Default separator character for list of parameters [","] (comma).
386      */
387     public static final String PARAM_SEP = ",";
388 
389 
390     /**
391      * Return the runtime parameters for the given command.
392      *
393      * The comma-delimited list of parameter properties can be provided
394      * as a ${command}.params element in the command resource. 
395      * <code>getParameters</code> will automatically retrieve the *.params element 
396      * and use it to create a list of the runtime values for each parameter. 
397      * The sequence of runtime values can then be merged with a prepared 
398      * statement (the command).
399      * Since the parameters are inserted into the command by position, 
400      * the column names and parameter names do not need to match. 
401      * @exception ResourceException if the command resource or command cannot 
402      * be found.
403      */
404     public List getParamList(String command) throws ResourceException {
405         
406         // If no command, return the list or array field
407         if (null==command) return paramListDefault();
408         
409         List values = new java.util.ArrayList();
410         // Lookup parameters for command ( * + .params)        
411         String paramToken = command + PARAMS;        
412         String params = lookup(paramToken);
413         if (null==params) {
414             throw new PropertiesException(
415                 PropertiesException.MESSAGE + paramToken);
416         }
417         else {           
418             // Tokenize into list/iterator (:TODO: Cache?) (:FIXME: List better than array?)
419             Iterator tokens = ConvertUtils.tokensToList(params,PARAM_SEP).iterator();
420             // Get the runtime property for each parameter (token)
421             try {
422                 while (tokens.hasNext()) { 
423                     values.add(BeanUtils.getProperty(this,(String)tokens.next()));
424                 }
425             }
426             catch (Exception e) {
427                 throw new PropertiesException(
428                     PropertiesException.MESSAGE + params);
429             }
430         }
431        return values;
432     }
433 
434 
435      // See interface for Javadoc
436     public void setParamList(List paramList) {
437         this.paramList = paramList;
438     }
439 
440 
441     /**
442      * The marked status of the record.
443      * <p>
444      * Records to be deleted are marked and may be
445      * restored before they are removed from the
446      * database.
447      * The default value is "1" - nominal.
448      */
449     private Short marked = ConvertUtils.SHORT_ONE;
450 
451 
452     /**
453      * Return the marked status.
454      * <p>
455      * @return the marked status
456      */
457     public Short getMarked() {
458         return (this.marked);
459     }
460 
461 
462     /**
463      * Set the marked status.
464      * @param marked The new marked status
465      */
466     public void setMarked(Short marked) {
467         this.marked = marked;
468     }
469 
470 
471 // ------------------------------------------------------------ Public Methods
472 
473 
474         // See interface for JavaDoc
475     public void populate(Map parameters) throws Exception {
476 
477         if (parameters!=null) {
478             BeanUtils.copyProperties(this,parameters);
479         }
480 
481     } // end populate
482 
483 
484 // ------------------------------------------------------------ Create Methods
485 
486 
487         // See interface for JavaDoc
488     public void executeUpdate(String command)
489             throws ResourceException {
490 
491         try {
492 
493              int result = StatementUtils.executeUpdate(
494                  null,lookup(command),getParameters(command));
495 
496         }
497         catch (SQLException e) {
498             throw new ResourceException(e);
499         }
500 
501     } // end executeUpdate
502 
503 
504     public void executeUpdateRoot(String command)
505             throws ResourceException {
506 
507         try {
508 
509              int result = StatementUtils.executeUpdate(
510                  null,lookupRoot(command));
511 
512         }
513         catch (SQLException e) {
514             throw new ResourceException(e);
515         }
516 
517     } // end executeUpdate
518 
519 // ----------------------------------------------------------- Retrieval Methods
520 
521 
522         // Convenience method
523     public int count(
524             String command)
525             throws ResourceException {
526 
527         Integer result = null;
528         try {
529             result = (Integer) StatementUtils.getColumn(
530                 null,
531                 1,
532                 lookup(command)
533             );
534         }
535         catch (SQLException e) {
536             throw new ResourceException(e);
537         }
538         return result.intValue();
539     }
540 
541 
542         // See interface for JavaDoc
543     public int count(
544             String command,
545             Object parameter)
546             throws ResourceException {
547 
548         Integer result = null;
549         try {
550             result = (Integer) StatementUtils.getColumn(
551                 null,
552                 1,
553                 lookup(command),
554                 parameter
555             );
556         }
557         catch (SQLException e) {
558             throw new ResourceException(e);
559         }
560         return result.intValue();
561     }
562 
563 
564 
565         // See interface for JavaDoc
566     public boolean findElement(
567             Object target,
568             String command,
569             Object key) throws ResourceException {
570 
571         boolean found = false;
572 
573         try {
574 
575             found = StatementUtils.getElement(null,target,
576                 lookup(command),key);
577 
578         }
579         catch (SQLException e) {
580             throw new ResourceException(e);
581         }
582 
583         return found;
584 
585    } // end findElement
586 
587 
588     /**
589      * Convenience method that calls
590      * <code>findCollection(Object,String,Object[])</code>
591      * with appropriate parameters.
592      */
593     public Collection findCollection(Object target,
594         String command) throws ResourceException {
595 
596         try {
597 
598             return StatementUtils.getCollection(null,
599                 target,lookup(command));
600 
601         }
602         catch (SQLException e) {
603             throw new ResourceException(e);
604         }
605 
606     } // end findCollection
607 
608 
609     /**
610      * Convenience method that calls
611      * <code>findCollection(Object,String,Object[])</code>
612      * with appropriate parameters.
613      */
614     protected final Collection findCollection(Object target,
615         String command, int parameter) throws ResourceException {
616 
617 
618         try {
619 
620             return StatementUtils.getCollection(null,
621                 target,lookup(command),parameter);
622 
623         }
624         catch (SQLException e) {
625             throw new ResourceException(e);
626         }
627     } // end findCollection
628 
629 
630 
631     /**
632      * Convenience method that calls
633      * <code>findCollection(Object,String,Object[])</code>
634      * with appropriate parameters.
635      */
636     protected final Collection findCollection(Object target,
637         String command, Object parameter) throws ResourceException {
638 
639         try {
640 
641             return StatementUtils.getCollection(null,
642                 target,lookup(command),parameter);
643 
644         }
645         catch (SQLException e) {
646             throw new ResourceException(e);
647         }
648 
649     } // end findCollection
650 
651 
652         // See interface for JavaDoc
653     public Collection findCollection(Object target,
654         String command, Object[] parameters) throws ResourceException {
655 
656         try {
657 
658             return StatementUtils.getCollection(null,
659                 target,lookup(command),parameters);
660 
661         }
662         catch (SQLException e) {
663             throw new ResourceException(e);
664         }
665 
666     } // end findCollection
667 
668 
669         // See interface for JavaDoc
670     public Collection findByProperty(
671             Object target,
672             String property,
673             String value) throws ParameterException, PopulateException,
674             ResourceException {
675 
676          return SearchUtils.getCollection(target,
677             Engine.getHits(
678                 Engine.getQuery(value,property)));
679 
680     } // end findByProperty
681 
682 
683 // ================================================================ UD Methods
684 
685     /**
686      * The storageKey field, if needed.
687      * <p>
688      * Many subclasses may choose to ingore this field and
689      * use the storageKey property as a wrapper around
690      * their own field of a specific type (e.g. 
691      * an Integer primaryKey property).
692      */
693     private Object storageKey = null;
694 
695 
696         // See interface for JavaDoc
697     public Object getStorageKey() {
698         return this.storageKey;
699     }
700 
701 
702         // See interface for JavaDoc
703     public void setStorageKey(Object storageKey) {
704         this.storageKey = storageKey;
705     }
706 
707     /**
708      * The timestamp of the last edit.
709      * <p>
710      * This can be checked against the edited
711      * timestamp on the record in storage
712      * to see if anyone else has alreaded
713      * edited the record.
714      */
715     private Timestamp modified = ConvertUtils.NULL_TIMESTAMP;
716 
717     /**
718      * Return the edited timestamp.
719      * <p>
720      * @return the edited timestamp.
721      */
722     public Timestamp getModified() {
723         return (this.modified);
724     }
725 
726 
727     /**
728      * Set the edited timestamp.
729      * @param edited The new edited timestamp.
730      */
731     public void setModified(Timestamp modified) {
732         this.modified = modified;
733     }
734 
735 
736     /**
737      * Check to see if another modified timetamp
738      * matches the one for this instance.
739      * Returns true if the timestamps match.
740      * @returns true if the timestamps match.
741      * @param edited The new edited timestamp.
742      */
743     public boolean isCurrent(Timestamp modified) {
744         return (modified!=getModified());
745     }
746 
747 
748     /**
749      * [:TODO: Javadoc]
750      */
751     private int resultCode = 0;
752 
753 
754         // See interface for JavaDoc
755     public int getResultCode() {
756         return resultCode;
757     }
758 
759 
760         // See interface for JavaDoc
761     public void setResultCode(int resultCode) {
762         this.resultCode = resultCode;
763     }
764 
765 
766     /**
767      * [:TODO: Javadoc]
768      */
769     private Object result = this;
770 
771 
772         // See interface for JavaDoc
773     public Object getResult() {
774         return result;
775     }
776 
777 
778         // See interface for JavaDoc
779     public void setResult(Object result) {
780         this.result = result;
781     }
782 
783 
784 // --------------------------------------------------------------------- store
785 
786 
787     /**
788      * If timestamp is null (or ConvertUtils.NULL_TIMESTAMP), 
789      * set our modified property to the current time. 
790      * This emulates how MySQL treats the first null Timestamp in a record.
791      */
792    public void fixModified() {   
793        Timestamp modified = getModified();
794        if ((null==modified) || (ConvertUtils.NULL_TIMESTAMP==modified)) {
795            setModified(new Timestamp(System.currentTimeMillis()));
796        }
797    }
798            
799    
800    public int updateResultCode(String command) throws ResourceException {     
801        
802         int result = 0;
803         try {
804 
805             result = StatementUtils.executeUpdate(
806                 null,lookup(command),getParameters(command));
807 
808         }
809         catch (SQLException e) {
810             throw new ResourceException(e);
811         }
812         
813         return result;
814    }
815 
816         // see interface for Javadoc
817     public Object update(Object parameters) throws Exception {
818         
819         populate((Map) parameters);
820         
821         String command = getParameter();
822 
823         fixModified();
824         setResultCode(updateResultCode(command));
825 
826         String message = null;
827         if (isNew()) message = Tokens.DATA_RECORD_INSERTED;
828         else message = Tokens.DATA_RECORD_UPDATED;
829 
830         ProcessResult result = new ProcessResultBase(getResult());
831             result.addMessage(message);
832             result.addMessage(getStorageKey());
833             result.setSingleForm(true);
834         return result;
835     }
836 
837 
838     public ProcessResult elementResult(String command) throws Exception {
839 
840         boolean found = false;
841         try {
842 
843             found = StatementUtils.getElement(null,this,
844                 lookup(command),getParameters(command));
845 
846         }
847         catch (SQLException e) {
848             throw new ResourceException(e);
849         }
850 
851         if (found) {
852 
853             setResult(this);
854             setResultCode(1);
855 
856         }
857         else {
858             setResultCode(0);
859         }
860 
861         ProcessResult result = new ProcessResultBase(this);
862             result.setSingleForm(true);
863          return result;         
864     }
865 
866 
867     /**
868      * Retrieve an element (record) from storage.
869      * The element is retrieved is determined by what command 
870      * has been set to the parameter property.
871      * <P>
872      * This signature is designed for compatibilty with
873      * the Executable interface.
874      */
875     public Object element(Object command) throws Exception {
876 
877         populate((Map) command);
878         
879         return elementResult(getParameter());       
880         
881     }
882 
883 
884     public Object collectionResult(String command) throws Exception {
885         
886         Collection result = null;
887 
888         try {
889 
890             result = StatementUtils.getCollection(null,
891                 this,lookup(command),getParameters(command));
892 
893         }
894         catch (SQLException e) {
895             throw new ResourceException(e);
896         }
897 
898         setResult(result);
899 
900         return new ProcessResultBase(result);
901         
902     }
903 
904         
905     /**
906      * Retrieve a collection of objects from storage.
907      * The objects retrieved is determined by what command 
908      * has been set to the parameter property.
909      * <P>
910      * This signature is designed for compatibilty with
911      * the Executable interface.
912      */
913     public Object collection(Object parameters) throws Exception {
914 
915         populate((Map) parameters);
916         
917         return collectionResult(getParameter());
918 
919     }
920 
921 // --------------------------------------------------------------------- store
922 
923     /**
924      * [:TODO: Javadoc]
925      */
926     public static String KEYS_NEXT = "keys.next";
927 
928 
929     /**
930      * [:TODO: Javadoc]
931      */
932     public static String KEYS_INC = "keys.inc";
933 
934 
935         // See interface for JavaDoc
936     public boolean isNew() {
937         Object key = getStorageKey();
938         return ((null==key) || (blankValue(key.toString())));
939     }
940 
941 
942         // See interface for JavaDoc
943         // @todo Refactor to use fixed-length high/low String as key.
944     public Object createKey(String keyName)
945             throws ResourceException {
946 
947         Object result = null;
948 
949         try {
950 
951             result = StatementUtils.createKey(
952                 null,
953                 1,
954                 lookupRoot(KEYS_NEXT),
955                 lookup(keyName),
956                 lookupRoot(KEYS_INC)
957             );
958         }
959 
960         catch (SQLException e) {
961             throw new ResourceException(e);
962         }
963 
964         return result;
965 
966     } // end createKey()
967 
968 
969         // See interface for JavaDoc
970     public void allocateKey() throws Exception {
971 
972         setStorageKey(createKey(TABLE));
973 
974     }
975 
976 
977         // see interface for Javadoc
978         // INSERT The name of the insert command
979         // UPDATE The name of the delete command
980     public void store() throws Exception {
981 
982         boolean isInsert = isNew();
983         String token = null;
984         if (isInsert) { 
985             token = INSERT; 
986             allocateKey();
987         }
988         else {
989             token = UPDATE;
990         }
991 
992         String command = lookup(token);
993 
994         Timestamp modified = getModified();
995         if ((null==modified) || (ConvertUtils.NULL_TIMESTAMP==modified)) {
996             setModified(new Timestamp(System.currentTimeMillis()));
997         }
998         
999         // :FIXME: Way to get remoteNode
1000 
1001         int result = 0;
1002         try {
1003 
1004             result = StatementUtils.executeUpdate(
1005                 null,command,getParameters(token));
1006 
1007         }
1008         catch (SQLException e) {
1009             throw new ResourceException(e);
1010         }
1011         setResultCode(result);
1012 
1013     }
1014 
1015 
1016     /**
1017      * Commit record to storage.
1018      * If storageKey is null, new storage for this object is created.
1019      * Otherwise, an existing object is updated.
1020      * @return ProcessResult with messages and this object as data
1021      * @exception ResourceException if data access error occurs
1022      @ @parameters The properties to use with operation
1023      *
1024      */
1025     public Object store(Object parameters) throws Exception {
1026 
1027         populate((Map) parameters);
1028 
1029         String message = null;
1030         if (isNew()) message = Tokens.DATA_RECORD_INSERTED;
1031         else message = Tokens.DATA_RECORD_UPDATED;
1032 
1033         store();
1034 
1035         ProcessResult result = new ProcessResultBase(getResult());
1036             result.addMessage(message);
1037             result.addMessage(getStorageKey());
1038             result.setSingleForm(true);
1039         return result;
1040     }
1041 
1042 
1043 // ------------------------------------------------------------------ retrieve
1044 
1045         // see interface for Javadoc
1046     public void retrieve() throws Exception {
1047 
1048         boolean found = findElement(this,RETRIEVE,getStorageKey());
1049 
1050         if (found) {
1051 
1052             setResult(this);
1053             setResultCode(1);
1054 
1055         }
1056         else {
1057             setResultCode(0);
1058         }
1059 
1060     }
1061 
1062         // see interface for Javadoc
1063     public Object retrieve(Object parameters) throws Exception {
1064 
1065         populate((Map) parameters);
1066         Object key = getStorageKey();
1067         if (null==key) {
1068             throw new ParameterException();
1069         }
1070 
1071         retrieve();
1072         int resultCode = getResultCode();
1073 
1074         ProcessResult result = new ProcessResultBase(this);
1075             if (0==resultCode) {
1076                 result.addMessage(Tokens.DATA_ACCESS_EMPTY);
1077             }
1078             else {
1079                 result.addMessage(Tokens.DATA_RECORD_RETRIEVED);
1080             }
1081             result.addMessage(key);
1082             result.setSingleForm(true);
1083 
1084         return result;
1085 
1086     } // end retrieve
1087 
1088 
1089 // -------------------------------------------------------------------- delete
1090 
1091 
1092         // see interface for Javadoc
1093     public void delete() throws Exception {
1094 
1095         retrieve();
1096         int result = getResultCode();
1097 
1098         if (0!=result) {
1099 
1100             try {
1101 
1102                 // Mark as deleted
1103                 result = StatementUtils.executeUpdate(null,
1104                     lookup(DELETE),getStorageKey());
1105             }
1106 
1107             catch (SQLException e) {
1108                 throw new ResourceException(e);
1109             }
1110 
1111             setResultCode(result);
1112         }
1113 
1114     }
1115 
1116 
1117     /**
1118      * [:TODO: Javadoc]
1119      */
1120     public Object delete(Object parameters) throws Exception {
1121 
1122         populate((Map) parameters);
1123         Object key = getStorageKey();
1124         if (null==key) {
1125             throw new ParameterException();
1126         }
1127 
1128         delete();
1129         int resultCode = getResultCode();
1130 
1131         ProcessResult result = new ProcessResultBase(this);
1132            if (0==resultCode) {
1133                 result.addMessage(Tokens.DATA_ACCESS_EMPTY);
1134             }
1135             else {
1136                 result.addMessage(Tokens.DATA_RECORD_DELETED);
1137             }
1138             result.addMessage(key);
1139             result.setSingleForm(true);
1140 
1141         return result;
1142 
1143     } // end delete
1144 
1145 
1146 // -------------------------------------------------------------------- recycle
1147 
1148 
1149         // see interface for Javadoc
1150     public void recycle() throws Exception {
1151 
1152         retrieve();
1153         int result = getResultCode();
1154 
1155         if (0!=result) {
1156 
1157             try {
1158 
1159                 // Mark as recycled
1160                 result = StatementUtils.executeUpdate(null,
1161                     lookup(RECYCLE),getStorageKey());
1162             }
1163 
1164             catch (SQLException e) {
1165                 throw new ResourceException(e);
1166             }
1167 
1168             setResultCode(result);
1169         }
1170 
1171     }
1172 
1173 
1174         // see interface for Javadoc
1175     public Object recycle(Object parameters) throws Exception {
1176 
1177         populate((Map) parameters);
1178         Object key = getStorageKey();
1179         if (null==key) {
1180             throw new ParameterException();
1181         }
1182 
1183         recycle();
1184         int resultCode = getResultCode();
1185 
1186         ProcessResult result = new ProcessResultBase(this);
1187            if (resultCode==0) {
1188                 result.addMessage(Tokens.DATA_ACCESS_EMPTY);
1189             }
1190             else {
1191                 result.addMessage(Tokens.DATA_RECORD_RECYCLED);
1192             }
1193             result.addMessage(key);
1194             result.setSingleForm(true);
1195 
1196         return result;
1197 
1198     } // end recycle
1199 
1200 
1201 // ------------------------------------------------------------------- restore
1202 
1203     /**
1204      * Unmark entry for deletion.
1205      * Returns copy of restored entry in target parameter.
1206      *
1207      * @return 0 if fails
1208      * @exception ResourceException if data access error occurs
1209      * @param target Bean to hold copy of record being unmarked
1210      * @param article Primary key of record to unmark
1211      */
1212     public void restore() throws Exception {
1213 
1214         int result = 0;
1215 
1216         try {
1217 
1218             result = StatementUtils.executeUpdate(null,
1219                 lookup(RESTORE),getStorageKey());
1220 
1221         }
1222         catch (SQLException e) {
1223             throw new ResourceException(e);
1224         }
1225 
1226         setResultCode(result);
1227 
1228     } // end restore
1229 
1230 
1231    /**
1232      * Update indicated entry in data storage.
1233      * Return confirmation message in a ProcessResult object.
1234      *
1235      * @param parameters The map or other object to use with this
1236      * operation
1237      * @throws ParameterException if article not found in parameters
1238      * @throws ResourceException if SQLException or other data exception
1239      */
1240     public Object restore(Object parameters) throws Exception {
1241 
1242         populate((Map) parameters);
1243         Object key = getStorageKey();
1244         if (null==key) {
1245             throw new ParameterException();
1246         }
1247 
1248         restore();
1249         int resultCode = getResultCode();
1250 
1251         ProcessResult result = new ProcessResultBase(this);
1252            if (0==resultCode) {
1253                 result.addMessage(Tokens.DATA_ACCESS_EMPTY);
1254             }
1255             else {
1256                 result.addMessage(Tokens.DATA_RECORD_RESTORED);
1257             }
1258             result.addMessage(key);
1259             result.setSingleForm(true);
1260 
1261         return result;
1262 
1263     } // end restore
1264 
1265 } // end StorageBeanBase
1266