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.broker.jmx;
018    
019    import java.io.IOException;
020    import java.util.ArrayList;
021    import java.util.Date;
022    import java.util.HashMap;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Set;
026    
027    import javax.jms.DeliveryMode;
028    import javax.jms.JMSException;
029    import javax.management.openmbean.ArrayType;
030    import javax.management.openmbean.CompositeData;
031    import javax.management.openmbean.CompositeDataSupport;
032    import javax.management.openmbean.CompositeType;
033    import javax.management.openmbean.OpenDataException;
034    import javax.management.openmbean.OpenType;
035    import javax.management.openmbean.SimpleType;
036    import javax.management.openmbean.TabularDataSupport;
037    import javax.management.openmbean.TabularType;
038    
039    import org.apache.activemq.broker.region.policy.SlowConsumerEntry;
040    import org.apache.activemq.broker.scheduler.Job;
041    import org.apache.activemq.command.ActiveMQBlobMessage;
042    import org.apache.activemq.command.ActiveMQBytesMessage;
043    import org.apache.activemq.command.ActiveMQMapMessage;
044    import org.apache.activemq.command.ActiveMQMessage;
045    import org.apache.activemq.command.ActiveMQObjectMessage;
046    import org.apache.activemq.command.ActiveMQStreamMessage;
047    import org.apache.activemq.command.ActiveMQTextMessage;
048    
049    public final class OpenTypeSupport {
050    
051        interface OpenTypeFactory {
052            CompositeType getCompositeType() throws OpenDataException;
053    
054            Map<String, Object> getFields(Object o) throws OpenDataException;
055        }
056    
057        private static final Map<Class, AbstractOpenTypeFactory> OPEN_TYPE_FACTORIES = new HashMap<Class, AbstractOpenTypeFactory>();
058    
059        abstract static class AbstractOpenTypeFactory implements OpenTypeFactory {
060    
061            private CompositeType compositeType;
062            private final List<String> itemNamesList = new ArrayList<String>();
063            private final List<String> itemDescriptionsList = new ArrayList<String>();
064            private final List<OpenType> itemTypesList = new ArrayList<OpenType>();
065    
066            public CompositeType getCompositeType() throws OpenDataException {
067                if (compositeType == null) {
068                    init();
069                    compositeType = createCompositeType();
070                }
071                return compositeType;
072            }
073    
074            protected void init() throws OpenDataException {
075            }
076    
077            protected CompositeType createCompositeType() throws OpenDataException {
078                String[] itemNames = itemNamesList.toArray(new String[itemNamesList.size()]);
079                String[] itemDescriptions = itemDescriptionsList.toArray(new String[itemDescriptionsList.size()]);
080                OpenType[] itemTypes = itemTypesList.toArray(new OpenType[itemTypesList.size()]);
081                return new CompositeType(getTypeName(), getDescription(), itemNames, itemDescriptions, itemTypes);
082            }
083    
084            protected abstract String getTypeName();
085    
086            protected void addItem(String name, String description, OpenType type) {
087                itemNamesList.add(name);
088                itemDescriptionsList.add(description);
089                itemTypesList.add(type);
090            }
091    
092            protected String getDescription() {
093                return getTypeName();
094            }
095    
096            public Map<String, Object> getFields(Object o) throws OpenDataException {
097                Map<String, Object> rc = new HashMap<String, Object>();
098                return rc;
099            }
100        }
101    
102        static class MessageOpenTypeFactory extends AbstractOpenTypeFactory {
103            protected TabularType stringPropertyTabularType;
104            protected TabularType booleanPropertyTabularType;
105            protected TabularType bytePropertyTabularType;
106            protected TabularType shortPropertyTabularType;
107            protected TabularType intPropertyTabularType;
108            protected TabularType longPropertyTabularType;
109            protected TabularType floatPropertyTabularType;
110            protected TabularType doublePropertyTabularType;
111    
112            @Override
113            protected String getTypeName() {
114                return ActiveMQMessage.class.getName();
115            }
116    
117            @Override
118            protected void init() throws OpenDataException {
119                super.init();
120                addItem("JMSCorrelationID", "JMSCorrelationID", SimpleType.STRING);
121                addItem("JMSDestination", "JMSDestination", SimpleType.STRING);
122                addItem("JMSMessageID", "JMSMessageID", SimpleType.STRING);
123                addItem("JMSReplyTo", "JMSReplyTo", SimpleType.STRING);
124                addItem("JMSType", "JMSType", SimpleType.STRING);
125                addItem("JMSDeliveryMode", "JMSDeliveryMode", SimpleType.STRING);
126                addItem("JMSExpiration", "JMSExpiration", SimpleType.LONG);
127                addItem("JMSPriority", "JMSPriority", SimpleType.INTEGER);
128                addItem("JMSRedelivered", "JMSRedelivered", SimpleType.BOOLEAN);
129                addItem("JMSTimestamp", "JMSTimestamp", SimpleType.DATE);
130                addItem(CompositeDataConstants.JMSXGROUP_ID, "Message Group ID", SimpleType.STRING);
131                addItem(CompositeDataConstants.JMSXGROUP_SEQ, "Message Group Sequence Number", SimpleType.INTEGER);
132                addItem(CompositeDataConstants.ORIGINAL_DESTINATION, "Original Destination Before Senting To DLQ", SimpleType.STRING);
133                addItem(CompositeDataConstants.PROPERTIES, "User Properties Text", SimpleType.STRING);
134    
135                // now lets expose the type safe properties
136                stringPropertyTabularType = createTabularType(String.class, SimpleType.STRING);
137                booleanPropertyTabularType = createTabularType(Boolean.class, SimpleType.BOOLEAN);
138                bytePropertyTabularType = createTabularType(Byte.class, SimpleType.BYTE);
139                shortPropertyTabularType = createTabularType(Short.class, SimpleType.SHORT);
140                intPropertyTabularType = createTabularType(Integer.class, SimpleType.INTEGER);
141                longPropertyTabularType = createTabularType(Long.class, SimpleType.LONG);
142                floatPropertyTabularType = createTabularType(Float.class, SimpleType.FLOAT);
143                doublePropertyTabularType = createTabularType(Double.class, SimpleType.DOUBLE);
144    
145                addItem(CompositeDataConstants.STRING_PROPERTIES, "User String Properties", stringPropertyTabularType);
146                addItem(CompositeDataConstants.BOOLEAN_PROPERTIES, "User Boolean Properties", booleanPropertyTabularType);
147                addItem(CompositeDataConstants.BYTE_PROPERTIES, "User Byte Properties", bytePropertyTabularType);
148                addItem(CompositeDataConstants.SHORT_PROPERTIES, "User Short Properties", shortPropertyTabularType);
149                addItem(CompositeDataConstants.INT_PROPERTIES, "User Integer Properties", intPropertyTabularType);
150                addItem(CompositeDataConstants.LONG_PROPERTIES, "User Long Properties", longPropertyTabularType);
151                addItem(CompositeDataConstants.FLOAT_PROPERTIES, "User Float Properties", floatPropertyTabularType);
152                addItem(CompositeDataConstants.DOUBLE_PROPERTIES, "User Double Properties", doublePropertyTabularType);
153            }
154    
155            @Override
156            public Map<String, Object> getFields(Object o) throws OpenDataException {
157                ActiveMQMessage m = (ActiveMQMessage)o;
158                Map<String, Object> rc = super.getFields(o);
159                rc.put("JMSCorrelationID", m.getJMSCorrelationID());
160                rc.put("JMSDestination", "" + m.getJMSDestination());
161                rc.put("JMSMessageID", m.getJMSMessageID());
162                rc.put("JMSReplyTo",toString(m.getJMSReplyTo()));
163                rc.put("JMSType", m.getJMSType());
164                rc.put("JMSDeliveryMode", m.getJMSDeliveryMode() == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON-PERSISTENT");
165                rc.put("JMSExpiration", Long.valueOf(m.getJMSExpiration()));
166                rc.put("JMSPriority", Integer.valueOf(m.getJMSPriority()));
167                rc.put("JMSRedelivered", Boolean.valueOf(m.getJMSRedelivered()));
168                rc.put("JMSTimestamp", new Date(m.getJMSTimestamp()));
169                rc.put(CompositeDataConstants.JMSXGROUP_ID, m.getGroupID());
170                rc.put(CompositeDataConstants.JMSXGROUP_SEQ, m.getGroupSequence());
171                rc.put(CompositeDataConstants.ORIGINAL_DESTINATION, toString(m.getOriginalDestination()));
172                try {
173                    rc.put(CompositeDataConstants.PROPERTIES, "" + m.getProperties());
174                } catch (IOException e) {
175                    rc.put(CompositeDataConstants.PROPERTIES, "");
176                }
177    
178                try {
179                    rc.put(CompositeDataConstants.STRING_PROPERTIES, createTabularData(m, stringPropertyTabularType, String.class));
180                } catch (IOException e) {
181                    rc.put(CompositeDataConstants.STRING_PROPERTIES, new TabularDataSupport(stringPropertyTabularType));
182                }
183                try {
184                    rc.put(CompositeDataConstants.BOOLEAN_PROPERTIES, createTabularData(m, booleanPropertyTabularType, Boolean.class));
185                } catch (IOException e) {
186                    rc.put(CompositeDataConstants.BOOLEAN_PROPERTIES, new TabularDataSupport(booleanPropertyTabularType));
187                }
188                try {
189                    rc.put(CompositeDataConstants.BYTE_PROPERTIES, createTabularData(m, bytePropertyTabularType, Byte.class));
190                } catch (IOException e) {
191                    rc.put(CompositeDataConstants.BYTE_PROPERTIES, new TabularDataSupport(bytePropertyTabularType));
192                }
193                try {
194                    rc.put(CompositeDataConstants.SHORT_PROPERTIES, createTabularData(m, shortPropertyTabularType, Short.class));
195                } catch (IOException e) {
196                    rc.put(CompositeDataConstants.SHORT_PROPERTIES, new TabularDataSupport(shortPropertyTabularType));
197                }
198                try {
199                    rc.put(CompositeDataConstants.INT_PROPERTIES, createTabularData(m, intPropertyTabularType, Integer.class));
200                } catch (IOException e) {
201                    rc.put(CompositeDataConstants.INT_PROPERTIES, new TabularDataSupport(intPropertyTabularType));
202                }
203                try {
204                    rc.put(CompositeDataConstants.LONG_PROPERTIES, createTabularData(m, longPropertyTabularType, Long.class));
205                } catch (IOException e) {
206                    rc.put(CompositeDataConstants.LONG_PROPERTIES, new TabularDataSupport(longPropertyTabularType));
207                }
208                try {
209                    rc.put(CompositeDataConstants.FLOAT_PROPERTIES, createTabularData(m, floatPropertyTabularType, Float.class));
210                } catch (IOException e) {
211                    rc.put(CompositeDataConstants.FLOAT_PROPERTIES, new TabularDataSupport(floatPropertyTabularType));
212                }
213                try {
214                    rc.put(CompositeDataConstants.DOUBLE_PROPERTIES, createTabularData(m, doublePropertyTabularType, Double.class));
215                } catch (IOException e) {
216                    rc.put(CompositeDataConstants.DOUBLE_PROPERTIES, new TabularDataSupport(doublePropertyTabularType));
217                }
218                return rc;
219            }
220    
221            protected String toString(Object value) {
222                if (value == null) {
223                    return null;
224                }
225                return value.toString();
226            }
227    
228    
229            protected <T> TabularType createTabularType(Class<T> type, OpenType openType) throws OpenDataException {
230                String typeName = "java.util.Map<java.lang.String, " + type.getName() + ">";
231                String[] keyValue = new String[]{"key", "value"};
232                OpenType[] openTypes = new OpenType[]{SimpleType.STRING, openType};
233                CompositeType rowType = new CompositeType(typeName, typeName, keyValue, keyValue, openTypes);
234                return new TabularType(typeName, typeName, rowType, new String[]{"key"});
235            }
236    
237            protected TabularDataSupport createTabularData(ActiveMQMessage m, TabularType type, Class valueType) throws IOException, OpenDataException {
238                TabularDataSupport answer = new TabularDataSupport(type);
239                Set<Map.Entry<String,Object>> entries = m.getProperties().entrySet();
240                for (Map.Entry<String, Object> entry : entries) {
241                    Object value = entry.getValue();
242                    if (valueType.isInstance(value)) {
243                        CompositeDataSupport compositeData = createTabularRowValue(type, entry.getKey(), value);
244                        answer.put(compositeData);
245                    }
246                }
247                return answer;
248            }
249    
250            protected CompositeDataSupport createTabularRowValue(TabularType type, String key, Object value) throws OpenDataException {
251                Map<String,Object> fields = new HashMap<String, Object>();
252                fields.put("key", key);
253                fields.put("value", value);
254                return new CompositeDataSupport(type.getRowType(), fields);
255            }
256        }
257    
258        static class ByteMessageOpenTypeFactory extends MessageOpenTypeFactory {
259    
260    
261            @Override
262            protected String getTypeName() {
263                return ActiveMQBytesMessage.class.getName();
264            }
265    
266            @Override
267            protected void init() throws OpenDataException {
268                super.init();
269                addItem(CompositeDataConstants.BODY_LENGTH, "Body length", SimpleType.LONG);
270                addItem(CompositeDataConstants.BODY_PREVIEW, "Body preview", new ArrayType(1, SimpleType.BYTE));
271            }
272    
273            @Override
274            public Map<String, Object> getFields(Object o) throws OpenDataException {
275                ActiveMQBytesMessage m = (ActiveMQBytesMessage)o;
276                m.setReadOnlyBody(true);
277                Map<String, Object> rc = super.getFields(o);
278                long length = 0;
279                try {
280                    length = m.getBodyLength();
281                    rc.put(CompositeDataConstants.BODY_LENGTH, Long.valueOf(length));
282                } catch (JMSException e) {
283                    rc.put(CompositeDataConstants.BODY_LENGTH, Long.valueOf(0));
284                }
285                try {
286                    byte preview[] = new byte[(int)Math.min(length, 255)];
287                    m.readBytes(preview);
288                    m.reset();
289    
290                    // This is whack! Java 1.5 JMX spec does not support primitive
291                    // arrays!
292                    // In 1.6 it seems it is supported.. but until then...
293                    Byte data[] = new Byte[preview.length];
294                    for (int i = 0; i < data.length; i++) {
295                        data[i] = new Byte(preview[i]);
296                    }
297    
298                    rc.put(CompositeDataConstants.BODY_PREVIEW, data);
299                } catch (JMSException e) {
300                    rc.put(CompositeDataConstants.BODY_PREVIEW, new Byte[] {});
301                }
302                return rc;
303            }
304    
305        }
306    
307        static class MapMessageOpenTypeFactory extends MessageOpenTypeFactory {
308    
309            @Override
310            protected String getTypeName() {
311                return ActiveMQMapMessage.class.getName();
312            }
313    
314            @Override
315            protected void init() throws OpenDataException {
316                super.init();
317                addItem(CompositeDataConstants.CONTENT_MAP, "Content map", SimpleType.STRING);
318            }
319    
320            @Override
321            public Map<String, Object> getFields(Object o) throws OpenDataException {
322                ActiveMQMapMessage m = (ActiveMQMapMessage)o;
323                Map<String, Object> rc = super.getFields(o);
324                try {
325                    rc.put(CompositeDataConstants.CONTENT_MAP, "" + m.getContentMap());
326                } catch (JMSException e) {
327                    rc.put(CompositeDataConstants.CONTENT_MAP, "");
328                }
329                return rc;
330            }
331        }
332    
333        static class ObjectMessageOpenTypeFactory extends MessageOpenTypeFactory {
334            @Override
335            protected String getTypeName() {
336                return ActiveMQObjectMessage.class.getName();
337            }
338    
339            @Override
340            protected void init() throws OpenDataException {
341                super.init();
342            }
343    
344            @Override
345            public Map<String, Object> getFields(Object o) throws OpenDataException {
346                Map<String, Object> rc = super.getFields(o);
347                return rc;
348            }
349        }
350    
351        static class StreamMessageOpenTypeFactory extends MessageOpenTypeFactory {
352            @Override
353            protected String getTypeName() {
354                return ActiveMQStreamMessage.class.getName();
355            }
356    
357            @Override
358            protected void init() throws OpenDataException {
359                super.init();
360            }
361    
362            @Override
363            public Map<String, Object> getFields(Object o) throws OpenDataException {
364                Map<String, Object> rc = super.getFields(o);
365                return rc;
366            }
367        }
368    
369        static class TextMessageOpenTypeFactory extends MessageOpenTypeFactory {
370    
371            @Override
372            protected String getTypeName() {
373                return ActiveMQTextMessage.class.getName();
374            }
375    
376            @Override
377            protected void init() throws OpenDataException {
378                super.init();
379                addItem(CompositeDataConstants.MESSAGE_TEXT, CompositeDataConstants.MESSAGE_TEXT, SimpleType.STRING);
380            }
381    
382            @Override
383            public Map<String, Object> getFields(Object o) throws OpenDataException {
384                ActiveMQTextMessage m = (ActiveMQTextMessage)o;
385                Map<String, Object> rc = super.getFields(o);
386                try {
387                    rc.put(CompositeDataConstants.MESSAGE_TEXT, "" + m.getText());
388                } catch (JMSException e) {
389                    rc.put(CompositeDataConstants.MESSAGE_TEXT, "");
390                }
391                return rc;
392            }
393        }
394    
395    
396        static class JobOpenTypeFactory extends AbstractOpenTypeFactory {
397    
398            @Override
399            protected String getTypeName() {
400                return Job.class.getName();
401            }
402    
403            @Override
404            protected void init() throws OpenDataException {
405                super.init();
406                addItem("jobId", "jobId", SimpleType.STRING);
407                addItem("cronEntry", "Cron entry", SimpleType.STRING);
408                addItem("start", "start time", SimpleType.STRING);
409                addItem("delay", "initial delay", SimpleType.LONG);
410                addItem("next", "next time", SimpleType.STRING);
411                addItem("period", "period between jobs", SimpleType.LONG);
412                addItem("repeat", "number of times to repeat", SimpleType.INTEGER);
413            }
414    
415            @Override
416            public Map<String, Object> getFields(Object o) throws OpenDataException {
417                Job job = (Job) o;
418                Map<String, Object> rc = super.getFields(o);
419                rc.put("jobId", job.getJobId());
420                rc.put("cronEntry", "" + job.getCronEntry());
421                rc.put("start", job.getStartTime());
422                rc.put("delay", job.getDelay());
423                rc.put("next", job.getNextExecutionTime());
424                rc.put("period", job.getPeriod());
425                rc.put("repeat", job.getRepeat());
426                return rc;
427            }
428        }
429    
430        static class ActiveMQBlobMessageOpenTypeFactory extends MessageOpenTypeFactory {
431    
432            @Override
433            protected String getTypeName() {
434                return ActiveMQBlobMessage.class.getName();
435            }
436    
437            @Override
438            protected void init() throws OpenDataException {
439                super.init();
440                addItem(CompositeDataConstants.MESSAGE_URL, "Body Url", SimpleType.STRING);
441            }
442    
443            @Override
444            public Map<String, Object> getFields(Object o) throws OpenDataException {
445                ActiveMQBlobMessage m = (ActiveMQBlobMessage)o;
446                Map<String, Object> rc = super.getFields(o);
447                try {
448                    rc.put(CompositeDataConstants.MESSAGE_URL, "" + m.getURL().toString());
449                } catch (JMSException e) {
450                    rc.put(CompositeDataConstants.MESSAGE_URL, "");
451                }
452                return rc;
453            }
454        }
455    
456        static class SlowConsumerEntryOpenTypeFactory extends AbstractOpenTypeFactory {
457           @Override
458            protected String getTypeName() {
459                return SlowConsumerEntry.class.getName();
460            }
461    
462            @Override
463            protected void init() throws OpenDataException {
464                super.init();
465                addItem("subscription", "the subscription view", SimpleType.OBJECTNAME);
466                addItem("slowCount", "number of times deemed slow", SimpleType.INTEGER);
467                addItem("markCount", "number of periods remaining slow", SimpleType.INTEGER);
468            }
469    
470            @Override
471            public Map<String, Object> getFields(Object o) throws OpenDataException {
472                SlowConsumerEntry entry = (SlowConsumerEntry) o;
473                Map<String, Object> rc = super.getFields(o);
474                rc.put("subscription", entry.getSubscription());
475                rc.put("slowCount", Integer.valueOf(entry.getSlowCount()));
476                rc.put("markCount", Integer.valueOf(entry.getMarkCount()));
477                return rc;
478            }
479        }
480    
481        static class HealthStatusOpenTypeFactory extends AbstractOpenTypeFactory {
482            @Override
483            protected String getTypeName() {
484                return HealthStatus.class.getName();
485            }
486    
487            @Override
488            protected void init() throws OpenDataException {
489                super.init();
490                addItem("healthId", "health check id", SimpleType.STRING);
491                addItem("level", "severity", SimpleType.STRING);
492                addItem("message", "severity", SimpleType.STRING);
493                addItem("resource", "event resource", SimpleType.STRING);
494            }
495    
496            @Override
497            public Map<String, Object> getFields(Object o) throws OpenDataException {
498                HealthStatus event = (HealthStatus) o;
499                Map<String, Object> rc = super.getFields(o);
500                rc.put("healthId", event.getHealthId());
501                rc.put("level", event.getLevel());
502                rc.put("message", event.getMessage());
503                rc.put("resource", event.getResource());
504                return rc;
505            }
506        }
507    
508        static {
509            OPEN_TYPE_FACTORIES.put(ActiveMQMessage.class, new MessageOpenTypeFactory());
510            OPEN_TYPE_FACTORIES.put(ActiveMQBytesMessage.class, new ByteMessageOpenTypeFactory());
511            OPEN_TYPE_FACTORIES.put(ActiveMQMapMessage.class, new MapMessageOpenTypeFactory());
512            OPEN_TYPE_FACTORIES.put(ActiveMQObjectMessage.class, new ObjectMessageOpenTypeFactory());
513            OPEN_TYPE_FACTORIES.put(ActiveMQStreamMessage.class, new StreamMessageOpenTypeFactory());
514            OPEN_TYPE_FACTORIES.put(ActiveMQTextMessage.class, new TextMessageOpenTypeFactory());
515            OPEN_TYPE_FACTORIES.put(Job.class, new JobOpenTypeFactory());
516            OPEN_TYPE_FACTORIES.put(SlowConsumerEntry.class, new SlowConsumerEntryOpenTypeFactory());
517            OPEN_TYPE_FACTORIES.put(ActiveMQBlobMessage.class, new ActiveMQBlobMessageOpenTypeFactory());
518            OPEN_TYPE_FACTORIES.put(HealthStatus.class, new HealthStatusOpenTypeFactory());
519        }
520    
521        private OpenTypeSupport() {
522        }
523    
524        public static OpenTypeFactory getFactory(Class<?> clazz) throws OpenDataException {
525            return OPEN_TYPE_FACTORIES.get(clazz);
526        }
527    
528        public static CompositeData convert(Object message) throws OpenDataException {
529            OpenTypeFactory f = getFactory(message.getClass());
530            if (f == null) {
531                throw new OpenDataException("Cannot create a CompositeData for type: " + message.getClass().getName());
532            }
533            CompositeType ct = f.getCompositeType();
534            Map<String, Object> fields = f.getFields(message);
535            return new CompositeDataSupport(ct, fields);
536        }
537    
538    }