/************************************************************************* * Copyright 2009-2016 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. ************************************************************************/ package com.eucalyptus.simpleworkflow.common.stateful; import com.ctc.wstx.exc.WstxEOFException; import com.eucalyptus.bootstrap.Bootstrap; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.component.Topology; import com.eucalyptus.simpleworkflow.stateful.NotifyResponseType; import com.eucalyptus.simpleworkflow.stateful.NotifyType; import com.eucalyptus.simpleworkflow.stateful.PollForNotificationResponseType; import com.eucalyptus.simpleworkflow.stateful.PollForNotificationType; import com.eucalyptus.util.Consumer; import com.eucalyptus.util.Consumers; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.async.AsyncRequests; import com.eucalyptus.util.async.ConnectionException; import com.eucalyptus.util.concurrent.ListenableFuture; import com.eucalyptus.ws.WebServicesException; import com.google.common.base.Objects; import com.google.common.base.Throwables; import org.apache.log4j.Logger; import java.net.ConnectException; import java.util.NoSuchElementException; import java.util.concurrent.ExecutionException; /** * Created by ethomas on 10/31/16. */ public class NotifyClientUtils { private static final Logger logger = Logger.getLogger( NotifyClientUtils.class ); public static void notifyChannel(final ChannelWrapper channelWrapper) { final NotifyType notify = new NotifyType( ); notify.setChannel(channelWrapper.getChannelName()); try { final ListenableFuture<NotifyResponseType> dispatchFuture = AsyncRequests.dispatch(Topology.lookup(PolledNotifications.class), notify); dispatchFuture.addListener( new Runnable( ) { @Override public void run() { try { dispatchFuture.get( ); } catch ( final InterruptedException e ) { logger.info( "Interrupted while sending notification for " + notify.getChannel(), e ); } catch ( final ExecutionException e ) { logger.error( "Error sending notification for " + notify.getChannel( ), e ); //TODO:STEVE: should retry notification? } } } ); } catch ( final Exception e ) { logger.error( "Error sending notification for " + notify.getChannel( ), e ); } } public static Consumer<Boolean> pollChannel(final ChannelWrapper channelWrapper, final long timeout, final Consumer<Boolean> resultConsumer) throws Exception { final Consumer<Boolean> consumer = Consumers.once(resultConsumer); final PollForNotificationType poll = new PollForNotificationType( ); poll.setChannel(channelWrapper.getChannelName()); poll.setTimeout( timeout ); if ( Bootstrap.isShuttingDown() ) { delayedPollFailure( 1000L, consumer ); return consumer; } final ServiceConfiguration polledNotificationsConfiguration; try { polledNotificationsConfiguration = Topology.lookup( PolledNotifications.class ); } catch ( final NoSuchElementException e ){ delayedPollFailure( 5000L, consumer ); return consumer; } final ListenableFuture<PollForNotificationResponseType> dispatchFuture = AsyncRequests.dispatch( polledNotificationsConfiguration, poll ); dispatchFuture.addListener( new Runnable( ) { @Override public void run( ) { try { final PollForNotificationResponseType response = dispatchFuture.get( ); consumer.accept(Objects.firstNonNull(response.getNotified(), false)); } catch ( final InterruptedException e ) { logger.info( "Interrupted while polling for task " + poll.getChannel( ), e ); } catch ( final ExecutionException e ) { if ( Bootstrap.isShuttingDown( ) ) { logger.info( "Error polling for task " + poll.getChannel( ) + ": " + Exceptions.getCauseMessage(e) ); } else { handleExecutionExceptionForPolling(e, poll); } } catch ( final Exception e ) { logger.error( "Error polling for task " + poll.getChannel( ), e ); } finally { consumer.accept( false ); } } } ); return consumer; } private static void delayedPollFailure( final long delay, final Consumer<Boolean> consumer) { try { Thread.sleep( delay ); } catch (InterruptedException e1) { Thread.currentThread( ).interrupt( ); } finally { consumer.accept( false ); } } private static void handleExecutionExceptionForPolling(ExecutionException e, PollForNotificationType poll) { Throwable cause = Throwables.getRootCause(e); // The following errors occur when the CLC is down or rebooting. // com.eucalyptus.ws.WebServicesException: Failed to marshall response: // com.eucalyptus.util.async.ConnectionException: Channel was closed before the response was received.:PollForNotificationType //java.net.ConnectException: Connection refused: // com.ctc.wstx.exc.WstxEOFException: Unexpected EOF in prolog // At this point, we just wait a couple of seconds to allow the CLC to reboot. It will probably take more than a couple of seconds, // but this way we will also slow the rate of log error accrual, as otherwise this method is called again immediately. if (cause instanceof WebServicesException || cause instanceof ConnectionException || cause instanceof ConnectException || cause instanceof WstxEOFException) { logger.info("Error polling for task " + poll.getChannel() + ", CLC likely down. Will sleep for 5 seconds"); logger.info(cause.getClass() + ":" + cause.getMessage()); try { Thread.sleep(5000L); } catch (InterruptedException e1) { logger.info("Interrupted while polling for task " + poll.getChannel(), e1); } } else { logger.error( "Error polling for task " + poll.getChannel( ), e ); } } public interface ChannelWrapper { public String getChannelName(); } }