/************************************************************************* * Copyright 2009-2012 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. * * This file may incorporate work covered under the following copyright * and permission notice: * * Copyright (C) 2009 Google Inc. * * 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 com.eucalyptus.util.async; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RunnableFuture; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import com.eucalyptus.empyrean.Empyrean; import com.eucalyptus.records.Logs; import com.eucalyptus.system.Threads; import com.eucalyptus.util.concurrent.GenericCheckedListenableFuture; import com.eucalyptus.util.concurrent.ListenableFuture; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import static com.eucalyptus.util.Parameters.checkParam; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.not; public class Futures { private static Logger LOG = Logger.getLogger( Futures.class ); enum WaitForResults implements Predicate<Map.Entry<?, Future<?>>> { INSTANCE; @Override public boolean apply( final Map.Entry<?, Future<?>> input ) { try { final Object result = input.getValue( ).get( ); LOG.trace( "Operation succeeded for " + result ); return true; } catch ( final InterruptedException ex ) { Thread.currentThread( ).interrupt( ); } catch ( final Exception ex ) { Logs.extreme( ).trace( ex, ex ); } return false; } } private static <T> Predicate<Map.Entry<T, Future<T>>> waitForResults( ) { Predicate func = WaitForResults.INSTANCE; return ( Predicate<Map.Entry<T, Future<T>>> ) func; } public static <T> Map<T, Future<T>> waitAll( Map<T, Future<T>> futures ) { Predicate<Map.Entry<T, Future<T>>> func = waitForResults( ); Map<T, Future<T>> res = Maps.filterEntries( futures, func ); return res; } public static <T> RunnableFuture<T> resultOf( Callable<T> call ) { return new FutureTask<T>( call ); } public static <T> CheckedListenableFuture<T> newGenericeFuture( ) { return new GenericCheckedListenableFuture<T>( ); } public static <T> CheckedListenableFuture<T> predestinedFuture( final T initValue ) { return new GenericCheckedListenableFuture<T>( ) { { this.set( initValue ); } }; } public static <T> CheckedListenableFuture<T> predestinedFailedFuture( final Throwable exValue ) { return new GenericCheckedListenableFuture<T>( ) { { this.setException( exValue ); } }; } /** * Returns a new {@code Callable} which will execute {@code firstCall} and, if it succeeds, * {@code secondCall} in sequence. The resulting {@code resultFuture} will return one of: * <ol> * <li>{@link Future#get()} returns the result of {@code secondCall}'s future result.</li> * <li>{@link Future#get()} throws the exception which caused {@code firstCall} to fail -- in this * case {@code secondCall} is not executed.</li> * <li>{@link Future#get()} throws the exception which caused {@code secondCall} to fail.</li> * </ol> * * @param <P> * @param firstCall * @param secondCall * @return resultFuture */ public static <T extends ListenableFuture<V>, V> Callable<T> combine( final Callable<T> firstCall, final Callable<T> secondCall ) { final CheckedListenableFuture<V> resultFuture = Futures.newGenericeFuture( ); final CheckedListenableFuture<T> intermediateFuture = Futures.newGenericeFuture( ); final Callable<T> chainingCallable = new Callable<T>( ) { @Override public String toString( ) { return Callable.class.getSimpleName( ) + ":[" + firstCall.toString( ) + "] ==> " + secondCall.toString( ); } @Override public T call( ) { try { try { final T res = firstCall.call( ); intermediateFuture.set( res ); } catch ( Exception ex ) { resultFuture.setException( ex ); intermediateFuture.setException( ex ); } Threads.lookup( Empyrean.class, Futures.class ).submit( new Runnable( ) { @Override public String toString( ) { return Runnable.class.getSimpleName( ) + ":" + firstCall.toString( ) + " ==> [" + secondCall.toString( ) + "]"; } @Override public void run( ) { try { intermediateFuture.get( ).get( ); try { T res2 = secondCall.call( ); resultFuture.set( res2.get( ) ); } catch ( Exception ex ) { resultFuture.setException( ex ); } } catch ( InterruptedException ex ) { LOG.error( ex ); Thread.currentThread( ).interrupt( ); resultFuture.setException( ex ); } catch ( ExecutionException ex ) { resultFuture.setException( ex.getCause( ) ); } catch ( Exception ex ) { resultFuture.setException( ex ); } } } ).get( ); } catch ( InterruptedException ex1 ) { Thread.currentThread( ).interrupt( ); resultFuture.setException( ex1 ); } catch ( RejectedExecutionException ex1 ) { resultFuture.setException( ex1 ); } catch ( Exception ex1 ) { resultFuture.setException( ex1 ); } return ( T ) resultFuture; } }; return chainingCallable; } public static <P> Callable<CheckedListenableFuture<P>> sequence( final Callable<CheckedListenableFuture<P>>... callables ) { checkParam( callables, not( emptyArray() ) ); if ( callables.length == 1 ) { return callables[0]; } else if ( callables.length == 2 ) { return Futures.combine( callables[0], callables[1] ); } else { final Callable<CheckedListenableFuture<P>>[] nextCallables = Arrays.copyOfRange( callables, 1, callables.length ); nextCallables[0] = Futures.combine( callables[0], callables[1] ); return sequence( nextCallables ); } } /** * TODO:GUAVA: remove and use the method available in Guava 10 */ public static <R> CheckedListenableFuture<List<R>> allAsList( final List<CheckedListenableFuture<R>> futures ) { final GenericCheckedListenableFuture<List<R>> combined = new GenericCheckedListenableFuture<List<R>>(); final List<R> resultList = Lists.newArrayListWithCapacity( futures.size() ); Iterables.addAll( resultList, Iterables.limit( Iterables.cycle( (R)null ), futures.size() ) ); final AtomicInteger completionCountdown = new AtomicInteger( futures.size() ); for ( int i=0; i<futures.size(); i++ ) { final int resultIndex = i; final CheckedListenableFuture<R> future = futures.get( i ); future.addListener( new Runnable() { @Override public void run() { try { resultList.set( resultIndex, future.get() ); } catch ( final ExecutionException e ){ combined.setException( e.getCause() ); } catch ( CancellationException e ) { combined.cancel( false ); } catch ( InterruptedException e ) { // complete so can't happen } if ( completionCountdown.decrementAndGet() == 0 ) { combined.set( resultList ); } } } ); } return combined; } }