/************************************************************************* * 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. * * 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. ************************************************************************/ package com.eucalyptus.ws; import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import com.eucalyptus.bootstrap.Bootstrap; import com.eucalyptus.bootstrap.BootstrapArgs; import com.eucalyptus.bootstrap.Bootstrapper; import com.eucalyptus.bootstrap.Hosts; import com.eucalyptus.bootstrap.Provides; import com.eucalyptus.bootstrap.RunDuring; import com.eucalyptus.component.Component; import com.eucalyptus.component.Component.State; import com.eucalyptus.component.ComponentId; import com.eucalyptus.component.ComponentIds; import com.eucalyptus.component.Components; import com.eucalyptus.component.Faults; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.component.ServiceConfigurations; import com.eucalyptus.component.Topology; import com.eucalyptus.empyrean.Empyrean; import com.eucalyptus.system.Threads; import com.eucalyptus.util.Exceptions; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.primitives.Ints; import java.util.concurrent.atomic.AtomicBoolean; @Provides( Empyrean.class ) @RunDuring( Bootstrap.Stage.RemoteServicesInit ) public class ServiceBootstrapper extends Bootstrapper.Simple { private static Logger LOG = Logger.getLogger( ServiceBootstrapper.class ); private static final int NUM_SERVICE_BOOTSTRAP_WORKERS = MoreObjects.firstNonNull( Ints.tryParse( System.getProperty( "com.eucalyptus.bootstrap.serviceWorkers", "" ) ), 40 ); static class ServiceBootstrapWorker { private static final ConcurrentMap<ServiceBootstrapWorker.Worker, Runnable> workers = Maps.newConcurrentMap( ); private static final Runnable IDLE = new Runnable( ) { @Override public String toString( ) { return "IDLE"; } @Override public void run( ) {} }; private static final AtomicBoolean running = new AtomicBoolean( true ); private static final BlockingQueue<Runnable> msgQueue = new LinkedBlockingQueue<>( ); private static final ExecutorService executor = Executors.newCachedThreadPool( Threads.threadFactory( "web-services-bootstrap-%d" ) ); private static final ServiceBootstrapWorker worker = new ServiceBootstrapWorker( ); private ServiceBootstrapWorker( ) { for ( int i = 0; i < NUM_SERVICE_BOOTSTRAP_WORKERS; i++ ) { executor.submit( new Worker( i ) ); } } public static void markFinished( ) { running.set( false ); executor.shutdownNow( ); } public static void submit( final Runnable run ) { LOG.info( run ); if ( !running.get( ) ) { throw new IllegalStateException( "Worker has been stopped: " + ServiceBootstrapWorker.class ); } else { msgQueue.add( run ); } } class Worker implements Runnable, Comparable<Worker> { private final String name; Worker( final int number ) { super( ); this.name = "service-bootstrap-worker-" + number; workers.put( this, IDLE ); } @Override public void run( ) { while ( !msgQueue.isEmpty( ) || running.get( ) ) { Runnable event; try { if ( ( event = msgQueue.poll( Long.MAX_VALUE, TimeUnit.MILLISECONDS ) ) != null ) { try { workers.replace( this, event ); event.run( ); } finally { workers.replace( this, IDLE ); } } } catch ( final InterruptedException e ) { Thread.currentThread( ).interrupt( ); break; } catch ( final Throwable e ) { Exceptions.trace( e ); } } LOG.debug( "Finished servicing bootstrap registration request queue: " + this.toString( ) ); } @Override public String toString( ) { final StringBuilder builder = new StringBuilder( ); builder.append( "ServiceBootstrapWorker" ) .append( " " ) .append( this.name ) .append( " work: " ) .append( workers.get( this ) ) .append( " thread=" ) .append( Thread.currentThread( ).toString( ) ); return builder.toString( ); } /** * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo( final Worker o ) { return this.name.compareTo( o.name ); } @Override public int hashCode( ) { final int prime = 31; int result = 1; result = prime * result + this.getOuterType( ).hashCode( ); result = prime * result + ( ( this.name == null ) ? 0 : this.name.hashCode( ) ); return result; } @Override public boolean equals( final Object obj ) { if ( this == obj ) { return true; } if ( obj == null ) { return false; } if ( this.getClass( ) != obj.getClass( ) ) { return false; } final Worker other = ( Worker ) obj; if ( !this.getOuterType( ).equals( other.getOuterType( ) ) ) { return false; } if ( this.name == null ) { if ( other.name != null ) { return false; } } else if ( !this.name.equals( other.name ) ) { return false; } return true; } private ServiceBootstrapWorker getOuterType( ) { return ServiceBootstrapWorker.this; } } static void waitAll( ) { try { while ( !msgQueue.isEmpty( ) ) { for ( final Map.Entry<Worker,Runnable> entry : workers.entrySet( ) ) { if ( entry.getValue( ) == IDLE ) continue; LOG.info( "Waiting for " + entry.getKey( ) ); } TimeUnit.MILLISECONDS.sleep( 200 ); } } catch ( final InterruptedException ex ) { Thread.currentThread( ).interrupt( ); } } } enum ShouldLoad implements Predicate<ServiceConfiguration> { INSTANCE; @Override public boolean apply( final ServiceConfiguration config ) { boolean ret = config.getComponentId( ).isAlwaysLocal( ) || config.isVmLocal( ) || ( BootstrapArgs.isCloudController( ) && config.getComponentId( ).isCloudLocal( ) ) || Hosts.isCoordinator( ); LOG.debug( "ServiceBootstrapper.shouldLoad(" + config.toString( ) + "):" + ret ); return ret; } } @Override public boolean load( ) { WebServices.restart( ); ServiceBootstrapper.execute( config -> { ServiceBootstrapWorker.submit( new Runnable( ) { @Override public void run( ) { try { Components.lookup( config.getComponentId( ) ).setup( config ); if ( config.lookupState( ).ordinal( ) < State.LOADED.ordinal( ) ) { Topology.transition( State.LOADED ).apply( config ); } } catch ( final Exception ex ) { Faults.failure( config, ex ); } } @Override public String toString( ) { return "ServiceBootstrap.load(): " + config.getFullName( ); } } ); return true; } ); ServiceBootstrapWorker.waitAll( ); return true; } @Override public boolean start( ) throws Exception { ServiceBootstrapper.execute( config -> { ServiceBootstrapWorker.submit( new Runnable( ) { @Override public void run( ) { try { Topology.transition( State.DISABLED ).apply( config ); } catch ( final Exception ex ) { Exceptions.maybeInterrupted( ex ); } } @Override public String toString( ) { return "ServiceBootstrap.start(): " + config.getFullName( ); } } ); return true; } ); try { ServiceBootstrapWorker.waitAll( ); } finally { ServiceBootstrapWorker.markFinished( ); } return true; } private static void execute( final Predicate<ServiceConfiguration> predicate ) throws NoSuchElementException { for ( final ComponentId compId : ComponentIds.list( ) ) { final Component comp = Components.lookup( compId ); if ( compId.isRegisterable( ) ) { for ( final ServiceConfiguration config : Iterables.filter( ServiceConfigurations.list( compId.getClass( ) ), ShouldLoad.INSTANCE ) ) { try { predicate.apply( config ); } catch ( final Exception ex ) { Exceptions.trace( ex ); } } } else if ( comp.hasLocalService( ) ) { final ServiceConfiguration config = comp.getLocalServiceConfiguration( ); if ( config.isVmLocal( ) || ( BootstrapArgs.isCloudController( ) && config.isHostLocal( ) ) ) { try { predicate.apply( config ); } catch ( final Exception ex ) { Exceptions.trace( ex ); } } } } } }