All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nosqlbench.engine.api.activityapi.errorhandling.HashedErrorHandler Maven / Gradle / Ivy

Go to download

The engine API for nosqlbench; Provides the interfaces needed to build internal modules for the nosqlbench core engine

There is a newer version: 5.17.0
Show newest version
/*
 *
 *    Copyright 2016 jshook
 *    Licensed 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 io.nosqlbench.engine.api.activityapi.errorhandling;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Allow error handlers to be registered for any exception type, with an explicit handler
 * that will be called if no specific other handler is matched.
 *
 * This error handler will automatically cascade up the error type hierarchy of the reported
 * error to find any matching handlers. If none are found between the reported error type
 * and the upper type bound, inclusive, then the default error handler is called, which
 * simply rethrows an error indicating that no more suitable error handler was provided.
 *
 * If you need to have the default error handler trigger before a certain supertype of
 * allowable errors is traversed, then set the upper bound with {@link #setUpperBound(Class)}.
 *
 * You may also register named groups of exceptions for which you can set the handler in
 * a single call.
 *
 * 

Type Parameters

* * The type parameter R represents a generic return type that is returned by a handler. * This is borrowed directly from the base error handler type {@link CycleErrorHandler} * from which this aggregating handler is built. R can be any type that makes sense * for your particular error handling logic. For example, a Boolean return type can * be used to signal whether down-chain handlers should be executed or not. * *

Patterns

* Some of the methods support matching exceptions by substring or regex. The patterns * are applied to the {@link Class#getSimpleName()} version of the classname. Further, * simple substrings consisting only of word characters are considered shortcuts for * single Throwable types and thus throw an exception if more than one is matched. * Patterns that contain non-word characters allow for bulk management. * * @param The subtype bound of exception to allow exception handlers for. * @param The result type that will be produced by these error handlers. */ public class HashedErrorHandler implements CycleErrorHandler { private final static Logger logger = LogManager.getLogger(HashedErrorHandler.class); private final CycleErrorHandler DEFAULT_defaultHandler = (cycle, error, errMsg) -> { throw new RuntimeException("no handler defined for type " + error.getClass() + " in cycle " + cycle + ", " + errMsg); }; private Class upperBound = Throwable.class; private final Map>> errorGroups = new ConcurrentHashMap<>(); private final Map, CycleErrorHandler> handlers = new ConcurrentHashMap<>(); private final Set> validClasses = new HashSet<>(); private CycleErrorHandler defaultHandler = DEFAULT_defaultHandler; /** * Set a group name for a set of classes. If the classes in the * group are not already in the list of recognized classes, then * they are added as well. * * @param groupName the name that the group will be referred to as * @param exceptions the set of exceptions to include in the group */ @java.lang.SafeVarargs public final void setGroup(String groupName, Class... exceptions) { this.errorGroups.put(groupName, new HashSet<>(Arrays.asList(exceptions))); this.addValidClasses(exceptions); } public Set> getGroup(String groupName) { return errorGroups.get(groupName); } /** * Set the error handler for the specified class, and any subclasses of it. * * @param errorHandler The error handler to be called when this class or any subclasses * that do not have their own more specific handler. * @param errorClasses The set of classes to set the handler for */ @java.lang.SafeVarargs public final synchronized void setHandlerForClasses( CycleErrorHandler errorHandler, Class... errorClasses ) { for (Class errorClass : errorClasses) { logger.debug("handling " + errorClass.getSimpleName() + " with " + errorHandler); handlers.put(errorClass, errorHandler); } } /** * Set the error handler for a named group of exception classes. * * @param errorHandler The error handler to be called when this class or any * subclasses that do not have their own more specific handler. * @param groupName The named group of exception classes * @throws RuntimeException if the group name is not found */ public final synchronized void setHandlerForGroup(String groupName, CycleErrorHandler errorHandler) { Set> classes = errorGroups.get(groupName); if (classes == null) { throw new RuntimeException("Group name '" + groupName + "' was not found."); } for (Class errorClass : classes) { setHandlerForClasses(errorHandler, errorClass); } } /** * Find the matching classes from the recognized classes, and then * set the handler for all of them. If the pattern includes only * word characters, then it is assumed to be a substring, and only * one match is allowed. If the pattern includes any non-word * characters, then it is presumed to be a regex, for which multiple * classes are allowed to be matched. * * @param pattern A substring or regex for the class names * @param errorHandler the error handler to be registered for the classes */ public final synchronized void setHandlerForPattern( String pattern, CycleErrorHandler errorHandler ) { this.findValidClasses(pattern).forEach( c -> setHandlerForClasses(errorHandler, c) ); } /** * Unset all class handlers. This does not reset the default handler. */ public final synchronized void resetAllClassHandlers() { handlers.clear(); } /** * Return the current list of active handler assignments. * @return an unmodifiable {@link Map} of {@link Class} to {@link CycleErrorHandler}. */ public final synchronized Map, CycleErrorHandler> getHandlers() { return Collections.unmodifiableMap(handlers); } /** * Add to the set of valid classes that will be used when searching for a class * by name. * * @param validClasses The classes that this error handler will search */ @java.lang.SafeVarargs public final synchronized void addValidClasses(Class... validClasses) { this.validClasses.addAll(Arrays.asList(validClasses)); } public Set> getValidClasses() { return Collections.unmodifiableSet(validClasses); } /** * Set the default handler that gets called on any exceptions that do not match a class * or super-class specific handler. * * @param errorHandler The error handler to be called as a last resort. * @return this HashedErrorHandler, for method chaining */ public HashedErrorHandler setDefaultHandler(CycleErrorHandler errorHandler) { Objects.requireNonNull(errorHandler); defaultHandler = errorHandler; return this; } /** * Sets the uppper bound on the Throwable type that you want to consider when * walking up the class hierarchy to find a handled supertype. By default, * this is simply {@link Throwable}. If the set of types that should be * handled directly are more limited than this, you can cause the default handler * to trigger when the upper bound type is found if the traversal gets that far. * * @param upperBound The Throwable subtype which is the lowest subtype to onAfterOpStop * @return this, for method chaining. */ public HashedErrorHandler setUpperBound(Class upperBound) { this.upperBound = upperBound; return this; } private List> findValidClasses(String partialClassName) { boolean requireOne = true; Pattern p = null; if (partialClassName.matches("^\\w+$")) { p = Pattern.compile("^.*" + partialClassName + ".*$"); } else { p = Pattern.compile(partialClassName); requireOne = false; } List> matchedClasses = this.validClasses.stream() .filter(cn -> cn.getSimpleName().matches("^.*" + partialClassName + ".*$")) .collect(Collectors.toList()); if (matchedClasses.size() == 0) { throw new RuntimeException( "Found no matching classes for class name pattern " + partialClassName + ". Valid class names are:\n" + validClasses.stream() .map(Class::getSimpleName) .collect(Collectors.joining("\n", " ", "")) ); } if (requireOne && matchedClasses.size() > 1) { throw new RuntimeException( "Found " + matchedClasses.size() + " matching classes for class name shortcut '" + partialClassName + "':\n" + matchedClasses.stream().map(Class::getSimpleName).collect(Collectors.joining(","))); } return matchedClasses; } /** * Handle the error according to the matching error handler for the supplied * {@link Throwable} subtype. Handlers of supertypes are used when a specific * handler is not found for the reported error class. This means that you * can install a default handler for a throwable that is a common parent to * your exceptions and have it onAfterOpStop all reported errors by default. * *

* The return type is contextual to how this handler class is used. If it is * important to use error handler implementations to control flow or other * optional execution, then the return type can be used as a form of signaling * for that. If you have no need for this, then simply use these classes with * a Void result type in the R parameter. * * @param cycle The activity cycle for which the error is being handled * @param throwable The exception that was thrown or that needs to be handled * @param errMsg A detailed message explaining the error * @return the handler result type, depending on the usage context */ @Override public R handleError(long cycle, T throwable, String errMsg) { Class errorClass = throwable.getClass(); CycleErrorHandler errorHandler = null; while (errorHandler == null) { errorHandler = handlers.get(errorClass); errorClass = errorClass.getSuperclass(); if (!upperBound.isAssignableFrom(errorClass)) { break; } } errorHandler = (errorHandler == null) ? defaultHandler : errorHandler; return errorHandler.handleError(cycle, throwable, errMsg); } public List getGroupNames() { return new ArrayList(this.errorGroups.keySet()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy