Comments (10)
Hey, Pavel, i think,
The issue is that @Transactional
adds a @CompileStatic
to the original method which doesn't work with the @Enforce
annotations because it tries to statically compile what is in the annotation. So to compensate for this I made the variations that you've seen:
@Enforce
// Original
@EnforceS
//Statically compiles the original method.
@EnforceT
//Add Transactionality to the original
@EnforceTS
//Statically compiles, and adds Transactionality to the original method.
I also added versions of @Transactional
and @CompileStatic
that don't mess with the @Enforce
annotations:
@EnforcerCompileStatic
@EnforcerTransactional
So double-check the documentation for this and see if it makes sense:
https://virtualdogbert.github.io/Enforcer/#enforcer-2-0-0
If not I'll have to update that, but the gist is that you can't use the Grails @Transational
annotation with @Enforce
, So if you need a method that uses @Enforce
to be transactional you would use EnforceT
. If you wanted that Method to also be Statically compiled you would use @EnforceTS. If you wanted the whole service to be Transactional and not mess with the existing Enforce Annotations you would use @EnforcerTransactional
on the class, combined with @EnforceT
or @EnforceTS
on the methods.
from enforcer.
@pshepilov-amc Did what I suggested work? Is the documentation clear enough or need some work? I want to make sure that it is clear, and that I draw the proper attention to it, so that others don't struggle with it and just think it's broken and not use it.
from enforcer.
@virtualdogbert, sorry for not responding for some time.
- In the documentation and in your first comment you mention that @transactional adds @CompileStatic behavior to the original method, did I get it right? But I can't confirm it: I can't find it in any documentation, can't find it in the source code, can't test it myself. For example, the following code compiles fine (although method bar() doesn't exist on User class)
class MyService {
@Transactional
void foo() {
User user = new User()
user.bar()
}
}
So could you please help me and point out the documentation, where I can read more about it?
-
What is the actual purpose of @EnforcerCompileStatic and @EnforcerTransactional. As I understood one should use it instead of @CompileStatic and @transactional when using @Enforce annotation. But how is that differ from using @EnforceS/@EnforceT/@EnforceTS ? Or it is just yet another way of doing it?
-
Have you considered implementing the following functionality?
When someone adds @Enforce(S/T) annotation we create to new methods: enforcerInvocation and newMethod. Then we move enforcer part into the first method and apply (S/T) logic to it only. Then move the method code to the second method and apply other annotations to it (e.g. @transactional, @CompileStatic etc.). Original method code is replaced by invocation of these two methods and its annotations are disabled (removed or so, if its possible).
So the following code
class MyService {
@EnforceS({ hasRole('ADMIN') })
@Transactional
void foo() {
User user = new User()
user.save()
}
}
will transform into something like
class MyService {
EnforcerService enforcerService
void foo() {
enforce_foo()
original_foo()
}
@CompileStatic
void enforce_foo() {
enforcerService.enforce ({ hasRole('ADMIN') })
}
@Transactional
void original_foo() {
User user = new User()
user.save()
}
}
From my point of view, this approach is more clear and more flexible in usage. What do you think about it?
from enforcer.
- It's not in the documentation but in the source code
https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/transactions/transform/TransactionalTransform.groovy
Which calls a compileMethodStatically method from:
https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/transform/AbstractMethodDecoratingTransformation.groovy#L221
This code is fairly complex and was hard for me to figure out. However, if you run the Gradle task console with your example and use the ASTBrowser you'll be able to see that it does add a compile static.
- Yes
@EnforcerCompileStatic
and@EnforcerTransactional
are essentially versions of CompileStatic and Transactional, that you can apply at a class level, that will not affect any method and you have an@Enforce
method on or it's variants.
@EnforceS
is Enforce but with the `@CompileStatic' functionality for the method, you are annotating.
@EnforceT
is Enforce but with the @Tansactional
functionality for the method, you are annotating.
@EnforceTS
is Enforce but with the @Tansactional
and `@CompileStatic' functionality for the method, you are annotating.
- Actually that's how the
@EnforceS
,@EnforceT
and@EnforceTS
already work you just don't really see it because it's happening at compile time. However is you use the Gradle console command, and look at the ATS browser you'll see the methods it adds.
class MyService {
@EnforceTS({ hasRole('ADMIN') })
void foo() {
User user = new User()
user.save()
}
@EnforceT({ hasRole('ADMIN') })
void bar() {
User user = new User()
user.save()
}
@EnforceS({ hasRole('ADMIN') })
void baz() {
User user = new User()
user.save()
}
}
will transform into something like
class MyService {
EnforcerService enforcerService
void foo() {
wrapped_foo()
}
@CompileStatic
@Transactional
void wrapped_foo() {
enforcerService.enforce ({ hasRole('ADMIN') })
User user = new User()
user.save()
}
void bar() {
wrapped_bar()
}
@Transactional
void wrapped_bar() {
enforcerService.enforce ({ hasRole('ADMIN') })
User user = new User()
user.save()
}
void baz() {
wrapped_baz()
}
@CompileStatic
void wrapped_baz() {
enforcerService.enforce ({ hasRole('ADMIN') })
User user = new User()
user.save()
}
}
However, when you look at the console AST Browser you'll see slightly different code because @Transactional
does its own wrapping, which in its code call weaving.
from enforcer.
Our issue with #3 is that we would like to separate transactions for enforcer logic and for main code. For example when we need to not open transaction for the main method, but enforcer logic requires transaction. Do you think its reasonable?
from enforcer.
So even with me explaining it in two different ways, you're still not understanding how this works...
Maybe another example
@EnforcerTransactional //Because @Transactional doesn't work with @Enforce.
class MyService {
//This would be transactional but not have any enforcement
void someMethod(){
User user = new User()
user.save()
}
//This would be transaction, compile static and have enforcment
@EnforceTS({ hasRole('ADMIN') })
void foo() {
User user = new User()
user.save()
}
///This would be transactional and have enforcment.
@EnforceT({ hasRole('ADMIN') })
void bar() {
User user = new User()
user.save()
}
//This woulld be compile static and have enforcement, but no transaction.
@EnforceS({ hasRole('ADMIN') })
void baz() {
//some code
}
}
I also asked Anton(on linkedin) to take a look at this thread to see if he understands what I'm trying to explain, and if he does maybe he can explain it to you, and explain where I'm going wrong trying to explain it to you...
from enforcer.
I think there were two misunderstandings at the beginning:
- Enforce annotations can not be used with Transactional and Compile static annotations - it is clear now, and that there is an alternative annotations from Enforce package
- The second one was about the possibility to detach the scope of transaction or static compilation of Enforce arguments and method itself
Currently, if you use for example transactional version on a method:
@EnforceT({ hasRole('ADMIN') }) // (1)
void bar() { // (2)
User user = new User()
user.save()
}
It will bound both closure from (1) and method from (2) into a single transaction. But sometime we would like to have transaction only in (1) and has no transaction in (2) or vice versa has no transaction in (1) and transaction in (2).
Things are getting worse if the method (2) is calling another service method that is transactional and/or the code is invoked asynchronously. In that case transaction from EnforceT became a parent transaction - and it is causing issues further down. That I believe the issue Pavel is facing in the background of a scene. And the solution he is looking for is to avoid transaction nesting to have a cleaner approach for transaction management.
So the question here is that is it possible now or in the future to separate scopes of a transaction and compile static scope for a closure of Enforce annotation and a method itself?
from enforcer.
I think I understand now, while the code example is bad example, the code the annotation calls requires a transaction, but the code in the method doesn't? In that case what the annotation is doing in injecting a call to the EnforcerService:
where the closure gets executed, calling a delegate method, that delegate method could have a @Transactional
added to it. This way you wouldn't have to mess with changing the annotation logic and get the same effect. The EnforcerService is in your code and you have the ability to update it, or preferably extend it using traits, which would add methods that the anotation could call.
Also if you want to be able to manipulate the transactions @EnforceT
and @EnforceTS
have all the same parameters as @Transactional
:
https://github.com/virtualdogbert/Enforcer/blob/master/src/main/groovy/com/virtualdogbert/ast/EnforceT.groovy#L68
So if you wanted something like
@Transactional(propagation = Propagation.REQUIRES_NEW)
you could have
@EnforceT(value = {/*some test*/}, propagation = Propagation.REQUIRES_NEW)
take a look at the java docs in the link for the annotations.
Also so you can see what the transforms are actually doing I created a couple of gists, one source using Enforcer and one that has what the transform does to the code, as viewed from the Groovy AST Browser
Sample Enforcer code
https://gist.github.com/virtualdogbert/35c97135a6a64ff6cb1362fc77a42c9c
SampleTransformed Enforcer code:
https://gist.github.com/virtualdogbert/bc4b3a84bc67ce60cf8a708e3372dbd5
Let me know if this helps.
from enforcer.
Hey, Tucker.
Thank you for your explanation, I appreciate your help. I will look into your notes more carefully and try to use it.
from enforcer.
No prob, sometime in the near future, I'll fold back in some of these explanations and examples into the docs, so hopefully, things will be clearer for anyone else who tries to uses it. If you have other questions let me know.
from enforcer.
Related Issues (15)
- Add the idea of creator to Domain objects HOT 2
- Create scripts to generate UI HOT 1
- Issue with arguments list in deployed war HOT 1
- Not working with new Gorm 6.1 in Grails 3.3 HOT 4
- 2.0 Eliminate use of Holders HOT 1
- Create Updated video documentation
- Grails compatibility ? HOT 9
- Create video tutorials for Enforcer HOT 1
- Where Closures HOT 2
- Add documentation to the templates
- 0.2.1 has been release, but 0.3.1 is still pending
- For next version make the Ast applicable at the class level HOT 1
- Add Scripts for generating feature flags HOT 1
- There is a bug in the domainRole trait HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from enforcer.