Search code examples
javarrserve

Detach and attach RServe connection: Cannot connect


I am trying to detach and then attach an RServe session from Java. Connecting to RServe works flawlessly, but attaching causes an exception.

package com.company.korana.r_interface;

import org.rosuda.REngine.Rserve.RConnection;

import com.company.korana.config.Config$;;

public class RToast {
    public static void main(String[] args) throws Exception {
        RConnection rConnection = new RConnection(Config$.MODULE$.rserveHost(), Config$.MODULE$.rservePort());

        rConnection.assign("testVariable", "hello from java");
        rConnection.eval("print('Hi R console from java')"); // <--- this is visible in the R console

        org.rosuda.REngine.Rserve.RSession sessionHandle = rConnection.detach();

        rConnection = sessionHandle.attach(); // <---- this throws

        System.out.println(rConnection.eval("testVariable").asString());
    }
}

Exception in thread "main" org.rosuda.REngine.Rserve.RserveException: Cannot connect: Connection refused: connect at org.rosuda.REngine.Rserve.RConnection.(RConnection.java:90) at org.rosuda.REngine.Rserve.RConnection.(RConnection.java:66) at org.rosuda.REngine.Rserve.RSession.attach(RSession.java:36) at com.company.korana.r_interface.RToast.main(RToast.java:16) Caused by: java.net.ConnectException: Connection refused: connect at java.net.DualStackPlainSocketImpl.connect0(Native Method) at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source) at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source) at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source) at java.net.AbstractPlainSocketImpl.connect(Unknown Source) at java.net.PlainSocketImpl.connect(Unknown Source) at java.net.SocksSocketImpl.connect(Unknown Source) at java.net.Socket.connect(Unknown Source) at java.net.Socket.connect(Unknown Source) at java.net.Socket.(Unknown Source) at java.net.Socket.(Unknown Source) at org.rosuda.REngine.Rserve.RConnection.(RConnection.java:85) ... 3 more

The Rserve is running on localhost with the default port.

EDIT: I'm using the following maven artifact:

<dependency>
    <groupId>org.rosuda.REngine</groupId>
    <artifactId>Rserve</artifactId>
    <version>1.8.1</version>
</dependency>

Solution

  • After inspection of the sources, it turns out that Rserve sessions, when detached, listen on a randomly allocated port.

    Rserv.c, line 1781

    while ((port = (((int) random()) & 0x7fff)+32768)>65000) {};
    

    So the port that needs to be reachable between the client and Rserve is something between 32768 and 65000. In my case, this didn't work because I was using docker.

    EDIT: I built a solution to use Rserve with docker without opening a huge range of ports (which does not work nicely with docker). The snipper below replaces my install.packages("Rserve"):

    RUN wget https://www.rforge.net/src/contrib/Rserve_1.8-5.tar.gz
    RUN tar -xf Rserve_1.8-5.tar.gz
    RUN rm Rserve_1.8-5.tar.gz
    RUN sed -i '1763s/.*/\t\tint port = 53000;/' /Rserve/src/Rserv.c
    RUN sed -i '1781s/.*//' /Rserve/src/Rserv.c
    RUN sed -i '1793s/.*/\t\tif (port>53100) {/' /Rserve/src/Rserv.c
    RUN tar -czvf Rserve_1.8-5.tar.gz /Rserve
    RUN R CMD INSTALL Rserve_1.8-5.tar.gz
    

    This modifies the Rserve sources so that ports are taken in the range [53000, 53100[ which is sufficient in my case.