Skip to content

Commit c4655a7

Browse files
committed
better fix for GRAILS-6726 "XML converter appends _$$_javassist postfix to node names"
1 parent eda109f commit c4655a7

File tree

6 files changed

+141
-16
lines changed

6 files changed

+141
-16
lines changed

src/java/grails/converters/XML.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
import org.apache.commons.logging.Log;
3333
import org.apache.commons.logging.LogFactory;
34-
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil;
34+
import org.codehaus.groovy.grails.support.proxy.EntityProxyHandler;
35+
import org.codehaus.groovy.grails.support.proxy.ProxyHandler;
3536
import org.codehaus.groovy.grails.web.converters.AbstractConverter;
3637
import org.codehaus.groovy.grails.web.converters.Converter;
3738
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
@@ -130,8 +131,15 @@ public String getElementName(Object o) {
130131
if (om instanceof NameAwareMarshaller) {
131132
return ((NameAwareMarshaller) om).getElementName(o);
132133
}
133-
final Class<?> clz = GrailsHibernateUtil.unwrapIfProxy(o).getClass();
134-
return GrailsNameUtils.getPropertyName(clz);
134+
final ProxyHandler proxyHandler = config.getProxyHandler();
135+
if(proxyHandler.isProxy(o) && (proxyHandler instanceof EntityProxyHandler)) {
136+
EntityProxyHandler entityProxyHandler = (EntityProxyHandler) proxyHandler;
137+
final Class<?> cls = entityProxyHandler.getProxiedClass(o);
138+
return GrailsNameUtils.getPropertyName(cls);
139+
}
140+
else {
141+
return GrailsNameUtils.getPropertyName(o.getClass());
142+
}
135143
}
136144

137145
public void convertAnother(Object o) throws ConverterException {

src/java/org/codehaus/groovy/grails/orm/hibernate/proxy/HibernateProxyHandler.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919

2020
import org.apache.commons.beanutils.PropertyUtils;
2121
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil;
22-
import org.codehaus.groovy.grails.support.proxy.ProxyHandler;
22+
import org.codehaus.groovy.grails.support.proxy.EntityProxyHandler;
2323
import org.hibernate.Hibernate;
2424
import org.hibernate.collection.AbstractPersistentCollection;
2525
import org.hibernate.collection.PersistentCollection;
2626
import org.hibernate.proxy.HibernateProxy;
27+
import org.hibernate.proxy.HibernateProxyHelper;
2728
import org.hibernate.proxy.LazyInitializer;
2829

2930
/**
@@ -32,7 +33,7 @@
3233
* @author Graeme Rocher
3334
* @since 1.2.2
3435
*/
35-
public class HibernateProxyHandler implements ProxyHandler {
36+
public class HibernateProxyHandler implements EntityProxyHandler {
3637

3738
public boolean isInitialized(Object o) {
3839
if (o instanceof HibernateProxy) {
@@ -125,4 +126,15 @@ else if (o instanceof AbstractPersistentCollection) {
125126
}
126127
}
127128
}
129+
130+
public Object getProxyIdentifier(Object o) {
131+
if(o instanceof HibernateProxy) {
132+
return ((HibernateProxy)o).getHibernateLazyInitializer().getIdentifier();
133+
}
134+
return null;
135+
}
136+
137+
public Class<?> getProxiedClass(Object o) {
138+
return HibernateProxyHelper.getClassWithoutInitializingProxy(o);
139+
}
128140
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2010 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.codehaus.groovy.grails.support.proxy;
18+
19+
/**
20+
* Methods specified to proxied entities
21+
*
22+
* @author Graeme Rocher
23+
* @since 1.3.6
24+
*/
25+
public interface EntityProxyHandler extends ProxyHandler{
26+
27+
/**
28+
* This method returns the identifier of the proxy or null if the
29+
* object is not a proxy
30+
*
31+
* @return The identifier of the identity
32+
*/
33+
Object getProxyIdentifier(Object o);
34+
35+
/**
36+
* Returns the proxied class without initializing the proxy
37+
*
38+
* @param o The object
39+
* @return The class
40+
*/
41+
Class<?> getProxiedClass(Object o);
42+
}

src/java/org/codehaus/groovy/grails/web/converters/marshaller/json/DomainClassMarshaller.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.codehaus.groovy.grails.commons.GrailsDomainClass;
3333
import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty;
3434
import org.codehaus.groovy.grails.support.proxy.DefaultProxyHandler;
35+
import org.codehaus.groovy.grails.support.proxy.EntityProxyHandler;
3536
import org.codehaus.groovy.grails.support.proxy.ProxyHandler;
3637
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
3738
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
@@ -173,10 +174,23 @@ else if (referenceObject instanceof Map) {
173174
}
174175

175176
protected void asShortObject(Object refObj, JSON json, GrailsDomainClassProperty idProperty, GrailsDomainClass referencedDomainClass) throws ConverterException {
177+
178+
Object idValue;
179+
180+
if(proxyHandler instanceof EntityProxyHandler) {
181+
idValue = ((EntityProxyHandler) proxyHandler).getProxyIdentifier(refObj);
182+
if(idValue == null) {
183+
idValue = extractValue(refObj, idProperty);
184+
}
185+
186+
}
187+
else {
188+
idValue = extractValue(refObj, idProperty);
189+
}
176190
JSONWriter writer = json.getWriter();
177191
writer.object();
178192
writer.key("class").value(referencedDomainClass.getName());
179-
writer.key("id").value(extractValue(refObj, idProperty));
193+
writer.key("id").value(idValue);
180194
writer.endObject();
181195
}
182196

src/java/org/codehaus/groovy/grails/web/converters/marshaller/xml/DomainClassMarshaller.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
3232
import org.codehaus.groovy.grails.commons.GrailsDomainClass;
3333
import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty;
34+
import org.codehaus.groovy.grails.support.proxy.EntityProxyHandler;
3435
import org.codehaus.groovy.grails.support.proxy.ProxyHandler;
3536
import org.codehaus.groovy.grails.web.converters.ConverterUtil;
3637
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException;
@@ -155,7 +156,18 @@ else if (referenceObject instanceof Map) {
155156

156157
protected void asShortObject(Object refObj, XML xml, GrailsDomainClassProperty idProperty,
157158
@SuppressWarnings("unused") GrailsDomainClass referencedDomainClass) throws ConverterException {
158-
Object idValue = new BeanWrapperImpl(refObj).getPropertyValue(idProperty.getName());
159+
Object idValue;
160+
if(proxyHandler instanceof EntityProxyHandler) {
161+
162+
idValue = ((EntityProxyHandler) proxyHandler).getProxyIdentifier(refObj);
163+
if(idValue == null) {
164+
idValue = new BeanWrapperImpl(refObj).getPropertyValue(idProperty.getName());
165+
}
166+
167+
}
168+
else {
169+
idValue = new BeanWrapperImpl(refObj).getPropertyValue(idProperty.getName());
170+
}
159171
xml.attribute("id",String.valueOf(idValue));
160172
}
161173

src/test/org/codehaus/groovy/grails/web/converters/XMLConverterTests.groovy

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class XMLConverterTests extends AbstractGrailsControllerTests {
2626

2727
// @todo this test is fragile and depends on runtime environment because
2828
// of hash key ordering variations
29-
assertEquals '''<?xml version="1.0" encoding="UTF-8"?><book><author>Stephen King</author><title>The Stand</title></book>''', response.contentAsString
29+
assertEquals '''<?xml version="1.0" encoding="UTF-8"?><xmlConverterTestBook><author>Stephen King</author><publisher /><title>The Stand</title></xmlConverterTestBook>''', response.contentAsString
3030
}
3131

3232
void testConvertErrors() {
@@ -35,17 +35,19 @@ class XMLConverterTests extends AbstractGrailsControllerTests {
3535

3636
// @todo this test is fragile and depends on runtime environment because
3737
// of hash key ordering variations
38+
39+
println response.contentAsString
3840

3941
def xml = new XmlSlurper().parseText(response.contentAsString)
4042

4143
def titleError = xml.error.find { it.@field == 'title' }
42-
assertEquals 'Property [title] of class [class Book] cannot be null', titleError.message.text()
44+
assertEquals 'Property [title] of class [class XmlConverterTestBook] cannot be null', titleError.message.text()
4345
def authorError = xml.error.find { it.@field == 'author' }
44-
assertEquals 'Property [author] of class [class Book] cannot be null', authorError.message.text()
46+
assertEquals 'Property [author] of class [class XmlConverterTestBook] cannot be null', authorError.message.text()
4547
}
4648

4749
void testProxiedDomainClassWithXMLConverter() {
48-
def obj = ga.getDomainClass("Book").newInstance()
50+
def obj = ga.getDomainClass("XmlConverterTestBook").newInstance()
4951
obj.title = "The Stand"
5052
obj.author = "Stephen King"
5153
def c = ga.getControllerClass("RestController").newInstance()
@@ -61,8 +63,29 @@ class XMLConverterTests extends AbstractGrailsControllerTests {
6163
assertTrue pum.supports(proxy)
6264
// @todo this test is fragile and depends on runtime environment because
6365
// of hash key ordering variations
64-
assertEquals( '''<?xml version="1.0" encoding="UTF-8"?><book><author>Stephen King</author><title>The Stand</title></book>''', response.contentAsString)
66+
assertEquals( '''<?xml version="1.0" encoding="UTF-8"?><xmlConverterTestBook><author>Stephen King</author><publisher /><title>The Stand</title></xmlConverterTestBook>''', response.contentAsString)
6567
}
68+
69+
void testMarshalProxiedAssociations() {
70+
71+
def obj = ga.getDomainClass("XmlConverterTestPublisher").newInstance()
72+
obj.name = "Apress"
73+
obj.id = 1L
74+
75+
def hibernateInitializer = [getImplementation:{obj},getPersistentClass:{obj.getClass()}] as LazyInitializer
76+
def proxy = [getHibernateLazyInitializer:{hibernateInitializer}] as HibernateProxy
77+
78+
def book = ga.getDomainClass("XmlConverterTestBook").newInstance()
79+
book.title = "The Stand"
80+
book.author = "Stephen King"
81+
book.publisher = obj
82+
83+
def c = ga.getControllerClass("RestController").newInstance()
84+
c.params.b = book
85+
c.testProxyAssociations()
86+
87+
assertEquals( '''<?xml version="1.0" encoding="UTF-8"?><xmlConverterTestBook><author>Stephen King</author><publisher id="1" /><title>The Stand</title></xmlConverterTestBook>''', response.contentAsString)
88+
}
6689

6790
void onSetUp() {
6891
GroovySystem.metaClassRegistry.removeMetaClass Errors
@@ -73,27 +96,41 @@ import grails.converters.*
7396
7497
class RestController {
7598
def test = {
76-
def b = new Book(title:'The Stand', author:'Stephen King')
99+
def b = new XmlConverterTestBook(title:'The Stand', author:'Stephen King')
77100
render b as XML
78101
}
79102
80103
def testProxy = {
81104
render params.b as XML
82105
}
106+
107+
def testProxyAssociations = {
108+
render params.b as XML
109+
}
83110
84111
def testErrors = {
85-
def b = new Book()
112+
def b = new XmlConverterTestBook()
86113
b.validate()
87114
render b.errors as XML
88115
}
89116
90117
}
91-
class Book {
118+
class XmlConverterTestBook {
92119
Long id
93120
Long version
94121
String title
95122
String author
123+
124+
XmlConverterTestPublisher publisher
125+
126+
}
127+
class XmlConverterTestPublisher {
128+
Long id
129+
Long version
130+
String name
131+
96132
97-
}'''
133+
}
134+
'''
98135
}
99136
}

0 commit comments

Comments
 (0)