1 /*
2 Copyright 2010 Sun Microsystems, Inc.
3 All rights reserved. Use is subject to license terms.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25 /*
26 * jtie_tconv_idcache_impl.hpp
27 */
28
29 #ifndef jtie_tconv_idcache_impl_hpp
30 #define jtie_tconv_idcache_impl_hpp
31
32 #include <assert.h> // not using namespaces yet
33 #include <jni.h>
34
35 //#include "helpers.hpp"
36
37 // ---------------------------------------------------------------------------
38 // JNI field/method IDs access & caching
39 // ---------------------------------------------------------------------------
40
41 #if 0 // XXX ddefines not used at this time
42
43 // compilation flags
44 #define JTIE_NO_JNI_ID_CACHING 0
45 #define JTIE_WEAK_JNI_ID_CACHING 1
46 #define JTIE_STRONG_JNI_ID_CACHING 2
47
48 #if !defined(JTIE_JNI_ID_CACHING)
49 # define JNI_ID_CACHING WEAK_CACHING
50 #elif (JTIE_JNI_ID_CACHING == JTIE_NO_JNI_ID_CACHING)
51 # define JNI_ID_CACHING NO_CACHING
52 #elif (JTIE_JNI_ID_CACHING == JTIE_WEAK_JNI_ID_CACHING)
53 # define JNI_ID_CACHING WEAK_CACHING
54 #elif (JTIE_JNI_ID_CACHING == JTIE_STRONG_JNI_ID_CACHING)
55 # define JNI_ID_CACHING STRONG_CACHING
56 #else
57 # error "Illegal value of JTIE_JNI_ID_CACHING"
58 #endif
59
60 #endif // XXX ddefines not used at this time
61
62 // ---------------------------------------------------------------------------
63
64 // Local JNI helper functions
65
66 template< typename T >
67 inline T
68 jniGetMemberID(JNIEnv * env,
69 jclass cls,
70 const char * name, const char * descriptor);
71
72 template<>
73 inline jmethodID
jniGetMemberID(JNIEnv * env,jclass cls,const char * name,const char * descriptor)74 jniGetMemberID< jmethodID >(JNIEnv * env,
75 jclass cls,
76 const char * name, const char * descriptor) {
77 return env->GetMethodID(cls, name, descriptor);
78 }
79
80 template<>
81 inline jfieldID
jniGetMemberID(JNIEnv * env,jclass cls,const char * name,const char * descriptor)82 jniGetMemberID< jfieldID >(JNIEnv * env,
83 jclass cls,
84 const char * name, const char * descriptor) {
85 return env->GetFieldID(cls, name, descriptor);
86 }
87
88 // ---------------------------------------------------------------------------
89
90 // XXX document these macros...
91
92 /**
93 * Defines an info type describing a field member of a Java class.
94 */
95 #define JTIE_DEFINE_FIELD_MEMBER_INFO( T ) \
96 JTIE_DEFINE_CLASS_MEMBER_INFO( T, _jfieldID )
97
98 /**
99 * Defines an info type describing a method member of a Java class.
100 */
101 #define JTIE_DEFINE_METHOD_MEMBER_INFO( T ) \
102 JTIE_DEFINE_CLASS_MEMBER_INFO( T, _jmethodID )
103
104 /**
105 * Defines an info type describing a member of a Java class.
106 */
107 #define JTIE_DEFINE_CLASS_MEMBER_INFO( T, IDT ) \
108 struct T { \
109 static const char * const class_name; \
110 static const char * const member_name; \
111 static const char * const member_descriptor; \
112 typedef IDT * memberID_t; \
113 };
114
115 /**
116 * Instantiates an info type describing a member of a Java class.
117 */
118 #define JTIE_INSTANTIATE_CLASS_MEMBER_INFO( T, CN, MN, MD ) \
119 const char * const T::class_name = CN; \
120 const char * const T::member_name = MN; \
121 const char * const T::member_descriptor = MD; \
122 template struct MemberId< T >; \
123 template struct MemberIdCache< T >;
124
125 /**
126 * Provides uniform access to the JNI Field/Method ID of a Java class member
127 * as described by the member info type 'C'.
128 *
129 * This base class does not cache the member ID and the class object, but
130 * it retrieves the member ID from JNI upon each access; different caching
131 * strategies are provided by derived classes.
132 *
133 * This class (and its derived classes) impose a strict usage pattern.
134 * For example, given definitions...
135 *
136 * // Defines the field info type for MyClass.myField.
137 * JTIE_DEFINE_FIELD_MEMBER_INFO(_MyClass_myField)
138 *
139 * // Provides a (cached) access to field Id of MyClass.myField.
140 * typedef JniMemberId< _MyClass_myField > MyClass_myField;
141 *
142 * any use of a member ID must be bracketed by getClass() and releaseRef():
143 *
144 * // obtain a class reference
145 * jclass cls = MyClass_myField::getClass(env);
146 * if (cls == NULL) {
147 * // exception pending
148 * } else {
149 * // get the field ID valid along with the class reference
150 * jfieldID fid = MyClass_myField::getId(env, cls);
151 * if (fid == NULL) {
152 * // exception pending
153 * } else {
154 * // OK to access field using 'fid'
155 * }
156 * // allow for releasing the class reference
157 * MyClass_myField::releaseRef(env, cls);
158 * }
159 *
160 * Derived classes implement any caching underneath this usage pattern.
161 */
162 template< typename C >
163 struct MemberId {
164 typedef typename C::memberID_t ID_t;
165
166 // number of JNI Get<Field|Method>ID() invocations for statistics
167 static unsigned long nIdLookUps;
168
169 /**
170 * Allows for storing a (global) class reference.
171 *
172 * Usually only called from getClass(), but enables "cache preloading"
173 * from a (native, static) function called at class initialization.
174 *
175 * Pre condition:
176 * - this thread has no pending JNI exception (!env->ExceptionCheck())
177 * - a valid local or global class reference: assert(cls != NULL)
178 */
setClassMemberId179 static void setClass(JNIEnv * env, jclass cls) {
180 assert(cls != NULL);
181 (void)env; (void)cls;
182 }
183
184 /**
185 * Returns a JNI Reference to the class declaring the member specified
186 * by info type 'C'.
187 *
188 * Depending upon the underlying caching strategy, a returned reference
189 * may be local or global, weak or strong; the scope of its use must be
190 * demarcated by releaseRef().
191 *
192 * Pre condition:
193 * - this thread has no pending JNI exception (!env->ExceptionCheck())
194 *
195 * Post condition:
196 * - return value is
197 * NULL:
198 * - this thread has a pending JNI exception (env->ExceptionCheck())
199 * otherwise:
200 * - this thread has no pending JNI exception (!env->ExceptionCheck())
201 * - the returned reference is valid (at least) until releaseRef()
202 */
getClassMemberId203 static jclass getClass(JNIEnv * env) {
204 assert(env->ExceptionCheck() == JNI_OK);
205 jclass cls = env->FindClass(C::class_name);
206 if (cls == NULL) { // break out for better diagnostics
207 assert(env->ExceptionCheck() != JNI_OK); // exception pending
208
209 //#ifndef NDEBUG // XXX for debugging
210 // print error diagnostics
211 char m[256];
212 #ifndef _WIN32
213 snprintf(m, 256, "JTie: failed to find Java class '%s'\n",
214 #else
215 _snprintf(m, 256, "JTie: failed to find Java class '%s'\n",
216 #endif
217 (C::class_name == NULL ? "NULL" : C::class_name));
218 fprintf(stderr, m);
219 env->ExceptionDescribe();
220 env->FatalError(m); // XXX for debugging
221 //#endif // NDEBUG
222 } else {
223 assert(env->ExceptionCheck() == JNI_OK); // ok
224 }
225 return cls;
226 }
227
228 /**
229 * Returns the JNI Field/Method ID of a Java class member.
230 *
231 * The member ID is only valid along with a class object obtained by
232 * getClass() and before releaseRef().
233 *
234 * Pre condition:
235 * - this thread has no pending JNI exception (!env->ExceptionCheck())
236 * - a valid class reference obtained by getClass(): assert(cls != NULL)
237 *
238 * Post condition:
239 * - return value is
240 * NULL:
241 * - this thread has a pending JNI exception (env->ExceptionCheck())
242 * otherwise:
243 * - this thread has no pending JNI exception (!env->ExceptionCheck())
244 * - the returned member ID is valid (at least) until releaseRef()
245 */
getIdMemberId246 static ID_t getId(JNIEnv * env, jclass cls) {
247 assert(cls != NULL);
248 // multithreaded access ok, inaccurate if non-atomic increment
249 nIdLookUps++;
250 return jniGetMemberID< ID_t >(env, cls,
251 C::member_name, C::member_descriptor);
252 }
253
254 /**
255 * Allows for a class reference to be released along with any member IDs.
256 *
257 * Pre condition:
258 * - a valid class reference obtained by getClass(): assert(cls != NULL)
259 * - this thread may have a pending JNI exception (env->ExceptionCheck())
260 */
releaseRefMemberId261 static void releaseRef(JNIEnv * env, jclass cls) {
262 assert(cls != NULL);
263 env->DeleteLocalRef(cls);
264 }
265 };
266
267 /**
268 * Base class for caching of JNI Field/Method IDs.
269 */
270 template< typename C >
271 struct MemberIdCache : MemberId< C > {
272 typedef typename C::memberID_t ID_t;
273
getIdMemberIdCache274 static ID_t getId(JNIEnv * env, jclass cls) {
275 assert(cls != NULL);
276 // the cached member id is only valid along with global class ref
277 assert(env->IsSameObject(gClassRef, NULL) == JNI_FALSE);
278 (void)env; (void)cls;
279 return mid;
280 }
281
282 protected:
283 // the cached global (weak or strong) class ref
284 static jclass gClassRef;
285
286 // the cached member id (only valid along with global class ref)
287 static ID_t mid;
288 };
289
290 /**
291 * Provides caching of JNI Field/Method IDs using weak class references,
292 * allowing classes to be unloaded when no longer used by Java code.
293 */
294 template< typename C >
295 struct MemberIdWeakCache : MemberIdCache< C > {
296 typedef MemberId< C > A;
297 typedef MemberIdCache< C > Base;
298
setClassMemberIdWeakCache299 static void setClass(JNIEnv * env, jclass cls) {
300 assert(cls != NULL);
301
302 // multithreaded access ok, sets same class/member object
303 Base::gClassRef = static_cast< jclass >(env->NewWeakGlobalRef(cls));
304 Base::mid = A::getId(env, cls);
305 }
306
307 using Base::getId; // use as inherited (some compiler wanted this)
308
getClassMemberIdWeakCache309 static jclass getClass(JNIEnv * env) {
310 // a weak global class ref may refer to a freed object at any time
311 // (i.e.: env->IsSameObject(Base::gClassRef, NULL))
312 // unless we've obtained a strong (local or global) non-NULL class ref
313 jclass cls = static_cast< jclass >(env->NewLocalRef(Base::gClassRef));
314 if (cls == NULL) {
315 // global class ref was NULL or referencing a freed object
316 cls = A::getClass(env);
317 if (cls == NULL) {
318 // exception pending
319 } else {
320 setClass(env, cls);
321 }
322 }
323 return cls;
324 }
325
releaseRefMemberIdWeakCache326 static void releaseRef(JNIEnv * env, jclass cls) {
327 assert(cls != NULL);
328 env->DeleteLocalRef(cls);
329 }
330 };
331
332 /**
333 * Provides caching of JNI Field/Method IDs using strong class references,
334 * preventing classes from being unloaded even if no longer used by Java code.
335 */
336 template< typename C >
337 struct MemberIdStrongCache : MemberIdCache< C > {
338 typedef MemberId< C > A;
339 typedef MemberIdCache< C > Base;
340
setClassMemberIdStrongCache341 static void setClass(JNIEnv * env, jclass cls) {
342 assert(cls != NULL);
343
344 // multithreaded access ok, sets same class/member object
345 Base::gClassRef = static_cast< jclass >(env->NewGlobalRef(cls));
346 Base::mid = A::getId(env, cls);
347 }
348
349 using Base::getId; // use as inherited (some compiler wanted this)
350
getClassMemberIdStrongCache351 static jclass getClass(JNIEnv * env) {
352 jclass cls = Base::gClassRef;
353 if (cls == NULL) {
354 cls = A::getClass(env);
355 if (cls == NULL) {
356 // exception pending
357 } else {
358 setClass(env, cls);
359 }
360 }
361 return cls;
362 }
363
releaseRefMemberIdStrongCache364 static void releaseRef(JNIEnv * env, jclass cls) {
365 assert(cls != NULL);
366 (void)env; (void)cls;
367 }
368 };
369
370 /**
371 * Provides caching of JNI Field/Method IDs using weak class references
372 * with preloading (at class initialization) -- VERY TRICKY, NOT SUPPORTED.
373 */
374 template< typename C >
375 struct MemberIdPreloadedWeakCache : MemberIdWeakCache< C > {
376 typedef MemberIdWeakCache< C > Base;
377
378 using Base::setClass; // use as inherited (some compiler wanted this)
379
380 using Base::getId; // use as inherited (some compiler wanted this)
381
getClassMemberIdPreloadedWeakCache382 static jclass getClass(JNIEnv * env) {
383 // weak global class ref is assumed to be preloaded and valid
384 jclass cls = Base::gClassRef;
385 assert(env->IsSameObject(cls, NULL) == JNI_FALSE);
386 return cls;
387 }
388
releaseRefMemberIdPreloadedWeakCache389 static void releaseRef(JNIEnv * env, jclass cls) {
390 assert(cls != NULL);
391 (void)env; (void)cls;
392 }
393 };
394
395 /**
396 * Provides caching of JNI Field/Method IDs using strong class references
397 * with preloading (at class initialization) -- VERY TRICKY, NOT SUPPORTED.
398 */
399 template< typename C >
400 struct MemberIdPreloadedStrongCache : MemberIdStrongCache< C > {
401 typedef MemberIdStrongCache< C > Base;
402
403 using Base::setClass; // use as inherited (some compiler wanted this)
404
405 using Base::getId; // use as inherited (some compiler wanted this)
406
getClassMemberIdPreloadedStrongCache407 static jclass getClass(JNIEnv * env) {
408 // strong global class ref is assumed to be preloaded and valid
409 jclass cls = Base::gClassRef;
410 assert(env->IsSameObject(cls, NULL) == JNI_FALSE);
411 return cls;
412 }
413
414 using Base::releaseRef; // use as inherited (some compiler wanted this)
415 };
416
417 // XXX static initialization <-> multiple compilation units <-> jtie_lib.hpp
418 template< typename C > unsigned long MemberId< C >
419 ::nIdLookUps = 0;
420
421 template< typename C > jclass MemberIdCache< C >
422 ::gClassRef = NULL;
423
424 template< typename C > typename C::memberID_t MemberIdCache< C >
425 ::mid = NULL;
426
427 // XXX document
428
429 /**
430 * The supported caching strategies for member IDs and class references.
431 */
432 enum JniMemberIdCaching {
433 NO_CACHING
434 ,WEAK_CACHING
435 ,STRONG_CACHING
436 #if 0 // preloaded caching very tricky, not supported at this time
437 ,WEAK_CACHING_PRELOAD
438 ,STRONG_CACHING_PRELOAD
439 #endif // preloaded caching very tricky, not supported at this time
440 };
441
442 /**
443 * Generic class for member ID access with selection of caching strategy.
444 */
445 template< JniMemberIdCaching M, typename C >
446 struct JniMemberId;
447
448 template< typename C >
449 struct JniMemberId< NO_CACHING, C >
450 : MemberId< C > {};
451
452 template< typename C >
453 struct JniMemberId< WEAK_CACHING, C >
454 : MemberIdWeakCache< C > {};
455
456 template< typename C >
457 struct JniMemberId< STRONG_CACHING, C >
458 : MemberIdStrongCache< C > {};
459
460 #if 0 // preloaded caching very tricky, not supported at this time
461 template< typename C >
462 struct JniMemberId< WEAK_CACHING_PRELOAD, C >
463 : MemberIdPreloadedWeakCache< C > {};
464
465 template< typename C >
466 struct JniMemberId< STRONG_CACHING_PRELOAD, C >
467 : MemberIdPreloadedStrongCache< C > {};
468 #endif // preloaded caching very tricky, not supported at this time
469
470 // ---------------------------------------------------------------------------
471
472 #endif // jtie_tconv_idcache_impl_hpp
473