001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.procedure2;
019
020import java.lang.Thread.UncaughtExceptionHandler;
021import java.util.Set;
022import java.util.concurrent.TimeUnit;
023import org.apache.hadoop.hbase.HBaseClassTestRule;
024import org.apache.hadoop.hbase.HBaseCommonTestingUtil;
025import org.apache.hadoop.hbase.testclassification.MasterTests;
026import org.apache.hadoop.hbase.testclassification.SmallTests;
027import org.junit.After;
028import org.junit.Before;
029import org.junit.ClassRule;
030import org.junit.Rule;
031import org.junit.Test;
032import org.junit.experimental.categories.Category;
033import org.junit.rules.ExpectedException;
034
035/**
036 * Make sure the {@link UncaughtExceptionHandler} will be called when there are unchecked exceptions
037 * thrown in the task.
038 * <p/>
039 * See HBASE-21875 and HBASE-21890 for more details.
040 */
041@Category({ MasterTests.class, SmallTests.class })
042public class TestRemoteProcedureDispatcherUncaughtExceptionHandler {
043
044  private static HBaseCommonTestingUtil UTIL = new HBaseCommonTestingUtil();
045
046  @ClassRule
047  public static final HBaseClassTestRule CLASS_RULE =
048    HBaseClassTestRule.forClass(TestRemoteProcedureDispatcherUncaughtExceptionHandler.class);
049
050  private static final class ExceptionHandler implements UncaughtExceptionHandler {
051
052    private Throwable error;
053
054    @Override
055    public synchronized void uncaughtException(Thread t, Throwable e) {
056      this.error = e;
057      notifyAll();
058    }
059
060    public synchronized void get() throws Throwable {
061      while (error == null) {
062        wait();
063      }
064      throw error;
065    }
066  }
067
068  private static final class Dispatcher extends RemoteProcedureDispatcher<Void, Integer> {
069
070    private final UncaughtExceptionHandler handler;
071
072    public Dispatcher(UncaughtExceptionHandler handler) {
073      super(UTIL.getConfiguration());
074      this.handler = handler;
075    }
076
077    @Override
078    protected UncaughtExceptionHandler getUncaughtExceptionHandler() {
079      return handler;
080    }
081
082    @Override
083    protected void remoteDispatch(Integer key, Set<RemoteProcedure> operations) {
084    }
085
086    @Override
087    protected void abortPendingOperations(Integer key, Set<RemoteProcedure> operations) {
088    }
089  }
090
091  @Rule
092  public ExpectedException thrown = ExpectedException.none();
093
094  private ExceptionHandler handler;
095
096  private Dispatcher dispatcher;
097
098  @Before
099  public void setUp() {
100    handler = new ExceptionHandler();
101    dispatcher = new Dispatcher(handler);
102    dispatcher.start();
103  }
104
105  @After
106  public void tearDown() {
107    dispatcher.stop();
108    dispatcher = null;
109    handler = null;
110  }
111
112  @Test
113  public void testSubmit() throws Throwable {
114    String message = "inject error";
115    thrown.expect(RuntimeException.class);
116    thrown.expectMessage(message);
117    dispatcher.submitTask(new Runnable() {
118
119      @Override
120      public void run() {
121        throw new RuntimeException(message);
122      }
123    });
124    handler.get();
125  }
126
127  @Test
128  public void testDelayedSubmit() throws Throwable {
129    String message = "inject error";
130    thrown.expect(RuntimeException.class);
131    thrown.expectMessage(message);
132    dispatcher.submitTask(new Runnable() {
133
134      @Override
135      public void run() {
136        throw new RuntimeException(message);
137      }
138    }, 100, TimeUnit.MILLISECONDS);
139    handler.get();
140  }
141}