/*
* Copyright 2016 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.activemq.client.interceptor;
import com.navercorp.pinpoint.bootstrap.config.Filter;
import com.navercorp.pinpoint.bootstrap.context.*;
import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.common.trace.AnnotationKey;
import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants;
import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientHeader;
import com.navercorp.pinpoint.plugin.activemq.client.field.getter.ActiveMQSessionGetter;
import com.navercorp.pinpoint.plugin.activemq.client.field.getter.SocketGetter;
import com.navercorp.pinpoint.plugin.activemq.client.field.getter.TransportGetter;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQMessageProducer;
import org.apache.activemq.ActiveMQSession;
import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFilter;
import org.apache.activemq.transport.failover.FailoverTransport;
import javax.jms.Message;
/**
* @author HyunGil Jeong
*/
@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE)
public class ActiveMQMessageProducerSendInterceptor implements AroundInterceptor {
private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
private final boolean isDebug = logger.isDebugEnabled();
private final TraceContext traceContext;
private final MethodDescriptor descriptor;
private final Filter<String> excludeDestinationFilter;
public ActiveMQMessageProducerSendInterceptor(TraceContext traceContext, MethodDescriptor descriptor, Filter<String> excludeDestinationFilter) {
this.traceContext = traceContext;
this.descriptor = descriptor;
this.excludeDestinationFilter = excludeDestinationFilter;
}
@Override
public void before(Object target, Object[] args) {
if (isDebug) {
logger.beforeInterceptor(target, args);
}
if (!validate(target, args)) {
return;
}
ActiveMQDestination activeMQDestination = (ActiveMQDestination) args[0];
if (filterDestination(activeMQDestination)) {
return;
}
Trace trace = traceContext.currentRawTraceObject();
if (trace == null) {
return;
}
Message message = (Message) args[1];
try {
if (trace.canSampled()) {
SpanEventRecorder recorder = trace.traceBlockBegin();
recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT);
TraceId nextId = trace.getTraceId().getNextTraceId();
recorder.recordNextSpanId(nextId.getSpanId());
ActiveMQClientHeader.setTraceId(message, nextId.getTransactionId());
ActiveMQClientHeader.setSpanId(message, nextId.getSpanId());
ActiveMQClientHeader.setParentSpanId(message, nextId.getParentSpanId());
ActiveMQClientHeader.setFlags(message, nextId.getFlags());
ActiveMQClientHeader.setParentApplicationName(message, traceContext.getApplicationName());
ActiveMQClientHeader.setParentApplicationType(message, traceContext.getServerTypeCode());
} else {
ActiveMQClientHeader.setSampled(message, false);
}
} catch (Throwable t) {
logger.warn("BEFORE. Cause:{}", t.getMessage(), t);
}
}
@Override
public void after(Object target, Object[] args, Object result, Throwable throwable) {
if (isDebug) {
logger.afterInterceptor(target, args);
}
if (!validate(target, args)) {
return;
}
Trace trace = traceContext.currentTraceObject();
if (trace == null) {
return;
}
try {
SpanEventRecorder recorder = trace.currentSpanEventRecorder();
recorder.recordApi(descriptor);
if (throwable == null) {
ActiveMQDestination destination = (ActiveMQDestination) args[0];
// This annotation indicates the uri to which the call is made
recorder.recordAttribute(AnnotationKey.MESSAGE_QUEUE_URI, destination.getQualifiedName());
// DestinationId is used to render the virtual queue node.
// We choose the queue/topic name as the logical name of the queue node.
recorder.recordDestinationId(destination.getPhysicalName());
ActiveMQSession session = ((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession();
ActiveMQConnection connection = session.getConnection();
Transport transport = getRootTransport(((TransportGetter) connection)._$PINPOINT$_getTransport());
String remoteAddress = transport.getRemoteAddress();
// Producer's endPoint should be the socket address of where the producer is actually connected to.
recorder.recordEndPoint(remoteAddress);
recorder.recordAttribute(ActiveMQClientConstants.ACTIVEMQ_BROKER_URL, remoteAddress);
} else {
recorder.recordException(throwable);
}
} catch (Throwable t) {
logger.warn("AFTER error. Cause:{}", t.getMessage(), t);
} finally {
trace.traceBlockEnd();
}
}
private boolean filterDestination(ActiveMQDestination destination) {
String destinationName = destination.getPhysicalName();
return this.excludeDestinationFilter.filter(destinationName);
}
private boolean validate(Object target, Object[] args) {
if (!(target instanceof ActiveMQMessageProducer)) {
return false;
}
if (!(target instanceof ActiveMQSessionGetter)) {
if (isDebug) {
logger.debug("Invalid target object. Need field accessor({}).", ActiveMQSessionGetter.class.getName());
}
return false;
}
if (!validateTransport(((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession())) {
return false;
}
if (args == null || args.length < 2) {
return false;
}
if (!(args[0] instanceof ActiveMQDestination)) {
return false;
}
if (!(args[1] instanceof Message)) {
return false;
}
return true;
}
private boolean validateTransport(ActiveMQSession session) {
if (session == null) {
return false;
}
ActiveMQConnection connection = session.getConnection();
if (!(connection instanceof TransportGetter)) {
if (isDebug) {
logger.debug("Invalid connection object. Need field accessor({}).", TransportGetter.class.getName());
}
return false;
}
Transport transport = getRootTransport(((TransportGetter) connection)._$PINPOINT$_getTransport());
if (!(transport instanceof SocketGetter)) {
if (isDebug) {
logger.debug("Transport not traceable({}).", transport.getClass().getName());
}
return false;
}
return true;
}
private Transport getRootTransport(Transport transport) {
Transport possiblyWrappedTransport = transport;
while (possiblyWrappedTransport instanceof TransportFilter) {
possiblyWrappedTransport = ((TransportFilter) possiblyWrappedTransport).getNext();
if (possiblyWrappedTransport instanceof FailoverTransport) {
possiblyWrappedTransport = ((FailoverTransport) possiblyWrappedTransport).getConnectedTransport();
}
}
return possiblyWrappedTransport;
}
}