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.
Related
I'm having a problem where the related table id fields return 'null' from my domain objects when using inheritance. Here is an example:
In /src/groovy/
BaseClass1.groovy
class BaseClass1 {
Long id
static mapping = {
tablePerConcreteClass true
}
}
BaseClass2.groovy
class BaseClass2 extends BaseClass1 {
String someOtherProperty
static constraints = {
someOtherProperty(maxSize:200)
}
static mapping = BaseClass1.mapping
}
In /grails-app/domain
ParentClass.groovy
class ParentClass extends BaseClass2 {
ChildClass myChild
static mapping = BaseClass2.mapping << {
version false
}
}
ChildClass.groovy
class ChildClass extends BaseClass1 {
String property
static mapping = BaseClass1.mapping
}
The problem appears here:
SomeotherCode.groovy
print parentClassInstance.myChild.id // returns the value
print parentClassInstance.myChildId // returns null
Any ideas what might be going on to get those dynamic properties to break like this?
After debugging into the get(AssociationName)Id source, I found the following:
The handler for this is:
GrailsDomainConfigurationUtil.getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
It threw an exception on the related class (value.getClass().getDeclaredMethod), saying NoSuchMethod for the method getId(). I was unable to remove the id declaration from the base class without Grails complaining that an identifier column was required. I tried marking id as public and it also complained that it wasn't there. So, I tried this
BaseClass {
Long id
public Long getId() { return this.#id }
}
and things worked on some classes, but not on others.
When I removed the ID declaration, I go an error: "Identity property not found, but required in domain class". On a whim, I tried adding #Entity to the concrete classes and viola! everything started working.
class BaseClass {
//Don't declare id!
}
#Entity
class ParentClass {}
#Entity
class ChildClass {}
I still think it is a grails bug that it needs to be added, but at least it is easy enough to work around.
I'm not sure why you are seeing this behavior, but I'm also not sure why you are doing some of the things you are doing here. Why have a domain class extend a POGO? Domains, Controllers, and Services are heavily managed by the Grails machinery, which probably was not designed for this sort of use. Specifically, I believe Grails builds the dynamic property getters for the GrailsDomainProperty(s) of GrailsDomainClass(es), not POGO's. In this case, you have an explicitly declared id field in BaseClass1 that is not a GrailsDomainProperty. I suspect that this POGO id property is not picked up by the Grails machinery that creates the dynamic property getters for Domains.
You might try putting BaseClass1/2 in /grails-app/domain, perhaps making them abstract if you don't want them instantiated, then extending them as you are and seeing if you observe the behavior you want.
The code below used to work under the JAXB implementation used by JDK 1.7, but now under JDK 1.8 it's broken. In the code below you will find the key change that seems to make it work in 1.8. The "fix" under 1.8 is not really a fix because it's bad practice to expose internal collections for direct modification by the outside world. I want to control access to the internal list through my class and I don't want to complicate things by making observable collections and listening to them. This is not acceptable.
Is there any way to get my original code to work under the JAXB of JD 1.8?
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
list.clear();
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
// return new ArrayList(list); // this was the original code that worked under 1.7
return list; //this is the only thing that works under 1.8
}
After more analysis, the problem seems to be coming from JAXB not calling the setter method for collections anymore (it used to under JDK 1.7). Now under JDK 1.8, it calls the getter and modifies the collection directly. This poses several problems:
1-forces the user to expose an internal collection to the outside world for free modification (bad practice)
2-doesn't allow the user to do any custom code when the list changes (such as what you could do if the setter was called). It might be possible to make an observable collection and listen to it, but this is a much more complicated workaround than just calling the setter method.
Background
When a collection property is mapped in JAXB it first checks the getter to see if the collection property has been pre-initialized. In the example below I want to have my property exposed as List<String>, but have the backing implementation be a LinkedList ready to hold 1000 items.
private List<String> foos = new LinkedList<String>(1000);
#XmlElement(name="foo")
public List<String> getFoos() {
return foos;
}
Why Your Code Used to Work
If you previously had JAXB call the setter on a property mapped to a collection that returned a non-null response from the getter, then there was a bug in that JAXB implementation. Your code should not have worked in the previous version either.
How to Get the Setter Called
To have the setter called you just need to have your getter return null, on a new instance of the object. Your code could look something like:
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Foo")
public class Foo {
private List<CustomObject> list = null;
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
if (null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
if (null == list) {
return null;
}
return new ArrayList(list);
}
}
UPDATE
If you don't need to perform any logic on the List returned from JAXB's unmarshalling then using field access may be an acceptable solution.
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
private List<CustomObject> list = null;
public synchronized void setList(List<CustomObject> values) {
if(null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
return new ArrayList(list);
}
}
The code below used to work under the JAXB implementation used by JDK 1.7, but now under JDK 1.8 it's broken. In the code below you will find the key change that seems to make it work in 1.8. The "fix" under 1.8 is not really a fix because it's bad practice to expose internal collections for direct modification by the outside world. I want to control access to the internal list through my class and I don't want to complicate things by making observable collections and listening to them. This is not acceptable.
Is there any way to get my original code to work under the JAXB of JD 1.8?
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
list.clear();
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
// return new ArrayList(list); // this was the original code that worked under 1.7
return list; //this is the only thing that works under 1.8
}
After more analysis, the problem seems to be coming from JAXB not calling the setter method for collections anymore (it used to under JDK 1.7). Now under JDK 1.8, it calls the getter and modifies the collection directly. This poses several problems:
1-forces the user to expose an internal collection to the outside world for free modification (bad practice)
2-doesn't allow the user to do any custom code when the list changes (such as what you could do if the setter was called). It might be possible to make an observable collection and listen to it, but this is a much more complicated workaround than just calling the setter method.
Background
When a collection property is mapped in JAXB it first checks the getter to see if the collection property has been pre-initialized. In the example below I want to have my property exposed as List<String>, but have the backing implementation be a LinkedList ready to hold 1000 items.
private List<String> foos = new LinkedList<String>(1000);
#XmlElement(name="foo")
public List<String> getFoos() {
return foos;
}
Why Your Code Used to Work
If you previously had JAXB call the setter on a property mapped to a collection that returned a non-null response from the getter, then there was a bug in that JAXB implementation. Your code should not have worked in the previous version either.
How to Get the Setter Called
To have the setter called you just need to have your getter return null, on a new instance of the object. Your code could look something like:
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Foo")
public class Foo {
private List<CustomObject> list = null;
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
if (null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
if (null == list) {
return null;
}
return new ArrayList(list);
}
}
UPDATE
If you don't need to perform any logic on the List returned from JAXB's unmarshalling then using field access may be an acceptable solution.
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
private List<CustomObject> list = null;
public synchronized void setList(List<CustomObject> values) {
if(null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
return new ArrayList(list);
}
}
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.
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)