FailoverRetrySupport.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.client.failover;
022 
023 import org.apache.qpid.client.AMQConnection;
024 
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027 
028 /**
029  * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified
030  * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due
031  * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request,
032  * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This
033  * automatic retrying is continued until the continuation succeeds, or throws an exception (different to
034  * FailoverException, which is used to signal the failure of the original condition).
035  *
036  <p/>The blocking condition used is that the connection is not currently failing over, and the mutex used is the
037  * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods.
038  * These are used like a lock and condition variable.
039  *
040  <p/>The wrapped operation may throw a FailoverException, this is an exception that can be raised by a
041  {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a
042  * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are
043  * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous
044  * methods that should not be attempted when a fail-over is in progress.
045  *
046  <p/>Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be
047  * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want
048  * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation
049  * until it succeeds. Synchronous methods are usually coordinated with a
050  {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants
051  * to start and throws a FailoverException in response to this.
052  *
053  <p/>Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be
054  * started during fail-over, but be delayed until any current fail-over has completed.
055  *
056  <p/><table id="crc"><caption>CRC Card</caption>
057  <tr><th> Responsibilities <th> Collaborations
058  <tr><td> Provide a continuation synchronized on a fail-over lock and condition.
059  <tr><td> Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception.
060  </table>
061  *
062  * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see
063  *      {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary
064  *      Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation.
065  *      Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type
066  *      to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that.
067  *
068  * @todo InterruptedException not handled well.
069  */
070 public class FailoverRetrySupport<T, E extends Exception> implements FailoverSupport<T, E>
071 {
072     /** Used for debugging. */
073     private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class);
074 
075     /** The protected operation that is to be retried in the event of fail-over. */
076     FailoverProtectedOperation<T, E> operation;
077 
078     /** The connection on which the fail-over protected operation is to be performed. */
079     AMQConnection connection;
080 
081     /**
082      * Creates an automatic retrying fail-over handler for the specified operation.
083      *
084      @param operation The fail-over protected operation to wrap in this handler.
085      */
086     public FailoverRetrySupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
087     {
088         this.operation = operation;
089         this.connection = con;
090     }
091 
092     /**
093      * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats
094      * until the operation throws AMQException or succeeds without being interrupted by fail-over.
095      *
096      @return The result of executing the continuation.
097      *
098      @throws E Any underlying exception is allowed to fall through.
099      */
100     public T execute() throws E
101     {
102         return connection.executeRetrySupport(operation);
103     }
104 }