mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Merged HEAD (5.1) to 5.1.N (5.1.1)
117473 bhorje: CM-690 trait extension git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.1.N/root@117558 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class AJDanglingExtensionError implements AJExtensibleCompilingError
|
||||||
|
{
|
||||||
|
private Method danglingMethod;
|
||||||
|
private Extend extendDeclaration;
|
||||||
|
|
||||||
|
public AJDanglingExtensionError(Method danglingMethod,Extend extendDeclaration)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.danglingMethod = danglingMethod;
|
||||||
|
this.extendDeclaration=extendDeclaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortMessage()
|
||||||
|
{
|
||||||
|
return "Dangling extension method "+danglingMethod+" "+extendDeclaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
504
source/java/org/alfresco/traitextender/AJExtender.java
Normal file
504
source/java/org/alfresco/traitextender/AJExtender.java
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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<>();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
public static interface ExtensionBypass<R>
|
||||||
|
{
|
||||||
|
R run() throws Throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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<>();
|
||||||
|
|
||||||
|
public CompiledExtensible(Class<? extends Extensible> extensible)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.extensible = extensible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Extensible> getExtensible()
|
||||||
|
{
|
||||||
|
return this.extensible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(AJExtensibleCompilingError error)
|
||||||
|
{
|
||||||
|
this.errors.add(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasErrors()
|
||||||
|
{
|
||||||
|
return !errors.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorsString()
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (AJExtensibleCompilingError error : errors)
|
||||||
|
{
|
||||||
|
builder.append(error.getShortMessage());
|
||||||
|
builder.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AJExtensibleCompilingError> getErrors()
|
||||||
|
{
|
||||||
|
return this.errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ExtensionRoute> getAllNotRouted()
|
||||||
|
{
|
||||||
|
return notRoutedMethods.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getExtendedMethodCount()
|
||||||
|
{
|
||||||
|
return routedMethods.size() + notRoutedMethods.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInfo()
|
||||||
|
{
|
||||||
|
return extensible.getName() + "{ " + routedMethods.size() + " routed methods; " + notRoutedMethods.size()
|
||||||
|
+ " not routed methods;" + errors.size() + " errors}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean areAJPointsEnabled()
|
||||||
|
{
|
||||||
|
return ajPointsLocalEnabled.get().peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enableAJPoints()
|
||||||
|
{
|
||||||
|
ajPointsLocalEnabled.get().push(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void revertAJPoints()
|
||||||
|
{
|
||||||
|
ajPointsLocalEnabled.get().pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> R throwableRun(AJExtender.ExtensionBypass<R> section) throws Throwable
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AJExtender.ajPointsLocalEnabled.get().push(false);
|
||||||
|
return section.run();
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AJExtender.ajPointsLocalEnabled.get().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> R run(AJExtender.ExtensionBypass<R> section, Class<?>[] exTypes) throws Throwable
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return throwableRun(section);
|
||||||
|
}
|
||||||
|
catch (Error | RuntimeException error)
|
||||||
|
{
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
catch (Throwable error)
|
||||||
|
{
|
||||||
|
throw asCheckThrowable(error,
|
||||||
|
exTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> R run(AJExtender.ExtensionBypass<R> section)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return throwableRun(section);
|
||||||
|
}
|
||||||
|
catch (Error | RuntimeException error)
|
||||||
|
{
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
catch (Throwable error)
|
||||||
|
{
|
||||||
|
throw new UndeclaredThrowableException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public interface AJExtensibleCompilingError
|
||||||
|
{
|
||||||
|
String getShortMessage();
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public class AJExtensibleCompilingException extends Exception implements AJExtensibleCompilingError
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public AJExtensibleCompilingException()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AJExtensibleCompilingException(String message, Throwable cause, boolean enableSuppression,
|
||||||
|
boolean writableStackTrace)
|
||||||
|
{
|
||||||
|
super(message,
|
||||||
|
cause,
|
||||||
|
enableSuppression,
|
||||||
|
writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AJExtensibleCompilingException(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message,
|
||||||
|
cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AJExtensibleCompilingException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AJExtensibleCompilingException(Throwable cause)
|
||||||
|
{
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getShortMessage()
|
||||||
|
{
|
||||||
|
return getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
122
source/java/org/alfresco/traitextender/AJProxyTrait.java
Normal file
122
source/java/org/alfresco/traitextender/AJProxyTrait.java
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
import org.alfresco.traitextender.AJExtender.ExtensionBypass;
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java {@link Proxy} {@link InvocationHandler} to be used in conjuction with
|
||||||
|
* asprctJ extended traits.<br>
|
||||||
|
* Method calls will be delegated to a given {@link Extensible} object method
|
||||||
|
* having the same signature within an {@link ExtensionBypass} context.
|
||||||
|
*
|
||||||
|
* @author Bogdan Horje
|
||||||
|
*/
|
||||||
|
public class AJProxyTrait implements InvocationHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Trait} {@link Proxy} factory method.
|
||||||
|
*
|
||||||
|
* @param extensible the {@link Extensible} object that defines the given
|
||||||
|
* {@link Trait}
|
||||||
|
* @param traitAPI the trait interface part that the given
|
||||||
|
* {@link Extensible} object defines
|
||||||
|
* @return a {@link Proxy} object for the given trait API interface with an
|
||||||
|
* {@link AJProxyTrait} attached. All method calls performed on the
|
||||||
|
* returned proxy will be delegated to a given {@link Extensible}
|
||||||
|
* object method having the same signature within an
|
||||||
|
* {@link ExtensionBypass} context.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <M extends Trait> M create(Object extensibleInterface, Class<M> traitAPI)
|
||||||
|
{
|
||||||
|
return (M) Proxy.newProxyInstance(AJProxyTrait.class.getClassLoader(),
|
||||||
|
new Class[] { traitAPI },
|
||||||
|
new AJProxyTrait(extensibleInterface));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object extensibleInterface;
|
||||||
|
|
||||||
|
private AJProxyTrait(Object extensibleInterface)
|
||||||
|
{
|
||||||
|
ParameterCheck.mandatory("extensible",
|
||||||
|
extensibleInterface);
|
||||||
|
this.extensibleInterface = extensibleInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable
|
||||||
|
{
|
||||||
|
final Method traitMethod = extensibleInterface.getClass().getMethod(method.getName(),
|
||||||
|
method.getParameterTypes());
|
||||||
|
|
||||||
|
if (AJExtender.isLocalProceeder(method))
|
||||||
|
{
|
||||||
|
return AJExtender.localProceed(args);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Class<?>[] exTypes = traitMethod.getExceptionTypes();
|
||||||
|
|
||||||
|
return AJExtender.run(new AJExtender.ExtensionBypass<Object>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Object run() throws Throwable
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return traitMethod.invoke(extensibleInterface,
|
||||||
|
args);
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException error)
|
||||||
|
{
|
||||||
|
throw new InvalidExtension(error);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException error)
|
||||||
|
{
|
||||||
|
throw new InvalidExtension(error);
|
||||||
|
}
|
||||||
|
catch (InvocationTargetException error)
|
||||||
|
{
|
||||||
|
Throwable targetException = error.getTargetException();
|
||||||
|
throw targetException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
exTypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "AJAutoTrait@" + System.identityHashCode(this) + " of " + extensibleInterface.getClass().getSimpleName() + "@"
|
||||||
|
+ System.identityHashCode(extensibleInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
source/java/org/alfresco/traitextender/Extend.java
Normal file
29
source/java/org/alfresco/traitextender/Extend.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Extend
|
||||||
|
{
|
||||||
|
Class<?> extensionAPI();
|
||||||
|
Class<? extends Trait> traitAPI();
|
||||||
|
}
|
62
source/java/org/alfresco/traitextender/ExtendedTrait.java
Normal file
62
source/java/org/alfresco/traitextender/ExtendedTrait.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class ExtendedTrait<T extends Trait>
|
||||||
|
{
|
||||||
|
private ConcurrentHashMap<Class<?>, Object> extensions = new ConcurrentHashMap<Class<?>, Object>();
|
||||||
|
|
||||||
|
private T trait;
|
||||||
|
|
||||||
|
public ExtendedTrait(T trait)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.trait = trait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getTrait()
|
||||||
|
{
|
||||||
|
return trait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> E getExtension(Class<E> extensionAPI)
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
E extension=(E) extensions.get(extensionAPI);
|
||||||
|
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized <E> E extend(Class<E> extensionAPI, ExtensionFactory<E> factory)
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
E extension=(E) extensions.get(extensionAPI);
|
||||||
|
|
||||||
|
if (extension==null)
|
||||||
|
{
|
||||||
|
extension = factory.createExtension(trait);
|
||||||
|
extensions.put(extensionAPI, extension);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
}
|
56
source/java/org/alfresco/traitextender/Extender.java
Normal file
56
source/java/org/alfresco/traitextender/Extender.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class Extender
|
||||||
|
{
|
||||||
|
private static Extender instance;
|
||||||
|
|
||||||
|
public static class Configuration {
|
||||||
|
private boolean enabled;
|
||||||
|
private List<String> disbaledBundles;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Extender getInstance()
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
instance = new ExtenderImpl();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void start(ExtensionBundle bundle);
|
||||||
|
|
||||||
|
public abstract void stop(ExtensionBundle bundle);
|
||||||
|
|
||||||
|
public abstract void stopAll();
|
||||||
|
|
||||||
|
public abstract <E, M extends Trait> E getExtension(Extensible anExtensible, ExtensionPoint<E, M> point);
|
||||||
|
|
||||||
|
public abstract void register(ExtensionPoint<?, ?> point, ExtensionFactory<?> factory);
|
||||||
|
|
||||||
|
public abstract void unregister(ExtensionPoint<?, ?> point);
|
||||||
|
|
||||||
|
}
|
105
source/java/org/alfresco/traitextender/ExtenderImpl.java
Normal file
105
source/java/org/alfresco/traitextender/ExtenderImpl.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
public class ExtenderImpl extends Extender
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(Extender.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private List<ExtensionBundle> bundles = new LinkedList<ExtensionBundle>();
|
||||||
|
|
||||||
|
private Map<ExtensionPoint<?, ?>, ExtensionFactory<?>> pointFactories = new ConcurrentHashMap<ExtensionPoint<?, ?>, ExtensionFactory<?>>();
|
||||||
|
|
||||||
|
public synchronized void start(ExtensionBundle bundle)
|
||||||
|
{
|
||||||
|
bundles.add(bundle);
|
||||||
|
bundle.start(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(ExtensionBundle bundle)
|
||||||
|
{
|
||||||
|
bundle.stop(this);
|
||||||
|
bundles.remove(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void stopAll()
|
||||||
|
{
|
||||||
|
List<ExtensionBundle> bundlesCopy = new LinkedList<>(bundles);
|
||||||
|
for (ExtensionBundle bundle : bundlesCopy)
|
||||||
|
{
|
||||||
|
stop(bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized <E, M extends Trait> E getExtension(Extensible anExtensible, ExtensionPoint<E, M> point)
|
||||||
|
{
|
||||||
|
E extension = null;
|
||||||
|
|
||||||
|
//consistency is checked at registration time
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ExtensionFactory<E> factory = (ExtensionFactory<E>) pointFactories.get(point);
|
||||||
|
|
||||||
|
if (factory != null)
|
||||||
|
{
|
||||||
|
ExtendedTrait<M> exTrait = anExtensible.getTrait(point.getTraitAPI());
|
||||||
|
|
||||||
|
extension = exTrait.getExtension(point.getExtensionAPI());
|
||||||
|
|
||||||
|
if (extension == null)
|
||||||
|
{
|
||||||
|
extension = exTrait.extend(point.getExtensionAPI(),
|
||||||
|
factory);
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("trait extension leak trace : " + System.identityHashCode(extension) + " : "
|
||||||
|
+ System.identityHashCode(exTrait) + " : " + System.identityHashCode(extension));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void register(ExtensionPoint<?, ?> point, ExtensionFactory<?> factory)
|
||||||
|
{
|
||||||
|
// point->factory type consistency checks
|
||||||
|
if (!factory.canCreateExtensionFor(point))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Invalid extension factory registry entry : " + point + "->" + factory);
|
||||||
|
}
|
||||||
|
pointFactories.put(point,
|
||||||
|
factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void unregister(ExtensionPoint<?, ?> point)
|
||||||
|
{
|
||||||
|
pointFactories.remove(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
source/java/org/alfresco/traitextender/Extensible.java
Normal file
25
source/java/org/alfresco/traitextender/Extensible.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
|
||||||
|
public interface Extensible
|
||||||
|
{
|
||||||
|
<T extends Trait> ExtendedTrait<T> getTrait(Class<? extends T> traitAPI);
|
||||||
|
}
|
29
source/java/org/alfresco/traitextender/ExtensionBundle.java
Normal file
29
source/java/org/alfresco/traitextender/ExtensionBundle.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ExtensionBundle
|
||||||
|
{
|
||||||
|
void start(Extender extender);
|
||||||
|
|
||||||
|
void stop(Extender extender);
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
}
|
26
source/java/org/alfresco/traitextender/ExtensionFactory.java
Normal file
26
source/java/org/alfresco/traitextender/ExtensionFactory.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public interface ExtensionFactory<E>
|
||||||
|
{
|
||||||
|
<T extends Trait> E createExtension(T trait);
|
||||||
|
|
||||||
|
boolean canCreateExtensionFor(ExtensionPoint<?, ?> point);
|
||||||
|
}
|
70
source/java/org/alfresco/traitextender/ExtensionPoint.java
Normal file
70
source/java/org/alfresco/traitextender/ExtensionPoint.java
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public class ExtensionPoint<E, M extends Trait>
|
||||||
|
{
|
||||||
|
private Class<E> extensionAPI;
|
||||||
|
|
||||||
|
private Class<M> traitAPI;
|
||||||
|
|
||||||
|
public ExtensionPoint(Class<E> extensionAPI, Class<M> traitAPI)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.extensionAPI = extensionAPI;
|
||||||
|
this.traitAPI = traitAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<E> getExtensionAPI()
|
||||||
|
{
|
||||||
|
return this.extensionAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<M> getTraitAPI()
|
||||||
|
{
|
||||||
|
return this.traitAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return extensionAPI.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (obj instanceof ExtensionPoint)
|
||||||
|
{
|
||||||
|
ExtensionPoint<?,?> pointObj = (ExtensionPoint<?,?>) obj;
|
||||||
|
return extensionAPI.equals(pointObj.extensionAPI)
|
||||||
|
&& traitAPI.equals(pointObj.traitAPI);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "{" + extensionAPI.toString() + "," + traitAPI.toString() + "}";
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public class ExtensionPointActivator
|
||||||
|
{
|
||||||
|
private ExtensionBundle bundle;
|
||||||
|
|
||||||
|
private ExtensionPoint<?, ?> extensionPoint;
|
||||||
|
|
||||||
|
private ExtensionFactory<?> extensionFactory;
|
||||||
|
|
||||||
|
public <E> ExtensionPointActivator(ExtensionBundle bundle, ExtensionPoint<E, ?> extensionPoint,
|
||||||
|
ExtensionFactory<E> extensionFactory)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.bundle = bundle;
|
||||||
|
this.extensionPoint = extensionPoint;
|
||||||
|
this.extensionFactory = extensionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public class ExtensionTargetException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = -502697833178766952L;
|
||||||
|
|
||||||
|
public ExtensionTargetException()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtensionTargetException(String message, Throwable cause, boolean enableSuppression,
|
||||||
|
boolean writableStackTrace)
|
||||||
|
{
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtensionTargetException(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtensionTargetException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtensionTargetException(Throwable cause)
|
||||||
|
{
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public abstract class InstanceExtension<E, T extends Trait>
|
||||||
|
{
|
||||||
|
protected T trait;
|
||||||
|
|
||||||
|
public InstanceExtension(T trait)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.trait = trait;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
|
public class InstanceExtensionFactory<I extends InstanceExtension<E, T>, T extends Trait, E> implements
|
||||||
|
ExtensionFactory<E>
|
||||||
|
{
|
||||||
|
private Class<? extends I> extensionClass;
|
||||||
|
|
||||||
|
private Class<T> traitAPI;
|
||||||
|
|
||||||
|
public <C extends I> InstanceExtensionFactory(Class<C> extensionClass, Class<T> traitAPI,
|
||||||
|
Class<? extends E> extensionAPI)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.extensionClass = extensionClass;
|
||||||
|
this.traitAPI = traitAPI;
|
||||||
|
|
||||||
|
if (!extensionAPI.isAssignableFrom(extensionClass))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Extension class " + extensionClass + " is incompatible with extensio API "
|
||||||
|
+ extensionAPI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <TO extends Trait> E createExtension(TO traitObject)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Trait RTTI will be performed anyway at Constructor#newInstance invocation time
|
||||||
|
T tTrait = (T) traitObject;
|
||||||
|
|
||||||
|
Constructor<? extends I> c = extensionClass.getConstructor(traitAPI);
|
||||||
|
return (E) c.newInstance(tTrait);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canCreateExtensionFor(ExtensionPoint<?, ?> point)
|
||||||
|
{
|
||||||
|
return point.getExtensionAPI().isAssignableFrom(extensionClass)
|
||||||
|
&& traitAPI.isAssignableFrom(point.getTraitAPI());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
source/java/org/alfresco/traitextender/InvalidExtension.java
Normal file
50
source/java/org/alfresco/traitextender/InvalidExtension.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public class InvalidExtension extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = -7146808120353555462L;
|
||||||
|
|
||||||
|
public InvalidExtension()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidExtension(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)
|
||||||
|
{
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidExtension(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidExtension(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidExtension(Throwable cause)
|
||||||
|
{
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ExtensionBundle} that supports simple {@link ExtensionPoint} to
|
||||||
|
* {@link ExtensionFactory} association registration.
|
||||||
|
*
|
||||||
|
* @author Bogdan Horje
|
||||||
|
*/
|
||||||
|
public class RegistryExtensionBundle implements ExtensionBundle
|
||||||
|
{
|
||||||
|
|
||||||
|
private Map<ExtensionPoint<?, ?>, ExtensionFactory<?>> factories = new HashMap<>();
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
public RegistryExtensionBundle(String id)
|
||||||
|
{
|
||||||
|
ParameterCheck.mandatory("id",
|
||||||
|
id);
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an association between the given {@link ExtensionPoint} and
|
||||||
|
* {@link ExtensionFactory}.<br>
|
||||||
|
* At {@link #start(Extender)} time all registered {@link ExtensionPoint}s
|
||||||
|
* will be registered with the given {@link Extender}.<br>
|
||||||
|
* At {@link #stop(Extender)} time all registered {@link ExtensionPoint}s
|
||||||
|
* will be unregistered with the given {@link Extender}.<br>
|
||||||
|
*
|
||||||
|
* @param point
|
||||||
|
* @param factory
|
||||||
|
*/
|
||||||
|
public <E, C extends E, M extends Trait> void register(ExtensionPoint<E, M> point, ExtensionFactory<C> factory)
|
||||||
|
{
|
||||||
|
factories.put(point,
|
||||||
|
factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Extender extender)
|
||||||
|
{
|
||||||
|
Set<Entry<ExtensionPoint<?, ?>, ExtensionFactory<?>>> factoryEntries = factories.entrySet();
|
||||||
|
for (Entry<ExtensionPoint<?, ?>, ExtensionFactory<?>> entry : factoryEntries)
|
||||||
|
{
|
||||||
|
extender.register(entry.getKey(),
|
||||||
|
entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop(Extender extender)
|
||||||
|
{
|
||||||
|
Set<Entry<ExtensionPoint<?, ?>, ExtensionFactory<?>>> factoryEntries = factories.entrySet();
|
||||||
|
for (Entry<ExtensionPoint<?, ?>, ExtensionFactory<?>> entry : factoryEntries)
|
||||||
|
{
|
||||||
|
extender.unregister(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
84
source/java/org/alfresco/traitextender/RouteExtensions.java
Normal file
84
source/java/org/alfresco/traitextender/RouteExtensions.java
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.alfresco.traitextender.AJExtender.ExtensionRoute;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
|
||||||
|
@Aspect
|
||||||
|
public class RouteExtensions
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(RouteExtensions.class);
|
||||||
|
|
||||||
|
@Around("execution(@org.alfresco.traitextender.Extend * *(..)) && (@annotation(extendAnnotation))")
|
||||||
|
public Object intercept(ProceedingJoinPoint pjp, Extend extendAnnotation) throws Throwable {
|
||||||
|
boolean ajPointsEnabled = AJExtender.areAJPointsEnabled();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AJExtender.enableAJPoints();
|
||||||
|
if (ajPointsEnabled)
|
||||||
|
{
|
||||||
|
Object extensibleObject = pjp.getThis();
|
||||||
|
if (!(extensibleObject instanceof Extensible))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Invalid extension point for non extensible class : "
|
||||||
|
+ extensibleObject.getClass());
|
||||||
|
}
|
||||||
|
Extensible extensible = (Extensible) extensibleObject;
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
ExtensionPoint point = new ExtensionPoint(extendAnnotation.extensionAPI(),
|
||||||
|
extendAnnotation.traitAPI());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Object extension = Extender.getInstance().getExtension(extensible,
|
||||||
|
point);
|
||||||
|
if (extension != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
return AJExtender.extendAroundAdvice(pjp,
|
||||||
|
extensible,
|
||||||
|
extendAnnotation,
|
||||||
|
extension);
|
||||||
|
}
|
||||||
|
else if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
MethodSignature ms = (MethodSignature) pjp.getSignature();
|
||||||
|
Method traitMethod = ms.getMethod();
|
||||||
|
|
||||||
|
AJExtender.oneTimeLiveLog(logger,
|
||||||
|
new ExtensionRoute(extendAnnotation,
|
||||||
|
traitMethod));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pjp.proceed();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
AJExtender.revertAJPoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public abstract class SingletonExtension<E, T extends Trait>
|
||||||
|
{
|
||||||
|
private ThreadLocal<T> localTrait = new ThreadLocal<>();
|
||||||
|
|
||||||
|
private Class<T> traitClass;
|
||||||
|
|
||||||
|
public SingletonExtension(Class<T> traitClass)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.traitClass = traitClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean acceptsTrait(Object trait)
|
||||||
|
{
|
||||||
|
return trait != null && acceptsTraitClass(trait.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean acceptsTraitClass(Class<?> aTraitClass)
|
||||||
|
{
|
||||||
|
return traitClass.isAssignableFrom(aTraitClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTrait(T trait)
|
||||||
|
{
|
||||||
|
localTrait.set(trait);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T getTrait()
|
||||||
|
{
|
||||||
|
return localTrait.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
public class SingletonExtensionFactory<E, S extends SingletonExtension<E, T>, T extends Trait> implements
|
||||||
|
ExtensionFactory<E>
|
||||||
|
{
|
||||||
|
private Log logger = LogFactory.getLog(SingletonExtensionFactory.class);
|
||||||
|
|
||||||
|
private class TraiSingletontHandler implements InvocationHandler
|
||||||
|
{
|
||||||
|
private S singleton;
|
||||||
|
|
||||||
|
private T trait;
|
||||||
|
|
||||||
|
public TraiSingletontHandler(S singleton, T trait)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.singleton = singleton;
|
||||||
|
this.trait = trait;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
|
||||||
|
{
|
||||||
|
// Subsequent trait calls might change the trait on this thread.
|
||||||
|
// We save the current trait on the current call-stack in order
|
||||||
|
// to restore it on the finally block.
|
||||||
|
|
||||||
|
T stackedTrait = this.singleton.getTrait();
|
||||||
|
|
||||||
|
this.singleton.setTrait(trait);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return method.invoke(this.singleton,
|
||||||
|
args);
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException error)
|
||||||
|
{
|
||||||
|
throw new InvalidExtension(error);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException error)
|
||||||
|
{
|
||||||
|
throw new InvalidExtension(error);
|
||||||
|
}
|
||||||
|
catch (InvocationTargetException error)
|
||||||
|
{
|
||||||
|
Throwable targetException = error.getTargetException();
|
||||||
|
throw targetException;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.singleton.setTrait(stackedTrait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private S singleton;
|
||||||
|
|
||||||
|
private Class<E> extensionAPI;
|
||||||
|
|
||||||
|
public SingletonExtensionFactory(S singleton, Class<E> extensionAPI) throws InvalidExtension
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
if (!extensionAPI.isAssignableFrom(singleton.getClass()))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension(singleton.getClass() + " is not an " + extensionAPI + " extension.");
|
||||||
|
}
|
||||||
|
this.singleton = singleton;
|
||||||
|
this.extensionAPI = extensionAPI;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <TO extends Trait> E createExtension(TO traitObject)
|
||||||
|
{
|
||||||
|
T tTrait = (T) traitObject;
|
||||||
|
// perform programmatic RTTI
|
||||||
|
if (!singleton.acceptsTrait(traitObject))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Extension factory error : " + singleton.getClass() + " does not support trait "
|
||||||
|
+ traitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
String traitTrace = traitObject != null ? traitObject.getClass().toString() : "<null> trait";
|
||||||
|
logger.info("Creating singleton extension " + singleton.getClass() + "<-" + traitTrace);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("Creating singleton extension " + singleton.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (E) Proxy.newProxyInstance(getClass().getClassLoader(),
|
||||||
|
new Class[] { extensionAPI },
|
||||||
|
new TraiSingletontHandler(singleton,
|
||||||
|
tTrait));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canCreateExtensionFor(ExtensionPoint<?, ?> point)
|
||||||
|
{
|
||||||
|
return point.getExtensionAPI().isAssignableFrom(singleton.getClass())
|
||||||
|
&& singleton.acceptsTraitClass(point.getTraitAPI());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public abstract class SpringBeanExtension<E,T extends Trait> extends SingletonExtension<E, T>
|
||||||
|
{
|
||||||
|
private SpringExtensionPoint extensionPoint;
|
||||||
|
|
||||||
|
public SpringBeanExtension(Class<T> traitClass)
|
||||||
|
{
|
||||||
|
super(traitClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtensionPoint(SpringExtensionPoint extensionPoint)
|
||||||
|
{
|
||||||
|
this.extensionPoint = extensionPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(RegistryExtensionBundle bundle) throws InvalidExtension
|
||||||
|
{
|
||||||
|
extensionPoint.register(bundle,this);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
public class SpringExtensionBundle implements InitializingBean
|
||||||
|
{
|
||||||
|
private static Log logger = LogFactory.getLog(SpringExtensionBundle.class);
|
||||||
|
|
||||||
|
private List<SpringBeanExtension<?, ?>> extensions = Collections.emptyList();
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private boolean enabled=true;
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtensions(List<SpringBeanExtension<?, ?>> extensions)
|
||||||
|
{
|
||||||
|
this.extensions = extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception
|
||||||
|
{
|
||||||
|
if (this.enabled)
|
||||||
|
{
|
||||||
|
logger.info("Starting extension bundle " + id);
|
||||||
|
|
||||||
|
RegistryExtensionBundle extensionBundle = new RegistryExtensionBundle(id);
|
||||||
|
|
||||||
|
for (SpringBeanExtension<?, ?> springExtension : extensions)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
springExtension.register(extensionBundle);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Could not register extension " + springExtension + " with "
|
||||||
|
+ extensionBundle,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Extender.getInstance().start(extensionBundle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("Extension bundle " + id + " is disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public class SpringExtensionPoint
|
||||||
|
{
|
||||||
|
private String trait;
|
||||||
|
|
||||||
|
private String extension;
|
||||||
|
|
||||||
|
public void setTrait(String trait)
|
||||||
|
{
|
||||||
|
this.trait = trait;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtension(String extension)
|
||||||
|
{
|
||||||
|
this.extension = extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
public void register(RegistryExtensionBundle bundle, SpringBeanExtension<?, ?> extensionBean)
|
||||||
|
throws InvalidExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class<?> extensionAPI = Class.forName(extension);
|
||||||
|
Class<? extends Trait> traitAPI = (Class<? extends Trait>) Class.forName(trait);
|
||||||
|
|
||||||
|
// perform RTTIs in order to avoid later cast exceptions
|
||||||
|
|
||||||
|
if (!Trait.class.isAssignableFrom(traitAPI))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Non " + Trait.class + " spring extension point : " + traitAPI);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extensionBean.acceptsTraitClass(traitAPI))
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Unsupported trait class : " + traitAPI + " in extension point targeting "
|
||||||
|
+ extensionBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle.register(new ExtensionPoint(extensionAPI,
|
||||||
|
traitAPI),
|
||||||
|
new SingletonExtensionFactory(extensionBean,
|
||||||
|
extensionAPI));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (InvalidExtension error)
|
||||||
|
{
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException error)
|
||||||
|
{
|
||||||
|
throw new InvalidExtension("Extension point definition class not found.",
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,15 @@
|
|||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
|
||||||
|
public class SpringTraitExtenderConfigurator implements InitializingBean
|
||||||
|
{
|
||||||
|
|
||||||
|
private boolean enableTraitExtender;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
source/java/org/alfresco/traitextender/Trait.java
Normal file
24
source/java/org/alfresco/traitextender/Trait.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2015 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
package org.alfresco.traitextender;
|
||||||
|
|
||||||
|
public interface Trait
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user