/* * Copyright 2014 NAVER Corp. * * 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.navercorp.pinpoint.rpc.stream; import com.navercorp.pinpoint.rpc.PinpointSocketException; import com.navercorp.pinpoint.rpc.packet.stream.StreamCode; import com.navercorp.pinpoint.rpc.packet.stream.StreamPingPacket; import com.navercorp.pinpoint.rpc.packet.stream.StreamPongPacket; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * @author koo.taejin */ public abstract class StreamChannel { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Channel channel; private final int streamChannelId; private final StreamChannelManager streamChannelManager; private final StreamChannelState state; private final CountDownLatch openLatch = new CountDownLatch(1); private List<StreamChannelStateChangeEventHandler> stateChangeEventHandlers = new CopyOnWriteArrayList<StreamChannelStateChangeEventHandler>(); public StreamChannel(Channel channel, int streamId, StreamChannelManager streamChannelManager) { this.channel = channel; this.streamChannelId = streamId; this.streamChannelManager = streamChannelManager; this.state = new StreamChannelState(); } public void addStateChangeEventHandler(StreamChannelStateChangeEventHandler stateChangeEventHandler) { stateChangeEventHandlers.add(stateChangeEventHandler); } public void setStateChangeEventHandler(List<StreamChannelStateChangeEventHandler> stateChangeEventHandlers) { this.stateChangeEventHandlers = stateChangeEventHandlers; } public List<StreamChannelStateChangeEventHandler> getStateChangeEventHandlers() { return new ArrayList<StreamChannelStateChangeEventHandler>(stateChangeEventHandlers); } boolean changeStateOpen() { return changeStateTo(StreamChannelStateCode.OPEN); } boolean changeStateConnected() { try { return changeStateTo(StreamChannelStateCode.CONNECTED); } finally { openLatch.countDown(); } } boolean changeStateClose() { try { if (checkState(StreamChannelStateCode.CLOSED)) { return true; } return changeStateTo(StreamChannelStateCode.CLOSED); } finally { openLatch.countDown(); } } public boolean awaitOpen() { try { openLatch.await(); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return false; } public boolean awaitOpen(long timeoutMillis) { try { return openLatch.await(timeoutMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return false; } public StreamChannelStateCode getCurrentState() { return state.getCurrentState(); } public ChannelFuture sendPing(int requestId) { assertState(StreamChannelStateCode.CONNECTED); StreamPingPacket packet = new StreamPingPacket(streamChannelId, requestId); return this.channel.write(packet); } public ChannelFuture sendPong(int requestId) { assertState(StreamChannelStateCode.CONNECTED); StreamPongPacket packet = new StreamPongPacket(streamChannelId, requestId); return this.channel.write(packet); } public void close() { this.streamChannelManager.clearResourceAndSendClose(getStreamId(), StreamCode.STATE_CLOSED); } public Channel getChannel() { return channel; } public int getStreamId() { return streamChannelId; } protected StreamChannelState getState() { return state; } public boolean isServer() { if (this instanceof ServerStreamChannel) { return true; } return false; } void assertState(StreamChannelStateCode stateCode) { final StreamChannelStateCode currentCode = getCurrentState(); if (!checkState(currentCode, stateCode)) { throw new PinpointSocketException("expected:<" + stateCode + "> but was:<" + currentCode + ">;"); } } boolean checkState(StreamChannelStateCode expectedCode) { return checkState(getCurrentState(), expectedCode); } boolean checkState(StreamChannelStateCode currentCode, StreamChannelStateCode expectedCode) { if (currentCode == expectedCode) { return true; } else { return false; } } protected boolean changeStateTo(StreamChannelStateCode nextState) { StreamChannelStateCode currentState = getCurrentState(); boolean isChanged = state.to(currentState, nextState); if (!isChanged && (getCurrentState() != StreamChannelStateCode.ILLEGAL_STATE)) { changeStateTo(StreamChannelStateCode.ILLEGAL_STATE); } if (isChanged) { for (StreamChannelStateChangeEventHandler handler : stateChangeEventHandlers) { try { handler.eventPerformed(this, nextState); } catch (Exception e) { handler.exceptionCaught(this, nextState, e); } } } return isChanged; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(this.getClass().getSimpleName()); sb.append("[Channel:"); sb.append(channel); sb.append(", StreamId:"); sb.append(getStreamId()); sb.append(", State:"); sb.append(getCurrentState()); sb.append("]."); return sb.toString(); } }