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>© 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 }