View Javadoc

1   package com.bradmcevoy.property;
2   
3   import com.bradmcevoy.http.Resource;
4   import com.bradmcevoy.http.exceptions.NotAuthorizedException;
5   import java.beans.PropertyDescriptor;
6   import java.lang.reflect.InvocationTargetException;
7   import java.util.ArrayList;
8   import java.util.List;
9   import javax.xml.namespace.QName;
10  import org.apache.commons.beanutils.PropertyUtils;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  /**
15   * A read/write source of properties which uses getter/setter style properties
16   * on the resource class.
17   *
18   * This is similar in concept to form variable binding in web frameworks like
19   * struts and MVC.
20   *
21   * @author brad
22   */
23  public class BeanPropertySource implements PropertySource {
24  
25      private static final Logger log = LoggerFactory.getLogger( BeanPropertySource.class );
26      private static final Object[] NOARGS = new Object[0];
27  
28      public Object getProperty( QName name, Resource r ) throws NotAuthorizedException {
29          PropertyDescriptor pd = getPropertyDescriptor( r, name.getLocalPart() );
30          if( pd == null ) {
31              throw new IllegalArgumentException( "no prop: " + name.getLocalPart() + " on " + r.getClass() );
32          }
33          try {
34              return pd.getReadMethod().invoke( r, NOARGS );
35          } catch( Exception ex ) {
36              if( ex.getCause() instanceof NotAuthorizedException ) {
37                  NotAuthorizedException na = (NotAuthorizedException) ex.getCause();
38                  throw na;
39              } else {
40                  throw new RuntimeException( name.toString(), ex );
41              }
42          }
43      }
44  
45      public void setProperty( QName name, Object value, Resource r ) throws NotAuthorizedException, PropertySetException {
46          log.debug( "setProperty: " + name + " = " + value );
47          PropertyDescriptor pd = getPropertyDescriptor( r, name.getLocalPart() );
48          try {
49              pd.getWriteMethod().invoke( r, value );
50          } catch( PropertySetException e ) {
51              throw e;
52          } catch( Exception ex ) {
53              if( ex.getCause() instanceof NotAuthorizedException ) {
54                  NotAuthorizedException na = (NotAuthorizedException) ex.getCause();
55                  throw na;
56              } else if( ex.getCause() instanceof PropertySetException ) {
57                  PropertySetException na = (PropertySetException) ex.getCause();
58                  throw na;
59              } else {
60                  if( value == null ) {
61                      log.error( "Exception setting property: " + name.toString() + " to null" );
62                  } else {
63                      log.error( "Exception setting property: " + name.toString() + " to value: " + value + " class:" + value.getClass() );
64                  }
65                  throw new RuntimeException( name.toString(), ex );
66              }
67          }
68      }
69  
70      public PropertyMetaData getPropertyMetaData( QName name, Resource r ) {
71          log.debug( "getPropertyMetaData" );
72          BeanPropertyResource anno = getAnnotation( r );
73          if( anno == null ) {
74              log.debug( " no annotation: ", r.getClass().getCanonicalName() );
75              return PropertyMetaData.UNKNOWN;
76          }
77          if( !name.getNamespaceURI().equals( anno.value() ) ) {
78              log.debug( "different namespace", anno.value(), name.getNamespaceURI() );
79              return PropertyMetaData.UNKNOWN;
80          }
81  
82          PropertyDescriptor pd = getPropertyDescriptor( r, name.getLocalPart() );
83          if( pd == null || pd.getReadMethod() == null ) {
84              log.debug( "no read method" );
85              return PropertyMetaData.UNKNOWN;
86          } else {
87              BeanPropertyAccess propAnno = pd.getReadMethod().getAnnotation( BeanPropertyAccess.class );
88              if( propAnno != null ) {
89                  if( !propAnno.value() ) {
90                      log.trace( "property is annotated and value is false, so do not allow access" );
91                      return PropertyMetaData.UNKNOWN;
92                  } else {
93                      log.trace( "property is annotated and value is true, so allow access" );
94                  }
95              } else {
96                  if( anno.enableByDefault() ) {
97                      log.trace( "no property annotation, property annotation is enable by default so allow access" );
98                  } else {
99                      log.trace( "no property annotation, class annotation says disable by default, decline access" );
100                     return PropertyMetaData.UNKNOWN;
101                 }
102             }
103             if( log.isDebugEnabled() ) {
104                 log.debug( "writable: " + anno.writable() + " - " + ( pd.getWriteMethod() != null ) );
105             }
106             boolean writable = anno.writable() && ( pd.getWriteMethod() != null );
107             if( writable ) {
108                 return new PropertyMetaData( PropertyAccessibility.WRITABLE, pd.getPropertyType() );
109             } else {
110                 return new PropertyMetaData( PropertyAccessibility.READ_ONLY, pd.getPropertyType() );
111             }
112         }
113     }
114 
115     public void clearProperty( QName name, Resource r ) throws NotAuthorizedException {
116         setProperty( name, null, r );
117     }
118 
119     public List<QName> getAllPropertyNames( Resource r ) {
120         BeanPropertyResource anno = getAnnotation( r );
121         if( anno == null ) return null;
122         PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors( r );
123         List<QName> list = new ArrayList<QName>();
124         for( PropertyDescriptor pd : pds ) {
125             if( pd.getReadMethod() != null ) {
126                 list.add( new QName( anno.value(), pd.getName() ) );
127             }
128         }
129         return list;
130     }
131 
132     private BeanPropertyResource getAnnotation( Resource r ) {
133         return r.getClass().getAnnotation( BeanPropertyResource.class );
134     }
135 
136     private PropertyDescriptor getPropertyDescriptor( Resource r, String name ) {
137         try {
138             PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor( r, name );
139             return pd;
140         } catch( IllegalAccessException ex ) {
141             throw new RuntimeException( ex );
142         } catch( InvocationTargetException ex ) {
143             throw new RuntimeException( ex );
144         } catch( NoSuchMethodException ex ) {
145             return null;
146         }
147 
148     }
149 }