Java tutorial
/** * The MIT License (MIT) * * Copyright (c) 2014 CrossBusiness, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.crossbusiness.resiliency.aspect.spring; import com.crossbusiness.resiliency.annotation.Fallback; import com.crossbusiness.resiliency.aspect.AbstractFallbackAspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Created by Sumanth Chinthagunta <xmlking@gmail.com> on 3/16/14. */ @Component @Order(112) @Aspect public class AnnotationFallbackAspect { protected final Logger log = LoggerFactory.getLogger(getClass()); @Autowired private ApplicationContext context; @Around("fallbackAnnotatedClass(fallbackConfig)") public Object fallbackOnClassLevel(ProceedingJoinPoint pjp, Fallback fallbackConfig) throws Throwable { return rerouteToFallback(pjp, fallbackConfig); } // @AfterThrowing( // pointcut="@annotation(fallbackConfig)", // throwing="ex") @Around("fallbackAnnotatedMethod(fallbackConfig)") public Object fallbackOnMethodLevel(ProceedingJoinPoint pjp, Fallback fallbackConfig) throws Throwable { return rerouteToFallback(pjp, fallbackConfig); } // @Around("fallbackMethodExecution(fallbackConfig)") public Object rerouteToFallback(ProceedingJoinPoint pjp, Fallback fallbackConfig) throws Throwable { String[] fallbacks = fallbackConfig.value(); Class<? extends Throwable>[] fallbackableExceptions = fallbackConfig.exceptions(); List<Object> fallbackBeans = new ArrayList<Object>(fallbacks.length); for (String fallback : fallbacks) { try { fallbackBeans.add(context.getBean(fallback)); } catch (BeansException be) { log.error("configuration error: cannot find bean with name: '{}'", fallback, be); //configuration errors should be fixed immediately. throw be; } } MethodSignature targetMethodSig = (MethodSignature) pjp.getSignature(); Method targetMethod = targetMethodSig.getMethod(); Class[] paramTypes = (Class[]) targetMethod.getParameterTypes(); Object[] args = pjp.getArgs(); log.debug("fallbacks: {} method: '{}'", fallbacks, targetMethod); try { return pjp.proceed(); } catch (Throwable t) { // if the exception is not what we're looking for, rethrow it if (!isFallbackableException(t, fallbackableExceptions)) throw t; log.debug("got exception while trying the targetBean method: '{}'. will try fallbackBean...", targetMethod); Iterator<Object> iter = fallbackBeans.iterator(); while (iter.hasNext()) { Object fallbackBean = iter.next(); Method fallbackMethod; try { fallbackMethod = fallbackBean.getClass().getMethod(targetMethod.getName(), paramTypes); } catch (NoSuchMethodException | SecurityException nsme) { log.error( "configuration error: No matchig method found in fallbackBean: '{}' that matches to targetBean method: '{}'", new Object[] { fallbackBean.getClass().getName(), targetMethod, nsme }); //configuration errors should be fixed immediately. throw nsme; } try { log.debug("trying fallbackBean method: '{}'...", fallbackMethod); return fallbackMethod.invoke(fallbackBean, args); } catch (IllegalArgumentException | IllegalAccessException iae) { log.error( "configuration error: arguments missmatch: fallbackBean method: '{}' arguments missmatch to targetBean method: '{}' arguments", new Object[] { fallbackMethod, targetMethod, iae }); //configuration errors should be fixed immediately. throw iae; } catch (InvocationTargetException ite) { log.debug( "got exception while trying the fallbackBean method: '{}'. will try next fallbackBean...", fallbackMethod); //fallbackBean method thrown an exception. try next bean or throw exception if this is the last bean if (!iter.hasNext()) { //TODO : do we still need to check isFallbackableException? throw ite.getCause(); } } } //code should never reach this line. throw t; } } static boolean isFallbackableException(Throwable t, Class<? extends Throwable>[] fallbackableExceptions) { for (Class<? extends Throwable> throwable : fallbackableExceptions) { if (throwable.isAssignableFrom(t.getClass())) { return true; } } return false; } /** * Matches the execution of any method with the @{@link com.crossbusiness.resiliency.annotation.Fallback} annotation. */ @Pointcut("execution(@com.crossbusiness.resiliency.annotation.Fallback * *(..)) && @annotation(fallbackConfig)") public void fallbackAnnotatedMethod(Fallback fallbackConfig) { } /** * Matches the execution of any public method in a type with the @{@link com.crossbusiness.resiliency.annotation.Fallback} * annotation, or any subtype of a type with the {@code Fallback} annotation. */ @Pointcut("execution(public * ((@com.crossbusiness.resiliency.annotation.Fallback *)+).*(..)) " + "&& within(@com.crossbusiness.resiliency.annotation.Fallback *) && @target(fallbackConfig) " + "&& !com.crossbusiness.resiliency.aspect.SystemArchitecture.groovyMOPMethods()") public void fallbackAnnotatedClass(Fallback fallbackConfig) { } /** * Definition of pointcut from super aspect - matched join points * will have Fallback Aspect applied. */ @Pointcut("(fallbackAnnotatedMethod(fallbackConfig) || fallbackAnnotatedClass(fallbackConfig))") public void fallbackMethodExecution(Fallback fallbackConfig) { } }