From 626bca6ece1e4d043f2508cdffc870d6647ac114 Mon Sep 17 00:00:00 2001
From: Michael Stack
Date: Tue, 5 Sep 2017 16:45:50 -0700
Subject: [PATCH 2/2] HBASE-16060 1.x clients cannot access table state talking
to 2.0 cluster
WIP. Adds back classes which can read/write ZK table state.
TODO: Add mirroring of state (and tests).
---
.../hadoop/hbase/zookeeper/ZooKeeperWatcher.java | 2 -
.../main/java/org/apache/hadoop/hbase/Cell.java | 1 +
.../hadoop/hbase/master/TableStateManager.java | 2 +-
.../master/hbase1/HBase1TableStateManager.java | 134 ++++++++
.../master/hbase1/HBase1ZKTableStateManager.java | 382 +++++++++++++++++++++
.../hadoop/hbase/master/hbase1/package-info.java | 26 ++
6 files changed, 544 insertions(+), 3 deletions(-)
create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1TableStateManager.java
create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1ZKTableStateManager.java
create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/package-info.java
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
index 6bec3527c0..61049ce37e 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
@@ -34,8 +34,6 @@ import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Abortable;
import org.apache.hadoop.hbase.AuthUtil;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.security.Superusers;
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/Cell.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/Cell.java
index 8a701f2c1d..3d7f001beb 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/Cell.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/Cell.java
@@ -57,6 +57,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience;
* consecutive bytes in the same byte[], whereas this interface allows fields to reside in separate
* byte[]'s.
*
+ * @see CellBuilder
*/
@InterfaceAudience.Public
public interface Cell {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableStateManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableStateManager.java
index fb83971cd0..a4e8e389e6 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableStateManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableStateManager.java
@@ -42,7 +42,7 @@ import org.apache.hadoop.hbase.client.TableState;
/**
* This is a helper class used to manage table states.
* States persisted in tableinfo and cached internally.
- * TODO: Cache state. Cut down on meta looksups.
+ * TODO: Cache state. Cut down on meta lookups.
*/
@InterfaceAudience.Private
public class TableStateManager {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1TableStateManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1TableStateManager.java
new file mode 100644
index 0000000000..6f34d3e81f
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1TableStateManager.java
@@ -0,0 +1,134 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.master.hbase1;
+
+import java.io.InterruptedIOException;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.CoordinatedStateException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
+
+/**
+ * Helper class for table state management for operations running inside
+ * RegionServer or HMaster. Taken from hbase branch-1. We do table state management
+ * differently in hbase-2.x. Classes in this package are to mirror table state
+ * transitions so hbase-1.x clients can continue to run against a hbase-2.x cluster.
+ *
+ * Depending on implementation, fetches information from HBase system table,
+ * local data store, ZooKeeper ensemble or somewhere else (only zk was implemented in hbase-1.x).
+ *
+ * Code running on client side (with no coordinated state context) should use hbase1
+ * org.apache.hadoop.hbase.zookeeper.ZKTableStateClientSideReader, not these classes, when
+ * reading.
+ *
+ * @deprecated In hbase-2.0.0. Remove in hbase-3.0.0
+ */
+@Deprecated
+@InterfaceAudience.Private
+public interface HBase1TableStateManager {
+
+ /**
+ * Sets the table into desired state. Fails silently if the table is already in this state.
+ * @param tableName table to process
+ * @param state new state of this table
+ * @throws CoordinatedStateException if error happened when trying to set table state
+ */
+ void setTableState(TableName tableName, ZooKeeperProtos.DeprecatedTableState.State state)
+ throws CoordinatedStateException;
+
+ /**
+ * Sets the specified table into the newState, but only if the table is already in
+ * one of the possibleCurrentStates (otherwise no operation is performed).
+ * @param tableName table to process
+ * @param newState new state for the table
+ * @param states table should be in one of these states for the operation
+ * to be performed
+ * @throws CoordinatedStateException if error happened while performing operation
+ * @return true if operation succeeded, false otherwise
+ */
+ boolean setTableStateIfInStates(TableName tableName,
+ ZooKeeperProtos.DeprecatedTableState.State newState,
+ ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws CoordinatedStateException;
+
+ /**
+ * Sets the specified table into the newState, but only if the table is NOT in
+ * one of the possibleCurrentStates (otherwise no operation is performed).
+ * @param tableName table to process
+ * @param newState new state for the table
+ * @param states table should NOT be in one of these states for the operation
+ * to be performed
+ * @throws CoordinatedStateException if error happened while performing operation
+ * @return true if operation succeeded, false otherwise
+ */
+ boolean setTableStateIfNotInStates(TableName tableName,
+ ZooKeeperProtos.DeprecatedTableState.State newState,
+ ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws CoordinatedStateException;
+
+ /**
+ * @return true if the table is in any one of the listed states, false otherwise.
+ */
+ boolean isTableState(TableName tableName, ZooKeeperProtos.DeprecatedTableState.State... states);
+
+ /**
+ * @return true if the table is in any one of the listed states, false otherwise.
+ */
+ boolean isTableState(TableName tableName, boolean checkSource,
+ ZooKeeperProtos.DeprecatedTableState.State... states);
+
+ /**
+ * Mark table as deleted. Fails silently if the table is not currently marked as disabled.
+ * @param tableName table to be deleted
+ * @throws CoordinatedStateException if error happened while performing operation
+ */
+ void setDeletedTable(TableName tableName) throws CoordinatedStateException;
+
+ /**
+ * Checks if table is present.
+ *
+ * @param tableName table we're checking
+ * @return true if the table is present, false otherwise
+ */
+ boolean isTablePresent(TableName tableName);
+
+ /**
+ * @return set of tables which are in any one of the listed states, empty Set if none
+ */
+ Set getTablesInStates(ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws InterruptedIOException, CoordinatedStateException;
+
+ /**
+ * If the table is found in the given state the in-memory state is removed. This
+ * helps in cases where CreateTable is to be retried by the client in case of
+ * failures. If deletePermanentState is true - the flag kept permanently is
+ * also reset.
+ *
+ * @param tableName table we're working on
+ * @param states if table isn't in any one of these states, operation aborts
+ * @param deletePermanentState if true, reset the permanent flag
+ * @throws CoordinatedStateException if error happened in underlying coordination engine
+ */
+ void checkAndRemoveTableState(TableName tableName,
+ ZooKeeperProtos.DeprecatedTableState.State states,
+ boolean deletePermanentState)
+ throws CoordinatedStateException;
+}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1ZKTableStateManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1ZKTableStateManager.java
new file mode 100644
index 0000000000..28edb17379
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/HBase1ZKTableStateManager.java
@@ -0,0 +1,382 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.master.hbase1;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.CoordinatedStateException;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.exceptions.DeserializationException;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
+
+/**
+ * Implementation of TableStateManager which reads, caches and sets state
+ * up in ZooKeeper. If multiple read/write clients, will make for confusion.
+ *
+ * To save on trips to the zookeeper ensemble, internally we cache table
+ * state.
+ *
+ *
Code running on client side (with no coordinated state context) should use hbase1
+ * org.apache.hadoop.hbase.zookeeper.ZKTableStateClientSideReader, not these classes, when
+ * reading.
+ *
+ * @deprecated In hbase-2.0.0. Remove in hbase-3.0.0
+ */
+@Deprecated
+@InterfaceAudience.Private
+public class HBase1ZKTableStateManager implements HBase1TableStateManager {
+ // A znode will exist under the table directory if it is in any of the
+ // following states: {@link TableState#ENABLING} , {@link TableState#DISABLING},
+ // or {@link TableState#DISABLED}. If {@link TableState#ENABLED}, there will
+ // be no entry for a table in zk. Thats how it currently works.
+
+ private static final Log LOG = LogFactory.getLog(HBase1ZKTableStateManager.class);
+ private final ZooKeeperWatcher watcher;
+
+ /**
+ * Cache of what we found in zookeeper so we don't have to go to zk ensemble
+ * for every query. Synchronize access rather than use concurrent Map because
+ * synchronization needs to span query of zk.
+ */
+ private final Map cache =
+ new HashMap();
+
+ public HBase1ZKTableStateManager(final ZooKeeperWatcher zkw) throws KeeperException,
+ InterruptedException {
+ super();
+ this.watcher = zkw;
+ populateTableStates();
+ }
+
+ /**
+ * Gets a list of all the tables set as disabled in zookeeper.
+ * @throws KeeperException, InterruptedException
+ */
+ private void populateTableStates() throws KeeperException, InterruptedException {
+ synchronized (this.cache) {
+ List children = ZKUtil.listChildrenNoWatch(this.watcher,
+ this.watcher.znodePaths.tableZNode);
+ if (children == null) return;
+ for (String child: children) {
+ TableName tableName = TableName.valueOf(child);
+ ZooKeeperProtos.DeprecatedTableState.State state = getTableState(this.watcher, tableName);
+ if (state != null) this.cache.put(tableName, state);
+ }
+ }
+ }
+
+ /**
+ * Sets table state in ZK. Sets no watches.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTableState(TableName tableName, ZooKeeperProtos.DeprecatedTableState.State state)
+ throws CoordinatedStateException {
+ synchronized (this.cache) {
+ LOG.info("Moving table " + tableName + " state from " + this.cache.get(tableName)
+ + " to " + state);
+ try {
+ setTableStateInZK(tableName, state);
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ }
+ }
+ }
+
+ /**
+ * Checks and sets table state in ZK. Sets no watches.
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean setTableStateIfInStates(TableName tableName,
+ ZooKeeperProtos.DeprecatedTableState.State newState,
+ ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws CoordinatedStateException {
+ synchronized (this.cache) {
+ // Transition ENABLED->DISABLING has to be performed with a hack, because
+ // we treat empty state as enabled in this case because 0.92- clusters.
+ if (
+ (newState == ZooKeeperProtos.DeprecatedTableState.State.DISABLING) &&
+ this.cache.get(tableName) != null && !isTableState(tableName, states) ||
+ (newState != ZooKeeperProtos.DeprecatedTableState.State.DISABLING &&
+ !isTableState(tableName, states) )) {
+ return false;
+ }
+ try {
+ setTableStateInZK(tableName, newState);
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Checks and sets table state in ZK. Sets no watches.
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean setTableStateIfNotInStates(TableName tableName,
+ ZooKeeperProtos.DeprecatedTableState.State newState,
+ ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws CoordinatedStateException {
+ synchronized (this.cache) {
+ if (isTableState(tableName, states)) {
+ // If the table is in the one of the states from the states list, the cache
+ // might be out-of-date, try to find it out from the master source (zookeeper server).
+ //
+ // Note: this adds extra zookeeper server calls and might have performance impact.
+ // However, this is not the happy path so we should not reach here often. Therefore,
+ // the performance impact should be minimal to none.
+ try {
+ ZooKeeperProtos.DeprecatedTableState.State curstate = getTableState(watcher, tableName);
+
+ if (isTableInState(Arrays.asList(states), curstate)) {
+ return false;
+ }
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ } catch (InterruptedException e) {
+ throw new CoordinatedStateException(e);
+ }
+ }
+ try {
+ setTableStateInZK(tableName, newState);
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ }
+ return true;
+ }
+ }
+
+ private void setTableStateInZK(final TableName tableName,
+ final ZooKeeperProtos.DeprecatedTableState.State state)
+ throws KeeperException {
+ String znode = ZKUtil.joinZNode(this.watcher.znodePaths.tableZNode,
+ tableName.getNameAsString());
+ if (ZKUtil.checkExists(this.watcher, znode) == -1) {
+ ZKUtil.createAndFailSilent(this.watcher, znode);
+ }
+ synchronized (this.cache) {
+ ZooKeeperProtos.DeprecatedTableState.Builder builder =
+ ZooKeeperProtos.DeprecatedTableState.newBuilder();
+ builder.setState(state);
+ byte [] data = ProtobufUtil.prependPBMagic(builder.build().toByteArray());
+ ZKUtil.setData(this.watcher, znode, data);
+ this.cache.put(tableName, state);
+ }
+ }
+
+ /**
+ * Checks if table is marked in specified state in ZK (using cache only). {@inheritDoc}
+ */
+ @Override
+ public boolean isTableState(final TableName tableName,
+ final ZooKeeperProtos.DeprecatedTableState.State... states) {
+ return isTableState(tableName, false, states); // only check cache
+ }
+
+ /**
+ * Checks if table is marked in specified state in ZK. {@inheritDoc}
+ */
+ @Override
+ public boolean isTableState(final TableName tableName, final boolean checkSource,
+ final ZooKeeperProtos.DeprecatedTableState.State... states) {
+ boolean isTableInSpecifiedState;
+ synchronized (this.cache) {
+ ZooKeeperProtos.DeprecatedTableState.State currentState = this.cache.get(tableName);
+ if (checkSource) {
+ // The cache might be out-of-date, try to find it out from the master source (zookeeper
+ // server) and update the cache.
+ try {
+ ZooKeeperProtos.DeprecatedTableState.State stateInZK = getTableState(watcher, tableName);
+
+ if (currentState != stateInZK) {
+ if (stateInZK != null) {
+ this.cache.put(tableName, stateInZK);
+ } else {
+ this.cache.remove(tableName);
+ }
+ currentState = stateInZK;
+ }
+ } catch (KeeperException | InterruptedException e) {
+ // Contacting zookeeper failed. Let us just trust the value in cache.
+ }
+ }
+ return isTableInState(Arrays.asList(states), currentState);
+ }
+ }
+
+ /**
+ * Deletes the table in zookeeper. Fails silently if the table is not currently disabled in
+ * zookeeper. Sets no watches. {@inheritDoc}
+ */
+ @Override
+ public void setDeletedTable(final TableName tableName)
+ throws CoordinatedStateException {
+ synchronized (this.cache) {
+ if (this.cache.remove(tableName) == null) {
+ LOG.warn("Moving table " + tableName + " state to deleted but was already deleted");
+ }
+ try {
+ ZKUtil.deleteNodeFailSilent(this.watcher,
+ ZKUtil.joinZNode(this.watcher.znodePaths.tableZNode, tableName.getNameAsString()));
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ }
+ }
+ }
+
+ /**
+ * check if table is present.
+ *
+ * @param tableName table we're working on
+ * @return true if the table is present
+ */
+ @Override
+ public boolean isTablePresent(final TableName tableName) {
+ synchronized (this.cache) {
+ ZooKeeperProtos.DeprecatedTableState.State state = this.cache.get(tableName);
+ return !(state == null);
+ }
+ }
+
+ /**
+ * Gets a list of all the tables set as disabling in zookeeper.
+ * @return Set of disabling tables, empty Set if none
+ * @throws CoordinatedStateException if error happened in underlying coordination engine
+ */
+ @Override
+ public Set getTablesInStates(ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws InterruptedIOException, CoordinatedStateException {
+ try {
+ return getAllTables(states);
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkAndRemoveTableState(TableName tableName,
+ ZooKeeperProtos.DeprecatedTableState.State states,
+ boolean deletePermanentState)
+ throws CoordinatedStateException {
+ synchronized (this.cache) {
+ if (isTableState(tableName, states)) {
+ this.cache.remove(tableName);
+ if (deletePermanentState) {
+ try {
+ ZKUtil.deleteNodeFailSilent(this.watcher,
+ ZKUtil.joinZNode(this.watcher.znodePaths.tableZNode,
+ tableName.getNameAsString()));
+ } catch (KeeperException e) {
+ throw new CoordinatedStateException(e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets a list of all the tables of specified states in zookeeper.
+ * @return Set of tables of specified states, empty Set if none
+ * @throws KeeperException
+ */
+ Set getAllTables(final ZooKeeperProtos.DeprecatedTableState.State... states)
+ throws KeeperException, InterruptedIOException {
+
+ Set allTables = new HashSet();
+ List children =
+ ZKUtil.listChildrenNoWatch(watcher, this.watcher.znodePaths.tableZNode);
+ if(children == null) return allTables;
+ for (String child: children) {
+ TableName tableName = TableName.valueOf(child);
+ ZooKeeperProtos.DeprecatedTableState.State state;
+ try {
+ state = getTableState(watcher, tableName);
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ for (ZooKeeperProtos.DeprecatedTableState.State expectedState: states) {
+ if (state == expectedState) {
+ allTables.add(tableName);
+ break;
+ }
+ }
+ }
+ return allTables;
+ }
+
+ /**
+ * Gets table state from ZK.
+ * @param zkw ZooKeeperWatcher instance to use
+ * @param tableName table we're checking
+ * @return Null or {@link ZooKeeperProtos.DeprecatedTableState.State} found in znode.
+ * @throws KeeperException
+ */
+ private ZooKeeperProtos.DeprecatedTableState.State getTableState(final ZooKeeperWatcher zkw,
+ final TableName tableName)
+ throws KeeperException, InterruptedException {
+ String znode = ZKUtil.joinZNode(this.watcher.znodePaths.tableZNode,
+ tableName.getNameAsString());
+ byte [] data = ZKUtil.getData(zkw, znode);
+ if (data == null || data.length <= 0) return null;
+ try {
+ ProtobufUtil.expectPBMagicPrefix(data);
+ ZooKeeperProtos.DeprecatedTableState.Builder builder =
+ ZooKeeperProtos.DeprecatedTableState.newBuilder();
+ int magicLen = ProtobufUtil.lengthOfPBMagic();
+ ProtobufUtil.mergeFrom(builder, data, magicLen, data.length - magicLen);
+ return builder.getState();
+ } catch (IOException e) {
+ KeeperException ke = new KeeperException.DataInconsistencyException();
+ ke.initCause(e);
+ throw ke;
+ } catch (DeserializationException e) {
+ throw ZKUtil.convert(e);
+ }
+ }
+
+ /**
+ * @return true if current state isn't null and is contained
+ * in the list of expected states.
+ */
+ private boolean isTableInState(final List expectedStates,
+ final ZooKeeperProtos.DeprecatedTableState.State currentState) {
+ return currentState != null && expectedStates.contains(currentState);
+ }
+}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/package-info.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/package-info.java
new file mode 100644
index 0000000000..8b4d360334
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/hbase1/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Provides classes that allow us echo state needed by branch-1 (hbase-1.x)
+ * clients. For most part, classes in this package come from branch-1.
+ * An example of something we do to make it so branch-1 clients can continue
+ * to do basic operations against an hbase-2.x cluster is our echoing of
+ * table state to zookeeper; table state is kept in a table in hbase-2.x but
+ * hbase-1.x clients look to zookeeper to read table state (ENABLED or DISABLED).
+ */
+package org.apache.hadoop.hbase.master.hbase1;
--
2.11.0 (Apple Git-81)