Merge branch '1.5.3-SNAPSHOT' into 1.6.2-SNAPSHOT
Conflicts:
server/src/main/java/org/apache/accumulo/server/master/Master.java
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/443888b4
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/443888b4
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/443888b4
Branch: refs/heads/1.6.2-SNAPSHOT
Commit: 443888b4db7674ec4e1502cb66ddb67f6956bc44
Parents: 53705e7 2579d51
Author: Josh Elser <[email protected]>
Authored: Wed Sep 24 15:07:29 2014 -0400
Committer: Josh Elser <[email protected]>
Committed: Wed Sep 24 15:07:29 2014 -0400
----------------------------------------------------------------------
.../accumulo/master/FateServiceHandler.java | 158 +++++++++++++++++--
1 file changed, 141 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/accumulo/blob/443888b4/server/master/src/main/java/org/apache/accumulo/master/FateServiceHandler.java
----------------------------------------------------------------------
diff --cc
server/master/src/main/java/org/apache/accumulo/master/FateServiceHandler.java
index cd522f5,0000000..a3ea117
mode 100644,000000..100644
---
a/server/master/src/main/java/org/apache/accumulo/master/FateServiceHandler.java
+++
b/server/master/src/main/java/org/apache/accumulo/master/FateServiceHandler.java
@@@ -1,426 -1,0 +1,550 @@@
+/*
+ * 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.accumulo.master;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
++import org.apache.accumulo.core.client.AccumuloSecurityException;
+import org.apache.accumulo.core.client.IteratorSetting;
+import org.apache.accumulo.core.client.NamespaceNotFoundException;
- import org.apache.accumulo.core.client.impl.TableOperationsImpl;
++import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.admin.TimeType;
+import org.apache.accumulo.core.client.impl.Namespaces;
++import org.apache.accumulo.core.client.impl.TableOperationsImpl;
+import org.apache.accumulo.core.client.impl.Tables;
+import org.apache.accumulo.core.client.impl.thrift.SecurityErrorCode;
+import org.apache.accumulo.core.client.impl.thrift.TableOperation;
+import
org.apache.accumulo.core.client.impl.thrift.TableOperationExceptionType;
+import org.apache.accumulo.core.client.impl.thrift.ThriftSecurityException;
+import
org.apache.accumulo.core.client.impl.thrift.ThriftTableOperationException;
+import org.apache.accumulo.core.iterators.IteratorUtil;
+import org.apache.accumulo.core.master.thrift.FateOperation;
+import org.apache.accumulo.core.master.thrift.FateService;
+import org.apache.accumulo.core.security.thrift.TCredentials;
+import org.apache.accumulo.core.util.ArgumentChecker.Validator;
+import org.apache.accumulo.core.util.ByteBufferUtil;
+import org.apache.accumulo.fate.ReadOnlyTStore.TStatus;
+import org.apache.accumulo.master.tableOps.BulkImport;
+import org.apache.accumulo.master.tableOps.CancelCompactions;
+import org.apache.accumulo.master.tableOps.ChangeTableState;
+import org.apache.accumulo.master.tableOps.CloneTable;
+import org.apache.accumulo.master.tableOps.CompactRange;
+import org.apache.accumulo.master.tableOps.CreateNamespace;
+import org.apache.accumulo.master.tableOps.CreateTable;
+import org.apache.accumulo.master.tableOps.DeleteNamespace;
+import org.apache.accumulo.master.tableOps.DeleteTable;
+import org.apache.accumulo.master.tableOps.ExportTable;
+import org.apache.accumulo.master.tableOps.ImportTable;
+import org.apache.accumulo.master.tableOps.RenameNamespace;
+import org.apache.accumulo.master.tableOps.RenameTable;
+import org.apache.accumulo.master.tableOps.TableRangeOp;
+import org.apache.accumulo.master.tableOps.TraceRepo;
+import org.apache.accumulo.server.client.ClientServiceHandler;
+import org.apache.accumulo.server.master.state.MergeInfo;
+import org.apache.accumulo.server.util.TablePropUtil;
+import org.apache.accumulo.trace.thrift.TInfo;
+import org.apache.hadoop.io.Text;
+import org.apache.log4j.Logger;
+
+/**
- *
++ *
+ */
+class FateServiceHandler implements FateService.Iface {
+
+ protected final Master master;
+ protected static final Logger log = Master.log;
+
+ public FateServiceHandler(Master master) {
+ this.master = master;
+ }
+
+ @Override
+ public long beginFateOperation(TInfo tinfo, TCredentials credentials)
throws ThriftSecurityException {
+ authenticate(credentials);
+ return master.fate.startTransaction();
+ }
+
+ @Override
+ public void executeFateOperation(TInfo tinfo, TCredentials c, long opid,
FateOperation op, List<ByteBuffer> arguments, Map<String,String> options,
+ boolean autoCleanup) throws ThriftSecurityException,
ThriftTableOperationException {
+ authenticate(c);
+
+ switch (op) {
+ case NAMESPACE_CREATE: {
+ TableOperation tableOp = TableOperation.CREATE;
+ String namespace = validateNamespaceArgument(arguments.get(0),
tableOp, null);
+
+ if (!master.security.canCreateNamespace(c, namespace))
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
CreateNamespace(c.getPrincipal(), namespace, options)), autoCleanup);
+ break;
+ }
+ case NAMESPACE_RENAME: {
+ TableOperation tableOp = TableOperation.RENAME;
+ String oldName = validateNamespaceArgument(arguments.get(0), tableOp,
Namespaces.NOT_DEFAULT.and(Namespaces.NOT_ACCUMULO));
+ String newName = validateNamespaceArgument(arguments.get(1), tableOp,
null);
+
+ String namespaceId =
ClientServiceHandler.checkNamespaceId(master.getInstance(), oldName, tableOp);
+ if (!master.security.canRenameNamespace(c, namespaceId, oldName,
newName))
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
RenameNamespace(namespaceId, oldName, newName)), autoCleanup);
+ break;
+ }
+ case NAMESPACE_DELETE: {
+ TableOperation tableOp = TableOperation.DELETE;
+ String namespace = validateNamespaceArgument(arguments.get(0),
tableOp, Namespaces.NOT_DEFAULT.and(Namespaces.NOT_ACCUMULO));
+
+ String namespaceId =
ClientServiceHandler.checkNamespaceId(master.getInstance(), namespace, tableOp);
+ if (!master.security.canDeleteNamespace(c, namespaceId))
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
DeleteNamespace(namespaceId)), autoCleanup);
+ break;
+ }
+ case TABLE_CREATE: {
+ TableOperation tableOp = TableOperation.CREATE;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, Tables.NOT_SYSTEM);
+ TimeType timeType =
TimeType.valueOf(ByteBufferUtil.toString(arguments.get(1)));
+
+ String namespaceId;
+
+ try {
+ namespaceId = Namespaces.getNamespaceId(master.getInstance(),
Tables.qualify(tableName).getFirst());
+ } catch (NamespaceNotFoundException e) {
+ throw new ThriftTableOperationException(null, tableName, tableOp,
TableOperationExceptionType.NAMESPACE_NOTFOUND, "");
+ }
+
+ if (!master.security.canCreateTable(c, tableName, namespaceId))
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
CreateTable(c.getPrincipal(), tableName, timeType, options, namespaceId)),
autoCleanup);
+
+ break;
+ }
+ case TABLE_RENAME: {
+ TableOperation tableOp = TableOperation.RENAME;
+ final String oldTableName =
validateTableNameArgument(arguments.get(0), tableOp, Tables.NOT_SYSTEM);
+ String newTableName = validateTableNameArgument(arguments.get(1),
tableOp, new Validator<String>() {
+
+ @Override
+ public boolean isValid(String argument) {
+ // verify they are in the same namespace
+ String oldNamespace = Tables.qualify(oldTableName).getFirst();
+ return oldNamespace.equals(Tables.qualify(argument).getFirst());
+ }
+
+ @Override
+ public String invalidMessage(String argument) {
+ return "Cannot move tables to a new namespace by renaming. The
namespace for " + oldTableName + " does not match " + argument;
+ }
+
+ });
+
+ String tableId =
ClientServiceHandler.checkTableId(master.getInstance(), oldTableName, tableOp);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canRenameTable(c, tableId, oldTableName,
newTableName, namespaceId))
++ final boolean canRename;
++ try {
++ canRename = master.security.canRenameTable(c, tableId,
oldTableName, newTableName, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, oldTableName,
TableOperation.RENAME);
++ throw e;
++ }
++
++ if (!canRename)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ try {
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
RenameTable(tableId, oldTableName, newTableName)), autoCleanup);
+ } catch (NamespaceNotFoundException e) {
+ throw new ThriftTableOperationException(null, oldTableName,
tableOp, TableOperationExceptionType.NAMESPACE_NOTFOUND, "");
+ }
+
+ break;
+ }
+ case TABLE_CLONE: {
+ TableOperation tableOp = TableOperation.CLONE;
+ String srcTableId = validateTableIdArgument(arguments.get(0),
tableOp, Tables.NOT_ROOT_ID);
+ String tableName = validateTableNameArgument(arguments.get(1),
tableOp, Tables.NOT_SYSTEM);
+ String namespaceId;
+ try {
+ namespaceId = Namespaces.getNamespaceId(master.getInstance(),
Tables.qualify(tableName).getFirst());
+ } catch (NamespaceNotFoundException e) {
+ // shouldn't happen, but possible once cloning between namespaces
is supported
+ throw new ThriftTableOperationException(null, tableName, tableOp,
TableOperationExceptionType.NAMESPACE_NOTFOUND, "");
+ }
-
- if (!master.security.canCloneTable(c, srcTableId, tableName,
namespaceId, namespaceId))
++
++ final boolean canCloneTable;
++ try {
++ canCloneTable = master.security.canCloneTable(c, srcTableId,
tableName, namespaceId, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, srcTableId, null,
TableOperation.CLONE);
++ throw e;
++ }
++
++ if (!canCloneTable)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ Map<String,String> propertiesToSet = new HashMap<String,String>();
+ Set<String> propertiesToExclude = new HashSet<String>();
+
+ for (Entry<String,String> entry : options.entrySet()) {
+ if
(entry.getKey().startsWith(TableOperationsImpl.CLONE_EXCLUDE_PREFIX)) {
+
propertiesToExclude.add(entry.getKey().substring(TableOperationsImpl.CLONE_EXCLUDE_PREFIX.length()));
+ continue;
+ }
+
+ if (!TablePropUtil.isPropertyValid(entry.getKey(),
entry.getValue())) {
+ throw new ThriftTableOperationException(null, tableName, tableOp,
TableOperationExceptionType.OTHER, "Property or value not valid "
+ + entry.getKey() + "=" + entry.getValue());
+ }
+
+ propertiesToSet.put(entry.getKey(), entry.getValue());
+ }
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
CloneTable(c.getPrincipal(), srcTableId, tableName, propertiesToSet,
propertiesToExclude)),
+ autoCleanup);
+
+ break;
+ }
+ case TABLE_DELETE: {
+ TableOperation tableOp = TableOperation.DELETE;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, Tables.NOT_SYSTEM);
+
+ final String tableId =
ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canDeleteTable(c, tableId, namespaceId))
++ final boolean canDeleteTable;
++ try {
++ canDeleteTable = master.security.canDeleteTable(c, tableId,
namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, tableName,
TableOperation.DELETE);
++ throw e;
++ }
++
++ if (!canDeleteTable)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
DeleteTable(tableId)), autoCleanup);
+ break;
+ }
+ case TABLE_ONLINE: {
+ TableOperation tableOp = TableOperation.ONLINE;
+ final String tableId = validateTableIdArgument(arguments.get(0),
tableOp, Tables.NOT_ROOT_ID);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canOnlineOfflineTable(c, tableId, op,
namespaceId))
++ final boolean canOnlineOfflineTable;
++ try {
++ canOnlineOfflineTable = master.security.canOnlineOfflineTable(c,
tableId, op, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, null,
TableOperation.ONLINE);
++ throw e;
++ }
++
++ if (!canOnlineOfflineTable)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
ChangeTableState(tableId, tableOp)), autoCleanup);
+ break;
+ }
+ case TABLE_OFFLINE: {
+ TableOperation tableOp = TableOperation.OFFLINE;
+ final String tableId = validateTableIdArgument(arguments.get(0),
tableOp, Tables.NOT_ROOT_ID);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canOnlineOfflineTable(c, tableId, op,
namespaceId))
++ final boolean canOnlineOfflineTable;
++ try {
++ canOnlineOfflineTable = master.security.canOnlineOfflineTable(c,
tableId, op, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, null,
TableOperation.OFFLINE);
++ throw e;
++ }
++
++ if (!canOnlineOfflineTable)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
ChangeTableState(tableId, tableOp)), autoCleanup);
+ break;
+ }
+ case TABLE_MERGE: {
+ TableOperation tableOp = TableOperation.MERGE;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, null);
+ Text startRow = ByteBufferUtil.toText(arguments.get(1));
+ Text endRow = ByteBufferUtil.toText(arguments.get(2));
+
+ final String tableId =
ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canMerge(c, tableId, namespaceId))
++ final boolean canMerge;
++ try {
++ canMerge = master.security.canMerge(c, tableId, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, tableName,
TableOperation.MERGE);
++ throw e;
++ }
++
++ if (!canMerge)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ Master.log.debug("Creating merge op: " + tableId + " " + startRow + "
" + endRow);
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
TableRangeOp(MergeInfo.Operation.MERGE, tableId, startRow, endRow)),
autoCleanup);
+ break;
+ }
+ case TABLE_DELETE_RANGE: {
+ TableOperation tableOp = TableOperation.DELETE_RANGE;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, Tables.NOT_SYSTEM);
+ Text startRow = ByteBufferUtil.toText(arguments.get(1));
+ Text endRow = ByteBufferUtil.toText(arguments.get(2));
+
+ final String tableId =
ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canDeleteRange(c, tableId, tableName, startRow,
endRow, namespaceId))
++ final boolean canDeleteRange;
++ try {
++ canDeleteRange = master.security.canDeleteRange(c, tableId,
tableName, startRow, endRow, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, tableName,
TableOperation.DELETE_RANGE);
++ throw e;
++ }
++
++ if (!canDeleteRange)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
TableRangeOp(MergeInfo.Operation.DELETE, tableId, startRow, endRow)),
autoCleanup);
+ break;
+ }
+ case TABLE_BULK_IMPORT: {
+ TableOperation tableOp = TableOperation.BULK_IMPORT;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, Tables.NOT_SYSTEM);
+ String dir = ByteBufferUtil.toString(arguments.get(1));
+ String failDir = ByteBufferUtil.toString(arguments.get(2));
+ boolean setTime =
Boolean.parseBoolean(ByteBufferUtil.toString(arguments.get(3)));
+
+ final String tableId =
ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
-
- if (!master.security.canBulkImport(c, tableId, tableName, dir,
failDir, namespaceId))
++
++ final boolean canBulkImport;
++ try {
++ canBulkImport = master.security.canBulkImport(c, tableId,
tableName, dir, failDir, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, tableName,
TableOperation.BULK_IMPORT);
++ throw e;
++ }
++
++ if (!canBulkImport)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
BulkImport(tableId, dir, failDir, setTime)), autoCleanup);
+ break;
+ }
+ case TABLE_COMPACT: {
+ TableOperation tableOp = TableOperation.COMPACT;
+ String tableId = validateTableIdArgument(arguments.get(0), tableOp,
null);
+ byte[] startRow = ByteBufferUtil.toBytes(arguments.get(1));
+ byte[] endRow = ByteBufferUtil.toBytes(arguments.get(2));
+ List<IteratorSetting> iterators =
IteratorUtil.decodeIteratorSettings(ByteBufferUtil.toBytes(arguments.get(3)));
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canCompact(c, tableId, namespaceId))
++ final boolean canCompact;
++ try {
++ canCompact = master.security.canCompact(c, tableId, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, null,
TableOperation.COMPACT);
++ throw e;
++ }
++
++ if (!canCompact)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
CompactRange(tableId, startRow, endRow, iterators)), autoCleanup);
+ break;
+ }
+ case TABLE_CANCEL_COMPACT: {
+ TableOperation tableOp = TableOperation.COMPACT_CANCEL;
+ String tableId = validateTableIdArgument(arguments.get(0), tableOp,
null);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
+
- if (!master.security.canCompact(c, tableId, namespaceId))
++ final boolean canCancelCompact;
++ try {
++ canCancelCompact = master.security.canCompact(c, tableId,
namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, null,
TableOperation.COMPACT_CANCEL);
++ throw e;
++ }
++
++ if (!canCancelCompact)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
CancelCompactions(tableId)), autoCleanup);
+ break;
+ }
+ case TABLE_IMPORT: {
+ TableOperation tableOp = TableOperation.IMPORT;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, Tables.NOT_SYSTEM);
+ String exportDir = ByteBufferUtil.toString(arguments.get(1));
+ String namespaceId;
+ try {
+ namespaceId = Namespaces.getNamespaceId(master.getInstance(),
Tables.qualify(tableName).getFirst());
+ } catch (NamespaceNotFoundException e) {
+ throw new ThriftTableOperationException(null, tableName, tableOp,
TableOperationExceptionType.NAMESPACE_NOTFOUND, "");
+ }
+
- if (!master.security.canImport(c, tableName, exportDir, namespaceId))
++ final boolean canImport;
++ try {
++ canImport = master.security.canImport(c, tableName, exportDir,
namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, null, tableName,
TableOperation.IMPORT);
++ throw e;
++ }
++
++ if (!canImport)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
ImportTable(c.getPrincipal(), tableName, exportDir, namespaceId)), autoCleanup);
+ break;
+ }
+ case TABLE_EXPORT: {
+ TableOperation tableOp = TableOperation.EXPORT;
+ String tableName = validateTableNameArgument(arguments.get(0),
tableOp, Tables.NOT_SYSTEM);
+ String exportDir = ByteBufferUtil.toString(arguments.get(1));
+
+ String tableId =
ClientServiceHandler.checkTableId(master.getInstance(), tableName, tableOp);
+ String namespaceId = Tables.getNamespaceId(master.getInstance(),
tableId);
-
- if (!master.security.canExport(c, tableId, tableName, exportDir,
namespaceId))
++
++ final boolean canExport;
++ try {
++ canExport = master.security.canExport(c, tableId, tableName,
exportDir, namespaceId);
++ } catch (ThriftSecurityException e) {
++ throwIfTableMissingSecurityException(e, tableId, tableName,
TableOperation.EXPORT);
++ throw e;
++ }
++
++ if (!canExport)
+ throw new ThriftSecurityException(c.getPrincipal(),
SecurityErrorCode.PERMISSION_DENIED);
+
+ master.fate.seedTransaction(opid, new TraceRepo<Master>(new
ExportTable(tableName, tableId, exportDir)), autoCleanup);
+ break;
+ }
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
++ /**
++ * Inspects the {@link ThriftSecurityException} and throws a {@link
ThriftTableOperationException} if the {@link SecurityErrorCode} on the
++ * {@link ThriftSecurityException} was {code}TABLE_DOESNT_EXIST{code}. If
the {@link ThriftSecurityException} is thrown because a table doesn't exist
anymore,
++ * clients will likely see an {@link AccumuloSecurityException} instead of
a {@link TableNotFoundException} as expected. If the
++ * {@link ThriftSecurityException} has a different {@link
SecurityErrorCode}, this method does nothing and expects the caller to properly
handle the original
++ * exception.
++ *
++ * @param e
++ * A caught ThriftSecurityException
++ * @param tableId
++ * Table ID being operated on, or null
++ * @param tableName
++ * Table name being operated on, or null
++ * @param op
++ * The TableOperation the Master was attempting to perform
++ * @throws ThriftTableOperationException
++ * Thrown if {@link e} was thrown because {@link
SecurityErrorCode#TABLE_DOESNT_EXIST}
++ */
++ private void throwIfTableMissingSecurityException(ThriftSecurityException
e, String tableId, String tableName, TableOperation op)
++ throws ThriftTableOperationException {
++ // ACCUMULO-3135 Table can be deleted after we get table ID but before we
can check permission
++ if (e.isSetCode() && SecurityErrorCode.TABLE_DOESNT_EXIST == e.getCode())
{
++ throw new ThriftTableOperationException(tableId, tableName, op,
TableOperationExceptionType.NOTFOUND, "Table no longer exists");
++ }
++ }
++
+ @Override
+ public String waitForFateOperation(TInfo tinfo, TCredentials credentials,
long opid) throws ThriftSecurityException, ThriftTableOperationException {
+ authenticate(credentials);
+
+ TStatus status = master.fate.waitForCompletion(opid);
+ if (status == TStatus.FAILED) {
+ Exception e = master.fate.getException(opid);
+ if (e instanceof ThriftTableOperationException)
+ throw (ThriftTableOperationException) e;
+ else if (e instanceof ThriftSecurityException)
+ throw (ThriftSecurityException) e;
+ else if (e instanceof RuntimeException)
+ throw (RuntimeException) e;
+ else
+ throw new RuntimeException(e);
+ }
+
+ String ret = master.fate.getReturn(opid);
+ if (ret == null)
+ ret = ""; // thrift does not like returning null
+ return ret;
+ }
+
+ @Override
+ public void finishFateOperation(TInfo tinfo, TCredentials credentials, long
opid) throws ThriftSecurityException {
+ authenticate(credentials);
+ master.fate.delete(opid);
+ }
+
+ protected void authenticate(TCredentials credentials) throws
ThriftSecurityException {
+ // this is a bit redundant, the credentials of the caller (the first arg)
will throw an exception if it fails to authenticate
+ // before the second arg is checked (which would return true or false)
+ if (!master.security.authenticateUser(credentials, credentials))
+ throw new ThriftSecurityException(credentials.getPrincipal(),
SecurityErrorCode.BAD_CREDENTIALS);
+ }
+
+ // Verify table name arguments are valid, and match any additional
restrictions
+ private String validateTableIdArgument(ByteBuffer tableIdArg,
TableOperation op, Validator<String> userValidator) throws
ThriftTableOperationException {
+ String tableId = tableIdArg == null ? null :
ByteBufferUtil.toString(tableIdArg);
+ try {
+ return Tables.VALID_ID.and(userValidator).validate(tableId);
+ } catch (IllegalArgumentException e) {
+ String why = e.getMessage();
+ log.warn(why);
+ throw new ThriftTableOperationException(tableId, null, op,
TableOperationExceptionType.INVALID_NAME, why);
+ }
+ }
+
+ // Verify table name arguments are valid, and match any additional
restrictions
+ private String validateTableNameArgument(ByteBuffer tableNameArg,
TableOperation op, Validator<String> userValidator) throws
ThriftTableOperationException {
+ String tableName = tableNameArg == null ? null :
ByteBufferUtil.toString(tableNameArg);
+ return _validateArgument(tableName, op,
Tables.VALID_NAME.and(userValidator));
+ }
+
+ // Verify namespace arguments are valid, and match any additional
restrictions
+ private String validateNamespaceArgument(ByteBuffer namespaceArg,
TableOperation op, Validator<String> userValidator) throws
ThriftTableOperationException {
+ String namespace = namespaceArg == null ? null :
ByteBufferUtil.toString(namespaceArg);
+ return _validateArgument(namespace, op,
Namespaces.VALID_NAME.and(userValidator));
+ }
+
+ // helper to handle the exception
+ private <T> T _validateArgument(T arg, TableOperation op, Validator<T>
validator) throws ThriftTableOperationException {
+ try {
+ return validator.validate(arg);
+ } catch (IllegalArgumentException e) {
+ String why = e.getMessage();
+ log.warn(why);
+ throw new ThriftTableOperationException(null, String.valueOf(arg), op,
TableOperationExceptionType.INVALID_NAME, why);
+ }
+ }
+}