/* This shareable image module provides low-level launcher for running JAVA
 * programs as web server scripts.
 * Use the following rules in the configuration file to load this module and 
 * bind an exec path to it:
 *
 *    ThreadPool java stack=600000 q_flag=1 limit=4
 *    Service jexec pool=test dynamic=(JEXEC,jexec_mst)
 *    Exec /$java_exec/* %jexec:/myarg
 *
 * The ThreadPool rule defines a pool of MSTs (message-based service
 * threads) for servicing requests.  The service rule defines the
 * service 'jexec', assigning it to pool 'java' and setting it's
 * MST start routine as 'JEXEC' in image jexec_mst (image is assumed
 * to be in directory WWW_SYSTEM).  The Exec rule makes it so that URL paths 
 * beginning with '/$java_exec/' will invoke the jexec scriptserver,
 * the percent and colon are a special syntax to indicate an MST-based
 * script rather than a DECnet-based script.
 *
 * Note Linking to the Java Development Kit shareables requires use of
 *      case sensitive globals.  In order for the LIB$FIND_IMAGE_SYMBOLS calls
 *      from the main http_server program to find the start and initialization
 *      routines, they must be in all upper case.
 *
 * Author: David Jones
 * Date:   24-OCT-1995
 */
/*
 * Define upcased versions of MST functions.
 */
#define mstshr_init MSTSHR_INIT
#define mstshr_cgi_symbols MSTSHR_CGI_SYMBOLS
#define mstshr_getenv MSTSHR_GETENV
#define tu_strcpy TU_STRCPY
#define tu_strlen TU_STRLEN
#define mst_read_cb MST_READ_CB
#define mst_write_cb MST_WRITE_CB
#define mst_close_cb MST_CLOSE_CB
#define mst_exit_cb MST_EXIT_CB
#define mst_format_error_cb MST_FORMAT_ERROR_CB
#define http_log_level_p HTTP_LOG_LEVEL_P
#define tlog_putlog_cb TLOG_PUTLOG_CB
#define http_reentrant_c_rtl_p HTTP_REENTRANT_C_RTL_P
#define http_reentrant_vms_rtp_p HTTP_REENTRANT_VMS_RTL_P

#include "mst_share.h"
#include "tutil.h"
#include <stdio.h>
#include "jni.h"		/* Java Native Interface definiions */
/*
 * Define global pointers.
 */
static JavaVM *jvm;			/* Java Virtual machine context */
static JDK1_1InitArgs vm_args;
static jint exec_read ( JNIEnv  *env, jclass this, jstring string, jint count);
static jint exec_write ( JNIEnv *env, jclass this, jstring string, jint count);
static jint exec_close ( JNIEnv *env, jclass this );
static jint exec_format_error ( JNIEnv *env, jclass string, jint count);
static JNINativeMethod wwwjexec_natives[4] = {
   { "read", "(Ljava/lang/String;I)I", (void *) &exec_read },
   { "write", "(Ljava/lang/String;I)I", (void *) &exec_write },
   { "close", "()I", (void *) &exec_close },
   { "formatError", "(ILjava/lang/String;)I", (void *) &exec_format_error }
};
static jfieldID cnx_id;			/* field for cnx field in WWWJexec */
/***************************************************************************/
/* Every dynamically loaded service must have an INIT routine, which is
 * called once during the processing of the Service rule to initialize
 * static structures needed by the MST.  By default the init routine
 * name is the start routine + the string _init, and 'init=name' clause
 * on the service rule will override the default.
 *
 * Arguments:
 *    mst_linkage vector  Structure containing addresses of essential
 *			items wee need from main program, such as
 *			address of putlog() routine.
 *
 *    char *info	Administrator provided argument (info=).
 *
 *    char *errmsg	Error text for log file when error status returned.
 */
int JEXEC_INIT ( mst_linkage vector, char *info, char *errmsg )
{
    JNIEnv *env;			/* per-thread environment context */
    jint status;
    jclass wwwjexec;
   /*
    * The first thing any init routine must do is call mstshr_init() to
    * initialize global variables declared in mst_share.h.  The callback
    * vector includes a version number which mstshr_init checks to make
    * sure the vector format being used by the server hasn't changed.
    * If mstshr_init fails, we can't proceed so return immediately.
    */
   if ( (1&mstshr_init ( vector, info, errmsg )) == 0 ) return 0;
   /*
    * Now do any initialization specific to this shareable image.
    * The info argument is an optional string that can be specified
    * on the service rule using 'info=string'
    *
.   * Any errors should place a text message describing the error in errmsg (up 
    * to 255 characters) and return an even (i.i. error) status code.
    *
    * Create the java virtual machine (VM), put in global scope so that
    * other threads can attach to it.
    */
    JNI_GetDefaultJavaVMInitArgs(&vm_args);	/* parameters for creating VM */
    status = JNI_CreateJavaVM ( &jvm, &env, &vm_args );
    if ( status < 0 ) {
	tu_strcpy ( errmsg, 
	    "Error in Jexec_init, could not create Java Virtual Machine" );
	return 0;
    }
    /*
     * Load the wwwjexec class and bind its native methods to routines
     * defined in this module.
     */
    wwwjexec = (*env)->FindClass(env, "WWWJexec");
    if ( wwwjexec == 0 ) {
	tu_strcpy ( errmsg, 
	    "Error in Jexec_init, could not find WWWJexec class definition" );
	return 0;
    }
    status = (*env)->RegisterNatives ( env, wwwjexec, wwwjexec_natives, 4 );
printf("status of registerNatives: %d\n", status );
    if ( status < 0 ) {
	tu_strcpy ( errmsg, 
	    "Error in Jexec_init, could not register Native methods" );
	return 0;
    }
    /*
     * Get field ID to use in access field.
     */
    cnx_id = (*env)->GetFieldID ( env, wwwjexec, "cnx", "I" );
    if ( cnx_id == 0 ) {
	tu_strcpy ( errmsg, 
	    "Error in Jexec_init, could lookup cnx field ID" );
	return 0;
    }
    /*
     * Return success status.
     */
    tu_strcpy ( errmsg, "testcgi scriptserver sucessfully initialized" );
    return 1;
}
/***************************************************************************/
/* Main routine to handle client requests.  To the server, this routine
 * must behave like a DECnet scriptserver task (e.g. WWWEXEC) only messages 
 * are transferred via mst_read() and mst_write() rather than $QIO's to a 
 * logical link.
 *
 * Arguments:
 *    mst_link_t link	Connection structure used by mst_read(), mst_write().
 *
 *    char *service	Service name (for logging purposes).
 *
 *    char *info	Script-directory argument from 'exec' rule that
 *			triggered the MST (exec /path/* %service:info).
 *
 *    int ndx		Service thread index, all services sharing same pool
 *			share the same thread index numbers.
 *
 *    int avail		Number of remaining threads in pool assigned to service.
 */
int JEXEC ( mst_link_t link, char *service, char *info, int ndx, int avail )
{
    int i, j, status, length;
    JNIEnv *jenv;
    JDK1_1AttachArgs targs;
    jint result;
    jclass cls;
    jmethodID mid;
    char *classname, line[512];
    struct mstshr_envbuf env;
    /*
     * Log that we began execution
     */
    if ( http_log_level > 0 ) tlog_putlog ( 1, 
	"Service!AZ/!SL connected, info: '!AZ', pool remaining: !SL!/", 
	service, ndx, info, avail );
    /*
     * Setup cgi environment (reads prologue as a consequence).
     */
    status = mstshr_cgi_symbols ( link, info, &env );
    if ( (status &1) == 0 ) return 0;
    /*
     * Attach thread to Java machine, return jenv pointer
     */
    result = (*jvm)->AttachCurrentThread(jvm, &jenv, &targs );
    if ( result < 0 ) {
	tlog_putlog ( 0, 
	   "Service!AZ/!SL error: failed to attach thread to java VM!/",
	    service, ndx );
    }
    /*
     * Parse class name out of script name and load class;
     */
    classname = mstshr_getenv ( "SCRIPT_NAME", &env );
    if ( classname ) {
	for ( j = i = 0; classname[i]; i++ ) if (classname[i] == '/') j = i+1;
	classname = &classname[j];
    }
    cls = (*jenv)->FindClass(jenv,classname);
    if ( cls == 0 ) {
	tlog_putlog(1,
	   "Service!AZ/!SL error: failed to load class '!AZ'!/",
	    service, ndx, classname );
	mid = 0;
    } else {
        /*
         * Locate WWWrun method
         */
        mid = (*jenv)->GetStaticMethodID(jenv,cls, "WWWrun",
		"[Ljava/lang/String;LWWWJexec;)V" );
        if ( mid == 0 ) {
             tlog_putlog ( 0,
	   "Service!AZ/!SL error: failed to find WWWrun method in '!AZ'!/",
	    service, ndx, classname );
	}
    }
    /*
     * Build argument list and invoke method.
     */
    if ( mid != 0 ) {
    }
    /*
     * Place connection in TEXT mode since that is simplest to deal with.
     */
    status = mst_write ( link, "<DNETTEXT>", 10, &length );
    if ( (status &1) == 0 ) return 0;

    status = mst_write ( link, "200 dummping env array", 10, &length );
    if ( (status &1) == 0 ) return 0;
    /*
     * Dump the scriptserver prologue back to the client.
     */
    for ( i = 0; i < 4; i++ ) {
	status = mst_write 
		(link, env.prolog[i], tu_strlen(env.prolog[i]), &length);
        if ( (status &1) == 0 ) break;
    }
    /*
     * Dump the 'environment' variables loaded by mstshr_cgi_symbols.
     */
    for ( i = 0; i < env.count; i++ ) {
	status = mst_write (link, env.ptr[i], tu_strlen(env.ptr[i]), &length);
        if ( (status &1) == 0 ) break;
    }
    /*
     * Cleanly shutdown connection and return.
     */
    status = mst_write ( link, "</DNETTEXT>", 11, &length );
    if ( (status &1) == 0 ) return 0;

    mst_close ( link );
    (*jvm)->DetachCurrentThread(jvm);
    
    return 1;
}
/*****************************************************************************/
/* Native methods for wwwjexec class:
 *    exec_write		write string to client of link.
 *
 */
static jint exec_read ( JNIEnv *env, jclass this, jstring string, jint count)
{ printf ("My native method called\n" ); return 1; }

static jint exec_write ( JNIEnv *env, jclass this, jstring string, jint count)
{ printf ("My native method called\n" ); return 1; }

static jint exec_close ( JNIEnv *env, jclass this )
{
    mst_link_t link;
    link = (mst_link_t) (*env)->GetIntField(env, this, cnx_id);
    return (jint) mst_close ( link );
}

static jint exec_format_error ( JNIEnv *env, jclass string, jint count)
{ printf ("My native method called\n" ); return 1; }
