1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.messaging.util; 17 18 import java.util.ArrayList; 19 import java.util.List; 20 21 /** 22 * Provides a generic and loose-coupled framework to execute one primary and multiple fallback 23 * strategies for solving a given task.<p> 24 * Basically, what would have been a nasty try-catch that's hard to separate and maintain: 25 * <pre><code> 26 * try { 27 * // doSomething() that may fail. 28 * } catch (Exception ex) { 29 * try { 30 * // fallback1() that may fail. 31 * } catch (Exception ex2) { 32 * try { 33 * // fallback2() that may fail. 34 * } catch (Exception ex3) { 35 * // ... 36 * } 37 * } 38 * } 39 * </code></pre> 40 * Now becomes:<br> 41 * <pre><code> 42 * FallbackStrategies 43 * .startWith(something) 44 * .thenTry(fallback1) 45 * .thenTry(fallback2) 46 * .execute(); 47 * </code></pre> 48 */ 49 public class FallbackStrategies<Input, Output> { 50 public interface Strategy<Input, Output> { execute(Input params)51 Output execute(Input params) throws Exception; 52 } 53 54 private final List<Strategy<Input, Output>> mChainedStrategies; 55 FallbackStrategies(final Strategy<Input, Output> primaryStrategy)56 private FallbackStrategies(final Strategy<Input, Output> primaryStrategy) { 57 mChainedStrategies = new ArrayList<Strategy<Input, Output>>(); 58 mChainedStrategies.add(primaryStrategy); 59 } 60 startWith( final Strategy<Input, Output> primaryStrategy)61 public static <Input, Output> FallbackStrategies<Input, Output> startWith( 62 final Strategy<Input, Output> primaryStrategy) { 63 return new FallbackStrategies<Input, Output>(primaryStrategy); 64 } 65 thenTry(final Strategy<Input, Output> strategy)66 public FallbackStrategies<Input, Output> thenTry(final Strategy<Input, Output> strategy) { 67 Assert.isFalse(mChainedStrategies.isEmpty()); 68 mChainedStrategies.add(strategy); 69 return this; 70 } 71 execute(final Input params)72 public Output execute(final Input params) { 73 final int count = mChainedStrategies.size(); 74 for (int i = 0; i < count; i++) { 75 final Strategy<Input, Output> strategy = mChainedStrategies.get(i); 76 try { 77 // If succeeds, this will directly return. 78 return strategy.execute(params); 79 } catch (Exception ex) { 80 LogUtil.e(LogUtil.BUGLE_TAG, "Exceptions occured when executing strategy " + 81 strategy + (i < count - 1 ? 82 ", attempting fallback " + mChainedStrategies.get(i + 1) : 83 ", and running out of fallbacks."), ex); 84 // This will fall through and continue with the next strategy (if any). 85 } 86 } 87 // Running out of strategies, return null. 88 // TODO: Should this accept user-defined fallback value other than null? 89 return null; 90 } 91 } 92