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:
Alan Davis
2015-11-14 13:32:09 +00:00
parent 8123671677
commit 98213d66d7
27 changed files with 1992 additions and 0 deletions

View File

@@ -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;
}
}

View 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);
}
}

View 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 AJExtensibleCompilingError
{
String getShortMessage();
}

View File

@@ -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();
}
}

View 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);
}
}

View 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();
}

View 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;
}
}

View 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);
}

View 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);
}
}

View 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);
}

View 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();
}

View 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);
}

View 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() + "}";
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View 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;
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());
}
}

View 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);
}
}

View File

@@ -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;
}
}

View 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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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.");
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
{
}
}

View 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
{
}