Open gavinking opened 10 years ago
Related to #1113.
It would be useful to have more context information in invoke
method, eg. instance of intercepted class (if any), or whole FunctionDeclaration
.
@thradec yes, surely. This above proposal is "minimal". If we can make it perform acceptable, it would be much nicer to have a Method
or Function
there instead of just the plain Callable
. Indeed, that was always the plan, even way-back-when.
+1
May also come in handy for security permissions as well. On Oct 14, 2014 8:42 AM, "Gavin King" notifications@github.com wrote:
@thradec https://github.com/thradec yes, surely. This above proposal is "minimal". If we can make it perform acceptable, it would be much nicer to have a Method or Function there instead of just the plain Callable. Indeed, that was always the plan, even way-back-when.
— Reply to this email directly or view it on GitHub https://github.com/ceylon/ceylon-spec/issues/1112#issuecomment-59036246.
It tastes a little static to me. I'm not convinced that anything can be accomplished by using thread locals, despite that it makes sense for XA transaction managers.
I guess by introducing interceptors on language level you are trying to allow creation of intercepted objects with custom parameter without having to pass custom values via setters.
Please, take a look at my drafy of how could CDI API look in Ceylon. I'm not planning on implementing CDI for Ceylon, but I think it would by nice to be able to use CDI like that:
// CDI
shared final interface InjectAnnotation() satisfies OptionalAnnotation<MyAnnotation, FunctionOrValueDeclaration> {}
shared annotation InjectAnnotation inject()
=> InjectAnnotation();
shared interface Provider<out Result, in Args>
given Args satisfies Anything[]{
Result get(Args);
}
shared interface Cdi {
shared formal Provider<Result, Args> createProvider<out Result, in Args>(Callable<Result, [inject Aynthing[], Args]> producer)
given Args satisfies Anything[];
}
// TX
shared interface TransactionManager {
shared formal Result inTransaction<out Result>(Callable<Result, []> atomicOperation);
}
shared final annotation class TransactionalAnnotation(TransactionManager transactionManager)
satisfies FunctionInterceptor<TransactionalAnnotation> {
shared actual Result invoke<Result,Args>(Callable<Result,Args> fun, Args args)
given Args satisfies Anything[] {
return transactionManager.inTransaction(() => fun(*args));
}
}
shared TransactionalAnnotation transactional(TransactionManager transactionManager) => TransactionalAnnotation(transactionManager);
shared final interface GlobalAnnotation() satisfies OptionalAnnotation<MyAnnotation, FunctionOrValueDeclaration> {}
shared annotation GlobalAnnotation global()
=> GlobalAnnotation();
// Application code
class TranactionalBean(inject [global TransactionManager transactionManager], [SomeType someObject]){
transactional(transactionManager)
void transactionalMethod(){
//...
}
}
class TranactionalEntity(TransactionManager transactionManager, SomeType someObject){
transactional(transactionManager)
void transactionalMethod(){
//...
}
}
TranactionalEntity produceTranactionalEntity(inject [global TransactionManager transactionManager], [SomeType someObject])
=> TranactionalEntity(transactionManager, someObject);
class TransactionalEntityManager(inject Cdi cdi) {
Provider<TranactionalEntity, [SomeType]> entityProvider = cdi.createProvider(produceTranactionalEntity);
TranactionalEntity newTransactionalEntity(SomeType someObject){
return entityProvider.get(someObject);
}
}
I used here #745 record types, so I could declare typesafe method createProvider
and still be able to specify which injected value I want to pass to interceptor.
How would generated code look like:
class TranactionalEntity(TransactionManager transactionManager, SomeType someObject){
TransactionalAnnotation transactionalAnnotation = transactional(transactionManager);
void transactionalMethod(){
transactionalAnnotation.invoke(() -> {
//...
});
}
}
It would require ability to pass some object to annotation making it not static and impossible to make acquirable via metamodel without providing specific instance of object. But maybe interceptor annotations could be callables returning interceptors for same argument list as in constructor.
It tastes a little static to me.
Well, I mean, more dynamic stuff can by built by plugging into static stuff.
Also, if we build this functionality as a macro/compiler plugin/whatever, then it would not stop constrain sophisticated frameworks from doing something different.
Well, I mean, more dynamic stuff can by built by plugging into static stuff.
I think it's less maintainable.
Also, if we build this functionality as a macro/compiler plugin/whatever, then it would not stop constrain sophisticated frameworks from doing something different.
I'm just concerned what and how would be possible to implement. I wished to suggest considering allowing passing constructor arguments to interceptors.
I wished to suggest considering allowing passing constructor arguments to interceptors.
I think they could certainly receive a metamodel object for the declaration they annotate. But what else would you want to pass to them?
I think they could certainly receive a metamodel object for the declaration they annotate. But what else would you want to pass to them?
Only if it's shared i guess...
class SomeClass(Integer parameter){
Attribute<SomeClass,Integer,Nothing> attribute = `SomeClass.parameter`;
}
Yields "Metamodel reference to local declaration".
Only if it's shared i guess...
Well that's something fixable.
Is there an example depicting how this is supposed to be used and what it would accomplish (e.g. a simple piece of code that uses this feature and a walkthrough of how the code executes using this feature)?
We've often discussed the problem of interceptors. What I would like to propose is the following, which is, ultimately, rather simple.
We introduce an interface in
ceylon.language.meta.interceptors
:(We'll also need a
ValueInterceptor
.)An annotation class may satisfy
FunctionInterceptor
:The compiler would be responsible for inserting the appropriate code to call
TransactionAnnotation.invoke(method, args)
whenevermethod()
is called.