/*
* Copyright 2015 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.plugin.thrift.common.client;
import static com.navercorp.pinpoint.bootstrap.plugin.test.Expectations.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.util.concurrent.CountDownLatch;
import org.apache.thrift.TBase;
import org.apache.thrift.TException;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.async.TAsyncMethodCall;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import com.navercorp.pinpoint.bootstrap.plugin.test.Expectations;
import com.navercorp.pinpoint.bootstrap.plugin.test.ExpectedAnnotation;
import com.navercorp.pinpoint.bootstrap.plugin.test.ExpectedTrace;
import com.navercorp.pinpoint.bootstrap.plugin.test.PluginTestVerifier;
import com.navercorp.pinpoint.plugin.thrift.common.TestEnvironment;
import com.navercorp.pinpoint.plugin.thrift.dto.EchoService;
/**
* @author HyunGil Jeong
*/
public class AsyncEchoTestClient implements EchoTestClient {
private final TestEnvironment environment;
private final TNonblockingTransport transport;
private final EchoService.AsyncClient asyncClient;
private final TAsyncClientManager asyncClientManager = new TAsyncClientManager();
private AsyncEchoTestClient(TestEnvironment environment) throws IOException {
this.environment = environment;
this.transport = new TNonblockingSocket(this.environment.getServerIp(), this.environment.getPort());
this.asyncClient = new EchoService.AsyncClient(this.environment.getProtocolFactory(), this.asyncClientManager, this.transport);
}
@Override
public String echo(String message) throws TException {
final CountDownLatch latch = new CountDownLatch(1);
final AsyncEchoResultHolder resultHolder = new AsyncEchoResultHolder();
final AsyncMethodCallback<String> callback = new EchoMethodCallback(latch, resultHolder);
this.asyncClient.echo(message, callback);
boolean isInterrupted = false;
while (true) {
try {
latch.await();
return resultHolder.getResult();
} catch (InterruptedException e) {
isInterrupted = true;
} finally {
if (isInterrupted) {
Thread.currentThread().interrupt();
}
}
}
}
@Override
public void verifyTraces(PluginTestVerifier verifier, String expectedMessage) throws Exception {
final InetSocketAddress actualServerAddress = this.environment.getServerAddress();
// ********** Asynchronous Traces
// SpanEvent - Asynchronous Invocation
ExpectedTrace asyncInvocationTrace = event("ASYNC", "Asynchronous Invocation");
// SpanEvent - TAsyncMethodCall.cleanUpAndFireCallback
Method cleanUpAndFireCallback = TAsyncMethodCall.class.getDeclaredMethod("cleanUpAndFireCallback",
SelectionKey.class);
ExpectedTrace cleanUpAndFireCallbackTrace = event("THRIFT_CLIENT_INTERNAL", cleanUpAndFireCallback);
// SpanEvent - TServiceClient.receiveBase
Method receiveBase = TServiceClient.class.getDeclaredMethod("receiveBase", TBase.class, String.class);
ExpectedAnnotation thriftResult = Expectations.annotation("thrift.result", "echo_result(success:"
+ expectedMessage + ")");
ExpectedTrace receiveBaseTrace = event("THRIFT_CLIENT_INTERNAL", // ServiceType
receiveBase, // Method
thriftResult // Annotation("thrift.result")
);
// ********** Root trace for Asynchronous traces
// SpanEvent - TAsyncClientManager.call
Method call = TAsyncClientManager.class.getDeclaredMethod("call", TAsyncMethodCall.class);
ExpectedAnnotation thriftUrl = Expectations.annotation("thrift.url", actualServerAddress.getHostName() + ":"
+ actualServerAddress.getPort() + "/com/navercorp/pinpoint/plugin/thrift/dto/EchoService/echo_call");
ExpectedTrace callTrace = event("THRIFT_CLIENT", // ServiceType
call, // Method
null, // rpc
null, // endPoint
actualServerAddress.getHostName() + ":" + actualServerAddress.getPort(), // destinationId
thriftUrl // Annotation("thrift.url")
);
verifier.verifyTrace(async(callTrace, asyncInvocationTrace, cleanUpAndFireCallbackTrace, receiveBaseTrace));
}
private static class AsyncEchoResultHolder {
private volatile String result;
public void setResult(String result) {
this.result = result;
}
public String getResult() {
return this.result;
}
}
@Override
public void close() {
if (this.asyncClientManager.isRunning()) {
this.asyncClientManager.stop();
}
if (this.transport.isOpen()) {
this.transport.close();
}
}
private static class EchoMethodCallback implements AsyncMethodCallback<String> {
private final CountDownLatch completeLatch;
private final AsyncEchoResultHolder resultHolder;
private EchoMethodCallback(final CountDownLatch completeLatch, final AsyncEchoResultHolder resultHolder) {
this.completeLatch = completeLatch;
this.resultHolder = resultHolder;
}
@Override
public void onComplete(String response) {
this.resultHolder.setResult(response);
this.completeLatch.countDown();
}
@Override
public void onError(Exception exception) {
try {
this.resultHolder.setResult(exception.toString());
} finally {
this.completeLatch.countDown();
}
}
}
public static class Client extends AsyncEchoTestClient {
public Client(TestEnvironment environment) throws IOException {
super(environment);
}
}
}