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}