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

4

6

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

Arletha answered 28/6, 2012 at 14:48 Comment(0)
B
4

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

Bigname answered 10/8, 2012 at 14:39 Comment(0)
Q
2

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!

Quiet answered 17/8, 2012 at 6:3 Comment(0)
S
2

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

Septuagenarian answered 11/1, 2013 at 9:8 Comment(0)
M
-1

We had this same issue and found that the hessian jar distributed on maven central was missing some key configuration files. If you download the jar directly from http://hessian.caucho.com/ the jar will include:

  • META-INF/hessian/serializers
  • META-INF/hessian/deserializers

We solved the issue by copying these two missing files into our project's src/main/resources.

Bonus: This solution also corrected an issue we had with hessian serialization of java.util.Locale

Messy answered 25/1, 2017 at 20:30 Comment(2)
there is nothing under META-INF other than the MANIFEST.MF in any of the jars from hessian.caucho.com/#JavaGarbe
but these files are found in the central version!Garbe

© 2022 - 2024 — McMap. All rights reserved.