2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
6 * http://www.apache.org/licenses/LICENSE-2.0
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
25 #include "structmember.h"
31 _DLL_EXPORT JCCEnv *env;
42 static void t_jccenv_dealloc(t_jccenv *self);
43 static PyObject *t_jccenv_attachCurrentThread(PyObject *self, PyObject *args);
44 static PyObject *t_jccenv_detachCurrentThread(PyObject *self);
45 static PyObject *t_jccenv_isCurrentThreadAttached(PyObject *self);
46 static PyObject *t_jccenv_strhash(PyObject *self, PyObject *arg);
47 static PyObject *t_jccenv__dumpRefs(PyObject *self,
48 PyObject *args, PyObject *kwds);
49 static PyObject *t_jccenv__addClassPath(PyObject *self, PyObject *args);
51 static PyObject *t_jccenv__get_jni_version(PyObject *self, void *data);
52 static PyObject *t_jccenv__get_java_version(PyObject *self, void *data);
53 static PyObject *t_jccenv__get_classpath(PyObject *self, void *data);
55 static PyGetSetDef t_jccenv_properties[] = {
56 { "jni_version", (getter) t_jccenv__get_jni_version, NULL, NULL, NULL },
57 { "java_version", (getter) t_jccenv__get_java_version, NULL, NULL, NULL },
58 { "classpath", (getter) t_jccenv__get_classpath, NULL, NULL, NULL },
59 { NULL, NULL, NULL, NULL, NULL }
62 static PyMemberDef t_jccenv_members[] = {
63 { NULL, 0, 0, 0, NULL }
66 static PyMethodDef t_jccenv_methods[] = {
67 { "attachCurrentThread", (PyCFunction) t_jccenv_attachCurrentThread,
69 { "detachCurrentThread", (PyCFunction) t_jccenv_detachCurrentThread,
71 { "isCurrentThreadAttached", (PyCFunction) t_jccenv_isCurrentThreadAttached,
73 { "strhash", (PyCFunction) t_jccenv_strhash,
75 { "_dumpRefs", (PyCFunction) t_jccenv__dumpRefs,
76 METH_VARARGS | METH_KEYWORDS, NULL },
77 { "_addClassPath", (PyCFunction) t_jccenv__addClassPath,
79 { NULL, NULL, 0, NULL }
82 PyTypeObject PY_TYPE(JCCEnv) = {
83 PyObject_HEAD_INIT(NULL)
85 "jcc.JCCEnv", /* tp_name */
86 sizeof(t_jccenv), /* tp_basicsize */
88 (destructor)t_jccenv_dealloc, /* tp_dealloc */
95 0, /* tp_as_sequence */
96 0, /* tp_as_mapping */
102 0, /* tp_as_buffer */
103 Py_TPFLAGS_DEFAULT, /* tp_flags */
104 "JCCEnv", /* tp_doc */
107 0, /* tp_richcompare */
108 0, /* tp_weaklistoffset */
111 t_jccenv_methods, /* tp_methods */
112 t_jccenv_members, /* tp_members */
113 t_jccenv_properties, /* tp_getset */
116 0, /* tp_descr_get */
117 0, /* tp_descr_set */
118 0, /* tp_dictoffset */
124 static void t_jccenv_dealloc(t_jccenv *self)
126 self->ob_type->tp_free((PyObject *) self);
129 static void add_option(char *name, char *value, JavaVMOption *option)
131 char *buf = new char[strlen(name) + strlen(value) + 1];
133 sprintf(buf, "%s%s", name, value);
134 option->optionString = buf;
138 static void add_paths(char *name, char *p0, char *p1, JavaVMOption *option)
140 #if defined(_MSC_VER) || defined(__WIN32)
145 char *buf = new char[strlen(name) + strlen(p0) + strlen(p1) + 4];
147 sprintf(buf, "%s%s%c%s", name, p0, pathsep, p1);
148 option->optionString = buf;
153 static PyObject *t_jccenv_attachCurrentThread(PyObject *self, PyObject *args)
156 int asDaemon = 0, result;
158 if (!PyArg_ParseTuple(args, "|si", &name, &asDaemon))
161 result = env->attachCurrentThread(name, asDaemon);
163 return PyInt_FromLong(result);
166 static PyObject *t_jccenv_detachCurrentThread(PyObject *self)
168 int result = env->vm->DetachCurrentThread();
170 env->set_vm_env(NULL);
172 return PyInt_FromLong(result);
175 static PyObject *t_jccenv_isCurrentThreadAttached(PyObject *self)
177 if (env->get_vm_env() != NULL)
183 static PyObject *t_jccenv_strhash(PyObject *self, PyObject *arg)
185 int hash = PyObject_Hash(arg);
188 sprintf(buffer, "%08x", (unsigned int) hash);
189 return PyString_FromStringAndSize(buffer, 8);
192 static PyObject *t_jccenv__dumpRefs(PyObject *self,
193 PyObject *args, PyObject *kwds)
195 static char *kwnames[] = {
196 "classes", "values", NULL
198 int classes = 0, values = 0;
201 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwnames,
206 result = PyDict_New();
208 result = PyList_New(env->refs.size());
212 for (std::multimap<int, countedRef>::iterator iter = env->refs.begin();
213 iter != env->refs.end();
215 if (classes) // return dict of { class name: instance count }
217 char *name = env->getClassName(iter->second.global);
218 PyObject *key = PyString_FromString(name);
219 PyObject *value = PyDict_GetItem(result, key);
222 value = PyInt_FromLong(1);
224 value = PyInt_FromLong(PyInt_AS_LONG(value) + 1);
226 PyDict_SetItem(result, key, value);
232 else if (values) // return list of (value string, ref count)
234 char *str = env->toString(iter->second.global);
235 PyObject *key = PyString_FromString(str);
236 PyObject *value = PyInt_FromLong(iter->second.count);
238 #if PY_VERSION_HEX < 0x02040000
239 PyList_SET_ITEM(result, count++, Py_BuildValue("(OO)", key, value));
241 PyList_SET_ITEM(result, count++, PyTuple_Pack(2, key, value));
248 else // return list of (id hash code, ref count)
250 PyObject *key = PyInt_FromLong(iter->first);
251 PyObject *value = PyInt_FromLong(iter->second.count);
253 #if PY_VERSION_HEX < 0x02040000
254 PyList_SET_ITEM(result, count++, Py_BuildValue("(OO)", key, value));
256 PyList_SET_ITEM(result, count++, PyTuple_Pack(2, key, value));
266 static PyObject *t_jccenv__addClassPath(PyObject *self, PyObject *args)
268 const char *classpath;
270 if (!PyArg_ParseTuple(args, "s", &classpath))
273 env->setClassPath(classpath);
278 static PyObject *t_jccenv__get_jni_version(PyObject *self, void *data)
280 return PyInt_FromLong(env->getJNIVersion());
283 static PyObject *t_jccenv__get_java_version(PyObject *self, void *data)
285 return env->fromJString(env->getJavaVersion(), 1);
288 static PyObject *t_jccenv__get_classpath(PyObject *self, void *data)
290 char *classpath = env->getClassPath();
294 PyObject *result = PyString_FromString(classpath);
303 _DLL_EXPORT PyObject *getVMEnv(PyObject *self)
307 t_jccenv *jccenv = (t_jccenv *) PY_TYPE(JCCEnv).tp_alloc(&PY_TYPE(JCCEnv), 0);
310 return (PyObject *) jccenv;
317 static void registerNatives(JNIEnv *vm_env);
320 _DLL_EXPORT PyObject *initJCC(PyObject *module)
322 static int _once_only = 1;
323 #if defined(_MSC_VER) || defined(__WIN32)
324 #define verstring(n) #n
325 PyObject *ver = PyString_FromString(verstring(JCC_VER));
327 PyObject *ver = PyString_FromString(JCC_VER);
329 PyObject_SetAttrString(module, "JCC_VERSION", ver); Py_DECREF(ver);
333 PyEval_InitThreads();
334 INSTALL_TYPE(JCCEnv, module);
337 env = new JCCEnv(NULL, NULL);
346 _DLL_EXPORT PyObject *initVM(PyObject *self, PyObject *args, PyObject *kwds)
348 static char *kwnames[] = {
349 "classpath", "initialheap", "maxheap", "maxstack",
352 char *classpath = NULL;
353 char *initialheap = NULL, *maxheap = NULL, *maxstack = NULL;
356 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzz", kwnames,
358 &initialheap, &maxheap, &maxstack,
364 PyObject *module_cp = NULL;
366 if (initialheap || maxheap || maxstack || vmargs)
368 PyErr_SetString(PyExc_ValueError,
369 "JVM is already running, options are ineffective");
373 if (classpath == NULL && self != NULL)
375 module_cp = PyObject_GetAttrString(self, "CLASSPATH");
376 if (module_cp != NULL)
377 classpath = PyString_AsString(module_cp);
380 if (classpath && classpath[0])
381 env->setClassPath(classpath);
383 Py_XDECREF(module_cp);
385 return getVMEnv(self);
389 JavaVMInitArgs vm_args;
390 JavaVMOption vm_options[32];
393 unsigned int nOptions = 0;
394 PyObject *module_cp = NULL;
396 vm_args.version = JNI_VERSION_1_4;
397 JNI_GetDefaultJavaVMInitArgs(&vm_args);
399 if (classpath == NULL && self != NULL)
401 module_cp = PyObject_GetAttrString(self, "CLASSPATH");
402 if (module_cp != NULL)
403 classpath = PyString_AsString(module_cp);
407 PyObject *jcc = PyImport_ImportModule("jcc");
408 PyObject *cp = PyObject_GetAttrString(jcc, "CLASSPATH");
411 add_paths("-Djava.class.path=", PyString_AsString(cp), classpath,
412 &vm_options[nOptions++]);
414 add_option("-Djava.class.path=", PyString_AsString(cp),
415 &vm_options[nOptions++]);
421 add_option("-Djava.class.path=", classpath,
422 &vm_options[nOptions++]);
425 Py_XDECREF(module_cp);
428 add_option("-Xms", initialheap, &vm_options[nOptions++]);
430 add_option("-Xmx", maxheap, &vm_options[nOptions++]);
432 add_option("-Xss", maxstack, &vm_options[nOptions++]);
437 char *buf = _strdup(vmargs);
439 char *buf = strdup(vmargs);
444 for (option = strtok(buf, sep); option; option = strtok(NULL, sep))
446 if (nOptions < sizeof(vm_options) / sizeof(JavaVMOption))
447 add_option("", option, &vm_options[nOptions++]);
451 for (unsigned int i = 0; i < nOptions; i++)
452 delete vm_options[i].optionString;
453 PyErr_Format(PyExc_ValueError, "Too many options (> %d)",
461 //vm_options[nOptions++].optionString = "-verbose:gc";
462 //vm_options[nOptions++].optionString = "-Xcheck:jni";
464 vm_args.nOptions = nOptions;
465 vm_args.ignoreUnrecognized = JNI_FALSE;
466 vm_args.options = vm_options;
468 if (JNI_CreateJavaVM(&vm, (void **) &vm_env, &vm_args) < 0)
470 for (unsigned int i = 0; i < nOptions; i++)
471 delete vm_options[i].optionString;
473 PyErr_Format(PyExc_ValueError,
474 "An error occurred while creating Java VM");
478 env->set_vm(vm, vm_env);
480 for (unsigned int i = 0; i < nOptions; i++)
481 delete vm_options[i].optionString;
483 t_jccenv *jccenv = (t_jccenv *) PY_TYPE(JCCEnv).tp_alloc(&PY_TYPE(JCCEnv), 0);
487 registerNatives(vm_env);
490 return (PyObject *) jccenv;
496 static void raise_error(JNIEnv *vm_env, const char *message)
498 jclass cls = vm_env->FindClass("org/apache/jcc/PythonException");
499 vm_env->ThrowNew(cls, message);
502 static void _PythonVM_init(JNIEnv *vm_env, jobject self,
503 jstring programName, jobjectArray args)
505 const char *str = vm_env->GetStringUTFChars(programName, JNI_FALSE);
509 // load python runtime for other .so modules to link (such as _time.so)
510 sprintf(buf, "libpython%d.%d.so", PY_MAJOR_VERSION, PY_MINOR_VERSION);
511 dlopen(buf, RTLD_NOW | RTLD_GLOBAL);
514 Py_SetProgramName((char *) str);
516 PyEval_InitThreads();
521 int argc = vm_env->GetArrayLength(args);
522 char **argv = (char **) calloc(argc + 1, sizeof(char *));
524 argv[0] = (char *) str;
525 for (int i = 0; i < argc; i++) {
526 jstring arg = (jstring) vm_env->GetObjectArrayElement(args, i);
527 argv[i + 1] = (char *) vm_env->GetStringUTFChars(arg, JNI_FALSE);
530 PySys_SetArgv(argc + 1, argv);
532 for (int i = 0; i < argc; i++) {
533 jstring arg = (jstring) vm_env->GetObjectArrayElement(args, i);
534 vm_env->ReleaseStringUTFChars(arg, argv[i + 1]);
539 PySys_SetArgv(1, (char **) &str);
541 vm_env->ReleaseStringUTFChars(programName, str);
542 PyEval_ReleaseLock();
545 static jobject _PythonVM_instantiate(JNIEnv *vm_env, jobject self,
546 jstring moduleName, jstring className)
548 PythonGIL gil(vm_env);
550 const char *modStr = vm_env->GetStringUTFChars(moduleName, JNI_FALSE);
552 PyImport_ImportModule((char *) modStr); // python 2.4 cast
554 vm_env->ReleaseStringUTFChars(moduleName, modStr);
558 raise_error(vm_env, "import failed");
562 const char *clsStr = vm_env->GetStringUTFChars(className, JNI_FALSE);
564 PyObject_GetAttrString(module, (char *) clsStr); // python 2.4 cast
568 vm_env->ReleaseStringUTFChars(className, clsStr);
573 raise_error(vm_env, "class not found");
577 obj = PyObject_CallFunctionObjArgs(cls, NULL);
582 raise_error(vm_env, "instantiation failed");
586 PyObject *cObj = PyObject_GetAttrString(obj, "_jobject");
590 raise_error(vm_env, "instance does not proxy a java object");
596 jobj = (jobject) PyCObject_AsVoidPtr(cObj);
599 jobj = vm_env->NewLocalRef(jobj);
607 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
611 if (!vm->GetEnv((void **) &vm_env, JNI_VERSION_1_4))
612 env = new JCCEnv(vm, vm_env);
614 registerNatives(vm_env);
616 return JNI_VERSION_1_4;
619 JNIEXPORT void JNICALL Java_org_apache_jcc_PythonVM_init(JNIEnv *vm_env, jobject self, jstring programName, jobjectArray args)
621 return _PythonVM_init(vm_env, self, programName, args);
624 JNIEXPORT jobject JNICALL Java_org_apache_jcc_PythonVM_instantiate(JNIEnv *vm_env, jobject self, jstring moduleName, jstring className)
626 return _PythonVM_instantiate(vm_env, self, moduleName, className);
629 JNIEXPORT jint JNICALL Java_org_apache_jcc_PythonVM_acquireThreadState(JNIEnv *vm_env)
631 PyGILState_STATE state = PyGILState_Ensure();
632 PyThreadState *tstate = PyGILState_GetThisThreadState();
635 if (tstate != NULL && tstate->gilstate_counter >= 1)
636 result = ++tstate->gilstate_counter;
638 PyGILState_Release(state);
642 JNIEXPORT jint JNICALL Java_org_apache_jcc_PythonVM_releaseThreadState(JNIEnv *vm_env)
644 PyGILState_STATE state = PyGILState_Ensure();
645 PyThreadState *tstate = PyGILState_GetThisThreadState();
648 if (tstate != NULL && tstate->gilstate_counter >= 1)
649 result = --tstate->gilstate_counter;
651 PyGILState_Release(state);
656 static void JNICALL _PythonException_getErrorInfo(JNIEnv *vm_env, jobject self)
658 PythonGIL gil(vm_env);
660 if (!PyErr_Occurred())
663 PyObject *type, *value, *tb, *errorName;
664 jclass jcls = vm_env->GetObjectClass(self);
666 PyErr_Fetch(&type, &value, &tb);
668 errorName = PyObject_GetAttrString(type, "__name__");
669 if (errorName != NULL)
672 vm_env->GetFieldID(jcls, "errorName", "Ljava/lang/String;");
673 jstring str = env->fromPyString(errorName);
675 vm_env->SetObjectField(self, fid, str);
676 vm_env->DeleteLocalRef(str);
677 Py_DECREF(errorName);
682 PyObject *message = PyObject_Str(value);
687 vm_env->GetFieldID(jcls, "message", "Ljava/lang/String;");
688 jstring str = env->fromPyString(message);
690 vm_env->SetObjectField(self, fid, str);
691 vm_env->DeleteLocalRef(str);
696 PyObject *module = NULL, *cls = NULL, *stringIO = NULL, *result = NULL;
697 PyObject *_stderr = PySys_GetObject("stderr");
701 module = PyImport_ImportModule("cStringIO");
705 cls = PyObject_GetAttrString(module, "StringIO");
710 stringIO = PyObject_CallObject(cls, NULL);
716 PySys_SetObject("stderr", stringIO);
718 PyErr_Restore(type, value, tb);
721 result = PyObject_CallMethod(stringIO, "getvalue", NULL);
727 vm_env->GetFieldID(jcls, "traceback", "Ljava/lang/String;");
728 jstring str = env->fromPyString(result);
730 vm_env->SetObjectField(self, fid, str);
731 vm_env->DeleteLocalRef(str);
735 PySys_SetObject("stderr", _stderr);
741 PyErr_Restore(type, value, tb);
744 static void JNICALL _PythonException_clear(JNIEnv *vm_env, jobject self)
746 PythonGIL gil(vm_env);
750 static void registerNatives(JNIEnv *vm_env)
752 jclass cls = vm_env->FindClass("org/apache/jcc/PythonException");
753 JNINativeMethod methods[] = {
754 { "getErrorInfo", "()V", (void *) _PythonException_getErrorInfo },
755 { "clear", "()V", (void *) _PythonException_clear },
758 vm_env->RegisterNatives(cls, methods, 2);
761 #endif /* _jcc_lib */