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.jms.Destination; 030 import javax.management.openmbean.ArrayType; 031 import javax.management.openmbean.CompositeData; 032 import javax.management.openmbean.CompositeDataSupport; 033 import javax.management.openmbean.CompositeType; 034 import javax.management.openmbean.OpenDataException; 035 import javax.management.openmbean.OpenType; 036 import javax.management.openmbean.SimpleType; 037 import javax.management.openmbean.TabularType; 038 import javax.management.openmbean.TabularDataSupport; 039 040 import org.apache.activemq.command.ActiveMQBytesMessage; 041 import org.apache.activemq.command.ActiveMQMapMessage; 042 import org.apache.activemq.command.ActiveMQMessage; 043 import org.apache.activemq.command.ActiveMQObjectMessage; 044 import org.apache.activemq.command.ActiveMQStreamMessage; 045 import org.apache.activemq.command.ActiveMQTextMessage; 046 import org.apache.activemq.command.Message; 047 import static org.apache.activemq.broker.jmx.CompositeDataConstants.*; 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, MessageOpenTypeFactory> OPEN_TYPE_FACTORIES = new HashMap<Class, MessageOpenTypeFactory>(); 058 059 abstract static class AbstractOpenTypeFactory implements OpenTypeFactory { 060 061 private CompositeType compositeType; 062 private List<String> itemNamesList = new ArrayList<String>(); 063 private List<String> itemDescriptionsList = new ArrayList<String>(); 064 private 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 protected String getTypeName() { 113 return ActiveMQMessage.class.getName(); 114 } 115 116 protected void init() throws OpenDataException { 117 super.init(); 118 addItem("JMSCorrelationID", "JMSCorrelationID", SimpleType.STRING); 119 addItem("JMSDestination", "JMSDestination", SimpleType.STRING); 120 addItem("JMSMessageID", "JMSMessageID", SimpleType.STRING); 121 addItem("JMSReplyTo", "JMSReplyTo", SimpleType.STRING); 122 addItem("JMSType", "JMSType", SimpleType.STRING); 123 addItem("JMSDeliveryMode", "JMSDeliveryMode", SimpleType.STRING); 124 addItem("JMSExpiration", "JMSExpiration", SimpleType.LONG); 125 addItem("JMSPriority", "JMSPriority", SimpleType.INTEGER); 126 addItem("JMSRedelivered", "JMSRedelivered", SimpleType.BOOLEAN); 127 addItem("JMSTimestamp", "JMSTimestamp", SimpleType.DATE); 128 addItem(JMSXGROUP_ID, "Message Group ID", SimpleType.STRING); 129 addItem(JMSXGROUP_SEQ, "Message Group Sequence Number", SimpleType.INTEGER); 130 addItem(ORIGINAL_DESTINATION, "Original Destination Before Senting To DLQ", SimpleType.STRING); 131 addItem(CompositeDataConstants.PROPERTIES, "User Properties Text", SimpleType.STRING); 132 133 // now lets expose the type safe properties 134 stringPropertyTabularType = createTabularType(String.class, SimpleType.STRING); 135 booleanPropertyTabularType = createTabularType(Boolean.class, SimpleType.BOOLEAN); 136 bytePropertyTabularType = createTabularType(Byte.class, SimpleType.BYTE); 137 shortPropertyTabularType = createTabularType(Short.class, SimpleType.SHORT); 138 intPropertyTabularType = createTabularType(Integer.class, SimpleType.INTEGER); 139 longPropertyTabularType = createTabularType(Long.class, SimpleType.LONG); 140 floatPropertyTabularType = createTabularType(Float.class, SimpleType.FLOAT); 141 doublePropertyTabularType = createTabularType(Double.class, SimpleType.DOUBLE); 142 143 addItem(CompositeDataConstants.STRING_PROPERTIES, "User String Properties", stringPropertyTabularType); 144 addItem(CompositeDataConstants.BOOLEAN_PROPERTIES, "User Boolean Properties", booleanPropertyTabularType); 145 addItem(CompositeDataConstants.BYTE_PROPERTIES, "User Byte Properties", bytePropertyTabularType); 146 addItem(CompositeDataConstants.SHORT_PROPERTIES, "User Short Properties", shortPropertyTabularType); 147 addItem(CompositeDataConstants.INT_PROPERTIES, "User Integer Properties", intPropertyTabularType); 148 addItem(CompositeDataConstants.LONG_PROPERTIES, "User Long Properties", longPropertyTabularType); 149 addItem(CompositeDataConstants.FLOAT_PROPERTIES, "User Float Properties", floatPropertyTabularType); 150 addItem(CompositeDataConstants.DOUBLE_PROPERTIES, "User Double Properties", doublePropertyTabularType); 151 } 152 153 public Map<String, Object> getFields(Object o) throws OpenDataException { 154 ActiveMQMessage m = (ActiveMQMessage)o; 155 Map<String, Object> rc = super.getFields(o); 156 rc.put("JMSCorrelationID", m.getJMSCorrelationID()); 157 rc.put("JMSDestination", "" + m.getJMSDestination()); 158 rc.put("JMSMessageID", m.getJMSMessageID()); 159 rc.put("JMSReplyTo",toString(m.getJMSReplyTo())); 160 rc.put("JMSType", m.getJMSType()); 161 rc.put("JMSDeliveryMode", m.getJMSDeliveryMode() == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON-PERSISTENT"); 162 rc.put("JMSExpiration", Long.valueOf(m.getJMSExpiration())); 163 rc.put("JMSPriority", Integer.valueOf(m.getJMSPriority())); 164 rc.put("JMSRedelivered", Boolean.valueOf(m.getJMSRedelivered())); 165 rc.put("JMSTimestamp", new Date(m.getJMSTimestamp())); 166 rc.put(JMSXGROUP_ID, m.getGroupID()); 167 rc.put(JMSXGROUP_SEQ, m.getGroupSequence()); 168 rc.put(ORIGINAL_DESTINATION, toString(m.getOriginalDestination())); 169 try { 170 rc.put(CompositeDataConstants.PROPERTIES, "" + m.getProperties()); 171 } catch (IOException e) { 172 rc.put(CompositeDataConstants.PROPERTIES, ""); 173 } 174 175 try { 176 rc.put(CompositeDataConstants.STRING_PROPERTIES, createTabularData(m, stringPropertyTabularType, String.class)); 177 } catch (IOException e) { 178 rc.put(CompositeDataConstants.STRING_PROPERTIES, new TabularDataSupport(stringPropertyTabularType)); 179 } 180 try { 181 rc.put(CompositeDataConstants.BOOLEAN_PROPERTIES, createTabularData(m, booleanPropertyTabularType, Boolean.class)); 182 } catch (IOException e) { 183 rc.put(CompositeDataConstants.BOOLEAN_PROPERTIES, new TabularDataSupport(booleanPropertyTabularType)); 184 } 185 try { 186 rc.put(CompositeDataConstants.BYTE_PROPERTIES, createTabularData(m, bytePropertyTabularType, Byte.class)); 187 } catch (IOException e) { 188 rc.put(CompositeDataConstants.BYTE_PROPERTIES, new TabularDataSupport(bytePropertyTabularType)); 189 } 190 try { 191 rc.put(CompositeDataConstants.SHORT_PROPERTIES, createTabularData(m, shortPropertyTabularType, Short.class)); 192 } catch (IOException e) { 193 rc.put(CompositeDataConstants.SHORT_PROPERTIES, new TabularDataSupport(shortPropertyTabularType)); 194 } 195 try { 196 rc.put(CompositeDataConstants.INT_PROPERTIES, createTabularData(m, intPropertyTabularType, Integer.class)); 197 } catch (IOException e) { 198 rc.put(CompositeDataConstants.INT_PROPERTIES, new TabularDataSupport(intPropertyTabularType)); 199 } 200 try { 201 rc.put(CompositeDataConstants.LONG_PROPERTIES, createTabularData(m, longPropertyTabularType, Long.class)); 202 } catch (IOException e) { 203 rc.put(CompositeDataConstants.LONG_PROPERTIES, new TabularDataSupport(longPropertyTabularType)); 204 } 205 try { 206 rc.put(CompositeDataConstants.FLOAT_PROPERTIES, createTabularData(m, floatPropertyTabularType, Float.class)); 207 } catch (IOException e) { 208 rc.put(CompositeDataConstants.FLOAT_PROPERTIES, new TabularDataSupport(floatPropertyTabularType)); 209 } 210 try { 211 rc.put(CompositeDataConstants.DOUBLE_PROPERTIES, createTabularData(m, doublePropertyTabularType, Double.class)); 212 } catch (IOException e) { 213 rc.put(CompositeDataConstants.DOUBLE_PROPERTIES, new TabularDataSupport(doublePropertyTabularType)); 214 } 215 return rc; 216 } 217 218 protected String toString(Object value) { 219 if (value == null) { 220 return null; 221 } 222 return value.toString(); 223 } 224 225 226 protected <T> TabularType createTabularType(Class<T> type, OpenType openType) throws OpenDataException { 227 String typeName = "java.util.Map<java.lang.String, " + type.getName() + ">"; 228 String[] keyValue = new String[]{"key", "value"}; 229 OpenType[] openTypes = new OpenType[]{SimpleType.STRING, openType}; 230 CompositeType rowType = new CompositeType(typeName, typeName, keyValue, keyValue, openTypes); 231 return new TabularType(typeName, typeName, rowType, new String[]{"key"}); 232 } 233 234 protected TabularDataSupport createTabularData(ActiveMQMessage m, TabularType type, Class valueType) throws IOException, OpenDataException { 235 TabularDataSupport answer = new TabularDataSupport(type); 236 Set<Map.Entry<String,Object>> entries = m.getProperties().entrySet(); 237 for (Map.Entry<String, Object> entry : entries) { 238 Object value = entry.getValue(); 239 if (valueType.isInstance(value)) { 240 CompositeDataSupport compositeData = createTabularRowValue(type, entry.getKey(), value); 241 answer.put(compositeData); 242 } 243 } 244 return answer; 245 } 246 247 protected CompositeDataSupport createTabularRowValue(TabularType type, String key, Object value) throws OpenDataException { 248 Map<String,Object> fields = new HashMap<String, Object>(); 249 fields.put("key", key); 250 fields.put("value", value); 251 return new CompositeDataSupport(type.getRowType(), fields); 252 } 253 } 254 255 static class ByteMessageOpenTypeFactory extends MessageOpenTypeFactory { 256 257 258 protected String getTypeName() { 259 return ActiveMQBytesMessage.class.getName(); 260 } 261 262 protected void init() throws OpenDataException { 263 super.init(); 264 addItem(BODY_LENGTH, "Body length", SimpleType.LONG); 265 addItem(BODY_PREVIEW, "Body preview", new ArrayType(1, SimpleType.BYTE)); 266 } 267 268 public Map<String, Object> getFields(Object o) throws OpenDataException { 269 ActiveMQBytesMessage m = (ActiveMQBytesMessage)o; 270 Map<String, Object> rc = super.getFields(o); 271 long length = 0; 272 try { 273 length = m.getBodyLength(); 274 rc.put(BODY_LENGTH, Long.valueOf(length)); 275 } catch (JMSException e) { 276 rc.put(BODY_LENGTH, Long.valueOf(0)); 277 } 278 try { 279 byte preview[] = new byte[(int)Math.min(length, 255)]; 280 m.readBytes(preview); 281 282 // This is whack! Java 1.5 JMX spec does not support primitive 283 // arrays! 284 // In 1.6 it seems it is supported.. but until then... 285 Byte data[] = new Byte[preview.length]; 286 for (int i = 0; i < data.length; i++) { 287 data[i] = new Byte(preview[i]); 288 } 289 290 rc.put(BODY_PREVIEW, data); 291 } catch (JMSException e) { 292 rc.put(BODY_PREVIEW, new byte[] {}); 293 } 294 return rc; 295 } 296 297 } 298 299 static class MapMessageOpenTypeFactory extends MessageOpenTypeFactory { 300 301 protected String getTypeName() { 302 return ActiveMQMapMessage.class.getName(); 303 } 304 305 protected void init() throws OpenDataException { 306 super.init(); 307 addItem(CONTENT_MAP, "Content map", SimpleType.STRING); 308 } 309 310 public Map<String, Object> getFields(Object o) throws OpenDataException { 311 ActiveMQMapMessage m = (ActiveMQMapMessage)o; 312 Map<String, Object> rc = super.getFields(o); 313 try { 314 rc.put(CONTENT_MAP, "" + m.getContentMap()); 315 } catch (JMSException e) { 316 rc.put(CONTENT_MAP, ""); 317 } 318 return rc; 319 } 320 } 321 322 static class ObjectMessageOpenTypeFactory extends MessageOpenTypeFactory { 323 protected String getTypeName() { 324 return ActiveMQObjectMessage.class.getName(); 325 } 326 327 protected void init() throws OpenDataException { 328 super.init(); 329 } 330 331 public Map<String, Object> getFields(Object o) throws OpenDataException { 332 Map<String, Object> rc = super.getFields(o); 333 return rc; 334 } 335 } 336 337 static class StreamMessageOpenTypeFactory extends MessageOpenTypeFactory { 338 protected String getTypeName() { 339 return ActiveMQStreamMessage.class.getName(); 340 } 341 342 protected void init() throws OpenDataException { 343 super.init(); 344 } 345 346 public Map<String, Object> getFields(Object o) throws OpenDataException { 347 Map<String, Object> rc = super.getFields(o); 348 return rc; 349 } 350 } 351 352 static class TextMessageOpenTypeFactory extends MessageOpenTypeFactory { 353 354 protected String getTypeName() { 355 return ActiveMQTextMessage.class.getName(); 356 } 357 358 protected void init() throws OpenDataException { 359 super.init(); 360 addItem(MESSAGE_TEXT, MESSAGE_TEXT, SimpleType.STRING); 361 } 362 363 public Map<String, Object> getFields(Object o) throws OpenDataException { 364 ActiveMQTextMessage m = (ActiveMQTextMessage)o; 365 Map<String, Object> rc = super.getFields(o); 366 try { 367 rc.put(MESSAGE_TEXT, "" + m.getText()); 368 } catch (JMSException e) { 369 rc.put(MESSAGE_TEXT, ""); 370 } 371 return rc; 372 } 373 } 374 375 static { 376 OPEN_TYPE_FACTORIES.put(ActiveMQMessage.class, new MessageOpenTypeFactory()); 377 OPEN_TYPE_FACTORIES.put(ActiveMQBytesMessage.class, new ByteMessageOpenTypeFactory()); 378 OPEN_TYPE_FACTORIES.put(ActiveMQMapMessage.class, new MapMessageOpenTypeFactory()); 379 OPEN_TYPE_FACTORIES.put(ActiveMQObjectMessage.class, new ObjectMessageOpenTypeFactory()); 380 OPEN_TYPE_FACTORIES.put(ActiveMQStreamMessage.class, new StreamMessageOpenTypeFactory()); 381 OPEN_TYPE_FACTORIES.put(ActiveMQTextMessage.class, new TextMessageOpenTypeFactory()); 382 } 383 384 private OpenTypeSupport() { 385 } 386 387 public static OpenTypeFactory getFactory(Class<? extends Message> clazz) throws OpenDataException { 388 return OPEN_TYPE_FACTORIES.get(clazz); 389 } 390 391 public static CompositeData convert(Message message) throws OpenDataException { 392 OpenTypeFactory f = getFactory(message.getClass()); 393 if (f == null) { 394 throw new OpenDataException("Cannot create a CompositeData for type: " + message.getClass().getName()); 395 } 396 CompositeType ct = f.getCompositeType(); 397 Map<String, Object> fields = f.getFields(message); 398 return new CompositeDataSupport(ct, fields); 399 } 400 401 }