diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorCheckable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorCheckable.java new file mode 100644 index 0000000..bbf1881 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorCheckable.java @@ -0,0 +1,40 @@ +/** + * 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.server.error; + +/** + * Check for errors to a given process + * @param Type of error that this monitor throws if it finds an error + * @see ErrorListenable + */ +public interface ErrorCheckable { + + /** + * Checks to see if any process to which the monitor is bound has created an error that would + * cause a failure. + * @throws E if there has been an error, allowing a fail-fast mechanism + */ + public void failOnError() throws E; + + /** + * Non-exceptional form of {@link #failOnError()}. Checks to see if any process to which the + * monitor is bound has created an error that would cause a failure. + * @return true if there has been an error,false otherwise + */ + public boolean checkForError(); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorHandlingUtils.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorHandlingUtils.java new file mode 100644 index 0000000..15dbde7 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorHandlingUtils.java @@ -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.server.error; + +/** + * Utility methods for using the inter-object error handling + */ +public class ErrorHandlingUtils { + + /** + * Determine if the stack trace contains the given calling class + * @param stack trace to examine + * @param clazz Class to search for + * @return true if the stack contains the calling class + */ + public static boolean stackContainsClass(StackTraceElement[] stack, Class clazz) { + String name = clazz.getName(); + for (StackTraceElement elem : stack) { + if (elem.getClassName().equals(name)) return true; + } + return false; + } + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorListenable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorListenable.java new file mode 100644 index 0000000..6e0e79d --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorListenable.java @@ -0,0 +1,31 @@ +/** + * 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.server.error; + +/** + * Generate errors that can be listened for from other processes via {@link ErrorListener}. + */ +public interface ErrorListenable { + + /** + * Listen for failures to a given process + * @param visitor + * @param errorable listener for the errors + */ + public void addErrorListener(ErrorVisitor visitor, L errorable); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorListener.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorListener.java new file mode 100644 index 0000000..94a2bd9 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorListener.java @@ -0,0 +1,33 @@ +/** + * 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.server.error; + +/** + * Listen for errors on a process or operation + * @param Type of exception that is expected + */ +public interface ErrorListener { + + /** + * Receive an error + * @param message reason for the error + * @param e exception causing the error + * @param info general information about the error + */ + public void receiveError(String message, E e, Object... info); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorMonitorable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorMonitorable.java new file mode 100644 index 0000000..66d8977 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorMonitorable.java @@ -0,0 +1,30 @@ +/** + * 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.server.error; + + +/** + * A basic join interface for an object that one can check for errors and an object that you pass + * pass an error. The error doesn't need to be propagated necessarily, though it might be if it is + * an {@link ErrorOrchestratable} + * @param Type of exception thrown when an error is found when calling {@link #failOnError()} + */ +public interface ErrorMonitorable extends ErrorCheckable, ErrorListener, + NamedMonitorable { + +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorOrchestratable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorOrchestratable.java new file mode 100644 index 0000000..ebee994 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorOrchestratable.java @@ -0,0 +1,29 @@ +/** + * 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.server.error; + +/** + * Join interface for an object that can receive error notifications and also propagate that error + * notification to registered listeners. + * @param Type of exception thrown when an error is found/passed + * @see ErrorListenable + * @see ErrorListener + */ +public interface ErrorOrchestratable extends ErrorListenable, + GenericErrorListenable, ErrorListener, NamedMonitorable { +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorVisitor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorVisitor.java new file mode 100644 index 0000000..b732f33 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ErrorVisitor.java @@ -0,0 +1,36 @@ +/** + * 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.server.error; + + +/** + * Simple visitor interface to update an error listener with an error notification + * @see ErrorOrchestratable + * @param Type of listener to update + */ +public interface ErrorVisitor { + + /** + * Visit the listener with the given error + * @param listener listener to update + * @param message error message + * @param e exception that caused the error + * @param info general information about the error + */ + public void visit(T listener, String message, Exception e, Object... info); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/FaultInjector.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/FaultInjector.java new file mode 100644 index 0000000..fe946d7 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/FaultInjector.java @@ -0,0 +1,44 @@ +/** + * 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.server.error; + +import org.apache.hadoop.hbase.server.error.impl.ErrorOrchestratorFactory; +import org.apache.hadoop.hbase.util.Pair; + +/** + * Inject faults when classes check to see if an error occurs. + *

+ * Can be added to any monitoring via {@link ErrorOrchestratorFactory#addFaultInjector(FaultInjector)} + * @see ErrorListener + * @see ErrorCheckable + * @param Type of exception that the corresponding {@link ErrorListener} and + * {@link ErrorListenable} are expecting + */ +public interface FaultInjector { + + /** + * Called by the specified class whenever checking for process errors. Care needs to be taken when + * using fault injectors to pass the correct size array back or the received error in the listener + * could not receive the correct number of argument and throw an error. + * @param trace full stack trace of the call to check for an error + * @return the information about the fault that should be returned if there was a fault, + * null otherwise + */ + public Pair injectFault(StackTraceElement[] trace); + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/GenericErrorListenable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/GenericErrorListenable.java new file mode 100644 index 0000000..10eb49b --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/GenericErrorListenable.java @@ -0,0 +1,30 @@ +/** + * 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.server.error; + +/** + * Add a generic listener for error notifications.This is complementary to an + * {@link ErrorListenable} and an {@link ErrorListener}. + *

+ * Internally, this is just a marker interface for the error handling framework to allow listeners + * to monitor each other. + * @param type of exception the error listener expects + */ +public interface GenericErrorListenable { + public void addErrorListener(ErrorListener errorListener); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/NamedMonitorable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/NamedMonitorable.java new file mode 100644 index 0000000..f36769f --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/NamedMonitorable.java @@ -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.server.error; + +/** + * Associate a monitor with a name to help with debugging. + */ +public interface NamedMonitorable { + + /** + * @param name name to use when logging about the monitor + */ + public void setName(String name); + + /** + * @return descriptive name for the monitor, used in logging. + */ + public String getName(); + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ProcessTimer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ProcessTimer.java new file mode 100644 index 0000000..68e2506 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/ProcessTimer.java @@ -0,0 +1,94 @@ +/** + * 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.server.error; + +import java.util.Timer; +import java.util.TimerTask; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.server.error.exception.TaskTimeoutException; + +/** + * Time a given process/operation and report a failure if the elapsed time exceeds the max allowed + * time. + */ +public class ProcessTimer { + + private static final Log LOG = LogFactory.getLog(ProcessTimer.class); + + private final long maxTime; + private volatile boolean complete; + private final Timer timer; + private TimerTask timerTask; + private long start; + + /** + * Create a generic timer for a task/process. + * @param listener listener to notify if the process times out + * @param maxTime max allowed running time for the process. Timer starts on calls to + * {@link #start()} + * @param info information about the process to pass along if the timer expires + */ + @SuppressWarnings("rawtypes") + public ProcessTimer(final ErrorListener listener, final long maxTime, final Object... info) { + this.maxTime = maxTime; + timer = new Timer(); + timerTask = new TimerTask() { + @SuppressWarnings("unchecked") + @Override + public void run() { + if (!ProcessTimer.this.complete) { + long end = System.currentTimeMillis(); + listener.receiveError("Timeout elapsed!", new TaskTimeoutException(start, end, + maxTime), info); + } + } + }; + } + + /** + * For all time forward, do not throw an error because the process has completed. + */ + public void complete() { + LOG.debug("Marking timer as complete - no error notifications will be received for this timer."); + this.complete = true; + this.timer.cancel(); + } + + /** + * Start a timer to fail a process if it takes longer than the expected time to complete. + * Non-blocking call. + */ + public void start() { + LOG.debug("Scheduling process timer to run in: " + maxTime + " ms"); + timer.schedule(timerTask, maxTime); + this.start = System.currentTimeMillis(); + } + + /** + * Trigger the timer immediately. + *

+ * Exposed for testing. + */ + public void trigger() { + LOG.debug("Triggering timer immediately!"); + this.timerTask.run(); + this.timer.cancel(); + } +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorDispatchable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorDispatchable.java new file mode 100644 index 0000000..47b420e --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorDispatchable.java @@ -0,0 +1,30 @@ +/** + * 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.server.error; + + +/** + * An error monitor (keeps track of the first it error received) and an error orchestrator (updates + * listeners when it gets an error notification). + * @param generic type to receive errors + * @param type of exception to receive + */ +public interface TypedErrorDispatchable extends + TypedErrorOrchestratable, ErrorMonitorable { + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorListenable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorListenable.java new file mode 100644 index 0000000..ebe42e3 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorListenable.java @@ -0,0 +1,33 @@ +/** + * 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.server.error; + +/** + * An error listenable that will notify the given type when it receives an error via an + * {@link ErrorVisitor} + * @param type to receive error notifications + * @see ErrorListenable + * @see ErrorVisitor + */ +public interface TypedErrorListenable{ + /** + * Add a typed error listener that will be visited by the {@link ErrorVisitor} for this dispatcher + * @param errorable listener for error notifications + */ + public void addErrorListener(T errorable); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorOrchestratable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorOrchestratable.java new file mode 100644 index 0000000..4add2af --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/TypedErrorOrchestratable.java @@ -0,0 +1,30 @@ +/** + * 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.server.error; + +/** + * An {@link ErrorOrchestratable} that will also allow error generic error listeners that convert + * error notifications via an {@link ErrorVisitor}. + * @see ErrorVisitor + * @see TypedErrorListenable + * @param Type of error listener that should listener for errors + * @param Type of exception to expect + */ +public interface TypedErrorOrchestratable extends ErrorOrchestratable, + TypedErrorListenable { +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/exception/TaskTimeoutException.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/exception/TaskTimeoutException.java new file mode 100644 index 0000000..a86749c --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/exception/TaskTimeoutException.java @@ -0,0 +1,49 @@ +/** + * 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.server.error.exception; + +import org.apache.hadoop.hbase.server.error.ProcessTimer; + +/** + * Exception for a timeout of a task. + * @see ProcessTimer + */ +@SuppressWarnings("serial") +public class TaskTimeoutException extends Exception { + + public TaskTimeoutException() { + super(); + } + + public TaskTimeoutException(long start, long end, long allowed) { + this("Timeout elapsed! Start:" + start + ", End:" + end + ", diff:" + (end - start) + ", max:" + + allowed); + } + + public TaskTimeoutException(String msg) { + super(msg); + } + + public TaskTimeoutException(String message, Throwable cause) { + super(message, cause); + } + + public TaskTimeoutException(Throwable cause) { + super(cause); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/exception/UnknownErrorException.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/exception/UnknownErrorException.java new file mode 100644 index 0000000..7d4beb1 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/exception/UnknownErrorException.java @@ -0,0 +1,29 @@ +/** + * 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.server.error.exception; + +import org.apache.hadoop.hbase.server.error.impl.ErrorMonitor; + +/** + * Exception when an {@link ErrorMonitor} doens't have an Exception when it receives an + * error. + */ +@SuppressWarnings("serial") +public class UnknownErrorException extends RuntimeException { + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/BridgedErrorOrchestrator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/BridgedErrorOrchestrator.java new file mode 100644 index 0000000..28cb2b9 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/BridgedErrorOrchestrator.java @@ -0,0 +1,52 @@ +/** + * 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.server.error.impl; + +import org.apache.hadoop.hbase.server.error.ErrorVisitor; +import org.apache.hadoop.hbase.server.error.TypedErrorOrchestratable; + +/** + * Helper class to bridge dispatching of errors and listening for errors for generic errror + * listeners + * @param Type of error listener that should listener for errors + * @param Type of exception to expect + */ +public class BridgedErrorOrchestrator extends ErrorOrchestrator implements + TypedErrorOrchestratable { + + protected final ErrorVisitor visitor; + + /** + * Create an error dispatcher bound to a single type of listener. Internally, uses a generic error + * dispatcher to handle passing along errors. + * @param visitor error visitor to use to visit all typed listeners + */ + public BridgedErrorOrchestrator( + ErrorVisitor visitor) { + this.visitor = visitor; + } + + public ErrorVisitor getVisitor() { + return this.visitor; + } + + @Override + public void addErrorListener(T errorable) { + addErrorListener(this.visitor, errorable); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorDispatcherFactory.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorDispatcherFactory.java new file mode 100644 index 0000000..d3b7601 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorDispatcherFactory.java @@ -0,0 +1,54 @@ +/** + * 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.server.error.impl; + +import java.util.List; + +import org.apache.hadoop.hbase.server.error.ErrorVisitor; +import org.apache.hadoop.hbase.server.error.FaultInjector; +import org.apache.hadoop.hbase.server.error.TypedErrorDispatchable; + +/** + * Generic error dispatcher factory that just creates an error dispatcher on request (potentially + * wrapping with an error injector via the {@link ErrorOrchestratorFactory}). + * @param Type of generic error listener the dispatchers should handle + * @param Expected error the dispatcher should handle + * @see ErrorOrchestratorFactory + */ +public class ErrorDispatcherFactory extends + ErrorOrchestratorFactory, T, E> { + + /** + * @param visitor + */ + public ErrorDispatcherFactory(ErrorVisitor visitor) { + super(visitor); + } + + @Override + protected TypedErrorDispatchable buildErrorMonitor(ErrorVisitor visitor) { + return new TypedErrorDispatcher(visitor); + } + + @Override + protected TypedErrorDispatchable wrapWithInjector(TypedErrorDispatchable dispatcher, + List> injectors) { + return new InjectingErrorMonitor, T, E>(dispatcher, injectors); + } + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorMonitor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorMonitor.java new file mode 100644 index 0000000..c0a3e5b --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorMonitor.java @@ -0,0 +1,82 @@ +/** + * 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.server.error.impl; + +import java.util.Arrays; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.server.error.ErrorMonitorable; +import org.apache.hadoop.hbase.server.error.exception.UnknownErrorException; + +/** + * Simple error monitor that keeps track of whether of its failure state, and the exception that + * should be thrown based on the received error. + *

+ * Ensures that an error is not propagated if an error has already been recieved, ensuring that you + * don't have infinite error propagation. + *

+ * Thread-safe. + * @param Type of exception to throw when calling {@link #failOnError()} + */ +public class ErrorMonitor extends NamedMonitor implements + ErrorMonitorable { + + private static final Log LOG = LogFactory.getLog(ErrorMonitor.class); + + private volatile boolean error = false; + protected E exception; + + /** + * Create an error monitor with a generic error name + */ + public ErrorMonitor() { + super("generic-error-monitor"); + } + + @Override + public void failOnError() throws E { + if (checkForError()) { + if (exception == null) throw new UnknownErrorException(); + throw exception; + } + } + + @Override + public boolean checkForError() { + return this.error; + } + + @Override + public void receiveError(String message, E e, Object... info) { + LOG.error(getNamePrefixForLog() + "Got an error:" + message + + ", info:" + Arrays.toString(info)); + receiveInternalError(e); + } + + /** + * Receive an error notification from internal sources. Can be used by subclasses to set an errors + * @param e exception that caused the error (can be null). + */ + protected void receiveInternalError(E e) { + // if we already got the error or we received the error + if (this.error) return; + this.error = true; + this.exception = e; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorOrchestrator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorOrchestrator.java new file mode 100644 index 0000000..22b9fa8 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorOrchestrator.java @@ -0,0 +1,104 @@ +/** + * 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.server.error.impl; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.server.error.ErrorListener; +import org.apache.hadoop.hbase.server.error.ErrorOrchestratable; +import org.apache.hadoop.hbase.server.error.ErrorVisitor; +import org.apache.hadoop.hbase.util.Pair; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimaps; + +/** + * The dispatcher acts as a central point of control of error handling. Any errors from the + * dispatcher get passed directly to the listeners. Likewise, any errors from the listeners get + * passed to the dispatcher and then back to any listeners. + *

+ * Any error listener (generic or typed) added will only be weakly referenced, allowing easier + * management of multiple listeners. + *

+ * A single error dispatcher should be used for each set of processes to monitor. This allows for a + * single source of truth for dispatch between all the interested parties. + * @param Type of exception to expect when receiving errors + */ +public class ErrorOrchestrator extends NamedMonitor implements + ErrorOrchestratable { + + private static final Log LOG = LogFactory.getLog(ErrorOrchestrator.class); + private final ForwardingErrorVisitor, E> genericVisitor = new ForwardingErrorVisitor, E>(); + protected final ListMultimap, WeakReference> listeners; + { + // needed since java typing isn't nice with static creation + ListMultimap, WeakReference> listeners = ArrayListMultimap.create(); + this.listeners = Multimaps.synchronizedListMultimap(listeners); + } + + public ErrorOrchestrator() { + super("generic-error-dispatcher"); + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public synchronized void receiveError(String message, E e, Object... info) { + // update all the listeners with the passed error + LOG.debug(getNamePrefixForLog() + " Recieved error, notifying listeners..."); + List, WeakReference>> toRemove = new ArrayList, WeakReference>>(); + for (Entry, WeakReference> entry : listeners.entries()) { + Object o = entry.getValue().get(); + if(o == null){ + // if the listener doesn't have a reference, then drop it from the list + // need to copy this over b/c guava is finicky with the entries + toRemove.add(new Pair, WeakReference>(entry.getKey(), entry.getValue())); + continue; + } + // otherwise notify the listener that we had a failure + ((ErrorVisitor) entry.getKey()).visit(o, message, e, info); + } + + // cleanup all visitors that aren't referenced anymore + if (toRemove.size() > 0) LOG.debug(getNamePrefixForLog() + " Cleaning up entries."); + for (Pair, WeakReference> entry : toRemove) { + this.listeners.remove(entry.getFirst(), entry.getSecond()); + } + } + + /** + * Add a generic error listener to hear about errors. This is generally used internally to + * establish error handling hierarchies. + * @param listener generic listener to listen for error notifications + */ + @Override + public void addErrorListener(ErrorListener listener) { + if (listener == null) return; + listeners.put(genericVisitor, new WeakReference>(listener)); + } + + @Override + public void addErrorListener(ErrorVisitor visitor, L errorable) { + this.listeners.put(visitor, new WeakReference(errorable)); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorOrchestratorFactory.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorOrchestratorFactory.java new file mode 100644 index 0000000..8b6b6a2 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ErrorOrchestratorFactory.java @@ -0,0 +1,107 @@ +/** + * 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.server.error.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.hadoop.hbase.server.error.ErrorCheckable; +import org.apache.hadoop.hbase.server.error.ErrorMonitorable; +import org.apache.hadoop.hbase.server.error.ErrorOrchestratable; +import org.apache.hadoop.hbase.server.error.ErrorVisitor; +import org.apache.hadoop.hbase.server.error.FaultInjector; + +/** + * Error factor that produces an error orchestrator, potentially wrapped with a + * {@link FaultInjector}. + * @param type for {@link ErrorOrchestratable} that should be used + * @param Type of error listener that the dispatcher from this factory can communicate + * @param type of exception that will be thrown on checks of + * {@link ErrorCheckable#failOnError()} from created error monitors + */ +public abstract class ErrorOrchestratorFactory, T, E extends Exception> { + private static final List> faults = new ArrayList>(); + + /** + * Add a fault injector that will run on checks of the {@link ErrorCheckable} generated by this + * factory. To ensure that faults are injected, this must be called before the the monitor is + * created via {@link #createErrorDispatcher()}. + *

+ * Exposed for TESTING. + * @param injector + */ + public static void addFaultInjector(FaultInjector injector) { + faults.add(injector); + } + + /** + * Complement to {@link #addFaultInjector(FaultInjector)} - removes any existing fault injectors + * set for the factory. + *

+ * Exposed for TESTING. + */ + public static void clearFaults() { + faults.clear(); + } + + protected final ErrorVisitor visitor; + + public ErrorOrchestratorFactory(ErrorVisitor visitor) { + this.visitor = visitor; + } + + /** + * Create a dispatcher with a specific visitor + * @param visitor visitor to pass on error notifications to bound error listeners + * @return an error dispatcher that is passes on errors to all listening objects + */ + public final D createErrorDispatcher(ErrorVisitor visitor) { + D monitor = buildErrorMonitor(visitor); + // wrap with a fault injector, if we need to + if (faults.size() > 0) { + return wrapWithInjector(monitor, faults); + } + return monitor; + } + + /** + * Create a dispatcher with a specific visitor. Uses the default visitor passed in the constructor + * @return an error dispatcher that is passes on errors to all listening objects + */ + public D createErrorDispatcher() { + return createErrorDispatcher(this.visitor); + } + + /** + * Build an error monitor. This will be wrapped via + * {@link #wrapWithInjector(ErrorMonitorable, List)} if there are fault injectors present. + * @return an error montior as needed. + */ + protected abstract D buildErrorMonitor(ErrorVisitor visitor); + + /** + * Wrap the built error monitor with an error injector. Subclasses should override if they need + * custom behavior, but you should have sure you know what you are doing or unexpected results + * could occur. + * @param dispatcher + * @param injectors + * @return + */ + protected abstract D wrapWithInjector(D dispatcher, + List> injectors); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ForwardingErrorVisitor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ForwardingErrorVisitor.java new file mode 100644 index 0000000..1fe0581 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/ForwardingErrorVisitor.java @@ -0,0 +1,36 @@ +/** + * 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.server.error.impl; + +import org.apache.hadoop.hbase.server.error.ErrorListener; +import org.apache.hadoop.hbase.server.error.ErrorVisitor; + +/** + * A simple error visitor that just forwards the received error onto an error visitor + * @param type of listener to notify + * @param type of exception expected for the listener + */ +public class ForwardingErrorVisitor, E extends Exception> implements + ErrorVisitor { + + @SuppressWarnings("unchecked") + @Override + public void visit(L listener, String message, Exception e, Object... info) { + listener.receiveError(message, (E) e, info); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/InjectingErrorMonitor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/InjectingErrorMonitor.java new file mode 100644 index 0000000..3fcac21 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/InjectingErrorMonitor.java @@ -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.server.error.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.hadoop.hbase.server.error.ErrorCheckable; +import org.apache.hadoop.hbase.server.error.ErrorOrchestratable; +import org.apache.hadoop.hbase.server.error.FaultInjector; +import org.apache.hadoop.hbase.server.error.TypedErrorDispatchable; +import org.apache.hadoop.hbase.server.error.impl.delegate.DelegatingErrorDispatcher; +import org.apache.hadoop.hbase.util.Pair; + +/** + * Error monitor that delegates for all methods, but wraps error checking to allow the fault + * injectors to have a chance to inject a fault into the running process + * @param {@link ErrorOrchestratable} to wrap for fault checking + * @param type of generic error listener that should be notified + * @param exception to be thrown on checks of {@link #failOnError()} + */ +public class InjectingErrorMonitor, T, E extends Exception> + extends DelegatingErrorDispatcher { + + private final List> faults; + + /** + * Wrap an error monitor with one that will inject faults on calls to {@link #checkForError()}. + * @param delegate base monitor to wrap + * @param faults injectors to run each time there is a check for an error + */ + @SuppressWarnings("unchecked") + public InjectingErrorMonitor(D delegate, List> faults) { + super(delegate); + // since we don't know the type of fault injector, we need to convert it. + // this is only used in tests, so throwing a class-cast here isn't too bad. + this.faults = new ArrayList>(faults.size()); + for (FaultInjector fault : faults) { + this.faults.add((FaultInjector) fault); + } + } + + @Override + public void failOnError() throws E { + // first fail if there is already an error + delegate.failOnError(); + // then check for an error via the update mechanism + if (this.checkForError()) delegate.failOnError(); + } + + /** + * Use the injectors to possibly inject an error into the delegate. Should call + * {@link ErrorCheckable#checkForError()} or {@link ErrorCheckable#failOnError()} after calling + * this method on return of true. + * @return true if an error found via injector or in the delegate, false + * otherwise + */ + @Override + public boolean checkForError() { + // if there are fault injectors, run them + if (faults.size() > 0) { + // get the caller of this method. Should be the direct calling class + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + for (FaultInjector injector : faults) { + Pair info = injector.injectFault(trace); + if (info != null) { + delegate.receiveError("Injected fail", info.getFirst(), info.getSecond()); + } + } + } + return delegate.checkForError(); + } +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/NamedMonitor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/NamedMonitor.java new file mode 100644 index 0000000..c6d7c5a --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/NamedMonitor.java @@ -0,0 +1,52 @@ +/** + * 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.server.error.impl; + +import org.apache.hadoop.hbase.server.error.NamedMonitorable; + +/** + * Base class for an error monitor with a name. + */ +public class NamedMonitor implements NamedMonitorable { + private String name; + + /** + * @param name Name of the monitor + */ + public NamedMonitor(String name) { + this.name = name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + + /** + * Get the name of the monitor for use in logging + * @return name of the monitor to be used as a prefix in log messages + */ + protected String getNamePrefixForLog() { + return this.getName() != null ? "(" + this.getName() + ")" : ""; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/TypedErrorDispatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/TypedErrorDispatcher.java new file mode 100644 index 0000000..1386ebd --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/TypedErrorDispatcher.java @@ -0,0 +1,70 @@ +/** + * 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.server.error.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.Abortable; +import org.apache.hadoop.hbase.server.error.ErrorVisitor; +import org.apache.hadoop.hbase.server.error.TypedErrorDispatchable; + +/** + * The dispatcher acts as a central point of control of error handling. Any errors from the + * dispatcher get passed directly to the listeners. Likewise, any errors from the listeners get + * passed to the dispatcher and then back to any listeners. + *

+ * This is different than an {@link ErrorOrchestrator} as it will only propagate an error + * once. This is useful, for instance, for informing multiple process in conjunction with an + * {@link Abortable}. + * @param generic error listener type to update + * @param Type of exception to throw when calling {@link #failOnError()} + */ +public class TypedErrorDispatcher extends BridgedErrorOrchestrator implements + TypedErrorDispatchable { + private static final Log LOG = LogFactory.getLog(TypedErrorDispatcher.class); + + private final ErrorMonitor monitor = new ErrorMonitor(); + + public TypedErrorDispatcher(ErrorVisitor visitor) { + super(visitor); + this.setName("single-error-dispatcher"); + } + + @Override + public synchronized void receiveError(String message, E e, Object... info) { + // if we already have an error, then ignore it + if (monitor.checkForError()) return; + + LOG.debug(getNamePrefixForLog() + "Accepting received error:" + message); + // mark that we got the error + monitor.receiveError(message, e, info); + + // notify all the listeners + super.receiveError(message, e, info); + } + + @Override + public void failOnError() throws E { + monitor.failOnError(); + } + + @Override + public boolean checkForError() { + return monitor.checkForError(); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/delegate/DelegatingErrorDispatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/delegate/DelegatingErrorDispatcher.java new file mode 100644 index 0000000..eeb1270 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/delegate/DelegatingErrorDispatcher.java @@ -0,0 +1,49 @@ +/** + * 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.server.error.impl.delegate; + +import org.apache.hadoop.hbase.server.error.TypedErrorDispatchable; + +/** + * Helper class for error monitor factories. + * @param Type of delegate to use + * @param type of generic error listener to update + * @param exception to expect for errors + */ +public class DelegatingErrorDispatcher, T, E extends Exception> + extends DelegatingErrorOrchestrator implements TypedErrorDispatchable { + + public DelegatingErrorDispatcher(D delegate) { + super(delegate); + } + + @Override + public void failOnError() throws E { + delegate.failOnError(); + } + + @Override + public boolean checkForError() { + return delegate.checkForError(); + } + + @Override + public void addErrorListener(T errorable) { + delegate.addErrorListener(errorable); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/delegate/DelegatingErrorOrchestrator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/delegate/DelegatingErrorOrchestrator.java new file mode 100644 index 0000000..398e313 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/server/error/impl/delegate/DelegatingErrorOrchestrator.java @@ -0,0 +1,63 @@ +/** + * 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.server.error.impl.delegate; + +import org.apache.hadoop.hbase.server.error.ErrorListener; +import org.apache.hadoop.hbase.server.error.ErrorOrchestratable; +import org.apache.hadoop.hbase.server.error.ErrorVisitor; + +/** + * Helper class for factories to wrap existing error handling functionality. + * @param type of delegate to delegate all functions + * @param General error listener to receive errors + * @param type of exception expected by the error listener + */ +public class DelegatingErrorOrchestrator, T, E extends Exception> + implements ErrorOrchestratable { + + protected final D delegate; + + public DelegatingErrorOrchestrator(D delegate) { + this.delegate = delegate; + } + + @Override + public void receiveError(String message, E e, Object... info) { + delegate.receiveError(message, e, info); + } + + @Override + public void setName(String name) { + delegate.setName(name); + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public void addErrorListener(ErrorVisitor visitor, L errorable) { + delegate.addErrorListener(visitor, errorable); + } + + @Override + public void addErrorListener(ErrorListener errorListener) { + delegate.addErrorListener(errorListener); + } +} \ No newline at end of file diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ErrorReceiver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ErrorReceiver.java new file mode 100644 index 0000000..daf11c5 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ErrorReceiver.java @@ -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.server.error.impl; + +import org.apache.hadoop.hbase.server.error.ErrorVisitor; + +/** + * Simple class that just gets an error and allows tester to check the error information + */ +public class ErrorReceiver { + public static final ErrorVisitor VISITOR = new ErrorVisitor() { + + @Override + public void visit(ErrorReceiver listener, String message, Exception e, Object... info) { + listener.receiveError(message, (String) info[0]); + } + }; + + String info; + public void receiveError(String msg, String info) { + this.info = info; + } +} \ No newline at end of file diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ErrorTestingUtils.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ErrorTestingUtils.java new file mode 100644 index 0000000..55f4459 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ErrorTestingUtils.java @@ -0,0 +1,41 @@ +/** + * 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.server.error.impl; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +/** + * Utility class for testing error propagation + */ +public class ErrorTestingUtils { + + /** + * Ensure the listener got the error notification with the given error information + * @param listener + * @param info + */ + public static void assertListenerReceivedError(SimpleErrorListener listener, Object... info) { + assertTrue("Listener did't get an error", listener.error); + assertArrayEquals("Listener had unexpected error info:" + Arrays.toString(listener.info), info, + listener.info); + } + +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ExceptionForErrorTesting.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ExceptionForErrorTesting.java new file mode 100644 index 0000000..d326bf6 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/ExceptionForErrorTesting.java @@ -0,0 +1,28 @@ +/** + * 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.server.error.impl; + +/** + * Exception thrown from the test + */ +@SuppressWarnings("serial") +public class ExceptionForErrorTesting extends Exception { + public ExceptionForErrorTesting(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/SimpleErrorListener.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/SimpleErrorListener.java new file mode 100644 index 0000000..c2ad419 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/SimpleErrorListener.java @@ -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.server.error.impl; + +import org.apache.hadoop.hbase.server.error.ErrorListener; + +/** + * Simple error listener that can be checked to see if it received an error ({@link #error}) and the + * information about the error received ({@link #info}). + */ +@SuppressWarnings("javadoc") +public class SimpleErrorListener implements ErrorListener { + + public boolean error = false; + public Object[] info = null; + + @Override + public void receiveError(String message, Exception e, Object... info) { + this.error = true; + this.info = info; + } + +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestErrorOrchestrator.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestErrorOrchestrator.java new file mode 100644 index 0000000..c41e975 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestErrorOrchestrator.java @@ -0,0 +1,68 @@ +/** + * 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.server.error.impl; + +import org.apache.hadoop.hbase.SmallTests; +import org.apache.hadoop.hbase.server.error.ProcessTimer; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Test that we propagate errors through an orchestrator as expected + */ +@Category(SmallTests.class) +public class TestErrorOrchestrator { + + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testErrorPropagation() { + + SimpleErrorListener listener1 = new SimpleErrorListener(); + SimpleErrorListener listener2 = new SimpleErrorListener(); + + ErrorOrchestrator monitor = new ErrorOrchestrator(); + + // add the listeners + monitor.addErrorListener(listener1); + monitor.addErrorListener(listener2); + + // create an artificial error + Object info = "info1"; + monitor.receiveError("Some error", new ExceptionForErrorTesting("error"), info); + + // make sure the listeners got the error + ErrorTestingUtils.assertListenerReceivedError(listener1, info); + ErrorTestingUtils.assertListenerReceivedError(listener2, info); + + // push another error, which should be passed to listeners + info = "info2"; + monitor.receiveError("another error", new ExceptionForErrorTesting("hello"), info); + // make sure we get the error in the listeners + ErrorTestingUtils.assertListenerReceivedError(listener1, info); + ErrorTestingUtils.assertListenerReceivedError(listener2, info); + + // now create a timer and check for that error + info = "timer"; + ProcessTimer timer = new ProcessTimer(monitor, 1000, info); + timer.start(); + timer.trigger(); + + ErrorTestingUtils.assertListenerReceivedError(listener1, info); + ErrorTestingUtils.assertListenerReceivedError(listener2, info); + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestFaultInjecting.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestFaultInjecting.java new file mode 100644 index 0000000..e8b1ca2 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestFaultInjecting.java @@ -0,0 +1,78 @@ +/** + * 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.server.error.impl; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.SmallTests; +import org.apache.hadoop.hbase.server.error.ErrorCheckable; +import org.apache.hadoop.hbase.server.error.ErrorHandlingUtils; +import org.apache.hadoop.hbase.server.error.FaultInjector; +import org.apache.hadoop.hbase.util.Pair; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Test that we can correctly inject faults for testing + */ +@Category(SmallTests.class) +public class TestFaultInjecting { + + private static final Log LOG = LogFactory.getLog(TestFaultInjecting.class); + + @Test + public void testSimpleFaultInjection() { + ErrorDispatcherFactory factory = new ErrorDispatcherFactory( + ErrorReceiver.VISITOR); + String info = "info"; + ErrorOrchestratorFactory.addFaultInjector(new StringFaultInjector(info)); + ErrorCheckable monitor = factory.createErrorDispatcher(); + + // test that we actual inject a fault + assertTrue("Monitor didn't get an injected error", monitor.checkForError()); + try { + monitor.failOnError(); + fail("Monitor didn't get an build an exception from the fault in the factory."); + } catch (ExceptionForErrorTesting e) { + LOG.debug("Correctly got an exception from the test!"); + } + } + + /** + * Fault injector that will always throw an error + */ + public static class StringFaultInjector implements FaultInjector { + private final String info; + + public StringFaultInjector(String info) { + this.info = info; + } + + @Override + public Pair injectFault(StackTraceElement[] trace) { + if (ErrorHandlingUtils.stackContainsClass(trace, TestFaultInjecting.class)) { + return new Pair(new ExceptionForErrorTesting( + "injected!"), new String[] { info }); + } + return null; + } + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestSingleErrorDispatcher.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestSingleErrorDispatcher.java new file mode 100644 index 0000000..0aacbe1 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/server/error/impl/TestSingleErrorDispatcher.java @@ -0,0 +1,95 @@ +/** + * 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.server.error.impl; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.SmallTests; +import org.apache.hadoop.hbase.server.error.ProcessTimer; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +/** + * Test using the single error dispatcher + */ +@Category(SmallTests.class) +public class TestSingleErrorDispatcher { + + private static final Log LOG = LogFactory.getLog(TestSingleErrorDispatcher.class); + @Test + public void testErrorPropagation() { + + SimpleErrorListener listener1 = new SimpleErrorListener(); + SimpleErrorListener listener2 = new SimpleErrorListener(); + + TypedErrorDispatcher, Exception> monitor = new TypedErrorDispatcher, Exception>( + new ForwardingErrorVisitor, Exception>()); + + // add the listeners + monitor.addErrorListener(listener1); + monitor.addErrorListener(listener2); + + // create an artificial error + Object info = "info1"; + monitor.receiveError("Some error", new ExceptionForErrorTesting("error"), info); + + // make sure the listeners got the error + ErrorTestingUtils.assertListenerReceivedError(listener1, info); + ErrorTestingUtils.assertListenerReceivedError(listener2, info); + + // make sure that we get an exception + try { + monitor.failOnError(); + fail("Monitor should have thrown an exception after getting error."); + } catch (Exception e) { + assertTrue("Got an unexcepted exception:" + e, e instanceof ExceptionForErrorTesting); + LOG.debug("Got the testing exception!"); + } + // push another error, but this shouldn't be passed to the listeners + monitor.receiveError("another error", new ExceptionForErrorTesting("hello"), + "shouldn't be found"); + // make sure we don't re-propagate the error + ErrorTestingUtils.assertListenerReceivedError(listener1, info); + ErrorTestingUtils.assertListenerReceivedError(listener2, info); + } + + @Test + public void testSingleDispatcherWithTimer() { + SimpleErrorListener listener1 = new SimpleErrorListener(); + SimpleErrorListener listener2 = new SimpleErrorListener(); + + TypedErrorDispatcher, Exception> monitor = new TypedErrorDispatcher, Exception>( + new ForwardingErrorVisitor, Exception>()); + + // add the listeners + monitor.addErrorListener(listener1); + monitor.addErrorListener(listener2); + + Object info = "message"; + ProcessTimer timer = new ProcessTimer(monitor, 1000, info); + timer.start(); + timer.trigger(); + + assertTrue("Monitor didn't get timeout", monitor.checkForError()); + ErrorTestingUtils.assertListenerReceivedError(listener1, info); + ErrorTestingUtils.assertListenerReceivedError(listener2, info); + } +}