View Javadoc
1 /*** 2 * Copyright (c) 2002, CodeStreet LLC. All rights reserved. 3 * <p> 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * <p> 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. Redistributions in binary 9 * form must reproduce the above copyright notice, this list of conditions and 10 * the following disclaimer in the documentation and/or other materials provided 11 * with the distribution. Neither the name of CodeStreet LLC. nor the names of 12 * its contributors may be used to endorse or promote products derived from this 13 * software without specific prior written permission. 14 * <p> 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * <p> 27 */ 28 29 package com.codestreet.messageforge; 30 31 import java.util.Date; 32 import java.sql.Timestamp; 33 import java.util.Iterator; 34 import java.util.Map; 35 import java.util.HashMap; 36 37 import java.lang.reflect.*; 38 import java.math.BigDecimal; 39 40 import javax.jms.*; 41 42 /*** 43 * Converter class for (Java) Beans. 44 * <p> 45 * A bean must satisfy the following requirements: it must have a no-arg 46 * constructor, all properties that it wishes to be marshalled must have public 47 * get/set methods, the get and set methods must follow the design pattern 48 * getXXX() or GetXXX()and setXXX() or SetXXX(), and the set methods must take 49 * one arg. 50 * </p> 51 * <p> 52 * Bean properties can primitive types (e.g. int), wrappers of primitive types 53 * (e.g. Integer), arrays of primitives (e.g. int[]), arrays of primitive 54 * wrappers (e.g. Integer[]), String, Date, BigDecimal,, String[], Date[], 55 * BigDecimal[], nested beans and arrays of nested beans. 56 * </p> 57 * <p> 58 * This implementation assumes that the following JMS extensions are supported 59 * by the JMS provider: Nested messages are allowed, arrays of primitive types 60 * are allowed, and arrays of wrappers of primitive types are allowed. All these 61 * assumptions are valid for TIBCO's E4JMS. 62 * 63 * @author <a href="mailto:jawaid.hakim@codestreet.com">jawaid.hakim </a> 64 */ 65 public abstract class ConverterBeanAndJMS extends Converter 66 { 67 /*** 68 * Marshal a bean into a JMS message. 69 * 70 * @param bean 71 * Bean. 72 * @param session 73 * JMS session - this is required to create the JMS message. 74 * @return Marshalled bean. 75 * @throws ConverterException 76 */ 77 public static MapMessage marshal(Object bean, Session session) 78 throws ConverterException 79 { 80 try 81 { 82 Class beanClass = bean.getClass(); 83 RBeanInfo beanInfo = BeanFactory.createBeanInfo( 84 beanClass.getName(), beanClass); 85 86 MapMessage nestedMsg = session.createMapMessage(); 87 nestedMsg.setString(BEANNAME_FIELD, getBeanClassName(beanInfo 88 .getName())); 89 90 for (Iterator getters = beanInfo.getGetters().entrySet().iterator(); getters.hasNext(); ) 91 { 92 Map.Entry entry = (Map.Entry)getters.next(); 93 Method method = (Method) entry.getValue(); 94 Object val = method.invoke(bean, EMPTY_PARAMS); 95 if (val != null) 96 { 97 String fldName = (String)entry.getKey(); 98 Class valType = val.getClass(); 99 if (valType.equals(String.class)) 100 { 101 nestedMsg.setObject(fldName, val.toString()); 102 } 103 else if (isPrimitiveWrapperType(valType)) 104 { 105 nestedMsg.setObject(fldName, val); 106 } 107 else if (valType.equals(BigDecimal.class)) 108 { 109 nestedMsg.setObject(fldName, Converter.getBigDecimalFormatter().format(val)); 110 } 111 else if (valType.equals(Timestamp.class)) 112 { 113 Timestamp realVal = (Timestamp) val; 114 String sVal = RDateFormat.getInstance().format( 115 new Date(realVal.getTime() 116 + (realVal.getNanos() / 1000000))); 117 nestedMsg.setObject(fldName, sVal); 118 } 119 else if (Date.class.isAssignableFrom(valType)) 120 { 121 String sVal = RDateFormat.getInstance().format((Date) val); 122 nestedMsg.setObject(fldName, sVal); 123 } 124 else if (valType.isArray()) 125 { 126 int len = Array.getLength(val); 127 nestedMsg.setBooleanProperty("JMS_TIBCO_MSG_EXT", true); 128 Class arrayType = val.getClass(); 129 if (isPrimitiveArray(arrayType) 130 || isPrimitiveWrapperArray(arrayType)) 131 { 132 // TODO: assumes that arrays of primitives and 133 // primitive wrappers are supported by the JMS 134 // provider 135 nestedMsg.setObject(fldName, val); 136 continue; 137 } 138 MapMessage arrayMsg = session.createMapMessage(); 139 int realLen = 0; 140 for (int i = 0; i < len; ++i) 141 { 142 Object elem = Array.get(val, i); 143 if (elem != null) 144 { 145 String index = String.valueOf(i); 146 if (elem.getClass().equals(String.class)) 147 { 148 arrayMsg.setObject(index, elem); 149 } 150 else if (valType.equals(BigDecimal.class)) 151 { 152 arrayMsg.setObject(index, Converter.getBigDecimalFormatter().format(elem)); 153 } 154 else if (elem.getClass() 155 .equals(Timestamp.class)) 156 { 157 Timestamp realVal = (Timestamp) elem; 158 arrayMsg 159 .setObject( 160 index, 161 RDateFormat.getInstance() 162 .format( 163 new Date( 164 realVal 165 .getTime() 166 + (realVal 167 .getNanos() / 1000000)))); 168 } 169 else if (Date.class.isAssignableFrom(elem.getClass())) 170 { 171 arrayMsg.setObject(index, 172 RDateFormat.getInstance().format( 173 (Date) elem)); 174 } 175 else 176 { 177 // TODO: assumes that this is a bean 178 if (realLen == 0) 179 arrayMsg.setBooleanProperty( 180 "JMS_TIBCO_MSG_EXT", true); 181 arrayMsg.setObject(String.valueOf(realLen), 182 marshal(elem, session)); 183 } 184 ++realLen; 185 } 186 } 187 arrayMsg.setInt("length", realLen); 188 nestedMsg.setObject(fldName, arrayMsg); 189 } 190 else if (val instanceof Message) 191 { 192 nestedMsg.setBooleanProperty("JMS_TIBCO_MSG_EXT", true); 193 nestedMsg.setObject(fldName, val); 194 } 195 else if (val instanceof java.util.Collection) 196 { 197 // TODO: check why User does not lazy load collections 198 // throw new ConverterException("Cannot marshal 199 // collection classes"); 200 } 201 else 202 { 203 // Handle everything else like a bean 204 nestedMsg.setBooleanProperty("JMS_TIBCO_MSG_EXT", true); 205 nestedMsg.setObject(fldName, marshal(val, session)); 206 } 207 } 208 } 209 210 return nestedMsg; 211 } 212 catch (Exception ex) 213 { 214 throw new ConverterException(ex); 215 } 216 } 217 218 /*** 219 * Unmarshal a bean from a JMS message. 220 * 221 * @param source 222 * JMS message. 223 * @return New bean instance. 224 * @throws ConverterException 225 */ 226 public static Object unmarshal(MapMessage source) throws ConverterException 227 { 228 try 229 { 230 String beanName = source.getString(BEANNAME_FIELD); 231 if (beanName == null) 232 throw new ConverterException("Bean type not found"); 233 234 // Instantiate bean 235 String fullName = RMsgFactoryImpl.getInstance() 236 .getFullBeanFieldName(beanName); 237 if (fullName == null) 238 throw new ConverterException("Qualified bean name not found"); 239 240 return unmarshal(source, beanName, fullName); 241 } 242 catch (Exception ex) 243 { 244 throw new ConverterException(ex); 245 } 246 } 247 248 /*** 249 * Unmarshal a bean from a JMS message. 250 * 251 * @param source 252 * JMS message. 253 * @param beanName 254 * Short name of bean. 255 * @param fullName 256 * Full name of bean (includes the package). 257 * @return New bean instance. 258 * @throws ConverterException 259 */ 260 public static Object unmarshal(MapMessage source, String beanName, 261 String fullName) throws ConverterException 262 { 263 try 264 { 265 Object bean = Class.forName(fullName).newInstance(); 266 267 // Set bean properties 268 RBeanInfo beanInfo = BeanFactory.createBeanInfo(beanName, bean 269 .getClass()); 270 for (Iterator setters = beanInfo.getSetters().values().iterator(); setters 271 .hasNext();) 272 { 273 Method method = (Method) setters.next(); 274 String fldName = method.getName().substring(3); 275 if (source.itemExists(fldName)) 276 { 277 Object val = source.getObject(fldName); 278 Class[] methodParameters = method.getParameterTypes(); 279 Class valType = methodParameters[0]; 280 if (valType.equals(String.class)) 281 { 282 Object[] parameters = {val.toString()}; 283 method.invoke(bean, parameters); 284 } 285 else if (isPrimitiveWrapperType(valType)) 286 { 287 Object[] parameters = {val}; 288 method.invoke(bean, parameters); 289 } 290 else if (valType.equals(BigDecimal.class)) 291 { 292 Object[] parameters = {new BigDecimal(val.toString())}; 293 method.invoke(bean, parameters); 294 } 295 else if (valType.equals(Timestamp.class)) 296 { 297 Object[] parameters = {new Timestamp( 298 RDateFormat.getInstance().parse(val.toString()) 299 .getTime())}; 300 method.invoke(bean, parameters); 301 } 302 else if (Date.class.isAssignableFrom(valType)) 303 { 304 Object[] parameters = {RDateFormat.getInstance().parse( 305 val.toString())}; 306 method.invoke(bean, parameters); 307 } 308 else if (valType.isArray()) 309 { 310 if (isPrimitiveArray(valType)) 311 { 312 Object[] parameters = {val}; 313 method.invoke(bean, parameters); 314 } 315 else if (isPrimitiveWrapperArray(valType)) 316 { 317 Object[] parameters = {val}; 318 method.invoke(bean, parameters); 319 } 320 else 321 { 322 // BigDecimal, String, Date, or Bean array 323 MapMessage arrayMsg = (MapMessage) val; 324 int len = arrayMsg.getInt("length"); 325 String arrayClsName = valType.getName(); 326 Object newArray = Array.newInstance(Class 327 .forName(arrayClsName.substring(2, 328 arrayClsName.indexOf(';'))), len); 329 for (int i = 0; i < len; ++i) 330 { 331 Object elem = arrayMsg.getObject(String 332 .valueOf(i)); 333 if (arrayClsName.equals("[Ljava.lang.String;")) 334 { 335 Array.set(newArray, i, elem); 336 } 337 else if (arrayClsName 338 .equals("[Ljava.math.BigDecimal;")) 339 { 340 Array.set(newArray, i, new BigDecimal(elem 341 .toString())); 342 } 343 else if (arrayClsName 344 .equals("[Ljava.util.Date;")) 345 { 346 Array.set(newArray, i, 347 RDateFormat.getInstance().parse( 348 elem.toString())); 349 } 350 else if (arrayClsName 351 .equals("[Ljava.sql.Timestamp;")) 352 { 353 Array 354 .set( 355 newArray, 356 i, 357 new Timestamp( 358 RDateFormat.getInstance() 359 .parse( 360 elem 361 .toString()) 362 .getTime())); 363 } 364 else if (elem instanceof MapMessage) 365 { 366 // TODO: assumes that this is a bean 367 Array.set(newArray, i, 368 unmarshal((MapMessage) elem)); 369 } 370 } 371 Object[] parameters = {newArray}; 372 method.invoke(bean, parameters); 373 } 374 } 375 else if (val instanceof MapMessage) 376 { 377 MapMessage msg = (MapMessage) val; 378 String beanFieldName = msg.getString(BEANNAME_FIELD); 379 if (beanFieldName != null) 380 { 381 Object[] parameters = {unmarshal(msg)}; 382 method.invoke(bean, parameters); 383 } 384 else 385 { 386 Object[] parameters = {msg}; 387 method.invoke(bean, parameters); 388 } 389 } 390 else 391 { 392 Object[] parameters = {val}; 393 method.invoke(bean, parameters); 394 } 395 } 396 } 397 return bean; 398 } 399 catch (Exception ex) 400 { 401 throw new ConverterException(ex); 402 } 403 } 404 405 /*** 406 * Get the unqualified name of the bean class. 407 * 408 * @param fullName 409 * Fully qualified name of the bean class. This can also be the 410 * unqualified name. 411 * @return Unqualified name of the bean class. 412 */ 413 private static String getBeanClassName(String fullName) 414 { 415 int index = fullName.lastIndexOf("."); 416 return fullName.substring(index + 1); 417 } 418 419 /*** 420 * Determine if a bean class is a primitive wrapper type. A primitive 421 * wrapper type can be directly inserted into a <tt>MapMessage</tt>. 422 * 423 * @param cls 424 * Bean class. 425 * @return Returns <tt>true</tt> if the bean class is a supported 426 * non-primitive type. Otherwise, returns <tt>false</tt>. 427 */ 428 private static boolean isPrimitiveWrapperType(Class cls) 429 { 430 String name = cls.getName(); 431 return primitiveTypes.containsKey(name) 432 || primitiveWrapperTypes.containsKey(name); 433 } 434 435 /*** 436 * Determine if a bean attribute is an array of <tt>primitive</tt> types. 437 * A primitive array type can be directly inserted into a 438 * <tt>MapMessage</tt>. 439 * 440 * @param cls 441 * Bean class. 442 * @return Returns <tt>true</tt> if the bean class is an array of 443 * <tt>primitive</tt> types. Otherwise, returns <tt>false</tt>. 444 */ 445 private static boolean isPrimitiveArray(Class cls) 446 { 447 String name = cls.getName(); 448 return primitiveTypesArray.containsKey(name); 449 } 450 451 /*** 452 * Determine if a bean class is an array of primitive wrapper type. A 453 * primitive wrapper array type can be directly inserted into a 454 * <tt>MapMessage</tt>. 455 * 456 * @param cls 457 * Bean class. 458 * @return Returns <tt>true</tt> if the bean class is an array of 459 * primitive wrapper types. Otherwise, returns <tt>false</tt>. 460 */ 461 private static boolean isPrimitiveWrapperArray(Class cls) 462 { 463 String name = cls.getName(); 464 return primitiveWrapperTypesArray.containsKey(name); 465 } 466 467 private static Map primitiveTypes = new HashMap(); 468 private static Map primitiveWrapperTypes = new HashMap(); 469 private static Map primitiveWrapperTypesArray = new HashMap(); 470 private static Map primitiveTypesArray = new HashMap(); 471 472 private static final Object[] EMPTY_PARAMS = {}; 473 private static final String BEANNAME_FIELD = "csmbeanname__"; 474 475 static 476 { 477 primitiveTypes.put("short", ""); 478 primitiveTypes.put("int", ""); 479 primitiveTypes.put("long", ""); 480 primitiveTypes.put("float", ""); 481 primitiveTypes.put("double", ""); 482 primitiveTypes.put("byte", ""); 483 primitiveTypes.put("boolean", ""); 484 primitiveTypes.put("char", ""); 485 486 primitiveWrapperTypes.put("java.lang.Short", ""); 487 primitiveWrapperTypes.put("java.lang.Integer", ""); 488 primitiveWrapperTypes.put("java.lang.Long", ""); 489 primitiveWrapperTypes.put("java.lang.Float", ""); 490 primitiveWrapperTypes.put("java.lang.Double", ""); 491 primitiveWrapperTypes.put("java.lang.Byte", ""); 492 primitiveWrapperTypes.put("java.lang.Boolean", ""); 493 primitiveWrapperTypes.put("java.lang.Character", ""); 494 495 primitiveTypesArray.put("[S", ""); 496 primitiveTypesArray.put("[I", ""); 497 primitiveTypesArray.put("[L", ""); 498 primitiveTypesArray.put("[F", ""); 499 primitiveTypesArray.put("[D", ""); 500 primitiveTypesArray.put("[B", ""); 501 primitiveTypesArray.put("[Z", ""); 502 primitiveTypesArray.put("[C", ""); 503 504 primitiveWrapperTypesArray.put("[Ljava.lang.Short;", ""); 505 primitiveWrapperTypesArray.put("[Ljava.lang.Integer;", ""); 506 primitiveWrapperTypesArray.put("[Ljava.lang.Long;", ""); 507 primitiveWrapperTypesArray.put("[Ljava.lang.Float;", ""); 508 primitiveWrapperTypesArray.put("[Ljava.lang.Double;", ""); 509 primitiveWrapperTypesArray.put("[Ljava.lang.Byte;", ""); 510 primitiveWrapperTypesArray.put("[Ljava.lang.Boolean;", ""); 511 primitiveWrapperTypesArray.put("[Ljava.lang.Character;", ""); 512 } 513 }

This page was automatically generated by Maven