/*******************************************************************************
*Copyright (c) 2009 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, only version 3 of the License.
*
*
* This file 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., 130 Castilian
* Dr., Goleta, CA 93101 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.
*******************************************************************************
* @author chris grzegorczyk <grze@eucalyptus.com>
*/
package com.eucalyptus.component;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import org.apache.log4j.Logger;
import org.mule.config.ConfigResource;
import com.eucalyptus.bootstrap.BootstrapException;
import com.eucalyptus.bootstrap.Bootstrapper;
import com.eucalyptus.records.EventRecord;
import com.eucalyptus.records.EventType;
import com.eucalyptus.records.Record;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.util.HasName;
import com.eucalyptus.util.NetworkUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* @author decker
*
*/
public class Component implements ComponentInformation, HasName<Component> {
private static Logger LOG = Logger.getLogger( Component.class );
public static String DISABLE_PATTERN = "euca.disable.%s";
public static String REMOTE_PATTERN = "euca.remote.%s";
private final String name;
private final com.eucalyptus.bootstrap.Component component;
private final Configuration configuration;
private ServiceBuilder<ServiceConfiguration> builder;
private Lifecycle lifecycle;
private Boolean enabled;
private Boolean local;
private Map<String, Service> services = Maps.newConcurrentHashMap( );
Component( String name, URI configFile ) throws ServiceRegistrationException {
this.name = name;
boolean enabled = false, local = false;
if ( System.getProperty( String.format( DISABLE_PATTERN, this.name ) ) == null ) {
enabled = true;
if ( System.getProperty( String.format( REMOTE_PATTERN, this.name ) ) == null ) {
local = true;
}
}
this.enabled = enabled;
this.local = local;
this.component = initComponent( );
if ( configFile != null ) {
this.configuration = new Configuration( this, configFile );
Components.register( this.configuration );
} else {
this.configuration = new Configuration( this );
}
this.lifecycle = new Lifecycle( this );
Components.register( this.lifecycle );
this.builder = new DefaultServiceBuilder( this );
}
public void removeService( final ServiceConfiguration config ) throws ServiceRegistrationException {
Service service = this.lookupServiceByHost( config.getHostName( ) );
this.builder.fireStop( config );
DispatcherFactory.remove( service );
Components.deregister( service );
EventRecord.caller( Component.class, config.isLocal( ) ? EventType.COMPONENT_SERVICE_STOP : EventType.COMPONENT_SERVICE_STOP, this.getName( ),
service.getName( ), service.getUri( ).toString( ) ).info( );
this.services.remove( service.getName( ) );
}
/**
* Builds a Service instance for this component using a service configuration created with the specified URI.
* @return
* @throws ServiceRegistrationException
*/
public Service buildService( URI uri ) throws ServiceRegistrationException {
this.enabled = true;
ServiceConfiguration config = this.builder.toConfiguration( uri );
Service service = new Service( this, config );
return this.setupService( config, service );
}
/**
* Builds a Service instance for this component using the provided service configuration.
* @return
* @throws ServiceRegistrationException
*/
public Service buildService( ServiceConfiguration config ) throws ServiceRegistrationException {
this.enabled = true;
Service service = new Service( this, config );
return this.setupService( config, service );
}
/**
* Builds a Service instance for this component using the local default values.
* @return
* @throws ServiceRegistrationException
*/
public Service buildService( ) throws ServiceRegistrationException {
this.enabled = true;
ServiceConfiguration conf = this.builder.toConfiguration( this.getConfiguration( ).getLocalUri( ) );
Service service = new Service( this, conf );
return this.setupService( conf, service );
}
private Service setupService( ServiceConfiguration config, Service service ) throws ServiceRegistrationException {
this.services.put( service.getName( ), service );
Components.register( service );
EventRecord.caller( Component.class, config.isLocal( ) ? EventType.COMPONENT_SERVICE_INIT : EventType.COMPONENT_SERVICE_INIT, this.getName( ),
service.getName( ), service.getUri( ).toString( ) ).info( );
return service;
}
public void startService( ServiceConfiguration service ) throws ServiceRegistrationException {
EventRecord.caller( Component.class, EventType.COMPONENT_SERVICE_START, this.getName( ), service.getName( ), service.getUri( ).toString( ) ).info( );
this.builder.fireStart( service );
this.lifecycle.setState( Lifecycles.State.STARTED );
}
private com.eucalyptus.bootstrap.Component initComponent( ) {
try {
com.eucalyptus.bootstrap.Component component = com.eucalyptus.bootstrap.Component.valueOf( name );
if ( component == null ) {
throw BootstrapException.throwError( "Error loading component. Failed to find component named '" + name );
}
return component;
} catch ( Exception e ) {
throw BootstrapException.throwError( "Error loading component. Failed to find component named '" + name, e );
}
}
public com.eucalyptus.bootstrap.Component getPeer( ) {
return this.component;
}
public String getName( ) {
return this.name;
}
public String toString( ) {
Record rec = EventRecord.caller( Component.class, EventType.COMPONENT_INFO, this.getName( ), "enabled", this.isEnabled( ), "local", this.isLocal( ),
"state", this.getLifecycle( ).getState( ) );
if( this.getConfiguration( ).getResource( ) != null ) {
for ( ConfigResource cfg : this.getConfiguration( ).getResource( ).getConfigurations( ) ) {
rec.next( ).append( ConfigResource.class, EventType.COMPONENT_INFO, this.getName( ), "->" + cfg.getUrl( ) );
}
}
for ( Bootstrapper b : this.configuration.getBootstrappers( ) ) {
rec.next( ).append( Bootstrapper.class, EventType.COMPONENT_INFO, this.getName( ), "->" + b.getClass( ).getSimpleName( ) );
}
return rec.toString( );
}
public Configuration getConfiguration( ) {
return this.configuration;
}
public ServiceBuilder<ServiceConfiguration> getBuilder( ) {
return this.builder;
}
public void markRemote( ) {
this.local = false;
}
public void markDisabled( ) {
this.local = false;
this.enabled = false;
}
public Boolean isEnabled( ) {
return this.enabled;
}
public Boolean isLocal( ) {
return this.local;
}
@Override
public int compareTo( Component that ) {
return this.getName( ).compareTo( that.getName( ) );
}
public List<ServiceConfiguration> list( ) throws ServiceRegistrationException {
return this.builder.list( );
}
public URI getUri( String hostName, Integer port ) {
return this.getConfiguration( ).makeUri( hostName, port );
}
public void setBuilder( ServiceBuilder<ServiceConfiguration> builder ) {
this.builder = builder;
}
Lifecycle getLifecycle( ) {
return this.lifecycle;
}
public Boolean isInitialized( ) {
return Lifecycles.State.INITIALIZED.equals( this.getLifecycle( ).getState( ) );
}
public Boolean isRunning( ) {
return Lifecycles.State.STARTED.equals( this.getLifecycle( ).getState( ) );
}
public NavigableSet<Service> getServices( ) {
return Sets.newTreeSet( this.services.values( ) );
}
public String getState( ) {
return this.lifecycle.getState( ).name( );
}
public Service lookupServiceByName( String name ) {
Exceptions.ifNullArgument( name );
for ( Service s : this.services.values( ) ) {
LOG.error( s );
if ( name.equals( s.getServiceConfiguration( ).getName( ) ) ) {
return s;
}
}
throw new NoSuchElementException( "No service found matching name: " + name + " for component: " + this.getName( ) );
}
public Service lookupServiceByHost( String hostName ) {
Exceptions.ifNullArgument( hostName );
for ( Service s : this.services.values( ) ) {
if ( hostName.equals( s.getServiceConfiguration( ).getHostName( ) ) ) {
return s;
}
}
for ( Service s : this.services.values( ) ) {
if ( hostName.equals( s.getEndpoint( ).getHost( ) ) ) {
return s;
}
}
if ( NetworkUtil.testLocal( hostName ) ) {
hostName = "localhost";
for ( Service s : this.services.values( ) ) {
if ( hostName.equals( s.getEndpoint( ).getHost( ) ) ) {
return s;
}
}
}
LOG.error( this.services.values( ) );
throw new NoSuchElementException( "No service found matching hostname: " + hostName + " for component: " + this.getName( ) );
}
public Boolean isRunningLocally( ) {
try {
for( Service s : this.services.values( ) ) {
if( s.isLocal( ) ) {
return true;
}
}
return false;
} catch ( NoSuchElementException ex ) {
LOG.trace( ex, ex );
return false;
}
}
}