1 /***
2 * Copyright (c) 2005, 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 com.tibco.tibrv.*;
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 *
59 * @author <a href="mailto:jawaid.hakim@codestreet.com">jawaid.hakim </a>
60 */
61 public abstract class ConverterBeanAndTibrv extends Converter
62 {
63 /***
64 * Marshal a bean into a TIBCO Rendezvous message.
65 *
66 * @param bean
67 * Bean.
68 * @return Marshalled bean.
69 * @throws ConverterException
70 */
71 public static TibrvMsg marshal(Object bean) throws ConverterException
72 {
73 try
74 {
75 Class beanClass = bean.getClass();
76 RBeanInfo beanInfo = BeanFactory.createBeanInfo(
77 beanClass.getName(), beanClass);
78
79 TibrvMsg nestedMsg = new TibrvMsg();
80 nestedMsg.add(BEANNAME_FIELD, getBeanClassName(beanInfo.getName()));
81
82 for (Iterator getters = beanInfo.getGetters().entrySet().iterator(); getters
83 .hasNext();)
84 {
85 Map.Entry entry = (Map.Entry) getters.next();
86 Method method = (Method) entry.getValue();
87 Object val = method.invoke(bean, EMPTY_PARAMS);
88 if (val != null)
89 {
90 String fldName = (String) entry.getKey();
91 Class valType = val.getClass();
92 if (valType.equals(String.class))
93 {
94 nestedMsg.add(fldName, val.toString());
95 }
96 else if (isPrimitiveWrapperType(valType))
97 {
98 nestedMsg.add(fldName, val);
99 }
100 else if (valType.equals(BigDecimal.class))
101 {
102 nestedMsg.add(fldName, Converter
103 .getBigDecimalFormatter().format(val));
104 }
105 else if (valType.equals(Timestamp.class))
106 {
107 Timestamp realVal = (Timestamp) val;
108 nestedMsg.add(fldName, new Date(realVal.getTime()));
109 }
110 else if (Date.class.isAssignableFrom(valType))
111 {
112 String sVal = RDateFormat.getInstance().format(
113 (Date) val);
114 nestedMsg.add(fldName, sVal);
115 }
116 else if (valType.isArray())
117 {
118 int len = Array.getLength(val);
119 Class arrayType = val.getClass();
120 if (isPrimitiveArray(arrayType)
121 || isPrimitiveWrapperArray(arrayType))
122 {
123 nestedMsg.add(fldName, val);
124 continue;
125 }
126 TibrvMsg arrayMsg = new TibrvMsg();
127 int realLen = 0;
128 for (int i = 0; i < len; ++i)
129 {
130 Object elem = Array.get(val, i);
131 if (elem != null)
132 {
133 String index = String.valueOf(i);
134 if (elem.getClass().equals(String.class))
135 {
136 arrayMsg.add(index, elem);
137 }
138 else if (valType.equals(BigDecimal.class))
139 {
140 arrayMsg.add(index, Converter
141 .getBigDecimalFormatter().format(
142 elem));
143 }
144 else if (elem.getClass()
145 .equals(Timestamp.class))
146 {
147 Timestamp realVal = (Timestamp) elem;
148 arrayMsg.add(index, new Date(realVal
149 .getTime()));
150 }
151 else if (Date.class.isAssignableFrom(elem
152 .getClass()))
153 {
154 arrayMsg.add(index, RDateFormat
155 .getInstance().format((Date) elem));
156 }
157 else
158 {
159 // TODO: assumes that this is a bean
160 if (realLen == 0)
161 arrayMsg.add(String.valueOf(realLen),
162 marshal(elem));
163 }
164 ++realLen;
165 }
166 }
167 arrayMsg.add("length", realLen);
168 nestedMsg.add(fldName, arrayMsg);
169 }
170 else if (val instanceof TibrvMsg)
171 {
172 nestedMsg.add(fldName, val);
173 }
174 else if (val instanceof java.util.Collection)
175 {
176 // TODO: check why User does not lazy load collections
177 // throw new ConverterException("Cannot marshal
178 // collection classes");
179 }
180 else
181 {
182 // Handle everything else like a bean
183 nestedMsg.add(fldName, marshal(val));
184 }
185 }
186 }
187
188 return nestedMsg;
189 }
190 catch (Exception ex)
191 {
192 throw new ConverterException(ex);
193 }
194 }
195
196 /***
197 * Unmarshal a bean from a TIBCO Rendezvous message.
198 *
199 * @param source
200 * TIBCO Rendezvous message.
201 * @return New bean instance.
202 * @throws ConverterException
203 */
204 public static Object unmarshal(TibrvMsg source) throws ConverterException
205 {
206 try
207 {
208 String beanName = (String) source.get(BEANNAME_FIELD);
209 if (beanName == null)
210 throw new ConverterException("Bean type not found");
211
212 // Instantiate bean
213 String fullName = RMsgFactoryImpl.getInstance()
214 .getFullBeanFieldName(beanName);
215 if (fullName == null)
216 throw new ConverterException("Qualified bean name not found");
217
218 return unmarshal(source, beanName, fullName);
219 }
220 catch (Exception ex)
221 {
222 throw new ConverterException(ex);
223 }
224 }
225
226 /***
227 * Unmarshal a bean from a TIBCO Rendezvous message.
228 *
229 * @param source
230 * TIBCO Rendezvous message.
231 * @param beanName
232 * Short name of bean.
233 * @param fullName
234 * Full name of bean (includes the package).
235 * @return New bean instance.
236 * @throws ConverterException
237 */
238 public static Object unmarshal(TibrvMsg source, String beanName,
239 String fullName) throws ConverterException
240 {
241 try
242 {
243 Object bean = Class.forName(fullName).newInstance();
244
245 // Set bean properties
246 RBeanInfo beanInfo = BeanFactory.createBeanInfo(beanName, bean
247 .getClass());
248 for (Iterator setters = beanInfo.getSetters().values().iterator(); setters
249 .hasNext();)
250 {
251 Method method = (Method) setters.next();
252 String fldName = method.getName().substring(3);
253 TibrvMsgField fld = source.getField(fldName);
254 if (fld != null)
255 {
256 Object val = fld.data;
257 Class[] methodParameters = method.getParameterTypes();
258 Class valType = methodParameters[0];
259 if (valType.equals(String.class))
260 {
261 Object[] parameters = { val.toString() };
262 method.invoke(bean, parameters);
263 }
264 else if (isPrimitiveWrapperType(valType))
265 {
266 Object[] parameters = { val };
267 method.invoke(bean, parameters);
268 }
269 else if (valType.equals(BigDecimal.class))
270 {
271 Object[] parameters = { new BigDecimal(val.toString()) };
272 method.invoke(bean, parameters);
273 }
274 else if (valType.equals(Timestamp.class))
275 {
276 Object[] parameters = { new Timestamp(((Date)val).getTime()) };
277 method.invoke(bean, parameters);
278 }
279 else if (Date.class.isAssignableFrom(valType))
280 {
281 Object[] parameters = { RDateFormat.getInstance()
282 .parse(val.toString()) };
283 method.invoke(bean, parameters);
284 }
285 else if (valType.isArray())
286 {
287 if (isPrimitiveArray(valType))
288 {
289 Object[] parameters = { val };
290 method.invoke(bean, parameters);
291 }
292 else if (isPrimitiveWrapperArray(valType))
293 {
294 Object[] parameters = { val };
295 method.invoke(bean, parameters);
296 }
297 else
298 {
299 // BigDecimal, String, Date, or Bean array
300 TibrvMsg arrayMsg = (TibrvMsg) val;
301 int len = arrayMsg.getInt("length", 0);
302 String arrayClsName = valType.getName();
303 Object newArray = Array.newInstance(Class
304 .forName(arrayClsName.substring(2,
305 arrayClsName.indexOf(';'))), len);
306 for (int i = 0; i < len; ++i)
307 {
308 Object elem = arrayMsg.get(String.valueOf(i));
309 if (arrayClsName.equals("[Ljava.lang.String;"))
310 {
311 Array.set(newArray, i, elem);
312 }
313 else if (arrayClsName
314 .equals("[Ljava.math.BigDecimal;"))
315 {
316 Array.set(newArray, i, new BigDecimal(elem
317 .toString()));
318 }
319 else if (arrayClsName
320 .equals("[Ljava.util.Date;"))
321 {
322 Array.set(newArray, i, elem);
323 }
324 else if (arrayClsName
325 .equals("[Ljava.sql.Timestamp;"))
326 {
327 Array
328 .set(
329 newArray,
330 i, new Timestamp(((Date)elem).getTime()));
331 }
332 else if (elem instanceof TibrvMsg)
333 {
334 // TODO: assumes that this is a bean
335 Array.set(newArray, i,
336 unmarshal((TibrvMsg) elem));
337 }
338 }
339 Object[] parameters = { newArray };
340 method.invoke(bean, parameters);
341 }
342 }
343 else if (val instanceof TibrvMsg)
344 {
345 TibrvMsg msg = (TibrvMsg) val;
346 String beanFieldName = (String) msg.get(BEANNAME_FIELD);
347 if (beanFieldName != null)
348 {
349 Object[] parameters = { unmarshal(msg) };
350 method.invoke(bean, parameters);
351 }
352 else
353 {
354 Object[] parameters = { msg };
355 method.invoke(bean, parameters);
356 }
357 }
358 else
359 {
360 Object[] parameters = { val };
361 method.invoke(bean, parameters);
362 }
363 }
364 }
365 return bean;
366 }
367 catch (Exception ex)
368 {
369 throw new ConverterException(ex);
370 }
371 }
372
373 /***
374 * Get the unqualified name of the bean class.
375 *
376 * @param fullName
377 * Fully qualified name of the bean class. This can also be the
378 * unqualified name.
379 * @return Unqualified name of the bean class.
380 */
381 private static String getBeanClassName(String fullName)
382 {
383 int index = fullName.lastIndexOf(".");
384 return fullName.substring(index + 1);
385 }
386
387 /***
388 * Determine if a bean class is a primitive wrapper type. A primitive
389 * wrapper type can be directly inserted into a <tt>TibrvMsg</tt>.
390 *
391 * @param cls
392 * Bean class.
393 * @return Returns <tt>true</tt> if the bean class is a supported
394 * non-primitive type. Otherwise, returns <tt>false</tt>.
395 */
396 private static boolean isPrimitiveWrapperType(Class cls)
397 {
398 String name = cls.getName();
399 return primitiveTypes.containsKey(name)
400 || primitiveWrapperTypes.containsKey(name);
401 }
402
403 /***
404 * Determine if a bean attribute is an array of <tt>primitive</tt> types.
405 * A primitive array type can be directly inserted into a <tt>TibrvMsg</tt>.
406 *
407 * @param cls
408 * Bean class.
409 * @return Returns <tt>true</tt> if the bean class is an array of
410 * <tt>primitive</tt> types. Otherwise, returns <tt>false</tt>.
411 */
412 private static boolean isPrimitiveArray(Class cls)
413 {
414 String name = cls.getName();
415 return primitiveTypesArray.containsKey(name);
416 }
417
418 /***
419 * Determine if a bean class is an array of primitive wrapper type. A
420 * primitive wrapper array type can be directly inserted into a
421 * <tt>TibrvMsg</tt>.
422 *
423 * @param cls
424 * Bean class.
425 * @return Returns <tt>true</tt> if the bean class is an array of
426 * primitive wrapper types. Otherwise, returns <tt>false</tt>.
427 */
428 private static boolean isPrimitiveWrapperArray(Class cls)
429 {
430 String name = cls.getName();
431 return primitiveWrapperTypesArray.containsKey(name);
432 }
433
434 private static Map primitiveTypes = new HashMap();
435
436 private static Map primitiveWrapperTypes = new HashMap();
437
438 private static Map primitiveWrapperTypesArray = new HashMap();
439
440 private static Map primitiveTypesArray = new HashMap();
441
442 private static final Object[] EMPTY_PARAMS = {};
443
444 private static final String BEANNAME_FIELD = "csmbeanname__";
445
446 static
447 {
448 primitiveTypes.put("short", "");
449 primitiveTypes.put("int", "");
450 primitiveTypes.put("long", "");
451 primitiveTypes.put("float", "");
452 primitiveTypes.put("double", "");
453 primitiveTypes.put("byte", "");
454 primitiveTypes.put("boolean", "");
455 primitiveTypes.put("char", "");
456
457 primitiveWrapperTypes.put("java.lang.Short", "");
458 primitiveWrapperTypes.put("java.lang.Integer", "");
459 primitiveWrapperTypes.put("java.lang.Long", "");
460 primitiveWrapperTypes.put("java.lang.Float", "");
461 primitiveWrapperTypes.put("java.lang.Double", "");
462 primitiveWrapperTypes.put("java.lang.Byte", "");
463 primitiveWrapperTypes.put("java.lang.Boolean", "");
464 primitiveWrapperTypes.put("java.lang.Char", "");
465
466 primitiveTypesArray.put("[S", "");
467 primitiveTypesArray.put("[I", "");
468 primitiveTypesArray.put("[L", "");
469 primitiveTypesArray.put("[F", "");
470 primitiveTypesArray.put("[D", "");
471 primitiveTypesArray.put("[B", "");
472 primitiveTypesArray.put("[Z", "");
473 primitiveTypesArray.put("[C", "");
474
475 primitiveWrapperTypesArray.put("[Ljava.lang.Short;", "");
476 primitiveWrapperTypesArray.put("[Ljava.lang.Integer;", "");
477 primitiveWrapperTypesArray.put("[Ljava.lang.Long;", "");
478 primitiveWrapperTypesArray.put("[Ljava.lang.Float;", "");
479 primitiveWrapperTypesArray.put("[Ljava.lang.Double;", "");
480 primitiveWrapperTypesArray.put("[Ljava.lang.Byte;", "");
481 primitiveWrapperTypesArray.put("[Ljava.lang.Boolean;", "");
482 primitiveWrapperTypesArray.put("[Ljava.lang.Char;", "");
483 }
484 }
This page was automatically generated by Maven