Class ExceptionInterceptor

java.lang.Object
com.ultikits.ultitools.aop.ExceptionInterceptor
All Implemented Interfaces:
MethodInterceptor

public class ExceptionInterceptor extends Object implements MethodInterceptor
AOP interceptor that handles @ExceptionCatch annotated methods.

When a method throws an exception that matches the @ExceptionCatch configuration, this interceptor catches it and returns a default value instead of propagating.

Since:
6.2.0
Author:
wisdomme
  • Field Details

    • LOGGER

      private static final Logger LOGGER
    • globalHandlers

      private final List<ExceptionHandler> globalHandlers
    • lookupCache

      private final AnnotationLookupCache<ExceptionCatch> lookupCache
      Instance-scoped on purpose - see AnnotationLookupCache's class javadoc.
    • context

      private final SimpleContainer context
      Container used to resolve @ExceptionCatch(handler = "...") beans.

      Injected per plugin rather than read from a global static holder: each plugin has its own container, and a single static holder would make the last plugin to initialise win, sending every earlier plugin's handler lookup to the wrong container. See issue #190.

  • Constructor Details

    • ExceptionInterceptor

      public ExceptionInterceptor()
      Creates an exception interceptor with no global handlers and no container.
    • ExceptionInterceptor

      public ExceptionInterceptor(List<ExceptionHandler> globalHandlers)
      Creates an exception interceptor with the given global handlers and no container.
      Parameters:
      globalHandlers - handlers to try for any exception
    • ExceptionInterceptor

      public ExceptionInterceptor(List<ExceptionHandler> globalHandlers, SimpleContainer context)
      Creates an exception interceptor bound to a plugin's container, with its own unshared AnnotationLookupCache.

      A caller that also builds an AopAdvisor for @ExceptionCatch should use ExceptionInterceptor(List, SimpleContainer, AnnotationLookupCache) instead and inject one shared instance into both (D-38). PluginManager.wireAop is that caller; this constructor exists for standalone construction and for exercising this interceptor in isolation.

      Parameters:
      globalHandlers - handlers to try for any exception
      context - the container used to resolve named handlers, may be null
    • ExceptionInterceptor

      public ExceptionInterceptor(List<ExceptionHandler> globalHandlers, SimpleContainer context, AnnotationLookupCache<ExceptionCatch> lookupCache)
      Creates an exception interceptor bound to a plugin's container, using a caller-supplied AnnotationLookupCache instead of building its own.

      Sharing the cache does not change what this interceptor asks it: invoke(com.ultikits.ultitools.aop.MethodInvocation) still resolves own-method, then class-level, then inherited-method, with class-level ranking between the other two - only the memoized answers are shared with whichever advisor was given the same instance (D-38).

      Parameters:
      globalHandlers - handlers to try for any exception
      context - the container used to resolve named handlers, may be null
      lookupCache - the cache to use instead of constructing a new one
  • Method Details

    • invoke

      public Object invoke(MethodInvocation invocation) throws Throwable
      Description copied from interface: MethodInterceptor
      Intercept the given method invocation.

      Implementations should call MethodInvocation.proceed() to continue the interceptor chain or invoke the target method.

      Specified by:
      invoke in interface MethodInterceptor
      Parameters:
      invocation - the method invocation context
      Returns:
      the result of the method invocation
      Throws:
      Throwable - if the interceptor or target method throws an exception
    • shouldCatch

      private boolean shouldCatch(Throwable e, Class<? extends Throwable>[] types)
      Checks if the given exception should be caught based on the configured types.
    • beanClassOf

      private static Class<?> beanClassOf(MethodInvocation invocation, Method method)
      The class the bean actually is, with any proxy layers unwrapped.

      Used for attribution only. Which class-level annotation governs a method is decided by AopAdvisor.findClassLevelAnnotation(Method, Class), which anchors on the declaring class and never consults the target. Attribution wants the opposite: for an inherited method the declaring class is often a framework base, which tells an operator nothing about which concrete bean failed and makes ErrorReportCollector's fingerprint dedup collapse two different beans into one report. The declaring class remains the fallback for a null target - that branch is not proxy identity and is out of scope for this method's unwrap. See issue #309.

      Unwrapping itself delegates to ProxyFactory.unwrap(Class): a proxy of a proxy already names the original target, so no hierarchy walk is needed here either.

      Parameters:
      invocation - the invocation in progress
      method - the method being intercepted
      Returns:
      the bean class, never null
    • handleCaughtException

      private Object handleCaughtException(Throwable e, MethodInvocation invocation, ExceptionCatch annotation) throws Throwable
      Handles a caught exception according to the annotation configuration.
      Throws:
      Throwable
    • parseDefaultValue

      private Object parseDefaultValue(String expression, Class<?> returnType)
      Parses a default value expression into an actual value.
    • getDefaultValue

      private Object getDefaultValue(Class<?> type)
      Gets the default value for a type.
    • getEmptyValue

      private Object getEmptyValue(Class<?> type)
      Gets an empty value for collection/array/string types.