# CircuitBreakers.java

    /*
     * Copyright 2009-2010 Rickard Öberg AB
     *
     * Licensed 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.qi4j.library.circuitbreaker;

    import org.qi4j.functional.Specification;
    import org.qi4j.io.Output;
    import org.qi4j.io.Receiver;
    import org.qi4j.io.Sender;

    /**
     * CircuitBreaker helper methods.
     */
    public class CircuitBreakers
    {
       public static <Item, ReceiverThrowable extends Throwable> Output<Item, ReceiverThrowable> withBreaker( final CircuitBreaker breaker, final Output<Item, ReceiverThrowable> output)
       {
          return new Output<Item, ReceiverThrowable>()
          {
             @Override
             public <SenderThrowableType extends Throwable> void receiveFrom(final Sender<? extends Item, SenderThrowableType> sender) throws ReceiverThrowable, SenderThrowableType
             {
                output.receiveFrom( new Sender<Item, SenderThrowableType>()
                {
                   @Override
                   public <ReceiverThrowableType extends Throwable> void sendTo(final Receiver<? super Item, ReceiverThrowableType> receiver) throws ReceiverThrowableType, SenderThrowableType
                   {
                      // Check breaker first
                      if (!breaker.isOn())
                         throw (ReceiverThrowableType) breaker.lastThrowable();

                      sender.sendTo( new Receiver<Item, ReceiverThrowableType>()
                      {
                         @Override
                         public void receive( Item item ) throws ReceiverThrowableType
                         {
                            try
                            {
                               receiver.receive( item );

                               // Notify breaker that it went well
                               breaker.success();
                            } catch (Throwable receiverThrowableType)
                            {
                               // Notify breaker of trouble
                               breaker.throwable( receiverThrowableType );

                               throw (ReceiverThrowableType) receiverThrowableType;
                            }
                         }
                      });
                   }
                });
             }
          };
       }

       /**
        * Allow all throwables that are equal to or subclasses of given list of throwables.
        *
        * @param throwables The Throwable types that are allowed.
        * @return A Specification that specifies the allowed Throwables.
        */
       public static Specification<Throwable> in( final Class<? extends Throwable>... throwables)
       {
          return new Specification<Throwable>()
          {
             @Override
             public boolean satisfiedBy( Throwable item )
             {
                Class<? extends Throwable> throwableClass = item.getClass();
                for (Class<? extends Throwable> throwable : throwables)
                {
                   if (throwable.isAssignableFrom( throwableClass ))
                      return true;
                }
                return false;
             }
          };
       }

       public static Specification<Throwable> rootCause( final Specification<Throwable> specification)
       {
          return new Specification<Throwable>()
          {
             @Override
             public boolean satisfiedBy( Throwable item )
             {
                return specification.satisfiedBy( unwrap(item) );
             }

             private Throwable unwrap(Throwable item)
             {
                if (item.getCause() != null)
                   return item.getCause();
                else
                   return item;
             }
          };
       }
    }