/** * Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved. * * 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.springsource.insight.plugin.rabbitmqClient; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.InetAddress; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import com.springsource.insight.intercept.InterceptConfiguration; import com.springsource.insight.intercept.trace.FrameBuilder; import com.springsource.insight.intercept.trace.TraceId; import org.aspectj.lang.JoinPoint; import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.Connection; import com.rabbitmq.client.impl.AMQConnection; import com.springsource.insight.collection.OperationCollectionAspectSupport; import com.springsource.insight.collection.OperationCollectionUtil; import com.springsource.insight.intercept.color.ColorManager.ColorParams; import com.springsource.insight.intercept.operation.Operation; import com.springsource.insight.intercept.operation.OperationMap; import com.springsource.insight.util.ArrayUtil; import com.springsource.insight.util.ClassUtil; import com.springsource.insight.util.ExtraReflectionUtils; import com.springsource.insight.util.MapUtil; import com.springsource.insight.util.ReflectionUtils; import com.springsource.insight.util.logging.InsightLogManager; public abstract class AbstractRabbitMQCollectionAspect extends OperationCollectionAspectSupport { private static final AtomicReference<Field> messageHeadersField = new AtomicReference<Field>(null); private static final AtomicReference<Class<?>> longStringClassHolder = new AtomicReference<Class<?>>(null); private static final AtomicReference<Object> bytesMethodHolder = new AtomicReference<Object>(null); static InterceptConfiguration interceptConfig = InterceptConfiguration.getInstance(); static FrameBuilder frameBuilder = interceptConfig.getFrameBuilder(); static final List<String> LONG_STRING_CLASSES = Collections.unmodifiableList( Arrays.asList( "com.rabbitmq.client.impl.LongString", "com.rabbitmq.client.LongString")); protected final RabbitPluginOperationType pluginOpType; protected AbstractRabbitMQCollectionAspect(RabbitPluginOperationType rabbitOpType) { if ((pluginOpType = rabbitOpType) == null) { throw new IllegalStateException("No plugin operation type specified"); } } @Override public String getPluginName() { return RabbitMQPluginRuntimeDescriptor.PLUGIN_NAME; } protected Operation createOperation(JoinPoint jp) { return new Operation() .type(pluginOpType.getOperationType()) .label(pluginOpType.getLabel()) .sourceCodeLocation(OperationCollectionUtil.getSourceCodeLocation(jp)) ; } protected void applyPropertiesData(Operation op, BasicProperties props) { OperationMap map = op.createMap("props"); map.putAnyNonEmpty("Type", props.getType()); map.putAnyNonEmpty("App Id", props.getAppId()); map.putAnyNonEmpty("User Id", props.getUserId()); map.put("Class Id", props.getClassId()); map.putAnyNonEmpty("Reply To", props.getReplyTo()); map.putAnyNonEmpty("Priority", props.getPriority()); map.putAnyNonEmpty("Class Name", props.getClassName()); map.putAnyNonEmpty("Timestamp", props.getTimestamp()); map.putAnyNonEmpty("Message Id", props.getMessageId()); map.putAnyNonEmpty("Expiration", props.getExpiration()); map.putAnyNonEmpty("Content Type", props.getContentType()); map.putAnyNonEmpty("Delivery Mode", props.getDeliveryMode()); map.putAnyNonEmpty("Correlation Id", props.getCorrelationId()); map.putAnyNonEmpty("Content Encoding", props.getContentEncoding()); updateHeadersMap(op, props.getHeaders()); } protected OperationMap updateHeadersMap(Operation op, Map<String, ?> headers) { return updateHeadersMap(op.createMap("headers"), headers); } protected OperationMap updateHeadersMap(OperationMap headersMap, Map<String, ?> headers) { if (MapUtil.size(headers) <= 0) { return headersMap; } Class<?> longStringClass = getLongStringClass(getClass()); if (longStringClass == null) { return headersMap; } Method bytesMethod = getBytesRetrievalMethod(getClass()); if (bytesMethod == null) { return headersMap; } for (Map.Entry<String, ?> entry : headers.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value == null) { continue; } Class<?> valueType = value.getClass(); if (!valueType.isAssignableFrom(longStringClass)) { continue; } final byte[] bytes; try { bytes = (byte[]) bytesMethod.invoke(value); if (ArrayUtil.length(bytes) <= 0) { continue; } } catch (Exception e) { if (_logger.isLoggable(Level.FINE)) { _logger.fine("Failed (" + e.getClass().getSimpleName() + ")" + " to get bytes of " + valueType.getName() + " instance for key=" + key + ": " + e.getMessage()); } continue; } headersMap.put(key, new String(bytes)); } return headersMap; } protected void applyConnectionData(Operation op, Connection conn) { InetAddress address = conn.getAddress(); String host = address.getHostAddress(); int port = conn.getPort(); final String connectionUrl = conn.toString(); op.put("host", host); op.put("port", port); op.put("connectionUrl", connectionUrl); //try to extract server version String serverVersion = getVersion(conn.getServerProperties()); op.putAnyNonEmpty("serverVersion", serverVersion); //try to extract client version String clientVersion = getVersion(conn.getClientProperties()); op.putAnyNonEmpty("clientVersion", clientVersion); } static String getVersion(Map<String, ?> properties) { if (MapUtil.size(properties) <= 0) { return null; } Object obj = properties.get("version"); if (obj != null) { return String.valueOf(obj); } else { return null; } } protected BasicProperties colorForward(BasicProperties orgProps, final Operation op, TraceId traceId) { Field headersField = getMessageHeadersField(); BasicProperties props = orgProps; if (headersField == null) { return props; } try { final Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> old = (props != null) ? props.getHeaders() : null; if (MapUtil.size(old) > 0) { map.putAll(old); } colorForward(new ColorParams() { public void setColor(String key, String value) { map.put(key, value); } public Operation getOperation() { return op; } }); if (traceId != null) map.put(TraceId.TRACE_ID_HEADER_NAME, traceId.toString()); if (props == null) { BasicProperties.Builder builder = new BasicProperties.Builder(); props = builder.build(); } Map<String, Object> hdrsMap = Collections.unmodifiableMap(map); ReflectionUtils.setField(headersField, props, hdrsMap); return props; } catch (Exception e) { if (_logger.isLoggable(Level.FINE)) { _logger.fine("colorForward(" + op + ")" + " failed (" + e.getClass().getSimpleName() + ")" + " to append color: " + e.getMessage()); } return props; } } static Field getMessageHeadersField() { // kind of a D.C.L. but works... Field field = messageHeadersField.get(); if (field == null) { if ((field = ExtraReflectionUtils.getAccessibleField(BasicProperties.class, "headers")) != null) { messageHeadersField.set(field); } } return field; } static Method getBytesRetrievalMethod(Class<?> anchor) { Object method = bytesMethodHolder.get(); if (method != null) { if (method instanceof Boolean) { return null; } else { return (Method) method; } } Class<?> longStringClass = getLongStringClass(anchor); if (longStringClass == null) { return null; } if ((method = ExtraReflectionUtils.getAccessibleMethod(longStringClass, "getBytes")) == null) { InsightLogManager.getLogger(anchor.getName()) .warning("getBytesRetrievalMethod(" + anchor.getSimpleName() + ") no match found") ; bytesMethodHolder.set(Boolean.FALSE); // avoid repeated calls return null; } bytesMethodHolder.set(method); return (Method) method; } static Class<?> getLongStringClass(Class<?> anchor) { Class<?> clazz = longStringClassHolder.get(); if (clazz != null) { // check if placeholder used if (clazz == String.class) { return null; } else { return clazz; } } ClassLoader cl = ClassUtil.getDefaultClassLoader(anchor); for (String className : LONG_STRING_CLASSES) { try { if ((clazz = ClassUtil.loadClassByName(cl, className)) == null) { throw new IllegalStateException("Failed to load present class"); } longStringClassHolder.set(clazz); return clazz; } catch (Exception e) { if (!(e instanceof ClassNotFoundException)) { InsightLogManager.getLogger(anchor.getName()) .warning("Failed (" + e.getClass().getSimpleName() + ")" + " to load class=" + className + ": " + e.getMessage()); } } } InsightLogManager.getLogger(anchor.getName()) .warning("getLongStringClass(" + anchor.getSimpleName() + ") no match found"); longStringClassHolder.set(String.class); // avoid repeated load attempts and use an incompatible class return null; } @Override public boolean isMetricsGenerator() { return true; // This provides an endpoint and external resource } }