View Javadoc

1   /*
2    * Copyright (c) 2011.  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.expreval.client.NullColumnValueException;
24  import org.apache.expreval.client.ResultMissingColumnException;
25  import org.apache.hadoop.hbase.client.Result;
26  import org.apache.hadoop.hbase.hbql.client.HBqlException;
27  import org.apache.hadoop.hbase.hbql.impl.Utils;
28  import org.apache.hadoop.hbase.hbql.io.IO;
29  import org.apache.hadoop.hbase.hbql.parser.ParserSupport;
30  import org.apache.hadoop.hbase.hbql.statement.args.ColumnWidth;
31  import org.apache.hadoop.hbase.hbql.statement.args.DefaultArg;
32  import org.apache.hadoop.hbase.hbql.util.AtomicReferences;
33  
34  import java.io.Serializable;
35  import java.lang.reflect.InvocationTargetException;
36  import java.lang.reflect.Method;
37  import java.util.Map;
38  import java.util.NavigableMap;
39  import java.util.concurrent.atomic.AtomicReference;
40  
41  public abstract class ColumnAttrib implements Serializable {
42  
43      private static final long serialVersionUID = 1L;
44  
45      private ColumnDefinition columnDefinition;
46  
47      private           AtomicReference<byte[]> atomicFamilyQualifiedBytes = AtomicReferences.newAtomicReference();
48      private           AtomicReference<byte[]> atomicFamilyBytes          = AtomicReferences.newAtomicReference();
49      private           AtomicReference<byte[]> atomicColumnBytes          = AtomicReferences.newAtomicReference();
50      private transient Method                  getterMethod               = null;
51      private transient Method                  setterMethod               = null;
52      private boolean embedded;
53  
54      public ColumnAttrib() {
55      }
56  
57      protected ColumnAttrib(final ColumnDefinition columnDefinition) {
58          this.columnDefinition = columnDefinition;
59          this.embedded = this.getFamilyName() != null && this.getFamilyName().equals(ParserSupport.EMBEDDED);
60      }
61  
62      private boolean isEmbedded() {
63          return this.embedded;
64      }
65  
66      public Object getDefaultValue() throws HBqlException {
67          return (this.hasDefaultArg()) ? this.getDefaultArg().getDefaultValue() : null;
68      }
69  
70      public boolean hasDefaultArg() throws HBqlException {
71          return this.getDefaultArg() != null;
72      }
73  
74      protected DefaultArg getDefaultArg() {
75          return null;
76      }
77  
78      public abstract Object getCurrentValue(final Object obj) throws HBqlException, ResultMissingColumnException, NullColumnValueException;
79  
80      public abstract void setCurrentValue(final Object obj,
81                                           final long timestamp,
82                                           final Object val) throws HBqlException;
83  
84      public abstract Map<Long, Object> getVersionMap(final Object obj) throws HBqlException;
85  
86      public abstract void setUnMappedCurrentValue(final Object obj,
87                                                   final String name,
88                                                   final byte[] value) throws HBqlException;
89  
90      public abstract void setUnMappedVersionMap(final Object obj,
91                                                 final String name,
92                                                 final NavigableMap<Long, byte[]> timeStampMap) throws HBqlException;
93  
94      protected abstract Method getMethod(final String methodName,
95                                          final Class<?>... params) throws NoSuchMethodException, HBqlException;
96  
97      protected abstract Class getComponentType() throws HBqlException;
98  
99      public abstract String getNameToUseInExceptions();
100 
101     public abstract String getEnclosingClassName();
102 
103     // This is necessary before sending off with filter
104     public void resetDefaultValue() throws HBqlException {
105         if (this.hasDefaultArg())
106             this.getDefaultArg().reset();
107     }
108 
109     public void setVersionMap(final Object obj, final NavigableMap<Long, byte[]> timeStampMap) throws HBqlException {
110 
111         final Map<Long, Object> mapVal = this.getVersionMap(obj);
112 
113         for (final Long timestamp : timeStampMap.keySet()) {
114             final Object val = this.getValueFromBytes(obj, timeStampMap.get(timestamp));
115             mapVal.put(timestamp, val);
116         }
117     }
118 
119     public String getFamilyQualifiedName() {
120         if (!this.isEmbedded() && Utils.isValidString(this.getFamilyName()))
121             return this.getFamilyName() + ":" + this.getColumnName();
122         else
123             return this.getColumnName();
124     }
125 
126     private AtomicReference<byte[]> getAtomicFamilyQualifiedBytes() {
127         return this.atomicFamilyQualifiedBytes;
128     }
129 
130     public byte[] getFamilyQualifiedNameAsBytes() throws HBqlException {
131         if (this.getAtomicFamilyQualifiedBytes().get() == null)
132             synchronized (this) {
133                 if (this.getAtomicFamilyQualifiedBytes().get() == null) {
134                     final byte[] b = IO.getSerialization().getStringAsBytes(this.getFamilyQualifiedName());
135                     this.getAtomicFamilyQualifiedBytes().set(b);
136                 }
137             }
138         return this.getAtomicFamilyQualifiedBytes().get();
139     }
140 
141     private AtomicReference<byte[]> getAtomicFamilyBytes() {
142         return this.atomicFamilyBytes;
143     }
144 
145     public byte[] getFamilyNameAsBytes() throws HBqlException {
146         if (this.getAtomicFamilyBytes().get() == null)
147             synchronized (this) {
148                 if (this.getAtomicFamilyBytes().get() == null) {
149                     final byte[] b = IO.getSerialization().getStringAsBytes(this.getFamilyName());
150                     this.getAtomicFamilyBytes().set(b);
151                 }
152             }
153         return this.getAtomicFamilyBytes().get();
154     }
155 
156     private AtomicReference<byte[]> getAtomicColumnBytes() {
157         return this.atomicColumnBytes;
158     }
159 
160     public byte[] getColumnNameAsBytes() throws HBqlException {
161         if (this.getAtomicColumnBytes().get() == null)
162             synchronized (this) {
163                 if (this.getAtomicColumnBytes().get() == null) {
164                     final byte[] b = IO.getSerialization().getStringAsBytes(this.getColumnName());
165                     this.getAtomicColumnBytes().set(b);
166                 }
167             }
168         return this.getAtomicColumnBytes().get();
169     }
170 
171     public boolean equals(final Object o) {
172 
173         if (!(o instanceof ColumnAttrib))
174             return false;
175 
176         final ColumnAttrib var = (ColumnAttrib)o;
177 
178         return var.getAliasName().equals(this.getAliasName())
179                && var.getFamilyQualifiedName().equals(this.getFamilyQualifiedName());
180     }
181 
182     public int hashCode() {
183         return this.getAliasName().hashCode() + this.getFamilyQualifiedName().hashCode();
184     }
185 
186     protected void defineAccessors() throws HBqlException {
187         try {
188             if (Utils.isValidString(this.getGetter())) {
189                 this.getterMethod = this.getMethod(this.getGetter());
190 
191                 // Check return type of getter
192                 final Class<?> returnType = this.getGetterMethod().getReturnType();
193 
194                 if (!(returnType.isArray() && returnType.getComponentType() == Byte.TYPE))
195                     throw new HBqlException(this.getEnclosingClassName() + "." + this.getGetter() + "()"
196                                             + " does not have a return type of byte[]");
197             }
198         }
199         catch (NoSuchMethodException e) {
200             throw new HBqlException("Missing method byte[] " + this.getEnclosingClassName() + "."
201                                     + this.getGetter() + "()");
202         }
203 
204         try {
205             if (Utils.isValidString(this.getSetter())) {
206                 this.setterMethod = this.getMethod(this.getSetter(), Class.forName("[B"));
207 
208                 // Check if it takes single byte[] arg
209                 final Class<?>[] args = this.getSetterMethod().getParameterTypes();
210                 if (args.length != 1 || !(args[0].isArray() && args[0].getComponentType() == Byte.TYPE))
211                     throw new HBqlException(this.getEnclosingClassName() + "." + this.getSetter() + "()"
212                                             + " does not have single byte[] arg");
213             }
214         }
215         catch (NoSuchMethodException e) {
216             throw new HBqlException("Missing method " + this.getEnclosingClassName()
217                                     + "." + this.getSetter() + "(byte[] arg)");
218         }
219         catch (ClassNotFoundException e) {
220             // This will not be hit
221             throw new HBqlException("Missing method " + this.getEnclosingClassName()
222                                     + "." + this.getSetter() + "(byte[] arg)");
223         }
224     }
225 
226     public byte[] invokeGetterMethod(final Object obj) throws HBqlException {
227         try {
228             return (byte[])this.getGetterMethod().invoke(obj);
229         }
230         catch (IllegalAccessException e) {
231             throw new HBqlException("Error getting value of " + this.getNameToUseInExceptions());
232         }
233         catch (InvocationTargetException e) {
234             throw new HBqlException("Error getting value of " + this.getNameToUseInExceptions());
235         }
236     }
237 
238     public Object invokeSetterMethod(final Object obj, final byte[] b) throws HBqlException {
239         try {
240             // TODO Resolve passing primitive to Object varargs
241             return this.getSetterMethod().invoke(obj, b);
242         }
243         catch (IllegalAccessException e) {
244             throw new HBqlException("Error setting value of " + this.getNameToUseInExceptions());
245         }
246         catch (InvocationTargetException e) {
247             throw new HBqlException("Error setting value of " + this.getNameToUseInExceptions());
248         }
249     }
250 
251     public byte[] getValueAsBytes(final Object obj) throws HBqlException {
252 
253         final byte[] retval;
254 
255         if (this.hasGetter()) {
256             retval = this.invokeGetterMethod(obj);
257         }
258         else {
259             final Object value;
260             try {
261                 value = this.getCurrentValue(obj);
262             }
263             catch (ResultMissingColumnException e) {
264                 return null;
265             }
266             catch (NullColumnValueException e) {
267                 return null;
268             }
269 
270             if (this.isAnArray()) {
271                 retval = IO.getSerialization().getArrayAsBytes(this.getFieldType(), value);
272             }
273             else {
274                 this.validateValueWidth(value);
275                 retval = IO.getSerialization().getScalarAsBytes(this.getFieldType(), value);
276             }
277         }
278 
279         return retval;
280     }
281 
282     public void validateValueWidth(final Object value) throws HBqlException {
283 
284         final ColumnWidth columnWidth = this.getColumnDefinition().getColumnWidth();
285         if (columnWidth.isWidthSpecified()) {
286             final int width = columnWidth.getWidth();
287             if (width > 0 && value instanceof String) {
288                 final String str = (String)value;
289                 if (str.length() != width)
290                     throw new HBqlException("Invalid length in " + this.getNameToUseInExceptions()
291                                             + " expecting width " + width + " but found " + str.length()
292                                             + " with string \"" + str + "\"");
293             }
294         }
295     }
296 
297     public Object getValueFromBytes(final Object obj, final byte[] b) throws HBqlException {
298 
299         if (this.hasSetter())
300             return this.invokeSetterMethod(obj, b);
301 
302         if (this.isAnArray())
303             return IO.getSerialization().getArrayFromBytes(this.getFieldType(), this.getComponentType(), b);
304         else
305             return IO.getSerialization().getScalarFromBytes(this.getFieldType(), b);
306     }
307 
308     public Object getValueFromBytes(final Result result) throws HBqlException, ResultMissingColumnException, NullColumnValueException {
309 
310         if (this.isAKeyAttrib()) {
311             return IO.getSerialization().getStringFromBytes(result.getRow());
312         }
313         else {
314             if (!result.containsColumn(this.getFamilyNameAsBytes(), this.getColumnNameAsBytes())) {
315 
316                 // See if a default value is present
317                 if (!this.hasDefaultArg())
318                     throw new ResultMissingColumnException(this.getFamilyQualifiedName());
319 
320                 return this.getDefaultValue();
321             }
322 
323             final byte[] b = result.getValue(this.getFamilyNameAsBytes(), this.getColumnNameAsBytes());
324 
325             if (b == null) {
326                 throw new NullColumnValueException(this.getFamilyQualifiedName());
327             }
328             else {
329                 if (this.isAnArray())
330                     return IO.getSerialization().getArrayFromBytes(this.getFieldType(), this.getComponentType(), b);
331                 else
332                     return IO.getSerialization().getScalarFromBytes(this.getFieldType(), b);
333             }
334         }
335     }
336 
337     public void setCurrentValue(final Object obj, final long timestamp, final byte[] b) throws HBqlException {
338         final Object val = this.getValueFromBytes(obj, b);
339         this.setCurrentValue(obj, timestamp, val);
340     }
341 
342     protected String getGetter() {
343         return this.getColumnDefinition().getGetter();
344     }
345 
346     protected String getSetter() {
347         return this.getColumnDefinition().getSetter();
348     }
349 
350     protected Method getGetterMethod() {
351         return this.getterMethod;
352     }
353 
354     protected Method getSetterMethod() {
355         return this.setterMethod;
356     }
357 
358     protected boolean hasGetter() {
359         return this.getGetterMethod() != null;
360     }
361 
362     protected boolean hasSetter() {
363         return this.getSetterMethod() != null;
364     }
365 
366     public boolean isACurrentValue() {
367         return true;
368     }
369 
370     public boolean isAVersionValue() {
371         return false;
372     }
373 
374     public ColumnDefinition getColumnDefinition() {
375         return this.columnDefinition;
376     }
377 
378     public boolean hasAlias() {
379         return Utils.isValidString(this.getColumnDefinition().getAliasName());
380     }
381 
382     public String getAliasName() {
383         return (this.hasAlias()) ? this.getColumnDefinition().getAliasName() : this.getFamilyQualifiedName();
384     }
385 
386     public boolean isASelectFamilyAttrib() {
387         return false;
388     }
389 
390     public boolean isAnArray() {
391         return this.getColumnDefinition().isAnArray();
392     }
393 
394     public String getFamilyName() {
395         return this.getColumnDefinition().getFamilyName();
396     }
397 
398     public String getColumnName() {
399         return this.getColumnDefinition().getColumnName();
400     }
401 
402     public FieldType getFieldType() {
403         return this.getColumnDefinition().getFieldType();
404     }
405 
406     public boolean isAKeyAttrib() {
407         return false;
408     }
409 }