Home | History | Annotate | Download | only in util
      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> {
     51         Output execute(Input params) throws Exception;
     52     }
     53 
     54     private final List<Strategy<Input, Output>> mChainedStrategies;
     55 
     56     private FallbackStrategies(final Strategy<Input, Output> primaryStrategy) {
     57         mChainedStrategies = new ArrayList<Strategy<Input, Output>>();
     58         mChainedStrategies.add(primaryStrategy);
     59     }
     60 
     61     public static <Input, Output> FallbackStrategies<Input, Output> startWith(
     62             final Strategy<Input, Output> primaryStrategy) {
     63         return new FallbackStrategies<Input, Output>(primaryStrategy);
     64     }
     65 
     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 
     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