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