/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.lucene.index; import java.io.IOException; /** * A utility for executing 2-phase commit on several objects. * * @see TwoPhaseCommit * @lucene.experimental */ public final class TwoPhaseCommitTool { /** No instance */ private TwoPhaseCommitTool() {} /** * Thrown by {@link TwoPhaseCommitTool#execute(TwoPhaseCommit...)} when an * object fails to prepareCommit(). */ public static class PrepareCommitFailException extends IOException { /** Sole constructor. */ public PrepareCommitFailException(Throwable cause, TwoPhaseCommit obj) { super("prepareCommit() failed on " + obj, cause); } } /** * Thrown by {@link TwoPhaseCommitTool#execute(TwoPhaseCommit...)} when an * object fails to commit(). */ public static class CommitFailException extends IOException { /** Sole constructor. */ public CommitFailException(Throwable cause, TwoPhaseCommit obj) { super("commit() failed on " + obj, cause); } } /** rollback all objects, discarding any exceptions that occur. */ private static void rollback(TwoPhaseCommit... objects) { for (TwoPhaseCommit tpc : objects) { // ignore any exception that occurs during rollback - we want to ensure // all objects are rolled-back. if (tpc != null) { try { tpc.rollback(); } catch (Throwable t) {} } } } /** * Executes a 2-phase commit algorithm by first * {@link TwoPhaseCommit#prepareCommit()} all objects and only if all succeed, * it proceeds with {@link TwoPhaseCommit#commit()}. If any of the objects * fail on either the preparation or actual commit, it terminates and * {@link TwoPhaseCommit#rollback()} all of them. * <p> * <b>NOTE:</b> it may happen that an object fails to commit, after few have * already successfully committed. This tool will still issue a rollback * instruction on them as well, but depending on the implementation, it may * not have any effect. * <p> * <b>NOTE:</b> if any of the objects are {@code null}, this method simply * skips over them. * * @throws PrepareCommitFailException * if any of the objects fail to * {@link TwoPhaseCommit#prepareCommit()} * @throws CommitFailException * if any of the objects fail to {@link TwoPhaseCommit#commit()} */ public static void execute(TwoPhaseCommit... objects) throws PrepareCommitFailException, CommitFailException { TwoPhaseCommit tpc = null; try { // first, all should successfully prepareCommit() for (int i = 0; i < objects.length; i++) { tpc = objects[i]; if (tpc != null) { tpc.prepareCommit(); } } } catch (Throwable t) { // first object that fails results in rollback all of them and // throwing an exception. rollback(objects); throw new PrepareCommitFailException(t, tpc); } // If all successfully prepareCommit(), attempt the actual commit() try { for (int i = 0; i < objects.length; i++) { tpc = objects[i]; if (tpc != null) { tpc.commit(); } } } catch (Throwable t) { // first object that fails results in rollback all of them and // throwing an exception. rollback(objects); throw new CommitFailException(t, tpc); } } }