diff --git a/source/java/org/alfresco/traitextender/AJDanglingExtensionError.java b/source/java/org/alfresco/traitextender/AJDanglingExtensionError.java
deleted file mode 100644
index 05540b65f0..0000000000
--- a/source/java/org/alfresco/traitextender/AJDanglingExtensionError.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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;
- }
-
-}
diff --git a/source/java/org/alfresco/traitextender/AJExtender.java b/source/java/org/alfresco/traitextender/AJExtender.java
index 87ce87a2ea..195253ef49 100644
--- a/source/java/org/alfresco/traitextender/AJExtender.java
+++ b/source/java/org/alfresco/traitextender/AJExtender.java
@@ -42,6 +42,32 @@ import org.aspectj.lang.reflect.MethodSignature;
import com.hazelcast.util.ConcurrentHashSet;
+/**
+ * Static utility used for aspectJ extension consistency , routing and for
+ * maintaining thread-local extension-bypass context stack.
+ * AspectJ extension routing distinguishes between the following contexts in
+ * which an extended method (i.e. a method with an {@link Extend} annotation)
+ * can be called:
+ *
+ * - Extend context
+ * when an extended method is called and the extension overrides the method call
+ *
+ * - Local proceed context
+ * when an extension method needs to execute the original method that it has
+ * overridden.
+ * - Extension bypass context
+ * when a call to an extended method needs to be completed with the extensions
+ * disabled for that call
+ *
+ *
+ * The {@link AJExtender} can check {@link ExtensionPoint} definitions for
+ * consistency by compiling extensible classes. The compilation process fails if
+ * there are inconsistencies between {@link ExtensionPoint}s and {@link Extend}
+ * annotated methods (egg. an annotated method can not be mapped to a method in
+ * the indicated extension by signature matching).
+ *
+ * @author Bogdan Horje
+ */
public class AJExtender
{
private static final Object[] SAFE_NULL_ARGS = new Object[0];
@@ -50,6 +76,9 @@ public class AJExtender
private static ConcurrentHashSet oneTimeLogSet = null;
+ /**
+ *
+ */
private static final ThreadLocal> ajPointsLocalEnabled = new ThreadLocal>()
{
protected Stack initialValue()
@@ -60,6 +89,9 @@ public class AJExtender
};
};
+ /**
+ * @author Bogdan Horje
+ */
static class ProceedingContext
{
final Extend extend;
@@ -83,12 +115,119 @@ public class AJExtender
};
};
+ /**
+ * Implementors are aspectJ extension ignoring closures. When executing
+ * {@link ExtensionBypass}es using {@link AJExtender#run(ExtensionBypass)} or
+ * {@link AJExtender#run(ExtensionBypass, Class[])} the {@link #run()}
+ * method code will ignore extension overrides.
+ *
+ * @author Bogdan Horje
+ */
public static interface ExtensionBypass
{
R run() throws Throwable;
}
- public static class CompiledExtensible
+ /**
+ * Thrown-exception or stored error resulted compiling inconsistencies found
+ * during aspectJ extensible classes compilation.
+ *
+ * @see {@link AJExtender#compile(Class)}
+ * @author Bogdan Horje
+ */
+ interface AJExtensibleCompilingError
+ {
+ String getShortMessage();
+ }
+
+ /**
+ * Thrown or stored on compiling inconsistencies found during aspectJ
+ * extensible classes compilation.
+ *
+ * @see {@link AJExtender#compile(Class)}
+ * @author Bogdan Horje
+ */
+ static class AJExtensibleCompilingException extends Exception implements AJExtensibleCompilingError
+ {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ AJExtensibleCompilingException()
+ {
+ super();
+ }
+
+ AJExtensibleCompilingException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace)
+ {
+ super(message,
+ cause,
+ enableSuppression,
+ writableStackTrace);
+ }
+
+ AJExtensibleCompilingException(String message, Throwable cause)
+ {
+ super(message,
+ cause);
+ }
+
+ AJExtensibleCompilingException(String message)
+ {
+ super(message);
+ }
+
+ AJExtensibleCompilingException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ @Override
+ public String getShortMessage()
+ {
+ return getMessage();
+ }
+
+ }
+
+ /**
+ * Signals the existence of extension methods that are not matched with
+ * same-signature methods through {@link Extend} annotations within
+ * {@link Extensible}.
+ *
+ * @author Bogdan Horje
+ */
+ static class AJDanglingExtensionError implements AJExtensibleCompilingError
+ {
+ private Method danglingMethod;
+
+ private Extend extendDeclaration;
+
+ AJDanglingExtensionError(Method danglingMethod, Extend extendDeclaration)
+ {
+ super();
+ this.danglingMethod = danglingMethod;
+ this.extendDeclaration = extendDeclaration;
+ }
+
+ @Override
+ public String getShortMessage()
+ {
+ return "Dangling extension method " + danglingMethod + " " + extendDeclaration;
+ }
+ }
+
+ /**
+ * An {@link Extensible} sub class compilation result containing all
+ * {@link Extend} mapped routes, not routed or dangling methods within the
+ * give extensible class and/or possible compilation errors.
+ *
+ * @author Bogdan Horje
+ */
+ static class CompiledExtensible
{
private Class extends Extensible> extensible;
@@ -98,28 +237,28 @@ public class AJExtender
private List errors = new LinkedList<>();
- public CompiledExtensible(Class extends Extensible> extensible)
+ CompiledExtensible(Class extends Extensible> extensible)
{
super();
this.extensible = extensible;
}
- public Class extends Extensible> getExtensible()
+ Class extends Extensible> getExtensible()
{
return this.extensible;
}
- public void add(AJExtensibleCompilingError error)
+ void add(AJExtensibleCompilingError error)
{
this.errors.add(error);
}
- public boolean hasErrors()
+ boolean hasErrors()
{
return !errors.isEmpty();
}
- public String getErrorsString()
+ String getErrorsString()
{
StringBuilder builder = new StringBuilder();
@@ -132,12 +271,12 @@ public class AJExtender
return builder.toString();
}
- public List getErrors()
+ List getErrors()
{
return this.errors;
}
- public void add(ExtensionRoute route)
+ void add(ExtensionRoute route)
{
if (route.extensionMethod == null)
{
@@ -152,24 +291,29 @@ public class AJExtender
}
}
- public Collection getAllNotRouted()
+ Collection getAllNotRouted()
{
return notRoutedMethods.values();
}
- public int getExtendedMethodCount()
+ int getExtendedMethodCount()
{
return routedMethods.size() + notRoutedMethods.size();
}
- public String getInfo()
+ String getInfo()
{
return extensible.getName() + "{ " + routedMethods.size() + " routed methods; " + notRoutedMethods.size()
+ " not routed methods;" + errors.size() + " errors}";
}
}
- public static class ExtensionRoute
+ /**
+ * Encapsulates extended-method to extension-method mapping information.
+ *
+ * @author Bogdan Horje
+ */
+ static class ExtensionRoute
{
final Extend extendAnnotation;
@@ -237,27 +381,48 @@ public class AJExtender
}
}
- public static boolean areAJPointsEnabled()
+ /**
+ * @return true
if aspectJ routed extensions are enabled on the
+ * call stack of the current thread
+ */
+ static boolean areAJPointsEnabled()
{
return ajPointsLocalEnabled.get().peek();
}
+ /**
+ * Pushes a new enabled state of the aspectJ routed extensions on the
+ * current thread execution stack.
+ */
static void enableAJPoints()
{
ajPointsLocalEnabled.get().push(true);
}
+ /**
+ * Pops the current aspectJ routed extensions enablement state from the
+ * current thread execution stack.
+ */
static void revertAJPoints()
{
ajPointsLocalEnabled.get().pop();
}
- public static R throwableRun(AJExtender.ExtensionBypass section) throws Throwable
+ /**
+ * Exception throwing extension-bypass closure runner method.
+ * Sets up adequate call contexts to avoid exception calling and than
+ * delegates to the given closure.
+ *
+ * @param closure
+ * @return
+ * @throws Throwable
+ */
+ static R throwableRun(AJExtender.ExtensionBypass closure) throws Throwable
{
try
{
AJExtender.ajPointsLocalEnabled.get().push(false);
- return section.run();
+ return closure.run();
}
finally
@@ -266,11 +431,24 @@ public class AJExtender
}
}
- public static R run(AJExtender.ExtensionBypass section, Class>[] exTypes) throws Throwable
+ /**
+ * Extension-bypass closure runner method.
+ * Sets up adequate call contexts to avoid exception calling and than
+ * delegates to the given closure.
+ * Only the given exTypes exceptions will be passed on to the calling
+ * context, all others will be wrapped as
+ * {@link UndeclaredThrowableException}s.
+ *
+ * @param closure
+ * @param exTypes
+ * @return
+ * @throws Throwable
+ */
+ public static R run(AJExtender.ExtensionBypass closure, Class>[] exTypes) throws Throwable
{
try
{
- return throwableRun(section);
+ return throwableRun(closure);
}
catch (Error | RuntimeException error)
{
@@ -283,7 +461,7 @@ public class AJExtender
}
}
- public static Throwable asCheckThrowable(Throwable error, Class>... checkedThrowableTypes)
+ static Throwable asCheckThrowable(Throwable error, Class>... checkedThrowableTypes)
{
Class extends Throwable> errorClass = error.getClass();
for (int i = 0; i < checkedThrowableTypes.length; i++)
@@ -296,11 +474,19 @@ public class AJExtender
return new UndeclaredThrowableException(error);
}
- public static R run(AJExtender.ExtensionBypass section)
+ /**
+ * Extension-bypass closure runner method.
+ * Sets up adequate call contexts to avoid exception calling and than
+ * delegates to the given closure.
+ *
+ * @param closure
+ * @return
+ */
+ public static R run(AJExtender.ExtensionBypass closure)
{
try
{
- return throwableRun(section);
+ return throwableRun(closure);
}
catch (Error | RuntimeException error)
{
@@ -312,7 +498,13 @@ public class AJExtender
}
}
- public static void oneTimeLiveLog(Log logger, ExtensionRoute route)
+ /**
+ * Logs each method routing path once per session.
+ *
+ * @param logger
+ * @param route
+ */
+ static void oneTimeLiveLog(Log logger, ExtensionRoute route)
{
synchronized (AJExtender.class)
{
@@ -336,8 +528,13 @@ public class AJExtender
}
}
- public static CompiledExtensible compile(Class extends Extensible> extensible)
- throws AJExtensibleCompilingException
+ /**
+ * @param extensible
+ * @return a compilation result containing all {@link Extend} mapped routes,
+ * not routed or dangling methods within the give extensible class.
+ * @throws AJExtensibleCompilingException
+ */
+ static CompiledExtensible compile(Class extends Extensible> extensible) throws AJExtensibleCompilingException
{
logger.info("Compiling extensible " + extensible);
@@ -416,7 +613,18 @@ public class AJExtender
return compiledExtensible;
}
- public static Object extendAroundAdvice(JoinPoint thisJoinPoint, Extensible extensible, Extend extendAnnotation,
+ /**
+ * Around advice helper that matches the advised method with its
+ * corresponding extension method, sets up aspectJ call contexts (egg. the
+ * local-proceed context) and delegates to the extension method.
+ *
+ * @param thisJoinPoint
+ * @param extensible
+ * @param extendAnnotation
+ * @param extension
+ * @return the result of the extended method
+ */
+ static Object extendAroundAdvice(JoinPoint thisJoinPoint, Extensible extensible, Extend extendAnnotation,
Object extension)
{
@@ -479,7 +687,12 @@ public class AJExtender
}
- public static boolean isLocalProceeder(Method method)
+ /**
+ * @param method
+ * @return true
if the given method has the same signature as
+ * the currently aspectJ extension-overridden method
+ */
+ static boolean isLocalProceeder(Method method)
{
if (!ajLocalProceedingJoinPoints.get().isEmpty())
{
@@ -495,7 +708,14 @@ public class AJExtender
}
}
- public static Object localProceed(Object[] args) throws Throwable
+ /**
+ * Calls the currently overridden method in local-proceed context - proceeds
+ * with the aspectJ join point saved on the current call stack.
+ *
+ * @param args
+ * @throws Throwable
+ */
+ static Object localProceed(Object[] args) throws Throwable
{
ProceedingContext proceedingCotext = ajLocalProceedingJoinPoints.get().peek();
Object[] safeArgs = args == null ? SAFE_NULL_ARGS : args;
diff --git a/source/java/org/alfresco/traitextender/AJExtensibleCompilingError.java b/source/java/org/alfresco/traitextender/AJExtensibleCompilingError.java
deleted file mode 100644
index 65b7ea29dc..0000000000
--- a/source/java/org/alfresco/traitextender/AJExtensibleCompilingError.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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();
-}
diff --git a/source/java/org/alfresco/traitextender/AJExtensibleCompilingException.java b/source/java/org/alfresco/traitextender/AJExtensibleCompilingException.java
deleted file mode 100644
index b450758bd8..0000000000
--- a/source/java/org/alfresco/traitextender/AJExtensibleCompilingException.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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();
- }
-
-}
diff --git a/source/java/org/alfresco/traitextender/Extend.java b/source/java/org/alfresco/traitextender/Extend.java
index 64f425536c..808b063a27 100644
--- a/source/java/org/alfresco/traitextender/Extend.java
+++ b/source/java/org/alfresco/traitextender/Extend.java
@@ -16,14 +16,28 @@
* 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;
+/**
+ * A runtime retained annotation that marks AJ-trait-extended methods of
+ * {@link Extensible} objects.
+ * It defines the actual circumstances in which the {@link ExtensionPoint}
+ * defined using {@link #extensionAPI()} and {@link #traitAPI()} has its
+ * extension invoked.
+ * Methods marked by this aspect are advised by an extension-routing around
+ * advice in {@link RouteExtensions}. Consequently the call will be routed to a
+ * method of an extension object having the same signature as the marked method.
+ *
+ * @author Bogdan Horje
+ */
@Retention(RetentionPolicy.RUNTIME)
public @interface Extend
{
Class> extensionAPI();
+
Class extends Trait> traitAPI();
}
diff --git a/source/java/org/alfresco/traitextender/ExtendedTrait.java b/source/java/org/alfresco/traitextender/ExtendedTrait.java
index c701cffeac..7cdd685653 100644
--- a/source/java/org/alfresco/traitextender/ExtendedTrait.java
+++ b/source/java/org/alfresco/traitextender/ExtendedTrait.java
@@ -16,10 +16,18 @@
* 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;
+/**
+ * Trait based extension reference holder.
+ * Keeps track of extension references for one extensible and allows the
+ * collection of those extensions when the extensible is collected.
+ *
+ * @author Bogdan Horje
+ */
public class ExtendedTrait
{
private ConcurrentHashMap, Object> extensions = new ConcurrentHashMap, Object>();
@@ -40,23 +48,24 @@ public class ExtendedTrait
public E getExtension(Class extensionAPI)
{
@SuppressWarnings("unchecked")
- E extension=(E) extensions.get(extensionAPI);
-
+ E extension = (E) extensions.get(extensionAPI);
+
return extension;
}
-
+
public synchronized E extend(Class extensionAPI, ExtensionFactory factory)
{
@SuppressWarnings("unchecked")
- E extension=(E) extensions.get(extensionAPI);
-
- if (extension==null)
+ E extension = (E) extensions.get(extensionAPI);
+
+ if (extension == null)
{
extension = factory.createExtension(trait);
- extensions.put(extensionAPI, extension);
-
+ extensions.put(extensionAPI,
+ extension);
+
}
-
+
return extension;
}
}
diff --git a/source/java/org/alfresco/traitextender/Extender.java b/source/java/org/alfresco/traitextender/Extender.java
index c9998533df..5afb067298 100644
--- a/source/java/org/alfresco/traitextender/Extender.java
+++ b/source/java/org/alfresco/traitextender/Extender.java
@@ -16,22 +16,37 @@
* 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;
-
-
+/**
+ * Trait-extender central extension registry and life cycle handler.
+ * Implementors must handle:
+ *
+ * -
+ * {@link ExtensionBundle} life cycle operations that start and stop bundles
+ * of extension points implementations at runtime.
+ * See also: {@link Extender#start(ExtensionBundle)} ,
+ * {@link Extender#stop(ExtensionBundle)} and {@link Extender#stopAll()} ).
+ * - The management of the extension point factory registry. At runtime
+ * extension point factories can be registered or unregistered. The registered
+ * factories will later be used for creating extensions of extensible-traits
+ * (see {@link Extender#getExtension(Extensible, ExtensionPoint))}.
+ * See also:{@link #register(ExtensionPoint, ExtensionFactory)} and
+ * {@link Extender#unregister(ExtensionPoint)}
+ * - The creation of extension for a given extensible trait.
+ * See: {@link Extender#getExtension(Extensible, ExtensionPoint)}
+ *
+ *
+ * @author Bogdan Horje
+ */
public abstract class Extender
{
private static Extender instance;
-
- public static class Configuration {
- private boolean enabled;
- private List disbaledBundles;
-
-
- }
+ /**
+ * @return singleton {@link ExtenderImpl} instance
+ */
public static synchronized Extender getInstance()
{
if (instance == null)
@@ -41,16 +56,72 @@ public abstract class Extender
return instance;
}
+ /**
+ * Start life-cycle phase trigger method.
+ * Upon successful execution the bundle will have all its extension points
+ * registered using {@link #register(ExtensionPoint, ExtensionFactory)} and
+ * ready to be.
+ *
+ * @param bundle to be started
+ */
public abstract void start(ExtensionBundle bundle);
-
+
+ /**
+ * Start life-cycle phase trigger method.
+ * Upon successful execution the bundle will have all its extension points
+ * unregistered using {@link #unregister(ExtensionPoint)}.
+ *
+ * @param bundle to be stopped
+ */
public abstract void stop(ExtensionBundle bundle);
-
+
+ /**
+ * Stops all previously registered {@link ExtensionBundle}s.
+ */
public abstract void stopAll();
+ /**
+ * Creates and returns a unique per {@link ExtensibleTrait} object or
+ * null
if no extension-point factory is registered for the
+ * given {@link ExtensionPoint}.
+ * Given that {@link Extensible#getTrait(Class)} is used to obtain the
+ * {@link ExtendedTrait} that the returned extension is uniquely associated
+ * with, the uniqueness and garbage collection of the returned extension is
+ * dependent on how the given {@link Extensible} handles its
+ * {@link ExtendedTrait}s.
+ *
+ * @param anExtensible
+ * @param point
+ * @return a unique per {@link ExtensibleTrait} extension object or
+ * null
if no extension-point factory is registered for
+ * the given {@link ExtensionPoint}
+ */
public abstract E getExtension(Extensible anExtensible, ExtensionPoint point);
-
+
+ /**
+ * Registers an extension-point to factory association to be used in
+ * extension creation. The presence of an association for a given extension
+ * point guarantees that, when requested, a unique extension object per
+ * {@link ExtensibleTrait} extension is returned by
+ * {@link #getExtension(Extensible, ExtensionPoint)}.
+ *
+ * @param point the extension point to be associated with the given
+ * extension factory during extension retrieval using
+ * {@link Extender#getExtension(Extensible, ExtensionPoint)}
+ * @param factory
+ */
public abstract void register(ExtensionPoint, ?> point, ExtensionFactory> factory);
-
+
+ /**
+ * Unregisters an extension-point to factory association of the given
+ * extension point. The absence of an association for a given extension
+ * point guarantees that, when requested, a null is returned by
+ * {@link #getExtension(Extensible, ExtensionPoint)}.
+ * Unregistering extension points does not force the garbage collection of
+ * the already created extensions.
+ *
+ * @param point
+ */
public abstract void unregister(ExtensionPoint, ?> point);
-
+
}
diff --git a/source/java/org/alfresco/traitextender/ExtenderImpl.java b/source/java/org/alfresco/traitextender/ExtenderImpl.java
index c0bed57a40..62db5496b6 100644
--- a/source/java/org/alfresco/traitextender/ExtenderImpl.java
+++ b/source/java/org/alfresco/traitextender/ExtenderImpl.java
@@ -27,12 +27,15 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+/**
+ * Standard-singleton {@link Extender} implementation.
+ *
+ * @author Bogdan Horje
+ */
public class ExtenderImpl extends Extender
{
private static Log logger = LogFactory.getLog(Extender.class);
-
-
private List bundles = new LinkedList();
private Map, ExtensionFactory>> pointFactories = new ConcurrentHashMap, ExtensionFactory>>();
@@ -62,7 +65,7 @@ public class ExtenderImpl extends Extender
{
E extension = null;
- //consistency is checked at registration time
+ // consistency is checked at registration time
@SuppressWarnings("unchecked")
ExtensionFactory factory = (ExtensionFactory) pointFactories.get(point);
diff --git a/source/java/org/alfresco/traitextender/Extensible.java b/source/java/org/alfresco/traitextender/Extensible.java
index d634cd6b7c..54e0673123 100644
--- a/source/java/org/alfresco/traitextender/Extensible.java
+++ b/source/java/org/alfresco/traitextender/Extensible.java
@@ -16,9 +16,25 @@
* 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;
-
+/**
+ * An {@link Extensible} object exposes a set of {@link Trait}s as
+ * {@link ExtendedTrait}s objects.
+ * An {@link ExtendedTrait} is an association between a {@link Trait} exposing
+ * object and several extension objects.
+ * The actual {@link Trait}s and associated extensions provided by an
+ * {@link Extensible} object are given by its {@link ExtensionPoint} handling
+ * strategy and by the current set of registered extensions (see
+ * {@link Extender}).
+ * The exposed {@link Trait}s can be thought of as parts of an object's
+ * interface that will be exposed to an extension. Upon the extension invocation
+ * the given trait instances will be made available to their corresponding
+ * extensions.
+ *
+ * @author Bogdan Horje
+ */
public interface Extensible
{
ExtendedTrait getTrait(Class extends T> traitAPI);
diff --git a/source/java/org/alfresco/traitextender/ExtensionBundle.java b/source/java/org/alfresco/traitextender/ExtensionBundle.java
index c1e9622bb3..6592e13725 100644
--- a/source/java/org/alfresco/traitextender/ExtensionBundle.java
+++ b/source/java/org/alfresco/traitextender/ExtensionBundle.java
@@ -16,14 +16,31 @@
* 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;
-
+/**
+ * Implementors are sets of extension implementations that are registered on
+ * specific {@link ExtensionPoint} for given {@link Extender}s.
+ *
+ * @author Bogdan Horje
+ */
public interface ExtensionBundle
{
+ /**
+ * Sets up an registers extension factories with the give {@link Extender}
+ * for all extensions defined by this bundle.
+ *
+ * @param extender
+ */
void start(Extender extender);
-
+
+ /**
+ * Unregisters all defined extensions from the given {@link Extender} .
+ *
+ * @param extender
+ */
void stop(Extender extender);
-
+
String getId();
}
diff --git a/source/java/org/alfresco/traitextender/ExtensionFactory.java b/source/java/org/alfresco/traitextender/ExtensionFactory.java
index 9726b562e4..60e0142c88 100644
--- a/source/java/org/alfresco/traitextender/ExtensionFactory.java
+++ b/source/java/org/alfresco/traitextender/ExtensionFactory.java
@@ -16,11 +16,27 @@
* 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;
+/**
+ * Creates extension instances for given {@link Trait}s and
+ * {@link ExtensionPoint}s.
+ *
+ * @author Bogdan Horje
+ */
public interface ExtensionFactory
{
E createExtension(T trait);
-
+
+ /**
+ * @param point
+ * @return true
if the given extensio-point API elements are
+ * compatible with the returned extension (i.e. the given extension
+ * API is assignable form the type of the extension created by this
+ * factory and the {@link Trait} accepted as aparameter in
+ * {@link #createExtension(Trait)} is assignable from the type of
+ * the given trait API).
+ */
boolean canCreateExtensionFor(ExtensionPoint, ?> point);
}
diff --git a/source/java/org/alfresco/traitextender/ExtensionPoint.java b/source/java/org/alfresco/traitextender/ExtensionPoint.java
index 18ab2ccd67..226f0c9b44 100644
--- a/source/java/org/alfresco/traitextender/ExtensionPoint.java
+++ b/source/java/org/alfresco/traitextender/ExtensionPoint.java
@@ -16,8 +16,20 @@
* 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;
+/**
+ * Defines a two-way interfacing mechanism between a {@link Trait} exposing
+ * object and an extension of that object.
+ * The extended object can call methods of the {@link #extensionAPI} which will
+ * be able to interact with the extended object through the {@link #traitAPI}
+ * interface it was paired with in the extension point. The actual circumstances
+ * in which the extension methods are invoked are not defined by the extension
+ * point.
+ *
+ * @author Bogdan Horje
+ */
public class ExtensionPoint
{
private Class extensionAPI;
@@ -52,9 +64,8 @@ public class ExtensionPoint
{
if (obj instanceof ExtensionPoint)
{
- ExtensionPoint,?> pointObj = (ExtensionPoint,?>) obj;
- return extensionAPI.equals(pointObj.extensionAPI)
- && traitAPI.equals(pointObj.traitAPI);
+ ExtensionPoint, ?> pointObj = (ExtensionPoint, ?>) obj;
+ return extensionAPI.equals(pointObj.extensionAPI) && traitAPI.equals(pointObj.traitAPI);
}
else
{
diff --git a/source/java/org/alfresco/traitextender/ExtensionPointActivator.java b/source/java/org/alfresco/traitextender/ExtensionPointActivator.java
deleted file mode 100644
index f4d445fc67..0000000000
--- a/source/java/org/alfresco/traitextender/ExtensionPointActivator.java
+++ /dev/null
@@ -1,21 +0,0 @@
-
-package org.alfresco.traitextender;
-
-public class ExtensionPointActivator
-{
- private ExtensionBundle bundle;
-
- private ExtensionPoint, ?> extensionPoint;
-
- private ExtensionFactory> extensionFactory;
-
- public ExtensionPointActivator(ExtensionBundle bundle, ExtensionPoint extensionPoint,
- ExtensionFactory extensionFactory)
- {
- super();
- this.bundle = bundle;
- this.extensionPoint = extensionPoint;
- this.extensionFactory = extensionFactory;
- }
-
-}
diff --git a/source/java/org/alfresco/traitextender/ExtensionTargetException.java b/source/java/org/alfresco/traitextender/ExtensionTargetException.java
index b6ead38069..bea81d61ea 100644
--- a/source/java/org/alfresco/traitextender/ExtensionTargetException.java
+++ b/source/java/org/alfresco/traitextender/ExtensionTargetException.java
@@ -16,8 +16,14 @@
* 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;
+/**
+ * Trait-extension runtime target-exception wrapper.
+ *
+ * @author Bogdan Horje
+ */
public class ExtensionTargetException extends RuntimeException
{
private static final long serialVersionUID = -502697833178766952L;
@@ -30,12 +36,16 @@ public class ExtensionTargetException extends RuntimeException
public ExtensionTargetException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace)
{
- super(message, cause, enableSuppression, writableStackTrace);
+ super(message,
+ cause,
+ enableSuppression,
+ writableStackTrace);
}
public ExtensionTargetException(String message, Throwable cause)
{
- super(message, cause);
+ super(message,
+ cause);
}
public ExtensionTargetException(String message)
diff --git a/source/java/org/alfresco/traitextender/InstanceExtension.java b/source/java/org/alfresco/traitextender/InstanceExtension.java
index 2569a6eef6..fe50e1403d 100644
--- a/source/java/org/alfresco/traitextender/InstanceExtension.java
+++ b/source/java/org/alfresco/traitextender/InstanceExtension.java
@@ -19,6 +19,12 @@
package org.alfresco.traitextender;
+/**
+ * Sub classes are extension API implementors that get instantiated once per
+ * extensible-extension point definition.
+ *
+ * @author Bogdan Horje
+ */
public abstract class InstanceExtension
{
protected T trait;
diff --git a/source/java/org/alfresco/traitextender/InstanceExtensionFactory.java b/source/java/org/alfresco/traitextender/InstanceExtensionFactory.java
index b155976603..28ebdfc6a1 100644
--- a/source/java/org/alfresco/traitextender/InstanceExtensionFactory.java
+++ b/source/java/org/alfresco/traitextender/InstanceExtensionFactory.java
@@ -21,6 +21,12 @@ package org.alfresco.traitextender;
import java.lang.reflect.Constructor;
+/**
+ * Creates extension sub classes that are extension API implementors once per
+ * extensible-extension point definition.
+ *
+ * @author Bogdan Horje
+ */
public class InstanceExtensionFactory, T extends Trait, E> implements
ExtensionFactory
{
@@ -48,7 +54,8 @@ public class InstanceExtensionFactory, T exten
{
try
{
- // Trait RTTI will be performed anyway at Constructor#newInstance invocation time
+ // Trait RTTI will be performed anyway at Constructor#newInstance
+ // invocation time
T tTrait = (T) traitObject;
Constructor extends I> c = extensionClass.getConstructor(traitAPI);
diff --git a/source/java/org/alfresco/traitextender/InvalidExtension.java b/source/java/org/alfresco/traitextender/InvalidExtension.java
index 7d3f885d48..b35f4a8fa1 100644
--- a/source/java/org/alfresco/traitextender/InvalidExtension.java
+++ b/source/java/org/alfresco/traitextender/InvalidExtension.java
@@ -16,8 +16,14 @@
* 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;
+/**
+ * Signals an invalid extension state or extension definition.
+ *
+ * @author Bogdan Horje
+ */
public class InvalidExtension extends RuntimeException
{
private static final long serialVersionUID = -7146808120353555462L;
@@ -29,12 +35,16 @@ public class InvalidExtension extends RuntimeException
public InvalidExtension(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)
{
- super(message, cause, enableSuppression, writableStackTrace);
+ super(message,
+ cause,
+ enableSuppression,
+ writableStackTrace);
}
public InvalidExtension(String message, Throwable cause)
{
- super(message, cause);
+ super(message,
+ cause);
}
public InvalidExtension(String message)
diff --git a/source/java/org/alfresco/traitextender/RouteExtensions.java b/source/java/org/alfresco/traitextender/RouteExtensions.java
index 5570f24c94..145313c6b0 100644
--- a/source/java/org/alfresco/traitextender/RouteExtensions.java
+++ b/source/java/org/alfresco/traitextender/RouteExtensions.java
@@ -16,6 +16,7 @@
* 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;
@@ -28,13 +29,29 @@ import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
+/**
+ * An method override extension routing aspect.
+ * Overrides calls to methods marked by an {@link Extend} annotation with calls
+ * to methods having the same signature in extensions registered for the
+ * {@link ExtensionPoint} referred by the {@link Extend} method annotation.
+ * Overriding methods can call the overridden method using its correspondent
+ * {@link Trait} representation (i.e. a method having the same signature).
+ * If no extension is defined the call proceeds with the original method.
+ * The aspect uses the {@link AJExtender} static utility to for extension
+ * invocation and for maintaining thread-local extension-bypass contexts as not all
+ * calls must be overridden and calls from within the extension must be aware of
+ * this context (see {@link AJProxyTrait}).
+ *
+ * @author Bogdan Horje
+ */
@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 {
+ public Object intercept(ProceedingJoinPoint pjp, Extend extendAnnotation) throws Throwable
+ {
boolean ajPointsEnabled = AJExtender.areAJPointsEnabled();
try
{
@@ -57,7 +74,7 @@ public class RouteExtensions
point);
if (extension != null)
{
-
+
return AJExtender.extendAroundAdvice(pjp,
extensible,
extendAnnotation,
diff --git a/source/java/org/alfresco/traitextender/SingletonExtension.java b/source/java/org/alfresco/traitextender/SingletonExtension.java
index b15f618893..890e0e7e64 100644
--- a/source/java/org/alfresco/traitextender/SingletonExtension.java
+++ b/source/java/org/alfresco/traitextender/SingletonExtension.java
@@ -19,6 +19,15 @@
package org.alfresco.traitextender;
+/**
+ * A singleton extension API implementor. The singleton extension continues to
+ * exist after the extensible has been collected. The instance of this extension
+ * is shared among {@link Extensible}s defining extension-points that this
+ * extension is bound to.The {@link Trait} it requires is set at call-time on
+ * the local thread.
+ *
+ * @author Bogdan Horje
+ */
public abstract class SingletonExtension
{
private ThreadLocal localTrait = new ThreadLocal<>();
@@ -35,7 +44,7 @@ public abstract class SingletonExtension
{
return trait != null && acceptsTraitClass(trait.getClass());
}
-
+
public boolean acceptsTraitClass(Class> aTraitClass)
{
return traitClass.isAssignableFrom(aTraitClass);
@@ -46,6 +55,10 @@ public abstract class SingletonExtension
localTrait.set(trait);
}
+ /**
+ * @return the {@link Trait} instance of the current execution extension
+ * call.
+ */
protected T getTrait()
{
return localTrait.get();
diff --git a/source/java/org/alfresco/traitextender/SingletonExtensionFactory.java b/source/java/org/alfresco/traitextender/SingletonExtensionFactory.java
index 903d75f99d..06b3858317 100644
--- a/source/java/org/alfresco/traitextender/SingletonExtensionFactory.java
+++ b/source/java/org/alfresco/traitextender/SingletonExtensionFactory.java
@@ -27,6 +27,13 @@ import java.lang.reflect.Proxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+/**
+ * Creates singleton extension sub classes that are extension API implementors.
+ * The singleton extensions continue to exist after the extensible has been
+ * collected.
+ *
+ * @author Bogdan Horje
+ */
public class SingletonExtensionFactory, T extends Trait> implements
ExtensionFactory
{
diff --git a/source/java/org/alfresco/traitextender/SpringBeanExtension.java b/source/java/org/alfresco/traitextender/SpringBeanExtension.java
index 32c5695708..89eb18284f 100644
--- a/source/java/org/alfresco/traitextender/SpringBeanExtension.java
+++ b/source/java/org/alfresco/traitextender/SpringBeanExtension.java
@@ -16,9 +16,17 @@
* 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 extends SingletonExtension
+/**
+ * A {@link SingletonExtension} extension-API implementor defined as a
+ * spring-bean.
+ * Handles also spring-bundle extension registrations.
+ *
+ * @author Bogdan Horje
+ */
+public abstract class SpringBeanExtension extends SingletonExtension
{
private SpringExtensionPoint extensionPoint;
@@ -31,9 +39,10 @@ public abstract class SpringBeanExtension extends SingletonEx
{
this.extensionPoint = extensionPoint;
}
-
- public void register(RegistryExtensionBundle bundle) throws InvalidExtension
+
+ public void register(RegistryExtensionBundle bundle) throws InvalidExtension
{
- extensionPoint.register(bundle,this);
+ extensionPoint.register(bundle,
+ this);
}
}
diff --git a/source/java/org/alfresco/traitextender/SpringExtensionBundle.java b/source/java/org/alfresco/traitextender/SpringExtensionBundle.java
index e16049c66e..11221d0829 100644
--- a/source/java/org/alfresco/traitextender/SpringExtensionBundle.java
+++ b/source/java/org/alfresco/traitextender/SpringExtensionBundle.java
@@ -26,6 +26,51 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
+/**
+ * A {@link SpringBeanExtension}s collection that get registered on the
+ * {@link Extender}'s registry on {@link #afterPropertiesSet()}.
+ * Works in conjunction with {@link SpringBeanExtension}s and
+ * {@link SpringExtensionPoint}s to define and start spring based
+ * {@link ExtensionBundle}s of {@link SingletonExtension}s.
+ * The spring-context XML sample bellow shows the definition of spring-bundled
+ * trait-extensions:
+ *
+ *
+ * {@code
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * }
+ *
+ *
+ * @author Bogdan Horje
+ */
public class SpringExtensionBundle implements InitializingBean
{
private static Log logger = LogFactory.getLog(SpringExtensionBundle.class);
@@ -38,6 +83,12 @@ public class SpringExtensionBundle implements InitializingBean
private RegistryExtensionBundle extensionBundle;
+ /**
+ * @param enabled true
if the current bundle should be
+ * registered.
+ * false
if the current bundle should skip extension
+ * registration
+ */
public void setEnabled(boolean enabled)
{
this.enabled = enabled;
@@ -53,6 +104,17 @@ public class SpringExtensionBundle implements InitializingBean
this.id = id;
}
+ /**
+ * Creates a {@link RegistryExtensionBundle} and registers all contained
+ * {@link SpringBeanExtension}s with it.
+ * When all extension have successfully registered it starts the
+ * {@link RegistryExtensionBundle}.
+ * The previously created {@link RegistryExtensionBundle} is stored for
+ * later start or {@link #stop()} operations.
+ *
+ * @see Extender#start(ExtensionBundle)
+ * @see Extender#stop(ExtensionBundle)
+ */
public synchronized void start()
{
@@ -81,6 +143,13 @@ public class SpringExtensionBundle implements InitializingBean
Extender.getInstance().start(extensionBundle);
}
+ /**
+ * Stops a previously {@link #start()} created
+ * {@link RegistryExtensionBundle}.
+ *
+ * @see Extender#start(ExtensionBundle)
+ * @see Extender#stop(ExtensionBundle)
+ */
public synchronized void stop()
{
if (extensionBundle == null)
@@ -96,7 +165,7 @@ public class SpringExtensionBundle implements InitializingBean
{
if (this.enabled)
{
- logger.info("The extension bundle " + id+" is spring-enabled. Starting ... ");
+ logger.info("The extension bundle " + id + " is spring-enabled. Starting ... ");
start();
}
else
diff --git a/source/java/org/alfresco/traitextender/SpringExtensionPoint.java b/source/java/org/alfresco/traitextender/SpringExtensionPoint.java
index c1573b3ce8..ab07a03993 100644
--- a/source/java/org/alfresco/traitextender/SpringExtensionPoint.java
+++ b/source/java/org/alfresco/traitextender/SpringExtensionPoint.java
@@ -19,6 +19,15 @@
package org.alfresco.traitextender;
+/**
+ * An {@link ExtensionPoint} spring bean wrapper with spring registering
+ * life-cycle management.
+ * Works in conjunction with {@link SpringBeanExtension}s and
+ * {@link SpringExtensionBundle}s to define spring based {@link ExtensionBundle}
+ * s of singleton extensions.
+ *
+ * @author Bogdan Horje
+ */
public class SpringExtensionPoint
{
private String trait;
diff --git a/source/java/org/alfresco/traitextender/SpringTraitExtenderConfigurator.java b/source/java/org/alfresco/traitextender/SpringTraitExtenderConfigurator.java
deleted file mode 100644
index 8a1523d909..0000000000
--- a/source/java/org/alfresco/traitextender/SpringTraitExtenderConfigurator.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.alfresco.traitextender;
-
-import org.springframework.beans.factory.InitializingBean;
-
-public class SpringTraitExtenderConfigurator implements InitializingBean
-{
-
- private boolean enableTraitExtender;
-
- @Override
- public void afterPropertiesSet() throws Exception
- {
- }
-
-}
diff --git a/source/java/org/alfresco/traitextender/Trait.java b/source/java/org/alfresco/traitextender/Trait.java
index cca5f18151..58c1fb689a 100644
--- a/source/java/org/alfresco/traitextender/Trait.java
+++ b/source/java/org/alfresco/traitextender/Trait.java
@@ -16,9 +16,19 @@
* 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;
+/**
+ * Markup interface.
+ * Subinterfaces represent distiguishing features of {@link Extensible} objects
+ * that are meant to be extended and exposed to the extending code.
+ * Ideally they would be the only means of interaction between extensions and
+ * the extended modules.
+ *
+ * @author Bogdan Horje
+ */
public interface Trait
{
-
+
}
diff --git a/source/test-java/org/alfresco/traitextender/AJExtensionsCompileTest.java b/source/test-java/org/alfresco/traitextender/AJExtensionsCompileTest.java
index 1979b2e58d..c2e5ba1b03 100644
--- a/source/test-java/org/alfresco/traitextender/AJExtensionsCompileTest.java
+++ b/source/test-java/org/alfresco/traitextender/AJExtensionsCompileTest.java
@@ -32,14 +32,14 @@ import org.springframework.core.type.filter.AssignableTypeFilter;
public class AJExtensionsCompileTest extends TestCase
{
- protected void compile(Class extends Extensible> extensible) throws AJExtensibleCompilingException
+ protected void compile(Class extends Extensible> extensible) throws Exception
{
Set> extensiblesSet = new HashSet<>();
extensiblesSet.add(extensible);
compile(extensiblesSet);
}
- protected void compile(Set> extensibles) throws AJExtensibleCompilingException
+ protected void compile(Set> extensibles) throws Exception
{
StringBuilder errorString = new StringBuilder();
boolean errorsFound = false;