Package org.xlightweb.client

Source Code of org.xlightweb.client.RetryHandler$InMemorySink

/*
*  Copyright (c) xlightweb.org, 2008 - 2009. All rights reserved.
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library 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
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xlightweb.org/
*/
package org.xlightweb.client;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.xlightweb.BodyDataSink;
import org.xlightweb.IBodyDestroyListener;
import org.xlightweb.IHttpExchange;
import org.xlightweb.IHttpResponse;
import org.xlightweb.IHttpResponseHeader;
import org.xlightweb.IHttpResponseHandler;
import org.xlightweb.IHttpRequest;
import org.xlightweb.HttpRequest;
import org.xlightweb.IHttpRequestHeader;
import org.xlightweb.IHttpRequestHandler;
import org.xlightweb.ProtocolException;
import org.xlightweb.Supports100Continue;
import org.xlightweb.client.DuplicatingBodyForwarder.ISink;
import org.xlightweb.client.DuplicatingBodyForwarder.BodyDataSinkAdapter;
import org.xsocket.Execution;
import org.xsocket.connection.IConnection.FlushMode;




/*
* Retry handler
* @author grro@xlightweb.org
*/
@Supports100Continue
final class RetryHandler implements IHttpRequestHandler {
   
   
    /**
     * RetryHandler is unsynchronized by config. See HttpUtils$RequestHandlerInfo
     */
 
  private static final Logger LOG = Logger.getLogger(RetryHandler.class.getName());

 
  static final String RETRY_KEY = "org.xlightweb.client.RetryHandler.retry";
  private static final String RETRY_COUNT_KEY = "org.xlightweb.client.RetryHandler.countTrials";

 
  private final HttpClient httpClient;
     
    // statistics
    private int countRetries = 0;

   
 
  public RetryHandler(HttpClient httpClient) {
      this.httpClient = httpClient;
    }
 
 
  int getCountRetries() {
      return countRetries;
  }


 
  static boolean isRetryable(IOException ioe) {
        if ((ProtocolException.class.isAssignableFrom(ioe.getClass())) &&
            (!isErrorStatusCodeRecevied((ProtocolException) ioe))) {
            return true;
        } else {
            return false;
        }
  }

   
    private static boolean isErrorStatusCodeRecevied(ProtocolException pe) {
        IHttpResponseHeader header = (IHttpResponseHeader) HttpClientConnection.getReceviedHeader(pe);
        if ((header != null) && (header.getStatus() >= 400)) {
            return true;
        }
       
        return false;
    }

 
 
  /**
   * {@inheritDoc}
   */
  public void onRequest(final IHttpExchange exchange) throws IOException {

      IHttpRequest request = exchange.getRequest();
     
      Boolean retry = (Boolean) request.getAttribute(RETRY_KEY);
      if (retry == null) {
          retry = true;
      }
     
      if (retry) {
          // handle GET and Delete request
          if (request.getMethod().equalsIgnoreCase("GET"|| request.getMethod().equalsIgnoreCase("DELETE")) {
              BodylessRetryResponseHandler retryHandler = new BodylessRetryResponseHandler(exchange, request.getRequestHeader().copy());
              exchange.forward(request, retryHandler);
              return;
             
          // handle PUT
          } else if (request.getMethod().equalsIgnoreCase("PUT")) { 
              BodyRetryResponseHandler retryHandler = new BodyRetryResponseHandler(exchange, request.getRequestHeader().copy());
             
              final BodyDataSink dataSink = exchange.forward(request.getRequestHeader(), retryHandler);
              dataSink.setFlushmode(FlushMode.ASYNC);
             
              // BodyDataSink
             
              DuplicatingBodyForwarder forwarder = new DuplicatingBodyForwarder(request.getNonBlockingBody(), new BodyDataSinkAdapter(dataSink), retryHandler);
              request.getNonBlockingBody().setDataHandler(forwarder);
              return;
          }
      }
     
      exchange.forward(request);
    }
 
 
 
    /**
     * BodyRetryResponseHandler is unsynchronized by config. See HttpUtils$RequestHandlerInfo
     */
  private final class BodyRetryResponseHandler implements IHttpResponseHandler, ISink {
     
      private final IHttpExchange exchange;
      private final IHttpRequestHeader requestHeader;
     
     
      private final InMemorySink inMemorySink = new InMemorySink();
      private Integer countTrials;

     
      BodyRetryResponseHandler(IHttpExchange exchange, IHttpRequestHeader requestHeader) {
          this.exchange = exchange;
          this.requestHeader = requestHeader;
         
          countTrials = (Integer) requestHeader.getAttribute(RETRY_COUNT_KEY);
          if (countTrials == null) {
              countTrials = 0;
          }
      }
     
     
      public void onData(ByteBuffer data) throws IOException {
          inMemorySink.onData(data);
      }
     
     
      public void close() throws IOException {
          inMemorySink.close();
      }
     
      public void destroy() {
          inMemorySink.destroy();
      }
     
      public void setDestroyListener(IBodyDestroyListener destroyListener) {
          inMemorySink.setDestroyListener(destroyListener);
      }

        public String getId() {
            return inMemorySink.getId();
        }
             
      public void onResponse(IHttpResponse response) throws IOException {
          exchange.send(response);
      }

     
      public void onException(IOException ioe) throws IOException {
          if (isRetryable(ioe) && (countTrials < httpClient.getMaxRetries())) {
                countRetries++;
               
                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("try to retrying request (retry num " +  countRetries + "). I/O exception  caught when processing request " + ioe.toString());
                }

                sendRetry(ioe);
               
            } else {
                exchange.sendError(ioe);
            }
      }

     
     
        private void sendRetry(final IOException ioe) throws IOException {
           
            Runnable task = new Runnable() {
               
                public void run() {
               
                    try {
                        requestHeader.setAttribute(RETRY_COUNT_KEY, ++countTrials);
                        requestHeader.setAttribute(CookieHandler.COOKIE_WARNING_KEY, false);
                           
                        IHttpResponseHandler respHdl = new IHttpResponseHandler() {
                           
                            @Execution(Execution.NONTHREADED)
                            public void onResponse(IHttpResponse response) throws IOException {
                                exchange.send(response);
                            }
                           
                            @Execution(Execution.NONTHREADED)
                            public void onException(IOException ioe) throws IOException {
                                exchange.sendError(ioe);
                            }
                        };
                       
                        BodyDataSink ds = httpClient.send(requestHeader, respHdl);
                        ds.setFlushmode(FlushMode.ASYNC);
                       
                        boolean isForwarding = inMemorySink.forwardTo(new BodyDataSinkAdapter(ds));
                        if (!isForwarding) {
                            exchange.sendError(ioe);
                        }
                       
                    } catch (IOException ioe) {
                        exchange.sendError(ioe);
                    }
                }
            };
                   
            httpClient.getWorkerpool().execute(task);
        }
  }
 
 
 
  private static final class InMemorySink implements ISink {
       
      /*
       * TODO: improve synchronization (make it less expensive)
       */
     
        private List<ByteBuffer> buffers = new ArrayList<ByteBuffer>();
        private IBodyDestroyListener destroyListener = null;
        private boolean isDestroyed = false;
        private boolean isClosed = false;
       
        private ISink forwardSink = null;
       
        public synchronized void onData(ByteBuffer data) throws IOException {
            if (forwardSink == null) {
                buffers.add(data);
            } else {
                forwardSink.onData(data);
            }
        }
       
        public synchronized void close() throws IOException {
            if (forwardSink == null) {
                isClosed = true;
            } else {
                forwardSink.close();
            }
        }
       
        public synchronized void destroy() {
            if (forwardSink == null) {
                isDestroyed = true;
            } else {
                forwardSink.destroy();
            }
        }
       
        public String getId() {
            return "<unset>";
        }
               
        public synchronized void setDestroyListener(IBodyDestroyListener destroyListener) {
            if (forwardSink == null) {
                this.destroyListener = destroyListener;
            } else {
                forwardSink.setDestroyListener(destroyListener);
            }
        }
       
       
        public synchronized boolean forwardTo(ISink sink) throws IOException {
            if (isDestroyed) {
                return false;
            }
           
            forwardSink = sink;
           
            if (destroyListener != null) {
                sink.setDestroyListener(destroyListener);
            }
           
               
            for (ByteBuffer buffer : buffers) {
                onData(buffer);
            }
               
            if (isClosed) {
                close();
            }
           
            return true;
        }
    }
 
 
 
    /**
     * BodyRetryResponseHandler is unsynchronized by config. See HttpUtils$RequestHandlerInfo
     */
    private final class BodylessRetryResponseHandler implements IHttpResponseHandler {

        private final AtomicBoolean isHandled = new AtomicBoolean(false);
        private final IHttpExchange exchange;
        private final IHttpRequestHeader requestHeader;
        private Integer countTrials;
       
       
        BodylessRetryResponseHandler(IHttpExchange exchange, IHttpRequestHeader requestHeader) {
            this.exchange = exchange;
            this.requestHeader = requestHeader;

            countTrials = (Integer) requestHeader.getAttribute(RETRY_COUNT_KEY);
            if (countTrials == null) {
                countTrials = 0;
            }
        }
       
       
        public void onResponse(IHttpResponse response) throws IOException {
            if (!isHandled.getAndSet(true)) {
                exchange.send(response);
            }
        }
       
        public void onException(IOException ioe) throws IOException {
           
            if (!isHandled.getAndSet(true)) {
                if (isRetryable(ioe) && (countTrials < httpClient.getMaxRetries())) {
                    if (LOG.isLoggable(Level.FINE)) {
                        LOG.fine("I/O exception  caught when processing request " + ioe.toString() + " retrying request");
                    }
                   
                    countRetries++;
                    sendRetry();
                   
                } else {
                    exchange.sendError(ioe);
                }
            }
        }

       
        private void sendRetry() throws IOException {
           
       
            Runnable task = new Runnable() {
               
                public void run() {
                   
                    try {
                        requestHeader.setAttribute(RETRY_COUNT_KEY, ++countTrials);
                        requestHeader.setAttribute(CookieHandler.COOKIE_WARNING_KEY, false);
                           
                        IHttpResponseHandler respHdl = new IHttpResponseHandler() {
                           
                            @Execution(Execution.NONTHREADED)
                            public void onResponse(IHttpResponse response) throws IOException {
                                exchange.send(response);
                            }
                           
                            @Execution(Execution.NONTHREADED)
                            public void onException(IOException ioe) throws IOException {
                                exchange.sendError(ioe);
                            }
                        };
                       
                        httpClient.send(new HttpRequest(requestHeader), respHdl);
                    } catch (IOException ioe) {
                        exchange.sendError(ioe);
                    }
                }
            };
         
            httpClient.getWorkerpool().execute(task);
        }
    } 
}
TOP

Related Classes of org.xlightweb.client.RetryHandler$InMemorySink

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.