001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.console.filter;
018
019import java.lang.reflect.Array;
020import java.lang.reflect.Method;
021import java.util.Enumeration;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.Map;
025import java.util.Properties;
026import java.util.Arrays;
027
028import javax.jms.DeliveryMode;
029import javax.jms.JMSException;
030import javax.management.Attribute;
031import javax.management.AttributeList;
032import javax.management.ObjectInstance;
033import javax.management.ObjectName;
034import javax.management.openmbean.CompositeDataSupport;
035
036import org.apache.activemq.command.ActiveMQBytesMessage;
037import org.apache.activemq.command.ActiveMQDestination;
038import org.apache.activemq.command.ActiveMQMapMessage;
039import org.apache.activemq.command.ActiveMQMessage;
040import org.apache.activemq.command.ActiveMQObjectMessage;
041import org.apache.activemq.command.ActiveMQStreamMessage;
042import org.apache.activemq.command.ActiveMQTextMessage;
043import org.apache.activemq.console.util.AmqMessagesUtil;
044
045public class MapTransformFilter extends ResultTransformFilter {
046    /**
047     * Creates a Map transform filter that is able to transform a variety of
048     * objects to a properties map object
049     * 
050     * @param next - the next query filter
051     */
052    public MapTransformFilter(QueryFilter next) {
053        super(next);
054    }
055
056    /**
057     * Transform the given object to a Map object
058     * 
059     * @param object - object to transform
060     * @return map object
061     */
062    protected Object transformElement(Object object) throws Exception {
063        // Use reflection to determine how the object should be transformed
064        try {
065            Method method = this.getClass().getDeclaredMethod("transformToMap", new Class[] {
066                object.getClass()
067            });
068            return (Map)method.invoke(this, new Object[] {
069                object
070            });
071        } catch (NoSuchMethodException e) {
072//            CommandContext.print("Unable to transform mbean of type: " + object.getClass().getName() + ". No corresponding transformToMap method found.");
073            return null;
074        }
075    }
076
077    /**
078     * Transform an ObjectInstance mbean to a Map
079     * 
080     * @param obj - ObjectInstance format of an mbean
081     * @return map object
082     */
083    protected Map transformToMap(ObjectInstance obj) {
084        return transformToMap(obj.getObjectName());
085    }
086
087    /**
088     * Transform an ObjectName mbean to a Map
089     * 
090     * @param objname - ObjectName format of an mbean
091     * @return map object
092     */
093    protected Map transformToMap(ObjectName objname) {
094        Properties props = new Properties();
095
096        // Parse object properties
097        Map objProps = objname.getKeyPropertyList();
098        for (Iterator i = objProps.keySet().iterator(); i.hasNext();) {
099            Object key = i.next();
100            Object val = objProps.get(key);
101            if (val != null) {
102                props.setProperty(key.toString(), getDisplayString(val));
103            }
104        }
105
106        return props;
107    }
108
109    /**
110     * Transform an Attribute List format of an mbean to a Map
111     * 
112     * @param list - AttributeList format of an mbean
113     * @return map object
114     */
115    protected Map transformToMap(AttributeList list) {
116        Properties props = new Properties();
117        for (Iterator i = list.iterator(); i.hasNext();) {
118            Attribute attrib = (Attribute)i.next();
119
120            // If attribute is an ObjectName
121            if (attrib.getName().equals(MBeansAttributeQueryFilter.KEY_OBJECT_NAME_ATTRIBUTE)) {
122                props.putAll(transformToMap((ObjectName)attrib.getValue()));
123            } else {
124                if (attrib.getValue() != null) {
125                    props.setProperty(attrib.getName(), getDisplayString(attrib.getValue()));
126                }
127            }
128        }
129
130        return props;
131    }
132
133    /**
134     * Transform an ActiveMQTextMessage to a Map
135     * 
136     * @param msg - text message to trasnform
137     * @return map object
138     * @throws JMSException
139     */
140    protected Map transformToMap(ActiveMQTextMessage msg) throws JMSException {
141        Properties props = new Properties();
142
143        props.putAll(transformToMap((ActiveMQMessage)msg));
144        if (msg.getText() != null) {
145            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "JMSText", msg.getText());
146        }
147
148        return props;
149    }
150
151    /**
152     * Transform an ActiveMQBytesMessage to a Map
153     * 
154     * @param msg - bytes message to transform
155     * @return map object
156     * @throws JMSException
157     */
158    protected Map transformToMap(ActiveMQBytesMessage msg) throws JMSException {
159        Properties props = new Properties();
160
161        props.putAll(transformToMap((ActiveMQMessage)msg));
162
163        long bodyLength = msg.getBodyLength();
164        byte[] msgBody;
165        int i = 0;
166        // Create separate bytes messages
167        for (i = 0; i < (bodyLength / Integer.MAX_VALUE); i++) {
168            msgBody = new byte[Integer.MAX_VALUE];
169            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "JMSBytes:" + (i + 1), new String(msgBody));
170        }
171        msgBody = new byte[(int)(bodyLength % Integer.MAX_VALUE)];
172        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "JMSBytes:" + (i + 1), new String(msgBody));
173
174        return props;
175    }
176
177    /**
178     * Transform an ActiveMQMessage to a Map
179     * 
180     * @param msg - object message to transform
181     * @return map object
182     * @throws JMSException
183     */
184    protected Map transformToMap(ActiveMQObjectMessage msg) throws JMSException {
185        Properties props = new Properties();
186
187        props.putAll(transformToMap((ActiveMQMessage)msg));
188        if (msg.getObject() != null) {
189            // Just add the class name and toString value of the object
190            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "JMSObjectClass", msg.getObject().getClass().getName());
191            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "JMSObjectString", getDisplayString(msg.getObject()));
192        }
193        return props;
194    }
195
196    /**
197     * Transform an ActiveMQMapMessage to a Map
198     * 
199     * @param msg - map message to transform
200     * @return map object
201     * @throws JMSException
202     */
203    protected Map transformToMap(ActiveMQMapMessage msg) throws JMSException {
204        Properties props = new Properties();
205
206        props.putAll(transformToMap((ActiveMQMessage)msg));
207
208        // Get map properties
209        Enumeration e = msg.getMapNames();
210        while (e.hasMoreElements()) {
211            String key = (String)e.nextElement();
212            Object val = msg.getObject(key);
213            if (val != null) {
214                props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + key, getDisplayString(val));
215            }
216        }
217
218        return props;
219    }
220
221    /**
222     * Transform an ActiveMQStreamMessage to a Map
223     * 
224     * @param msg - stream message to transform
225     * @return map object
226     * @throws JMSException
227     */
228    protected Map transformToMap(ActiveMQStreamMessage msg) throws JMSException {
229        Properties props = new Properties();
230
231        props.putAll(transformToMap((ActiveMQMessage)msg));
232        // Just set the toString of the message as the body of the stream
233        // message
234        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "JMSStreamMessage", getDisplayString(msg));
235
236        return props;
237    }
238
239    /**
240     * Transform an ActiveMQMessage to a Map
241     * 
242     * @param msg - message to transform
243     * @return map object
244     * @throws JMSException
245     */
246    protected Map<String, String> transformToMap(ActiveMQMessage msg) throws JMSException {
247        Map<String, String> props = new HashMap<String, String>();
248
249        // Get JMS properties
250        if (msg.getJMSCorrelationID() != null) {
251            props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSCorrelationID", msg.getJMSCorrelationID());
252        }
253        props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSDeliveryMode", (msg.getJMSDeliveryMode() == DeliveryMode.PERSISTENT) ? "persistent" : "non-persistent");
254        if (msg.getJMSDestination() != null) {
255            props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSDestination", ((ActiveMQDestination)msg.getJMSDestination()).getPhysicalName());
256        }
257        props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSExpiration", Long.toString(msg.getJMSExpiration()));
258        props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSMessageID", msg.getJMSMessageID());
259        props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSPriority", Integer.toString(msg.getJMSPriority()));
260        props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSRedelivered", Boolean.toString(msg.getJMSRedelivered()));
261        if (msg.getJMSReplyTo() != null) {
262            props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSReplyTo", ((ActiveMQDestination)msg.getJMSReplyTo()).getPhysicalName());
263        }
264        props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSTimestamp", Long.toString(msg.getJMSTimestamp()));
265        if (msg.getJMSType() != null) {
266            props.put(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSType", msg.getJMSType());
267        }
268        if (msg.getOriginalDestination() != null) {
269            props.put(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + "OriginalDestination", msg.getOriginalDestination().getPhysicalName());
270        }
271        // Get custom properties
272        Enumeration e = msg.getPropertyNames();
273        while (e.hasMoreElements()) {
274            String name = (String)e.nextElement();
275            if (msg.getObjectProperty(name) != null) {
276                props.put(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + name, getDisplayString(msg.getObjectProperty(name)));
277            }
278        }
279
280        return props;
281    }
282
283    /**
284     * Transform an openMBean composite data to a Map
285     * 
286     * @param data - composite data to transform
287     * @return map object
288     */
289    protected Map transformToMap(CompositeDataSupport data) {
290        Properties props = new Properties();
291
292        String typeName = data.getCompositeType().getTypeName();
293
294        // Retrieve text message
295        if (typeName.equals(ActiveMQTextMessage.class.getName())) {
296            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "Text", data.get("Text").toString());
297
298            // Retrieve byte preview
299        } else if (typeName.equals(ActiveMQBytesMessage.class.getName())) {
300            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "BodyLength", data.get("BodyLength").toString());
301            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "BodyPreview", new String((byte[])data.get("BodyPreview")));
302
303            // Expand content map
304        } else if (typeName.equals(ActiveMQMapMessage.class.getName())) {
305            Map contentMap = (Map)data.get("ContentMap");
306            for (Iterator i = contentMap.keySet().iterator(); i.hasNext();) {
307                String key = (String)i.next();
308                props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + key, contentMap.get(key).toString());
309            }
310
311            // Do nothing
312        } else if (typeName.equals(ActiveMQObjectMessage.class.getName()) || typeName.equals(ActiveMQStreamMessage.class.getName()) || typeName.equals(ActiveMQMessage.class.getName())) {
313
314            // Unrecognized composite data. Throw exception.
315        } else {
316            throw new IllegalArgumentException("Unrecognized composite data to transform. composite type: " + typeName);
317        }
318
319        // Process the JMS message header values
320        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSCorrelationID", "" + data.get("JMSCorrelationID"));
321        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSDestination", "" + data.get("JMSDestination"));
322        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSMessageID", "" + data.get("JMSMessageID"));
323        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSReplyTo", "" + data.get("JMSReplyTo"));
324        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSType", "" + data.get("JMSType"));
325        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSDeliveryMode", "" + data.get("JMSDeliveryMode"));
326        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSExpiration", "" + data.get("JMSExpiration"));
327        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSPriority", "" + data.get("JMSPriority"));
328        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSRedelivered", "" + data.get("JMSRedelivered"));
329        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSTimestamp", "" + data.get("JMSTimestamp"));
330
331        // Process the JMS custom message properties
332        props.setProperty(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + "Properties", "" + data.get("Properties"));
333
334        return props;
335    }
336
337        @SuppressWarnings("unchecked")
338        protected String getDisplayString(Object obj) {
339                if (null == obj)
340                        return "null";
341                
342                if (obj != null && obj.getClass().isArray()) {
343                        Class type = obj.getClass().getComponentType();
344                        if (!type.isPrimitive()) {
345                                obj = Arrays.asList((Object[]) obj);
346                        } else {
347                                // for primitives, we can't use Arrays.toString(), so we have to roll something similar.
348                                int len = Array.getLength(obj);
349                                if (0 == len)
350                                        return "[]";
351                                StringBuilder bldr = new StringBuilder();
352                                bldr.append("[");
353                                for (int i = 0; i <= len; i++) {
354                                        bldr.append(Array.get(obj, i));
355                                        if (i + 1 >= len)
356                                                return bldr.append("]").toString();
357                                        bldr.append(",");
358                                }
359                        }
360
361                }
362        
363        return obj.toString();
364    }
365}