View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.search.attribute;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  
22  import net.sf.ehcache.Element;
23  
24  /***
25   * Extracts a search attribute determining the value as a javabean property on either
26   * the key or the value. If the property exists on both the key and the value an exception is thrown
27   *
28   * @author teck
29   */
30  /***
31   * @author teck
32   *
33   */
34  public class JavaBeanAttributeExtractor implements AttributeExtractor {
35  
36      private static final Object NO_VALUE = new Object();
37  
38      private transient volatile MethodRef lastKeyMethod;
39      private transient volatile MethodRef lastValueMethod;
40  
41      private final String isMethodName;
42      private final String getMethodName;
43      private final String beanProperty;
44  
45      /***
46       * Constructor
47       *
48       * @param beanProperty the bean property name to extract
49       */
50      public JavaBeanAttributeExtractor(String beanProperty) {
51          if (beanProperty == null) {
52              throw new NullPointerException();
53          }
54  
55          if (beanProperty.length() == 0) {
56              throw new IllegalArgumentException("bean property empty");
57          }
58  
59          this.beanProperty = beanProperty;
60  
61          String upperFirstProp = "" + Character.toUpperCase(beanProperty.charAt(0));
62          if (beanProperty.length() > 1) {
63              upperFirstProp += beanProperty.substring(1);
64          }
65  
66          isMethodName = "is" + upperFirstProp;
67          getMethodName = "get" + upperFirstProp;
68      }
69  
70      /***
71       * {@inheritDoc}
72       */
73      public Object attributeFor(Element element, String attributeName) throws AttributeExtractorException {
74          Object attribute = NO_VALUE;
75  
76          final Object key = element.getObjectKey();
77  
78          if (key != null) {
79              MethodRef keyMethod = lastKeyMethod;
80              if (keyMethod == null || keyMethod.targetClass != key.getClass()) {
81                  keyMethod = findMethod(key);
82                  lastKeyMethod = keyMethod;
83              }
84              if (keyMethod.method != null) {
85                  attribute = getValue(keyMethod.method, key);
86              }
87          }
88  
89          final Object value = element.getObjectValue();
90  
91          if (value != null) {
92              MethodRef valueMethod = lastValueMethod;
93              if (valueMethod == null || valueMethod.targetClass != value.getClass()) {
94                  valueMethod = findMethod(value);
95                  lastValueMethod = valueMethod;
96              }
97  
98              if (valueMethod.method != null) {
99                  if (attribute != NO_VALUE) {
100                     throw new AttributeExtractorException("Bean property [" + beanProperty + "] present on both key and value");
101                 }
102 
103                 return getValue(valueMethod.method, value);
104             }
105         }
106 
107         if (attribute != NO_VALUE) {
108             return attribute;
109         }
110 
111         throw new AttributeExtractorException("Bean property [" + beanProperty + "] not present on either key or value");
112     }
113 
114     private MethodRef findMethod(Object obj) {
115         final Class target = obj.getClass();
116 
117         try {
118             return new MethodRef(target, target.getMethod(getMethodName));
119         } catch (SecurityException e) {
120             throw new AttributeExtractorException(e);
121         } catch (NoSuchMethodException e) {
122             // keep looking
123         }
124 
125         try {
126             Method m = target.getMethod(isMethodName);
127             if (m.getReturnType().equals(Boolean.class) || m.getReturnType().equals(Boolean.TYPE)) {
128                 return new MethodRef(target, m);
129             }
130         } catch (SecurityException e) {
131             throw new AttributeExtractorException(e);
132         } catch (NoSuchMethodException e) {
133             //
134         }
135 
136         // no applicable method available
137         return new MethodRef(target, null);
138     }
139 
140     private Object getValue(Method method, Object key) {
141         try {
142             return method.invoke(key);
143         } catch (Throwable t) {
144             if (t instanceof InvocationTargetException) {
145                 t = t.getCause();
146             }
147 
148             if (t instanceof Error) {
149                 throw ((Error) t);
150             }
151 
152             throw new AttributeExtractorException("Error getting bean property [" + beanProperty + "] on instance of "
153                     + key.getClass().getName(), t);
154         }
155     }
156 
157     /***
158      * A cached method lookup. Method is null to indicate the method is not present/accessible
159      */
160     private static class MethodRef {
161         private final Class targetClass;
162         private final Method method;
163 
164         MethodRef(Class target, Method method) {
165             this.targetClass = target;
166             this.method = method;
167         }
168     }
169 
170 }