/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.exception;
import static java.util.Optional.empty;
import static org.mule.runtime.api.util.Preconditions.checkArgument;
import static org.mule.runtime.api.util.Preconditions.checkState;
import org.mule.runtime.api.component.ComponentIdentifier;
import org.mule.runtime.api.message.ErrorType;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.collections.map.HashedMap;
/**
* Locator for error types.
*
* The locator is responsible for getting the error type of any exception.
*
* An exception can be a general exception or an exception thrown by a configured component.
*
* There's a default mapping that will be used when there's no specific mapping between a component and the exception type thrown.
*
* To create an {@code ErrorTypeLocator} you must use the {@code Builder}. An instance of the builder can be created using the
* static method {@code #builder}.
*
* @since 4.0
*/
public class ErrorTypeLocator {
private ExceptionMapper defaultExceptionMapper;
private Map<ComponentIdentifier, ExceptionMapper> componentExceptionMappers;
private ErrorTypeLocator(ExceptionMapper defaultExceptionMapper,
Map<ComponentIdentifier, ExceptionMapper> componentExceptionMappers) {
this.defaultExceptionMapper = defaultExceptionMapper;
this.componentExceptionMappers = componentExceptionMappers;
}
/**
* Finds the {@code ErrorType} related to the provided {@code exception} based on the general mapping rules of the runtime.
*
* @param exception the exception related to the error type
* @return the error type related to the exception. If there's no mapping then the error type related to UNKNOWN will be
* returned.
*/
public ErrorType lookupErrorType(Throwable exception) {
return lookupErrorType(exception.getClass());
}
/**
* Finds the {@code ErrorType} related to the provided {@code exception} based on the general mapping rules of the runtime.
*
* @param exceptionType the exception {@link Class} related to the error type
* @return the error type related to the exception. If there's no mapping then the error type related to UNKNOWN will be
* returned.
*/
public ErrorType lookupErrorType(Class<? extends Throwable> exceptionType) {
return defaultExceptionMapper.resolveErrorType(exceptionType).get();
}
/**
* Finds the {@code ErrorType} related to a component defined by the {@link ComponentIdentifier} based on the exception thrown
* by the component and the mappings configured in the {@code ErrorTypeLocator}.
*
* If no mapping is available then the {@link #lookupErrorType(Throwable)} rules applies.
*
* @param componentIdentifier the identifier of the component that throw the exception.
* @param exception the exception thrown by the component.
* @return the error type related to the exception based on the component mappings. If there's no mapping then the error type
* related to UNKNOWN will be returned.
*/
public ErrorType lookupComponentErrorType(ComponentIdentifier componentIdentifier,
Class<? extends Throwable> exception) {
ExceptionMapper exceptionMapper =
componentExceptionMappers.get(componentIdentifier);
Optional<ErrorType> errorType = empty();
if (exceptionMapper != null) {
errorType = exceptionMapper.resolveErrorType(exception);
}
return errorType.orElseGet(() -> defaultExceptionMapper.resolveErrorType(exception).get());
}
/**
* Finds the {@code ErrorType} related to a component defined by the {@link ComponentIdentifier} based on the exception thrown
* by the component and the mappings configured in the {@code ErrorTypeLocator}.
*
* If no mapping is available then the {@link #lookupErrorType(Throwable)} rules applies.
*
* @param componentIdentifier the identifier of the component that throw the exception.
* @param exception the exception thrown by the component.
* @return the error type related to the exception based on the component mappings. If there's no mapping then the error type
* related to UNKNOWN will be returned.
*/
public ErrorType lookupComponentErrorType(ComponentIdentifier componentIdentifier, Throwable exception) {
return lookupComponentErrorType(componentIdentifier, exception.getClass());
}
/**
* Adds an {@link ExceptionMapper} for a particular component identified by a {@link ComponentIdentifier}.
*
* @param componentIdentifier identifier of a component.
* @param exceptionMapper exception mapper for the component.
*/
public void addComponentExceptionMapper(ComponentIdentifier componentIdentifier, ExceptionMapper exceptionMapper) {
this.componentExceptionMappers.put(componentIdentifier, exceptionMapper);
}
/**
* Builder for creating instances of {@link ErrorTypeLocator}.
*
* @param errorTypeRepository repository of error types.
* @return a builder for creating an {@link ErrorTypeLocator}
*/
public static Builder builder(ErrorTypeRepository errorTypeRepository) {
return new Builder(errorTypeRepository);
}
/**
* Builder for {@link ErrorTypeLocator}
*
* @since 4.0
*/
public static class Builder {
/**
* Creates a builder instance.
*/
public Builder(ErrorTypeRepository errorTypeRepository) {
checkArgument(errorTypeRepository != null, "error type repository cannot be null");
}
private ExceptionMapper defaultExceptionMapper;
private Map<ComponentIdentifier, ExceptionMapper> componentExceptionMappers = new HashedMap();
/**
* Sets the default exception mapper to use when a component doesn't define a mapping for an exception type.
*
* @param exceptionMapper default exception mapper.
* @return {@code this} builder.
*/
public Builder defaultExceptionMapper(ExceptionMapper exceptionMapper) {
this.defaultExceptionMapper = exceptionMapper;
return this;
}
/**
* Adds an {@link ExceptionMapper} for a particular component identified by the componentIdentifier.
*
* @param componentIdentifier identifier of a component.
* @param exceptionMapper exception mapper for the component.
* @return {@code this} builder.
*/
public Builder addComponentExceptionMapper(ComponentIdentifier componentIdentifier, ExceptionMapper exceptionMapper) {
this.componentExceptionMappers.put(componentIdentifier, exceptionMapper);
return this;
}
/**
* Builds an {@link ErrorTypeLocator} instance with the provided configuration.
*
* @return an {@link ErrorTypeLocator} instance.
*/
public ErrorTypeLocator build() {
checkState(defaultExceptionMapper != null, "default exception mapper cannot not be null");
checkState(componentExceptionMappers != null, "component exception mappers cannot not be null");
return new ErrorTypeLocator(defaultExceptionMapper, componentExceptionMappers);
}
}
}