All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.taobao.api.internal.toplink.endpoint.EndpointChannelHandler Maven / Gradle / Ivy

package com.taobao.api.internal.toplink.endpoint;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import com.taobao.api.internal.toplink.BufferManager;
import com.taobao.api.internal.toplink.DefaultLoggerFactory;
import com.taobao.api.internal.toplink.LinkException;
import com.taobao.api.internal.toplink.Logger;
import com.taobao.api.internal.toplink.LoggerFactory;
import com.taobao.api.internal.toplink.Text;
import com.taobao.api.internal.toplink.channel.ChannelContext;
import com.taobao.api.internal.toplink.channel.ChannelException;
import com.taobao.api.internal.toplink.channel.ChannelSender;
import com.taobao.api.internal.toplink.channel.SimpleChannelHandler;
import com.taobao.api.internal.toplink.channel.ChannelSender.SendHandler;
import com.taobao.api.internal.toplink.schedule.Scheduler;

// make timing
public class EndpointChannelHandler extends SimpleChannelHandler {
	private Logger logger;
	private Endpoint endpoint;
	private AtomicInteger flag;
	private Map callbackByFlag;
	// all connect in/out endpoints
	private Map idByToken;
	private Scheduler scheduler;
	private StateHandler stateHandler;

	public EndpointChannelHandler() {
		this(DefaultLoggerFactory.getDefault());
	}

	public EndpointChannelHandler(LoggerFactory loggerFactory) {
		this.logger = loggerFactory.create(this);
		this.flag = new AtomicInteger();
		this.callbackByFlag = new ConcurrentHashMap();
		this.idByToken = new ConcurrentHashMap();
	}

	protected void setEndpoint(Endpoint endpoint) {
		this.endpoint = endpoint;
	}

	public void setScheduler(Scheduler scheduler) {
		this.scheduler = scheduler;
	}

	public void setStateHandler(StateHandler stateHandler) {
		this.stateHandler = stateHandler;
	}

	protected final void pending(Message msg, ChannelSender sender) throws ChannelException {
		this.pending(msg, sender, null);
	}

	// all send in Endpoint module must call here
	protected final void pending(Message msg, ChannelSender sender, SendCallback callback) throws ChannelException {
		if (callback != null) {
			callback.flag = msg.flag = this.flag.incrementAndGet();
			this.callbackByFlag.put(msg.flag, callback);
		}
		ByteBuffer buffer = BufferManager.getBuffer();
		MessageIO.writeMessage(buffer, msg);
		sender.send(buffer, new InnerSendHandler(buffer));
	}

	public void cancel(SendCallback callback) {
		this.callbackByFlag.remove(callback.flag);
	}

	@Override
	public void onConnect(ChannelContext context) throws Exception {
	}

	@Override
	public void onError(ChannelContext context) throws Exception {
		this.logger.error(Text.E_CHANNEL_ERROR, context.getError());
	}

	@SuppressWarnings("unchecked")
	public final void onMessage(ChannelContext context) throws Exception {
		Object msg = context.getMessage();

		if (msg instanceof ByteBuffer) {
			this.onMessage(context, (ByteBuffer) msg);
			return;
		}

		for (ByteBuffer buffer : (List) msg)
			this.onMessage(context, buffer);
	}

	private void onMessage(ChannelContext context, ByteBuffer buffer) throws LinkException {
		Message msg = MessageIO.readMessage(buffer);

		if (msg.messageType == MessageType.CONNECT) {
			this.handleConnect(context, msg);
			return;
		}

		SendCallback callback = this.callbackByFlag.remove(msg.flag);

		if (msg.messageType == MessageType.CONNECTACK) {
			this.handleConnectAck(callback, msg);
			return;
		}

		Identity msgFrom = msg.token != null ? this.idByToken.get(msg.token) : null;
		// must CONNECT/CONNECTACK for got token before SEND
		if (msgFrom == null) {
			LinkException error = new LinkException(String.format(
					"[%s] %s: v=%s, type=%s, token=%s, flag=%s, code=%s, phase=%s, content=%s",
					this.endpoint.getIdentity(),
					Text.E_UNKNOWN_MSG_FROM,
					msg.protocolVersion,
					msg.messageType,
					msg.token,
					msg.flag,
					msg.statusCode,
					msg.statusPhase,
					msg.content));
			if (callback == null)
				throw error;
			callback.setError(error);
			return;
		}

		// raise callback of client
		if (callback != null) {
			this.handleCallback(callback, msg, msgFrom);
			return;
		} else if (this.isError(msg)) {
			this.logger.error(Text.E_GOT_ERROR, msgFrom, msg.statusCode, msg.statusPhase);
			return;
		}

		if (msg.messageType != MessageType.SEND &&
				msg.messageType != MessageType.SENDACK)
			throw new LinkException(String.format(Text.E_UNKNOWN_MSG_TYPE, msg.messageType));

		// raise onMessage for async receive mode
		if (this.endpoint.getMessageHandler() == null)
			return;
		// exec directly
		if (this.scheduler == null) {
			this.internalOnMessage(context, msg, msgFrom);
			return;
		}
		// dispatch
		this.scheduler.schedule(msgFrom, this.createTask(context, msg, msgFrom));
	}

	private Runnable createTask(final ChannelContext context, final Message message, final Identity messageFrom) {
		return new MessageScheduleTask(message) {
			public void run() {
				try {
					internalOnMessage(context, message, messageFrom);
				} catch (LinkException e) {
					logger.error(e);
				}
			}
		};
	}

	private void internalOnMessage(ChannelContext context, Message message, Identity messageFrom) throws LinkException {
		if (message.messageType == MessageType.SENDACK) {
			this.endpoint.getMessageHandler().onAckMessage(
					new EndpointBaseContext(context, messageFrom, message));
			return;
		}

		EndpointContext endpointContext = new
				EndpointContext(context, messageFrom, message, this.endpoint);
		try {
			this.endpoint.getMessageHandler().onMessage(endpointContext);
		} catch (Exception e) {
			this.logger.error(e);
			// onMessage error should be reply to client
			if (e instanceof LinkException)
				endpointContext.error(
						((LinkException) e).getErrorCode(),
						this.parseStatusPhase(((LinkException) e)));
			else
				endpointContext.error(0, this.parseStatusPhase(e));
		}
	}

	// deal with connect-in message from endpoint,
	// parse identity send from endpoint and assign it a token,
	// token just used for routing message-from, not auth
	private void handleConnect(ChannelContext context, Message connectMessage) throws ChannelException {
		Message ack = this.createConnectAckMessage(connectMessage);
		ack.messageType = MessageType.CONNECTACK;
		try {
			Identity id = this.endpoint.getIdentity().parse(connectMessage.content);
			EndpointProxy proxy = this.endpoint.getEndpoint(id);
			// set connect-in version as the sender protocol version
			ChannelSenderWrapper senderWrapper =
					new ChannelSenderWrapper(context.getSender(), connectMessage.protocolVersion);
			proxy.add(senderWrapper);
			if (proxy.getToken() == null) {
				synchronized (proxy) {
					if (proxy.getToken() == null)
						// uuid for token? or get from id?
						proxy.setToken(UUID.randomUUID().toString());
				}
			}
			ack.token = proxy.getToken();
			this.idByToken.put(proxy.getToken(), id);

			if (this.stateHandler != null)
				this.stateHandler.onConnect(proxy, senderWrapper, id);

			this.logger.info(Text.E_ACCEPT, this.endpoint.getIdentity(), id, proxy.getToken());
		} catch (LinkException e) {
			ack.statusCode = e.getErrorCode();
			ack.statusPhase = this.parseStatusPhase(e);
			this.logger.error(Text.E_REFUSE, e);
		}
		final ByteBuffer buffer = BufferManager.getBuffer();
		MessageIO.writeMessage(buffer, ack);
		context.reply(buffer, new InnerSendHandler(buffer));
	}

	private void handleConnectAck(SendCallback callback, Message msg) throws LinkException {
		if (callback == null)
			throw new LinkException(Text.E_NO_CALLBACK);
		if (this.isError(msg))
			callback.setError(new LinkException(msg.statusCode, msg.statusPhase));
		else if (msg.token == null) {
			callback.setError(new LinkException(Text.E_NULL_TOKEN));
		} else {
			callback.setComplete();
			// set token for proxy for sending message next time
			callback.getTarget().setToken(msg.token);
			// store token from target endpoint for receiving it's message
			// next time
			this.idByToken.put(msg.token, callback.getTarget().getIdentity());
			this.logger.info(Text.E_CONNECT_SUCCESS, callback.getTarget().getIdentity(), msg.token);
		}
	}

	private void handleCallback(SendCallback callback, Message msg, Identity msgFrom) {
		if (!callback.getTarget().getIdentity().equals(msgFrom)) {
			this.logger.warn(
					Text.E_IDENTITY_NOT_MATCH_WITH_CALLBACK,
					msgFrom, callback.getTarget().getIdentity());
			return;
		}
		if (this.isError(msg))
			callback.setError(new LinkException(msg.statusCode, msg.statusPhase));
		else
			callback.setReturn(msg.content);
	}

	private boolean isError(Message msg) {
		return msg.statusCode > 0 ||
				(msg.statusPhase != null && msg.statusPhase != "");
	}

	private Message createConnectAckMessage(Message connectMessage) {
		Message msg = new Message();
		// version match with message from
		msg.protocolVersion = connectMessage.protocolVersion;
		msg.flag = connectMessage.flag;
		msg.token = connectMessage.token;
		return msg;
	}

	private String parseStatusPhase(Exception e) {
		return e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage();
	}

	private String parseStatusPhase(LinkException e) {
		return e.getMessage() == null && e.getErrorCode() <= 0 ? Text.E_UNKNOWN_ERROR : e.getMessage();
	}

	class InnerSendHandler implements SendHandler {
		private ByteBuffer buffer;

		public InnerSendHandler(ByteBuffer buffer) {
			this.buffer = buffer;
		}

		public void onSendComplete(boolean success) {
			BufferManager.returnBuffer(this.buffer);
		}

	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy