/* * Copyright 2016-2018 The OpenZipkin Authors * * 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 zipkin2.finagle; import com.twitter.finagle.Service; import com.twitter.util.Await; import com.twitter.util.Future; import com.twitter.util.Try; import java.io.IOException; import java.util.Collections; import java.util.List; import scala.runtime.AbstractFunction1; import scala.runtime.BoxedUnit; import zipkin2.Call; import zipkin2.Callback; import zipkin2.CheckResult; import zipkin2.reporter.Sender; /** Receives the Finagle generated traces and sends them off. */ public abstract class FinagleSender<C extends ZipkinTracer.Config, Req, Rep> extends Sender { final Service<Req, Rep> client; /** close is typically called from a different thread */ volatile boolean closeCalled; protected FinagleSender(C config) { if (config == null) throw new NullPointerException("config == null"); this.client = newClient(config); if (client == null) throw new NullPointerException("client == null"); } protected abstract Service<Req, Rep> newClient(C config); @Override public Call<Void> sendSpans(List<byte[]> spans) { if (closeCalled) throw new IllegalStateException("closed"); return new SendSpans(spans); } protected abstract Req makeRequest(List<byte[]> spans) throws Exception; /** sends an empty message to the configured host. */ @Override public CheckResult check() { try { sendSpans(Collections.emptyList()).execute(); return CheckResult.OK; } catch (Exception e) { return CheckResult.failed(e); } } @Override public void close() { closeFuture(); } public Future<BoxedUnit> closeFuture() { if (closeCalled) return Future.Done(); closeCalled = true; return client.close(); } static final class WrappedException extends RuntimeException { WrappedException(Exception e) { super(e); } } class SendSpans extends Call.Base<Void> { final List<byte[]> spans; SendSpans(List<byte[]> spans) { this.spans = spans; } @Override protected Void doExecute() throws IOException { try { Await.result(client.apply(makeRequest(spans))); } catch (RuntimeException |IOException e) { throw e; } catch (Exception e) { throw new WrappedException(e); } return null; } @Override protected void doEnqueue(Callback<Void> callback) { try { client.apply(makeRequest(spans)).respond(new AbstractFunction1<Try<Rep>, BoxedUnit>() { @Override public BoxedUnit apply(Try<Rep> result) { if (result.isReturn()) { callback.onSuccess(null); } else { callback.onError(result.throwable()); } return BoxedUnit.UNIT; } }); } catch (Exception e) { callback.onError(e); } } @Override public Call<Void> clone() { return new SendSpans(spans); } } }