mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2)
      125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@125788 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
	
		
			
				
	
	
		
			732 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			732 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * #%L
 | |
|  * Alfresco Repository
 | |
|  * %%
 | |
|  * Copyright (C) 2005 - 2016 Alfresco Software Limited
 | |
|  * %%
 | |
|  * This file is part of the Alfresco software. 
 | |
|  * If the software was purchased under a paid Alfresco license, the terms of 
 | |
|  * the paid license agreement will prevail.  Otherwise, the software is 
 | |
|  * provided under the following open source license terms:
 | |
|  * 
 | |
|  * Alfresco is free software: you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as published by
 | |
|  * the Free Software Foundation, either version 3 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  * 
 | |
|  * Alfresco is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU Lesser General Public License for more details.
 | |
|  * 
 | |
|  * You should have received a copy of the GNU Lesser General Public License
 | |
|  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 | |
|  * #L%
 | |
|  */
 | |
| 
 | |
| package org.alfresco.traitextender;
 | |
| 
 | |
| import java.lang.reflect.InvocationTargetException;
 | |
| import java.lang.reflect.Method;
 | |
| import java.lang.reflect.UndeclaredThrowableException;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Arrays;
 | |
| import java.util.Collection;
 | |
| import java.util.HashMap;
 | |
| import java.util.HashSet;
 | |
| import java.util.LinkedList;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.Set;
 | |
| import java.util.Stack;
 | |
| 
 | |
| import org.alfresco.util.ParameterCheck;
 | |
| import org.apache.commons.logging.Log;
 | |
| import org.apache.commons.logging.LogFactory;
 | |
| import org.aspectj.lang.JoinPoint;
 | |
| import org.aspectj.lang.ProceedingJoinPoint;
 | |
| import org.aspectj.lang.reflect.MethodSignature;
 | |
| 
 | |
| import com.hazelcast.util.ConcurrentHashSet;
 | |
| 
 | |
| /**
 | |
|  * Static utility used for aspectJ extension consistency , routing and for
 | |
|  * maintaining thread-local extension-bypass context stack.<br>
 | |
|  * AspectJ extension routing distinguishes between the following contexts in
 | |
|  * which an extended method (i.e. a method with an {@link Extend} annotation)
 | |
|  * can be called:
 | |
|  * <ol>
 | |
|  * <li>Extend context<br>
 | |
|  * when an extended method is called and the extension overrides the method call
 | |
|  * </li>
 | |
|  * <li>Local proceed context<br>
 | |
|  * when an extension method needs to execute the original method that it has
 | |
|  * overridden.</li>
 | |
|  * <li>Extension bypass context<br>
 | |
|  * when a call to an extended method needs to be completed with the extensions
 | |
|  * disabled for that call</li>
 | |
|  * </ol>
 | |
|  * <br>
 | |
|  * The {@link AJExtender} can check {@link ExtensionPoint} definitions for
 | |
|  * consistency by compiling extensible classes. The compilation process fails if
 | |
|  * there are inconsistencies between {@link ExtensionPoint}s and {@link Extend}
 | |
|  * annotated methods (egg. an annotated method can not be mapped to a method in
 | |
|  * the indicated extension by signature matching).
 | |
|  * 
 | |
|  * @author Bogdan Horje
 | |
|  */
 | |
| public class AJExtender
 | |
| {
 | |
|     private static final Object[] SAFE_NULL_ARGS = new Object[0];
 | |
| 
 | |
|     private static Log logger = LogFactory.getLog(AJExtender.class);
 | |
| 
 | |
|     private static ConcurrentHashSet<ExtensionRoute> oneTimeLogSet = null;
 | |
| 
 | |
|     /**
 | |
|      *  
 | |
|      */
 | |
|     private static final ThreadLocal<Stack<Boolean>> ajPointsLocalEnabled = new ThreadLocal<Stack<Boolean>>()
 | |
|     {
 | |
|         protected Stack<Boolean> initialValue()
 | |
|         {
 | |
|             Stack<Boolean> enablementStack = new Stack<Boolean>();
 | |
|             enablementStack.push(true);
 | |
|             return enablementStack;
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     /**
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     static class ProceedingContext
 | |
|     {
 | |
|         final Extend extend;
 | |
| 
 | |
|         final ProceedingJoinPoint proceedingJoinPoint;
 | |
| 
 | |
|         ProceedingContext(Extend extend, ProceedingJoinPoint proceedingJoinPoint)
 | |
|         {
 | |
|             super();
 | |
|             this.extend = extend;
 | |
|             this.proceedingJoinPoint = proceedingJoinPoint;
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     private static final ThreadLocal<Stack<ProceedingContext>> ajLocalProceedingJoinPoints = new ThreadLocal<Stack<ProceedingContext>>()
 | |
|     {
 | |
|         protected java.util.Stack<ProceedingContext> initialValue()
 | |
|         {
 | |
|             return new Stack<>();
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     /**
 | |
|      * Implementors are aspectJ extension ignoring closures. When executing
 | |
|      * {@link ExtensionBypass}es using {@link AJExtender#run(ExtensionBypass)} or
 | |
|      * {@link AJExtender#run(ExtensionBypass, Class[])} the {@link #run()}
 | |
|      * method code will ignore extension overrides.
 | |
|      *
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     public static interface ExtensionBypass<R>
 | |
|     {
 | |
|         R run() throws Throwable;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Thrown-exception or stored error resulted compiling inconsistencies found
 | |
|      * during aspectJ extensible classes compilation.
 | |
|      *
 | |
|      * @see {@link AJExtender#compile(Class)}
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     interface AJExtensibleCompilingError
 | |
|     {
 | |
|         String getShortMessage();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Thrown or stored on compiling inconsistencies found during aspectJ
 | |
|      * extensible classes compilation.
 | |
|      *
 | |
|      * @see {@link AJExtender#compile(Class)}
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     static class AJExtensibleCompilingException extends Exception implements AJExtensibleCompilingError
 | |
|     {
 | |
| 
 | |
|         /**
 | |
|          * 
 | |
|          */
 | |
|         private static final long serialVersionUID = 1L;
 | |
| 
 | |
|         AJExtensibleCompilingException()
 | |
|         {
 | |
|             super();
 | |
|         }
 | |
| 
 | |
|         AJExtensibleCompilingException(String message, Throwable cause, boolean enableSuppression,
 | |
|                     boolean writableStackTrace)
 | |
|         {
 | |
|             super(message,
 | |
|                   cause,
 | |
|                   enableSuppression,
 | |
|                   writableStackTrace);
 | |
|         }
 | |
| 
 | |
|         AJExtensibleCompilingException(String message, Throwable cause)
 | |
|         {
 | |
|             super(message,
 | |
|                   cause);
 | |
|         }
 | |
| 
 | |
|         AJExtensibleCompilingException(String message)
 | |
|         {
 | |
|             super(message);
 | |
|         }
 | |
| 
 | |
|         AJExtensibleCompilingException(Throwable cause)
 | |
|         {
 | |
|             super(cause);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public String getShortMessage()
 | |
|         {
 | |
|             return getMessage();
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Signals the existence of extension methods that are not matched with
 | |
|      * same-signature methods through {@link Extend} annotations within
 | |
|      * {@link Extensible}.
 | |
|      *
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     static class AJDanglingExtensionError implements AJExtensibleCompilingError
 | |
|     {
 | |
|         private Method danglingMethod;
 | |
| 
 | |
|         private Extend extendDeclaration;
 | |
| 
 | |
|         AJDanglingExtensionError(Method danglingMethod, Extend extendDeclaration)
 | |
|         {
 | |
|             super();
 | |
|             this.danglingMethod = danglingMethod;
 | |
|             this.extendDeclaration = extendDeclaration;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public String getShortMessage()
 | |
|         {
 | |
|             return "Dangling extension method " + danglingMethod + " " + extendDeclaration;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * An {@link Extensible} sub class compilation result containing all
 | |
|      * {@link Extend} mapped routes, not routed or dangling methods within the
 | |
|      * give extensible class and/or possible compilation errors.
 | |
|      *
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     static class CompiledExtensible
 | |
|     {
 | |
|         private Class<? extends Extensible> extensible;
 | |
| 
 | |
|         private Map<Method, ExtensionRoute> routedMethods = new HashMap<>();
 | |
| 
 | |
|         private Map<Method, ExtensionRoute> notRoutedMethods = new HashMap<>();
 | |
| 
 | |
|         private List<AJExtensibleCompilingError> errors = new LinkedList<>();
 | |
| 
 | |
|         CompiledExtensible(Class<? extends Extensible> extensible)
 | |
|         {
 | |
|             super();
 | |
|             this.extensible = extensible;
 | |
|         }
 | |
| 
 | |
|         Class<? extends Extensible> getExtensible()
 | |
|         {
 | |
|             return this.extensible;
 | |
|         }
 | |
| 
 | |
|         void add(AJExtensibleCompilingError error)
 | |
|         {
 | |
|             this.errors.add(error);
 | |
|         }
 | |
| 
 | |
|         boolean hasErrors()
 | |
|         {
 | |
|             return !errors.isEmpty();
 | |
|         }
 | |
| 
 | |
|         String getErrorsString()
 | |
|         {
 | |
|             StringBuilder builder = new StringBuilder();
 | |
| 
 | |
|             for (AJExtensibleCompilingError error : errors)
 | |
|             {
 | |
|                 builder.append(error.getShortMessage());
 | |
|                 builder.append("\n");
 | |
|             }
 | |
| 
 | |
|             return builder.toString();
 | |
|         }
 | |
| 
 | |
|         List<AJExtensibleCompilingError> getErrors()
 | |
|         {
 | |
|             return this.errors;
 | |
|         }
 | |
| 
 | |
|         void add(ExtensionRoute route)
 | |
|         {
 | |
|             if (route.extensionMethod == null)
 | |
|             {
 | |
|                 notRoutedMethods.remove(route.extendedMethod);
 | |
|                 routedMethods.put(route.extendedMethod,
 | |
|                                   route);
 | |
|             }
 | |
|             else if (!routedMethods.containsKey(route.extendedMethod))
 | |
|             {
 | |
|                 routedMethods.put(route.extendedMethod,
 | |
|                                   route);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         Collection<ExtensionRoute> getAllNotRouted()
 | |
|         {
 | |
|             return notRoutedMethods.values();
 | |
|         }
 | |
| 
 | |
|         int getExtendedMethodCount()
 | |
|         {
 | |
|             return routedMethods.size() + notRoutedMethods.size();
 | |
|         }
 | |
| 
 | |
|         String getInfo()
 | |
|         {
 | |
|             return extensible.getName() + "{ " + routedMethods.size() + " routed methods; " + notRoutedMethods.size()
 | |
|                         + " not routed methods;" + errors.size() + " errors}";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Encapsulates extended-method to extension-method mapping information.
 | |
|      *
 | |
|      * @author Bogdan Horje
 | |
|      */
 | |
|     static class ExtensionRoute
 | |
|     {
 | |
|         final Extend extendAnnotation;
 | |
| 
 | |
|         final Method extendedMethod;
 | |
| 
 | |
|         final Method extensionMethod;
 | |
| 
 | |
|         ExtensionRoute(Extend extendAnnotation, Method traitMethod)
 | |
|         {
 | |
|             this(extendAnnotation,
 | |
|                  traitMethod,
 | |
|                  null);
 | |
|         }
 | |
| 
 | |
|         ExtensionRoute(Extend extendAnnotation, Method extendedMethod, Method extensionMethod)
 | |
|         {
 | |
|             super();
 | |
|             ParameterCheck.mandatory("extendAnnotation",
 | |
|                                      extendAnnotation);
 | |
|             ParameterCheck.mandatory("traitMethod",
 | |
|                                      extendedMethod);
 | |
| 
 | |
|             this.extendAnnotation = extendAnnotation;
 | |
|             this.extendedMethod = extendedMethod;
 | |
|             this.extensionMethod = extensionMethod;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public boolean equals(Object obj)
 | |
|         {
 | |
|             if (obj instanceof ExtensionRoute)
 | |
|             {
 | |
|                 ExtensionRoute route = (ExtensionRoute) obj;
 | |
|                 return extendAnnotation.traitAPI().equals(route.extendAnnotation.traitAPI())
 | |
|                             && extendAnnotation.extensionAPI().equals(route.extendAnnotation.extensionAPI())
 | |
|                             && extendedMethod.equals(route.extendedMethod)
 | |
|                             && ((extensionMethod == null && route.extensionMethod == null) || (extensionMethod != null && extensionMethod
 | |
|                                         .equals(route.extensionMethod)));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public String toString()
 | |
|         {
 | |
|             String extensionString = "NOT ROUTED";
 | |
| 
 | |
|             if (extensionMethod != null)
 | |
|             {
 | |
|                 Class<?> exDeclClass = extendedMethod.getDeclaringClass();
 | |
|                 extensionString = extensionMethod.toGenericString() + "#" + exDeclClass;
 | |
|             }
 | |
| 
 | |
|             return extendAnnotation.toString() + "\t\n[" + extendedMethod.toGenericString() + " -> " + extensionString
 | |
|                         + "]";
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public int hashCode()
 | |
|         {
 | |
|             return extendAnnotation.hashCode();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return <code>true</code> if aspectJ routed extensions are enabled on the
 | |
|      *         call stack of the current thread
 | |
|      */
 | |
|     static boolean areAJPointsEnabled()
 | |
|     {
 | |
|         return ajPointsLocalEnabled.get().peek();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Pushes a new enabled state of the aspectJ routed extensions on the
 | |
|      * current thread execution stack.
 | |
|      */
 | |
|     static void enableAJPoints()
 | |
|     {
 | |
|         ajPointsLocalEnabled.get().push(true);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Pops the current aspectJ routed extensions enablement state from the
 | |
|      * current thread execution stack.
 | |
|      */
 | |
|     static void revertAJPoints()
 | |
|     {
 | |
|         ajPointsLocalEnabled.get().pop();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Exception throwing extension-bypass closure runner method. <br>
 | |
|      * Sets up adequate call contexts to avoid exception calling and than
 | |
|      * delegates to the given closure.
 | |
|      * 
 | |
|      * @param closure
 | |
|      * @return
 | |
|      * @throws Throwable
 | |
|      */
 | |
|     static <R> R throwableRun(AJExtender.ExtensionBypass<R> closure) throws Throwable
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             AJExtender.ajPointsLocalEnabled.get().push(false);
 | |
|             return closure.run();
 | |
| 
 | |
|         }
 | |
|         finally
 | |
|         {
 | |
|             AJExtender.ajPointsLocalEnabled.get().pop();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Extension-bypass closure runner method. <br>
 | |
|      * Sets up adequate call contexts to avoid exception calling and than
 | |
|      * delegates to the given closure.<br>
 | |
|      * Only the given exTypes exceptions will be passed on to the calling
 | |
|      * context, all others will be wrapped as
 | |
|      * {@link UndeclaredThrowableException}s.
 | |
|      * 
 | |
|      * @param closure
 | |
|      * @param exTypes
 | |
|      * @return
 | |
|      * @throws Throwable
 | |
|      */
 | |
|     public static <R> R run(AJExtender.ExtensionBypass<R> closure, Class<?>[] exTypes) throws Throwable
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             return throwableRun(closure);
 | |
|         }
 | |
|         catch (Error | RuntimeException error)
 | |
|         {
 | |
|             throw error;
 | |
|         }
 | |
|         catch (Throwable error)
 | |
|         {
 | |
|             throw asCheckThrowable(error,
 | |
|                                    exTypes);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     static Throwable asCheckThrowable(Throwable error, Class<?>... checkedThrowableTypes)
 | |
|     {
 | |
|         Class<? extends Throwable> errorClass = error.getClass();
 | |
|         for (int i = 0; i < checkedThrowableTypes.length; i++)
 | |
|         {
 | |
|             if (errorClass.equals(checkedThrowableTypes[i]))
 | |
|             {
 | |
|                 return error;
 | |
|             }
 | |
|         }
 | |
|         return new UndeclaredThrowableException(error);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Extension-bypass closure runner method. <br>
 | |
|      * Sets up adequate call contexts to avoid exception calling and than
 | |
|      * delegates to the given closure.
 | |
|      * 
 | |
|      * @param closure
 | |
|      * @return
 | |
|      */
 | |
|     public static <R> R run(AJExtender.ExtensionBypass<R> closure)
 | |
|     {
 | |
|         try
 | |
|         {
 | |
|             return throwableRun(closure);
 | |
|         }
 | |
|         catch (Error | RuntimeException error)
 | |
|         {
 | |
|             throw error;
 | |
|         }
 | |
|         catch (Throwable error)
 | |
|         {
 | |
|             throw new UndeclaredThrowableException(error);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Logs each method routing path once per session.
 | |
|      * 
 | |
|      * @param logger
 | |
|      * @param route
 | |
|      */
 | |
|     static void oneTimeLiveLog(Log logger, ExtensionRoute route)
 | |
|     {
 | |
|         synchronized (AJExtender.class)
 | |
|         {
 | |
|             if (oneTimeLogSet == null)
 | |
|             {
 | |
|                 oneTimeLogSet = new ConcurrentHashSet<>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         synchronized (oneTimeLogSet)
 | |
|         {
 | |
|             if (oneTimeLogSet.contains(route))
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 logger.debug(route.toString());
 | |
|                 oneTimeLogSet.add(route);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param extensible
 | |
|      * @return a compilation result containing all {@link Extend} mapped routes,
 | |
|      *         not routed or dangling methods within the give extensible class.
 | |
|      * @throws AJExtensibleCompilingException
 | |
|      */
 | |
|     static CompiledExtensible compile(Class<? extends Extensible> extensible) throws AJExtensibleCompilingException
 | |
|     {
 | |
|         logger.info("Compiling extensible " + extensible);
 | |
| 
 | |
|         CompiledExtensible compiledExtensible = new CompiledExtensible(extensible);
 | |
| 
 | |
|         List<Method> methods = new ArrayList<>();
 | |
|         Class<?> extendDeclaring = extensible;
 | |
|         while (extendDeclaring != null)
 | |
|         {
 | |
|             Method[] declaredMethods = extendDeclaring.getDeclaredMethods();
 | |
|             methods.addAll(Arrays.asList(declaredMethods));
 | |
|             extendDeclaring = extendDeclaring.getSuperclass();
 | |
|         }
 | |
|         Set<Extend> extendDeclarations = new HashSet<>();
 | |
|         Set<Method> routedExtensionMethods = new HashSet<>();
 | |
|         for (Method method : methods)
 | |
|         {
 | |
| 
 | |
|             Extend extend = method.getAnnotation(Extend.class);
 | |
|             if (extend != null)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     extendDeclarations.add(extend);
 | |
|                     Class<?> extensionAPI = extend.extensionAPI();
 | |
|                     Method extensionMethod = extensionAPI.getMethod(method.getName(),
 | |
|                                                                     method.getParameterTypes());
 | |
|                     compiledExtensible.add(new ExtensionRoute(extend,
 | |
|                                                               method,
 | |
|                                                               extensionMethod));
 | |
|                     routedExtensionMethods.add(extensionMethod);
 | |
|                 }
 | |
|                 catch (NoSuchMethodException error)
 | |
|                 {
 | |
|                     AJExtensibleCompilingException ajCompilingError = new AJExtensibleCompilingException("No route for "
 | |
|                                                                                                                      + method.toGenericString()
 | |
|                                                                                                                      + " @"
 | |
|                                                                                                                      + extend,
 | |
|                                                                                                          error);
 | |
|                     compiledExtensible.add(ajCompilingError);
 | |
|                 }
 | |
|                 catch (SecurityException error)
 | |
|                 {
 | |
|                     AJExtensibleCompilingException ajCompilingError = new AJExtensibleCompilingException("Access denined to route for "
 | |
|                                                                                                                      + method.toGenericString()
 | |
|                                                                                                                      + " @"
 | |
|                                                                                                                      + extend,
 | |
|                                                                                                          error);
 | |
|                     compiledExtensible.add(ajCompilingError);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         final Set<Method> allObjectMethods = new HashSet<>(Arrays.asList(Object.class.getMethods()));
 | |
| 
 | |
|         for (Extend extend : extendDeclarations)
 | |
|         {
 | |
|             Class<?> extension = extend.extensionAPI();
 | |
| 
 | |
|             Set<Method> allExtensionMethods = new HashSet<>(Arrays.asList(extension.getMethods()));
 | |
|             allExtensionMethods.removeAll(allObjectMethods);
 | |
|             allExtensionMethods.removeAll(routedExtensionMethods);
 | |
|             if (!allExtensionMethods.isEmpty())
 | |
|             {
 | |
|                 for (Method method : allExtensionMethods)
 | |
|                 {
 | |
|                     compiledExtensible.add(new AJDanglingExtensionError(method,
 | |
|                                                                         extend));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         logger.info(compiledExtensible.getInfo());
 | |
| 
 | |
|         return compiledExtensible;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Around advice helper that matches the advised method with its
 | |
|      * corresponding extension method, sets up aspectJ call contexts (egg. the
 | |
|      * local-proceed context) and delegates to the extension method.
 | |
|      * 
 | |
|      * @param thisJoinPoint
 | |
|      * @param extensible
 | |
|      * @param extendAnnotation
 | |
|      * @param extension
 | |
|      * @return the result of the extended method
 | |
|      */
 | |
|     static Object extendAroundAdvice(JoinPoint thisJoinPoint, Extensible extensible, Extend extendAnnotation,
 | |
|                 Object extension)
 | |
|     {
 | |
| 
 | |
|         MethodSignature ms = (MethodSignature) thisJoinPoint.getSignature();
 | |
|         Method method = ms.getMethod();
 | |
|         try
 | |
|         {
 | |
|             ajLocalProceedingJoinPoints.get().push(new ProceedingContext(extendAnnotation,
 | |
|                                                                          (ProceedingJoinPoint) thisJoinPoint));
 | |
| 
 | |
|             Method extensionMethod = extension.getClass().getMethod(method.getName(),
 | |
|                                                                     method.getParameterTypes());
 | |
|             if (logger.isDebugEnabled())
 | |
|             {
 | |
|                 oneTimeLiveLog(AJExtender.logger,
 | |
|                                new ExtensionRoute(extendAnnotation,
 | |
|                                                   method,
 | |
|                                                   extensionMethod));
 | |
|             }
 | |
| 
 | |
|             return extensionMethod.invoke(extension,
 | |
|                                           thisJoinPoint.getArgs());
 | |
|         }
 | |
|         catch (IllegalAccessException error)
 | |
|         {
 | |
|             throw new InvalidExtension("Ivalid extension : " + error.getMessage(),
 | |
|                                        error);
 | |
|         }
 | |
|         catch (IllegalArgumentException error)
 | |
|         {
 | |
|             throw new InvalidExtension("Ivalid extension : " + error.getMessage(),
 | |
|                                        error);
 | |
|         }
 | |
|         catch (InvocationTargetException error)
 | |
|         {
 | |
|             Throwable targetException = error.getTargetException();
 | |
|             if (targetException instanceof RuntimeException)
 | |
|             {
 | |
|                 throw (RuntimeException) targetException;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 throw new ExtensionTargetException(targetException);
 | |
|             }
 | |
|         }
 | |
|         catch (NoSuchMethodException error)
 | |
|         {
 | |
|             throw new InvalidExtension("Ivalid extension : " + error.getMessage(),
 | |
|                                        error);
 | |
|         }
 | |
|         catch (SecurityException error)
 | |
|         {
 | |
|             throw new InvalidExtension("Ivalid extension : " + error.getMessage(),
 | |
|                                        error);
 | |
|         }
 | |
|         finally
 | |
|         {
 | |
|             ajLocalProceedingJoinPoints.get().pop();
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param method
 | |
|      * @return <code>true</code> if the given method has the same signature as
 | |
|      *         the currently aspectJ extension-overridden method
 | |
|      */
 | |
|     static boolean isLocalProceeder(Method method)
 | |
|     {
 | |
|         if (!ajLocalProceedingJoinPoints.get().isEmpty())
 | |
|         {
 | |
|             ProceedingContext proceedingCotext = ajLocalProceedingJoinPoints.get().peek();
 | |
|             MethodSignature ms = (MethodSignature) proceedingCotext.proceedingJoinPoint.getSignature();
 | |
|             Method jpMethod = ms.getMethod();
 | |
|             return jpMethod.getName().endsWith(method.getName()) && Arrays.equals(jpMethod.getParameterTypes(),
 | |
|                                                                                   method.getParameterTypes());
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Calls the currently overridden method in local-proceed context - proceeds
 | |
|      * with the aspectJ join point saved on the current call stack.<br>
 | |
|      * 
 | |
|      * @param args
 | |
|      * @throws Throwable
 | |
|      */
 | |
|     static Object localProceed(Object[] args) throws Throwable
 | |
|     {
 | |
|         ProceedingContext proceedingCotext = ajLocalProceedingJoinPoints.get().peek();
 | |
|         Object[] safeArgs = args == null ? SAFE_NULL_ARGS : args;
 | |
|         return proceedingCotext.proceedingJoinPoint.proceed(safeArgs);
 | |
|     }
 | |
| }
 |