View Javadoc

1   /*
2    * Copyright 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.client;
22  
23  import org.apache.hadoop.hbase.HRegionInfo;
24  import org.apache.hadoop.hbase.HServerAddress;
25  import org.apache.hadoop.hbase.util.Bytes;
26  import org.apache.hadoop.io.Writable;
27  
28  import java.io.DataInput;
29  import java.io.DataOutput;
30  import java.io.IOException;
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.TreeMap;
39  import java.util.TreeSet;
40  
41  /**
42   * @deprecated Use MultiAction instead
43   * Data type class for putting multiple regions worth of puts in one RPC.
44   */
45  public class MultiPut extends Operation implements Writable {
46    public HServerAddress address; // client code ONLY
47  
48    // TODO make this configurable
49    public static final int DEFAULT_MAX_PUT_OUTPUT = 10;
50  
51    // map of regions to lists of puts for that region.
52    public Map<byte[], List<Put> > puts = new TreeMap<byte[], List<Put>>(Bytes.BYTES_COMPARATOR);
53  
54    /**
55     * Writable constructor only.
56     */
57    public MultiPut() {}
58  
59    /**
60     * MultiPut for putting multiple regions worth of puts in one RPC.
61     * @param a address
62     */
63    public MultiPut(HServerAddress a) {
64      address = a;
65    }
66  
67    public int size() {
68      int size = 0;
69      for( List<Put> l : puts.values()) {
70        size += l.size();
71      }
72      return size;
73    }
74  
75    public void add(byte[] regionName, Put aPut) {
76      List<Put> rsput = puts.get(regionName);
77      if (rsput == null) {
78        rsput = new ArrayList<Put>();
79        puts.put(regionName, rsput);
80      }
81      rsput.add(aPut);
82    }
83  
84    public Collection<Put> allPuts() {
85      List<Put> res = new ArrayList<Put>();
86      for ( List<Put> pp : puts.values() ) {
87        res.addAll(pp);
88      }
89      return res;
90    }
91  
92    /**
93     * Compile the table and column family (i.e. schema) information 
94     * into a String. Useful for parsing and aggregation by debugging,
95     * logging, and administration tools.
96     * @return Map
97     */
98    @Override
99    public Map<String, Object> getFingerprint() {
100     Map<String, Object> map = new HashMap<String, Object>();
101     // for extensibility, we have a map of table information that we will
102     // populate with only family information for each table
103     Map<String, Map> tableInfo = 
104       new HashMap<String, Map>();
105     map.put("tables", tableInfo);
106     for (Map.Entry<byte[], List<Put>> entry : puts.entrySet()) {
107       // our fingerprint only concerns itself with which families are touched,
108       // not how many Puts touch them, so we use this Set to do just that.
109       Set<String> familySet;
110       try {
111         // since the puts are stored by region, we may have already
112         // recorded families for this region. if that is the case,
113         // we want to add to the existing Set. if not, we make a new Set.
114         String tableName = Bytes.toStringBinary(
115             HRegionInfo.parseRegionName(entry.getKey())[0]);
116         if (tableInfo.get(tableName) == null) {
117           Map<String, Object> table = new HashMap<String, Object>();
118           familySet = new TreeSet<String>();
119           table.put("families", familySet);
120           tableInfo.put(tableName, table);
121         } else {
122           familySet = (Set<String>) tableInfo.get(tableName).get("families");
123         }
124       } catch (IOException ioe) {
125         // in the case of parse error, default to labeling by region
126         Map<String, Object> table = new HashMap<String, Object>();
127         familySet = new TreeSet<String>();
128         table.put("families", familySet);
129         tableInfo.put(Bytes.toStringBinary(entry.getKey()), table);
130       }   
131       // we now iterate through each Put and keep track of which families 
132       // are affected in this table.
133       for (Put p : entry.getValue()) {
134         for (byte[] fam : p.getFamilyMap().keySet()) {
135           familySet.add(Bytes.toStringBinary(fam));
136         }
137       }
138     }
139     return map;
140   }
141 
142   /**
143    * Compile the details beyond the scope of getFingerprint (mostly 
144    * toMap from the Puts) into a Map along with the fingerprinted 
145    * information. Useful for debugging, logging, and administration tools.
146    * @param maxCols a limit on the number of columns output prior to truncation
147    * @return Map
148    */
149   @Override
150   public Map<String, Object> toMap(int maxCols) {
151     Map<String, Object> map = getFingerprint();
152     Map<String, Object> tableInfo = (Map<String, Object>) map.get("tables");
153     int putCount = 0;
154     for (Map.Entry<byte[], List<Put>> entry : puts.entrySet()) {
155       // If the limit has been hit for put output, just adjust our counter
156       if (putCount >= DEFAULT_MAX_PUT_OUTPUT) {
157         putCount += entry.getValue().size();
158         continue;
159       }
160       List<Put> regionPuts = entry.getValue();
161       List<Map<String, Object>> putSummaries =
162         new ArrayList<Map<String, Object>>();
163       // find out how many of this region's puts we can add without busting
164       // the maximum
165       int regionPutsToAdd = regionPuts.size();
166       putCount += regionPutsToAdd;
167       if (putCount > DEFAULT_MAX_PUT_OUTPUT) {
168         regionPutsToAdd -= putCount - DEFAULT_MAX_PUT_OUTPUT;
169       }
170       for (Iterator<Put> iter = regionPuts.iterator(); regionPutsToAdd-- > 0;) {
171         putSummaries.add(iter.next().toMap(maxCols));
172       }
173       // attempt to extract the table name from the region name
174       String tableName = "";
175       try {
176         tableName = Bytes.toStringBinary(
177             HRegionInfo.parseRegionName(entry.getKey())[0]);
178       } catch (IOException ioe) {
179         // in the case of parse error, default to labeling by region
180         tableName = Bytes.toStringBinary(entry.getKey());
181       }
182       // since the puts are stored by region, we may have already 
183       // recorded puts for this table. if that is the case, 
184       // we want to add to the existing List. if not, we place a new list 
185       // in the map
186       Map<String, Object> table =
187         (Map<String, Object>) tableInfo.get(tableName);
188       if (table == null) {
189         // in case the Put has changed since getFingerprint's map was built
190         table = new HashMap<String, Object>();
191         tableInfo.put(tableName, table);
192         table.put("puts", putSummaries);
193       } else if (table.get("puts") == null) {
194         table.put("puts", putSummaries);
195       } else {
196         ((List<Map<String, Object>>) table.get("puts")).addAll(putSummaries);
197       }
198     }
199     map.put("totalPuts", putCount);
200     return map;
201   }
202 
203   @Override
204   public void write(DataOutput out) throws IOException {
205     out.writeInt(puts.size());
206     for( Map.Entry<byte[],List<Put>> e : puts.entrySet()) {
207       Bytes.writeByteArray(out, e.getKey());
208 
209       List<Put> ps = e.getValue();
210       out.writeInt(ps.size());
211       for( Put p : ps ) {
212         p.write(out);
213       }
214     }
215   }
216 
217   @Override
218   public void readFields(DataInput in) throws IOException {
219     puts.clear();
220 
221     int mapSize = in.readInt();
222 
223     for (int i = 0 ; i < mapSize; i++) {
224       byte[] key = Bytes.readByteArray(in);
225 
226       int listSize = in.readInt();
227       List<Put> ps = new ArrayList<Put>(listSize);
228       for ( int j = 0 ; j < listSize; j++ ) {
229         Put put = new Put();
230         put.readFields(in);
231         ps.add(put);
232       }
233       puts.put(key, ps);
234     }
235   }
236 }