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 }