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     */
017    package org.apache.activemq.console.filter;
018    
019    import java.lang.reflect.Array;
020    import java.lang.reflect.Method;
021    import java.util.Enumeration;
022    import java.util.HashMap;
023    import java.util.Iterator;
024    import java.util.Map;
025    import java.util.Properties;
026    import java.util.Arrays;
027    
028    import javax.jms.DeliveryMode;
029    import javax.jms.JMSException;
030    import javax.management.Attribute;
031    import javax.management.AttributeList;
032    import javax.management.ObjectInstance;
033    import javax.management.ObjectName;
034    import javax.management.openmbean.CompositeDataSupport;
035    
036    import org.apache.activemq.command.ActiveMQBytesMessage;
037    import org.apache.activemq.command.ActiveMQDestination;
038    import org.apache.activemq.command.ActiveMQMapMessage;
039    import org.apache.activemq.command.ActiveMQMessage;
040    import org.apache.activemq.command.ActiveMQObjectMessage;
041    import org.apache.activemq.command.ActiveMQStreamMessage;
042    import org.apache.activemq.command.ActiveMQTextMessage;
043    import org.apache.activemq.console.util.AmqMessagesUtil;
044    
045    public 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    
269            // Get custom properties
270            Enumeration e = msg.getPropertyNames();
271            while (e.hasMoreElements()) {
272                String name = (String)e.nextElement();
273                if (msg.getObjectProperty(name) != null) {
274                    props.put(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + name, getDisplayString(msg.getObjectProperty(name)));
275                }
276            }
277    
278            return props;
279        }
280    
281        /**
282         * Transform an openMBean composite data to a Map
283         * 
284         * @param data - composite data to transform
285         * @return map object
286         */
287        protected Map transformToMap(CompositeDataSupport data) {
288            Properties props = new Properties();
289    
290            String typeName = data.getCompositeType().getTypeName();
291    
292            // Retrieve text message
293            if (typeName.equals(ActiveMQTextMessage.class.getName())) {
294                props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "Text", data.get("Text").toString());
295    
296                // Retrieve byte preview
297            } else if (typeName.equals(ActiveMQBytesMessage.class.getName())) {
298                props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "BodyLength", data.get("BodyLength").toString());
299                props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + "BodyPreview", new String((byte[])data.get("BodyPreview")));
300    
301                // Expand content map
302            } else if (typeName.equals(ActiveMQMapMessage.class.getName())) {
303                Map contentMap = (Map)data.get("ContentMap");
304                for (Iterator i = contentMap.keySet().iterator(); i.hasNext();) {
305                    String key = (String)i.next();
306                    props.setProperty(AmqMessagesUtil.JMS_MESSAGE_BODY_PREFIX + key, contentMap.get(key).toString());
307                }
308    
309                // Do nothing
310            } else if (typeName.equals(ActiveMQObjectMessage.class.getName()) || typeName.equals(ActiveMQStreamMessage.class.getName()) || typeName.equals(ActiveMQMessage.class.getName())) {
311    
312                // Unrecognized composite data. Throw exception.
313            } else {
314                throw new IllegalArgumentException("Unrecognized composite data to transform. composite type: " + typeName);
315            }
316    
317            // Process the JMS message header values
318            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSCorrelationID", "" + data.get("JMSCorrelationID"));
319            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSDestination", "" + data.get("JMSDestination"));
320            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSMessageID", "" + data.get("JMSMessageID"));
321            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSReplyTo", "" + data.get("JMSReplyTo"));
322            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSType", "" + data.get("JMSType"));
323            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSDeliveryMode", "" + data.get("JMSDeliveryMode"));
324            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSExpiration", "" + data.get("JMSExpiration"));
325            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSPriority", "" + data.get("JMSPriority"));
326            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSRedelivered", "" + data.get("JMSRedelivered"));
327            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_HEADER_PREFIX + "JMSTimestamp", "" + data.get("JMSTimestamp"));
328    
329            // Process the JMS custom message properties
330            props.setProperty(AmqMessagesUtil.JMS_MESSAGE_CUSTOM_PREFIX + "Properties", "" + data.get("Properties"));
331    
332            return props;
333        }
334    
335            @SuppressWarnings("unchecked")
336            protected String getDisplayString(Object obj) {
337                    if (null == obj)
338                            return "null";
339                    
340                    if (obj != null && obj.getClass().isArray()) {
341                            Class type = obj.getClass().getComponentType();
342                            if (!type.isPrimitive()) {
343                                    obj = Arrays.asList((Object[]) obj);
344                            } else {
345                                    // for primitives, we can't use Arrays.toString(), so we have to roll something similar.
346                                    int len = Array.getLength(obj);
347                                    if (0 == len)
348                                            return "[]";
349                                    StringBuilder bldr = new StringBuilder();
350                                    bldr.append("[");
351                                    for (int i = 0; i <= len; i++) {
352                                            bldr.append(Array.get(obj, i));
353                                            if (i + 1 >= len)
354                                                    return bldr.append("]").toString();
355                                            bldr.append(",");
356                                    }
357                            }
358    
359                    }
360            
361            return obj.toString();
362        }
363    }