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

org.fisco.bcos.channel.client.Service Maven / Gradle / Ivy

There is a newer version: 2.6.6
Show newest version
package org.fisco.bcos.channel.client;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import org.fisco.bcos.channel.dto.*;
import org.fisco.bcos.channel.handler.ChannelConnections;
import org.fisco.bcos.channel.handler.GroupChannelConnectionsConfig;
import org.fisco.bcos.web3j.protocol.core.methods.response.TransactionReceipt;
import org.fisco.bcos.channel.handler.ConnectionCallback;
import org.fisco.bcos.channel.handler.ConnectionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Service {
	private static Logger logger = LoggerFactory.getLogger(Service.class);
	private Integer connectSeconds = 30;
	private Integer connectSleepPerMillis = 1;
	private String orgID;
	private GroupChannelConnectionsConfig allChannelConnections;
	private ChannelPushCallback pushCallback;
	private Map seq2Callback = new ConcurrentHashMap();
	private static int groupId;
	static private ObjectMapper objectMapper = new ObjectMapper();
	private BigInteger number = BigInteger.valueOf(0);
	
	/**
	 * add transaction seq callback
	 */
	private Map seq2TransactionCallback = new ConcurrentHashMap();
	private Timer timeoutHandler = new HashedWheelTimer();
	private ThreadPoolTaskExecutor threadPool;

	private Set topics = new HashSet();
	public void setTopics(Set topics) {
		try {
			this.topics = topics;
		}
		catch (Exception e) {
			logger.error("system error", e);
		}
	}
	public Integer getConnectSeconds() {
		return connectSeconds;
	}

	public void setConnectSeconds(Integer connectSeconds) {
		this.connectSeconds = connectSeconds;
	}

	public Integer getConnectSleepPerMillis() {
		return connectSleepPerMillis;
	}

	public void setConnectSleepPerMillis(Integer connectSleepPerMillis) {
		this.connectSleepPerMillis = connectSleepPerMillis;
	}

	public String getOrgID() {
		return orgID;
	}

	public void setOrgID(String orgID) {
		this.orgID = orgID;
	}

	public ChannelPushCallback getPushCallback() {
		return pushCallback;
	}

	public void setPushCallback(ChannelPushCallback pushCallback) {
		this.pushCallback = pushCallback;
	}

	public GroupChannelConnectionsConfig getAllChannelConnections() {
		return allChannelConnections;
	}

	public void setAllChannelConnections(GroupChannelConnectionsConfig allChannelConnections) {
		this.allChannelConnections = allChannelConnections;
	}

	public void run() throws Exception {
		logger.debug("init ChannelService");
		int flag = 0;
		for(ChannelConnections channelConnections :allChannelConnections.getAllChannelConnections()) {

			if (channelConnections.getGroupId() == groupId) {
                    flag=1;
				try {
					ConnectionCallback connectionCallback = new ConnectionCallback(topics);
					connectionCallback.setChannelService(this);

					channelConnections.setCallback(connectionCallback);
					channelConnections.init();
					channelConnections.setThreadPool(threadPool);
					channelConnections.startConnect();

					int sleepTime = 0;
					boolean running = false;
					while (true) {
						Map networkConnection = channelConnections.getNetworkConnections();
						for (String key : networkConnection.keySet()) {
							if (networkConnection.get(key) != null && networkConnection.get(key).channel().isActive()) {
								running = true;
								break;
							}
						}

						if (running || sleepTime > connectSeconds * 1000) {
							break;
						} else {
							Thread.sleep(connectSleepPerMillis);
							sleepTime += connectSleepPerMillis;
						}
					}

					if (!running) {
						logger.error("connectSeconds = " + connectSeconds);
						logger.error("cannot not connect to node success, please checkout the node and the sdk config!");
						throw new Exception("Init ChannelService fail!Please Refer To Link Below:https://github.com/FISCO-BCOS/web3sdk/wiki/web3sdk-debug");
					}
				} catch (InterruptedException e) {
					logger.error("system error ", e);
					Thread.currentThread().interrupt();
				} catch (Exception e) {
					logger.error("system error ", e);
					throw e;
				}

			}
		}
		if(flag == 0) {
			throw new Exception("Please set the right groupId");
		}
	}

	public EthereumResponse sendEthereumMessage(EthereumRequest request) {
		class Callback extends EthereumResponseCallback  {
			Callback() {
				try {
					semaphore.acquire(1);

				} catch (InterruptedException e) {
					logger.error("error :", e);
                    Thread.currentThread().interrupt();
				}
			}

			@Override
			public void onResponse(EthereumResponse response) {
				ethereumResponse = response;

				if(ethereumResponse != null && ethereumResponse.getContent() != null) {
					logger.debug("response: {}", response.getContent());
				}
				else {
					logger.error("ethereum error");
				}

				semaphore.release();
			}

			public EthereumResponse ethereumResponse;
			public Semaphore semaphore = new Semaphore(1, true);
		};

		Callback callback = new Callback();

		asyncSendEthereumMessage(request, callback);
		try {
			callback.semaphore.acquire(1);
		} catch (InterruptedException e) {
			logger.error("system error:", e);
            Thread.currentThread().interrupt();
		}

		return callback.ethereumResponse;
	}


	public EthereumResponse sendEthereumMessage(EthereumRequest request, TransactionSucCallback transactionSucCallback) {
        class Callback extends EthereumResponseCallback  {
			public EthereumResponse ethereumResponse;
			public Semaphore semaphore = new Semaphore(1, true);

            Callback() {
                try {
                    semaphore.acquire(1);
                } catch (InterruptedException e) {
                    logger.error("error:", e);
                    Thread.currentThread().interrupt();
                }
            }

            @Override
            public void onResponse(EthereumResponse response) {
                ethereumResponse = response;

                logger.info("response: {}", response.getContent());

                semaphore.release();
            }

        }

        Callback callback = new Callback();
        asyncSendEthereumMessage(request, callback, transactionSucCallback);
        try {
            callback.semaphore.acquire(1);
        } catch (InterruptedException e) {
            logger.error("system error:", e);
            Thread.currentThread().interrupt();
        }

        return callback.ethereumResponse;
    }

    public void asyncSendEthereumMessage(EthereumRequest request, EthereumResponseCallback ethereumResponseCallback, TransactionSucCallback transactionSucCallback) {
        this.asyncSendEthereumMessage(request, ethereumResponseCallback);
        if(request.getTimeout() > 0) {
            final TransactionSucCallback callbackInner = transactionSucCallback;
            callbackInner.setTimeout(timeoutHandler.newTimeout(new TimerTask() {
                @Override
                public void run(Timeout timeout) throws Exception {
                    //处理超时逻辑
                    callbackInner.onTimeout();
                    //timeout时清除map的数据,所以尽管后面有回包数据,也会找不到seq->callback的关系
                    seq2TransactionCallback.remove(request.getMessageID());
                }
            }, request.getTimeout(), TimeUnit.MILLISECONDS));
            this.seq2TransactionCallback.put(request.getMessageID(), callbackInner);
        } else {
            this.seq2TransactionCallback.put(request.getMessageID(), transactionSucCallback);
        }
    }

	public ChannelResponse sendChannelMessage2(ChannelRequest request) {
		class Callback extends ChannelResponseCallback2  {
			Callback() {
				try {
					semaphore.acquire(1);

				} catch (InterruptedException e) {
					logger.error("error:", e);
                    Thread.currentThread().interrupt();
				}
			}

			@Override
			public void onResponseMessage(ChannelResponse response) {
				channelResponse = response;

				logger.debug("response: {}", response.getContent());

				semaphore.release();
			}

			public ChannelResponse channelResponse;
			public Semaphore semaphore = new Semaphore(1, true);

		};

		Callback callback = new Callback();

		asyncSendChannelMessage2(request, callback);
		try {
			callback.semaphore.acquire(1);
		} catch (InterruptedException e) {
			logger.error("system error:", e);
            Thread.currentThread().interrupt();
		}

		return callback.channelResponse;
	}

	public void asyncSendEthereumMessage(EthereumRequest request, EthereumResponseCallback callback) {
		logger.debug("Ethereum message: " + request.getMessageID());

		Boolean sended = false;

		EthereumMessage ethereumMessage = new EthereumMessage();

		ethereumMessage.setSeq(request.getMessageID());
		ethereumMessage.setResult(0);
		ethereumMessage.setType((short) 0x12);
		ethereumMessage.setData(request.getContent().getBytes());

		// 选取发送节点
		try {

			ChannelConnections fromChannelConnections = allChannelConnections.getAllChannelConnections().stream().filter(x->x.getGroupId()==groupId).findFirst().get();

			if (fromChannelConnections == null) {
				// 没有找到对应的链
				// 返回错误
				logger.error("not found:{}", orgID);

				throw new Exception("not found orgID");
			}


			ChannelHandlerContext ctx = fromChannelConnections.randomNetworkConnection();

			ByteBuf out = ctx.alloc().buffer();
			ethereumMessage.writeHeader(out);
			ethereumMessage.writeExtra(out);

			seq2Callback.put(request.getMessageID(), callback);

			if(request.getTimeout() > 0) {
				final EthereumResponseCallback callbackInner = callback; //ethereum名字可能会搞混,换成channel
				callback.setTimeout(timeoutHandler.newTimeout(new TimerTask() {
					EthereumResponseCallback _callback = callbackInner;

					@Override
					public void run(Timeout timeout) throws Exception {
						//处理超时逻辑
						_callback.onTimeout();
					}
				}, request.getTimeout(), TimeUnit.MILLISECONDS));
			}

			ctx.writeAndFlush(out);

			logger.debug("send Ethereum message to " + ((SocketChannel)ctx.channel()).remoteAddress().getAddress().getHostAddress()
					+ ":"
					+ ((SocketChannel)ctx.channel()).remoteAddress().getPort() + " success");

			sended = true;
		} catch (Exception e) {
			logger.error("system error: " + e.getMessage());

			EthereumResponse response = new EthereumResponse();
			response.setErrorCode(-1);
			response.setErrorMessage(e.getMessage()+"   requset send failed! Please Refer To Link Below:https://github.com/FISCO-BCOS/web3sdk/wiki/web3sdk-debug");
			response.setContent("");
			response.setMessageID(request.getMessageID());

			if(callback.getTimeout() != null) {
				callback.getTimeout().cancel();
			}
			callback.onResponse(response);
		}
	}


	public void asyncSendChannelMessage2(ChannelRequest request, ChannelResponseCallback2 callback) {
		try {
			logger.debug("ChannelRequest: " + request.getMessageID());
			callback.setService(this);

			ChannelMessage2 channelMessage = new ChannelMessage2();

			channelMessage.setSeq(request.getMessageID());
			channelMessage.setResult(0);
			channelMessage.setType((short) 0x30); //链上链下请求0x30
			channelMessage.setData(request.getContent().getBytes());
			channelMessage.setTopic(request.getToTopic());

			try {
				List fromConnectionInfos = new ArrayList();

				// 设置发送节点
				ChannelConnections fromChannelConnections = allChannelConnections.getAllChannelConnections().stream().filter(x->x.getGroupId()==groupId).findFirst().get();
				if (fromChannelConnections == null) {
					// 没有找到对应的链
					// 返回错误
					logger.error("not found orgID:{}", orgID);

					throw new Exception("not found orgID");
				}
				fromConnectionInfos.addAll(fromChannelConnections.getConnections());
				logger.debug("FromOrg:{} nodes:{}", request.getFromOrg(), fromChannelConnections.getConnections().size());

				callback.setFromChannelConnections(fromChannelConnections);
				callback.setFromConnectionInfos(fromConnectionInfos);

				//设置消息内容
				callback.setRequest(channelMessage);

				seq2Callback.put(request.getMessageID(), callback);

				if(request.getTimeout() > 0) {
					final ChannelResponseCallback2 callbackInner = callback;
					callback.setTimeout(timeoutHandler.newTimeout(new TimerTask() {
						ChannelResponseCallback2 _callback = callbackInner;

						@Override
						public void run(Timeout timeout) throws Exception {
							//处理超时逻辑
							_callback.onTimeout();
						}
					}, request.getTimeout(), TimeUnit.MILLISECONDS));
				}

				callback.retrySendMessage();
			} catch (Exception e) {
				logger.error("send message fail:", e);

				ChannelResponse response = new ChannelResponse();
				response.setErrorCode(100);
				response.setMessageID(request.getMessageID());
				response.setErrorMessage(e.getMessage());
				response.setContent("");

				callback.onResponse(response);
				return;
			}
		} catch (Exception e) {
			logger.error("system error", e);
		}
	}


	public void sendResponseMessage(ChannelResponse response, ConnectionInfo info, ChannelHandlerContext ctx, String fromNode, String toNode, String seq) {
		try {
			ChannelMessage responseMessage = new ChannelMessage();

			responseMessage.setData(response.getContent().getBytes());
			responseMessage.setResult(response.getErrorCode());
			responseMessage.setSeq(seq);
			responseMessage.setType((short)0x21); //链上链下回包0x21
			responseMessage.setToNode(fromNode);
			responseMessage.setFromNode(toNode);

			ByteBuf out = ctx.alloc().buffer();
			responseMessage.writeHeader(out);
			responseMessage.writeExtra(out);

			ctx.writeAndFlush(out);

			logger.debug("response seq:{} length:{}", response.getMessageID(), out.readableBytes());
		} catch (Exception e) {
			logger.error("system error", e);
		}
	}

	//链上链下二期回包
	public void sendResponseMessage2(ChannelResponse response, ChannelHandlerContext ctx, String seq, String topic) {
		try {
			ChannelMessage2 responseMessage = new ChannelMessage2();

			responseMessage.setData(response.getContent().getBytes());
			responseMessage.setResult(response.getErrorCode());
			responseMessage.setSeq(seq);
			responseMessage.setType((short)0x31); //链上链下二期回包0x31
			responseMessage.setTopic(topic);

			ByteBuf out = ctx.alloc().buffer();
			responseMessage.writeHeader(out);
			responseMessage.writeExtra(out);

			ctx.writeAndFlush(out);

			logger.debug("response seq:{} length:{}", response.getMessageID(), out.readableBytes());
		} catch (Exception e) {
			logger.error("system error:", e);
		}
	}

	public void onReceiveChannelMessage(ChannelHandlerContext ctx, ChannelMessage message) {
		ChannelResponseCallback callback = (ChannelResponseCallback)seq2Callback.get(message.getSeq());
		logger.debug("onReceiveChannelMessage seq:{}", message.getSeq());

		if(message.getType() == 0x20) { //链上链下请求
			logger.debug("channel Message PUSH");
			if(callback != null) {
				//清空callback再处理
				logger.debug("seq already existed,clean:{}", message.getSeq());
				seq2Callback.remove(message.getSeq());
			}

			try {
				ChannelPush push = new ChannelPush();

				if(pushCallback != null) {
					push.setService(this);
					push.setCtx(ctx);
					push.setMessageID(message.getSeq());
					push.setFromNode(message.getFromNode());
					push.setToNode(message.getToNode());
					push.setSeq(message.getSeq());
					push.setMessageID(message.getSeq());

					push.setContent(new String(message.getData(), 0, message.getData().length));

					pushCallback.onPush(push);
				}
				else {
					logger.error("can not push,unset push callback");
				}
			}
			catch(Exception e) {
				logger.error("pushCallback error:", e);
			}
		}
		else if(message.getType() == 0x21) { //链上链下回包
			logger.debug("channel response:{}", message.getSeq());
			if(callback != null) {
				logger.debug("found callback response");

				ChannelResponse response = new ChannelResponse();
				if(message.getResult() != 0) {
					response.setErrorCode(message.getResult());
					response.setErrorMessage("response error");
				}

				response.setErrorCode(message.getResult());
				response.setMessageID(message.getSeq());
				if(message.getData() != null) {
					response.setContent(new String(message.getData()));
				}

				callback.onResponse(response);
			}
			else {
				logger.error("can not found response callback,timeout:{}", message.getData());
				return;
			}
		}
	}

	public void onReceiveEthereumMessage(ChannelHandlerContext ctx, EthereumMessage message) {
		EthereumResponseCallback callback = (EthereumResponseCallback)seq2Callback.get(message.getSeq());
		logger.debug("EthereumResponse seq:{}", message.getSeq());

		if(callback != null) {
			logger.debug("found callback EthereumResponse");

			if(callback.getTimeout() != null) {
				callback.getTimeout().cancel();
			}

			EthereumResponse response = new EthereumResponse();
			if(message.getResult() != 0) {
				response.setErrorMessage("EthereumResponse error");
			}

			response.setErrorCode(message.getResult());
			response.setMessageID(message.getSeq());
			response.setContent(new String(message.getData()));

			callback.onResponse(response);

			seq2Callback.remove(message.getSeq());
		}
		else {
			logger.debug("no callback push message");
		}
	}

	public void onReceiveChannelMessage2(ChannelHandlerContext ctx, ChannelMessage2 message) {
		ChannelResponseCallback2 callback = (ChannelResponseCallback2)seq2Callback.get(message.getSeq());
		logger.debug("ChannelResponse seq:{}", message.getSeq());

		if(message.getType() == 0x30) { //链上链下请求
			logger.debug("channel PUSH");
			if(callback != null) {
				//清空callback再处理
				logger.debug("seq already existed,clear:{}", message.getSeq());
				seq2Callback.remove(message.getSeq());
			}

			try {
				ChannelPush2 push = new ChannelPush2();

				if(pushCallback != null) {
					//pushCallback.setInfo(info);
					push.setSeq(message.getSeq());
					push.setService(this);
					push.setCtx(ctx);
					push.setTopic(message.getTopic());

					push.setSeq(message.getSeq());
					push.setMessageID(message.getSeq());
					push.setContent(new String(message.getData(), 0, message.getData().length));

					pushCallback.onPush(push);
				}
				else {
					logger.error("can not push,unset push callback");
				}
			}
			catch(Exception e) {
				logger.error("push error:", e);
			}
		}
		else if(message.getType() == 0x31) { //链上链下回包
			logger.debug("channel message:{}", message.getSeq());
			if(callback != null) {
				logger.debug("found callback response");

				ChannelResponse response = new ChannelResponse();
				if(message.getResult() != 0) {
					response.setErrorCode(message.getResult());
					response.setErrorMessage("response errors");
				}

				response.setErrorCode(message.getResult());
				response.setMessageID(message.getSeq());
				if(message.getData() != null) {
					response.setContent(new String(message.getData()));
				}

				callback.onResponse(response);
			}
			else {
				logger.error("can not found response callback,timeout:{}", message.getData());
				return;
			}
		}
	}
	
	public void onReceiveBlockNotify(ChannelHandlerContext ctx, ChannelMessage2 message) {
		try {
			String data = new String(message.getData());
			String[] split = data.split(",");
			if(split.length != 2) {
				logger.error("Block notify format error: {}", data);
				return;
			}
			
			//Integer groupID = Integer.parseInt(split[0]);
			Integer number = Integer.parseInt(split[1]);
			
			setNumber(BigInteger.valueOf((long)number));
		}
		catch(Exception e) {
			logger.error("Block notify error", e);
		}
	}

	public void onReceiveTransactionMessage(ChannelHandlerContext ctx, EthereumMessage message) {
        TransactionSucCallback callback = (TransactionSucCallback)seq2TransactionCallback.get(message.getSeq());
        logger.info("receive transaction success seq:{}", message.getSeq());

        if(callback != null) {
            logger.info("found callback transaction callback");

            if(callback.getTimeout() != null) {
                //停止定时器,防止多响应一次
                callback.getTimeout().cancel();
            }
            
            try {
            	TransactionReceipt receipt = objectMapper.readValue(message.getData(), TransactionReceipt.class);
            	
            	callback.onResponse(receipt);
            }
            catch(Exception e) {
            	TransactionReceipt receipt = new TransactionReceipt();
            	receipt.setStatus("Decode receipt error: " + e.getLocalizedMessage());
            	
            	callback.onResponse(receipt);
            }

            seq2TransactionCallback.remove(message.getSeq());
        }
        else {
            logger.info("callback is null");
        }
    }

	public String newSeq() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}

	public Map getSeq2Callback() {
		return seq2Callback;
	}

	public void setSeq2Callback(Map seq2Callback) {
		this.seq2Callback = seq2Callback;
	}

	public ThreadPoolTaskExecutor getThreadPool() {
		return threadPool;
	}

	public void setThreadPool(ThreadPoolTaskExecutor threadPool) {
		this.threadPool = threadPool;
	}

	public int getGroupId() {
		return groupId;
	}

	public void setGroupId(int groupId) {
		this.groupId = groupId;
	}
	public BigInteger getNumber() {
		return number;
	}
	public void setNumber(BigInteger number) {
		this.number = number;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy