1/*
2 * JavaAppLauncher: a simple Java application launcher for Mac OS X.
3 * StartJava.cpp
4 *
5 * Paul J. Lucas [paul@lightcrafts.com]
6 *
7 * This code is based on:
8 * http://developer.apple.com/samplecode/simpleJavaLauncher/simpleJavaLauncher.html
9 * http://developer.apple.com/samplecode/JavaSplashScreen/JavaSplashScreen.html
10 */
11
12// standard
13#include <jni.h>
14#include <pthread.h>
15#include <sys/types.h>
16#include <sys/resource.h>               /* for getrlimit(2) */
17#ifdef DEBUG
18#include <iostream>
19#endif
20
21// local
22#include "JavaParamBlock.h"
23#include "LC_JNIUtils.h"
24#include "UI.h"
25
26using namespace std;
27using namespace LightCrafts;
28
29JavaVM *g_jvm;
30
31/**
32 * Convert the array of char* pointers of the main() arguments to a Java
33 * String[].
34 */
35static jobjectArray convertMainArgs( JNIEnv *env, JavaParamBlock const *jpb ) {
36    jclass jString_class = env->FindClass( "java/lang/String" );
37    if ( !jString_class )
38        LC_die( @"Unexpected", @"Missing java.lang.String" );
39
40    jobjectArray jArgs =
41        env->NewObjectArray( jpb->main_argc, jString_class, 0 );
42    if ( !jArgs )
43        LC_die( @"Unexpected", @"Could not allocate main() array" );
44    if ( jpb->main_argc ) {
45        for ( int i = 0; i < jpb->main_argc; ++i ) {
46            auto_jstring jArg( env, env->NewStringUTF( jpb->main_argv[i] ) );
47            if ( !jArg )
48                LC_die( @"Unexpected", @"Could not allocate main() string" );
49            env->SetObjectArrayElement( jArgs, i, jArg );
50        }
51    } else {
52        //
53        // No "Arguments" key was found so leave the array empty and use that
54        // for "no arguments" to main().
55        //
56    }
57
58    return jArgs;
59}
60
61/**
62 * This method is called when the Java application is ready to open files.
63 */
64JNIEXPORT void JNICALL readyToOpenFiles( JNIEnv*, jclass ) {
65#ifdef DEBUG
66    cout << "*** In readyToOpenFiles()" << endl;
67#endif
68    extern bool g_javaIsReady;
69    g_javaIsReady = true;
70}
71
72/**
73 * Start the JVM.  This must be called in its own thread.
74 */
75void startJava( JavaParamBlock const *jpb, jclass *pLauncher_class ) {
76    //
77    // Start a JVM.
78    //
79    JNIEnv *env;
80    if ( JNI_CreateJavaVM( &g_jvm, (void**)&env, (void*)&jpb->jvm_args ) != 0 )
81        LC_die( @"Unexpected", @"Error starting JVM" );
82
83    //
84    // Find the application's main class.
85    //
86    *pLauncher_class = env->FindClass( jpb->main_className );
87    if ( LC_exceptionOccurred( env ) || !*pLauncher_class )
88        LC_die( @"Corrupted", @"Missing main()" );
89    *pLauncher_class = (jclass)env->NewGlobalRef( *pLauncher_class );
90
91    //
92    // Find the main class's main() method.
93    //
94    jmethodID const jMain_methodID = env->GetStaticMethodID(
95        *pLauncher_class, "main", "([Ljava/lang/String;)V"
96    );
97    if ( LC_exceptionOccurred( env ) || !jMain_methodID )
98        LC_die( @"Corrupted", @"Missing main() method" );
99
100    //
101    // Register native methods.
102    //
103    JNINativeMethod const jMethod = {
104        "readyToOpenFiles", "()V", reinterpret_cast<void*>( &readyToOpenFiles )
105    };
106    if ( env->RegisterNatives( *pLauncher_class, &jMethod, 1 ) < 0 )
107        LC_die( @"Unexpected", @"RegisterNatives() failed" );
108
109    //
110    // Call the main class's main() method.
111    //
112    env->CallStaticVoidMethod(
113        *pLauncher_class, jMain_methodID, convertMainArgs( env, jpb )
114    );
115    if ( LC_exceptionOccurred( env ) )
116        LC_die( @"Unexpected", @"main() threw exception" );
117
118    //
119    // Detach the current thread so that it appears to have exited when the
120    // Java application's main() method exits.
121    //
122    if ( g_jvm->DetachCurrentThread() != 0 )
123        LC_die( @"Unexpected", @"Could not detach thread" );
124
125    //
126    // Unloads a Java VM and reclaims its resources.  Only this thread can
127    // unload the VM.  This call blocks until this thread is only remaining
128    // user thread before it destroys the VM.
129    //
130    g_jvm->DestroyJavaVM();
131}
132
133/* vim:set et sw=4 ts=4: */
134