Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BigDecimal value is always zero when transfered with Spring remoting via Hessian

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?

Server side (Tomcat 7)

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;      
    }
}

Client side

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

like image 949
Vedran Avatar asked Jun 28 '12 14:06

Vedran


3 Answers

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

like image 50
keuleJ Avatar answered Nov 17 '22 02:11

keuleJ


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!

like image 27
BigAl Avatar answered Nov 17 '22 02:11

BigAl


This seems to be a known bug. See http://bugs.caucho.com/view.php?id=3920 for details and possible workarounds.

like image 28
Lothar Avatar answered Nov 17 '22 02:11

Lothar