Package com.googlecode.richrest.client.transferrer

Source Code of com.googlecode.richrest.client.transferrer.AbstractTransferrer$Abortor

package com.googlecode.richrest.client.transferrer;

import java.io.IOException;
import java.io.Serializable;
import java.net.Socket;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import com.googlecode.richrest.client.Abortable;
import com.googlecode.richrest.client.Client;
import com.googlecode.richrest.client.Transfer;
import com.googlecode.richrest.client.Transferrer;
import com.googlecode.richrest.client.event.NetworkEvent;
import com.googlecode.richrest.client.event.NetworkListener;
import com.googlecode.richrest.client.event.NetworkPublisher;
import com.googlecode.richrest.client.event.TransferEvent;
import com.googlecode.richrest.client.event.TransferListener;
import com.googlecode.richrest.client.event.TransferPublisher;
import com.googlecode.richrest.util.PropertiesUtils;
import com.googlecode.richrest.util.ServiceUtils;
import com.googlecode.richrest.util.logger.Logger;
import com.googlecode.richrest.util.logger.LoggerFactory;
import com.googlecode.richrest.util.serializer.stream.JavaStreamSerializer;
import com.googlecode.richrest.util.serializer.stream.StreamSerializer;

/**
* HTTP传输器基类
* @author <a href="mailto:liangfei0201@gmail.com">liangfei</a>
*/
public abstract class AbstractTransferrer<T> implements Transferrer {

  /**
   * 日志输出接口
   */
  protected final Logger logger = LoggerFactory.getLogger(getClass());

  /**
   * 序列化器配置参数名
   */
  public final static String SERIALIZER_KEY = "serializer";

  /**
   * 序列化器
   */
  protected StreamSerializer serializer;

  /**
   * 服务器主机配置参数名
   */
  public final static String HOST_ADDRESS_KEY = "host.address";

  /**
   * 服务器主机
   */
  protected String hostAddress;

  /**
   * 服务器端口配置参数名
   */
  public final static String HOST_PORT_KEY = "host.port";

  /**
   * 未知服务器端口
   */
  protected final static int UNKNOWN_HOST_PORT = -1;

  /**
   * 服务器端口
   */
  protected int hostPort = UNKNOWN_HOST_PORT;

  /**
   * 应用上下文路径配置参数名
   */
  public final static String CONTEXT_PATH_KEY = "context.path";

  /**
   * 应用上下文路径
   */
  protected String contextPath;

  /**
   * 应用上下文路径配置参数名
   */
  public final static String MAPPING_PATH_KEY = "mapping.path";

  /**
   * 应用上下文路径
   */
  protected String mappingPath;

  /**
   * Action请求后缀配置参数名
   */
  public final static String MAPPING_EXTENSION_KEY = "mapping.extension";

  /**
   * 缺省Action后缀
   */
  protected final static String DEFAULT_MAPPING_EXTENSION = "data";

  /**
   * Action请求后缀
   */
  protected String mappingExtension;

  /**
   * 连接超时时间配置参数名
   */
  public final static String CONNECTION_TIMEOUT_KEY = "connection.timeout";

  /**
   * 未知连接超时时间
   */
  protected final static int UNKNOWN_CONNECTION_TIMEOUT = -1;

  /**
   * 连接超时时间
   */
  protected int connectionTimeout = UNKNOWN_CONNECTION_TIMEOUT;

  /**
   * 套接字读取超时时间配置参数名
   */
  public final static String SOCKET_TIMEOUT_KEY = "socket.timeout";

  /**
   * 未知套接字读取时间
   */
  protected final static int UNKNOWN_SOCKET_TIMEOUT = -1;

  /**
   * 套接字读取超时时间
   */
  protected int socketTimeout = UNKNOWN_SOCKET_TIMEOUT;

  /**
   * 连接监控时间间隔配置参数名
   */
  public final static String CONNECTION_CHECK_INTERVAL_KEY = "connection.check.interval";

  /**
   * 缺省连接监控时间间隔
   */
  protected final static int DEFAULT_CONNECTION_CHECK_INTERVAL = 5000;

  /**
   * 连接监控时间间隔
   */
  protected int connectionCheckInterval = DEFAULT_CONNECTION_CHECK_INTERVAL;

  /**
   * 最大连接数配置参数名
   */
  public final static String MAX_CONNECTION_SIZE_KEY = "max.connection.size";

  /**
   * 缺省最大连接数
   */
  protected final static int DEFAULT_MAX_CONNECTION_SIZE = 20;

  /**
   * 最大连接数
   */
  protected int maxConnectionSize = DEFAULT_MAX_CONNECTION_SIZE;

  // HTTP协议前缀
  private final static String HTTP_PROTOCAL = "http://";

  // HTTP缺省端口
  private final static int HTTP_DEFAULT_PORT = 80;

  // Action请求URL前缀
  private String urlPrefix;

  // Action请求URL后缀
  private String urlSuffix;

  private Client client;

  public Client getClient() {
    return client;
  }

  public void init(Client client, Properties config) {
    if (client == null)
      throw new NullPointerException("Client == null!");
    if (config == null)
      throw new NullPointerException("Properties == null!");
    if (this.client != null)
      throw new IllegalStateException("Transporter already initialized!");
    this.client = client;

    // 读取序列化器
    serializer = PropertiesUtils.getInstanceProperty(config, SERIALIZER_KEY, StreamSerializer.class, JavaStreamSerializer.class);
    client.addPropertyInfo(SERIALIZER_KEY, "序列化策略", "暂未实现动态切换序列化策略,修改后不会生效!",
        JavaStreamSerializer.class.getName(), ServiceUtils.getServiceClassNames(StreamSerializer.class).toArray(new String[0]));

    // 读取服务器主机名
    hostAddress = PropertiesUtils.getStringProperty(config, HOST_ADDRESS_KEY, null);
    if (hostAddress == null)
      throw new NullPointerException("server.host == null, 服务器主机名不能为空!");
    client.addPropertyInfo(HOST_ADDRESS_KEY, "服务器主机名", "暂未实现动态切换服务器名,修改后不会生效!", "");

    // 读取服务器端口
    hostPort = PropertiesUtils.getIntProperty(config, HOST_PORT_KEY, UNKNOWN_HOST_PORT);
    client.addPropertyInfo(HOST_PORT_KEY,  "服务器端口", "暂未实现动态切换服务器端口,修改后不会生效!", "80");

    // 读取上下文路径
    contextPath = PropertiesUtils.getStringProperty(config, CONTEXT_PATH_KEY, "");
    client.addPropertyInfo(CONTEXT_PATH_KEY,  "应用上下文路径", "暂未实现动态切换应用上下文路径,修改后不会生效!", "/");

    // 读取Action后缀
    mappingExtension = PropertiesUtils.getStringProperty(config, MAPPING_EXTENSION_KEY, DEFAULT_MAPPING_EXTENSION);
    client.addPropertyInfo(MAPPING_EXTENSION_KEY,  "Action后缀", "暂未实现动态修改Action后缀,修改后不会生效!", "data");

    // 读取连接超时时间
    connectionTimeout = PropertiesUtils.getIntProperty(config, CONNECTION_TIMEOUT_KEY, UNKNOWN_CONNECTION_TIMEOUT);
    client.addPropertyInfo(CONNECTION_TIMEOUT_KEY,  "HTTP请求连接超时时间(ms)", "暂未实现动态修改HTTP请求连接超时时间,修改后不会生效!", "30000");

    // 读取套接字读取超时时间
    socketTimeout = PropertiesUtils.getIntProperty(config, SOCKET_TIMEOUT_KEY, UNKNOWN_SOCKET_TIMEOUT);
    client.addPropertyInfo(SOCKET_TIMEOUT_KEY,  "套接字读取超时时间(ms)", "暂未实现动态修改套接字读取超时时间,修改后不会生效!", "0");

    // 读取监控时间间隔
    connectionCheckInterval = PropertiesUtils.getIntProperty(config, CONNECTION_CHECK_INTERVAL_KEY, DEFAULT_CONNECTION_CHECK_INTERVAL);
    client.addPropertyInfo(CONNECTION_CHECK_INTERVAL_KEY,  "网络连接状态检查时间间隔(ms)", "暂未实现动态修改网络连接状态检查时间间隔,修改后不会生效!", "0");
    if (connectionCheckInterval > 0) {
      scheduler = Executors.newScheduledThreadPool(1);
      future = scheduler.scheduleWithFixedDelay(new Runnable() {
        public void run() {
          refresh();
        }
      }, connectionCheckInterval, connectionCheckInterval, TimeUnit.MILLISECONDS);
    } else {
      scheduler = null;
      future = null;
    }
    // 初始化连接状态事件
    boolean changed = refresh();
    if (! changed) // 如果未改变,也触发
      connectionPublisher.publishEvent(new NetworkEvent(AbstractTransferrer.this, isConnected()));

    // ---- 组装前缀 ----
    urlPrefix = HTTP_PROTOCAL + hostAddress;
    if (hostPort != UNKNOWN_HOST_PORT && hostPort != HTTP_DEFAULT_PORT)
      urlPrefix += ":" + hostPort;
    if (contextPath.length() > 0)
      urlPrefix += "/" + contextPath;
    urlPrefix = urlPrefix + "/";
    // ---- 组装后缀 ----
    urlSuffix = "." + mappingExtension;
  }

  public void close() throws IOException {
    try {
      transportationPublisher.clearListeners();
    } finally {
      try {
        connectionPublisher.clearListeners();
      } finally {
        try {
          if (future != null && ! future.isCancelled())
            future.cancel(true);
        } finally {
          if (scheduler != null && ! scheduler.isShutdown())
            scheduler.shutdown();
        }
      }
    }
  }

  public Serializable transfer(Transfer transmission) throws Exception {
    String uri = transmission.getActionName();
    Serializable model = transmission.getModel();
    String method = transmission.getMethod();
    Map<String, String> headers = transmission.getHeaders();
    uri = urlPrefix + uri + urlSuffix;
    Serializable result = null;
    T request = getRequest(method, uri);
    if (headers != null && headers.size() > 0) {
      for (Map.Entry<String, String> entry : headers.entrySet()) {
        setHeader(request, entry.getKey(), entry.getValue());
      }
    }
    Abortor abortor = new Abortor(request);
    addTransfer(transmission, abortor);
    try {
      result = transfer(request, uri, model);
      if (result instanceof Exception) { // 抛出异常
        Exception e = (Exception)result;
        logger.error(e.getMessage(), new RuntimeException());
        throw e;
      }
      if (result instanceof Error) { // 抛出错误
        Error e = (Error)result;
        logger.error(e.getMessage(), new RuntimeException());
        throw e;
      }
      return result;
    } catch (Throwable e) {
      result = e;
      if (e instanceof Error)
        throw (Error)e;
      if (e instanceof RuntimeException)
        throw (RuntimeException)e;
      if (e instanceof IOException)
        throw (IOException)e;
      throw new RuntimeException(e.getMessage(), e);
    } finally {
      removeTransfer(transmission, result);
    }
  }

  private final Collection<Transfer> executions = new HashSet<Transfer>();

  public boolean isTransferring() {
    synchronized (executions) {
      return ! executions.isEmpty();
    }
  }

  public Collection<Transfer> getTransfers() {
    Collection<Transfer> copies = new HashSet<Transfer>();
    synchronized (executions) {
      copies.addAll(executions);
    }
    return Collections.unmodifiableCollection(copies);
  }

  protected void addTransfer(Transfer execution, Abortable abortor) {
    transportationPublisher.publishEvent(new TransferEvent(AbstractTransferrer.this, execution));
    synchronized (executions) {
      if (executions.size() >= maxConnectionSize) {
        try {
          executions.wait();
        } catch (InterruptedException e) {
          // ignore
        }
      }
      executions.add(execution);
    }
    execution.transferring(abortor);
    transportationPublisher.publishEvent(new TransferEvent(AbstractTransferrer.this, execution));
  }

  protected void removeTransfer(Transfer execution, Serializable result) {
    synchronized (executions) {
      executions.remove(execution);
      executions.notify();
    }
    execution.transferred(result);
    transportationPublisher.publishEvent(new TransferEvent(AbstractTransferrer.this, execution));
  }

  /**
   * 获取请求信息
   * @param url Action名称
   * @return 请求信息
   * @throws IOException
   */
  protected abstract T getRequest(String method, String url) throws IOException;

  /**
   * 设置头信息
   * @param key 名称
   * @param value 值
   * @throws IOException
   */
  protected abstract void setHeader(T request, String key, String value) throws IOException;

  /**
   * 传输对象
   * @param request 请求信息
   * @param url 请求路径
   * @param model 传入对象
   * @return 传回对象
   * @throws IOException 传输出错时抛出
   */
  protected abstract Serializable transfer(T request, String url, Serializable model) throws IOException;

  /**
   * 打断连接
   * @param request 请求信息
   * @throws IOException
   */
  protected abstract void abort(T request) throws IOException;

  /**
   * 断言响应信息状态码为成功状态
   * @param code 状态码
   * @param msg 状态信息
   * @throws IOException 如果不是成功状态码时抛出
   */
  protected void assertSuccessStatusCode(int code, String msg) throws IOException {
    if (code < 200 || code > 299) // HTTP协议,200-299表示(向后兼容)成功状态码
      throw new IOException("ERROR[" + code + "] Reason: " + msg);
  }

  private TransferPublisher transportationPublisher = new TransferPublisher();

  public void addTransferListener(TransferListener listener) {
    transportationPublisher.addListener(listener);
  }

  public void removeTransferListener(TransferListener listener) {
    transportationPublisher.removeListener(listener);
  }

  private final class Abortor implements Abortable {

    private final T request;

    Abortor(T request) {
      this.request = request;
    }

    public void abort() throws IOException {
      AbstractTransferrer.this.abort(request);
    }
  }

  public boolean ping() {
    try {
      new Socket(hostAddress, hostPort).close();
      return true;
    } catch (IOException e) {
      return false;
    }
  }

  private ScheduledExecutorService scheduler;

  private ScheduledFuture<?> future;

  private boolean connected;

  private final Object connectionLock = new Object();

  public boolean isConnected() {
    synchronized (connectionLock) {
      return connected;
    }
  }

  public boolean refresh() {
    synchronized (connectionLock) {
      try {
        boolean currentConnection = ping();
        if (connected != currentConnection) {
          connected = currentConnection;
          connectionPublisher.publishEvent(new NetworkEvent(AbstractTransferrer.this, connected));
          return true;
        }
      } catch (Throwable e) {
        logger.warn(e.getMessage(), e);
      }
      return false;
    }
  }

  private NetworkPublisher connectionPublisher = new NetworkPublisher();

  public void addNetworkListener(NetworkListener listener) {
    connectionPublisher.addListener(listener);
    connectionPublisher.publishEvent(listener, new NetworkEvent(AbstractTransferrer.this, isConnected()));
  }

  public void removeNetworkListener(NetworkListener listener) {
    connectionPublisher.removeListener(listener);
  }

}
TOP

Related Classes of com.googlecode.richrest.client.transferrer.AbstractTransferrer$Abortor

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.