View Javadoc

1   /*
2    * Copyright (c) 2010.  The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.hbql.mapping;
22  
23  import org.apache.hadoop.hbase.client.Result;
24  import org.apache.hadoop.hbase.hbql.client.Column;
25  import org.apache.hadoop.hbase.hbql.client.ColumnVersionMap;
26  import org.apache.hadoop.hbase.hbql.client.HBqlException;
27  import org.apache.hadoop.hbase.hbql.client.Mapping;
28  import org.apache.hadoop.hbase.hbql.impl.HConnectionImpl;
29  import org.apache.hadoop.hbase.hbql.statement.select.SelectElement;
30  import org.apache.hadoop.hbase.hbql.util.Maps;
31  
32  import java.lang.reflect.Field;
33  import java.util.List;
34  import java.util.Map;
35  
36  public class AnnotationResultAccessor extends ResultAccessor {
37  
38  
39      private final Class<?> clazz;
40      private final Map<String, CurrentValueAnnotationAttrib> columnMap = Maps.newHashMap();
41      private final Map<String, VersionAnnotationAttrib> columnVersionMap = Maps.newHashMap();
42  
43      private AnnotationResultAccessor(final TableMapping tableMapping, final Class clazz) throws HBqlException {
44  
45          super(new MappingContext(tableMapping));
46  
47          this.getMappingContext().setResultAccessor(this);
48  
49          this.clazz = clazz;
50  
51          // Make sure there is an empty constructor declared
52          try {
53              this.getClazz().getConstructor();
54          }
55          catch (NoSuchMethodException e) {
56              throw new HBqlException("Class " + this + " is missing a null constructor");
57          }
58  
59          for (final Field field : clazz.getDeclaredFields()) {
60              if (field.getAnnotation(Column.class) != null)
61                  this.processColumnAnnotation(field);
62  
63              if (field.getAnnotation(ColumnVersionMap.class) != null)
64                  this.processColumnVersionAnnotation(field);
65          }
66  
67          if (!this.getColumnMap().containsKey(this.getKeyAttrib().getFamilyQualifiedName()))
68              throw new HBqlException(this.getClazz().getName() + " must contain a mapping to key attribute "
69                                      + this.getKeyAttrib().getFamilyQualifiedName());
70      }
71  
72      public static boolean isAnnotatedObject(final Class<?> clazz) {
73          return clazz.getAnnotation(org.apache.hadoop.hbase.hbql.client.Mapping.class) != null;
74      }
75  
76      public static AnnotationResultAccessor newAnnotationMapping(final HConnectionImpl conn,
77                                                                  final Class<?> clazz) throws HBqlException {
78  
79          Mapping mappingAnnotation = clazz.getAnnotation(Mapping.class);
80  
81          if (mappingAnnotation == null)
82              throw new HBqlException("Class " + clazz.getName() + " is missing @Mapping annotation");
83  
84          if (mappingAnnotation.name() == null || mappingAnnotation.name().length() == 0)
85              throw new HBqlException("@Mapping annotation for class " + clazz.getName() + " is missing a name");
86  
87          final TableMapping tableMapping = conn.getMapping(mappingAnnotation.name());
88          return new AnnotationResultAccessor(tableMapping, clazz);
89      }
90  
91      private void processColumnAnnotation(final Field field) throws HBqlException {
92  
93          final Column columnAnno = field.getAnnotation(Column.class);
94          final String attribName = columnAnno.name().length() == 0 ? field.getName() : columnAnno.name();
95          final HRecordAttrib columnAttrib = (HRecordAttrib)this.getMapping().getAttribByVariableName(attribName);
96  
97          if (columnAttrib == null)
98              throw new HBqlException("Unknown attribute " + this.getMapping() + "." + attribName
99                                      + " in " + this.getClazz().getName());
100 
101         if (this.getColumnMap().containsKey(columnAttrib.getFamilyQualifiedName()))
102             throw new HBqlException("Cannot map multiple instance variables in " + this.getClazz().getName()
103                                     + " to " + columnAttrib.getFamilyQualifiedName());
104 
105         final CurrentValueAnnotationAttrib attrib = new CurrentValueAnnotationAttrib(field, columnAttrib);
106         this.getColumnMap().put(columnAttrib.getFamilyQualifiedName(), attrib);
107     }
108 
109     private void processColumnVersionAnnotation(final Field field) throws HBqlException {
110 
111         final ColumnVersionMap versionAnno = field.getAnnotation(ColumnVersionMap.class);
112         final String attribName = versionAnno.name().length() == 0 ? field.getName() : versionAnno.name();
113         final ColumnAttrib columnAttrib = this.getMapping().getAttribByVariableName(attribName);
114 
115         this.getColumnVersionMap().put(columnAttrib.getFamilyQualifiedName(),
116                                        new VersionAnnotationAttrib(columnAttrib.getFamilyName(),
117                                                                    columnAttrib.getColumnName(),
118                                                                    field,
119                                                                    columnAttrib.getFieldType(),
120                                                                    columnAttrib.getGetter(),
121                                                                    columnAttrib.getSetter()));
122     }
123 
124     public ColumnAttrib getKeyAttrib() throws HBqlException {
125         final String name = this.getMapping().getKeyAttrib().getFamilyQualifiedName();
126         return this.getAttrib(name);
127     }
128 
129     public ColumnAttrib getColumnAttribByQualifiedName(final String familyName,
130                                                        final String columnName) throws HBqlException {
131         final ColumnAttrib attrib = this.getTableMapping().getAttribFromFamilyQualifiedName(familyName
132                                                                                             + ":" + columnName);
133         return this.getColumnAttribByName(attrib.getFamilyQualifiedName());
134     }
135 
136     public ColumnAttrib getColumnAttribByName(final String name) throws HBqlException {
137         final String valname = this.getMapping().getAttribByVariableName(name).getFamilyQualifiedName();
138         return this.getAttrib(valname);
139     }
140 
141     private Class<?> getClazz() {
142         return this.clazz;
143     }
144 
145     private Map<String, CurrentValueAnnotationAttrib> getColumnMap() {
146         return this.columnMap;
147     }
148 
149     private Map<String, VersionAnnotationAttrib> getColumnVersionMap() {
150         return this.columnVersionMap;
151     }
152 
153     public CurrentValueAnnotationAttrib getAttrib(final String name) {
154         return this.getColumnMap().get(name);
155     }
156 
157     public VersionAnnotationAttrib getVersionAttrib(final String name) {
158         return this.getColumnVersionMap().get(name);
159     }
160 
161     private Object newInstance() throws IllegalAccessException, InstantiationException {
162         return this.getClazz().newInstance();
163     }
164 
165     public Object newObject(final HConnectionImpl conn,
166                             final MappingContext mappingContext,
167                             final List<SelectElement> selectElementList,
168                             final int maxVersions,
169                             final Result result) throws HBqlException {
170         try {
171             // Create object and assign values
172             final Object newobj = this.createNewObject();
173             this.assignSelectValues(conn, newobj, selectElementList, maxVersions, result);
174             return newobj;
175         }
176         catch (Exception e) {
177             e.printStackTrace();
178             throw new HBqlException("Error in newObject() " + e.getMessage());
179         }
180     }
181 
182     private void assignSelectValues(final HConnectionImpl conn,
183                                     final Object newobj,
184                                     final List<SelectElement> selectElementList,
185                                     final int maxVersions,
186                                     final Result result) throws HBqlException {
187 
188         // Set key value
189         final ColumnAttrib keyAttrib = this.getKeyAttrib();
190         this.getAttrib(keyAttrib.getFamilyQualifiedName()).setCurrentValue(newobj, 0, result.getRow());
191 
192         // Set the non-key values
193         for (final SelectElement selectElement : selectElementList)
194             selectElement.assignSelectValue(conn, newobj, maxVersions, result);
195     }
196 
197     private Object createNewObject() throws HBqlException {
198 
199         // Create new instance
200         final Object newobj;
201         try {
202             newobj = this.newInstance();
203         }
204         catch (Exception e) {
205             e.printStackTrace();
206             throw new HBqlException("Cannot create new instance of " + this.getClazz().getName());
207         }
208 
209         return newobj;
210     }
211 }