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