What are the alternatives to overriding asType() when writing conversion code? - grails

It appears the convention for converting objects in Groovy is to use the as operator and override asType(). For example:
class Id {
def value
#Override
public Object asType(Class type) {
if (type == FormattedId) {
return new FormattedId(value: value.toUpperCase())
}
}
}
def formattedId = new Id(value: "test") as FormattedId
However, Grails over-writes the implementation of asType() for all objects at runtime so that it can support idioms like render as JSON.
An alternative is to re-write the asType() in the Grails Bootstrap class as follows:
def init = { servletContext ->
Id.metaClass.asType = { Class type ->
if (type == FormattedId) {
return new FormattedId(value: value.toUpperCase())
}
}
}
However, this leads to code duplication (DRY) as you now need to repeat the above in both the Bootstrap and the Id class otherwise the as FormattedId will not work outside the Grails container.
What alternatives exist to writing conversion code in Groovy/Grails that do not break good code/OO design principals like the Single Responsibility Principal or DRY? Are Mixins are good use here?

You can use the Grails support for Codecs to automatically add encodeAs* functions to your Grails archetypes:
class FormattedIdCodec {
static encode = { target ->
new FormattedId((target as String).toUpperCase()
}
}
Then you can use the following in your code:
def formattedId = new Id(value: "test").encodeAsFormattedId

My un-elegant solution is to rename the original asType(), and make a new asType() that calls it, and to also make your BootStrap overwrite astType with a call to that method:
so, your class:
class Id {
def value
#Override
public Object asType(Class type) {
return oldAsType(type);
}
public Object oldAsType(Class type) {
if (type == FormattedId) {
return new FormattedId(value: value.toUpperCase())
}
}
}
In my app, I had asType defined in a number of classes, so I ended up using a common closure in BootStrap.groovy:
def useOldAsType = {Class clazz ->
delegate.oldAsType(clazz)
}
Id.metaClass.asType = useOldAsType;
Value.metaClass.asType = useOldAsType;
OtherClass.metaClass.asType = useOldAsType;
SubclassOfValue.metaClass.asType = useOldAsType;
Note that if you have a subclass that does not override asType, but you want it to use the superclass's, you must also set it in BootStrap.

Related

ExpandoMetaClass in Grails to override behavior in a Java library?

I'm using Grails with third-party java libraries, and I'd like to override behavior in one of those libraries.
I've attempted to do this in Bootstrap.groovy, like so:
// class overrides
ExpandoMetaClass.enableGlobally()
SimpleStringFilter.metaClass.passesFilter = {Object itemId, Item item ->
final Property<?> p = item.getItemProperty(propertyId);
if (p == null) {
return false;
}
Object propertyValue = p.getValue();
if (propertyValue == null) {
return false;
}
final String value = ignoreCase ? propertyValue.toString()
.toLowerCase() : propertyValue.toString();
if (onlyMatchPrefix) {
if (!value.startsWith(filterString)) {
return false;
}
} else {
if (!value.contains(filterString)) {
return false;
}
}
return true;
}
I know for a fact that the method passesFilter in the SimpleStringFilter class gets called, but I've set a breakpoint in my code above and it's never hit.
For reference, the signature of the java method is:
public boolean passesFilter(Object itemId, Item item)
So, can one actually override behavior in java libraries with ExpandoMetaClass globally? Can someone explain the behavior and nuances of using it in this way?
Thanks!
The issue you are facing is that Java classes don't invoke metaClass. Groovy can't override method calls to Java classes using metaClass.
This is a common misunderstanding when you see something like this:
def object = new MyJavaObject()
object.metaClass.someMethod = { ... }
What is actually happening above is you are creating a Groovy object that wraps the MyJavaObject which then allows that instance to invoke through metaClass.

Groovy Meta Programming

I want to override a method definition in Grails. I am trying to use Groovy metaprogramming as the class which I want to override belongs to a framework.
Below is the original class.
class SpringSocialSimpleSignInAdapter implements SignInAdapter {
private RequestCache requestCache
SpringSocialSimpleSignInAdapter(RequestCache requestCache) {
this.requestCache = requestCache;
}
String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
SignInUtils.signin localUserId
extractOriginalUrl request
}
}
I am trying to override like below
SpringSocialSimpleSignInAdapter.metaClass.signIn = {java.lang.String str, org.springframework.social.connect.Connection conn, org.springframework.web.context.request.NativeWebRequest webreq ->
println 'coming here....' // my implementation here
return 'something'
}
But for some reason overriding is not hapening. I am not able to figure it out. Any help would be greatly appretiated.
Thanks
Yeah, seems like that bug. I don't know your whole scenario, but anyway, here's a small workaround i made:
In your class definition, you don't implement the interface
You create your object and do your metamagic
Use groovy coercion to make it act as the interface and then you can pass it around
Here is a small script i made using JIRA bug to prove it:
interface I {
def doIt()
}
class T /*implements I*/ {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
// here the coercion happens and the assertion works fine
def i = t as I
assert !i.doIt()
assert !t.doIt()
// here the polymorphism happens fine
def iOnlyAcceptInterface(I i) { assert !i.doIt() }
iOnlyAcceptInterface(i)

Grails - access static config object in codec class

I am currently updating my Grails project in order not to use the deprecated ConfigurationHolder class.
This goes fine in most cases, but I am facing trouble in my custom codec classes, where I have been using the following approach until now:
import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH
class MyCodec {
static boolean myStaticConfigProperty=CH.config.myStaticConfigProperty
static encode = { something ->
if(myStaticConfigProperty)
...
}
}
Direct injection using
def grailsApplication
does not work in this case since this will be injected as a non-static object.
Instead I have tried to use the approach suggested in this post getting-grails-2-0-0m1-config-info-in-domain-object-and-static-scope, but I cannot make it work even after injecting the grailsApplication object into my codec metaclasses in the bootstrap:
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.codecClasses) {
cc.clazz.metaClass.getGrailsApplication = { -> grailsApplication }
cc.clazz.metaClass.static.getGrailsApplication = { -> grailsApplication }
}
}
}
Could anyone suggest an approach that will allow me to access the config object in a static way inside codec classes?
I'd suggest something like this completely untested code:
class MyCodec {
static def grailsConfig
static boolean myStaticConfigProperty = grailsConfig.myStaticConfigProperty
static encode = { something ->
if(myStaticConfigProperty)
...
}
}
class BootStrap {
def grailsApplication
def init = { servletContext ->
for (cc in grailsApplication.codecClasses) {
cc.grailsConfig = grailsApplication.config
}
}
}
If all of your codec classes just need the same one configuration property, you could skip injecting the grailsApplication and/or the config object entirely, and just set the one static property from BootStrap.
it works for me in grails 2.2.3
import grails.util.Holders as holders;
class MyFileCodec {
static encode = {file ->
def configPath= holders.grailsApplication.config.share.contextPath
return "${configPath}/${file.name}"
}
}
grails.util.Holders has been introduced since grails 2.0, it's the way to access config object.

custom Grails validation

Normally for a Grails domain or command class, you declare your constraints and the framework adds a validate() method that checks whether each of these constraints is valid for the current instance e.g.
class Adult {
String name
Integer age
void preValidate() {
// Implementation omitted
}
static constraints = {
name(blank: false)
age(min: 18)
}
}
def p = new Person(name: 'bob', age: 21)
p.validate()
In my case I want to make sure that preValidate is always executed before the class is validated. I could achieve this by adding a method
def customValidate() {
preValidate()
validate()
}
But then everyone who uses this class needs to remember to call customValidate instead of validate. I can't do this either
def validate() {
preValidate()
super.validate()
}
Because validate is not a method of the parent class (it's added by metaprogramming). Is there another way to achieve my goal?
You should be able to accomplish this by using your own version of validate on the metaclass, when your domain/command class has a preValidate() method. Something similar to the below code in your BootStrap.groovy could work for you:
class BootStrap {
def grailsApplication // Set via dependency injection
def init = { servletContext ->
for (artefactClass in grailsApplication.allArtefacts) {
def origValidate = artefactClass.metaClass.getMetaMethod('validate', [] as Class[])
if (!origValidate) {
continue
}
def preValidateMethod = artefactClass.metaClass.getMetaMethod('preValidate', [] as Class[])
if (!preValidateMethod) {
continue
}
artefactClass.metaClass.validate = {
preValidateMethod.invoke(delegate)
origValidate.invoke(delegate)
}
}
}
def destroy = {
}
}
You may be able to accomplish your goal using the beforeValidate() event. It's described in the 1.3.6 Release Notes.

Role based domain class field access in grails

I am developing a grails application.In that some cases I want to control the domain class fields based on the role.So that in each call to getter setter method of domain class I want to apply some filter based on role(Logged in user's role).I am assuming that grails will create getter setter method at runtime for the domin classes.So while writing grails code is it possible to apply this logic.If it is possible then how to apply?
Example:
Domain Class :
class Book{
String name;
double price;
}
Controller:
def index={
Book book=Book.get(1);
println book.name;
println book.price;
}
In the above code "println book.price;" this line should work only for particular role.For some other role it should throw some exception.
Is it possible achieve?Is there any plugin to do this?
Please give some help on this....Thanks
You can create get/set methods for the properties you want to control access to and put your security logic there. Assuming you've written your own security service or are using a security plugin like the Spring Security (Acegi) plugin you would:
class Book{
String name;
double price;
def authenticateService
void setPrice(double price) {
if(!authenticateService.ifAllGranted('ROLE_PRICE_FIXER')) {
throw new Exception("You are not authorized to set book prices")
}
this.price = price
}
double getPrice() {
if(!authenticateService.ifAllGranted('ROLE_PRICE_FIXER')) {
throw new Exception("You are not authorized to get book prices")
}
return this.price
}
}
I am not aware of any plugin that allows access controls to be put on domain properties.
You could also consider using a custom validator or a spring errors object to catch attempts to set a field before saving it.
EDIT: Here is an example of what I was thinking. You could generalize quite a bit more and the code here hasn't been tested so it probably won't run as is.
class securedDomain {
String securedField
def fieldSetBy = [:]
def previousValue = [:]
static transients = ['fieldSetBy', 'previousValue']
static constraints = {
securedField(validator: { v, o ->
def access = User.findByName(fieldSetBy['securedField']).hasAccess('securedField')
if(!access) securedField = previousValue['securedField']
return access
})
void setProperty(String name, value) {
if(name == "securedField") {
fieldSetBy['securedField'] = session.user
previousValue['securedField'] = securedField
securedField = value
} else {
super(name, value)
}
}

Resources