001    /*
002     * Copyright 2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.scaffold.sql;
018    
019    
020    import java.sql.SQLException;
021    import java.sql.Timestamp;
022    import java.util.Collection;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Properties;
027    
028    import org.apache.commons.beanutils.BeanUtils;
029    import org.apache.commons.scaffold.lang.ParameterException;
030    import org.apache.commons.scaffold.lang.PopulateException;
031    import org.apache.commons.scaffold.lang.PropertiesException;
032    import org.apache.commons.scaffold.lang.ResourceException;
033    import org.apache.commons.scaffold.lang.Tokens;
034    import org.apache.commons.scaffold.lucene.Engine;
035    import org.apache.commons.scaffold.lucene.SearchUtils;
036    import org.apache.commons.scaffold.text.ConvertUtils;
037    import org.apache.commons.scaffold.util.ProcessBeanBase;
038    import org.apache.commons.scaffold.util.ProcessResult;
039    import org.apache.commons.scaffold.util.ProcessResultBase;
040    import org.apache.commons.scaffold.util.StorageBean;
041    
042    // ------------------------------------------------------------------------ 78
043    
044    /**
045     * Implements StorageBean interface using
046     * <code>StatementUtils</code> and <code>ResultSetUtils</code> in
047     * this package. Specialty operations may be added using the same
048     * utilities.
049     * <P>
050     * <B>To use this class, the developer must:</B>
051     * <UL>
052     * <LI>Declare a subclass adding the properties to be stored.
053     * <LI>Provide in a set of standard SQL queries naming the properties
054     * to be stored as columns in the command
055     * <LI>Override <code>getStorageKey</code> to return the property used
056     * as the primary or unique key in the SQL commands.
057     * <LI>Override <code>getParameters</code> to return an array of
058     * properties as needed by object's insert and update SQL commands.
059     * <LI>Override <code>getPrefix</code> to set a unique prefix
060     * for each StorageBean class.
061     * Include the prefix in the resource file, but not in the Java code.
062     * <LI>Provide keys.next and keys.inc SQL commands to obtain a new
063     * primary key or override createKey to use some other method.
064     * </UL>
065     * <P>
066     * <B>To specify a connection pool and load the SQL commands:</B>
067     * <UL>
068     * <LI>Load the <code>ConnectionAdaptor</code> for your database
069     * connection pool
070     * <LI>Load the <code>Properties</code> file with your commands and
071     * pass it to the StorageBeanBase <code>init</code> method.
072     * </UL>
073     * <P>The <code>ConnectionServlet</code> in this package is designed
074     * to load <code>ConnectionAdapters<code> and resource
075     * (<code>Properties</code>) files.
076     * <P>
077     * Regarding the queries:
078     * <UL>
079     * <LI>The SQL commands are given in a standard Properties file
080     * (e.g, user.retrieve=SELECT name,address FROM userTable).
081     * The default runtime location is
082     * [WEB-INF/classes/resources/command.properties].
083     * <LI>See the command.properties.sample file in this package
084     * for a starter file.
085     * <LI>All the subclass share the same Properties file.
086     * The prefix property lets you use the same standard query names for
087     * more than one StorageBean.
088     * <LI>The standard queries are insert, update, retrieve, delete, and
089     * recycle, and restore.
090     * <LI>The insert and update commands should use same columns in same
091     * order.
092     * <LI>The retrieve, delete, recycle, and restore command should use
093     * the storageKey property for selection.
094     * <LI>Hint: If columns cannot follow property naming conventions,
095     * rename column in command
096     * <LI>Hint: You can use the same object for more than one table or
097     * resultset if you like
098     * </UL>
099     *
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