1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.danube.beanconsole;
14
15 import java.beans.BeanInfo;
16 import java.beans.IntrospectionException;
17 import java.beans.Introspector;
18 import java.beans.MethodDescriptor;
19 import java.beans.PropertyDescriptor;
20 import java.beans.PropertyEditor;
21 import java.beans.PropertyEditorManager;
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.lang.reflect.Array;
25 import java.lang.reflect.Method;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import org.springframework.beans.BeansException;
36 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
37 import org.springframework.beans.factory.support.AbstractBeanDefinition;
38 import org.springframework.context.ApplicationContext;
39 import org.springframework.context.ConfigurableApplicationContext;
40
41
42
43
44
45
46 public class BeanHelper {
47
48 public static final Object[] EMPTY_ARRAY = new Object[0];
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 @SuppressWarnings("unchecked")
72 public static Object navigate(Object from, String path)
73 throws BeanAccessException {
74
75 if (path.length() == 0) {
76 return from;
77 }
78
79 Object current = from;
80 int l = 0;
81 if (path.startsWith("/")) {
82 l = 1;
83 }
84 while (l < path.length()) {
85 int i = path.indexOf('/', l);
86 String p;
87 if (i >= 0) {
88 p = path.substring(l, i);
89 l = i + 1;
90 } else {
91 p = path.substring(l);
92 l = path.length();
93 }
94
95 Object next = null;
96 if ((p.length() > 2) && p.startsWith("[") && p.endsWith("]")) {
97 p = p.substring(1, p.length() - 1);
98 try {
99 int n = Integer.parseInt(p);
100 if (n < 0) {
101 throw new BeanAccessException(path.substring(1, l), new IndexOutOfBoundsException(p));
102 }
103 if (current instanceof List) {
104 next = ((List<?>)current).get(n);
105 } else if (current.getClass().isArray()) {
106 next = Array.get(current, n);
107 } else if (current instanceof Collection) {
108 int ii = -1;
109 Iterator<?> iterator = ((Collection)current).iterator();
110 while (iterator.hasNext() && (ii != n)) {
111 Object o = iterator.next();
112 ii++;
113 if (ii == n) {
114 next = o;
115 }
116 }
117 }
118 } catch (NumberFormatException e) {
119 if (current instanceof ApplicationContext) {
120 ApplicationContext context = (ApplicationContext)current;
121 try {
122 Object bean = context.getBean(p);
123 next = bean;
124 } catch (BeansException ignore) {
125 }
126 }
127 if ((next == null) && (current instanceof Map)) {
128 next = ((Map<?, ?>)current).get(p);
129 if (next == null) {
130 Iterator<Map.Entry> it = ((Map)current).entrySet().iterator();
131 while (it.hasNext() && (next == null)) {
132 Map.Entry entry = it.next();
133 if ((entry.getKey() != null) && p.equals(entry.getKey().toString())) {
134 next = entry.getValue();
135 }
136 }
137 }
138
139 }
140 }
141 } else {
142 try {
143 BeanInfo beanInfo = Introspector.getBeanInfo(current.getClass());
144 for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
145 if (propertyDescriptor.getName().equals(p)) {
146 Method readMethod = propertyDescriptor.getReadMethod();
147 if (readMethod == null) {
148 return null;
149 }
150 try {
151 next = readMethod.invoke(current, EMPTY_ARRAY);
152 break;
153 } catch (Exception ei) {
154 throw new BeanAccessException(path.substring(1, l), ei);
155 }
156 }
157 }
158 } catch (IntrospectionException ex) {
159 throw new BeanAccessException(path.substring(1, l), new RuntimeException(ex));
160 }
161 }
162
163 if (next == null) {
164 throw new BeanAccessException(path.substring(1, l), new IllegalArgumentException("Unknown attribute '" + p + "'"));
165 } else {
166 current = next;
167 }
168 i = i + 1;
169 }
170 return current;
171 }
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 @SuppressWarnings("unchecked")
188 public static void prepare(Object object, Map<String, Object> result) {
189 Set<BeanDef> properties = new LinkedHashSet<BeanDef>();
190 Set<MethodDescriptor> methods = new LinkedHashSet<MethodDescriptor>();
191
192 result.put("properties", properties);
193 result.put("methods", methods);
194
195 if (object instanceof ConfigurableApplicationContext) {
196 Set<BeanDef> beans = new LinkedHashSet<BeanDef>();
197 ConfigurableListableBeanFactory factory = ((ConfigurableApplicationContext)object).getBeanFactory();
198 Set<String> names = new HashSet<String>();
199
200 String[] beanNames = factory.getBeanDefinitionNames();
201 for (String beanName : beanNames) {
202 AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)factory.getBeanDefinition(beanName);
203 String access = null;
204 String value = null;
205 String type = null;
206 boolean followable = true;
207 if (beanDefinition.isSingleton()) {
208 access = BeanDef.RO;
209 Object bean = factory.getBean(beanName);
210 value = toString(bean);
211 type = toTypeString(bean);
212 followable = isFollowable(bean);
213 } else {
214 access = BeanDef.NO_ACCESS;
215 value = "<no access - not a singleton bean>";
216 type = beanDefinition.getBeanClassName();
217 followable = false;
218 }
219 beans.add(new BeanDef(beanName, "", type, access, value, followable));
220 names.add(beanName);
221 }
222
223 beanNames = factory.getSingletonNames();
224 for (String beanName : beanNames) {
225 if (!names.contains(beanName)) {
226 String access = BeanDef.RO;
227 Object bean = factory.getBean(beanName);
228 String value = toString(bean);
229 String type = toTypeString(bean);
230 boolean followable = isFollowable(bean);
231 beans.add(new BeanDef(beanName, "", type, access, value, followable));
232 }
233 }
234
235 result.put("beans", beans);
236 } else if (object instanceof Map) {
237 Set<BeanDef> elements = new LinkedHashSet<BeanDef>();
238 Map<Object, Object> map = (Map<Object, Object>)object;
239 for (Map.Entry<Object, Object> entry : map.entrySet()) {
240 String key = toString(entry.getKey());
241 Object val = entry.getValue();
242 String value = toString(val);
243 String type = toTypeString(val);
244 boolean followable = isFollowable(val);
245 BeanDef def = new BeanDef(key, "", type, BeanDef.RO, value, followable);
246 elements.add(def);
247 }
248 result.put("map", elements);
249 } else if (object instanceof Collection) {
250 Set<BeanDef> elements = new LinkedHashSet<BeanDef>();
251 for (Object obj : (Collection<?>)object) {
252 String value = toString(obj);
253 String type = toTypeString(obj);
254 boolean followable = isFollowable(obj);
255 BeanDef def = new BeanDef(null, "", type, BeanDef.RO, value, followable);
256 elements.add(def);
257 }
258 result.put("collection", elements);
259 } else if (object instanceof Object[]) {
260 Set<BeanDef> elements = new LinkedHashSet<BeanDef>();
261 for (Object obj : (Object[])object) {
262 String value = toString(obj);
263 String type = toTypeString(obj);
264 boolean followable = isFollowable(obj);
265 BeanDef def = new BeanDef(null, "", type, BeanDef.RO, value, followable);
266 elements.add(def);
267 }
268 result.put("collection", elements);
269 }
270
271 try {
272 BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
273 for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
274 String name = propertyDescriptor.getDisplayName();
275 String access = getAccessType(propertyDescriptor);
276 if (!BeanDef.WO.equals(access) && !BeanDef.WO_RAW.equals(access)) {
277 Class<?> cls = propertyDescriptor.getPropertyType();
278 String type = toTypeString(cls);
279 String value = null;
280 boolean followable = false;
281 if (BeanDef.RW.equals(access) || BeanDef.RO.equals(access) || BeanDef.RW_RAW.equals(access) || BeanDef.RO_RAW.equals(access)) {
282 Object val = getPropertyValue(object, propertyDescriptor);
283 value = toString(val);
284 followable = isFollowable(cls);
285 } else {
286 value = "";
287 }
288 BeanDef def = new BeanDef(name, propertyDescriptor.getShortDescription(), type, access, value, followable);
289 properties.add(def);
290 }
291 }
292
293 for (MethodDescriptor methodDescriptor : getMethodDescriptors(beanInfo)) {
294 methods.add(methodDescriptor);
295 }
296 } catch (IntrospectionException e) {
297
298 e.printStackTrace();
299 }
300 }
301
302
303
304
305
306
307
308 public static String toString(Object object) {
309 if (object == null) {
310 return "<null>";
311 }
312 PropertyEditor propertyEditor = PropertyEditorManager.findEditor(object.getClass());
313 if (propertyEditor == null) {
314 return object.toString();
315 }
316 propertyEditor.setValue(object);
317 return propertyEditor.getAsText();
318
319 }
320
321
322
323
324
325
326
327 public static String toTypeString(Class<?> type) {
328 if (type.isArray()) {
329 return "[" + type.getComponentType().getName() + "]";
330 } else {
331 return type.getName();
332 }
333 }
334
335
336
337
338
339
340
341 public static String toTypeString(Object object) {
342 if (object == null) {
343 return "<null>";
344 } else {
345 return toTypeString(object.getClass());
346 }
347 }
348
349
350
351
352
353
354
355 protected static String createName(String path) {
356 if (path.length() == 0) {
357 return "Root Bean";
358 } else {
359 int i = path.lastIndexOf('/');
360 if (i > 0) {
361 return path.substring(path.lastIndexOf("/") + 1);
362 } else {
363 return path.substring(1);
364 }
365 }
366 }
367
368
369
370
371
372
373
374 public static String createPath(String path) {
375 if (path.length() == 0) {
376 return "/";
377 }
378 return path;
379 }
380
381
382
383
384
385
386 public static String createResourcePath(String path) {
387 if ((path == null) || "/".equals(path)) {
388 return "";
389 }
390 if (!path.startsWith("/")) {
391 path = "/" + path;
392 }
393 if (path.endsWith("/")) {
394 return path.substring(0, path.length() - 1);
395 } else {
396 return path;
397 }
398 }
399
400
401
402
403
404
405 public static String[] convertPath(String path) {
406 if ((path == null) || "".equals(path) || "/".equals(path)) {
407 return new String[0];
408 }
409
410 int i = 0;
411 int l = 0;
412 if (path.startsWith("/")) {
413 i = path.indexOf('/', 1);
414 l = 1;
415 } else {
416 i = path.indexOf('/');
417 }
418 if (i >= 0) {
419 ArrayList<String> pathList = new ArrayList<String>();
420 while (i > 0) {
421 pathList.add(path.substring(l, i));
422 l = i + 1;
423 i = path.indexOf('/', i+1);
424 }
425 pathList.add(path.substring(l));
426 String[] ps = new String[pathList.size()];
427 return pathList.toArray(ps);
428 } else {
429 return new String[]{path};
430 }
431 }
432
433
434
435
436
437
438
439 public static String getAccessType(PropertyDescriptor property) {
440 Method readMethod = property.getReadMethod();
441 Method writeMethod = property.getWriteMethod();
442 if ((readMethod != null) && (writeMethod != null)) {
443 if (PropertyEditorManager.findEditor(readMethod.getReturnType()) != null) {
444 return BeanDef.RW;
445 } else {
446 return BeanDef.RW_RAW;
447 }
448 } else if (readMethod != null) {
449 if (PropertyEditorManager.findEditor(readMethod.getReturnType()) != null) {
450 return BeanDef.RO;
451 } else {
452 return BeanDef.RO_RAW;
453 }
454 } else if (writeMethod != null) {
455 if (PropertyEditorManager.findEditor(writeMethod.getParameterTypes()[0]) != null) {
456 return BeanDef.WO;
457 } else {
458 return BeanDef.WO_RAW;
459 }
460 }
461 return BeanDef.NO_ACCESS;
462 }
463
464
465
466
467
468
469
470
471 public static Object getPropertyValue(Object bean, PropertyDescriptor property) {
472 Method readMethod = property.getReadMethod();
473 if (readMethod == null) {
474 return null;
475 }
476 try {
477 return readMethod.invoke(bean, EMPTY_ARRAY);
478 } catch (Exception e) {
479 StringWriter res = new StringWriter();
480 res.append("Error on read method: " + readMethod.toString() + "\n");
481 e.printStackTrace(new PrintWriter(res));
482 return res.toString();
483 }
484 }
485
486
487
488
489
490
491
492
493 public static String getPropertyValueAsString(Object object, String propertyName) {
494 try {
495 BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
496
497 for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
498 String name = propertyDescriptor.getDisplayName();
499 if (name.equals(propertyName)) {
500 return getPropertyValueAsString(object, propertyDescriptor);
501 }
502 }
503 } catch (IntrospectionException e) {
504
505 e.printStackTrace();
506 }
507 return null;
508 }
509
510
511
512
513
514
515
516
517 public static String getPropertyValueAsString(Object bean, PropertyDescriptor property) {
518 Method readMethod = property.getReadMethod();
519 if (readMethod == null) {
520 return "";
521 }
522 try {
523 Object value = readMethod.invoke(bean, EMPTY_ARRAY);
524 return toString(value);
525 } catch (Exception e) {
526 StringWriter res = new StringWriter();
527 res.append("Error on read method: " + readMethod.toString() + "\n");
528 e.printStackTrace(new PrintWriter(res));
529 return res.toString();
530 }
531 }
532
533
534
535
536
537
538
539
540 @SuppressWarnings("unchecked")
541 public static Collection<MethodDescriptor> getMethodDescriptors(BeanInfo beanInfo) {
542 Set<Method> attributes = new HashSet<Method>();
543 for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
544 Method readMethod = property.getReadMethod();
545 if (readMethod != null) {
546 attributes.add(readMethod);
547 }
548 Method writeMethod = property.getWriteMethod();
549 if ((writeMethod != null) && (readMethod != null)) {
550 attributes.add(writeMethod);
551 }
552 }
553
554 ArrayList<MethodDescriptor> result = new ArrayList<MethodDescriptor>();
555
556 for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) {
557 Method method = methodDescriptor.getMethod();
558 if (!attributes.contains(method)) {
559 boolean ok = true;
560 Class[] types = method.getParameterTypes();
561 for (int j = 0; j < types.length; j++) {
562 PropertyEditor propertyEditor = PropertyEditorManager.findEditor(types[j]);
563 ok = ok && (propertyEditor != null);
564 }
565 if (ok) {
566 result.add(methodDescriptor);
567 }
568 }
569 }
570 return result;
571 }
572
573
574
575
576
577
578 public static boolean isFollowable(Class<?> cls) {
579 return !String.class.equals(cls) && !cls.isPrimitive()
580 }
581
582
583
584
585
586
587 public static boolean isFollowable(Object object) {
588 return (object != null) && isFollowable(object.getClass());
589 }
590
591
592
593
594
595
596 public static String stackTrace(Throwable t) {
597 StringWriter res = new StringWriter();
598 PrintWriter out = new PrintWriter(res);
599 t.printStackTrace(out);
600 out.flush();
601 return res.toString();
602 }
603 }