/*
* Copyright 2017 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.client;
import com.navercorp.pinpoint.rpc.DefaultFuture;
import com.navercorp.pinpoint.rpc.Future;
import com.navercorp.pinpoint.rpc.PinpointSocketException;
import com.navercorp.pinpoint.rpc.ResponseMessage;
import com.navercorp.pinpoint.rpc.cluster.ClusterOption;
import com.navercorp.pinpoint.rpc.packet.RequestPacket;
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.StreamChannelContext;
import com.navercorp.pinpoint.rpc.stream.StreamChannelStateChangeEventHandler;
import com.navercorp.pinpoint.rpc.util.AssertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.SocketAddress;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Woonduk Kang(emeroad)
*/
public class DefaultPinpointClient implements PinpointClient {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private volatile PinpointClientHandler pinpointClientHandler;
private volatile boolean closed;
private List<PinpointClientReconnectEventListener> reconnectEventListeners = new CopyOnWriteArrayList<PinpointClientReconnectEventListener>();
public DefaultPinpointClient() {
this(new ReconnectStateClientHandler());
}
public DefaultPinpointClient(PinpointClientHandler pinpointClientHandler) {
AssertUtils.assertNotNull(pinpointClientHandler, "pinpointClientHandler");
this.pinpointClientHandler = pinpointClientHandler;
pinpointClientHandler.setPinpointClient(this);
}
@Override
public void reconnectSocketHandler(PinpointClientHandler pinpointClientHandler) {
AssertUtils.assertNotNull(pinpointClientHandler, "pinpointClientHandler");
if (closed) {
logger.warn("reconnectClientHandler(). pinpointClientHandler force close.");
pinpointClientHandler.close();
return;
}
logger.warn("reconnectClientHandler:{}", pinpointClientHandler);
this.pinpointClientHandler = pinpointClientHandler;
notifyReconnectEvent();
}
/*
because reconnectEventListener's constructor contains Dummy and can't be access through setter,
guarantee it is not null.
*/
@Override
public boolean addPinpointClientReconnectEventListener(PinpointClientReconnectEventListener eventListener) {
if (eventListener == null) {
return false;
}
return this.reconnectEventListeners.add(eventListener);
}
@Override
public boolean removePinpointClientReconnectEventListener(PinpointClientReconnectEventListener eventListener) {
if (eventListener == null) {
return false;
}
return this.reconnectEventListeners.remove(eventListener);
}
private void notifyReconnectEvent() {
for (PinpointClientReconnectEventListener eachListener : this.reconnectEventListeners) {
eachListener.reconnectPerformed(this);
}
}
@Override
public void sendSync(byte[] bytes) {
ensureOpen();
pinpointClientHandler.sendSync(bytes);
}
@Override
public Future sendAsync(byte[] bytes) {
ensureOpen();
return pinpointClientHandler.sendAsync(bytes);
}
@Override
public void send(byte[] bytes) {
ensureOpen();
pinpointClientHandler.send(bytes);
}
@Override
public Future<ResponseMessage> request(byte[] bytes) {
if (pinpointClientHandler == null) {
return returnFailureFuture();
}
return pinpointClientHandler.request(bytes);
}
@Override
public void response(RequestPacket requestPacket, byte[] payload) {
response(requestPacket.getRequestId(), payload);
}
@Override
public void response(int requestId, byte[] payload) {
ensureOpen();
pinpointClientHandler.response(requestId, payload);
}
@Override
public ClientStreamChannelContext openStream(byte[] payload, ClientStreamChannelMessageListener messageListener) {
return openStream(payload, messageListener, null);
}
@Override
public ClientStreamChannelContext openStream(byte[] payload, ClientStreamChannelMessageListener messageListener, StreamChannelStateChangeEventHandler<ClientStreamChannel> stateChangeListener) {
// StreamChannel must be changed into interface in order to throw the StreamChannel that returns failure.
// fow now throw just exception
ensureOpen();
return pinpointClientHandler.openStream(payload, messageListener, stateChangeListener);
}
@Override
public SocketAddress getRemoteAddress() {
return pinpointClientHandler.getRemoteAddress();
}
@Override
public ClusterOption getLocalClusterOption() {
return pinpointClientHandler.getLocalClusterOption();
}
@Override
public ClusterOption getRemoteClusterOption() {
return pinpointClientHandler.getRemoteClusterOption();
}
@Override
public StreamChannelContext findStreamChannel(int streamChannelId) {
ensureOpen();
return pinpointClientHandler.findStreamChannel(streamChannelId);
}
private Future<ResponseMessage> returnFailureFuture() {
DefaultFuture<ResponseMessage> future = new DefaultFuture<ResponseMessage>();
future.setFailure(new PinpointSocketException("pinpointClientHandler is null"));
return future;
}
private void ensureOpen() {
if (pinpointClientHandler == null) {
throw new PinpointSocketException("pinpointClientHandler is null");
}
}
/**
* write ping packet on tcp channel
* PinpointSocketException throws when writing fails.
*
*/
@Override
public void sendPing() {
PinpointClientHandler pinpointClientHandler = this.pinpointClientHandler;
if (pinpointClientHandler == null) {
return;
}
pinpointClientHandler.sendPing();
}
@Override
public void close() {
synchronized (this) {
if (closed) {
return;
}
closed = true;
}
PinpointClientHandler pinpointClientHandler = this.pinpointClientHandler;
if (pinpointClientHandler == null) {
return;
}
pinpointClientHandler.close();
}
@Override
public boolean isClosed() {
return closed;
}
@Override
public boolean isConnected() {
return this.pinpointClientHandler.isConnected();
}
}