Java tutorial
/* * Copyright 2017 PingCAP, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * See the License for the specific language governing permissions and * limitations under the License. */ package com.pingcap.tikv.catalog; import static com.google.common.base.Preconditions.checkArgument; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.protobuf.ByteString; import com.pingcap.tikv.Snapshot; import com.pingcap.tikv.codec.CodecDataInput; import com.pingcap.tikv.codec.CodecDataOutput; import com.pingcap.tikv.codec.KeyUtils; import com.pingcap.tikv.exception.TiClientInternalException; import com.pingcap.tikv.kvproto.Kvrpcpb; import com.pingcap.tikv.meta.TiDBInfo; import com.pingcap.tikv.meta.TiTableInfo; import com.pingcap.tikv.types.BytesType; import com.pingcap.tikv.types.IntegerType; import com.pingcap.tikv.util.Pair; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.apache.log4j.Logger; public class CatalogTransaction { protected static final Logger logger = Logger.getLogger(Catalog.class); private final Snapshot snapshot; private final byte[] prefix; private static final byte[] META_PREFIX = new byte[] { 'm' }; private static final byte HASH_DATA_FLAG = 'h'; private static final byte STR_DATA_FLAG = 's'; private static ByteString KEY_DB = ByteString.copyFromUtf8("DBs"); private static ByteString KEY_TABLE = ByteString.copyFromUtf8("Table"); private static ByteString KEY_SCHEMA_VERSION = ByteString.copyFromUtf8("SchemaVersionKey"); private static final String DB_PREFIX = "DB"; public CatalogTransaction(Snapshot snapshot) { this.snapshot = snapshot; this.prefix = META_PREFIX; } private void encodeStringDataKey(CodecDataOutput cdo, byte[] key) { cdo.write(prefix); BytesType.writeBytes(cdo, key); IntegerType.writeULong(cdo, STR_DATA_FLAG); } private void encodeHashDataKey(CodecDataOutput cdo, byte[] key, byte[] field) { encodeHashDataKeyPrefix(cdo, key); BytesType.writeBytes(cdo, field); } private void encodeHashDataKeyPrefix(CodecDataOutput cdo, byte[] key) { cdo.write(prefix); BytesType.writeBytes(cdo, key); IntegerType.writeULong(cdo, HASH_DATA_FLAG); } private Pair<ByteString, ByteString> decodeHashDataKey(ByteString rawKey) { checkArgument(KeyUtils.hasPrefix(rawKey, ByteString.copyFrom(prefix)), "invalid encoded hash data key prefix: " + new String(prefix)); CodecDataInput cdi = new CodecDataInput(rawKey.toByteArray()); cdi.skipBytes(prefix.length); byte[] key = BytesType.readBytes(cdi); long typeFlag = IntegerType.readULong(cdi); if (typeFlag != HASH_DATA_FLAG) { throw new TiClientInternalException("Invalid hash data flag: " + typeFlag); } byte[] field = BytesType.readBytes(cdi); return Pair.create(ByteString.copyFrom(key), ByteString.copyFrom(field)); } private ByteString hashGet(ByteString key, ByteString field) { CodecDataOutput cdo = new CodecDataOutput(); encodeHashDataKey(cdo, key.toByteArray(), field.toByteArray()); return snapshot.get(cdo.toByteString()); } private ByteString bytesGet(ByteString key) { CodecDataOutput cdo = new CodecDataOutput(); encodeStringDataKey(cdo, key.toByteArray()); return snapshot.get(cdo.toByteString()); } private List<Pair<ByteString, ByteString>> hashGetFields(ByteString key) { CodecDataOutput cdo = new CodecDataOutput(); encodeHashDataKeyPrefix(cdo, key.toByteArray()); ByteString encodedKey = cdo.toByteString(); Iterator<Kvrpcpb.KvPair> iterator = snapshot.scan(encodedKey); List<Pair<ByteString, ByteString>> fields = new ArrayList<>(); while (iterator.hasNext()) { Kvrpcpb.KvPair kv = iterator.next(); if (!KeyUtils.hasPrefix(kv.getKey(), encodedKey)) { break; } fields.add(Pair.create(decodeHashDataKey(kv.getKey()).second, kv.getValue())); } return fields; } private static ByteString encodeDatabaseID(long id) { return ByteString.copyFrom(String.format("%s:%d", DB_PREFIX, id).getBytes()); } public long getLatestSchemaVersion() { ByteString versionBytes = bytesGet(KEY_SCHEMA_VERSION); CodecDataInput cdi = new CodecDataInput(versionBytes.toByteArray()); return Long.parseLong(new String(cdi.toByteArray(), StandardCharsets.UTF_8)); } public List<TiDBInfo> getDatabases() { List<Pair<ByteString, ByteString>> fields = hashGetFields(KEY_DB); ImmutableList.Builder<TiDBInfo> builder = ImmutableList.builder(); for (Pair<ByteString, ByteString> pair : fields) { builder.add(parseFromJson(pair.second, TiDBInfo.class)); } return builder.build(); } public TiDBInfo getDatabase(long id) { ByteString dbKey = encodeDatabaseID(id); ByteString json = hashGet(KEY_DB, dbKey); if (json == null || json.isEmpty()) { return null; } return parseFromJson(json, TiDBInfo.class); } public List<TiTableInfo> getTables(long dbId) { ByteString dbKey = encodeDatabaseID(dbId); List<Pair<ByteString, ByteString>> fields = hashGetFields(dbKey); ImmutableList.Builder<TiTableInfo> builder = ImmutableList.builder(); for (Pair<ByteString, ByteString> pair : fields) { if (KeyUtils.hasPrefix(pair.first, KEY_TABLE)) { builder.add(parseFromJson(pair.second, TiTableInfo.class)); } } return builder.build(); } public static <T> T parseFromJson(ByteString json, Class<T> cls) { Objects.requireNonNull(json, "json is null"); Objects.requireNonNull(cls, "cls is null"); logger.debug(String.format("Parse Json %s : %s", cls.getSimpleName(), json.toStringUtf8())); ObjectMapper mapper = new ObjectMapper(); try { return mapper.readValue(json.toStringUtf8(), cls); } catch (JsonParseException | JsonMappingException e) { String errMsg = String.format("Invalid JSON value for Type %s: %s\n", cls.getSimpleName(), json.toStringUtf8()); throw new TiClientInternalException(errMsg, e); } catch (Exception e1) { throw new TiClientInternalException("Error parsing Json", e1); } } }