/* * (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. * Authors: * wuhua <wq163@163.com> , boyan <killme2008@gmail.com> */ package com.taobao.metamorphosis.client; import java.net.InetSocketAddress; import java.net.URI; import java.nio.channels.FileChannel; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import com.taobao.gecko.core.buffer.IoBuffer; import com.taobao.gecko.core.command.RequestCommand; import com.taobao.gecko.core.command.ResponseCommand; import com.taobao.gecko.core.command.ResponseStatus; import com.taobao.gecko.core.nio.impl.TimerRef; import com.taobao.gecko.service.Connection; import com.taobao.gecko.service.ConnectionLifeCycleListener; import com.taobao.gecko.service.ConnectionSelector; import com.taobao.gecko.service.GroupAllConnectionCallBackListener; import com.taobao.gecko.service.MultiGroupCallBackListener; import com.taobao.gecko.service.RemotingClient; import com.taobao.gecko.service.RemotingContext; import com.taobao.gecko.service.RequestProcessor; import com.taobao.gecko.service.SingleRequestCallBackListener; import com.taobao.gecko.service.config.ClientConfig; import com.taobao.gecko.service.exception.NotifyRemotingException; import com.taobao.metamorphosis.network.BooleanCommand; import com.taobao.metamorphosis.network.RemotingUtils; /** * RemotingClient��װ��������ӵĽ����͹رյļ��� * * @author boyan * @Date 2011-4-27 * */ public class RemotingClientWrapper implements RemotingClient { private final RemotingClient remotingClient; private final ConcurrentHashMap<String/* url */, Set<Object>/* references */> refsCache = new ConcurrentHashMap<String, Set<Object>>(); public RemotingClientWrapper(final RemotingClient remotingClient) { super(); this.remotingClient = remotingClient; } @Override public void connect(String url, String targetGroup, int connCount) throws NotifyRemotingException { this.remotingClient.connect(url, targetGroup, connCount); } @Override public void connect(String url, String targetGroup) throws NotifyRemotingException { this.remotingClient.connect(url, targetGroup); } @Override public void addAllProcessors( final Map<Class<? extends RequestCommand>, RequestProcessor<? extends RequestCommand>> map) { this.remotingClient.addAllProcessors(map); } @Override public void addConnectionLifeCycleListener(final ConnectionLifeCycleListener connectionLifeCycleListener) { this.remotingClient.addConnectionLifeCycleListener(connectionLifeCycleListener); } @Override public void awaitReadyInterrupt(final String url, final long time) throws NotifyRemotingException, InterruptedException { this.remotingClient.awaitReadyInterrupt(url, time); } @Override public void awaitReadyInterrupt(final String url) throws NotifyRemotingException, InterruptedException { this.remotingClient.awaitReadyInterrupt(url); } @Override public void connect(String url) throws NotifyRemotingException { this.connect(url, 1); } @Override public void connect(String url, int connCount) throws NotifyRemotingException { this.connectWithRef(url, connCount, null); } @Override public void close(String url, boolean allowReconnect) throws NotifyRemotingException { this.closeWithRef(url, null, allowReconnect); } public synchronized void closeWithRef(final String url, Object ref, final boolean allowReconnect) throws NotifyRemotingException { final Set<Object> refs = this.getReferences(url); if (refs != null) { refs.remove(ref); if (refs.isEmpty() || this.isOnlyMe(refs)) { int times = 0; while (times++ < 3) { try { this.remotingClient.close(url, allowReconnect); this.remotingClient.awaitClosed(url, 5000); break; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (TimeoutException e) { // ignore } } } } } @Override public void awaitClosed(String arg0, long arg1) throws InterruptedException, TimeoutException { this.remotingClient.awaitClosed(arg0, arg1); } @Override public void awaitClosed(String arg0) throws InterruptedException, TimeoutException { this.remotingClient.awaitClosed(arg0); } private boolean isOnlyMe(final Set<Object> refs) { return refs.size() == 1 && refs.contains(this); } /** * Whether to enable loopback connection for metaq client, default is false. */ private static final boolean ENABLE_LOOPBACK_CONNECTION = Boolean.valueOf(System.getProperty( "metaq.client.loopback.connection.enable", "false")); public synchronized void connectWithRef(String url, final int connCount, Object ref) throws NotifyRemotingException { if (ENABLE_LOOPBACK_CONNECTION) { url = this.tryGetLoopbackURL(url); } final Set<Object> refs = this.getReferences(url); this.remotingClient.connect(url, connCount); refs.add(ref); } static String tryGetLoopbackURL(String url) { try { URI uri = new URI(url); String localHostName = RemotingUtils.getLocalHost(); if (uri.getHost().equals(localHostName)) { // replace url with loopback connection url = uri.getScheme() + "://localhost:" + uri.getPort(); } } catch (Exception e) { // ignore } return url; } private Set<Object> getReferences(final String url) { Set<Object> refs = this.refsCache.get(url); if (refs == null) { refs = new HashSet<Object>(); final Set<Object> oldRefs = this.refsCache.putIfAbsent(url, refs); if (oldRefs != null) { refs = oldRefs; } } return refs; } public void connectWithRef(final String url, Object ref) throws NotifyRemotingException { this.connectWithRef(url, 1, ref); } @Override public Object getAttribute(final String group, final String key) { return this.remotingClient.getAttribute(group, key); } @Override public int getConnectionCount(final String group) { return this.remotingClient.getConnectionCount(group); } @Override public Set<String> getGroupSet() { return this.remotingClient.getGroupSet(); } @Override public RequestProcessor<? extends RequestCommand> getProcessor(final Class<? extends RequestCommand> clazz) { return this.remotingClient.getProcessor(clazz); } @Override public InetSocketAddress getRemoteAddress(final String url) { return this.remotingClient.getRemoteAddress(url); } @Override public String getRemoteAddressString(final String url) { return this.remotingClient.getRemoteAddressString(url); } @Override public RemotingContext getRemotingContext() { return this.remotingClient.getRemotingContext(); } @Override public void insertTimer(final TimerRef timerRef) { this.remotingClient.insertTimer(timerRef); } @Override public ResponseCommand invokeToGroup(final String group, final RequestCommand command, final long time, final TimeUnit timeUnit) throws InterruptedException, TimeoutException, NotifyRemotingException { ResponseCommand resp = this.remotingClient.invokeToGroup(group, command, time, timeUnit); if (resp.getResponseStatus() == ResponseStatus.ERROR_COMM) { BooleanCommand booleanCommand = (BooleanCommand) resp; // It's ugly,but it work right now. if (booleanCommand.getErrorMsg().contains("�޿�������")) { // try to connect it. this.connectWithRef(group, this); } } return resp; } @Override public ResponseCommand invokeToGroup(final String group, final RequestCommand command) throws InterruptedException, TimeoutException, NotifyRemotingException { return this.remotingClient.invokeToGroup(group, command); } @Override public Map<Connection, ResponseCommand> invokeToGroupAllConnections(final String group, final RequestCommand command, final long time, final TimeUnit timeUnit) throws InterruptedException, NotifyRemotingException { return this.remotingClient.invokeToGroupAllConnections(group, command, time, timeUnit); } @Override public Map<Connection, ResponseCommand> invokeToGroupAllConnections(final String group, final RequestCommand command) throws InterruptedException, NotifyRemotingException { return this.remotingClient.invokeToGroupAllConnections(group, command); } @Override public boolean isConnected(final String url) { return this.remotingClient.isConnected(url); } @Override public boolean isStarted() { return this.remotingClient.isStarted(); } @Override public <T extends RequestCommand> void registerProcessor(final Class<T> commandClazz, final RequestProcessor<T> processor) { this.remotingClient.registerProcessor(commandClazz, processor); } @Override public Object removeAttribute(final String group, final String key) { return this.remotingClient.removeAttribute(group, key); } @Override public void removeConnectionLifeCycleListener(final ConnectionLifeCycleListener connectionLifeCycleListener) { this.remotingClient.removeConnectionLifeCycleListener(connectionLifeCycleListener); } @Override public Connection selectConnectionForGroup(final String group, final ConnectionSelector connectionSelector, final RequestCommand request) throws NotifyRemotingException { return this.remotingClient.selectConnectionForGroup(group, connectionSelector, request); } @Override public void sendToAllConnections(final RequestCommand command) throws NotifyRemotingException { this.remotingClient.sendToAllConnections(command); } @Override public void sendToGroup(final String group, final RequestCommand command, final SingleRequestCallBackListener listener, final long time, final TimeUnit timeunut) throws NotifyRemotingException { this.remotingClient.sendToGroup(group, command, listener, time, timeunut); } @Override public void sendToGroup(final String group, final RequestCommand command, final SingleRequestCallBackListener listener) throws NotifyRemotingException { this.remotingClient.sendToGroup(group, command, listener); } @Override public void sendToGroup(final String group, final RequestCommand command) throws NotifyRemotingException { this.remotingClient.sendToGroup(group, command); } @Override public void sendToGroupAllConnections(final String group, final RequestCommand command, final GroupAllConnectionCallBackListener listener, final long time, final TimeUnit timeUnit) throws NotifyRemotingException { this.remotingClient.sendToGroupAllConnections(group, command, listener, time, timeUnit); } @Override public void sendToGroupAllConnections(final String group, final RequestCommand command, final GroupAllConnectionCallBackListener listener) throws NotifyRemotingException { this.remotingClient.sendToGroupAllConnections(group, command, listener); } @Override public void sendToGroupAllConnections(final String group, final RequestCommand command) throws NotifyRemotingException { this.remotingClient.sendToGroupAllConnections(group, command); } @Override public void sendToGroups(final Map<String, RequestCommand> groupObjects, final MultiGroupCallBackListener listener, final long timeout, final TimeUnit timeUnit, final Object... args) throws NotifyRemotingException { this.remotingClient.sendToGroups(groupObjects, listener, timeout, timeUnit, args); } @Override public void sendToGroups(final Map<String, RequestCommand> groupObjects) throws NotifyRemotingException { this.remotingClient.sendToGroups(groupObjects); } @Override public void setAttribute(final String group, final String key, final Object value) { this.remotingClient.setAttribute(group, key, value); } @Override public Object setAttributeIfAbsent(final String group, final String key, final Object value) { return this.remotingClient.setAttributeIfAbsent(group, key, value); } @Override public void setClientConfig(final ClientConfig clientConfig) { this.remotingClient.setClientConfig(clientConfig); } @Override public void setConnectionSelector(final ConnectionSelector selector) { this.remotingClient.setConnectionSelector(selector); } @Override public void start() throws NotifyRemotingException { this.remotingClient.start(); } @Override public void stop() throws NotifyRemotingException { this.remotingClient.stop(); this.refsCache.clear(); } @Override public RequestProcessor<? extends RequestCommand> unreigsterProcessor(final Class<? extends RequestCommand> clazz) { return this.remotingClient.unreigsterProcessor(clazz); } @Override public void transferToGroup(String group, IoBuffer head, IoBuffer tail, FileChannel channel, long position, long size, Integer opaque, SingleRequestCallBackListener listener, long time, TimeUnit unit) throws NotifyRemotingException { this.remotingClient.transferToGroup(group, head, tail, channel, position, size, opaque, listener, time, unit); } @Override public void transferToGroup(String group, IoBuffer head, IoBuffer tail, FileChannel channel, long position, long size) throws NotifyRemotingException { this.remotingClient.transferToGroup(group, head, tail, channel, position, size); } }