/*************************************************************************
* Copyright 2009-2014 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 edu.ucsb.eucalyptus.msgs;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.UUID;
import javax.persistence.Transient;
import org.apache.log4j.Logger;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.JiBXException;
import com.eucalyptus.auth.principal.Principals;
import com.eucalyptus.auth.principal.User;
import com.eucalyptus.binding.BindingManager;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.empyrean.ServiceId;
import com.eucalyptus.http.MappingHttpMessage;
import com.eucalyptus.system.Threads;
import com.eucalyptus.util.Classes;
import com.eucalyptus.util.Exceptions;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
public class BaseMessage implements BaseMessageMarker {
@Transient
private static Logger LOG = Logger.getLogger( BaseMessage.class );
private String correlationId;
private String userId;
private String effectiveUserId;
private BaseCallerContext callerContext;
private Boolean _return = true;
private String statusMessage;
private Integer _epoch; //NOTE:GRZE: intentionally violating naming conventions to avoid shadowing/conflicts
private ArrayList<ServiceId> _services = Lists.newArrayList( ); //NOTE:GRZE: intentionally violating naming conventions to avoid shadowing/conflicts
private ArrayList<ServiceId> _disabledServices = Lists.newArrayList( ); //NOTE:GRZE: intentionally violating naming conventions to avoid shadowing/conflicts
private ArrayList<ServiceId> _notreadyServices = Lists.newArrayList( ); //NOTE:GRZE: intentionally violating naming conventions to avoid shadowing/conflicts
private ArrayList<ServiceId> _stoppedServices = Lists.newArrayList( ); //NOTE:GRZE: intentionally violating naming conventions to avoid shadowing/conflicts
public BaseMessage( ) {
super( );
this.correlationId = UUID.randomUUID( ).toString( );
this._return = true;
}
public BaseMessage( String userId ) {
this( );
this.userId = userId;
this.effectiveUserId = userId;
this.statusMessage = null;
}
public BaseMessage( BaseMessage copy ) {
this( );
this.effectiveUserId = copy != null ? copy.getEffectiveUserId( ) : null;
this.correlationId = copy != null ? copy.getCorrelationId( ) : null;
}
public String getCorrelationId( ) {
if ( this.correlationId == null ) {
Logger.getLogger( "EXHAUST" ).error( Exceptions.filterStackTrace( new RuntimeException( "Creating UUID for message which did not have it set correctly: "
+ this.getClass( ) ) ) );
return ( this.correlationId = UUID.randomUUID( ).toString( ) );
} else {
return this.correlationId;
}
}
public void setCorrelationId( String correlationId ) {
this.correlationId = correlationId;
}
@Deprecated
public void setUserId( String userId ) {
this.userId = userId;
}
@Deprecated
public String getUserId( ) {
return this.userId;
}
public Boolean get_return( ) {
return this._return;
}
public Boolean get_return( boolean ifError ) {
return get_return( );
}
@SuppressWarnings( "unchecked" )
public <TYPE extends BaseMessage> TYPE markWinning( ) {
this._return = true;
return ( TYPE ) this;
}
@SuppressWarnings( "unchecked" )
public <TYPE extends BaseMessage> TYPE markFailed( ) {
this._return = false;
return ( TYPE ) this;
}
@SuppressWarnings( "unchecked" )
public <TYPE extends BaseMessage> TYPE markPrivileged( ) {
this.effectiveUserId = Principals.systemUser( ).getName( );
return ( TYPE ) this;
}
@SuppressWarnings( "unchecked" )
public <TYPE extends BaseMessage> TYPE markUnprivileged( ) {
this.effectiveUserId = this.userId;
return ( TYPE ) this;
}
public void set_return( Boolean return1 ) {
this._return = return1;
}
public String getStatusMessage( ) {
return this.statusMessage;
}
public void setStatusMessage( String statusMessage ) {
this.statusMessage = statusMessage;
}
@Deprecated
public void setEffectiveUserId( String effectiveUserId ) {
this.effectiveUserId = effectiveUserId;
}
public String getEffectiveUserId( ) {
return this.effectiveUserId;
}
public BaseCallerContext getCallerContext( ) {
return callerContext;
}
public void setCallerContext( final BaseCallerContext callerContext ) {
this.callerContext = callerContext;
}
/**
* Creates a default SYSTEM generated message.
*
* @param <TYPE>
* @return
*/
public <TYPE extends BaseMessage> TYPE regarding( ) {
regarding( null );
return ( TYPE ) this;
}
public <TYPE extends BaseMessage> TYPE regarding( BaseMessage msg ) {
this.correlationId = UUID.randomUUID( ).toString( );
this.userId = Principals.systemFullName( ).getUserName( );
this.effectiveUserId = Principals.systemFullName( ).getUserName( );
return ( TYPE ) this;
}
public <TYPE extends BaseMessage> TYPE regardingUserRequest( BaseMessage msg ) {
this.userId = msg.userId;
return ( TYPE ) this;
}
public <TYPE extends BaseMessage> TYPE lookupAndSetCorrelationId(){
String corrId = null;
try{
corrId = Contexts.lookup().getCorrelationId();
}catch(final Exception ex){
corrId = Threads.getCorrelationId();
}
if(corrId != null && corrId.length()>=36){
return this.regardingRequestId(corrId);
}else
return ( TYPE ) this;
}
public <TYPE extends BaseMessage> TYPE regardingRequestId(final String msgId){
if(msgId!=null){
String requestId = null;
String postfix = null;
if (! msgId.contains("::")){
requestId = msgId;
postfix = requestId;
}
else {
requestId = msgId.substring(0, msgId.indexOf("::"));
postfix = msgId.substring(msgId.indexOf("::")+2);
}
String uuid = null;
try{
String baseHex = postfix.substring(9,13);
Integer baseHexInt = Integer.parseInt(baseHex, 16);
Integer newHexInt = (baseHexInt+1) % 65536;
String newHex = Integer.toHexString(newHexInt);
while(newHex.length()<4) {
newHex = "0"+newHex;
}
uuid = UUID.randomUUID( ).toString( );
uuid = uuid.substring(0, 9) + newHex + uuid.substring(13);
}catch(final Exception ex){
uuid = UUID.randomUUID( ).toString( );
}
this.correlationId = String.format("%s::%s", requestId, uuid);
}
return ( TYPE ) this;
}
public boolean hasRequestId(){
return this.correlationId!=null && this.correlationId.indexOf("::") > 0;
}
public String toString( ) {
String str = this.toString( BindingManager.defaultBindingName( ) );
str = ( str != null )
? str
: this.toString( "eucalyptus_ucsb_edu" );
str = ( str != null )
? str
: "Failed to bind message of type: " + this.getClass( ).getName( ) + " at "
+ Thread.currentThread( ).getStackTrace( )[1].toString( );
return str;
}
/**
* Get the XML form of the message.
*
* @param namespace
* @return String representation of the object, null if binding fails.
*/
public String toString( String namespace ) {
ByteArrayOutputStream temp = new ByteArrayOutputStream( );
Class targetClass = Iterables.find( Classes.classAncestors( this ), new Predicate<Class>( ) {
@Override
public boolean apply( Class arg0 ) {
return !arg0.isAnonymousClass( );
}
} );
try {
IBindingFactory bindingFactory = BindingDirectory.getFactory( namespace, targetClass );
IMarshallingContext mctx = bindingFactory.createMarshallingContext( );
mctx.setIndent( 2 );
mctx.marshalDocument( this, "UTF-8", null, temp );
} catch ( JiBXException e ) {
Logger.getLogger( BaseMessage.class ).debug( e, e );
} catch ( Exception e ) {
Logger.getLogger( BaseMessage.class ).error( e, e );
}
return temp.toString( );
}
public <TYPE extends BaseMessage> TYPE getReply( ) {
Class msgClass = this.getClass( );
while ( !msgClass.getSimpleName( ).endsWith( "Type" ) ) {
msgClass = msgClass.getSuperclass( );
}
String replyType = msgClass.getName( ).replaceAll( "Type$", "" ) + "ResponseType";
try {
Class<TYPE> responseClass = (Class<TYPE>) ClassLoader.getSystemClassLoader( ).loadClass( replyType );
return reply( responseClass.newInstance() );
} catch ( Exception e ) {
Logger.getLogger( BaseMessage.class ).debug( e, e );
throw new TypeNotPresentException( this.correlationId, e );
}
}
protected <TYPE extends BaseMessage> TYPE reply( TYPE reply ) {
reply.setCorrelationId( this.correlationId );
return reply;
}
public String toSimpleString( ) {
StringBuilder buf = new StringBuilder( );
buf.append( this.getClass( ).getSimpleName( ) )
.append( ":" ).append( this.correlationId )
.append( ":return=" ).append( this.get_return( ) )
.append( ":epoch=" ).append( this.get_epoch( ) )
.append( ":status=" ).append( this.getStatusMessage( ) );
return buf.toString( );
}
/**
* @return the epoch
*/
public Integer get_epoch( ) {
return this._epoch;
}
/**
* @param epoch the epoch to set
*/
public void set_epoch( Integer epoch ) {
this._epoch = epoch;
}
/**
* @return the services
*/
public ArrayList<ServiceId> get_services( ) {
return this._services;
}
/**
* @param services the services to set
*/
public void set_services( ArrayList<ServiceId> services ) {
this._services = services;
}
/**
* Get the message from within a ChannelEvent. Returns null if no message found.
*
* @param <T>
* @param e
* @return message or null if no msg.
*/
public static <T extends BaseMessage> T extractMessage( ChannelEvent e ) {
if ( e instanceof MessageEvent ) {
final MessageEvent msge = ( MessageEvent ) e;
MappingHttpMessage msgHttp = null;
if ( msge.getMessage( ) instanceof BaseMessage ) {
return ( T ) msge.getMessage( );
} else if ( msge.getMessage( ) instanceof MappingHttpMessage
&& ( msgHttp = ( MappingHttpMessage ) msge.getMessage( ) ).getMessage( ) instanceof BaseMessage ) {
return ( T ) msgHttp.getMessage( );
} else if ( msge.getMessage( ) instanceof Supplier
&& ( ( Supplier ) msge.getMessage( ) ).get( ) instanceof BaseMessage ) {
return ( T ) ( ( Supplier ) msge.getMessage( ) ).get( );
} else {
return null;
}
} else {
return null;
}
}
public BaseMessage setUser( User user ) {
if ( user == null ) {
this.setUser( Principals.nobodyUser( ) );
} else {
this.userId = user.getName( );
this.effectiveUserId = user.isSystemAdmin( )
? Principals.systemUser( ).getName( )
: user.getName( );
}
return this;
}
public ArrayList<ServiceId> get_disabledServices( ) {
return this._disabledServices;
}
public void set_disabledServices( ArrayList<ServiceId> _disabledServices ) {
this._disabledServices = _disabledServices;
}
public ArrayList<ServiceId> get_notreadyServices( ) {
return this._notreadyServices;
}
public void set_notreadyServices( ArrayList<ServiceId> _notreadyServices ) {
this._notreadyServices = _notreadyServices;
}
public ArrayList<ServiceId> get_stoppedServices() {
return this._stoppedServices;
}
public void set_stoppedServices( final ArrayList<ServiceId> _stoppedServices ) {
this._stoppedServices = _stoppedServices;
}
}