Author: taylor
Date: Fri Feb 10 11:52:07 2006
New Revision: 376821

URL: http://svn.apache.org/viewcvs?rev=376821&view=rev
Log:
transactional interceptors to playback failed DAO DML operations
useful for databases such as Oracle TAF for failover of client connections

Added:
    
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayDecisionMaker.java
    
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayInterceptor.java
    
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/TransactionalMethodReplayDecisionMaker.java

Added: 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayDecisionMaker.java
URL: 
http://svn.apache.org/viewcvs/portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayDecisionMaker.java?rev=376821&view=auto
==============================================================================
--- 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayDecisionMaker.java
 (added)
+++ 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayDecisionMaker.java
 Fri Feb 10 11:52:07 2006
@@ -0,0 +1,22 @@
+package org.apache.jetspeed.util.interceptors;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * A interface which is akin to a <B>gateway</B>
+ * in BPMN notation. Concrete implementations can make a decision
+ * as to whether or not a method invocation should be replayed.
+ * 
+ * @author a336317
+ */
+public interface MethodReplayDecisionMaker {
+       
+       /**
+        * 
+        * @param invocation The MethodInvocation object
+        * @param exception Exception thrown on previous invocation attempt
+        * @return True if we should replay the method, false otherwise
+        */
+       public boolean shouldReplay(MethodInvocation invocation, Exception 
exception);
+       
+}

Added: 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayInterceptor.java
URL: 
http://svn.apache.org/viewcvs/portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayInterceptor.java?rev=376821&view=auto
==============================================================================
--- 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayInterceptor.java
 (added)
+++ 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/MethodReplayInterceptor.java
 Fri Feb 10 11:52:07 2006
@@ -0,0 +1,126 @@
+package org.apache.jetspeed.util.interceptors;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Generic aspect that will attempt to replay a method invocation
+ * if one of a set of specified exceptions is thrown from its execution.
+ * 
+ * @author a336317
+ */
+public class MethodReplayInterceptor implements MethodInterceptor {
+       
+       /** Log reference */
+       private Log log = LogFactory.getLog(MethodReplayInterceptor.class);
+       
+       /** Serialization version identifier */
+       private static final long serialVersionUID = -1316279974504594833L;
+
+       /** How many times we should attempt to retry the method invocation if 
it fails */
+       private int retryCount;
+       
+       /** How long we should wait before retrying - specified in milliseconds 
**/
+       private int retryInterval;
+       
+       /** Object which decides whether or not a method invocation should be 
replayed */
+       private TransactionalMethodReplayDecisionMaker replayDecisionMaker;
+       
+       /**
+        * Encloses <code>super.invoke()</code> in a try/catch block, where the
+        * catch block contains additional retry logic.
+        */
+       public Object invoke(MethodInvocation invocation) throws Throwable {
+               //TODO Make this more elegant - this logic can be simpler
+               try{
+                       return invocation.proceed();
+               }
+               catch(Exception exp){
+                       
+                       //determine whether to retry or just throw the 
exception back up
+                       if(!this.isReplayable(invocation,exp)){
+                               throw exp;
+                       }
+                       
+                       //TODO should this be at level WARN/ERROR?
+                       if(log.isDebugEnabled()){
+                               log.debug("Invocation for method ["+ 
invocation.getMethod().toString() 
+                                               +"] failed. Will attempt to 
replay method invocation ["+ retryCount +"] times with an interval of ["+ 
retryInterval +"] milliseconds");
+                       }
+                       
+                       int retryCounter = 1;
+                       Exception lastExp = null;
+                       
+                       while((retryCounter<retryCount)){
+                               
+                               try{
+                                       if(log.isDebugEnabled()){
+                                               log.debug("Sleeping for ["+ 
retryInterval +"] milliseconds before replaying invocation for method ["+ 
invocation.getMethod().toString() +"].");
+                                       }
+                                       Thread.sleep(this.retryInterval);
+                                       
+                                       if(log.isDebugEnabled()){
+                                               log.debug("Attempt invocation 
["+ retryCounter +"] for method ["+ invocation.getMethod().toString() +"].");
+                                       }
+                                       //returning from a finally block will 
discard the
+                                       //exception
+                                       return invocation.proceed();
+                               }
+                               catch(Exception exp2){
+                                       //determine whether to retry or just 
throw the exception back up
+                                       if(!this.isReplayable(invocation,exp)){
+                                               throw exp;
+                                       }
+                                       
+                                       if(log.isDebugEnabled()){
+                                               log.debug("Attempt ["+ 
retryCounter +"] to replay invocation for method ["+ 
invocation.getMethod().toString() 
+                                                               +"] failed. ["+ 
(retryCount - retryCounter) +"] attempts left.");
+                                       }
+                                       
+                                       lastExp = exp2;
+                                       retryCounter++;
+                               }
+                       }
+                       if(log.isDebugEnabled()){
+                               log.debug("["+ retryCounter +"] attempts to 
replay invocation for method ["+ invocation.getMethod().toString() 
+                                               +"] failed. Throwing exception 
["+ lastExp.getClass().getName() +"]");
+                       }
+                       throw lastExp;
+               }
+               
+       }
+
+       public int getRetryCount() {
+               return retryCount;
+       }
+
+       public void setRetryCount(int retryCount) {
+               this.retryCount = retryCount;
+       }
+
+       public int getRetryInterval() {
+               return retryInterval;
+       }
+
+       public void setRetryInterval(int retryInterval) {
+               this.retryInterval = retryInterval;
+       }
+       
+       /**
+        * Determine if we should attempt to replay the method given that the 
previous
+        * invocation returned the passed exception.
+        * @param exp
+        * @return True if we should replay the method.
+        */
+       private boolean isReplayable(MethodInvocation invocation, Exception 
exp){
+               return replayDecisionMaker.shouldReplay(invocation,exp);
+       }
+
+       public void setReplayDecisionMaker(
+                       TransactionalMethodReplayDecisionMaker 
replayDecisionMaker) {
+               this.replayDecisionMaker = replayDecisionMaker;
+       }
+
+}

Added: 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/TransactionalMethodReplayDecisionMaker.java
URL: 
http://svn.apache.org/viewcvs/portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/TransactionalMethodReplayDecisionMaker.java?rev=376821&view=auto
==============================================================================
--- 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/TransactionalMethodReplayDecisionMaker.java
 (added)
+++ 
portals/jetspeed-2/trunk/components/page-manager/src/java/org/apache/jetspeed/util/interceptors/TransactionalMethodReplayDecisionMaker.java
 Fri Feb 10 11:52:07 2006
@@ -0,0 +1,82 @@
+package org.apache.jetspeed.util.interceptors;
+
+import java.sql.SQLException;
+import java.util.StringTokenizer;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * MethodReplayDecisionMaker intended for use with methods marked as 
transactional,
+ * where the decision to replay the method is based on the content of the 
underlying
+ * exception from the resource.
+ * 
+ * @author a336317
+ */
+public class TransactionalMethodReplayDecisionMaker implements
+               MethodReplayDecisionMaker {
+       
+       private int[] sqlErrorCodes;
+
+       public boolean shouldReplay(MethodInvocation invocation, Exception 
exception) {
+               
+               //TODO This needs to be a lot more elegant than it currently is 
- see Spring code
+               //for exception translators to see what we can do here.
+               
+               //exception must be of type SQLException and have an error code 
value, else we keep
+               //walking down the root cause tree to a maximum depth of 3
+               if(exception.getCause() instanceof SQLException){
+                       SQLException sqlExp = 
(SQLException)exception.getCause();
+                       int errorCode = sqlExp.getErrorCode();
+                       
+                       if(errorCode!=0){
+                               return isErrorCodeListed(errorCode);
+                       }
+               }
+               
+               if(exception.getCause().getCause() instanceof SQLException){
+                       
+                       SQLException sqlExp = 
(SQLException)exception.getCause().getCause();
+                       int errorCode = sqlExp.getErrorCode();
+                       
+                       
+                       if(errorCode!=0){
+                               return isErrorCodeListed(errorCode);
+                       }
+               }
+               
+               if(exception.getCause().getCause().getCause() instanceof 
SQLException){
+                       SQLException sqlExp = 
(SQLException)exception.getCause().getCause().getCause();
+                       int errorCode = sqlExp.getErrorCode();
+                       
+                       
+                       if(errorCode!=0){
+                               return isErrorCodeListed(errorCode);
+                       }
+               }
+               
+               
+               return false;
+       }
+
+
+       public void setSqlErrorCodes(String sqlErrorCodesString) {
+               StringTokenizer tokenizer = new 
StringTokenizer(sqlErrorCodesString,",");
+               
+               this.sqlErrorCodes = new int[tokenizer.countTokens()];
+               
+               for(int i=0;tokenizer.hasMoreTokens();i++){
+                       this.sqlErrorCodes[i] = new 
Integer(tokenizer.nextToken()).intValue();                  
+               }
+       }
+       
+       
+       private boolean isErrorCodeListed(int errorCode){
+               for(int i=0;i<this.sqlErrorCodes.length;i++){
+                       
+                       if(this.sqlErrorCodes[i]==errorCode) return true;
+                       
+               }
+               return false;
+       }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to