com.taobao.gecko.service.impl.DefaultRemotingClient.java Source code

Java tutorial

Introduction

Here is the source code for com.taobao.gecko.service.impl.DefaultRemotingClient.java

Source

/*
 * (C) 2007-2012 Alibaba Group Holding Limited.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * 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.taobao.gecko.service.impl;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.taobao.gecko.core.command.Constants;
import com.taobao.gecko.core.config.Configuration;
import com.taobao.gecko.core.extension.ConnectFailListener;
import com.taobao.gecko.core.extension.GeckoTCPConnectorController;
import com.taobao.gecko.core.nio.NioSession;
import com.taobao.gecko.core.nio.impl.SocketChannelController;
import com.taobao.gecko.core.nio.impl.TimerRef;
import com.taobao.gecko.core.util.RemotingUtils;
import com.taobao.gecko.core.util.StringUtils;
import com.taobao.gecko.service.Connection;
import com.taobao.gecko.service.RemotingClient;
import com.taobao.gecko.service.config.ClientConfig;
import com.taobao.gecko.service.exception.NotifyRemotingException;

/**
 * 
 * RemotingClient
 * 
 * @author boyan
 * 
 * @since 1.0, 2009-12-16 03:42:14
 */

public class DefaultRemotingClient extends BaseRemotingController implements RemotingClient, ConnectFailListener {

    private ReconnectManager reconnectManager;

    private static final Log log = LogFactory.getLog(DefaultRemotingClient.class);

    public DefaultRemotingClient(final ClientConfig clientConfig) {
        super(clientConfig);
        this.config = clientConfig;
        // Integer.MAX_VALUE
        this.setAttribute(Constants.DEFAULT_GROUP, Constants.CONNECTION_COUNT_ATTR, Integer.MAX_VALUE);

    }

    @Override
    public void close(final String group, final boolean allowReconnect) throws NotifyRemotingException {
        if (!this.started) {
            throw new NotifyRemotingException("The controller has been stopped");
        }
        if (group == null) {
            throw new IllegalArgumentException("null group");
        }
        if (!allowReconnect) {
            // 
            this.reconnectManager.cancelReconnectGroup(group);
            // 
            this.attributes.remove(group);
        }
        final List<Connection> connections = this.remotingContext.getConnectionsByGroup(group);
        if (connections != null) {
            for (final Connection conn : connections) {
                if (conn.isConnected()) {
                    conn.close(allowReconnect);
                }
            }
        }

    }

    @Override
    public void awaitClosed(String url, long time) throws InterruptedException, TimeoutException {
        if (time <= 0) {
            throw new IllegalArgumentException("Invalid timeout");
        }
        this.remotingContext.awaitGroupConnectionsEmpty(url, time);
    }

    @Override
    public void awaitClosed(String url) throws InterruptedException, TimeoutException {
        this.awaitClosed(url, 5000);
    }

    @Override
    public void connect(String url, String targetGroup, int connCount) throws NotifyRemotingException {
        if (connCount <= 0) {
            throw new IllegalArgumentException("0");
        }
        url = url.trim();
        if (this.isGroupConnectPending(targetGroup)) {
            return;
        }

        final InetSocketAddress remoteAddress = this.getSocketAddrFromGroup(url);

        final Set<String> groupSet = new HashSet<String>();
        groupSet.add(targetGroup);
        this.reconnectManager.removeCanceledGroup(targetGroup);
        // 
        if (this.setAttributeIfAbsent(targetGroup, Constants.CONNECTION_COUNT_ATTR, connCount) != null) {
            return;
        }
        // 
        if (this.setAttributeIfAbsent(targetGroup, Constants.GROUP_CONNECTION_READY_LOCK, new Object()) != null) {
            return;
        }
        for (int i = 0; i < connCount; i++) {
            try {
                final TimerRef timerRef = new TimerRef(((ClientConfig) this.config).getConnectTimeout(), null);
                final Future<NioSession> future = ((GeckoTCPConnectorController) this.controller)
                        .connect(remoteAddress, groupSet, remoteAddress, timerRef);
                final CheckConnectFutureRunner runnable = new CheckConnectFutureRunner(future, remoteAddress,
                        groupSet, this);
                timerRef.setRunnable(runnable);
                this.insertTimer(timerRef);
            } catch (final Exception e) {
                log.error("" + RemotingUtils.getAddrString(remoteAddress) + ",", e);
                this.reconnectManager.addReconnectTask(new ReconnectTask(groupSet, remoteAddress));
            }
        }

    }

    @Override
    public void connect(String url, String targetGroup) throws NotifyRemotingException {
        this.connect(url, targetGroup, 1);
    }

    /**
     * 
     */
    @Override
    public synchronized void connect(String group, final int connCount) throws NotifyRemotingException {
        this.connect(group, group, connCount);
    }

    /**
     * 
     * 
     * @param group
     * @return
     */
    private boolean isGroupConnectPending(final String group) {
        final Object readyLock = this.getAttribute(group, Constants.GROUP_CONNECTION_READY_LOCK);
        final Object attribute = this.getAttribute(group, Constants.CONNECTION_COUNT_ATTR);
        return readyLock != null && attribute != null;
    }

    public ReconnectManager getReconnectManager() {
        return this.reconnectManager;
    }

    /**
     * 
     * 
     * 
     * 
     * @author boyan
     * 
     * @since 1.0, 2009-12-23 01:49:41
     */
    public static final class CheckConnectFutureRunner implements Runnable {
        final Future<NioSession> future;
        final InetSocketAddress remoteAddress;
        final Set<String> groupSet;
        final DefaultRemotingClient remotingClient;

        public CheckConnectFutureRunner(final Future<NioSession> future, final InetSocketAddress remoteAddress,
                final Set<String> groupSet, final DefaultRemotingClient remotingClient) {
            super();
            this.future = future;
            this.remoteAddress = remoteAddress;
            this.groupSet = groupSet;
            this.remotingClient = remotingClient;
        }

        @Override
        public void run() {
            try {
                if (!this.future.isDone() && this.future.get(10, TimeUnit.MILLISECONDS) == null) {
                    this.addReconnectTask();
                }
            } catch (final Exception e) {
                log.error("" + this.remoteAddress + "", e);
                this.addReconnectTask();
            }
        }

        private void addReconnectTask() {
            final ReconnectManager reconnectManager = this.remotingClient.getReconnectManager();
            reconnectManager.addReconnectTask(new ReconnectTask(this.groupSet, this.remoteAddress));
        }

    }

    private InetSocketAddress getSocketAddrFromGroup(String group) throws NotifyRemotingException {
        if (group == null) {
            throw new IllegalArgumentException("Null group");
        }
        group = group.trim();
        if (!group.startsWith(this.config.getWireFormatType().getScheme())) {
            throw new NotifyRemotingException(
                    "Group" + this.config.getWireFormatType().getScheme() + "");
        }
        try {
            final URI uri = new URI(group);
            return new InetSocketAddress(uri.getHost(), uri.getPort());
        } catch (final Exception e) {
            throw new NotifyRemotingException("uri,url=" + group, e);
        }
    }

    @Override
    public void connect(final String group) throws NotifyRemotingException {
        this.connect(group, 1);

    }

    @Override
    public void awaitReadyInterrupt(final String group) throws NotifyRemotingException, InterruptedException {
        final Object readyLock = this.getAttribute(group, Constants.GROUP_CONNECTION_READY_LOCK);
        final Object attribute = this.getAttribute(group, Constants.CONNECTION_COUNT_ATTR);
        if (readyLock == null || attribute == null) {
            throw new IllegalStateException("connect");
        }
        final long defaultConnectTimeout = ((ClientConfig) this.config).getConnectTimeout();
        this.awaitReadyInterrupt(group, defaultConnectTimeout * (Integer) attribute);
    }

    @Override
    public void awaitReadyInterrupt(final String group, final long time)
            throws NotifyRemotingException, InterruptedException {
        if (StringUtils.isBlank(group)) {
            throw new IllegalArgumentException("Blank group");
        }
        // 
        final Object readyLock = this.getAttribute(group, Constants.GROUP_CONNECTION_READY_LOCK);
        final Object attribute = this.getAttribute(group, Constants.CONNECTION_COUNT_ATTR);
        if (readyLock == null || attribute == null) {
            throw new IllegalStateException("connect");
        } else {
            final int maxConnCount = (Integer) attribute;
            long totalTime = 0;
            synchronized (readyLock) {
                while (this.getConnectionCount(group) != maxConnCount) {
                    final long start = System.currentTimeMillis();
                    readyLock.wait(1000);
                    totalTime += System.currentTimeMillis() - start;
                    if (totalTime >= time) {
                        throw new NotifyRemotingException("" + time + "");
                    }
                }
            }
        }

    }

    @Override
    public InetSocketAddress getRemoteAddress(final String group) {
        if (this.remotingContext == null) {
            return null;
        }
        final List<Connection> connections = this.remotingContext.getConnectionsByGroup(group);
        if (connections == null || connections.size() == 0) {
            return null;
        }
        for (final Connection conn : connections) {
            if (conn.getRemoteSocketAddress() != null) {
                return conn.getRemoteSocketAddress();
            }
        }
        return null;
    }

    @Override
    public String getRemoteAddressString(final String group) {
        return RemotingUtils.getAddrString(this.getRemoteAddress(group));
    }

    @Override
    public boolean isConnected(final String group) {
        if (this.remotingContext == null) {
            return false;
        }
        final List<Connection> connections = this.remotingContext.getConnectionsByGroup(group);
        if (connections == null || connections.size() == 0) {
            return false;
        }
        for (final Connection conn : connections) {
            if (conn.isConnected()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void setClientConfig(final ClientConfig clientConfig) {
        if (this.controller != null && this.controller.isStarted()) {
            throw new IllegalStateException("RemotingClient");
        }
        this.config = clientConfig;
    }

    @Override
    protected void doStart() throws NotifyRemotingException {
        this.startController();
        this.startReconnectManager();
    }

    private void startReconnectManager() {
        // 
        this.reconnectManager = new ReconnectManager((GeckoTCPConnectorController) this.controller,
                (ClientConfig) this.config, this);
        ((GeckoHandler) this.controller.getHandler()).setReconnectManager(this.reconnectManager);
        this.reconnectManager.start();
    }

    private void startController() throws NotifyRemotingException {
        try {
            this.controller.start();
        } catch (final IOException e) {
            throw new NotifyRemotingException("", e);
        }
    }

    @Override
    protected void doStop() throws NotifyRemotingException {
        this.stopReconnectManager();
        this.closeAllConnection();
    }

    private void closeAllConnection() throws NotifyRemotingException {
        // 
        final List<Connection> connections = this.remotingContext.getConnectionsByGroup(Constants.DEFAULT_GROUP);
        if (connections != null) {
            for (final Connection conn : connections) {
                ((DefaultConnection) conn).setReady(true);// 
                conn.close(false);
            }
        }
    }

    private void stopReconnectManager() {
        this.reconnectManager.stop();
    }

    /**
     * 
     */
    @Override
    @SuppressWarnings("unchecked")
    public void onConnectFail(final Object... args) {
        if (args.length >= 2) {
            final Set<String> groupSet = (Set<String>) args[0];
            final InetSocketAddress remoteAddr = (InetSocketAddress) args[1];
            this.reconnectManager.addReconnectTask(new ReconnectTask(groupSet, remoteAddr));
            if (args.length >= 3) {
                final TimerRef timerRef = (TimerRef) args[2];
                timerRef.cancel();
            }
        }

    }

    @Override
    protected SocketChannelController initController(final Configuration conf) {
        final GeckoTCPConnectorController notifyTCPConnectorController = new GeckoTCPConnectorController(conf);
        // 
        notifyTCPConnectorController.setConnectFailListener(this);
        return notifyTCPConnectorController;
    }

}