When I call a remote method that returns a BigDecimal value via Spring's Hessian functionality, it always returns zero. Calling the method directly or using the plain Hessian servlet (non Spring) works normally.
What can be done to fix this?
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/remoting/*</url-pattern>
</servlet-mapping>
</web-app>
remoting-servlet.xml:
<beans>
<context:annotation-config />
<context:component-scan base-package="hr.spi.logic.lcspi" />
<tx:annotation-driven proxy-target-class="true" />
<bean name="/lcspi/lc302/poslovi" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="posloviLogic" />
<property name="serviceInterface" value="hr.spi.logic.lcspi.lc302.PosloviLogicInterface" />
</bean>
</beans>
Service class whose method I call:
package hr.spi.logic.lcspi.lc302;
@Transactional
@Repository
public class PosloviLogic implements PosloviLogicInterface {
@Override
public BigDecimal test()
{
BigDecimal bd = new BigDecimal("2.2");
return bd;
}
}
Spring configuration - applicationContextHessian.xml:
<beans>
<bean id="posloviLogic" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8080/SpringWebTest/remoting/lcspi/lc302/poslovi" />
<property name="serviceInterface" value="hr.spi.logic.lcspi.lc302.PosloviLogicInterface" />
</bean>
</beans>
Console application test:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContextHessian.xml");
try {
PosloviLogicInterface posloviLogic = (PosloviLogicInterface) context.getBean("posloviLogic");
BigDecimal bd = posloviLogic.test();
System.out.println(bd); // This returns 0.00
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
EDIT: Libraries used were Spring 3.2 and Hessian 4.0.7
You could use HessianServlet.setSerializerFactory() to set your own SerializerFactory and return the com.caucho.hessian.io.BigDecimalDeserializer as Deserializer for BigDecimal.
We patched it like this and it works. No idea why it isn't implemented this way.
See http://www.jarvana.com/jarvana/view/com/caucho/hessian/4.0.7/hessian-4.0.7-src.jar!/com/caucho/hessian/server/HessianServlet.java?format=ok
We solved this issue like this:
Like @keuleJ we build our own SerializerFactory (see below) but we do not return com.caucho.hessian.io.BigDecimalDeserializer because it is not checking for null.
public class BigDecimalSerializerFactory extends AbstractSerializerFactory {
private BigDecimalSerializer bigDecimalSerializer = new BigDecimalSerializer();
private BigDecimalDeserializer bigDecimalDeserializer = new BigDecimalDeserializer();
@Override
public Serializer getSerializer(Class cl) throws HessianProtocolException {
if (BigDecimal.class.isAssignableFrom(cl)) {
return bigDecimalSerializer;
}
return null;
}
@Override
public Deserializer getDeserializer(Class cl) throws HessianProtocolException {
if (BigDecimal.class.isAssignableFrom(cl)) {
return bigDecimalDeserializer;
}
return null;
}
}
Then we defined our own Deserializer. It differs from the implementation of com.couchos#s one by verifying if value is not null. It is necessary to extend AbstractStringValueDeserialize!
public class BigDecimalDeserializer extends AbstractStringValueDeserializer {
@Override
public Class getType() {
return BigDecimal.class;
}
@Override
protected Object create(String value) {
if (null != value) {
return new BigDecimal(value);
} else {
return null;
}
}
}
The Serializer only transfers the BigDecimal to String representation:
public class BigDecimalSerializer extends AbstractSerializer {
@Override
public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
if (obj == null)
out.writeNull();
else {
Class cl = obj.getClass();
if (out.addRef(obj))
return;
int ref = out.writeObjectBegin(cl.getName());
BigDecimal bi = (BigDecimal) obj;
if (ref < -1) {
out.writeString("value");
out.writeString(bi.toString());
out.writeMapEnd();
} else {
if (ref == -1) {
out.writeInt(1);
out.writeString("value");
out.writeObjectBegin(cl.getName());
}
out.writeString(bi.toString());
}
}
}
}
This implementation works for us, not only for BigDecimal but for joda DateTime too.
To use this you have to add the Serializer Factory to
SerializerFactory serializerFactory = newSerializerFactory();
serializerFactory.addFactory(new BigDecimalSerializerFactory());
You have to do this both, on server side and on client side!
HINT! In our case we have had a combined problem with BigDecimal and DateTime. Thus stacktraces and the debug views were weird. So if you use "non-standard" objects check them for their serialization!
This seems to be a known bug. See http://bugs.caucho.com/view.php?id=3920 for details and possible workarounds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With