Skip to content

Commit

Permalink
Update exception handling framework for web services.
Browse files Browse the repository at this point in the history
A new EucalyptusWebServiceException class is added for extention by
service specific exceptions. The new ErrorHandlerSupport class uses the
exception information to support handling of service errors.

The new QueryBindingInfo annotation allows error sub-classes to declare
the HTTP status code for use with that error condition. A Role
enumeration is added to identify the party at fault for an error
condition.

BaseMessageSupplier is added to encapsulate a message and any associated
binding details (initially just the HTTP status code) and web services
framework code is updated to allow responses of this type.
  • Loading branch information
sjones4 committed Jan 24, 2013
1 parent 397f30b commit 362b3a4
Show file tree
Hide file tree
Showing 10 changed files with 370 additions and 11 deletions.
Expand Up @@ -76,6 +76,7 @@
import com.eucalyptus.records.Logs;
import com.eucalyptus.ws.util.ReplyQueue;
import edu.ucsb.eucalyptus.msgs.BaseMessage;
import edu.ucsb.eucalyptus.msgs.BaseMessageSupplier;
import edu.ucsb.eucalyptus.msgs.ExceptionResponseType;
import edu.ucsb.eucalyptus.msgs.HasRequest;
import static com.eucalyptus.util.Parameters.checkParam;
Expand Down Expand Up @@ -215,8 +216,25 @@ public static Context createWrapped( String dest, final BaseMessage msg ) {
}
}

@SuppressWarnings( "unchecked" )
public static void response( BaseMessage responseMessage ) {
response( responseMessage, responseMessage );
}

/**
* Respond with the given supplier.
*
* <p>This allows a response with associated details such as an HTTP status
* code.</p>
*
* @param responseMessageSupplier The supplier to use
*/
public static void response( final BaseMessageSupplier responseMessageSupplier ) {
response( responseMessageSupplier, responseMessageSupplier.getBaseMessage() );
}

@SuppressWarnings( "unchecked" )
private static void response( final Object message,
final BaseMessage responseMessage ) {
if ( responseMessage instanceof ExceptionResponseType ) {
Logs.exhaust( ).trace( responseMessage );
}
Expand All @@ -226,7 +244,7 @@ public static void response( BaseMessage responseMessage ) {
EventRecord.here( ServiceContext.class, EventType.MSG_REPLY, responseMessage.getCorrelationId( ), responseMessage.getClass( ).getSimpleName( ),
String.format( "%.3f ms", ( System.nanoTime( ) - ctx.getCreationTime( ) ) / 1000000.0 ) ).trace( );
Channel channel = ctx.getChannel( );
Channels.write( channel, responseMessage );
Channels.write( channel, message );
clear( ctx );
} catch ( NoSuchContextException e ) {
LOG.warn( "Received a reply for absent client: No channel to write response message: " + e.getMessage( ) );
Expand Down
@@ -0,0 +1,66 @@
/*************************************************************************
* Copyright 2009-2013 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.ws;

import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.text.IsEmptyString.isEmptyOrNullString;
import static com.eucalyptus.util.Parameters.checkParam;
import javax.annotation.Nonnull;
import com.eucalyptus.util.EucalyptusCloudException;
import com.eucalyptus.ws.protocol.QueryBindingInfo;

/**
* Represents an expected error condition from a web service.
*
* <p>The codes used will be service specific, the {@link Role} should be
* interpreted at binding time to a valid meaningful to the service.</p>
*
* <p>Annotations for binding specific defaults should be placed on this
* class and overridden as appropriate in the exception class hierarchy.</p>
*/
@QueryBindingInfo( statusCode = 500 )
public class EucalyptusWebServiceException extends EucalyptusCloudException {
private static final long serialVersionUID = 1L;

private final String code;
private final Role role;

public EucalyptusWebServiceException( final String code,
final Role role,
final String message ) {
super( message );
checkParam( code, not( isEmptyOrNullString() ) );
checkParam( role, notNullValue() );
checkParam( message, not( isEmptyOrNullString() ) );
this.code = code;
this.role = role;
}

@Nonnull
public String getCode() {
return code;
}

@Nonnull
public Role getRole() {
return role;
}
}
36 changes: 36 additions & 0 deletions clc/modules/msgs/src/main/java/com/eucalyptus/ws/Role.java
@@ -0,0 +1,36 @@
/*************************************************************************
* Copyright 2009-2013 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.ws;

/**
* Represents a role in a message exchange pattern.
*/
public enum Role {

/**
* The message sender, a.k.a. client
*/
Sender,

/**
* The message recipient, a.k.a. server
*/
Receiver
}
Expand Up @@ -193,6 +193,7 @@ public void outgoingMessage( ChannelHandlerContext ctx, MessageEvent event ) thr
try {//use request binding
this.binding.toStream( byteOut, message );
} catch ( BindingException ex ) {
Logs.extreme( ).error( ex, ex );
try {//use default binding with request namespace
BindingManager.getDefaultBinding( ).toStream( byteOut, message, this.namespace );
} catch ( BindingException ex1 ) {//use default binding
Expand Down
@@ -0,0 +1,40 @@
/*************************************************************************
* Copyright 2009-2013 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.ws.protocol;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation to use for classes that define Query API binding details.
*/
@Target( ElementType.TYPE )
@Retention( RetentionPolicy.RUNTIME )
public @interface QueryBindingInfo {

/**
* The HTTP status code to use.
*
* @return The status code.
*/
int statusCode() default 200;
}
Expand Up @@ -76,6 +76,7 @@
import org.jboss.netty.channel.DownstreamMessageEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.timeout.IdleStateEvent;
import com.eucalyptus.component.ServiceOperations;
Expand All @@ -95,6 +96,7 @@
import com.eucalyptus.records.Logs;
import com.eucalyptus.ws.util.RequestQueue;
import edu.ucsb.eucalyptus.msgs.BaseMessage;
import edu.ucsb.eucalyptus.msgs.BaseMessageSupplier;
import edu.ucsb.eucalyptus.msgs.EucalyptusErrorMessageType;

@ChannelPipelineCoverage( "one" )
Expand All @@ -119,7 +121,7 @@ public void exceptionCaught( final ChannelHandlerContext ctx, final ExceptionEve
public void handleDownstream( final ChannelHandlerContext ctx, ChannelEvent e ) throws Exception {
if ( Logs.isExtrrreeeme( ) ) LOG.trace( this.getClass( ).getSimpleName( ) + "[outgoing]: " + e.getClass( ) );
BaseMessage reply = BaseMessage.extractMessage( e );
if ( reply instanceof BaseMessage ) {
if ( reply != null ) {
MessageEvent newEvent = makeDownstreamNewEvent( ctx, e, reply );
ctx.sendDownstream( newEvent );
} else if ( e instanceof ExceptionEvent ) {
Expand Down Expand Up @@ -156,19 +158,28 @@ private MessageEvent makeDownstreamNewEvent( ChannelHandlerContext ctx, ChannelE
final MappingHttpResponse response = new MappingHttpResponse( request.getProtocolVersion( ) );
final DownstreamMessageEvent newEvent = new DownstreamMessageEvent( ctx.getChannel( ), e.getFuture( ), response, null );
response.setMessage( reply );
setStatus( response, e );
return newEvent;
// Contexts.clear( reqCtx );
} else {
final MappingHttpResponse response = new MappingHttpResponse( HttpVersion.HTTP_1_1 ) {
{
setMessage( new EucalyptusErrorMessageType( this.getClass( ).getSimpleName( ), "Received a NULL reply" ) );
}
};
final DownstreamMessageEvent newEvent = new DownstreamMessageEvent( ctx.getChannel( ), e.getFuture( ), response, null );
return newEvent;
final MappingHttpResponse response = new MappingHttpResponse( HttpVersion.HTTP_1_1 );
response.setMessage( new EucalyptusErrorMessageType( this.getClass( ).getSimpleName( ), "Received a NULL reply" ) );
setStatus( response, e );
return new DownstreamMessageEvent( ctx.getChannel( ), e.getFuture( ), response, null );
}
}

private void setStatus( final MappingHttpResponse response, final ChannelEvent e ) {
if ( e instanceof MessageEvent ) {
final MessageEvent msge = ( MessageEvent ) e;
if ( msge.getMessage( ) instanceof BaseMessageSupplier ) {
final HttpResponseStatus status = ((BaseMessageSupplier) msge.getMessage()).getStatus();
if ( status != null ) {
response.setStatus( status );
}
}
}
}

@Override
public void handleUpstream( final ChannelHandlerContext ctx, final ChannelEvent e ) throws Exception {
final MappingHttpMessage request = MappingHttpMessage.extractMessage( e );
Expand Down
Expand Up @@ -84,6 +84,7 @@
import com.eucalyptus.util.LogUtil;
import edu.ucsb.eucalyptus.constants.IsData;
import edu.ucsb.eucalyptus.msgs.BaseMessage;
import edu.ucsb.eucalyptus.msgs.BaseMessageSupplier;
import edu.ucsb.eucalyptus.msgs.GetObjectResponseType;
import edu.ucsb.eucalyptus.msgs.WalrusDataGetResponseType;

Expand Down Expand Up @@ -119,6 +120,8 @@ public void handleDownstream( final ChannelHandlerContext ctx, ChannelEvent e )
} else {
ctx.sendDownstream( msge );
}
} else if ( msge.getMessage( ) instanceof BaseMessageSupplier ) {// Handle single request-response MEP
ctx.sendDownstream( msge );
} else if ( msge.getMessage( ) instanceof Throwable ) {
ctx.sendDownstream( e );
} else {
Expand Down

0 comments on commit 362b3a4

Please sign in to comment.