You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ec...@apache.org on 2013/02/25 23:50:29 UTC
svn commit: r1449950 [5/35] - in /hbase/trunk: ./ hbase-client/
hbase-client/src/ hbase-client/src/main/ hbase-client/src/main/java/
hbase-client/src/main/java/org/ hbase-client/src/main/java/org/apache/
hbase-client/src/main/java/org/apache/hadoop/ hb...
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/ServerName.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/ServerName.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/ServerName.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/ServerName.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,355 @@
+/**
+ *
+ * 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;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.exceptions.DeserializationException;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.RootRegionServer;
+import org.apache.hadoop.hbase.util.Addressing;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import java.util.Collection;
+import java.util.regex.Pattern;
+
+/**
+ * Instance of an HBase ServerName.
+ * A server name is used uniquely identifying a server instance and is made
+ * of the combination of hostname, port, and startcode. The startcode
+ * distingushes restarted servers on same hostname and port (startcode is
+ * usually timestamp of server startup). The {@link #toString()} format of
+ * ServerName is safe to use in the filesystem and as znode name up in
+ * ZooKeeper. Its format is:
+ * <code><hostname> '{@link #SERVERNAME_SEPARATOR}' <port> '{@link #SERVERNAME_SEPARATOR}' <startcode></code>.
+ * For example, if hostname is <code>example.org</code>, port is <code>1234</code>,
+ * and the startcode for the regionserver is <code>1212121212</code>, then
+ * the {@link #toString()} would be <code>example.org,1234,1212121212</code>.
+ *
+ * <p>You can obtain a versioned serialized form of this class by calling
+ * {@link #getVersionedBytes()}. To deserialize, call {@link #parseVersionedServerName(byte[])}
+ *
+ * <p>Immutable.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public class ServerName implements Comparable<ServerName> {
+ /**
+ * Version for this class.
+ * Its a short rather than a byte so I can for sure distinguish between this
+ * version of this class and the version previous to this which did not have
+ * a version.
+ */
+ private static final short VERSION = 0;
+ static final byte [] VERSION_BYTES = Bytes.toBytes(VERSION);
+
+ /**
+ * What to use if no startcode supplied.
+ */
+ public static final int NON_STARTCODE = -1;
+
+ /**
+ * This character is used as separator between server hostname, port and
+ * startcode.
+ */
+ public static final String SERVERNAME_SEPARATOR = ",";
+
+ public static final Pattern SERVERNAME_PATTERN =
+ Pattern.compile("[^" + SERVERNAME_SEPARATOR + "]+" +
+ SERVERNAME_SEPARATOR + Addressing.VALID_PORT_REGEX +
+ SERVERNAME_SEPARATOR + Addressing.VALID_PORT_REGEX + "$");
+
+ /**
+ * What to use if server name is unknown.
+ */
+ public static final String UNKNOWN_SERVERNAME = "#unknown#";
+
+ private final String servername;
+ private final String hostname;
+ private final int port;
+ private final long startcode;
+
+ /**
+ * Cached versioned bytes of this ServerName instance.
+ * @see #getVersionedBytes()
+ */
+ private byte [] bytes;
+
+ public ServerName(final String hostname, final int port, final long startcode) {
+ this.hostname = hostname;
+ this.port = port;
+ this.startcode = startcode;
+ this.servername = getServerName(hostname, port, startcode);
+ }
+
+ public ServerName(final String serverName) {
+ this(parseHostname(serverName), parsePort(serverName),
+ parseStartcode(serverName));
+ }
+
+ public ServerName(final String hostAndPort, final long startCode) {
+ this(Addressing.parseHostname(hostAndPort),
+ Addressing.parsePort(hostAndPort), startCode);
+ }
+
+ public static String parseHostname(final String serverName) {
+ if (serverName == null || serverName.length() <= 0) {
+ throw new IllegalArgumentException("Passed hostname is null or empty");
+ }
+ int index = serverName.indexOf(SERVERNAME_SEPARATOR);
+ return serverName.substring(0, index);
+ }
+
+ public static int parsePort(final String serverName) {
+ String [] split = serverName.split(SERVERNAME_SEPARATOR);
+ return Integer.parseInt(split[1]);
+ }
+
+ public static long parseStartcode(final String serverName) {
+ int index = serverName.lastIndexOf(SERVERNAME_SEPARATOR);
+ return Long.parseLong(serverName.substring(index + 1));
+ }
+
+ @Override
+ public String toString() {
+ return getServerName();
+ }
+
+ /**
+ * @return {@link #getServerName()} as bytes with a short-sized prefix with
+ * the ServerName#VERSION of this class.
+ */
+ public synchronized byte [] getVersionedBytes() {
+ if (this.bytes == null) {
+ this.bytes = Bytes.add(VERSION_BYTES, Bytes.toBytes(getServerName()));
+ }
+ return this.bytes;
+ }
+
+ public String getServerName() {
+ return servername;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public long getStartcode() {
+ return startcode;
+ }
+
+ /**
+ * @param hostName
+ * @param port
+ * @param startcode
+ * @return Server name made of the concatenation of hostname, port and
+ * startcode formatted as <code><hostname> ',' <port> ',' <startcode></code>
+ */
+ public static String getServerName(String hostName, int port, long startcode) {
+ final StringBuilder name = new StringBuilder(hostName.length() + 1 + 5 + 1 + 13);
+ name.append(hostName);
+ name.append(SERVERNAME_SEPARATOR);
+ name.append(port);
+ name.append(SERVERNAME_SEPARATOR);
+ name.append(startcode);
+ return name.toString();
+ }
+
+ /**
+ * @param hostAndPort String in form of <hostname> ':' <port>
+ * @param startcode
+ * @return Server name made of the concatenation of hostname, port and
+ * startcode formatted as <code><hostname> ',' <port> ',' <startcode></code>
+ */
+ public static String getServerName(final String hostAndPort,
+ final long startcode) {
+ int index = hostAndPort.indexOf(":");
+ if (index <= 0) throw new IllegalArgumentException("Expected <hostname> ':' <port>");
+ return getServerName(hostAndPort.substring(0, index),
+ Integer.parseInt(hostAndPort.substring(index + 1)), startcode);
+ }
+
+ /**
+ * @return Hostname and port formatted as described at
+ * {@link Addressing#createHostAndPortStr(String, int)}
+ */
+ public String getHostAndPort() {
+ return Addressing.createHostAndPortStr(this.hostname, this.port);
+ }
+
+ /**
+ * @param serverName ServerName in form specified by {@link #getServerName()}
+ * @return The server start code parsed from <code>servername</code>
+ */
+ public static long getServerStartcodeFromServerName(final String serverName) {
+ int index = serverName.lastIndexOf(SERVERNAME_SEPARATOR);
+ return Long.parseLong(serverName.substring(index + 1));
+ }
+
+ /**
+ * Utility method to excise the start code from a server name
+ * @param inServerName full server name
+ * @return server name less its start code
+ */
+ public static String getServerNameLessStartCode(String inServerName) {
+ if (inServerName != null && inServerName.length() > 0) {
+ int index = inServerName.lastIndexOf(SERVERNAME_SEPARATOR);
+ if (index > 0) {
+ return inServerName.substring(0, index);
+ }
+ }
+ return inServerName;
+ }
+
+ @Override
+ public int compareTo(ServerName other) {
+ int compare = this.getHostname().toLowerCase().
+ compareTo(other.getHostname().toLowerCase());
+ if (compare != 0) return compare;
+ compare = this.getPort() - other.getPort();
+ if (compare != 0) return compare;
+ return (int)(this.getStartcode() - other.getStartcode());
+ }
+
+ @Override
+ public int hashCode() {
+ return getServerName().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) return false;
+ if (!(o instanceof ServerName)) return false;
+ return this.compareTo((ServerName)o) == 0;
+ }
+
+
+ /**
+ * @return ServerName with matching hostname and port.
+ */
+ public static ServerName findServerWithSameHostnamePort(final Collection<ServerName> names,
+ final ServerName serverName) {
+ for (ServerName sn: names) {
+ if (isSameHostnameAndPort(serverName, sn)) return sn;
+ }
+ return null;
+ }
+
+ /**
+ * @param left
+ * @param right
+ * @return True if <code>other</code> has same hostname and port.
+ */
+ public static boolean isSameHostnameAndPort(final ServerName left,
+ final ServerName right) {
+ if (left == null) return false;
+ if (right == null) return false;
+ return left.getHostname().equals(right.getHostname()) &&
+ left.getPort() == right.getPort();
+ }
+
+ /**
+ * Use this method instantiating a {@link ServerName} from bytes
+ * gotten from a call to {@link #getVersionedBytes()}. Will take care of the
+ * case where bytes were written by an earlier version of hbase.
+ * @param versionedBytes Pass bytes gotten from a call to {@link #getVersionedBytes()}
+ * @return A ServerName instance.
+ * @see #getVersionedBytes()
+ */
+ public static ServerName parseVersionedServerName(final byte [] versionedBytes) {
+ // Version is a short.
+ short version = Bytes.toShort(versionedBytes);
+ if (version == VERSION) {
+ int length = versionedBytes.length - Bytes.SIZEOF_SHORT;
+ return new ServerName(Bytes.toString(versionedBytes, Bytes.SIZEOF_SHORT, length));
+ }
+ // Presume the bytes were written with an old version of hbase and that the
+ // bytes are actually a String of the form "'<hostname>' ':' '<port>'".
+ return new ServerName(Bytes.toString(versionedBytes), NON_STARTCODE);
+ }
+
+ /**
+ * @param str Either an instance of {@link ServerName#toString()} or a
+ * "'<hostname>' ':' '<port>'".
+ * @return A ServerName instance.
+ */
+ public static ServerName parseServerName(final String str) {
+ return SERVERNAME_PATTERN.matcher(str).matches()? new ServerName(str):
+ new ServerName(str, NON_STARTCODE);
+ }
+
+
+ /**
+ * @return true if the String follows the pattern of {@link ServerName#toString()}, false
+ * otherwise.
+ */
+ public static boolean isFullServerName(final String str){
+ if (str == null ||str.isEmpty()) return false;
+ return SERVERNAME_PATTERN.matcher(str).matches();
+ }
+
+ /**
+ * Get a ServerName from the passed in data bytes.
+ * @param data Data with a serialize server name in it; can handle the old style
+ * servername where servername was host and port. Works too with data that
+ * begins w/ the pb 'PBUF' magic and that is then followed by a protobuf that
+ * has a serialized {@link ServerName} in it.
+ * @return Returns null if <code>data</code> is null else converts passed data
+ * to a ServerName instance.
+ * @throws DeserializationException
+ */
+ public static ServerName parseFrom(final byte [] data) throws DeserializationException {
+ if (data == null || data.length <= 0) return null;
+ if (ProtobufUtil.isPBMagicPrefix(data)) {
+ int prefixLen = ProtobufUtil.lengthOfPBMagic();
+ try {
+ RootRegionServer rss =
+ RootRegionServer.newBuilder().mergeFrom(data, prefixLen, data.length - prefixLen).build();
+ org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName sn = rss.getServer();
+ return new ServerName(sn.getHostName(), sn.getPort(), sn.getStartCode());
+ } catch (InvalidProtocolBufferException e) {
+ // A failed parse of the znode is pretty catastrophic. Rather than loop
+ // retrying hoping the bad bytes will changes, and rather than change
+ // the signature on this method to add an IOE which will send ripples all
+ // over the code base, throw a RuntimeException. This should "never" happen.
+ // Fail fast if it does.
+ throw new DeserializationException(e);
+ }
+ }
+ // The str returned could be old style -- pre hbase-1502 -- which was
+ // hostname and port seperated by a colon rather than hostname, port and
+ // startcode delimited by a ','.
+ String str = Bytes.toString(data);
+ int index = str.indexOf(ServerName.SERVERNAME_SEPARATOR);
+ if (index != -1) {
+ // Presume its ServerName serialized with versioned bytes.
+ return ServerName.parseVersionedServerName(data);
+ }
+ // Presume it a hostname:port format.
+ String hostname = Addressing.parseHostname(str);
+ int port = Addressing.parsePort(str);
+ return new ServerName(hostname, port, -1L);
+ }
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,38 @@
+/**
+ *
+ * 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;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+
+/**
+ * Implementers are Stoppable.
+ */
+@InterfaceAudience.Private
+public interface Stoppable {
+ /**
+ * Stop this service.
+ * @param why Why we're stopping.
+ */
+ public void stop(String why);
+
+ /**
+ * @return True if {@link #stop(String)} has been closed.
+ */
+ public boolean isStopped();
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,640 @@
+/**
+ * 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.catalog;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.client.AdminProtocol;
+import org.apache.hadoop.hbase.client.HConnection;
+import org.apache.hadoop.hbase.client.HConnectionManager;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.RetriesExhaustedException;
+import org.apache.hadoop.hbase.exceptions.NotAllMetaRegionsOnlineException;
+import org.apache.hadoop.hbase.exceptions.ServerNotRunningYetException;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.zookeeper.MetaNodeTracker;
+import org.apache.hadoop.hbase.zookeeper.RootRegionTracker;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.hadoop.ipc.RemoteException;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.NoRouteToHostException;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Tracks the availability of the catalog tables <code>-ROOT-</code> and
+ * <code>.META.</code>.
+ *
+ * This class is "read-only" in that the locations of the catalog tables cannot
+ * be explicitly set. Instead, ZooKeeper is used to learn of the availability
+ * and location of <code>-ROOT-</code>. <code>-ROOT-</code> is used to learn of
+ * the location of <code>.META.</code> If not available in <code>-ROOT-</code>,
+ * ZooKeeper is used to monitor for a new location of <code>.META.</code>.
+ *
+ * <p>Call {@link #start()} to start up operation. Call {@link #stop()}} to
+ * interrupt waits and close up shop.
+ */
+@InterfaceAudience.Private
+public class CatalogTracker {
+ // TODO: This class needs a rethink. The original intent was that it would be
+ // the one-stop-shop for root and meta locations and that it would get this
+ // info from reading and watching zk state. The class was to be used by
+ // servers when they needed to know of root and meta movement but also by
+ // client-side (inside in HTable) so rather than figure root and meta
+ // locations on fault, the client would instead get notifications out of zk.
+ //
+ // But this original intent is frustrated by the fact that this class has to
+ // read an hbase table, the -ROOT- table, to figure out the .META. region
+ // location which means we depend on an HConnection. HConnection will do
+ // retrying but also, it has its own mechanism for finding root and meta
+ // locations (and for 'verifying'; it tries the location and if it fails, does
+ // new lookup, etc.). So, at least for now, HConnection (or HTable) can't
+ // have a CT since CT needs a HConnection (Even then, do want HT to have a CT?
+ // For HT keep up a session with ZK? Rather, shouldn't we do like asynchbase
+ // where we'd open a connection to zk, read what we need then let the
+ // connection go?). The 'fix' is make it so both root and meta addresses
+ // are wholey up in zk -- not in zk (root) -- and in an hbase table (meta).
+ //
+ // But even then, this class does 'verification' of the location and it does
+ // this by making a call over an HConnection (which will do its own root
+ // and meta lookups). Isn't this verification 'useless' since when we
+ // return, whatever is dependent on the result of this call then needs to
+ // use HConnection; what we have verified may change in meantime (HConnection
+ // uses the CT primitives, the root and meta trackers finding root locations).
+ //
+ // When meta is moved to zk, this class may make more sense. In the
+ // meantime, it does not cohere. It should just watch meta and root and not
+ // NOT do verification -- let that be out in HConnection since its going to
+ // be done there ultimately anyways.
+ //
+ // This class has spread throughout the codebase. It needs to be reigned in.
+ // This class should be used server-side only, even if we move meta location
+ // up into zk. Currently its used over in the client package. Its used in
+ // MetaReader and MetaEditor classes usually just to get the Configuration
+ // its using (It does this indirectly by asking its HConnection for its
+ // Configuration and even then this is just used to get an HConnection out on
+ // the other end). I made https://issues.apache.org/jira/browse/HBASE-4495 for
+ // doing CT fixup. St.Ack 09/30/2011.
+ //
+
+ // TODO: Timeouts have never been as advertised in here and its worse now
+ // with retries; i.e. the HConnection retries and pause goes ahead whatever
+ // the passed timeout is. Fix.
+ private static final Log LOG = LogFactory.getLog(CatalogTracker.class);
+ private final HConnection connection;
+ private final ZooKeeperWatcher zookeeper;
+ private final RootRegionTracker rootRegionTracker;
+ private final MetaNodeTracker metaNodeTracker;
+ private final AtomicBoolean metaAvailable = new AtomicBoolean(false);
+ private boolean instantiatedzkw = false;
+ private Abortable abortable;
+
+ /*
+ * Do not clear this address once set. Its needed when we do
+ * server shutdown processing -- we need to know who had .META. last. If you
+ * want to know if the address is good, rely on {@link #metaAvailable} value.
+ */
+ private ServerName metaLocation;
+
+ private boolean stopped = false;
+
+ static final byte [] ROOT_REGION_NAME =
+ HRegionInfo.ROOT_REGIONINFO.getRegionName();
+ static final byte [] META_REGION_NAME =
+ HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
+
+ /**
+ * Constructs a catalog tracker. Find current state of catalog tables.
+ * Begin active tracking by executing {@link #start()} post construction. Does
+ * not timeout.
+ *
+ * @param conf
+ * the {@link Configuration} from which a {@link HConnection} will be
+ * obtained; if problem, this connections
+ * {@link HConnection#abort(String, Throwable)} will be called.
+ * @throws IOException
+ */
+ public CatalogTracker(final Configuration conf) throws IOException {
+ this(null, conf, null);
+ }
+
+ /**
+ * Constructs the catalog tracker. Find current state of catalog tables.
+ * Begin active tracking by executing {@link #start()} post construction.
+ * Does not timeout.
+ * @param zk If zk is null, we'll create an instance (and shut it down
+ * when {@link #stop()} is called) else we'll use what is passed.
+ * @param conf
+ * @param abortable If fatal exception we'll call abort on this. May be null.
+ * If it is we'll use the Connection associated with the passed
+ * {@link Configuration} as our Abortable.
+ * @throws IOException
+ */
+ public CatalogTracker(final ZooKeeperWatcher zk, final Configuration conf,
+ Abortable abortable)
+ throws IOException {
+ this(zk, conf, HConnectionManager.getConnection(conf), abortable);
+ }
+
+ public CatalogTracker(final ZooKeeperWatcher zk, final Configuration conf,
+ HConnection connection, Abortable abortable)
+ throws IOException {
+ this.connection = connection;
+ if (abortable == null) {
+ // A connection is abortable.
+ this.abortable = this.connection;
+ }
+ Abortable throwableAborter = new Abortable() {
+
+ @Override
+ public void abort(String why, Throwable e) {
+ throw new RuntimeException(why, e);
+ }
+
+ @Override
+ public boolean isAborted() {
+ return true;
+ }
+
+ };
+ if (zk == null) {
+ // Create our own. Set flag so we tear it down on stop.
+ this.zookeeper =
+ new ZooKeeperWatcher(conf, "catalogtracker-on-" + connection.toString(),
+ abortable);
+ instantiatedzkw = true;
+ } else {
+ this.zookeeper = zk;
+ }
+ this.rootRegionTracker = new RootRegionTracker(zookeeper, throwableAborter);
+ final CatalogTracker ct = this;
+ // Override nodeDeleted so we get notified when meta node deleted
+ this.metaNodeTracker = new MetaNodeTracker(zookeeper, throwableAborter) {
+ public void nodeDeleted(String path) {
+ if (!path.equals(node)) return;
+ ct.resetMetaLocation();
+ }
+ };
+ }
+
+ /**
+ * Starts the catalog tracker.
+ * Determines current availability of catalog tables and ensures all further
+ * transitions of either region are tracked.
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void start() throws IOException, InterruptedException {
+ LOG.debug("Starting catalog tracker " + this);
+ try {
+ this.rootRegionTracker.start();
+ this.metaNodeTracker.start();
+ } catch (RuntimeException e) {
+ Throwable t = e.getCause();
+ this.abortable.abort(e.getMessage(), t);
+ throw new IOException("Attempt to start root/meta tracker failed.", t);
+ }
+ }
+
+ /**
+ * Stop working.
+ * Interrupts any ongoing waits.
+ */
+ public void stop() {
+ if (!this.stopped) {
+ LOG.debug("Stopping catalog tracker " + this);
+ this.stopped = true;
+ this.rootRegionTracker.stop();
+ this.metaNodeTracker.stop();
+ try {
+ if (this.connection != null) {
+ this.connection.close();
+ }
+ } catch (IOException e) {
+ // Although the {@link Closeable} interface throws an {@link
+ // IOException}, in reality, the implementation would never do that.
+ LOG.error("Attempt to close catalog tracker's connection failed.", e);
+ }
+ if (this.instantiatedzkw) {
+ this.zookeeper.close();
+ }
+ // Call this and it will interrupt any ongoing waits on meta.
+ synchronized (this.metaAvailable) {
+ this.metaAvailable.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Gets the current location for <code>-ROOT-</code> or null if location is
+ * not currently available.
+ * @return {@link ServerName} for server hosting <code>-ROOT-</code> or null
+ * if none available
+ * @throws InterruptedException
+ */
+ public ServerName getRootLocation() throws InterruptedException {
+ return this.rootRegionTracker.getRootRegionLocation();
+ }
+
+ /**
+ * @return {@link ServerName} for server hosting <code>.META.</code> or null
+ * if none available
+ */
+ public ServerName getMetaLocation() {
+ return this.metaLocation;
+ }
+
+ /**
+ * Method used by master on startup trying to figure state of cluster.
+ * Returns the current meta location unless its null. In this latter case,
+ * it has not yet been set so go check whats up in <code>-ROOT-</code> and
+ * return that.
+ * @return {@link ServerName} for server hosting <code>.META.</code> or if null,
+ * we'll read the location that is up in <code>-ROOT-</code> table (which
+ * could be null or just plain stale).
+ * @throws IOException
+ */
+ public ServerName getMetaLocationOrReadLocationFromRoot() throws IOException {
+ ServerName sn = getMetaLocation();
+ return sn != null? sn: MetaReader.getMetaRegionLocation(this);
+ }
+
+ /**
+ * Gets the current location for <code>-ROOT-</code> if available and waits
+ * for up to the specified timeout if not immediately available. Returns null
+ * if the timeout elapses before root is available.
+ * @param timeout maximum time to wait for root availability, in milliseconds
+ * @return {@link ServerName} for server hosting <code>-ROOT-</code> or null
+ * if none available
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NotAllMetaRegionsOnlineException if root not available before
+ * timeout
+ */
+ public ServerName waitForRoot(final long timeout)
+ throws InterruptedException, NotAllMetaRegionsOnlineException {
+ ServerName sn = rootRegionTracker.waitRootRegionLocation(timeout);
+ if (sn == null) {
+ throw new NotAllMetaRegionsOnlineException("Timed out; " + timeout + "ms");
+ }
+ return sn;
+ }
+
+ /**
+ * Gets a connection to the server hosting root, as reported by ZooKeeper,
+ * waiting up to the specified timeout for availability.
+ * @param timeout How long to wait on root location
+ * @see #waitForRoot(long) for additional information
+ * @return connection to server hosting root
+ * @throws InterruptedException
+ * @throws NotAllMetaRegionsOnlineException if timed out waiting
+ * @throws IOException
+ * @deprecated Use #getRootServerConnection(long)
+ */
+ public AdminProtocol waitForRootServerConnection(long timeout)
+ throws InterruptedException, NotAllMetaRegionsOnlineException, IOException {
+ return getRootServerConnection(timeout);
+ }
+
+ /**
+ * Gets a connection to the server hosting root, as reported by ZooKeeper,
+ * waiting up to the specified timeout for availability.
+ * <p>WARNING: Does not retry. Use an {@link HTable} instead.
+ * @param timeout How long to wait on root location
+ * @see #waitForRoot(long) for additional information
+ * @return connection to server hosting root
+ * @throws InterruptedException
+ * @throws NotAllMetaRegionsOnlineException if timed out waiting
+ * @throws IOException
+ */
+ AdminProtocol getRootServerConnection(long timeout)
+ throws InterruptedException, NotAllMetaRegionsOnlineException, IOException {
+ return getCachedConnection(waitForRoot(timeout));
+ }
+
+ /**
+ * Gets a connection to the server currently hosting <code>.META.</code> or
+ * null if location is not currently available.
+ * <p>
+ * If a location is known, a connection to the cached location is returned.
+ * If refresh is true, the cached connection is verified first before
+ * returning. If the connection is not valid, it is reset and rechecked.
+ * <p>
+ * If no location for meta is currently known, method checks ROOT for a new
+ * location, verifies META is currently there, and returns a cached connection
+ * to the server hosting META.
+ *
+ * @return connection to server hosting meta, null if location not available
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ private AdminProtocol getMetaServerConnection()
+ throws IOException, InterruptedException {
+ synchronized (metaAvailable) {
+ if (metaAvailable.get()) {
+ AdminProtocol current = getCachedConnection(this.metaLocation);
+ // If we are to refresh, verify we have a good connection by making
+ // an invocation on it.
+ if (verifyRegionLocation(current, this.metaLocation, META_REGION_NAME)) {
+ return current;
+ }
+ resetMetaLocation();
+ }
+ // We got here because there is no meta available or because whats
+ // available is bad.
+
+ // Now read the current .META. content from -ROOT-. Note: This goes via
+ // an HConnection. It has its own way of figuring root and meta locations
+ // which we have to wait on.
+ ServerName newLocation = MetaReader.getMetaRegionLocation(this);
+ if (newLocation == null) return null;
+
+ AdminProtocol newConnection = getCachedConnection(newLocation);
+ if (verifyRegionLocation(newConnection, newLocation, META_REGION_NAME)) {
+ setMetaLocation(newLocation);
+ return newConnection;
+ } else {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("New .META. server: " + newLocation + " isn't valid." +
+ " Cached .META. server: " + this.metaLocation);
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Waits indefinitely for availability of <code>.META.</code>. Used during
+ * cluster startup. Does not verify meta, just that something has been
+ * set up in zk.
+ * @see #waitForMeta(long)
+ * @throws InterruptedException if interrupted while waiting
+ */
+ public void waitForMeta() throws InterruptedException {
+ while (!this.stopped) {
+ try {
+ if (waitForMeta(100) != null) break;
+ } catch (NotAllMetaRegionsOnlineException e) {
+ if (LOG.isTraceEnabled()) {
+ LOG.info(".META. still not available, sleeping and retrying." +
+ " Reason: " + e.getMessage());
+ }
+ } catch (IOException e) {
+ LOG.info("Retrying", e);
+ }
+ }
+ }
+
+ /**
+ * Gets the current location for <code>.META.</code> if available and waits
+ * for up to the specified timeout if not immediately available. Throws an
+ * exception if timed out waiting. This method differs from {@link #waitForMeta()}
+ * in that it will go ahead and verify the location gotten from ZooKeeper and
+ * -ROOT- region by trying to use returned connection.
+ * @param timeout maximum time to wait for meta availability, in milliseconds
+ * @return {@link ServerName} for server hosting <code>.META.</code> or null
+ * if none available
+ * @throws InterruptedException if interrupted while waiting
+ * @throws IOException unexpected exception connecting to meta server
+ * @throws NotAllMetaRegionsOnlineException if meta not available before
+ * timeout
+ */
+ public ServerName waitForMeta(long timeout)
+ throws InterruptedException, IOException, NotAllMetaRegionsOnlineException {
+ long stop = timeout == 0 ? Long.MAX_VALUE : System.currentTimeMillis() + timeout;
+ long waitTime = Math.min(50, timeout);
+ synchronized (metaAvailable) {
+ while(!stopped && System.currentTimeMillis() < stop) {
+ if (getMetaServerConnection() != null) {
+ return metaLocation;
+ }
+ // perhaps -ROOT- region isn't available, let us wait a bit and retry.
+ metaAvailable.wait(waitTime);
+ }
+ if (getMetaServerConnection() == null) {
+ throw new NotAllMetaRegionsOnlineException("Timed out (" + timeout + "ms)");
+ }
+ return metaLocation;
+ }
+ }
+
+ /**
+ * Gets a connection to the server hosting meta, as reported by ZooKeeper,
+ * waiting up to the specified timeout for availability.
+ * @see #waitForMeta(long) for additional information
+ * @return connection to server hosting meta
+ * @throws InterruptedException
+ * @throws NotAllMetaRegionsOnlineException if timed out waiting
+ * @throws IOException
+ * @deprecated Does not retry; use an HTable instance instead.
+ */
+ public AdminProtocol waitForMetaServerConnection(long timeout)
+ throws InterruptedException, NotAllMetaRegionsOnlineException, IOException {
+ return getCachedConnection(waitForMeta(timeout));
+ }
+
+ /**
+ * Called when we figure current meta is off (called from zk callback).
+ */
+ public void resetMetaLocation() {
+ LOG.debug("Current cached META location, " + metaLocation +
+ ", is not valid, resetting");
+ synchronized(this.metaAvailable) {
+ this.metaAvailable.set(false);
+ this.metaAvailable.notifyAll();
+ }
+ }
+
+ /**
+ * @param metaLocation
+ */
+ void setMetaLocation(final ServerName metaLocation) {
+ LOG.debug("Set new cached META location: " + metaLocation);
+ synchronized (this.metaAvailable) {
+ this.metaLocation = metaLocation;
+ this.metaAvailable.set(true);
+ // no synchronization because these are private and already under lock
+ this.metaAvailable.notifyAll();
+ }
+ }
+
+ /**
+ * @param sn ServerName to get a connection against.
+ * @return The AdminProtocol we got when we connected to <code>sn</code>
+ * May have come from cache, may not be good, may have been setup by this
+ * invocation, or may be null.
+ * @throws IOException
+ */
+ private AdminProtocol getCachedConnection(ServerName sn)
+ throws IOException {
+ if (sn == null) {
+ return null;
+ }
+ AdminProtocol protocol = null;
+ try {
+ protocol = connection.getAdmin(sn);
+ } catch (RetriesExhaustedException e) {
+ if (e.getCause() != null && e.getCause() instanceof ConnectException) {
+ // Catch this; presume it means the cached connection has gone bad.
+ } else {
+ throw e;
+ }
+ } catch (SocketTimeoutException e) {
+ LOG.debug("Timed out connecting to " + sn);
+ } catch (NoRouteToHostException e) {
+ LOG.debug("Connecting to " + sn, e);
+ } catch (SocketException e) {
+ LOG.debug("Exception connecting to " + sn);
+ } catch (UnknownHostException e) {
+ LOG.debug("Unknown host exception connecting to " + sn);
+ } catch (IOException ioe) {
+ Throwable cause = ioe.getCause();
+ if (ioe instanceof ConnectException) {
+ // Catch. Connect refused.
+ } else if (cause != null && cause instanceof EOFException) {
+ // Catch. Other end disconnected us.
+ } else if (cause != null && cause.getMessage() != null &&
+ cause.getMessage().toLowerCase().contains("connection reset")) {
+ // Catch. Connection reset.
+ } else {
+ throw ioe;
+ }
+
+ }
+ return protocol;
+ }
+
+ /**
+ * Verify we can connect to <code>hostingServer</code> and that its carrying
+ * <code>regionName</code>.
+ * @param hostingServer Interface to the server hosting <code>regionName</code>
+ * @param serverName The servername that goes with the <code>metaServer</code>
+ * Interface. Used logging.
+ * @param regionName The regionname we are interested in.
+ * @return True if we were able to verify the region located at other side of
+ * the Interface.
+ * @throws IOException
+ */
+ // TODO: We should be able to get the ServerName from the AdminProtocol
+ // rather than have to pass it in. Its made awkward by the fact that the
+ // HRI is likely a proxy against remote server so the getServerName needs
+ // to be fixed to go to a local method or to a cache before we can do this.
+ private boolean verifyRegionLocation(AdminProtocol hostingServer,
+ final ServerName address, final byte [] regionName)
+ throws IOException {
+ if (hostingServer == null) {
+ LOG.info("Passed hostingServer is null");
+ return false;
+ }
+ Throwable t = null;
+ try {
+ // Try and get regioninfo from the hosting server.
+ return ProtobufUtil.getRegionInfo(hostingServer, regionName) != null;
+ } catch (ConnectException e) {
+ t = e;
+ } catch (RetriesExhaustedException e) {
+ t = e;
+ } catch (RemoteException e) {
+ IOException ioe = e.unwrapRemoteException();
+ t = ioe;
+ } catch (IOException e) {
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof EOFException) {
+ t = cause;
+ } else if (cause != null && cause.getMessage() != null
+ && cause.getMessage().contains("Connection reset")) {
+ t = cause;
+ } else {
+ t = e;
+ }
+ }
+ LOG.info("Failed verification of " + Bytes.toStringBinary(regionName) +
+ " at address=" + address + "; " + t);
+ return false;
+ }
+
+ /**
+ * Verify <code>-ROOT-</code> is deployed and accessible.
+ * @param timeout How long to wait on zk for root address (passed through to
+ * the internal call to {@link #waitForRootServerConnection(long)}.
+ * @return True if the <code>-ROOT-</code> location is healthy.
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public boolean verifyRootRegionLocation(final long timeout)
+ throws InterruptedException, IOException {
+ AdminProtocol connection = null;
+ try {
+ connection = waitForRootServerConnection(timeout);
+ } catch (NotAllMetaRegionsOnlineException e) {
+ // Pass
+ } catch (ServerNotRunningYetException e) {
+ // Pass -- remote server is not up so can't be carrying root
+ } catch (UnknownHostException e) {
+ // Pass -- server name doesn't resolve so it can't be assigned anything.
+ }
+ return (connection == null)? false:
+ verifyRegionLocation(connection,
+ this.rootRegionTracker.getRootRegionLocation(), ROOT_REGION_NAME);
+ }
+
+ /**
+ * Verify <code>.META.</code> is deployed and accessible.
+ * @param timeout How long to wait on zk for <code>.META.</code> address
+ * (passed through to the internal call to {@link #waitForMetaServerConnection(long)}.
+ * @return True if the <code>.META.</code> location is healthy.
+ * @throws IOException Some unexpected IOE.
+ * @throws InterruptedException
+ */
+ public boolean verifyMetaRegionLocation(final long timeout)
+ throws InterruptedException, IOException {
+ AdminProtocol connection = null;
+ try {
+ connection = waitForMetaServerConnection(timeout);
+ } catch (NotAllMetaRegionsOnlineException e) {
+ // Pass
+ } catch (ServerNotRunningYetException e) {
+ // Pass -- remote server is not up so can't be carrying .META.
+ } catch (UnknownHostException e) {
+ // Pass -- server name doesn't resolve so it can't be assigned anything.
+ } catch (RetriesExhaustedException e) {
+ // Pass -- failed after bunch of retries.
+ LOG.debug("Failed verify meta region location after retries", e);
+ }
+ return connection != null;
+ }
+
+ // Used by tests.
+ MetaNodeTracker getMetaNodeTracker() {
+ return this.metaNodeTracker;
+ }
+
+ public HConnection getConnection() {
+ return this.connection;
+ }
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,642 @@
+/**
+ * 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.catalog;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Pair;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Reads region and assignment information from <code>.META.</code>.
+ */
+@InterfaceAudience.Private
+public class MetaReader {
+ // TODO: Strip CatalogTracker from this class. Its all over and in the end
+ // its only used to get its Configuration so we can get associated
+ // Connection.
+ private static final Log LOG = LogFactory.getLog(MetaReader.class);
+
+ static final byte [] META_REGION_PREFIX;
+ static {
+ // Copy the prefix from FIRST_META_REGIONINFO into META_REGION_PREFIX.
+ // FIRST_META_REGIONINFO == '.META.,,1'. META_REGION_PREFIX == '.META.,'
+ int len = HRegionInfo.FIRST_META_REGIONINFO.getRegionName().length - 2;
+ META_REGION_PREFIX = new byte [len];
+ System.arraycopy(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), 0,
+ META_REGION_PREFIX, 0, len);
+ }
+
+ /**
+ * @param row
+ * @return True if <code>row</code> is row of <code>-ROOT-</code> table.
+ */
+ private static boolean isRootTableRow(final byte [] row) {
+ if (row.length < META_REGION_PREFIX.length + 2 /* ',', + '1' */) {
+ // Can't be meta table region.
+ return false;
+ }
+ // Compare the prefix of row. If it matches META_REGION_PREFIX prefix,
+ // then this is row from -ROOT_ table.
+ return Bytes.equals(row, 0, META_REGION_PREFIX.length,
+ META_REGION_PREFIX, 0, META_REGION_PREFIX.length);
+ }
+
+ /**
+ * Performs a full scan of <code>.META.</code>, skipping regions from any
+ * tables in the specified set of disabled tables.
+ * @param catalogTracker
+ * @param disabledTables set of disabled tables that will not be returned
+ * @return Returns a map of every region to it's currently assigned server,
+ * according to META. If the region does not have an assignment it will have
+ * a null value in the map.
+ * @throws IOException
+ */
+ public static Map<HRegionInfo, ServerName> fullScan(
+ CatalogTracker catalogTracker, final Set<String> disabledTables)
+ throws IOException {
+ return fullScan(catalogTracker, disabledTables, false);
+ }
+
+ /**
+ * Performs a full scan of <code>.META.</code>, skipping regions from any
+ * tables in the specified set of disabled tables.
+ * @param catalogTracker
+ * @param disabledTables set of disabled tables that will not be returned
+ * @param excludeOfflinedSplitParents If true, do not include offlined split
+ * parents in the return.
+ * @return Returns a map of every region to it's currently assigned server,
+ * according to META. If the region does not have an assignment it will have
+ * a null value in the map.
+ * @throws IOException
+ */
+ public static Map<HRegionInfo, ServerName> fullScan(
+ CatalogTracker catalogTracker, final Set<String> disabledTables,
+ final boolean excludeOfflinedSplitParents)
+ throws IOException {
+ final Map<HRegionInfo, ServerName> regions =
+ new TreeMap<HRegionInfo, ServerName>();
+ Visitor v = new Visitor() {
+ @Override
+ public boolean visit(Result r) throws IOException {
+ if (r == null || r.isEmpty()) return true;
+ Pair<HRegionInfo, ServerName> region = HRegionInfo.getHRegionInfoAndServerName(r);
+ HRegionInfo hri = region.getFirst();
+ if (hri == null) return true;
+ if (hri.getTableNameAsString() == null) return true;
+ if (disabledTables.contains(
+ hri.getTableNameAsString())) return true;
+ // Are we to include split parents in the list?
+ if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
+ regions.put(hri, region.getSecond());
+ return true;
+ }
+ };
+ fullScan(catalogTracker, v);
+ return regions;
+ }
+
+ /**
+ * Performs a full scan of <code>.META.</code>.
+ * @return List of {@link Result}
+ * @throws IOException
+ */
+ public static List<Result> fullScan(CatalogTracker catalogTracker)
+ throws IOException {
+ CollectAllVisitor v = new CollectAllVisitor();
+ fullScan(catalogTracker, v, null);
+ return v.getResults();
+ }
+
+ /**
+ * Performs a full scan of a <code>-ROOT-</code> table.
+ * @return List of {@link Result}
+ * @throws IOException
+ */
+ public static List<Result> fullScanOfRoot(CatalogTracker catalogTracker)
+ throws IOException {
+ CollectAllVisitor v = new CollectAllVisitor();
+ fullScan(catalogTracker, v, null, true);
+ return v.getResults();
+ }
+
+ /**
+ * Performs a full scan of <code>.META.</code>.
+ * @param catalogTracker
+ * @param visitor Visitor invoked against each row.
+ * @throws IOException
+ */
+ public static void fullScan(CatalogTracker catalogTracker,
+ final Visitor visitor)
+ throws IOException {
+ fullScan(catalogTracker, visitor, null);
+ }
+
+ /**
+ * Performs a full scan of <code>.META.</code>.
+ * @param catalogTracker
+ * @param visitor Visitor invoked against each row.
+ * @param startrow Where to start the scan. Pass null if want to begin scan
+ * at first row (The visitor will stop the Scan when its done so no need to
+ * pass a stoprow).
+ * @throws IOException
+ */
+ public static void fullScan(CatalogTracker catalogTracker,
+ final Visitor visitor, final byte [] startrow)
+ throws IOException {
+ fullScan(catalogTracker, visitor, startrow, false);
+ }
+
+ /**
+ * Callers should call close on the returned {@link HTable} instance.
+ * @param catalogTracker We'll use this catalogtracker's connection
+ * @param tableName Table to get an {@link HTable} against.
+ * @return An {@link HTable} for <code>tableName</code>
+ * @throws IOException
+ */
+ private static HTable getHTable(final CatalogTracker catalogTracker,
+ final byte [] tableName)
+ throws IOException {
+ // Passing the CatalogTracker's connection configuration ensures this
+ // HTable instance uses the CatalogTracker's connection.
+ org.apache.hadoop.hbase.client.HConnection c = catalogTracker.getConnection();
+ if (c == null) throw new NullPointerException("No connection");
+ return new HTable(catalogTracker.getConnection().getConfiguration(), tableName);
+ }
+
+ /**
+ * Callers should call close on the returned {@link HTable} instance.
+ * @param catalogTracker
+ * @param row Row we are putting
+ * @return
+ * @throws IOException
+ */
+ static HTable getCatalogHTable(final CatalogTracker catalogTracker,
+ final byte [] row)
+ throws IOException {
+ return isRootTableRow(row)?
+ getRootHTable(catalogTracker):
+ getMetaHTable(catalogTracker);
+ }
+
+ /**
+ * Callers should call close on the returned {@link HTable} instance.
+ * @param ct
+ * @return An {@link HTable} for <code>.META.</code>
+ * @throws IOException
+ */
+ static HTable getMetaHTable(final CatalogTracker ct)
+ throws IOException {
+ return getHTable(ct, HConstants.META_TABLE_NAME);
+ }
+
+ /**
+ * Callers should call close on the returned {@link HTable} instance.
+ * @param ct
+ * @return An {@link HTable} for <code>-ROOT-</code>
+ * @throws IOException
+ */
+ static HTable getRootHTable(final CatalogTracker ct)
+ throws IOException {
+ return getHTable(ct, HConstants.ROOT_TABLE_NAME);
+ }
+
+ /**
+ * @param t Table to use (will be closed when done).
+ * @param g Get to run
+ * @throws IOException
+ */
+ private static Result get(final HTable t, final Get g) throws IOException {
+ try {
+ return t.get(g);
+ } finally {
+ t.close();
+ }
+ }
+
+ /**
+ * Gets the location of <code>.META.</code> region by reading content of
+ * <code>-ROOT-</code>.
+ * @param ct
+ * @return location of <code>.META.</code> region as a {@link ServerName} or
+ * null if not found
+ * @throws IOException
+ */
+ static ServerName getMetaRegionLocation(final CatalogTracker ct)
+ throws IOException {
+ return MetaReader.readRegionLocation(ct, CatalogTracker.META_REGION_NAME);
+ }
+
+ /**
+ * Reads the location of the specified region
+ * @param catalogTracker
+ * @param regionName region whose location we are after
+ * @return location of region as a {@link ServerName} or null if not found
+ * @throws IOException
+ */
+ static ServerName readRegionLocation(CatalogTracker catalogTracker,
+ byte [] regionName)
+ throws IOException {
+ Pair<HRegionInfo, ServerName> pair = getRegion(catalogTracker, regionName);
+ return (pair == null || pair.getSecond() == null)? null: pair.getSecond();
+ }
+
+ /**
+ * Gets the region info and assignment for the specified region.
+ * @param catalogTracker
+ * @param regionName Region to lookup.
+ * @return Location and HRegionInfo for <code>regionName</code>
+ * @throws IOException
+ */
+ public static Pair<HRegionInfo, ServerName> getRegion(
+ CatalogTracker catalogTracker, byte [] regionName)
+ throws IOException {
+ Get get = new Get(regionName);
+ get.addFamily(HConstants.CATALOG_FAMILY);
+ Result r = get(getCatalogHTable(catalogTracker, regionName), get);
+ return (r == null || r.isEmpty())? null: HRegionInfo.getHRegionInfoAndServerName(r);
+ }
+
+ /**
+ * Checks if the specified table exists. Looks at the META table hosted on
+ * the specified server.
+ * @param catalogTracker
+ * @param tableName table to check
+ * @return true if the table exists in meta, false if not
+ * @throws IOException
+ */
+ public static boolean tableExists(CatalogTracker catalogTracker,
+ String tableName)
+ throws IOException {
+ if (tableName.equals(HTableDescriptor.ROOT_TABLEDESC.getNameAsString()) ||
+ tableName.equals(HTableDescriptor.META_TABLEDESC.getNameAsString())) {
+ // Catalog tables always exist.
+ return true;
+ }
+ final byte [] tableNameBytes = Bytes.toBytes(tableName);
+ // Make a version of ResultCollectingVisitor that only collects the first
+ CollectingVisitor<HRegionInfo> visitor = new CollectingVisitor<HRegionInfo>() {
+ private HRegionInfo current = null;
+
+ @Override
+ public boolean visit(Result r) throws IOException {
+ this.current =
+ HRegionInfo.getHRegionInfo(r, HConstants.REGIONINFO_QUALIFIER);
+ if (this.current == null) {
+ LOG.warn("No serialized HRegionInfo in " + r);
+ return true;
+ }
+ if (!isInsideTable(this.current, tableNameBytes)) return false;
+ // Else call super and add this Result to the collection.
+ super.visit(r);
+ // Stop collecting regions from table after we get one.
+ return false;
+ }
+
+ @Override
+ void add(Result r) {
+ // Add the current HRI.
+ this.results.add(this.current);
+ }
+ };
+ fullScan(catalogTracker, visitor, getTableStartRowForMeta(tableNameBytes));
+ // If visitor has results >= 1 then table exists.
+ return visitor.getResults().size() >= 1;
+ }
+
+ /**
+ * Gets all of the regions of the specified table.
+ * @param catalogTracker
+ * @param tableName
+ * @return Ordered list of {@link HRegionInfo}.
+ * @throws IOException
+ */
+ public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
+ byte [] tableName)
+ throws IOException {
+ return getTableRegions(catalogTracker, tableName, false);
+ }
+
+ /**
+ * Gets all of the regions of the specified table.
+ * @param catalogTracker
+ * @param tableName
+ * @param excludeOfflinedSplitParents If true, do not include offlined split
+ * parents in the return.
+ * @return Ordered list of {@link HRegionInfo}.
+ * @throws IOException
+ */
+ public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
+ byte [] tableName, final boolean excludeOfflinedSplitParents)
+ throws IOException {
+ List<Pair<HRegionInfo, ServerName>> result = null;
+ try {
+ result = getTableRegionsAndLocations(catalogTracker, tableName,
+ excludeOfflinedSplitParents);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return getListOfHRegionInfos(result);
+ }
+
+ static List<HRegionInfo> getListOfHRegionInfos(final List<Pair<HRegionInfo, ServerName>> pairs) {
+ if (pairs == null || pairs.isEmpty()) return null;
+ List<HRegionInfo> result = new ArrayList<HRegionInfo>(pairs.size());
+ for (Pair<HRegionInfo, ServerName> pair: pairs) {
+ result.add(pair.getFirst());
+ }
+ return result;
+ }
+
+ /**
+ * @param current
+ * @param tableName
+ * @return True if <code>current</code> tablename is equal to
+ * <code>tableName</code>
+ */
+ static boolean isInsideTable(final HRegionInfo current, final byte [] tableName) {
+ return Bytes.equals(tableName, current.getTableName());
+ }
+
+ /**
+ * @param tableName
+ * @return Place to start Scan in <code>.META.</code> when passed a
+ * <code>tableName</code>; returns <tableName&rt; <,&rt; <,&rt;
+ */
+ static byte [] getTableStartRowForMeta(final byte [] tableName) {
+ byte [] startRow = new byte[tableName.length + 2];
+ System.arraycopy(tableName, 0, startRow, 0, tableName.length);
+ startRow[startRow.length - 2] = HConstants.DELIMITER;
+ startRow[startRow.length - 1] = HConstants.DELIMITER;
+ return startRow;
+ }
+
+ /**
+ * This method creates a Scan object that will only scan catalog rows that
+ * belong to the specified table. It doesn't specify any columns.
+ * This is a better alternative to just using a start row and scan until
+ * it hits a new table since that requires parsing the HRI to get the table
+ * name.
+ * @param tableName bytes of table's name
+ * @return configured Scan object
+ */
+ public static Scan getScanForTableName(byte[] tableName) {
+ String strName = Bytes.toString(tableName);
+ // Start key is just the table name with delimiters
+ byte[] startKey = Bytes.toBytes(strName + ",,");
+ // Stop key appends the smallest possible char to the table name
+ byte[] stopKey = Bytes.toBytes(strName + " ,,");
+
+ Scan scan = new Scan(startKey);
+ scan.setStopRow(stopKey);
+ return scan;
+ }
+
+ /**
+ * @param catalogTracker
+ * @param tableName
+ * @return Return list of regioninfos and server.
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public static List<Pair<HRegionInfo, ServerName>>
+ getTableRegionsAndLocations(CatalogTracker catalogTracker, String tableName)
+ throws IOException, InterruptedException {
+ return getTableRegionsAndLocations(catalogTracker, Bytes.toBytes(tableName),
+ true);
+ }
+
+ /**
+ * @param catalogTracker
+ * @param tableName
+ * @return Return list of regioninfos and server addresses.
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public static List<Pair<HRegionInfo, ServerName>>
+ getTableRegionsAndLocations(final CatalogTracker catalogTracker,
+ final byte [] tableName, final boolean excludeOfflinedSplitParents)
+ throws IOException, InterruptedException {
+ if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
+ // If root, do a bit of special handling.
+ ServerName serverName = catalogTracker.getRootLocation();
+ List<Pair<HRegionInfo, ServerName>> list =
+ new ArrayList<Pair<HRegionInfo, ServerName>>();
+ list.add(new Pair<HRegionInfo, ServerName>(HRegionInfo.ROOT_REGIONINFO,
+ serverName));
+ return list;
+ }
+ // Make a version of CollectingVisitor that collects HRegionInfo and ServerAddress
+ CollectingVisitor<Pair<HRegionInfo, ServerName>> visitor =
+ new CollectingVisitor<Pair<HRegionInfo, ServerName>>() {
+ private Pair<HRegionInfo, ServerName> current = null;
+
+ @Override
+ public boolean visit(Result r) throws IOException {
+ HRegionInfo hri =
+ HRegionInfo.getHRegionInfo(r, HConstants.REGIONINFO_QUALIFIER);
+ if (hri == null) {
+ LOG.warn("No serialized HRegionInfo in " + r);
+ return true;
+ }
+ if (!isInsideTable(hri, tableName)) return false;
+ if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
+ ServerName sn = HRegionInfo.getServerName(r);
+ // Populate this.current so available when we call #add
+ this.current = new Pair<HRegionInfo, ServerName>(hri, sn);
+ // Else call super and add this Result to the collection.
+ return super.visit(r);
+ }
+
+ @Override
+ void add(Result r) {
+ this.results.add(this.current);
+ }
+ };
+ fullScan(catalogTracker, visitor, getTableStartRowForMeta(tableName),
+ Bytes.equals(tableName, HConstants.META_TABLE_NAME));
+ return visitor.getResults();
+ }
+
+ /**
+ * @param catalogTracker
+ * @param serverName
+ * @return List of user regions installed on this server (does not include
+ * catalog regions).
+ * @throws IOException
+ */
+ public static NavigableMap<HRegionInfo, Result>
+ getServerUserRegions(CatalogTracker catalogTracker, final ServerName serverName)
+ throws IOException {
+ final NavigableMap<HRegionInfo, Result> hris = new TreeMap<HRegionInfo, Result>();
+ // Fill the above hris map with entries from .META. that have the passed
+ // servername.
+ CollectingVisitor<Result> v = new CollectingVisitor<Result>() {
+ @Override
+ void add(Result r) {
+ if (r == null || r.isEmpty()) return;
+ ServerName sn = HRegionInfo.getServerName(r);
+ if (sn != null && sn.equals(serverName)) this.results.add(r);
+ }
+ };
+ fullScan(catalogTracker, v);
+ List<Result> results = v.getResults();
+ if (results != null && !results.isEmpty()) {
+ // Convert results to Map keyed by HRI
+ for (Result r: results) {
+ Pair<HRegionInfo, ServerName> p = HRegionInfo.getHRegionInfoAndServerName(r);
+ if (p != null && p.getFirst() != null) hris.put(p.getFirst(), r);
+ }
+ }
+ return hris;
+ }
+
+ public static void fullScanMetaAndPrint(final CatalogTracker catalogTracker)
+ throws IOException {
+ Visitor v = new Visitor() {
+ @Override
+ public boolean visit(Result r) throws IOException {
+ if (r == null || r.isEmpty()) return true;
+ LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
+ HRegionInfo hrim = HRegionInfo.getHRegionInfo(r);
+ LOG.info("fullScanMetaAndPrint.HRI Print= " + hrim);
+ return true;
+ }
+ };
+ fullScan(catalogTracker, v);
+ }
+
+ /**
+ * Performs a full scan of a catalog table.
+ * @param catalogTracker
+ * @param visitor Visitor invoked against each row.
+ * @param startrow Where to start the scan. Pass null if want to begin scan
+ * at first row.
+ * @param scanRoot True if we are to scan <code>-ROOT-</code> rather than
+ * <code>.META.</code>, the default (pass false to scan .META.)
+ * @throws IOException
+ */
+ static void fullScan(CatalogTracker catalogTracker,
+ final Visitor visitor, final byte [] startrow, final boolean scanRoot)
+ throws IOException {
+ Scan scan = new Scan();
+ if (startrow != null) scan.setStartRow(startrow);
+ if (startrow == null && !scanRoot) {
+ int caching = catalogTracker.getConnection().getConfiguration()
+ .getInt(HConstants.HBASE_META_SCANNER_CACHING, 100);
+ scan.setCaching(caching);
+ }
+ scan.addFamily(HConstants.CATALOG_FAMILY);
+ HTable metaTable = scanRoot?
+ getRootHTable(catalogTracker): getMetaHTable(catalogTracker);
+ ResultScanner scanner = metaTable.getScanner(scan);
+ try {
+ Result data;
+ while((data = scanner.next()) != null) {
+ if (data.isEmpty()) continue;
+ // Break if visit returns false.
+ if (!visitor.visit(data)) break;
+ }
+ } finally {
+ scanner.close();
+ metaTable.close();
+ }
+ return;
+ }
+
+ /**
+ * Implementations 'visit' a catalog table row.
+ */
+ public interface Visitor {
+ /**
+ * Visit the catalog table row.
+ * @param r A row from catalog table
+ * @return True if we are to proceed scanning the table, else false if
+ * we are to stop now.
+ */
+ public boolean visit(final Result r) throws IOException;
+ }
+
+ /**
+ * A {@link Visitor} that collects content out of passed {@link Result}.
+ */
+ static abstract class CollectingVisitor<T> implements Visitor {
+ final List<T> results = new ArrayList<T>();
+ @Override
+ public boolean visit(Result r) throws IOException {
+ if (r == null || r.isEmpty()) return true;
+ add(r);
+ return true;
+ }
+
+ abstract void add(Result r);
+
+ /**
+ * @return Collected results; wait till visits complete to collect all
+ * possible results
+ */
+ List<T> getResults() {
+ return this.results;
+ }
+ }
+
+ /**
+ * Collects all returned.
+ */
+ static class CollectAllVisitor extends CollectingVisitor<Result> {
+ @Override
+ void add(Result r) {
+ this.results.add(r);
+ }
+ }
+
+ /**
+ * Count regions in <code>.META.</code> for passed table.
+ * @param c
+ * @param tableName
+ * @return Count or regions in table <code>tableName</code>
+ * @throws IOException
+ */
+ public static int getRegionCount(final Configuration c, final String tableName) throws IOException {
+ HTable t = new HTable(c, tableName);
+ try {
+ return t.getRegionLocations().size();
+ } finally {
+ t.close();
+ }
+ }
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AbstractClientScanner.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,77 @@
+/**
+ * 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.client;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ * Helper class for custom client scanners.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public abstract class AbstractClientScanner implements ResultScanner {
+
+ @Override
+ public Iterator<Result> iterator() {
+ return new Iterator<Result>() {
+ // The next RowResult, possibly pre-read
+ Result next = null;
+
+ // return true if there is another item pending, false if there isn't.
+ // this method is where the actual advancing takes place, but you need
+ // to call next() to consume it. hasNext() will only advance if there
+ // isn't a pending next().
+ public boolean hasNext() {
+ if (next == null) {
+ try {
+ next = AbstractClientScanner.this.next();
+ return next != null;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return true;
+ }
+
+ // get the pending next item and advance the iterator. returns null if
+ // there is no next item.
+ public Result next() {
+ // since hasNext() does the real advancing, we call this to determine
+ // if there is a next before proceeding.
+ if (!hasNext()) {
+ return null;
+ }
+
+ // if we get to here, then hasNext() has given us an item to return.
+ // we want to return the item and then null out the next pointer, so
+ // we use a temporary variable.
+ Result temp = next;
+ next = null;
+ return temp;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Action.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Action.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Action.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Action.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,88 @@
+/**
+ *
+ * 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.client;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * A Get, Put or Delete associated with it's region. Used internally by
+ * {@link HTable#batch} to associate the action with it's region and maintain
+ * the index from the original request.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public class Action<R> implements Comparable<R> {
+
+ private Row action;
+ private int originalIndex;
+ private R result;
+
+ /**
+ * This constructor is replaced by {@link #Action(Row, int)}
+ */
+ @Deprecated
+ public Action(byte[] regionName, Row action, int originalIndex) {
+ this(action, originalIndex);
+ }
+
+ public Action(Row action, int originalIndex) {
+ super();
+ this.action = action;
+ this.originalIndex = originalIndex;
+ }
+
+ @Deprecated
+ public byte[] getRegionName() {
+ return null;
+ }
+
+ @Deprecated
+ public void setRegionName(byte[] regionName) {
+ }
+
+ public R getResult() {
+ return result;
+ }
+
+ public void setResult(R result) {
+ this.result = result;
+ }
+
+ public Row getAction() {
+ return action;
+ }
+
+ public int getOriginalIndex() {
+ return originalIndex;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ return action.compareTo(((Action) o).getAction());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ Action<?> other = (Action<?>) obj;
+ return compareTo(other) == 0;
+ }
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminProtocol.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminProtocol.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminProtocol.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminProtocol.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,35 @@
+/**
+ * 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.client;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.IpcProtocol;
+import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
+import org.apache.hadoop.hbase.security.KerberosInfo;
+import org.apache.hadoop.hbase.security.TokenInfo;
+
+/**
+ * Protocol that a HBase client uses to communicate with a region server.
+ */
+@KerberosInfo(
+ serverPrincipal = "hbase.regionserver.kerberos.principal")
+@TokenInfo("HBASE_AUTH_TOKEN")
+@InterfaceAudience.Private
+public interface AdminProtocol
+extends AdminService.BlockingInterface, IpcProtocol {}
\ No newline at end of file
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Append.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,90 @@
+/*
+ * 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.client;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Performs Append operations on a single row.
+ * <p>
+ * Note that this operation does not appear atomic to readers. Appends are done
+ * under a single row lock, so write operations to a row are synchronized, but
+ * readers do not take row locks so get and scan operations can see this
+ * operation partially completed.
+ * <p>
+ * To append to a set of columns of a row, instantiate an Append object with the
+ * row to append to. At least one column to append must be specified using the
+ * {@link #add(byte[], byte[], byte[])} method.
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public class Append extends Mutation {
+ private static final String RETURN_RESULTS = "_rr_";
+ /**
+ * @param returnResults
+ * True (default) if the append operation should return the results.
+ * A client that is not interested in the result can save network
+ * bandwidth setting this to false.
+ */
+ public void setReturnResults(boolean returnResults) {
+ setAttribute(RETURN_RESULTS, Bytes.toBytes(returnResults));
+ }
+
+ /**
+ * @return current setting for returnResults
+ */
+ public boolean isReturnResults() {
+ byte[] v = getAttribute(RETURN_RESULTS);
+ return v == null ? true : Bytes.toBoolean(v);
+ }
+
+ /**
+ * Create a Append operation for the specified row.
+ * <p>
+ * At least one column must be appended to.
+ * @param row row key
+ */
+ public Append(byte[] row) {
+ this.row = Arrays.copyOf(row, row.length);
+ }
+
+ /**
+ * Add the specified column and value to this Append operation.
+ * @param family family name
+ * @param qualifier column qualifier
+ * @param value value to append to specified column
+ * @return this
+ */
+ public Append add(byte [] family, byte [] qualifier, byte [] value) {
+ List<KeyValue> list = familyMap.get(family);
+ if(list == null) {
+ list = new ArrayList<KeyValue>();
+ }
+ list.add(new KeyValue(
+ this.row, family, qualifier, this.ts, KeyValue.Type.Put, value));
+ familyMap.put(family, list);
+ return this;
+ }
+}
\ No newline at end of file
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Attributes.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Attributes.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Attributes.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Attributes.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.client;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+
+import java.util.Map;
+
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public interface Attributes {
+ /**
+ * Sets an attribute.
+ * In case value = null attribute is removed from the attributes map.
+ * Attribute names starting with _ indicate system attributes.
+ * @param name attribute name
+ * @param value attribute value
+ */
+ public void setAttribute(String name, byte[] value);
+
+ /**
+ * Gets an attribute
+ * @param name attribute name
+ * @return attribute value if attribute is set, <tt>null</tt> otherwise
+ */
+ public byte[] getAttribute(String name);
+
+ /**
+ * Gets all attributes
+ * @return unmodifiable map of all attributes
+ */
+ public Map<String, byte[]> getAttributesMap();
+}
Added: hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ClientProtocol.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ClientProtocol.java?rev=1449950&view=auto
==============================================================================
--- hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ClientProtocol.java (added)
+++ hbase/trunk/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ClientProtocol.java Mon Feb 25 22:50:17 2013
@@ -0,0 +1,37 @@
+/**
+ * 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.client;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.hbase.IpcProtocol;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ClientService;
+import org.apache.hadoop.hbase.security.KerberosInfo;
+import org.apache.hadoop.hbase.security.TokenInfo;
+
+/**
+ * Protocol that a HBase client uses to communicate with a region server.
+ */
+@KerberosInfo(
+ serverPrincipal = "hbase.regionserver.kerberos.principal")
+@TokenInfo("HBASE_AUTH_TOKEN")
+@InterfaceAudience.Public
+@InterfaceStability.Evolving
+public interface ClientProtocol
+extends ClientService.BlockingInterface, IpcProtocol {}
\ No newline at end of file