/* * 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.collector.cluster.route; import com.navercorp.pinpoint.collector.cluster.ClusterPointLocator; import com.navercorp.pinpoint.collector.cluster.PinpointServerClusterPoint; import com.navercorp.pinpoint.collector.cluster.TargetClusterPoint; import com.navercorp.pinpoint.collector.cluster.route.filter.RouteFilter; import com.navercorp.pinpoint.rpc.ResponseMessage; import com.navercorp.pinpoint.rpc.packet.stream.StreamClosePacket; import com.navercorp.pinpoint.rpc.packet.stream.StreamResponsePacket; import com.navercorp.pinpoint.rpc.server.PinpointServer; import com.navercorp.pinpoint.rpc.stream.ClientStreamChannel; import com.navercorp.pinpoint.rpc.stream.ClientStreamChannelContext; import com.navercorp.pinpoint.rpc.stream.ClientStreamChannelMessageListener; import com.navercorp.pinpoint.rpc.stream.ServerStreamChannel; import com.navercorp.pinpoint.rpc.stream.ServerStreamChannelContext; import com.navercorp.pinpoint.rpc.stream.StreamChannelStateChangeEventHandler; import com.navercorp.pinpoint.rpc.stream.StreamChannelStateCode; import com.navercorp.pinpoint.thrift.dto.command.TCommandTransferResponse; import com.navercorp.pinpoint.thrift.dto.command.TRouteResult; import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializer; import com.navercorp.pinpoint.thrift.io.SerializerFactory; import com.navercorp.pinpoint.thrift.util.SerializationUtils; import org.apache.thrift.TBase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; /** * @author koo.taejin */ public class StreamRouteHandler extends AbstractRouteHandler<StreamEvent> { public static final String ATTACHMENT_KEY = StreamRouteManager.class.getSimpleName(); private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final RouteFilterChain<StreamEvent> streamCreateFilterChain; private final RouteFilterChain<ResponseEvent> responseFilterChain; private final RouteFilterChain<StreamRouteCloseEvent> streamCloseFilterChain; @Autowired private SerializerFactory<HeaderTBaseSerializer> commandSerializerFactory; public StreamRouteHandler(ClusterPointLocator<TargetClusterPoint> targetClusterPointLocator, RouteFilterChain<StreamEvent> streamCreateFilterChain, RouteFilterChain<ResponseEvent> responseFilterChain, RouteFilterChain<StreamRouteCloseEvent> streamCloseFilterChain) { super(targetClusterPointLocator); this.streamCreateFilterChain = streamCreateFilterChain; this.responseFilterChain = responseFilterChain; this.streamCloseFilterChain = streamCloseFilterChain; } @Override public void addRequestFilter(RouteFilter<StreamEvent> filter) { this.streamCreateFilterChain.addLast(filter); } @Override public void addResponseFilter(RouteFilter<ResponseEvent> filter) { this.responseFilterChain.addLast(filter); } public void addCloseFilter(RouteFilter<StreamRouteCloseEvent> filter) { this.streamCloseFilterChain.addLast(filter); } @Override public TCommandTransferResponse onRoute(StreamEvent event) { streamCreateFilterChain.doEvent(event); TCommandTransferResponse routeResult = onRoute0(event); return routeResult; } private TCommandTransferResponse onRoute0(StreamEvent event) { TBase<?,?> requestObject = event.getRequestObject(); if (requestObject == null) { return createResponse(TRouteResult.EMPTY_REQUEST); } TargetClusterPoint clusterPoint = findClusterPoint(event.getDeliveryCommand()); if (clusterPoint == null) { return createResponse(TRouteResult.NOT_FOUND); } if (!clusterPoint.isSupportCommand(requestObject)) { return createResponse(TRouteResult.NOT_SUPPORTED_REQUEST); } try { if (clusterPoint instanceof PinpointServerClusterPoint) { StreamRouteManager routeManager = new StreamRouteManager(event); ServerStreamChannelContext consumerContext = event.getStreamChannelContext(); consumerContext.setAttributeIfAbsent(ATTACHMENT_KEY, routeManager); ClientStreamChannelContext producerContext = createStreamChannel((PinpointServerClusterPoint) clusterPoint, event.getDeliveryCommand().getPayload(), routeManager); if (producerContext.getCreateFailPacket() == null) { routeManager.setProducer(producerContext.getStreamChannel()); producerContext.getStreamChannel().addStateChangeEventHandler(routeManager); return createResponse(TRouteResult.OK); } } else { return createResponse(TRouteResult.NOT_SUPPORTED_SERVICE); } } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Create StreamChannel({}) failed. Error:{}", clusterPoint, e.getMessage(), e); } } return createResponse(TRouteResult.UNKNOWN); } private ClientStreamChannelContext createStreamChannel(PinpointServerClusterPoint clusterPoint, byte[] payload, ClientStreamChannelMessageListener messageListener) { PinpointServer pinpointServer = clusterPoint.getPinpointServer(); return pinpointServer.openStream(payload, messageListener); } public void close(ServerStreamChannelContext consumerContext) { Object attachmentListener = consumerContext.getAttribute(ATTACHMENT_KEY); if (attachmentListener instanceof StreamRouteManager) { ((StreamRouteManager)attachmentListener).close(); } } private TCommandTransferResponse createResponse(TRouteResult result) { return createResponse(result, new byte[0]); } private TCommandTransferResponse createResponse(TRouteResult result, byte[] payload) { TCommandTransferResponse response = new TCommandTransferResponse(); response.setRouteResult(result); response.setPayload(payload); return response; } private byte[] serialize(TBase<?,?> result) { return SerializationUtils.serialize(result, commandSerializerFactory, null); } // fix me : StreamRouteManager will change worker thread pattern. private class StreamRouteManager implements ClientStreamChannelMessageListener, StreamChannelStateChangeEventHandler<ClientStreamChannel> { private final StreamEvent streamEvent; private final ServerStreamChannel consumer; private ClientStreamChannel producer; public StreamRouteManager(StreamEvent streamEvent) { this.streamEvent = streamEvent; this.consumer = streamEvent.getStreamChannelContext().getStreamChannel(); } @Override public void handleStreamData(ClientStreamChannelContext producerContext, StreamResponsePacket packet) { StreamChannelStateCode stateCode = consumer.getCurrentState(); if (StreamChannelStateCode.CONNECTED == stateCode) { TCommandTransferResponse response = createResponse(TRouteResult.OK, packet.getPayload()); responseFilterChain.doEvent(new ResponseEvent(streamEvent, -1, response)); consumer.sendData(serialize(response)); } else { logger.warn("Can not route stream data to consumer.(state:{})", stateCode); if (StreamChannelStateCode.CONNECT_ARRIVED != stateCode) { close(); } } } @Override public void handleStreamClose(ClientStreamChannelContext producerContext, StreamClosePacket packet) { ResponseMessage responseMessage = new ResponseMessage(); responseMessage.setMessage(packet.getPayload()); StreamRouteCloseEvent event = new StreamRouteCloseEvent(streamEvent.getDeliveryCommand(), producerContext, streamEvent.getStreamChannelContext()); streamCloseFilterChain.doEvent(event); consumer.close(); } @Override public void eventPerformed(ClientStreamChannel streamChannel, StreamChannelStateCode updatedStateCode) throws Exception { logger.info("eventPerformed streamChannel:{}, stateCode:{}", streamChannel, updatedStateCode); switch (updatedStateCode) { case CLOSED: case ILLEGAL_STATE: if (consumer != null) { consumer.close(); } break; } } @Override public void exceptionCaught(ClientStreamChannel streamChannel, StreamChannelStateCode updatedStateCode, Throwable e) { logger.warn("exceptionCaught message:{}, streamChannel:{}, stateCode:{}", e.getMessage(), streamChannel, updatedStateCode, e); } public void close() { if (consumer != null) { consumer.close(); } if (producer != null) { producer.close(); } } public ClientStreamChannel getProducer() { return producer; } public void setProducer(ClientStreamChannel sourceStreamChannel) { this.producer = sourceStreamChannel; } } }