Skip to main content

Set Up RMI and Hessian Remoting in Spring Boot 2

Learn how to configure Java RMI and Hessian remoting in Spring Boot 2 with RmiServiceExporter, HessianServiceExporter, and their proxy factory beans. Includes server and client configuration, shared interface setup, and a comparison table.

Java RMI and the Hessian binary web-service format are considered legacy remoting protocols, but they still appear in enterprise codebases that predate REST or in environments where binary efficiency matters more than interoperability. Spring Boot 2 makes both trivially easy to configure through dedicated exporter beans — no XML, no servlet configuration, just a few @Bean declarations. This guide shows you how to expose and consume a service over both protocols with the minimal possible code.

Maven Dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.18</version>
</parent>

<dependencies>
    <!-- Web layer required by Hessian (HTTP transport) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Hessian client/server library -->
    <dependency>
        <groupId>com.caucho</groupId>
        <artifactId>hessian</artifactId>
        <version>4.0.66</version>
    </dependency>
</dependencies>

Shared Service Interface

Both RMI and Hessian expose a service through an interface. Keep the interface in a shared module (or JAR) available to both server and client:

package com.ankurm.service;

import java.io.Serializable;
import java.util.List;

// All parameter and return types must be Serializable for RMI
public interface ProductService {
    Product findById(int id);
    List<Product> findAll();
    Product save(Product product);
}

// DTO must implement Serializable for network transport
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    private int    id;
    private String name;
    private double price;

    public Product() {}
    public Product(int id, String name, double price) {
        this.id = id; this.name = name; this.price = price;
    }
    // getters and setters...
    public int    getId()    { return id; }
    public String getName()  { return name; }
    public double getPrice() { return price; }
    public void setId(int id)          { this.id = id; }
    public void setName(String name)   { this.name = name; }
    public void setPrice(double price) { this.price = price; }

    @Override public String toString() {
        return "Product{id=" + id + ", name='" + name + "', price=" + price + "}";
    }
}

Service Implementation

package com.ankurm.service;

import org.springframework.stereotype.Service;
import java.util.*;

@Service
public class ProductServiceImpl implements ProductService {

    // In-memory store for demonstration
    private static final Map<Integer, Product> STORE = new LinkedHashMap<>();
    static {
        STORE.put(1, new Product(1, "Laptop",  999.99));
        STORE.put(2, new Product(2, "Monitor", 349.00));
        STORE.put(3, new Product(3, "Keyboard", 59.99));
    }

    @Override
    public Product findById(int id) {
        return STORE.getOrDefault(id,
            new Product(-1, "Not Found", 0));
    }

    @Override
    public List<Product> findAll() {
        return new ArrayList<>(STORE.values());
    }

    @Override
    public Product save(Product product) {
        STORE.put(product.getId(), product);
        return product;
    }
}

Part 1 — Hessian Remoting

Server — Exporting via HessianServiceExporter

import org.springframework.remoting.caucho.HessianServiceExporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;

import java.util.Map;

@Configuration
public class HessianServerConfig {

    @Autowired
    private ProductService productService;

    // Exporter bean: wraps the service in Hessian serialisation
    @Bean(name = "/hessian/products")
    public HessianServiceExporter hessianProductService() {
        HessianServiceExporter exporter = new HessianServiceExporter();
        exporter.setService(productService);
        exporter.setServiceInterface(ProductService.class);
        return exporter;
    }

    // Map the URL path to the exporter bean
    @Bean
    public HandlerMapping hessianMapping() {
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setOrder(1);
        mapping.setUrlMap(Map.of("/hessian/products",
            hessianProductService()));
        return mapping;
    }
}

Client — Consuming with HessianProxyFactoryBean

import org.springframework.remoting.caucho.HessianProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HessianClientConfig {

    @Bean
    public HessianProxyFactoryBean productServiceProxy() {
        HessianProxyFactoryBean factory = new HessianProxyFactoryBean();
        factory.setServiceUrl("http://localhost:8080/hessian/products");
        factory.setServiceInterface(ProductService.class);
        return factory;
    }
}

// Usage in any Spring component:
@Service
public class OrderService {
    @Autowired ProductService productService; // injected as a Hessian proxy

    public void printAllProducts() {
        productService.findAll().forEach(System.out::println);
    }
}

Part 2 — Java RMI Remoting

Server — Exporting via RmiServiceExporter

import org.springframework.remoting.rmi.RmiServiceExporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RmiServerConfig {

    @Autowired
    private ProductService productService;

    @Bean
    public RmiServiceExporter rmiProductService() {
        RmiServiceExporter exporter = new RmiServiceExporter();
        exporter.setServiceName("ProductService");   // JNDI name in RMI registry
        exporter.setService(productService);
        exporter.setServiceInterface(ProductService.class);
        exporter.setRegistryPort(1099);              // default RMI port
        return exporter;
    }
}

Client — Consuming with RmiProxyFactoryBean

import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RmiClientConfig {

    @Bean
    public RmiProxyFactoryBean productServiceProxy() {
        RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
        factory.setServiceUrl("rmi://localhost:1099/ProductService");
        factory.setServiceInterface(ProductService.class);
        return factory;
    }
}

RMI vs Hessian Comparison

FeatureJava RMIHessian
TransportCustom TCP (JRMP)HTTP
Firewall friendly?No (custom port)Yes (port 80/443)
Language interopJava onlyMultiple languages
Payload formatJava serialisationBinary (compact)
Load balancer supportDifficultYes (standard HTTP)
Best forInternal Java-to-Java callsCross-firewall binary RPC

See Also

Conclusion

Spring Boot 2 makes both RMI and Hessian remoting a matter of registering a single exporter bean on the server and a single proxy factory bean on the client. The service interface and DTO classes are shared transparently — callers treat the remote service exactly like a local Spring bean. Choose Hessian when you need to cross firewalls or when non-Java clients may appear in the future; choose RMI for pure Java, low-latency internal communication. For any greenfield project, however, a REST or gRPC API is the modern choice with better tooling, observability, and broader ecosystem support.

No Comments yet!

This site uses Akismet to reduce spam. Learn how your comment data is processed.