NEURON
njvm.cpp
Go to the documentation of this file.
1 /*
2  March 2001 modified by Michael Hines so that NEURON starts
3  the Java VM
4 */
5 
6 // nj_load() supports the load_java hoc command
7 
8 // nrn_InitializeJavaVM makes the Java virtual machine ready for use
9 // this is done from ivocmain.cpp shortly after NEURON is launched.
10 // Initializing just before the first load_java command did not work.
11 // Mac and MSWIN dynamically load the jvm.
12 
13 #include <../../nrnconf.h>
14 #ifdef WIN32
15 #include <windows.h>
16 #endif
17 
18 #include "njconf.h" // which jvm version to use
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <jni.h>
23 #include <InterViews/resource.h>
24 #include "nrnoc2iv.h"
25 #include "njvm.h"
26 #if HAVE_LOCALE_H
27 // Java may set the locale so sprint(buf, "%g", 1.5) gives "1,5"
28 // if so we will need to set it back.
29 #include <locale.h>
30 #endif
31 #if defined(JVM_DLOPEN)
32 #include <dlfcn.h>
33 jint nrn_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
34 #endif
35 
36 // Java virtual machine version
37 // The Mac only has Java 1 via the MRJ. No reason not to always use
38 // Java2 for mswin. Unix can use either (see njconf.h.in)
39 // However, only Java2 is fully supported and allows additions to
40 // the classpath from hoc.
41 #if carbon
42 #undef MAC
43 #endif
44 
45 #ifndef USEJVM
46 #if MAC
47 #define USEJVM 1
48 #else
49 #define USEJVM 2
50 #endif
51 #endif
52 
53 extern "C" {
54 // Java has threads and NEURON does not. It is important that when
55 // Java calls a function in NEURON and then NEURON calls back a function
56 // in Java that the proper thread environment is used. This is done
57 // with the njvm.h macros jnisave and jnirestore that put the env into nrnjava_env
58 // How are errors handled?
59 JNIEnv* nrnjava_env;
61 JavaVM* nrnjava_vm; // CreateJavaVM fills this in but we do not use it.
62 extern char* neuron_home;
63 extern int(*p_hoc_load_java)();
64 static int nj_load();
65 extern void* (*p_java2nrn_cons)(Object*);
66 
67 #ifdef WIN32
68 #undef _WIN32
69 #define _WIN32
70 #endif
71 #ifdef _WIN32
72 extern "C" char* hoc_back2forward(char*);
73 jint nrn_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
74 #endif
75 #if MAC
76 static jint nrn_GetDefaultJavaVMInitArgs(void*);
77 static jint nrn_CreateJavaVM(JavaVM **pvm, JNIEnv **penv, void *args);
78 #endif
79 }
80 int convertJavaClassToHoc(JNIEnv*, const char*, const char*, const char*);
81 void nrnjava_init();
82 
83 #ifdef _WIN32
84 #define PATH_SEPARATOR ';'
85 #else /* UNIX */
86 #define PATH_SEPARATOR ':'
87 #endif
88 
89 #define NULL_CHECK(arg) nrn_assert((arg))
90 #if USEJVM == 2
91 /*
92  * List of VM options to be specified when the VM is created.
93  */
94 static JavaVMOption *options;
95 static int numOptions, maxOptions;
96 #endif
97 
98 extern "C" { // needed by microsoft visual c++
99 // support load_java command in hoc.
100 // e.g. load_java("java.lang.String", "JString")
101 // p_hoc_load_java points to this
102 static int nj_load() {
103  // first time through, initialize
104  if (!p_java2nrn_cons) {
105  if (!nrnjava_root_env) {
106  hoc_execerror("The JavaVM is not available.", 0);
107  }
108  nrnjava_init();
109  if (!p_java2nrn_cons) {
110  hoc_execerror(" Java portion of NEURON was not initialized.", 0);
111  }
112  }
113  char* jname; // fully qualified name. e.g. java.lang.String
114  char* hname; // hoc name, e.g. JString
115  char* path = ""; // classpath addition to find jname. See NrnClassLoader.add
116 
117  jname = gargstr(1);
118  // hocname same as jname if not specified as second arg
119  if (ifarg(2)) {
120  hname = gargstr(2);
121  }else{
122  hname = jname;
123  }
124  if (ifarg(3)) {
125  path = gargstr(3);
126  }
127  // see neuron/Neuron.java makeHocClass
128  return convertJavaClassToHoc(nrnjava_env, jname, hname, path);
129 }
130 }
131 
132 #if USEJVM == 2
133 // copied from /usr/j2se/src.jar : src/launcher/java.cpp
134 /*
135  * Adds a new VM option with the given given name and value.
136  */
137 static void
138 AddOption(char *str, void *info)
139 {
140  int i;
141  /*
142  * Expand options array if needed to accomodate at least one more
143  * VM option.
144  */
145  if (numOptions >= maxOptions) {
146  if (options == 0) {
147  maxOptions = 4;
148  options = new JavaVMOption[maxOptions];
149  } else {
150  JavaVMOption *tmp;
151  tmp = new JavaVMOption[maxOptions*2];
152  for (i=0; i < numOptions; ++i) {
153  tmp[i].optionString = options[i].optionString;
154  tmp[i].extraInfo = options[i].extraInfo;
155  }
156  maxOptions *= 2;
157  delete [] options;
158  options = tmp;
159  }
160  }
161  options[numOptions].optionString = str;
162  options[numOptions++].extraInfo = info;
163 }
164 #endif
165 
166 // copied from /usr/j2se/src.jar : src/launcher/java.cpp
167 /*
168  * Prints the version information from the java.version and other properties.
169  */
170 
171 static void
173 {
174  jclass ver;
175  jmethodID print;
176 
177  NULL_CHECK(ver = (env)->FindClass("sun/misc/Version"));
178  NULL_CHECK(print = (env)->GetStaticMethodID(ver, "print", "()V"));
179 
180  (env)->CallStaticVoidMethod(ver, print);
181 }
182 
183 static void myabort() {
184  printf("my abort\n");
185  exit(1);
186 }
187 
188 static void myexit() {
189  printf("my exit\n");
190  exit(1);
191 }
192 
193 jint myvfprintf(FILE* fp, const char* format, va_list args);
194 
195 jint myvfprintf(FILE* fp, const char* format, va_list args) {
196  char buf[1024];
197  vsprintf(buf, format, args);
198  printf("%s", buf);
199  return 1;
200 }
201 
202 #if USEJVM == 1
203 // allowing Mac classic dlopen and Unix static
204 static void initialize_jvm1();
205 static void initialize_jvm1() {
206  char* classpath;
207  JDK1_1InitArgs args;
208  args.version = 0x00010001;
209  //args.debugging = 1;
210  //args.vfprintf = myvfprintf;
211 #if MAC
212  if (nrn_GetDefaultJavaVMInitArgs(&args) < 0) {
213  return;
214  }
215  classpath = new char[strlen(args.classpath) + 2*strlen(neuron_home) + 100];
216  // following string for mac, then convert ; to :
217  sprintf(classpath, "%s;/%s/classes/neuron.jar",
218  args.classpath, neuron_home);
219  for (char* cp = classpath; *cp; ++cp) {
220  if (*cp == ';') {
221  *cp = ':';
222  }else if (*cp == ':') {
223  *cp = '/';
224  }
225  }
226 #else
227  JNI_GetDefaultJavaMVInitArgs(&args);
228  const char* ucpenv = getenv("CLASSPATH");
229 // Find classes first in the working directory where neuron was launched.
230 // Then the users CLASSPATH
231 // environment variable (if any). And lastly, the, $NEURONHOME/classes
232  if (ucpenv == nil) {
233  ucpenv = "."; // can't hurt to have it twice
234  }
235  int len = strlen(args.classpath) + strlen(ucpenv) + 2*strlen(neuron_home) + 100;
236  classpath = new char[len];
237 
238  sprintf(classpath,
239  "%s%c.%c%s%c%s/classes/neuron.jar",
240  args.classpath, PATH_SEPARATOR, PATH_SEPARATOR,
242  );
243 
244 //printf("%s\n", classpath);
245 
246  sprintf(classpath, "%s:.:%s/classes/neuron.jar", args.classpath, neuron_home);
247 #endif
248  args.classpath = classpath;
249  //for (int i = 0; args.properties[i]; ++i) {
250  //printf("properties |%s|\n", args.properties[i]);
251  //}
252  //args.debugging = 1;
253  //args.vfprintf = myvfprintf;
254  printf("classpath |%s|\n", args.classpath);
255 #if MAC
256  jint res = nrn_CreateJavaVM(&nrnjava_vm, &nrnjava_root_env, (void*)&args);
257 #else
258  jint res = JNI_CreateJavaVM(&nrnjava_vm, (void**)&nrnjava_root_env, &args);
259 #endif
261  if (res < 0) {
262  fprintf(stderr, "JNI_CreateJavaVM returned %d\n", res);
263  }else{
265  fprintf(stderr, "Created Java VM\n");
266  }
267 }
268 #endif
269 
270 #if USEJVM == 2
271 // allowing mswin and unix dlopen and unix static
272 static void initialize_jvm2();
273 static void initialize_jvm2() {
274  JavaVMInitArgs args;
275 // Because we want to dynamically append to the classpath from hoc
276 // we do all class loading through the neuron/NrnClassLoader in order
277 // to defeat the security policy.
278  char* classpath;
279  int len = strlen(neuron_home) + 100;
280  classpath = new char[len];
281  sprintf(classpath, "-Djava.class.path=%s/classes/nrnclsld.jar", neuron_home);
282 
283 #if defined(_WIN32)
284  hoc_back2forward(classpath);
285 #endif
286 
287 //printf("%s\n", classpath);
288 
289  args.version = JNI_VERSION_1_2;
290 //printf("version = %lx\n", args.version);
291 
292  AddOption(classpath, nil);
293 // AddOption("-verbose", nil);
294 // AddOption("abort", myabort);
295 // AddOption("exit", myexit);
296  args.nOptions = numOptions;
297  args.options = options;
298  args.ignoreUnrecognized = JNI_FALSE;
299 
300 #if defined(_WIN32) || defined(JVM_DLOPEN)
301  jint res = nrn_CreateJavaVM(&nrnjava_vm, (void**)&nrnjava_root_env, &args);
302  if (res == -10) { return; }
303 #else
304  jint res = JNI_CreateJavaVM(&nrnjava_vm, (void**)&nrnjava_root_env, &args);
305 #endif
307  delete [] classpath;
308  delete [] options;
309  if (res < 0) {
310  switch (res) {
311  case JNI_EVERSION:
312  fprintf(stderr, "JNI Version error. VM is not JNI_VERSION_1_2\n");
313  break;
314  case JNI_ENOMEM:
315  fprintf(stderr, "Not enough memory\n");
316  break;
317  case JNI_EINVAL:
318  fprintf(stderr, "invalid arguments\n");
319  break;
320  default:
321  fprintf(stderr, "JNI_CreateJavaVM returned %d\n", res);
322  break;
323  }
324  fprintf(stderr, "Info: optional feature Java VM is not present.\n");
325  }else{
327  if (nrn_istty_) {
328  fprintf(stderr, "Created Java VM\n");
330  }
331  }
332 }
333 #endif
334 
336  if (nrnjava_root_env) { // hmm. NEURON must have been loaded by java
339  } else {
340 #if USEJVM == 2
341  initialize_jvm2();
342 #else
343  initialize_jvm1();
344 #endif
345  }
346 
347 #if HAVE_LOCALE_H
348  // in case Java set the locale such that the radix is a ',', set it
349  // back to a '.'
350  char radixtest[50];
351  sprintf(radixtest, "%g", 1.5);
352 //printf("radixtest=|%s|\n", radixtest);
353  if (strchr(radixtest, ',')) {
354  setlocale(LC_NUMERIC, "C");
355 // sprintf(radixtest, "%g", 1.5);
356 //printf("after setlocale(LC_NUMERIC, \"C\"), radixtest=|%s|\n", radixtest);
357  }
358 #endif
359 }
360 
361 #if defined(JVM_DLOPEN)
362 
363 #include <InterViews/session.h>
364 #include <OS/string.h>
365 #include <InterViews/style.h>
366 typedef jint(*PCJVM)(JavaVM**, void**, void*);
367 
368 jint nrn_CreateJavaVM(JavaVM **pvm, void **penv, void *args) {
369  jint res;
370 
371  *pvm = 0;
372  *penv = 0;
373  Session* ses = Session::instance();
374  String str("libjvm.so");
375  char* name = "jvmdll";
376  if (ses && !ses->style()->find_attribute(name, str)){
377 // fprintf(stderr, "\"%s\" not defined in nrn.defaults\n", name);
378  return -10;
379  }
380  void* handle = (void *) dlopen(str.string(), RTLD_NOW | RTLD_GLOBAL);
381  if (!handle) {
382  fprintf(stderr, "dlopen(\"%s\") failed: %s\n", str.string(), dlerror());
383  return -1;
384  }
385 #if defined(DARWIN)
386  PCJVM addr = (PCJVM)dlsym(handle, "JNI_CreateJavaVM_Impl");
387 #else
388  PCJVM addr = (PCJVM)dlsym(handle, "JNI_CreateJavaVM");
389 #endif
390  if (!addr) {
391  fprintf(stderr, "%s\n", dlerror());
392  return -1;
393  }
394  res = (*addr)(pvm, penv, args);
395  return res;
396 }
397 #endif
398 
399 #ifdef _WIN32
400 #if _MSC_VER
401 #undef bool
402 #endif
403 #if defined(__MWERKS__) && __MWERKS__ >= 7
404 #undef bool
405 #endif
406 #include <InterViews/session.h>
407 #include <OS/string.h>
408 #include <InterViews/style.h>
409 static int jerr_;
410 static void *
411 dlopen (const char *name, int)
412 {
413  void *ret;
414 
415  {
416  /* handle for the named library */
417  String str;
418  jerr_ = 0;
419  if (!Session::instance()->style()->find_attribute(name, str)){
420  //fprintf(stderr, "\"%s\" not defined in nrn.def\n", name);
421  jerr_ = -10;
422  ret = NULL;
423  }else{
424  ret = (void *) LoadLibrary (str.string());
425  if (ret == NULL) {
426  DWORD dw = GetLastError();
427  fprintf(stderr, "LoadLibrary(\"%s\") failed with error %d\n", str.string(), dw);
428  jerr_ = -1;
429  }
430  }
431  }
432 
433  return ret;
434 }
435 
436 static void *
437 dlsym (void *handle, const char *name)
438 {
439  void *ret = (void *) GetProcAddress ((HMODULE) handle, name);
440  if (!ret) {
441  fprintf(stderr, "Could not GetProcAddress for \"%s\"\n", name);
442  }
443  return ret;
444 }
445 
446 #if defined(_MSC_VER)
447 typedef jint(CALLBACK *PCJVM)(JavaVM**, void**, void*);
448 #else
449 typedef jint(*PCJVM)(JavaVM**, void**, void*);
450 #endif
451 
452 jint nrn_CreateJavaVM(JavaVM **pvm, void **penv, void *args) {
453  jint res;
454 
455  *pvm = 0;
456  *penv = 0;
457  void* handle = dlopen("jvmdll", 0);
458  if (!handle) { return jerr_; }
459  PCJVM addr = (PCJVM)dlsym(handle, "JNI_CreateJavaVM");
460  if (!addr) { return -1; }
461  res = (*addr)(pvm, penv, args);
462  return res;
463 }
464 #endif
465 
466 #if MAC
467 extern "C" {
468 bool is_mac_dll(FSSpec*);
469 extern OSErr __path2fss(const char* name, FSSpec*);
470 }
471 typedef jint(*PCJVM)(JavaVM**, JNIEnv**, void*);
472 typedef jint(*PIJVM)(void*);
473 typedef CFragConnectionID(*PF)();
474 static PCJVM caddr;
475 
476 static jint nrn_GetDefaultJavaVMInitArgs(void* args) {
477  PIJVM iaddr = 0;
478  long i, cnt;
479  CFragConnectionID id;
480  CFragSymbolClass sc;
481  Ptr sa;
482  Str255 sname;
483  OSErr myErr;
484  FSSpec fs;
485  char name[256];
486 
487  sprintf(name, "%s:nrnjvmdll", neuron_home);
488 
489  if ((__path2fss(name, &fs) == fnfErr) || !is_mac_dll(&fs)) {
490  fprintf(stderr, "%s is not the nrnjvmdll\n", name);
491  return -1;
492  }
493 
494  myErr = GetDiskFragment(&fs, 0, kCFragGoesToEOF,
495  0, kLoadCFrag, &id, &sa, sname);
496  //myErr = GetSharedLibrary("\pMRJLib", kPowerPCCFragArch, kLoadCFrag,
497  // &id, &sa, sname);
498  if (myErr) {
499  sname[sname[0]+1]='\0';
500  fprintf(stderr, "could not load the Java VM : %s\n", sname+1);
501  return -1;
502  }
503  sa = 0;
504  myErr = CountSymbols(id, &cnt);
505  //printf("%d symbols exported\n", cnt);
506  for (i=0; i < cnt; ++i) {
507  myErr = GetIndSymbol(id, i, sname, &sa, &sc);
508  sname[sname[0]+1] = '\0';
509  //printf("%d %s\n", i, sname+1);
510  if (strcmp((sname+1), "nrn2_GetDefaultJavaVMInitArgs") == 0) {
511  iaddr = (PIJVM)sa;
512  }
513  if (strcmp((sname+1), "nrn2_CreateJavaVM") == 0) {
514  caddr = (PCJVM)sa;
515  }
516  }
517  if (iaddr) {
518  jint res = (*iaddr)(args);
519  if (res < 0) {
520  fprintf(stderr, "call to JNI_GetDefaultJavaVMInitArgs returned %d\n", res);
521  }
522  return res;
523  }
524  fprintf(stderr, "no address for JNI_GetDefaultJavaVMInitArgs\n");
525  return -1;
526 }
527 
528 static jint nrn_CreateJavaVM(JavaVM **pvm, JNIEnv **penv, void *args) {
529  jint res;
530  *pvm = 0;
531  *penv = 0;
532  if (!caddr) {
533  fprintf(stderr, "no address for JNI_CreateJavaVM\n");
534  return -1;
535  }
536  res = (*caddr)(pvm, penv, args);
537  return res;
538 }
539 #endif
static void myabort()
Definition: njvm.cpp:183
JavaVM * nrnjava_vm
Definition: njvm.cpp:61
char * hoc_back2forward(char *s)
Definition: mswinprt.cpp:193
static void AddOption(char *str, void *info)
Definition: njvm.cpp:138
const char * dlerror(void)
Definition: osxdlfcn.cpp:174
int convertJavaClassToHoc(JNIEnv *, const char *, const char *, const char *)
Make a hoc equivalent to Java class (jname) calling it hname in hoc.
Definition: nrnjava.cpp:299
static void PrintJavaVersion(JNIEnv *env)
Definition: njvm.cpp:172
#define print
Definition: redef.h:109
JNIEnv * nrnjava_env
Definition: njvm.cpp:59
sprintf(buf," if (secondorder) {\ " int _i;\" " for(_i=0;_i< %d;++_i) {\" " _p[_slist%d[_i]]+=dt *_p[_dlist%d[_i]];\" " }}\", numeqn, listnum, listnum)
#define PATH_SEPARATOR
Definition: njvm.cpp:86
#define NULL_CHECK(arg)
Definition: njvm.cpp:89
#define gargstr
Definition: hocdec.h:14
static List * info
const char * string() const
Definition: string.h:139
int nrn_istty_
Definition: hoc.cpp:870
#define RTLD_NOW
Definition: osxdlfcn.h:61
bool is_mac_dll(FSSpec *)
Definition: ivocmac.cpp:58
JNIEnv * nrnjava_root_env
Definition: njvm.cpp:60
static int maxOptions
Definition: njvm.cpp:95
static Frame * fp
Definition: code.cpp:154
static int numOptions
Definition: njvm.cpp:95
#define printf
Definition: mwprefix.h:26
#define ret
Definition: redef.h:123
int
Definition: nrnmusic.cpp:71
void hoc_execerror(const char *, const char *)
Definition: hoc.cpp:741
#define cnt
Definition: spt2queue.cpp:19
char * getenv(const char *s)
Definition: macprt.cpp:67
void *(* p_java2nrn_cons)(Object *)
Definition: hoc_oop.cpp:22
fprintf(stderr, "Don't know the location of params at %p\, pp)
static JavaVMOption * options
Definition: njvm.cpp:94
static int nj_load()
Definition: njvm.cpp:102
void * dlopen(const char *path, int mode)
Definition: osxdlfcn.cpp:85
static char * env[]
Definition: inithoc.cpp:228
void(* PF)(void *, int)
Definition: ivoc.cpp:25
char * name
Definition: init.cpp:16
#define nil
Definition: enter-scope.h:36
void nrnjava_init()
Definition: nrnjava.cpp:132
static char * format
Definition: matrixio.c:386
OSErr __path2fss(const char *name, FSSpec *)
int ifarg(int)
Definition: code.cpp:1562
Definition: hocdec.h:226
void nrn_InitializeJavaVM()
Definition: njvm.cpp:335
#define i
Definition: md1redef.h:12
#define id
Definition: md1redef.h:33
static void initialize_jvm2()
Definition: njvm.cpp:273
int(* p_hoc_load_java)()
Definition: fileio.cpp:619
Definition: string.h:34
char buf[512]
Definition: init.cpp:13
void * dlsym(void *handle, const char *symbol)
Definition: osxdlfcn.cpp:193
static void myexit()
Definition: njvm.cpp:188
jint myvfprintf(FILE *fp, const char *format, va_list args)
Definition: njvm.cpp:195
char * neuron_home
Definition: hoc_init.cpp:268
#define Session
Definition: ivocmain.cpp:7
return NULL
Definition: cabcode.cpp:461
static char * ver[10]
Definition: nrnversion.cpp:16
#define RTLD_GLOBAL
Definition: osxdlfcn.h:63