com.github.dynamicextensionsalfresco.policy.BehaviourProxy.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of annotations-runtime Show documentation
Show all versions of annotations-runtime Show documentation
Adds an OSGi container to alfresco repository supporting dynamic code reloading, classpath isolation and a bunch of other useful features
package com.github.dynamicextensionsalfresco.policy
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.concurrent.ConcurrentHashMap
import com.github.dynamicextensionsalfresco.metrics.Timer
import com.github.dynamicextensionsalfresco.metrics.time
import org.alfresco.repo.policy.Behaviour
import org.alfresco.repo.policy.Policy
import org.alfresco.service.cmr.repository.NodeRef
import java.lang.reflect.InvocationTargetException
/**
* Proxy that allows a [Behaviour] to be garbage-collected.
*
*
* This class prevents dangling references to [Behaviour] instances when code is undeployed from an OSGi
* container, as the [PolicyComponent] interface offers no means of unregistering [Behaviour]s. Dangling
* references to the [BehaviourProxy] itself will continue to exist throughout the lifetime of the Alfresco
* process, however. There will be a slight memory leak for every time you redeploy a Dynamic Extension that contains a
* Behaviour. (Further revisions of this class may add the ability to reattach a Behaviour once a module gets updated.)
* @author Laurens Fridael
*/
public class BehaviourProxy(private var behaviour: Behaviour, val timer: Timer) : Behaviour by behaviour {
private val proxiesByPolicyClass = ConcurrentHashMap, ProxyPolicy>()
@Suppress("UNCHECKED_CAST")
override fun getInterface(policy: Class?): T {
return proxiesByPolicyClass.getOrPut(policy) {
if (behaviour is NoOpBehaviour) {
val proxyHandler = ProxyPolicyInvocationHandler(null, behaviour, timer)
val proxy = Proxy.newProxyInstance(javaClass.classLoader, arrayOf(policy), proxyHandler)
ProxyPolicy(proxy, proxyHandler)
} else {
val originalHandler = behaviour.getInterface(policy)
val proxyHandler = ProxyPolicyInvocationHandler(originalHandler, behaviour, timer)
val proxy = Proxy.newProxyInstance(javaClass.classLoader, arrayOf(policy), proxyHandler)
ProxyPolicy(proxy, proxyHandler)
}
}.proxy as T
}
/**
* Clears the reference to the original [Behaviour] and clears the target references for the
* [ProxyPolicyComponentInvocationHandler]s.
*/
@Synchronized public fun release() {
behaviour = NoOpBehaviour(behaviour.notificationFrequency, behaviour.isEnabled)
for (proxyPolicy in proxiesByPolicyClass.values) {
proxyPolicy.handler.release()
}
}
private class ProxyPolicyInvocationHandler(private var target: Any?, private var behaviour: Behaviour?, val timer: Timer) : InvocationHandler {
override fun invoke(proxy: Any, method: Method, args: Array?): Any? {
if (method.declaringClass.isAssignableFrom(Any::class.java)) {
// Direct Object methods to ourselves.
if (args != null) {
return method.invoke(this, *args)
} else {
return method.invoke(this)
}
} else if (Policy::class.java.isAssignableFrom(method.declaringClass)) {
/* Policy interface operations always return void. */
if (behaviour != null) {
try {
timer.time( {
behaviour.toString() + " " + args?.filterIsInstance(NodeRef::class.java)?.joinToString(",")
} , {
if (args != null) {
return method.invoke(target, *args)
} else {
return method.invoke(target)
}
})
} catch(e: InvocationTargetException) {
throw e.targetException
}
}
return null
} else {
/* We should never get to this point. */
throw AssertionError("Cannot handle methods from " + method.declaringClass)
}
}
fun release() {
target = null
behaviour = null
}
}
override fun toString(): String {
return behaviour.toString()
}
private class ProxyPolicy(val proxy: Any, val handler: ProxyPolicyInvocationHandler)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy