3

I'm creating a C++ wrapper for an existing jarfile. In this case, I'm doing this with the Spigot Minecraft server jarfile.

When I execute the application, I have the issue where the input and the output of the application is dominated by the Java application. This means that when the java application terminates successfully, so does the C++ application, which indicates that the file descriptor for stdin is getting closed.

I've looked through a number of existing stackoverflow posts, and the closest that I've seen to achieve this, was making use of a forked process, and then piping the file descriptors using pipe() and dup():

C/Linux - having trouble with redirecting stdin and stout

I'm currently rebuilding the code to make it more portable, and allow me to add additional functionality to the C++ code, but the following code is what I've used to get started, and is what I'll be using to test this.

#include <jni.h>
#include <iostream>

using namespace std;

int main () {
    JavaVM *jvm;
    JNIEnv *env;

    JavaVMInitArgs vm_args;
    JavaVMOption* options = new JavaVMOption[1];

    options[0].optionString = "-Djava.class.path=/path/to/spigot.jar";

    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;

    jint instance = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    delete options;

    if (instance != JNI_OK) {
        cin.get();
        exit(EXIT_FAILURE);
    }

    cout << "JVM Version: ";
    jint ver = env->GetVersion();
    cout << ((ver>>16)&0x0f) << "." << (ver&0x0f) << endl;

    jclass cls = env->FindClass("org/bukkit/craftbukkit/Main");

    if (cls == nullptr) {
        cout << "Error starting minecraft" << endl;
    } else {
        jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");

        if (mid == nullptr) {
            cout << "Error: main not found" << endl;
        } else {
            jobjectArray arr = env->NewObjectArray(1,
                           env->FindClass("java/lang/String"),
                           env->NewStringUTF(""));

            env->CallStaticVoidMethod(cls, mid, arr);
            cout << "Started" << endl;
            cout << endl;
        }
    }

    cin.get();
    jvm->DestroyJavaVM();

    return 0;
}

Ideally, I would like to have the input and output of the java application run on a different set of file descriptors for stdin, stdout, and stderr, without forking it.

Is there a way to indicate to the JVM, using the JNI library within c++ to achieve this goal?

humroben
  • 77
  • 7

1 Answers1

2

You can just call System.setIn and System.setOut. Alternatively, just call the native implementations setIn0 and setOut0 directly or copy their implementation.

The latter would look something like:

    // Make a FileOutputStream
    jclass os_cla = env->FindClass("java/io/FileOutputStream");
    jmethodID os_init = env->GetMethodID(os_cla, "<init>", "(Ljava/lang/String;)V");
    jobject os = env->NewObject(os_cla, os_init, env->NewStringUTF("output.txt"));

    // Make a PrintStream
    jclass ps_cla = env->FindClass("java/io/PrintStream");
    jmethodID ps_init = env->GetMethodID(ps_cla, "<init>", "(Ljava/io/PrintStream;)V");
    jobject ps = env->NewObject(ps_cla, ps_init, os);

    // Reassign System.out
    jclass system_cla = env->FindClass("java/lang/System");
    jfieldID fid = env->GetStaticFieldID(system_cla, "out", "Ljava/io/PrintStream;");
    env->SetStaticObjectField(system_cla, fid, ps);
Oo.oO
  • 9,723
  • 3
  • 19
  • 40
Botje
  • 15,729
  • 2
  • 22
  • 32
  • 2
    your solution has minor flaws (like slightly broken method names or type names) but it's short and straight to the point. $humroben - you can find fully working sample (based on Botje's solution) here: https://github.com/mkowsiak/jnicookbook/tree/master/recipeNo049 – Oo.oO Jun 14 '19 at 17:30
  • Thanks for the edit! I only had the JNI manual at hand and wasn't in a position to test-compile. – Botje Jun 14 '19 at 20:58
  • Thank you for the solution, I will get to testing this now, and I will report back on the outcome. – humroben Jun 14 '19 at 23:00
  • Excellent, I've gotten the output to be redirected to a log file of its own. Now to work on the input and I'll then be off to the races. Thank you so much for the help – humroben Jun 14 '19 at 23:37