001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.thrift;
020
021import java.io.UnsupportedEncodingException;
022import java.nio.ByteBuffer;
023import java.security.PrivilegedExceptionAction;
024import java.text.NumberFormat;
025import java.util.ArrayList;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029import javax.security.auth.Subject;
030import javax.security.auth.login.LoginContext;
031import javax.security.sasl.Sasl;
032import org.apache.hadoop.hbase.thrift.generated.AlreadyExists;
033import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor;
034import org.apache.hadoop.hbase.thrift.generated.Hbase;
035import org.apache.hadoop.hbase.thrift.generated.Mutation;
036import org.apache.hadoop.hbase.thrift.generated.TCell;
037import org.apache.hadoop.hbase.thrift.generated.TRowResult;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.apache.hadoop.hbase.util.ClientUtils;
040import org.apache.thrift.protocol.TBinaryProtocol;
041import org.apache.thrift.protocol.TProtocol;
042import org.apache.thrift.transport.TSaslClientTransport;
043import org.apache.thrift.transport.TSocket;
044import org.apache.thrift.transport.TTransport;
045import org.apache.yetus.audience.InterfaceAudience;
046
047/**
048 * See the instructions under hbase-examples/README.txt
049 */
050@InterfaceAudience.Private
051public class DemoClient {
052
053  static protected int port;
054  static protected String host;
055
056  private static boolean secure = false;
057  private static String serverPrincipal = "hbase";
058
059  public static void main(String[] args) throws Exception {
060    if (args.length < 2 || args.length > 4 || (args.length > 2 && !isBoolean(args[2]))) {
061      System.out.println("Invalid arguments!");
062      System.out.println("Usage: DemoClient host port [secure=false [server-principal=hbase] ]");
063
064      System.exit(-1);
065    }
066
067    port = Integer.parseInt(args[1]);
068    host = args[0];
069
070    if (args.length > 2) {
071      secure = Boolean.parseBoolean(args[2]);
072    }
073
074    if (args.length == 4) {
075      serverPrincipal = args[3];
076    }
077
078    final DemoClient client = new DemoClient();
079    Subject.doAs(getSubject(),
080      new PrivilegedExceptionAction<Void>() {
081        @Override
082        public Void run() throws Exception {
083          client.run();
084          return null;
085        }
086      });
087  }
088
089  private static boolean isBoolean(String s){
090    return Boolean.TRUE.toString().equalsIgnoreCase(s) ||
091            Boolean.FALSE.toString().equalsIgnoreCase(s);
092  }
093
094  DemoClient() {
095  }
096
097    // Helper to translate strings to UTF8 bytes
098    private byte[] bytes(String s) {
099        try {
100            return s.getBytes("UTF-8");
101        } catch (UnsupportedEncodingException e) {
102            e.printStackTrace();
103            return null;
104        }
105    }
106
107  private void run() throws Exception {
108    TTransport transport = new TSocket(host, port);
109    if (secure) {
110      Map<String, String> saslProperties = new HashMap<>();
111      saslProperties.put(Sasl.QOP, "auth-conf,auth-int,auth");
112      /*
113       * The Thrift server the DemoClient is trying to connect to
114       * must have a matching principal, and support authentication.
115       *
116       * The HBase cluster must be secure, allow proxy user.
117       */
118      transport = new TSaslClientTransport("GSSAPI", null,
119              serverPrincipal, // Thrift server user name, should be an authorized proxy user.
120              host, // Thrift server domain
121              saslProperties, null, transport);
122    }
123
124    transport.open();
125
126    TProtocol protocol = new TBinaryProtocol(transport, true, true);
127    Hbase.Client client = new Hbase.Client(protocol);
128
129    byte[] t = bytes("demo_table");
130
131    // Scan all tables, look for the demo table and delete it.
132    System.out.println("scanning tables...");
133
134    for (ByteBuffer name : client.getTableNames()) {
135      System.out.println("  found: " + ClientUtils.utf8(name.array()));
136
137      if (ClientUtils.utf8(name.array()).equals(ClientUtils.utf8(t))) {
138        if (client.isTableEnabled(name)) {
139          System.out.println("    disabling table: " + ClientUtils.utf8(name.array()));
140          client.disableTable(name);
141        }
142
143        System.out.println("    deleting table: " + ClientUtils.utf8(name.array()));
144        client.deleteTable(name);
145      }
146    }
147
148    // Create the demo table with two column families, entry: and unused:
149    ArrayList<ColumnDescriptor> columns = new ArrayList<>(2);
150    ColumnDescriptor col;
151    col = new ColumnDescriptor();
152    col.name = ByteBuffer.wrap(bytes("entry:"));
153    col.timeToLive = Integer.MAX_VALUE;
154    col.maxVersions = 10;
155    columns.add(col);
156    col = new ColumnDescriptor();
157    col.name = ByteBuffer.wrap(bytes("unused:"));
158    col.timeToLive = Integer.MAX_VALUE;
159    columns.add(col);
160
161    System.out.println("creating table: " + ClientUtils.utf8(t));
162
163    try {
164      client.createTable(ByteBuffer.wrap(t), columns);
165    } catch (AlreadyExists ae) {
166      System.out.println("WARN: " + ae.message);
167    }
168
169    System.out.println("column families in " + ClientUtils.utf8(t) + ": ");
170    Map<ByteBuffer, ColumnDescriptor> columnMap = client.getColumnDescriptors(ByteBuffer.wrap(t));
171
172    for (ColumnDescriptor col2 : columnMap.values()) {
173      System.out.println("  column: " + ClientUtils.utf8(col2.name.array()) + ", maxVer: "
174          + col2.maxVersions);
175    }
176
177    Map<ByteBuffer, ByteBuffer> dummyAttributes = null;
178    boolean writeToWal = false;
179
180    // Test UTF-8 handling
181    byte[] invalid = {(byte) 'f', (byte) 'o', (byte) 'o', (byte) '-',
182                      (byte) 0xfc, (byte) 0xa1, (byte) 0xa1, (byte) 0xa1, (byte) 0xa1};
183    byte[] valid = {(byte) 'f', (byte) 'o', (byte) 'o', (byte) '-',
184                    (byte) 0xE7, (byte) 0x94, (byte) 0x9F, (byte) 0xE3, (byte) 0x83,
185                    (byte) 0x93, (byte) 0xE3, (byte) 0x83, (byte) 0xBC, (byte) 0xE3,
186                    (byte) 0x83, (byte) 0xAB};
187
188    ArrayList<Mutation> mutations;
189    // non-utf8 is fine for data
190    mutations = new ArrayList<>(1);
191    mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:foo")),
192            ByteBuffer.wrap(invalid), writeToWal));
193    client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(bytes("foo")),
194            mutations, dummyAttributes);
195
196    // this row name is valid utf8
197    mutations = new ArrayList<>(1);
198    mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:foo")),
199            ByteBuffer.wrap(valid), writeToWal));
200    client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(valid), mutations, dummyAttributes);
201
202    // non-utf8 is now allowed in row names because HBase stores values as binary
203    mutations = new ArrayList<>(1);
204    mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:foo")),
205            ByteBuffer.wrap(invalid), writeToWal));
206    client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(invalid), mutations, dummyAttributes);
207
208    // Run a scanner on the rows we just created
209    ArrayList<ByteBuffer> columnNames = new ArrayList<>();
210    columnNames.add(ByteBuffer.wrap(bytes("entry:")));
211
212    System.out.println("Starting scanner...");
213    int scanner = client.scannerOpen(ByteBuffer.wrap(t), ByteBuffer.wrap(bytes("")), columnNames,
214            dummyAttributes);
215
216    while (true) {
217      List<TRowResult> entry = client.scannerGet(scanner);
218
219      if (entry.isEmpty()) {
220        break;
221      }
222
223      printRow(entry);
224    }
225
226    // Run some operations on a bunch of rows
227    for (int i = 100; i >= 0; --i) {
228      // format row keys as "00000" to "00100"
229      NumberFormat nf = NumberFormat.getInstance();
230      nf.setMinimumIntegerDigits(5);
231      nf.setGroupingUsed(false);
232      byte[] row = bytes(nf.format(i));
233
234      mutations = new ArrayList<>(1);
235      mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("unused:")),
236              ByteBuffer.wrap(bytes("DELETE_ME")), writeToWal));
237      client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), mutations, dummyAttributes);
238      printRow(client.getRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), dummyAttributes));
239      client.deleteAllRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), dummyAttributes);
240
241      // sleep to force later timestamp
242      try {
243        Thread.sleep(50);
244      } catch (InterruptedException e) {
245        // no-op
246      }
247
248      mutations = new ArrayList<>(2);
249      mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:num")),
250              ByteBuffer.wrap(bytes("0")), writeToWal));
251      mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:foo")),
252              ByteBuffer.wrap(bytes("FOO")), writeToWal));
253      client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), mutations, dummyAttributes);
254      printRow(client.getRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), dummyAttributes));
255
256      Mutation m;
257      mutations = new ArrayList<>(2);
258      m = new Mutation();
259      m.column = ByteBuffer.wrap(bytes("entry:foo"));
260      m.isDelete = true;
261      mutations.add(m);
262      m = new Mutation();
263      m.column = ByteBuffer.wrap(bytes("entry:num"));
264      m.value = ByteBuffer.wrap(bytes("-1"));
265      mutations.add(m);
266      client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), mutations, dummyAttributes);
267      printRow(client.getRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), dummyAttributes));
268
269      mutations = new ArrayList<>();
270      mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:num")),
271              ByteBuffer.wrap(bytes(Integer.toString(i))), writeToWal));
272      mutations.add(new Mutation(false, ByteBuffer.wrap(bytes("entry:sqr")),
273              ByteBuffer.wrap(bytes(Integer.toString(i * i))), writeToWal));
274      client.mutateRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), mutations, dummyAttributes);
275      printRow(client.getRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), dummyAttributes));
276
277      // sleep to force later timestamp
278      try {
279        Thread.sleep(50);
280      } catch (InterruptedException e) {
281        // no-op
282      }
283
284      mutations.clear();
285      m = new Mutation();
286      m.column = ByteBuffer.wrap(bytes("entry:num"));
287      m.value= ByteBuffer.wrap(bytes("-999"));
288      mutations.add(m);
289      m = new Mutation();
290      m.column = ByteBuffer.wrap(bytes("entry:sqr"));
291      m.isDelete = true;
292      client.mutateRowTs(ByteBuffer.wrap(t), ByteBuffer.wrap(row), mutations, 1,
293              dummyAttributes); // shouldn't override latest
294      printRow(client.getRow(ByteBuffer.wrap(t), ByteBuffer.wrap(row), dummyAttributes));
295
296      List<TCell> versions = client.getVer(ByteBuffer.wrap(t), ByteBuffer.wrap(row),
297              ByteBuffer.wrap(bytes("entry:num")), 10, dummyAttributes);
298      printVersions(ByteBuffer.wrap(row), versions);
299
300      if (versions.isEmpty()) {
301        System.out.println("FATAL: wrong # of versions");
302        System.exit(-1);
303      }
304
305      List<TCell> result = client.get(ByteBuffer.wrap(t), ByteBuffer.wrap(row),
306              ByteBuffer.wrap(bytes("entry:foo")), dummyAttributes);
307
308      if (!result.isEmpty()) {
309        System.out.println("FATAL: shouldn't get here");
310        System.exit(-1);
311      }
312
313      System.out.println("");
314    }
315
316    // scan all rows/columnNames
317    columnNames.clear();
318
319    for (ColumnDescriptor col2 : client.getColumnDescriptors(ByteBuffer.wrap(t)).values()) {
320      System.out.println("column with name: " + new String(col2.name.array()));
321      System.out.println(col2.toString());
322
323      columnNames.add(col2.name);
324    }
325
326    System.out.println("Starting scanner...");
327    scanner = client.scannerOpenWithStop(ByteBuffer.wrap(t), ByteBuffer.wrap(bytes("00020")),
328            ByteBuffer.wrap(bytes("00040")), columnNames, dummyAttributes);
329
330    while (true) {
331      List<TRowResult> entry = client.scannerGet(scanner);
332
333      if (entry.isEmpty()) {
334        System.out.println("Scanner finished");
335        break;
336      }
337
338      printRow(entry);
339    }
340
341    transport.close();
342  }
343
344  private void printVersions(ByteBuffer row, List<TCell> versions) {
345    StringBuilder rowStr = new StringBuilder();
346
347    for (TCell cell : versions) {
348      rowStr.append(ClientUtils.utf8(cell.value.array()));
349      rowStr.append("; ");
350    }
351
352    System.out.println("row: " + ClientUtils.utf8(row.array()) + ", values: " + rowStr);
353  }
354
355  private void printRow(TRowResult rowResult) {
356    ClientUtils.printRow(rowResult);
357  }
358
359  private void printRow(List<TRowResult> rows) {
360    for (TRowResult rowResult : rows) {
361      printRow(rowResult);
362    }
363  }
364
365  static Subject getSubject() throws Exception {
366    if (!secure) {
367      return new Subject();
368    }
369
370    LoginContext context = ClientUtils.getLoginContext();
371    context.login();
372    return context.getSubject();
373  }
374}