You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2013/09/17 07:45:04 UTC
svn commit: r1523907 - in /hbase/branches/0.96:
hbase-client/src/main/java/org/apache/hadoop/hbase/
hbase-server/src/main/java/org/apache/hadoop/hbase/master/
hbase-server/src/test/java/org/apache/hadoop/hbase/
Author: stack
Date: Tue Sep 17 05:45:03 2013
New Revision: 1523907
URL: http://svn.apache.org/r1523907
Log:
HBASE-9510 Namespace operations should throw clean exceptions
Added:
hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceExistException.java
hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceNotFoundException.java
Modified:
hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableNamespaceManager.java
hbase/branches/0.96/hbase-server/src/test/java/org/apache/hadoop/hbase/TestNamespace.java
Added: hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceExistException.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceExistException.java?rev=1523907&view=auto
==============================================================================
--- hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceExistException.java (added)
+++ hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceExistException.java Tue Sep 17 05:45:03 2013
@@ -0,0 +1,39 @@
+/**
+ * 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;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Thrown when a namespace exists but should not
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public class NamespaceExistException extends DoNotRetryIOException {
+
+ private static final long serialVersionUID = -1582357514338825412L;
+
+ public NamespaceExistException() {
+ }
+
+ public NamespaceExistException(String namespace) {
+ super(namespace);
+ }
+}
Added: hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceNotFoundException.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceNotFoundException.java?rev=1523907&view=auto
==============================================================================
--- hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceNotFoundException.java (added)
+++ hbase/branches/0.96/hbase-client/src/main/java/org/apache/hadoop/hbase/NamespaceNotFoundException.java Tue Sep 17 05:45:03 2013
@@ -0,0 +1,39 @@
+/**
+ * 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;
+import org.apache.hadoop.classification.InterfaceStability;
+
+/**
+ * Thrown when a namespace can not be located
+ */
+@InterfaceAudience.Public
+@InterfaceStability.Stable
+public class NamespaceNotFoundException extends DoNotRetryIOException {
+
+ private static final long serialVersionUID = -6673607347330260324L;
+
+ public NamespaceNotFoundException() {
+ }
+
+ public NamespaceNotFoundException(String namespace) {
+ super(namespace);
+ }
+}
Modified: hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java?rev=1523907&r1=1523906&r2=1523907&view=diff
==============================================================================
--- hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java (original)
+++ hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java Tue Sep 17 05:45:03 2013
@@ -51,22 +51,21 @@ import org.apache.hadoop.hbase.Abortable
import org.apache.hadoop.hbase.Chore;
import org.apache.hadoop.hbase.ClusterId;
import org.apache.hadoop.hbase.ClusterStatus;
-import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.HBaseIOException;
-import org.apache.hadoop.hbase.NamespaceDescriptor;
-import org.apache.hadoop.hbase.constraint.ConstraintException;
-import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.HealthCheckChore;
import org.apache.hadoop.hbase.MasterNotRunningException;
+import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
import org.apache.hadoop.hbase.PleaseHoldException;
import org.apache.hadoop.hbase.Server;
import org.apache.hadoop.hbase.ServerLoad;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableDescriptors;
+import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotDisabledException;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.UnknownRegionException;
@@ -217,8 +216,11 @@ import org.apache.hadoop.metrics.util.MB
import org.apache.hadoop.net.DNS;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
+import org.apache.hadoop.hbase.exceptions.DeserializationException;
+import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;
import com.google.protobuf.RpcCallback;
@@ -365,7 +367,7 @@ MasterServices, Server {
/** The health check chore. */
private HealthCheckChore healthCheckChore;
-
+
/**
* is in distributedLogReplay mode. When true, SplitLogWorker directly replays WAL edits to newly
* assigned region servers instead of creating recovered.edits files.
@@ -490,7 +492,7 @@ MasterServices, Server {
}
}
- distributedLogReplay = this.conf.getBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY,
+ distributedLogReplay = this.conf.getBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY,
HConstants.DEFAULT_DISTRIBUTED_LOG_REPLAY_CONFIG);
}
@@ -903,7 +905,7 @@ MasterServices, Server {
status.setStatus("Starting namespace manager");
initNamespace();
}
-
+
if (this.cpHost != null) {
try {
this.cpHost.preMasterInitialization();
@@ -1362,6 +1364,7 @@ MasterServices, Server {
return !isStopped();
}
+ @Override
public IsMasterRunningResponse isMasterRunning(RpcController c, IsMasterRunningRequest req)
throws ServiceException {
return IsMasterRunningResponse.newBuilder().setIsMasterRunning(isMasterRunning()).build();
@@ -1719,9 +1722,7 @@ MasterServices, Server {
}
String namespace = hTableDescriptor.getTableName().getNamespaceAsString();
- if (getNamespaceDescriptor(namespace) == null) {
- throw new ConstraintException("Namespace " + namespace + " does not exist");
- }
+ getNamespaceDescriptor(namespace); // ensure namespace exists
HRegionInfo[] newRegions = getHRegionInfos(hTableDescriptor, splitKeys);
checkInitialized();
@@ -2102,6 +2103,7 @@ MasterServices, Server {
}
}
Collections.sort(backupMasters, new Comparator<ServerName>() {
+ @Override
public int compare(ServerName s1, ServerName s2) {
return s1.getServerName().compareTo(s2.getServerName());
}});
@@ -2209,6 +2211,7 @@ MasterServices, Server {
this.zooKeeper.reconnectAfterExpiration();
Callable<Boolean> callable = new Callable<Boolean> () {
+ @Override
public Boolean call() throws InterruptedException,
IOException, KeeperException {
MonitoredTask status =
@@ -2384,6 +2387,7 @@ MasterServices, Server {
return this.stopped;
}
+ @Override
public boolean isAborted() {
return this.abort;
}
@@ -2415,6 +2419,7 @@ MasterServices, Server {
*
* @return true if master is ready to go, false if not.
*/
+ @Override
public boolean isInitialized() {
return initialized;
}
@@ -2424,6 +2429,7 @@ MasterServices, Server {
* assignMeta to prevent processing of ServerShutdownHandler.
* @return true if assignMeta has completed;
*/
+ @Override
public boolean isServerShutdownHandlerEnabled() {
return this.serverShutdownHandlerEnabled;
}
@@ -2522,6 +2528,7 @@ MasterServices, Server {
* @return GetTableDescriptorsResponse
* @throws ServiceException
*/
+ @Override
public GetTableDescriptorsResponse getTableDescriptors(
RpcController controller, GetTableDescriptorsRequest req) throws ServiceException {
List<HTableDescriptor> descriptors = new ArrayList<HTableDescriptor>();
@@ -2590,6 +2597,7 @@ MasterServices, Server {
* @return GetTableNamesResponse
* @throws ServiceException
*/
+ @Override
public GetTableNamesResponse getTableNames(
RpcController controller, GetTableNamesRequest req) throws ServiceException {
try {
@@ -3031,6 +3039,7 @@ MasterServices, Server {
return org.apache.commons.lang.StringUtils.isNotBlank(healthScriptLocation);
}
+ @Override
public void createNamespace(NamespaceDescriptor descriptor) throws IOException {
TableName.isLegalNamespaceName(Bytes.toBytes(descriptor.getName()));
if (cpHost != null) {
@@ -3045,6 +3054,7 @@ MasterServices, Server {
}
}
+ @Override
public void modifyNamespace(NamespaceDescriptor descriptor) throws IOException {
TableName.isLegalNamespaceName(Bytes.toBytes(descriptor.getName()));
if (cpHost != null) {
@@ -3059,6 +3069,7 @@ MasterServices, Server {
}
}
+ @Override
public void deleteNamespace(String name) throws IOException {
if (cpHost != null) {
if (cpHost.preDeleteNamespace(name)) {
@@ -3072,19 +3083,29 @@ MasterServices, Server {
}
}
+ @Override
public NamespaceDescriptor getNamespaceDescriptor(String name) throws IOException {
- return tableNamespaceManager.get(name);
+ NamespaceDescriptor nsd = tableNamespaceManager.get(name);
+ if (nsd == null) {
+ throw new NamespaceNotFoundException(name);
+ }
+ return nsd;
}
+ @Override
public List<NamespaceDescriptor> listNamespaceDescriptors() throws IOException {
return Lists.newArrayList(tableNamespaceManager.list());
}
+ @Override
public List<HTableDescriptor> listTableDescriptorsByNamespace(String name) throws IOException {
+ getNamespaceDescriptor(name); // check that namespace exists
return Lists.newArrayList(tableDescriptors.getByNamespace(name).values());
}
+ @Override
public List<TableName> listTableNamesByNamespace(String name) throws IOException {
+ getNamespaceDescriptor(name); // check that namespace exists
List<TableName> tableNames = Lists.newArrayList();
for (HTableDescriptor descriptor: tableDescriptors.getByNamespace(name).values()) {
tableNames.add(descriptor.getTableName());
Modified: hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableNamespaceManager.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableNamespaceManager.java?rev=1523907&r1=1523906&r2=1523907&view=diff
==============================================================================
--- hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableNamespaceManager.java (original)
+++ hbase/branches/0.96/hbase-server/src/main/java/org/apache/hadoop/hbase/master/TableNamespaceManager.java Tue Sep 17 05:45:03 2013
@@ -33,6 +33,8 @@ import org.apache.hadoop.hbase.HConstant
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceExistException;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.ZKNamespaceManager;
import org.apache.hadoop.hbase.catalog.MetaReader;
@@ -113,14 +115,14 @@ public class TableNamespaceManager {
nsTable = new HTable(conf, TableName.NAMESPACE_TABLE_NAME);
zkNamespaceManager = new ZKNamespaceManager(masterServices.getZooKeeper());
zkNamespaceManager.start();
-
+
if (get(nsTable, NamespaceDescriptor.DEFAULT_NAMESPACE.getName()) == null) {
create(nsTable, NamespaceDescriptor.DEFAULT_NAMESPACE);
}
if (get(nsTable, NamespaceDescriptor.SYSTEM_NAMESPACE.getName()) == null) {
create(nsTable, NamespaceDescriptor.SYSTEM_NAMESPACE);
}
-
+
ResultScanner scanner = nsTable.getScanner(HTableDescriptor.NAMESPACE_FAMILY_INFO_BYTES);
try {
for(Result result : scanner) {
@@ -160,7 +162,7 @@ public class TableNamespaceManager {
public synchronized void update(NamespaceDescriptor ns) throws IOException {
HTable table = getNamespaceTable();
if (get(table, ns.getName()) == null) {
- throw new ConstraintException("Namespace "+ns.getName()+" does not exist");
+ throw new NamespaceNotFoundException(ns.getName());
}
upsert(table, ns);
}
@@ -179,7 +181,7 @@ public class TableNamespaceManager {
private void create(HTable table, NamespaceDescriptor ns) throws IOException {
if (get(table, ns.getName()) != null) {
- throw new ConstraintException("Namespace "+ns.getName()+" already exists");
+ throw new NamespaceExistException(ns.getName());
}
FileSystem fs = masterServices.getMasterFileSystem().getFileSystem();
fs.mkdirs(FSUtils.getNamespaceDir(
@@ -203,6 +205,9 @@ public class TableNamespaceManager {
}
public synchronized void remove(String name) throws IOException {
+ if (get(name) == null) {
+ throw new NamespaceNotFoundException(name);
+ }
if (NamespaceDescriptor.RESERVED_NAMESPACES.contains(name)) {
throw new ConstraintException("Reserved namespace "+name+" cannot be removed.");
}
@@ -210,7 +215,7 @@ public class TableNamespaceManager {
try {
tableCount = masterServices.listTableDescriptorsByNamespace(name).size();
} catch (FileNotFoundException fnfe) {
- throw new ConstraintException("namespace " + name + " does not exist");
+ throw new NamespaceNotFoundException(name);
}
if (tableCount > 0) {
throw new ConstraintException("Only empty namespaces can be removed. " +
Modified: hbase/branches/0.96/hbase-server/src/test/java/org/apache/hadoop/hbase/TestNamespace.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.96/hbase-server/src/test/java/org/apache/hadoop/hbase/TestNamespace.java?rev=1523907&r1=1523906&r2=1523907&view=diff
==============================================================================
--- hbase/branches/0.96/hbase-server/src/test/java/org/apache/hadoop/hbase/TestNamespace.java (original)
+++ hbase/branches/0.96/hbase-server/src/test/java/org/apache/hadoop/hbase/TestNamespace.java Tue Sep 17 05:45:03 2013
@@ -18,7 +18,17 @@
*/
package org.apache.hadoop.hbase;
-import com.google.common.collect.Sets;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.fs.FileSystem;
@@ -33,7 +43,9 @@ import org.apache.hadoop.hbase.util.Byte
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import com.google.common.collect.Sets;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
@@ -117,8 +129,8 @@ public class TestNamespace {
}
//verify system tables aren't listed
assertEquals(0, admin.listTables().length);
-
- //Try creating default and system namespaces.
+
+ //Try creating default and system namespaces.
boolean exceptionCaught = false;
try {
admin.createNamespace(NamespaceDescriptor.DEFAULT_NAMESPACE);
@@ -139,7 +151,7 @@ public class TestNamespace {
assertTrue(exceptionCaught);
}
}
-
+
@Test
public void testDeleteReservedNS() throws Exception {
boolean exceptionCaught = false;
@@ -192,7 +204,7 @@ public class TestNamespace {
LOG.info(testName);
byte[] tableName = Bytes.toBytes("my_table");
- byte[] tableNameFoo = Bytes.toBytes(nsName+".my_table");
+ byte[] tableNameFoo = Bytes.toBytes(nsName+":my_table");
//create namespace and verify
admin.createNamespace(NamespaceDescriptor.create(nsName).build());
TEST_UTIL.createTable(tableName, Bytes.toBytes(nsName));
@@ -219,8 +231,8 @@ public class TestNamespace {
desc.addFamily(colDesc);
try {
admin.createTable(desc);
- fail("Expected no namespace constraint exception");
- } catch (ConstraintException ex) {
+ fail("Expected no namespace exists exception");
+ } catch (NamespaceNotFoundException ex) {
}
//create table and in new namespace
admin.createNamespace(NamespaceDescriptor.create(nsName).build());
@@ -262,7 +274,7 @@ public class TestNamespace {
HColumnDescriptor colDesc = new HColumnDescriptor("cf1");
desc.addFamily(colDesc);
admin.createTable(desc);
- assertTrue(admin.listTables().length == 1);
+ assertTrue(admin.listTables().length == 1);
admin.disableTable(desc.getTableName());
admin.deleteTable(desc.getTableName());
}
@@ -307,4 +319,109 @@ public class TestNamespace {
ZooKeeperWatcher.namespaceZNode).size());
}
+ @Test(timeout = 60000)
+ public void testNamespaceOperations() throws IOException {
+ admin.createNamespace(NamespaceDescriptor.create(prefix + "ns1").build());
+ admin.createNamespace(NamespaceDescriptor.create(prefix + "ns2").build());
+
+ // create namespace that already exists
+ runWithExpectedException(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ admin.createNamespace(NamespaceDescriptor.create(prefix + "ns1").build());
+ return null;
+ }
+ }, NamespaceExistException.class);
+
+ // create a table in non-existing namespace
+ runWithExpectedException(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("non_existing_namespace", "table1"));
+ htd.addFamily(new HColumnDescriptor("family1"));
+ admin.createTable(htd);
+ return null;
+ }
+ }, NamespaceNotFoundException.class);
+
+ // get descriptor for existing namespace
+ admin.getNamespaceDescriptor(prefix + "ns1");
+
+ // get descriptor for non-existing namespace
+ runWithExpectedException(new Callable<NamespaceDescriptor>() {
+ @Override
+ public NamespaceDescriptor call() throws Exception {
+ return admin.getNamespaceDescriptor("non_existing_namespace");
+ }
+ }, NamespaceNotFoundException.class);
+
+ // delete descriptor for existing namespace
+ admin.deleteNamespace(prefix + "ns2");
+
+ // delete descriptor for non-existing namespace
+ runWithExpectedException(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ admin.deleteNamespace("non_existing_namespace");
+ return null;
+ }
+ }, NamespaceNotFoundException.class);
+
+ // modify namespace descriptor for existing namespace
+ NamespaceDescriptor ns1 = admin.getNamespaceDescriptor(prefix + "ns1");
+ ns1.setConfiguration("foo", "bar");
+ admin.modifyNamespace(ns1);
+
+ // modify namespace descriptor for non-existing namespace
+ runWithExpectedException(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ admin.modifyNamespace(NamespaceDescriptor.create("non_existing_namespace").build());
+ return null;
+ }
+ }, NamespaceNotFoundException.class);
+
+ // get table descriptors for existing namespace
+ HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(prefix + "ns1", "table1"));
+ htd.addFamily(new HColumnDescriptor("family1"));
+ admin.createTable(htd);
+ HTableDescriptor[] htds = admin.listTableDescriptorsByNamespace(prefix + "ns1");
+ assertNotNull("Should have not returned null", htds);
+ assertEquals("Should have returned non-empty array", 1, htds.length);
+
+ // get table descriptors for non-existing namespace
+ runWithExpectedException(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ admin.listTableDescriptorsByNamespace("non_existing_namespace");
+ return null;
+ }
+ }, NamespaceNotFoundException.class);
+
+ // get table names for existing namespace
+ TableName[] tableNames = admin.listTableNamesByNamespace(prefix + "ns1");
+ assertNotNull("Should have not returned null", tableNames);
+ assertEquals("Should have returned non-empty array", 1, tableNames.length);
+
+ // get table names for non-existing namespace
+ runWithExpectedException(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ admin.listTableNamesByNamespace("non_existing_namespace");
+ return null;
+ }
+ }, NamespaceNotFoundException.class);
+
+ }
+
+ private static <V, E> void runWithExpectedException(Callable<V> callable, Class<E> exceptionClass) {
+ try {
+ callable.call();
+ } catch(Exception ex) {
+ Assert.assertEquals(exceptionClass, ex.getClass());
+ return;
+ }
+ fail("Should have thrown exception " + exceptionClass);
+ }
+
}