View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.client;
19  
20  import java.io.DataInput;
21  import java.io.DataOutput;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.hadoop.hbase.KeyValue;
29  import org.apache.hadoop.hbase.util.Bytes;
30  import org.apache.hadoop.io.Writable;
31  
32  /**
33   * Performs Append operations on a single row.
34   * <p>
35   * Note that this operation does not appear atomic to readers. Appends are done
36   * under a single row lock, so write operations to a row are synchronized, but
37   * readers do not take row locks so get and scan operations can see this
38   * operation partially completed.
39   * <p>
40   * To append to a set of columns of a row, instantiate an Append object with the
41   * row to append to. At least one column to append must be specified using the
42   * {@link #add(byte[], byte[], byte[])} method.
43   */
44  public class Append extends Mutation {
45    private static final String RETURN_RESULTS = "_rr_";
46    private static final byte APPEND_VERSION = (byte)1;
47  
48    /**
49     * @param returnResults
50     *          True (default) if the append operation should return the results.
51     *          A client that is not interested in the result can save network
52     *          bandwidth setting this to false.
53     */
54    public void setReturnResults(boolean returnResults) {
55      setAttribute(RETURN_RESULTS, Bytes.toBytes(returnResults));
56    }
57  
58    /**
59     * @return current setting for returnResults
60     */
61    public boolean isReturnResults() {
62      byte[] v = getAttribute(RETURN_RESULTS);
63      return v == null ? true : Bytes.toBoolean(v);
64    }
65  
66    /** Constructor for Writable.  DO NOT USE */
67    public Append() {}
68  
69    /**
70     * Create a Append operation for the specified row.
71     * <p>
72     * At least one column must be appended to.
73     * @param row row key
74     */
75    public Append(byte[] row) {
76      this.row = Arrays.copyOf(row, row.length);
77    }
78  
79    /**
80     * Add the specified column and value to this Append operation.
81     * @param family family name
82     * @param qualifier column qualifier
83     * @param value value to append to specified column
84     * @return this
85     */
86    public Append add(byte [] family, byte [] qualifier, byte [] value) {
87      List<KeyValue> list = familyMap.get(family);
88      if(list == null) {
89        list = new ArrayList<KeyValue>();
90      }
91      list.add(new KeyValue(
92          this.row, family, qualifier, this.ts, KeyValue.Type.Put, value));
93      familyMap.put(family, list);
94      return this;
95    }
96  
97    @Override
98    public void readFields(final DataInput in)
99    throws IOException {
100     int version = in.readByte();
101     if (version > APPEND_VERSION) {
102       throw new IOException("version not supported: "+version);
103     }
104     this.row = Bytes.readByteArray(in);
105     this.ts = in.readLong();
106     this.lockId = in.readLong();
107     this.writeToWAL = in.readBoolean();
108     int numFamilies = in.readInt();
109     if (!this.familyMap.isEmpty()) this.familyMap.clear();
110     for(int i=0;i<numFamilies;i++) {
111       byte [] family = Bytes.readByteArray(in);
112       int numKeys = in.readInt();
113       List<KeyValue> keys = new ArrayList<KeyValue>(numKeys);
114       int totalLen = in.readInt();
115       byte [] buf = new byte[totalLen];
116       int offset = 0;
117       for (int j = 0; j < numKeys; j++) {
118         int keyLength = in.readInt();
119         in.readFully(buf, offset, keyLength);
120         keys.add(new KeyValue(buf, offset, keyLength));
121         offset += keyLength;
122       }
123       this.familyMap.put(family, keys);
124     }
125     readAttributes(in);
126   }
127 
128   @Override
129   public void write(final DataOutput out)
130   throws IOException {
131     out.writeByte(APPEND_VERSION);
132     Bytes.writeByteArray(out, this.row);
133     out.writeLong(this.ts);
134     out.writeLong(this.lockId);
135     out.writeBoolean(this.writeToWAL);
136     out.writeInt(familyMap.size());
137     for (Map.Entry<byte [], List<KeyValue>> entry : familyMap.entrySet()) {
138       Bytes.writeByteArray(out, entry.getKey());
139       List<KeyValue> keys = entry.getValue();
140       out.writeInt(keys.size());
141       int totalLen = 0;
142       for(KeyValue kv : keys) {
143         totalLen += kv.getLength();
144       }
145       out.writeInt(totalLen);
146       for(KeyValue kv : keys) {
147         out.writeInt(kv.getLength());
148         out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
149       }
150     }
151     writeAttributes(out);
152   }
153 }