Как я могу отделить потоки stdin / stout / stderr Java-приложения, выполненного в C ++ - PullRequest
3 голосов
/ 11 июня 2019

Я создаю оболочку C ++ для существующего jarfile.В этом случае я делаю это с помощью jarfile сервера Spigot Minecraft.

Когда я запускаю приложение, у меня возникает проблема, когда во входных и выходных данных приложения преобладает Java-приложение.Это означает, что когда приложение java успешно завершается, то же самое происходит и с приложением C ++, которое указывает, что файловый дескриптор для stdin закрывается.

Я просмотрел несколько существующих сообщений stackoverflow и самые близкиеЯ видел, как добиться этого, использовал разветвленный процесс, а затем передавал дескрипторы файлов, используя pipe() и dup():

C / Linux - возникли проблемы с перенаправлением stdin иstout

В настоящее время я перестраиваю код, чтобы сделать его более переносимым, и позволю мне добавить дополнительные функции в код C ++, но следующий код - это то, что я использовал для начала, иэто то, что я буду использовать для проверки этого.

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

В идеале я хотел бы, чтобы вход и выход java-приложения запускались на другом наборе файловых дескрипторов для stdin, stdout и stderr, без разветвления.

Есть ли способ указать JVM, используя библиотеку JNI в c ++ для достижения этой цели?

1 Ответ

2 голосов
/ 14 июня 2019

Вы можете просто позвонить System.setIn и System.setOut. Либо просто вызовите собственные реализации setIn0 и setOut0 напрямую или скопируйте их реализацию.

Последний будет выглядеть примерно так:

    // 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);
...