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