/* * 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; import com.navercorp.pinpoint.bootstrap.config.Filter; import com.navercorp.pinpoint.bootstrap.instrument.*; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext; import java.security.ProtectionDomain; import static com.navercorp.pinpoint.common.util.VarArgs.va; /** * @author HyunGil Jeong */ public class ActiveMQClientPlugin implements ProfilerPlugin, TransformTemplateAware { private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); private TransformTemplate transformTemplate; @Override public void setup(ProfilerPluginSetupContext context) { ActiveMQClientPluginConfig config = new ActiveMQClientPluginConfig(context.getConfig()); if (!config.isTraceActiveMQClient()) { return; } if (config.isTraceActiveMQClientConsumer() || config.isTraceActiveMQClientProducer()) { this.addTransportEditor(); this.addConnectionEditor(); this.addMessageDispatchChannelEditor(); Filter<String> excludeDestinationFilter = config.getExcludeDestinationFilter(); if (config.isTraceActiveMQClientProducer()) { this.addProducerEditor(excludeDestinationFilter); } if (config.isTraceActiveMQClientConsumer()) { this.addConsumerEditor(excludeDestinationFilter); } } } private void addTransportEditor() { transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_FAILOVER_TRANSPORT_FQCN, new TransformCallback() { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addGetter(ActiveMQClientConstants.FIELD_GETTER_URI, ActiveMQClientConstants.FIELD_URI_TRANSPORT_SOCKET); return target.toBytecode(); } }); transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_TCP_TRANSPORT_FQCN, new TransformCallback() { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addGetter(ActiveMQClientConstants.FIELD_GETTER_SOCKET, ActiveMQClientConstants.FIELD_TCP_TRANSPORT_SOCKET); return target.toBytecode(); } }); } // ActiveMQConnection.getTransport() method has been made public in version 5.1.0. // Inject transport field getter to cover for prior versions. private void addConnectionEditor() { transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_CONNECTION_FQCN, new TransformCallback() { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addGetter(ActiveMQClientConstants.FIELD_GETTER_TRANSPORT, ActiveMQClientConstants.FIELD_ACTIVEMQ_CONNECTION_TRANSPORT); return target.toBytecode(); } }); } private void addMessageDispatchChannelEditor() { TransformCallback messageDispatchChannelTransformer = new TransformCallback() { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); // MessageDispatchChannel is an interface (5.4.0+) if (!target.isInterceptable()) { return null; } final InstrumentMethod enqueue = target.getDeclaredMethod("enqueue", "org.apache.activemq.command.MessageDispatch"); if (enqueue != null) { enqueue.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_ENQUEUE_INTERCEPTOR_FQCN); } final InstrumentMethod dequeue = target.getDeclaredMethod("dequeue", "long"); if (dequeue != null) { dequeue.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_DEQUEUE_INTERCEPTOR_FQCN); } return target.toBytecode(); } }; transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_FQCN, messageDispatchChannelTransformer); transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_FIFO_FQCN, messageDispatchChannelTransformer); transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_SIMPLE_PRIORITY_FQCN, messageDispatchChannelTransformer); } private void addProducerEditor(final Filter<String> excludeDestinationFilter) { final MethodFilter methodFilter = MethodFilters.chain( MethodFilters.name("send"), MethodFilters.argAt(0, "javax.jms.Destination"), MethodFilters.argAt(1, "javax.jms.Message") ); transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_PRODUCER_FQCN, new TransformCallback() { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addGetter(ActiveMQClientConstants.FIELD_GETTER_ACTIVEMQ_SESSION, ActiveMQClientConstants.FIELD_ACTIVEMQ_MESSAGE_PRODUCER_SESSION); for (InstrumentMethod method : target.getDeclaredMethods(methodFilter)) { try { method.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_PRODUCER_SEND_INTERCEPTOR_FQCN, va(excludeDestinationFilter)); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unsupported method " + method, e); } } } return target.toBytecode(); } }); } private void addConsumerEditor(final Filter<String> excludeDestinationFilter) { transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_FQCN, new TransformCallback() { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); target.addGetter(ActiveMQClientConstants.FIELD_GETTER_ACTIVEMQ_SESSION, ActiveMQClientConstants.FIELD_ACTIVEMQ_MESSAGE_CONSUMER_SESSION); final InstrumentMethod dispatchMethod = target.getDeclaredMethod("dispatch", "org.apache.activemq.command.MessageDispatch"); if (dispatchMethod != null) { dispatchMethod.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_DISPATCH_INTERCEPTOR_FQCN, va(excludeDestinationFilter)); } target.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_RECEIVE_INTERCEPTOR_FQCN); return target.toBytecode(); } }); } @Override public void setTransformTemplate(TransformTemplate transformTemplate) { this.transformTemplate = transformTemplate; } }