001    package com.sptci.prevayler.transaction;
002    
003    import com.sptci.ReflectionUtility;
004    import com.sptci.prevayler.PrevalentException;
005    import org.prevayler.TransactionWithQuery;
006    
007    import java.io.Serializable;
008    import java.lang.reflect.InvocationTargetException;
009    import java.lang.reflect.Method;
010    import java.util.Collection;
011    import java.util.Date;
012    
013    /**
014     * A general purpose transaction used to execute transactional methods on
015     * the prevalent system.
016     *
017     * <p>&copy; Copyright 2008 <a href='http://sptci.com' target='_top'>Sans
018     *   Pareil Technologies, Inc.</a></p>
019     * @author Rakesh Vidyadharan 2008-05-28
020     * @version $Id: Transaction.java 4345 2008-06-30 21:22:03Z rakesh $
021     */
022    public class Transaction<P> implements TransactionWithQuery
023    {
024      private static final long serialVersionUID = 1l;
025    
026      /** The name of the transactional method being invoked. */
027      private final String method;
028    
029      /** The types of the parameters for the transactional method. */
030      private final Class[] types;
031    
032      /** The values for {@link #types} to specify in the method invocation. */
033      private final Object[] values;
034    
035      /**
036       * Create a new instance of the transaction with the specified values.
037       *
038       * @param method The {@link #method} name to use.
039       * @param parameter The parameter for the {@link #method}.
040       */
041      public Transaction( final String method, final Parameter parameter )
042      {
043        this.method = method;
044        types = new Class[2];
045        types[0] = parameter.getType();
046    
047        values = new Object[2];
048        values[0] = parameter.getValue();
049      }
050    
051      /**
052       * Create a new instance of the transaction with the specified values.
053       *
054       * @param method The {@link #method} name to use.
055       * @param parameters The parameters for the {@link #method}.
056       */
057      public Transaction( final String method, final Collection<Parameter> parameters )
058      {
059        this.method = method;
060        types = new Class[ parameters.size() + 1 ];
061        values = new Object[ parameters.size() + 1 ];
062    
063        int index = 0;
064        for ( Parameter parameter : parameters )
065        {
066          types[index] = parameter.getType();
067          values[index++] = parameter.getValue();
068        }
069      }
070    
071      /**
072       * Create a new instance of the transaction with the specified values.
073       *
074       * @param method The {@link #method} name to use.
075       * @param parameters The array of parameters for the {@link #method}.
076       */
077      public Transaction( final String method, final Parameter[] parameters )
078      {
079        this.method = method;
080        types = new Class[ parameters.length + 1 ];
081        values = new Object[ parameters.length + 1 ];
082    
083        int index = 0;
084        for ( Parameter parameter : parameters )
085        {
086          types[index] = parameter.getType();
087          values[index++] = parameter.getValue();
088        }
089      }
090    
091      /**
092       * Implementation of the interface method.  Invokes the {@link #method}
093       * on the prevalent system with the specified {@link #values}.
094       *
095       * @param prevalentSystem The prevalent system on which the transaction
096       *   is to be performed and the query executed.
097       * @param executionTime The timestamp for the transaction.
098       * @return The results of exeecuting the transaction.
099       * @throws PrevalentException Usually thrown when the prevalent system
100       *   traps errors that are checked by the system.
101       * @throws Exception If unknown errors are encountered while executing the
102       *   transaction or query.
103       */
104      @SuppressWarnings( value = "unchecked" )
105      public P executeAndQuery( final Object prevalentSystem,
106          final Date executionTime ) throws Exception
107      {
108        P result;
109    
110        try
111        {
112          types[ types.length - 1 ] = Date.class;
113          values[ values.length - 1 ] = executionTime;
114    
115          final Method m =
116              ReflectionUtility.fetchMethod( prevalentSystem, method, types );
117          result = (P) m.invoke( prevalentSystem, values );
118        }
119        catch ( Exception ex )
120        {
121          if ( ex instanceof InvocationTargetException )
122          {
123            final Throwable cause = ex.getCause();
124            if ( cause instanceof PrevalentException )
125            {
126              throw (PrevalentException) cause;
127            }
128            else
129            {
130              throw new PrevalentException( cause );
131            }
132          }
133    
134          throw ex;
135        }
136    
137        return result;
138      }
139    
140      /**
141       * A mapping object used to capture the class type and instance of a
142       * parameter to a method defined on the prevalent system.
143       */
144      public static class Parameter implements Serializable
145      {
146        private static final long serialVersionUID = 1l;
147    
148        /** The class of the parameter. */
149        private final Class type;
150    
151        /** The instance of the parameter. */
152        private final Object value;
153    
154        /**
155         * Craete a new instance of the parameter with the specified values.
156         *
157         * @param type The {@link #type} to use.
158         * @param value The {@link #value} to use.
159         */
160        public Parameter( final Class type, final Object value )
161        {
162          this.type = type;
163          this.value = value;
164        }
165    
166        /**
167         * Create a new instance using the specified parameter value.  The
168         * method declaration should use the class of the specified value and
169         * not a super-class.
170         *
171         * @param value The {@link #value} to use.  The {@link #type} is set
172         *   from {@link java.lang.Object#getClass} method.
173         */
174        public Parameter( final Object value )
175        {
176          this( value.getClass(), value );
177        }
178    
179        /**
180         * Return the {@link #type} field.
181         *
182         * @return The reference to {@link #type}.
183         */
184        public Class getType()
185        {
186          return type;
187        }
188    
189        /**
190         * Return the {@link #value} field.
191         *
192         * @return The reference to {@link #value}.
193         */
194        public Object getValue()
195        {
196          return value;
197        }
198      }
199    }