001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.util.interceptors;
018:
019: import org.aopalliance.intercept.MethodInterceptor;
020: import org.aopalliance.intercept.MethodInvocation;
021: import org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023:
024: /**
025: * Generic aspect that will attempt to replay a method invocation if one of a
026: * set of specified exceptions is thrown from its execution.
027: *
028: * @author a336317
029: */
030: public class MethodReplayInterceptor implements MethodInterceptor {
031:
032: /** Log reference */
033: private Log log = LogFactory.getLog(MethodReplayInterceptor.class);
034:
035: /** Serialization version identifier */
036: private static final long serialVersionUID = -1316279974504594833L;
037:
038: /**
039: * How many times we should attempt to retry the method invocation if it
040: * fails
041: */
042: private int retryCount;
043:
044: /** How long we should wait before retrying - specified in milliseconds * */
045: private int retryInterval;
046:
047: /**
048: * Object which decides whether or not a method invocation should be
049: * replayed
050: */
051: private TransactionalMethodReplayDecisionMaker replayDecisionMaker;
052:
053: /**
054: * Encloses <code>super.invoke()</code> in a try/catch block, where the
055: * catch block contains additional retry logic.
056: */
057: public Object invoke(MethodInvocation invocation) throws Throwable {
058: // TODO Make this more elegant - this logic can be simpler
059: try {
060: return invocation.proceed();
061: } catch (Exception exp) {
062:
063: // determine whether to retry or just throw the exception back up
064: if (!this .isReplayable(invocation, exp)) {
065: throw exp;
066: }
067:
068: // TODO should this be at level WARN/ERROR?
069: if (log.isDebugEnabled()) {
070: log
071: .debug("Invocation for method ["
072: + invocation.getMethod().toString()
073: + "] failed. Will attempt to replay method invocation ["
074: + retryCount
075: + "] times with an interval of ["
076: + retryInterval + "] milliseconds");
077: }
078:
079: int retryCounter = 1;
080: Exception lastExp = null;
081:
082: while ((retryCounter < retryCount)) {
083:
084: try {
085: if (log.isDebugEnabled()) {
086: log
087: .debug("Sleeping for ["
088: + retryInterval
089: + "] milliseconds before replaying invocation for method ["
090: + invocation.getMethod()
091: .toString() + "].");
092: }
093: Thread.sleep(this .retryInterval);
094:
095: if (log.isDebugEnabled()) {
096: log.debug("Attempt invocation [" + retryCounter
097: + "] for method ["
098: + invocation.getMethod().toString()
099: + "].");
100: }
101: // returning from a finally block will discard the
102: // exception
103: return invocation.proceed();
104: } catch (Exception exp2) {
105: // determine whether to retry or just throw the exception
106: // back up
107: if (!this .isReplayable(invocation, exp)) {
108: throw exp;
109: }
110:
111: if (log.isDebugEnabled()) {
112: log.debug("Attempt [" + retryCounter
113: + "] to replay invocation for method ["
114: + invocation.getMethod().toString()
115: + "] failed. ["
116: + (retryCount - retryCounter)
117: + "] attempts left.");
118: }
119:
120: lastExp = exp2;
121: retryCounter++;
122: }
123: }
124: if (log.isDebugEnabled()) {
125: log
126: .debug("["
127: + retryCounter
128: + "] attempts to replay invocation for method ["
129: + invocation.getMethod().toString()
130: + "] failed. Throwing exception ["
131: + lastExp.getClass().getName() + "]");
132: }
133: throw lastExp;
134: }
135:
136: }
137:
138: public int getRetryCount() {
139: return retryCount;
140: }
141:
142: public void setRetryCount(int retryCount) {
143: this .retryCount = retryCount;
144: }
145:
146: public int getRetryInterval() {
147: return retryInterval;
148: }
149:
150: public void setRetryInterval(int retryInterval) {
151: this .retryInterval = retryInterval;
152: }
153:
154: /**
155: * Determine if we should attempt to replay the method given that the
156: * previous invocation returned the passed exception.
157: *
158: * @param exp
159: * @return True if we should replay the method.
160: */
161: private boolean isReplayable(MethodInvocation invocation,
162: Exception exp) {
163: return replayDecisionMaker.shouldReplay(invocation, exp);
164: }
165:
166: public void setReplayDecisionMaker(
167: TransactionalMethodReplayDecisionMaker replayDecisionMaker) {
168: this.replayDecisionMaker = replayDecisionMaker;
169: }
170:
171: }
|