diff --git a/plugins/activemq-client/.gitignore b/plugins/activemq-client/.gitignore
new file mode 100644
index 000000000000..8c2d47305b59
--- /dev/null
+++ b/plugins/activemq-client/.gitignore
@@ -0,0 +1,5 @@
+/target/
+/.settings/
+/.classpath
+/.project
+/*.iml
diff --git a/plugins/activemq-client/pom.xml b/plugins/activemq-client/pom.xml
new file mode 100644
index 000000000000..4c12232301f8
--- /dev/null
+++ b/plugins/activemq-client/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ com.navercorp.pinpoint
+ pom
+ ../..
+ 1.6.0-SNAPSHOT
+
+
+ pinpoint-activemq-client-plugin
+ pinpoint-activemq-client-plugin
+ jar
+
+
+
+ com.navercorp.pinpoint
+ pinpoint-bootstrap-core
+ provided
+
+
+ org.apache.activemq
+ activemq-client
+ provided
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientConstants.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientConstants.java
new file mode 100644
index 000000000000..346c61ec0183
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientConstants.java
@@ -0,0 +1,75 @@
+/*
+ * 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.common.trace.AnnotationKey;
+import com.navercorp.pinpoint.common.trace.AnnotationKeyFactory;
+import com.navercorp.pinpoint.common.trace.AnnotationKeyProperty;
+import com.navercorp.pinpoint.common.trace.ServiceType;
+import com.navercorp.pinpoint.common.trace.ServiceTypeFactory;
+
+import static com.navercorp.pinpoint.common.trace.ServiceTypeProperty.QUEUE;
+import static com.navercorp.pinpoint.common.trace.ServiceTypeProperty.RECORD_STATISTICS;
+
+/**
+ * @author HyunGil Jeong
+ */
+public final class ActiveMQClientConstants {
+
+ private ActiveMQClientConstants() {
+
+ }
+
+ public static final ServiceType ACTIVEMQ_CLIENT = ServiceTypeFactory.of(8310, "ACTIVEMQ_CLIENT", QUEUE, RECORD_STATISTICS);
+ public static final ServiceType ACTIVEMQ_CLIENT_INTERNAL = ServiceTypeFactory.of(8311, "ACTIVEMQ_CLIENT_INTERNAL", "ACTIVE_MQ_CLIENT");
+
+ public static final AnnotationKey ACTIVEMQ_BROKER_URL = AnnotationKeyFactory.of(101, "activemq.broker.address", AnnotationKeyProperty.VIEW_IN_RECORD_SET);
+ public static final AnnotationKey ACTIVEMQ_MESSAGE = AnnotationKeyFactory.of(102, "activemq.message", AnnotationKeyProperty.VIEW_IN_RECORD_SET);
+
+ public static final String UNKNOWN_ADDRESS = "Unknown";
+
+ public static final String ACTIVEMQ_CLIENT_SCOPE = "ActiveMQClientScope";
+
+ private static final String PLUGIN_BASE = "com.navercorp.pinpoint.plugin.activemq.client";
+ private static final String INTERCEPTOR_BASE = PLUGIN_BASE + ".interceptor";
+
+ public static final String ACTIVEMQ_TCP_TRANSPORT_FQCN = "org.apache.activemq.transport.tcp.TcpTransport";
+
+ public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_FIFO_FQCN = "org.apache.activemq.FifoMessageDispatchChannel";
+ public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_SIMPLE_PRIORITY_FQCN = "org.apache.activemq.SimplePriorityMessageDispatchChannel";
+ public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_ENQUEUE_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".MessageDispatchChannelEnqueueInterceptor";
+ public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_DEQUEUE_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".MessageDispatchChannelDequeueInterceptor";
+
+ public static final String ACTIVEMQ_MESSAGE_PRODUCER_FQCN = "org.apache.activemq.ActiveMQMessageProducer";
+ public static final String ACTIVEMQ_MESSAGE_PRODUCER_SEND_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".ActiveMQMessageProducerSendInterceptor";
+
+ public static final String ACTIVEMQ_MESSAGE_CONSUMER_FQCN = "org.apache.activemq.ActiveMQMessageConsumer";
+ public static final String ACTIVEMQ_MESSAGE_CONSUMER_DISPATCH_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".ActiveMQMessageConsumerDispatchInterceptor";
+ public static final String ACTIVEMQ_MESSAGE_CONSUMER_RECEIVE_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".ActiveMQMessageConsumerReceiveInterceptor";
+
+ // field names
+ public static final String FIELD_ACTIVEMQ_MESSAGE_PRODUCER_SESSION = "session";
+ public static final String FIELD_ACTIVEMQ_MESSAGE_CONSUMER_SESSION = "session";
+ public static final String FIELD_TCP_TRANSPORT_SOCKET = "socket";
+
+ private static final String FIELD_BASE = PLUGIN_BASE + ".field";
+ private static final String FIELD_GETTER_BASE = FIELD_BASE + ".getter";
+
+ // field getter FQCN
+ public static final String FIELD_GETTER_ACTIVEMQ_SESSION = FIELD_GETTER_BASE + ".ActiveMQSessionGetter";
+ public static final String FIELD_GETTER_SOCKET = FIELD_GETTER_BASE + ".SocketGetter";
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientHeader.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientHeader.java
new file mode 100644
index 000000000000..1ff10803ba05
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientHeader.java
@@ -0,0 +1,204 @@
+/*
+ * 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 org.apache.activemq.command.ActiveMQMessage;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+/**
+ * @author HyunGil Jeong
+ */
+public enum ActiveMQClientHeader {
+ ACTIVEMQ_TRACE_ID("Pinpoint-TraceID"),
+ ACTIVEMQ_SPAN_ID("Pinpoint-SpanID"),
+ ACTIVEMQ_PARENT_SPAN_ID("Pinpoint-pSpanID"),
+ ACTIVEMQ_SAMPLED("Pinpoint-Sampled"),
+ ACTIVEMQ_FLAGS("Pinpoint-Flags"),
+ ACTIVEMQ_PARENT_APPLICATION_NAME("Pinpoint-pAppName"),
+ ACTIVEMQ_PARENT_APPLICATION_TYPE("Pinpoint-pAppType");
+// ACTIVEMQ_HOST("Pinpoint-Host");
+
+ private final String id;
+
+ ActiveMQClientHeader(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ private interface MessageHandler {
+ void setMessage(Message message, ActiveMQClientHeader key, T value) throws JMSException;
+
+ T getMessage(Message message, ActiveMQClientHeader key, T defaultValue);
+ }
+
+ private static abstract class MessageHandlerBase implements MessageHandler {
+
+ @Override
+ public final void setMessage(Message message, ActiveMQClientHeader key, T value) throws JMSException {
+ String id = key.id;
+ if (message instanceof ActiveMQMessage) {
+ ActiveMQMessage activeMQMessage = (ActiveMQMessage) message;
+ if (activeMQMessage.isReadOnlyProperties()) {
+ activeMQMessage.setReadOnlyProperties(false);
+ setMessage0(message, id, value);
+ activeMQMessage.setReadOnlyProperties(true);
+ return;
+ }
+ }
+ setMessage0(message, id, value);
+ }
+
+ @Override
+ public final T getMessage(Message message, ActiveMQClientHeader key, T defaultValue) {
+ String id = key.id;
+ try {
+ if (message.propertyExists(id)) {
+ return getMessage0(message, id);
+ }
+ } catch (JMSException e) {
+ // just ignore and return default value
+ }
+ return defaultValue;
+ }
+
+ protected abstract void setMessage0(Message message, String id, T value) throws JMSException;
+
+ protected abstract T getMessage0(Message message, String id) throws JMSException;
+ }
+
+ private static final MessageHandler STRING_MESSAGE_HANDLER = new MessageHandlerBase() {
+
+ @Override
+ protected void setMessage0(Message message, String id, String value) throws JMSException {
+ message.setStringProperty(id, value);
+ }
+
+ @Override
+ protected String getMessage0(Message message, String id) throws JMSException {
+ return message.getStringProperty(id);
+ }
+ };
+
+ private static final MessageHandler LONG_MESSAGE_HANDLER = new MessageHandlerBase() {
+
+ @Override
+ protected void setMessage0(Message message, String id, Long value) throws JMSException {
+ message.setLongProperty(id, value);
+ }
+
+ @Override
+ protected Long getMessage0(Message message, String id) throws JMSException {
+ return message.getLongProperty(id);
+ }
+ };
+
+ private static final MessageHandler SHORT_MESSAGE_HANDLER = new MessageHandlerBase() {
+
+ @Override
+ protected void setMessage0(Message message, String id, Short value) throws JMSException {
+ message.setShortProperty(id, value);
+ }
+
+ @Override
+ protected Short getMessage0(Message message, String id) throws JMSException {
+ return message.getShortProperty(id);
+ }
+ };
+
+ private static final MessageHandler BOOLEAN_MESSAGE_HANDLER = new MessageHandlerBase() {
+
+ @Override
+ protected void setMessage0(Message message, String id, Boolean value) throws JMSException {
+ message.setBooleanProperty(id, value);
+ }
+
+ @Override
+ protected Boolean getMessage0(Message message, String id) throws JMSException {
+ return message.getBooleanProperty(id);
+ }
+ };
+
+ public static void setTraceId(Message message, String traceId) throws JMSException {
+ STRING_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_TRACE_ID, traceId);
+ }
+
+ public static String getTraceId(Message message, String defaultValue) {
+ return STRING_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_TRACE_ID, defaultValue);
+ }
+
+ public static void setSpanId(Message message, Long spanId) throws JMSException {
+ LONG_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_SPAN_ID, spanId);
+ }
+
+ public static Long getSpanId(Message message, Long defaultValue) {
+ return LONG_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_SPAN_ID, defaultValue);
+ }
+
+ public static void setParentSpanId(Message message, Long parentSpanId) throws JMSException {
+ LONG_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_PARENT_SPAN_ID, parentSpanId);
+ }
+
+ public static Long getParentSpanId(Message message, Long defaultValue) {
+ return LONG_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_PARENT_SPAN_ID, defaultValue);
+ }
+
+ public static void setSampled(Message message, Boolean sampled) throws JMSException {
+ BOOLEAN_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_SAMPLED, sampled);
+ }
+
+ public static Boolean getSampled(Message message, Boolean defaultValue) {
+ return BOOLEAN_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_SAMPLED, defaultValue);
+ }
+
+ public static void setFlags(Message message, Short flags) throws JMSException {
+ SHORT_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_FLAGS, flags);
+ }
+
+ public static Short getFlags(Message message, Short defaultValue) {
+ return SHORT_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_FLAGS, defaultValue);
+ }
+
+ public static void setParentApplicationName(Message message, String parentApplicationName) throws JMSException {
+ STRING_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_PARENT_APPLICATION_NAME, parentApplicationName);
+ }
+
+ public static String getParentApplicationName(Message message, String defaultValue) {
+ return STRING_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_PARENT_APPLICATION_NAME, defaultValue);
+ }
+
+ public static void setParentApplicationType(Message message, Short parentApplicationType) throws JMSException {
+ SHORT_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_PARENT_APPLICATION_TYPE, parentApplicationType);
+ }
+
+ public static Short getParentApplicationType(Message message, Short defaultValue) {
+ return SHORT_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_PARENT_APPLICATION_TYPE, defaultValue);
+ }
+
+// public static void setHost(Message message, String host) throws JMSException {
+// STRING_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_HOST, host);
+// }
+//
+// public static String getHost(Message message, String defaultValue) {
+// return STRING_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_HOST, defaultValue);
+// }
+
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientPlugin.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientPlugin.java
new file mode 100644
index 000000000000..c8043e2a6b60
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientPlugin.java
@@ -0,0 +1,130 @@
+/*
+ * 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.instrument.InstrumentClass;
+import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;
+import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;
+import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor;
+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.plugin.ProfilerPlugin;
+import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext;
+
+import java.security.ProtectionDomain;
+
+/**
+ * @author HyunGil Jeong
+ */
+public class ActiveMQClientPlugin implements ProfilerPlugin, TransformTemplateAware {
+
+ private TransformTemplate transformTemplate;
+
+ @Override
+ public void setup(ProfilerPluginSetupContext context) {
+ this.addTransportEditor();
+ this.addMessageDispatchChannelEditor();
+ this.addProducerEditor();
+ this.addConsumerEditor();
+ }
+
+ private void addTransportEditor() {
+ 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();
+ }
+ });
+ }
+
+ private void addMessageDispatchChannelEditor() {
+ String[] messageDispatchChannelImplsFqcn = {
+ ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_FIFO_FQCN,
+ ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_SIMPLE_PRIORITY_FQCN
+ };
+ for (String messageDispatchChannelImplFqcn : messageDispatchChannelImplsFqcn) {
+ transformTemplate.transform(messageDispatchChannelImplFqcn, 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);
+
+ 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();
+ }
+ });
+ }
+ }
+
+ private void addProducerEditor() {
+ 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);
+
+ final InstrumentMethod method = target.getDeclaredMethod("send", "javax.jms.Destination", "javax.jms.Message", "int", "int", "long", "org.apache.activemq.AsyncCallback");
+ if (method != null) {
+ method.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_PRODUCER_SEND_INTERCEPTOR_FQCN);
+ }
+
+ return target.toBytecode();
+ }
+ });
+ }
+
+ private void addConsumerEditor() {
+ 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);
+ }
+
+ target.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_RECEIVE_INTERCEPTOR_FQCN);
+
+ return target.toBytecode();
+ }
+ });
+ }
+
+ @Override
+ public void setTransformTemplate(TransformTemplate transformTemplate) {
+ this.transformTemplate = transformTemplate;
+ }
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientTraceMetadataProvider.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientTraceMetadataProvider.java
new file mode 100644
index 000000000000..ad5d0469248c
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientTraceMetadataProvider.java
@@ -0,0 +1,38 @@
+/*
+ * 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.common.trace.AnnotationKey;
+import com.navercorp.pinpoint.common.trace.AnnotationKeyMatchers;
+import com.navercorp.pinpoint.common.trace.TraceMetadataProvider;
+import com.navercorp.pinpoint.common.trace.TraceMetadataSetupContext;
+
+/**
+ * @author HyunGil Jeong
+ */
+public class ActiveMQClientTraceMetadataProvider implements TraceMetadataProvider {
+
+ @Override
+ public void setup(TraceMetadataSetupContext context) {
+ context.addServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT, AnnotationKeyMatchers.exact(AnnotationKey.MESSAGE_QUEUE_URI));
+ context.addServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL, AnnotationKeyMatchers.ARGS_MATCHER);
+
+ context.addAnnotationKey(ActiveMQClientConstants.ACTIVEMQ_BROKER_URL);
+ context.addAnnotationKey(ActiveMQClientConstants.ACTIVEMQ_MESSAGE);
+ }
+
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientUtils.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientUtils.java
new file mode 100644
index 000000000000..99d970dfbe0a
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientUtils.java
@@ -0,0 +1,42 @@
+/*
+ * 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 java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+/**
+ * @author HyunGil Jeong
+ */
+public class ActiveMQClientUtils {
+
+ private ActiveMQClientUtils() {
+ }
+
+ public static String getEndPoint(SocketAddress socketAddress) {
+ String endPoint = ActiveMQClientConstants.UNKNOWN_ADDRESS;
+ if (socketAddress instanceof InetSocketAddress) {
+ InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
+ InetAddress remoteAddress = inetSocketAddress.getAddress();
+ if (remoteAddress != null) {
+ endPoint = remoteAddress.getHostAddress() + ":" + inetSocketAddress.getPort();
+ }
+ }
+ return endPoint;
+ }
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/descriptor/ActiveMQConsumerEntryMethodDescriptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/descriptor/ActiveMQConsumerEntryMethodDescriptor.java
new file mode 100644
index 000000000000..386a86660ecf
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/descriptor/ActiveMQConsumerEntryMethodDescriptor.java
@@ -0,0 +1,88 @@
+/*
+ * 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.descriptor;
+
+import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor;
+import com.navercorp.pinpoint.common.trace.MethodType;
+
+/**
+ * @author HyunGil Jeong
+ */
+public class ActiveMQConsumerEntryMethodDescriptor implements MethodDescriptor {
+
+ private int apiId = 0;
+ private int type = MethodType.WEB_REQUEST;
+
+ @Override
+ public String getMethodName() {
+ return "";
+ }
+
+ @Override
+ public String getClassName() {
+ return "";
+ }
+
+ @Override
+ public String[] getParameterTypes() {
+ return null;
+ }
+
+ @Override
+ public String[] getParameterVariableName() {
+ return null;
+ }
+
+ @Override
+ public String getParameterDescriptor() {
+ return "()";
+ }
+
+ @Override
+ public int getLineNumber() {
+ return -1;
+ }
+
+ @Override
+ public String getFullName() {
+ return ActiveMQConsumerEntryMethodDescriptor.class.getName();
+ }
+
+ @Override
+ public void setApiId(int apiId) {
+ this.apiId = apiId;
+ }
+
+ @Override
+ public int getApiId() {
+ return this.apiId;
+ }
+
+ @Override
+ public String getApiDescriptor() {
+ return "ActiveMQ Consumer Invocation";
+ }
+
+ @Override
+ public int getType() {
+ return this.type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/ActiveMQSessionGetter.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/ActiveMQSessionGetter.java
new file mode 100644
index 000000000000..1fa6628b8e39
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/ActiveMQSessionGetter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.field.getter;
+
+import org.apache.activemq.ActiveMQSession;
+
+/**
+ * @author HyunGil Jeong
+ */
+public interface ActiveMQSessionGetter {
+ ActiveMQSession _$PINPOINT$_getActiveMQSession();
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/SocketGetter.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/SocketGetter.java
new file mode 100644
index 000000000000..029b87b7182d
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/SocketGetter.java
@@ -0,0 +1,26 @@
+/*
+ * 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.field.getter;
+
+import java.net.Socket;
+
+/**
+ * @author HyunGil Jeong
+ */
+public interface SocketGetter {
+ Socket _$PINPOINT$_getSocket();
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerDispatchInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerDispatchInterceptor.java
new file mode 100644
index 000000000000..33830955ba82
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerDispatchInterceptor.java
@@ -0,0 +1,185 @@
+/*
+ * 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.context.MethodDescriptor;
+import com.navercorp.pinpoint.bootstrap.context.SpanId;
+import com.navercorp.pinpoint.bootstrap.context.SpanRecorder;
+import com.navercorp.pinpoint.bootstrap.context.Trace;
+import com.navercorp.pinpoint.bootstrap.context.TraceContext;
+import com.navercorp.pinpoint.bootstrap.context.TraceId;
+import com.navercorp.pinpoint.bootstrap.interceptor.SpanSimpleAroundInterceptor;
+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.ServiceType;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientHeader;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientUtils;
+import com.navercorp.pinpoint.plugin.activemq.client.descriptor.ActiveMQConsumerEntryMethodDescriptor;
+import com.navercorp.pinpoint.plugin.activemq.client.field.getter.ActiveMQSessionGetter;
+import com.navercorp.pinpoint.plugin.activemq.client.field.getter.SocketGetter;
+import org.apache.activemq.ActiveMQConnection;
+import org.apache.activemq.ActiveMQMessageConsumer;
+import org.apache.activemq.ActiveMQSession;
+import org.apache.activemq.command.ActiveMQDestination;
+import org.apache.activemq.command.ActiveMQMessage;
+import org.apache.activemq.command.Message;
+import org.apache.activemq.command.MessageDispatch;
+import org.apache.activemq.transport.Transport;
+import org.apache.activemq.transport.TransportFilter;
+
+import java.net.Socket;
+import java.net.SocketAddress;
+
+/**
+ * @author HyunGil Jeong
+ */
+@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE)
+public class ActiveMQMessageConsumerDispatchInterceptor extends SpanSimpleAroundInterceptor {
+
+ private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
+ private final boolean isDebug = logger.isDebugEnabled();
+
+ public ActiveMQMessageConsumerDispatchInterceptor(TraceContext traceContext) {
+ this(traceContext, new ActiveMQConsumerEntryMethodDescriptor());
+ }
+
+ private ActiveMQMessageConsumerDispatchInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) {
+ super(traceContext, methodDescriptor, ActiveMQMessageConsumerDispatchInterceptor.class);
+ traceContext.cacheApi(methodDescriptor);
+ }
+
+ @Override
+ protected Trace createTrace(Object target, Object[] args) {
+ if (!validate(target, args)) {
+ return null;
+ }
+ MessageDispatch md = (MessageDispatch) args[0];
+ ActiveMQMessage message = (ActiveMQMessage) md.getMessage();
+ // These might trigger unmarshalling.
+ if (!ActiveMQClientHeader.getSampled(message, true)) {
+ return traceContext.disableSampling();
+ }
+ String transactionId = ActiveMQClientHeader.getTraceId(message, null);
+ if (transactionId != null) {
+ long parentSpanId = ActiveMQClientHeader.getParentSpanId(message, SpanId.NULL);
+ long spanId = ActiveMQClientHeader.getSpanId(message, SpanId.NULL);
+ short flags = ActiveMQClientHeader.getFlags(message, (short) 0);
+ final TraceId traceId = traceContext.createTraceId(transactionId, parentSpanId, spanId, flags);
+ return traceContext.continueTraceObject(traceId);
+ } else {
+ return traceContext.newTraceObject();
+ }
+ }
+
+ @Override
+ protected void doInBeforeTrace(SpanRecorder recorder, Object target, Object[] args) {
+ recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT);
+
+ ActiveMQSession session = ((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession();
+ ActiveMQConnection connection = session.getConnection();
+ Transport transport = getRootTransport(connection.getTransport());
+ Socket socket = ((SocketGetter) transport)._$PINPOINT$_getSocket();
+
+ SocketAddress localSocketAddress = socket.getLocalSocketAddress();
+ String endPoint = ActiveMQClientUtils.getEndPoint(localSocketAddress);
+ // Endpoint should be the local socket address of the consumer.
+ recorder.recordEndPoint(endPoint);
+
+ SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
+ String remoteAddress = ActiveMQClientUtils.getEndPoint(remoteSocketAddress);
+ // Remote address is the socket address of where the consumer is connected to.
+ recorder.recordRemoteAddress(remoteAddress);
+
+ MessageDispatch md = (MessageDispatch) args[0];
+ ActiveMQMessage message = (ActiveMQMessage) md.getMessage();
+
+ ActiveMQDestination destination = message.getDestination();
+ // Rpc name is the URI of the queue/topic we're consuming from.
+ recorder.recordRpcName(destination.getQualifiedName());
+ // Record acceptor host as the queue/topic name in order to generate virtual queue node.
+ recorder.recordAcceptorHost(destination.getPhysicalName());
+
+ String parentApplicationName = ActiveMQClientHeader.getParentApplicationName(message, null);
+ if (!recorder.isRoot() && parentApplicationName != null) {
+ short parentApplicationType = ActiveMQClientHeader.getParentApplicationType(message, ServiceType.UNDEFINED.getCode());
+ recorder.recordParentApplication(parentApplicationName, parentApplicationType);
+ }
+ }
+
+ @Override
+ protected void doInAfterTrace(SpanRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
+ recorder.recordApi(methodDescriptor);
+ if (throwable != null) {
+ recorder.recordException(throwable);
+ }
+ }
+
+ private boolean validate(Object target, Object[] args) {
+ if (!(target instanceof ActiveMQMessageConsumer)) {
+ 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 < 1) {
+ return false;
+ }
+ if (!(args[0] instanceof MessageDispatch)) {
+ return false;
+ }
+ MessageDispatch md = (MessageDispatch) args[0];
+ Message message = md.getMessage();
+ if (!(message instanceof ActiveMQMessage)) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean validateTransport(ActiveMQSession session) {
+ if (session == null) {
+ return false;
+ }
+ ActiveMQConnection connection = session.getConnection();
+ if (connection == null) {
+ return false;
+ }
+ Transport transport = getRootTransport(connection.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();
+ }
+ return possiblyWrappedTransport;
+ }
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerReceiveInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerReceiveInterceptor.java
new file mode 100644
index 000000000000..fc137352e606
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerReceiveInterceptor.java
@@ -0,0 +1,92 @@
+/*
+ * 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.context.MethodDescriptor;
+import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
+import com.navercorp.pinpoint.bootstrap.context.TraceContext;
+import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin;
+import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
+import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod;
+import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethods;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants;
+import org.apache.activemq.command.ActiveMQTextMessage;
+
+import javax.jms.JMSException;
+
+/**
+ * @author HyunGil Jeong
+ */
+@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE)
+@TargetMethods({
+ @TargetMethod(name = "receive"),
+ @TargetMethod(name = "receive", paramTypes = "long"),
+ @TargetMethod(name = "receiveNoWait")
+})
+public class ActiveMQMessageConsumerReceiveInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
+
+ public ActiveMQMessageConsumerReceiveInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
+ super(traceContext, descriptor);
+ }
+
+ // These methods may be polled, producing a lot of garbage log.
+ // Instead, only log when the method is actually traced.
+ @Override
+ protected void logBeforeInterceptor(Object target, Object[] args) {
+ return;
+ }
+
+ @Override
+ protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) {
+ if (isDebug) {
+ super.logBeforeInterceptor(target, args);
+ }
+ }
+
+ // These methods may be polled, producing a lot of garbage log.
+ // Instead, only log when the method is actually traced.
+ @Override
+ protected void logAfterInterceptor(Object target, Object[] args, Object result, Throwable throwable) {
+ return;
+ }
+
+ @Override
+ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
+ if (isDebug) {
+ super.logAfterInterceptor(target, args, result, throwable);
+ }
+ recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL);
+ recorder.recordApi(getMethodDescriptor());
+ if (throwable != null) {
+ recorder.recordException(throwable);
+ } else {
+ if (result != null) {
+ StringBuilder sb = new StringBuilder(result.getClass().getSimpleName());
+ try {
+ // should we record other message types as well?
+ if (result instanceof ActiveMQTextMessage) {
+ // could trigger decoding (would it affect the client? if so, we might need to copy first)
+ sb.append("{").append(((ActiveMQTextMessage) result).getText()).append("}");
+ }
+ } catch (JMSException e) {
+ // ignore
+ }
+ recorder.recordAttribute(ActiveMQClientConstants.ACTIVEMQ_MESSAGE, sb.toString());
+ }
+ }
+ }
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageProducerSendInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageProducerSendInterceptor.java
new file mode 100644
index 000000000000..2861f5a61e0d
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageProducerSendInterceptor.java
@@ -0,0 +1,196 @@
+/*
+ * 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.context.MethodDescriptor;
+import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
+import com.navercorp.pinpoint.bootstrap.context.Trace;
+import com.navercorp.pinpoint.bootstrap.context.TraceContext;
+import com.navercorp.pinpoint.bootstrap.context.TraceId;
+import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor;
+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.exception.PinpointException;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientHeader;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientUtils;
+import com.navercorp.pinpoint.plugin.activemq.client.field.getter.ActiveMQSessionGetter;
+import com.navercorp.pinpoint.plugin.activemq.client.field.getter.SocketGetter;
+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.command.ActiveMQMessage;
+import org.apache.activemq.transport.Transport;
+import org.apache.activemq.transport.TransportFilter;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+/**
+ * @author HyunGil Jeong
+ */
+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;
+
+ public ActiveMQMessageProducerSendInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
+ this.traceContext = traceContext;
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ public void before(Object target, Object[] args) {
+ if (isDebug) {
+ logger.beforeInterceptor(target, args);
+ }
+ if (!validate(target, args)) {
+ 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) {
+ ActiveMQSession session = ((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession();
+ ActiveMQConnection connection = session.getConnection();
+ Transport transport = getRootTransport(connection.getTransport());
+ Socket socket = ((SocketGetter) transport)._$PINPOINT$_getSocket();
+ SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
+ String remoteAddress = ActiveMQClientUtils.getEndPoint(remoteSocketAddress);
+ // 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);
+
+ 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());
+ } else {
+ recorder.recordException(throwable);
+ }
+ } catch (Throwable t) {
+ logger.warn("AFTER error. Cause:{}", t.getMessage(), t);
+ } finally {
+ trace.traceBlockEnd();
+ }
+ }
+
+ 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 == null) {
+ return false;
+ }
+ Transport transport = getRootTransport(connection.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();
+ }
+ return possiblyWrappedTransport;
+ }
+
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelDequeueInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelDequeueInterceptor.java
new file mode 100644
index 000000000000..ea8e501ad260
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelDequeueInterceptor.java
@@ -0,0 +1,71 @@
+/*
+ * 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.context.MethodDescriptor;
+import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
+import com.navercorp.pinpoint.bootstrap.context.TraceContext;
+import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin;
+import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Name;
+import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
+import com.navercorp.pinpoint.bootstrap.interceptor.scope.ExecutionPolicy;
+import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants;
+
+/**
+ * @author HyunGil Jeong
+ */
+@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE, executionPolicy = ExecutionPolicy.INTERNAL)
+public class MessageDispatchChannelDequeueInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
+
+ public MessageDispatchChannelDequeueInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
+ super(traceContext, descriptor);
+ }
+
+ // These methods may be polled, producing a lot of garbage log.
+ // Instead, only log when the method is actually traced.
+ @Override
+ protected void logBeforeInterceptor(Object target, Object[] args) {
+ return;
+ }
+
+ @Override
+ protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) {
+ if (isDebug) {
+ super.logBeforeInterceptor(target, args);
+ }
+ }
+
+ // These methods may be polled, producing a lot of garbage log.
+ // Instead, only log when the method is actually traced.
+ @Override
+ protected void logAfterInterceptor(Object target, Object[] args, Object result, Throwable throwable) {
+ return;
+ }
+
+ @Override
+ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
+ if (isDebug) {
+ super.logAfterInterceptor(target, args, result, throwable);
+ }
+ recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL);
+ recorder.recordApi(getMethodDescriptor());
+ if (throwable != null) {
+ recorder.recordException(throwable);
+ }
+ }
+}
diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelEnqueueInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelEnqueueInterceptor.java
new file mode 100644
index 000000000000..524bb5232dba
--- /dev/null
+++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelEnqueueInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * 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.context.MethodDescriptor;
+import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder;
+import com.navercorp.pinpoint.bootstrap.context.TraceContext;
+import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin;
+import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope;
+import com.navercorp.pinpoint.bootstrap.interceptor.scope.ExecutionPolicy;
+import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants;
+
+/**
+ * @author HyunGil Jeong
+ */
+@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE, executionPolicy = ExecutionPolicy.INTERNAL)
+public class MessageDispatchChannelEnqueueInterceptor extends SpanEventSimpleAroundInterceptorForPlugin {
+
+ public MessageDispatchChannelEnqueueInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
+ super(traceContext, descriptor);
+ }
+
+ @Override
+ protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) {
+
+ }
+
+ @Override
+ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) {
+ recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL);
+ recorder.recordApi(getMethodDescriptor());
+ if (throwable != null) {
+ recorder.recordException(throwable);
+ }
+ }
+}
diff --git a/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin
new file mode 100644
index 000000000000..c6b85ac938dc
--- /dev/null
+++ b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin
@@ -0,0 +1 @@
+com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientPlugin
\ No newline at end of file
diff --git a/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider
new file mode 100644
index 000000000000..1556809a36d9
--- /dev/null
+++ b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider
@@ -0,0 +1 @@
+com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientTraceMetadataProvider
\ No newline at end of file
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 0afc7050b065..0ea5583f2214 100644
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -42,6 +42,7 @@
logback
dubbo
cassandra
+ activemq-client
@@ -195,6 +196,11 @@
pinpoint-dubbo-plugin
${project.version}
+
+ com.navercorp.pinpoint
+ pinpoint-activemq-client-plugin
+ ${project.version}
+
diff --git a/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js b/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js
index a6ed89ed4004..81fb8c85a80b 100644
--- a/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js
+++ b/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js
@@ -31,6 +31,7 @@
"sImageDir": './images/',
"sBoldKey": null,
"htIcons": {
+ 'ACTIVEMQ_CLIENT': 'ACTIVEMQ_CLIENT.png',
'APACHE': 'APACHE.png',
'APACHE_GROUP': 'APACHE.png',
'ARCUS': 'ARCUS.png',
diff --git a/web/src/main/webapp/images/icons/ACTIVEMQ_CLIENT.png b/web/src/main/webapp/images/icons/ACTIVEMQ_CLIENT.png
new file mode 100644
index 000000000000..a2caf8a35620
Binary files /dev/null and b/web/src/main/webapp/images/icons/ACTIVEMQ_CLIENT.png differ
diff --git a/web/src/main/webapp/images/servermap/ACTIVEMQ_CLIENT.png b/web/src/main/webapp/images/servermap/ACTIVEMQ_CLIENT.png
new file mode 100644
index 000000000000..9287431fc23f
Binary files /dev/null and b/web/src/main/webapp/images/servermap/ACTIVEMQ_CLIENT.png differ