View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.util.interceptors;
18  
19  import org.aopalliance.intercept.MethodInterceptor;
20  import org.aopalliance.intercept.MethodInvocation;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  /***
25   * Generic aspect that will attempt to replay a method invocation if one of a
26   * set of specified exceptions is thrown from its execution.
27   * 
28   * @author a336317
29   */
30  public class MethodReplayInterceptor implements MethodInterceptor
31  {
32  
33      /*** Log reference */
34      private Log log = LogFactory.getLog(MethodReplayInterceptor.class);
35  
36      /*** Serialization version identifier */
37      private static final long serialVersionUID = -1316279974504594833L;
38  
39      /***
40       * How many times we should attempt to retry the method invocation if it
41       * fails
42       */
43      private int retryCount;
44  
45      /*** How long we should wait before retrying - specified in milliseconds * */
46      private int retryInterval;
47  
48      /***
49       * Object which decides whether or not a method invocation should be
50       * replayed
51       */
52      private TransactionalMethodReplayDecisionMaker replayDecisionMaker;
53  
54      /***
55       * Encloses <code>super.invoke()</code> in a try/catch block, where the
56       * catch block contains additional retry logic.
57       */
58      public Object invoke(MethodInvocation invocation) throws Throwable
59      {
60          // TODO Make this more elegant - this logic can be simpler
61          try
62          {
63              return invocation.proceed();
64          } catch (Exception exp)
65          {
66  
67              // determine whether to retry or just throw the exception back up
68              if (!this.isReplayable(invocation, exp)) { throw exp; }
69  
70              // TODO should this be at level WARN/ERROR?
71              if (log.isDebugEnabled())
72              {
73                  log
74                          .debug("Invocation for method ["
75                                  + invocation.getMethod().toString()
76                                  + "] failed. Will attempt to replay method invocation ["
77                                  + retryCount + "] times with an interval of ["
78                                  + retryInterval + "] milliseconds");
79              }
80  
81              int retryCounter = 1;
82              Exception lastExp = null;
83  
84              while ((retryCounter < retryCount))
85              {
86  
87                  try
88                  {
89                      if (log.isDebugEnabled())
90                      {
91                          log
92                                  .debug("Sleeping for ["
93                                          + retryInterval
94                                          + "] milliseconds before replaying invocation for method ["
95                                          + invocation.getMethod().toString()
96                                          + "].");
97                      }
98                      Thread.sleep(this.retryInterval);
99  
100                     if (log.isDebugEnabled())
101                     {
102                         log.debug("Attempt invocation [" + retryCounter
103                                 + "] for method ["
104                                 + invocation.getMethod().toString() + "].");
105                     }
106                     // returning from a finally block will discard the
107                     // exception
108                     return invocation.proceed();
109                 } catch (Exception exp2)
110                 {
111                     // determine whether to retry or just throw the exception
112                     // back up
113                     if (!this.isReplayable(invocation, exp)) { throw exp; }
114 
115                     if (log.isDebugEnabled())
116                     {
117                         log.debug("Attempt [" + retryCounter
118                                 + "] to replay invocation for method ["
119                                 + invocation.getMethod().toString()
120                                 + "] failed. [" + (retryCount - retryCounter)
121                                 + "] attempts left.");
122                     }
123 
124                     lastExp = exp2;
125                     retryCounter++;
126                 }
127             }
128             if (log.isDebugEnabled())
129             {
130                 log.debug("[" + retryCounter
131                         + "] attempts to replay invocation for method ["
132                         + invocation.getMethod().toString()
133                         + "] failed. Throwing exception ["
134                         + lastExp.getClass().getName() + "]");
135             }
136             throw lastExp;
137         }
138 
139     }
140 
141     public int getRetryCount()
142     {
143         return retryCount;
144     }
145 
146     public void setRetryCount(int retryCount)
147     {
148         this.retryCount = retryCount;
149     }
150 
151     public int getRetryInterval()
152     {
153         return retryInterval;
154     }
155 
156     public void setRetryInterval(int retryInterval)
157     {
158         this.retryInterval = retryInterval;
159     }
160 
161     /***
162      * Determine if we should attempt to replay the method given that the
163      * previous invocation returned the passed exception.
164      * 
165      * @param exp
166      * @return True if we should replay the method.
167      */
168     private boolean isReplayable(MethodInvocation invocation, Exception exp)
169     {
170         return replayDecisionMaker.shouldReplay(invocation, exp);
171     }
172 
173     public void setReplayDecisionMaker(
174             TransactionalMethodReplayDecisionMaker replayDecisionMaker)
175     {
176         this.replayDecisionMaker = replayDecisionMaker;
177     }
178 
179 }