/*
* Copyright 2013 EMC Corporation. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.emc.atmos.api.jersey;
import com.emc.atmos.AtmosException;
import com.emc.atmos.api.AtmosConfig;
import com.emc.atmos.api.RestUtil;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.filter.ClientFilter;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
public class RetryFilter extends ClientFilter {
private static final int ATMOS_1040_DELAY_MS = 300;
private static final Logger log = Logger.getLogger( RetryFilter.class );
private AtmosConfig config;
public RetryFilter( AtmosConfig config ) {
this.config = config;
}
@Override
public ClientResponse handle( ClientRequest clientRequest ) throws ClientHandlerException {
int retryCount = 0;
InputStream entityStream = null;
if ( clientRequest.getEntity() instanceof InputStream ) entityStream = (InputStream) clientRequest.getEntity();
while ( true ) {
try {
// if using an InputStream, mark the stream so we can rewind it in case of an error
if ( entityStream != null && entityStream.markSupported() )
entityStream.mark( config.getRetryBufferSize() );
return getNext().handle( clientRequest );
} catch ( RuntimeException orig ) {
Throwable t = orig;
// in this case, the exception was wrapped by Jersey
if ( t instanceof ClientHandlerException ) t = t.getCause();
if ( t instanceof AtmosException ) {
AtmosException ae = (AtmosException) t;
// retry all 50x errors
if ( ae.getHttpCode() < 500 ) throw orig;
// add small delay to Atmos code 1040 (server busy)
if ( ae.getErrorCode() == 1040 ) {
try {
Thread.sleep( ATMOS_1040_DELAY_MS );
} catch ( InterruptedException e ) {
log.warn( "Interrupted while waiting after a 1040 response: " + e.getMessage() );
}
}
// retry all IO exceptions unless wschecksum is enabled (can't overwrite data in this case)
} else if ( !(t instanceof IOException)
|| clientRequest.getHeaders().getFirst( RestUtil.XHEADER_WSCHECKSUM ) != null ) throw orig;
// only retry maxRetries times
if ( ++retryCount > config.getMaxRetries() ) throw orig;
// attempt to reset InputStream if it has been read from
if ( entityStream != null ) {
if ( !(entityStream instanceof MeasuredInputStream)
|| ((MeasuredInputStream) entityStream).getRead() > 0 ) {
try {
if ( !entityStream.markSupported() ) throw new IOException( "Mark is not supported" );
entityStream.reset();
} catch ( IOException e ) {
log.warn( "Could not reset entity stream for retry: " + e.getMessage() );
throw orig;
}
}
}
log.info( "Error received in response (" + t + "), retrying..." );
// wait for retry delay
if ( config.getRetryDelayMillis() > 0 ) {
try {
Thread.sleep( config.getRetryDelayMillis() );
} catch ( InterruptedException e ) {
log.warn( "Interrupted while waiting to retry: " + e.getMessage() );
}
}
}
}
}
}