1 /**
2  * @copyright
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  * @endcopyright
22  *
23  * @file JNIUtil.h
24  * @brief Interface of the class JNIUtil
25  */
26 
27 #ifndef JNIUTIL_H
28 #define JNIUTIL_H
29 
30 #include <list>
31 #include <vector>
32 #include "Pool.h"
33 struct apr_pool_t;
34 class JNIMutex;
35 class SVNBase;
36 #include <jni.h>
37 #include <fstream>
38 #include <apr_time.h>
39 #include <string>
40 #include <vector>
41 
42 struct svn_error_t;
43 struct svn_string_t;
44 
45 #include "svn_error.h"
46 
47 
48 /**
49  * The name of the package in which the JavaHL classes are defined.
50  */
51 #define JAVAHL_PACKAGE "org/apache/subversion/javahl"
52 
53 /**
54  * Construct a JavaHL class name for JNIEnv::FindClass.
55  */
56 #define JAVAHL_CLASS(name) JAVAHL_PACKAGE name
57 
58 /**
59  * Construct a JavaHL class parameter name for JNIEnv::GetMethodID & co.
60  */
61 #define JAVAHL_ARG(name) "L" JAVAHL_PACKAGE name
62 
63 
64 /**
65  * Class to hold a number of JNI related utility methods.  No Objects
66  * of this class are ever created.
67  */
68 class JNIUtil
69 {
70  public:
71   static svn_error_t *preprocessPath(const char *&path, apr_pool_t *pool);
72 
73   /**
74    * Throw the Java NativeException instance named by
75    * exceptionClassName.  A NativeException sub-class must supply a
76    * 3-arg constructor identical to that of NativeException.  @a
77    * source is any file name and line number information.
78    */
79   static void throwNativeException(const char *exceptionClassName,
80                                    const char *msg,
81                                    const char *source = NULL,
82                                    int aprErr = -1);
83 
84   static void throwNullPointerException(const char *message);
85   static jbyteArray makeJByteArray(const void *data, int length);
86   static jbyteArray makeJByteArray(const svn_string_t *str);
87   static jobject createDate(apr_time_t time);
88   static apr_time_t getDate(jobject jdate);
89   static void logMessage(const char *message);
90   static int getLogLevel();
91   static void initLogFile(int level, jstring path);
92   static jstring makeJString(const char *txt);
93   static JNIEnv *getEnv();
94 
95   /**
96    * @return Whether any Throwable has been raised.
97    */
isExceptionThrown()98   static bool isExceptionThrown() { return isJavaExceptionThrown(); }
isJavaExceptionThrown()99   static bool isJavaExceptionThrown()
100     {
101       return getEnv()->ExceptionCheck();
102     }
103 
104   static svn_error_t *wrapJavaException();
105   static jthrowable unwrapJavaException(const svn_error_t *err);
106 
107   static void handleAPRError(int error, const char *op);
108 
109   /**
110    * Put @a object in the list of finalized objects queued up to be
111    * deleted (by another thread) during the next operation.
112    *
113    * @param object The C++ peer of the finalized (Java) object.
114    * @since 1.4.0
115    */
116   static void enqueueForDeletion(SVNBase *object);
117 
118   /**
119    * @deprecated Use the more appropriately named
120    * enqueueForDeletion() instead.
121    */
122   static void putFinalizedClient(SVNBase *cl);
123 
124   /**
125    * Convert any exception that may have been thrown into a textual
126    * representation.  Return @c NULL if no exception has
127    * occurred. Useful for converting Java @c Exceptions into @c
128    * svn_error_t's.
129    */
130   static const char *thrownExceptionToCString(SVN::Pool &in_pool);
131 
132   /**
133    * Check if a Java exception was thrown and convert it to a
134    * Subversion error, using @a errorcode as the generic error code.
135    */
136   static svn_error_t* checkJavaException(apr_status_t errorcode);
137 
138   /**
139    * Create a Java exception corresponding to err, and run
140    * svn_error_clear() on err.
141    */
142   static jthrowable createClientException(svn_error_t *err,
143                                           jthrowable jcause = NULL);
144 
145   /**
146    * Throw a Java exception corresponding to err, and run
147    * svn_error_clear() on err.
148    */
149   static void handleSVNError(svn_error_t *err, jthrowable jcause = NULL);
150 
151   static std::string makeSVNErrorMessage(svn_error_t *err,
152                                          jstring *jerror_message,
153                                          jobject *jmessage_stack);
154 
155   /**
156    * Create and throw a java.lang.Throwable instance.
157    *
158    * @param name The class name (in path form, with slashes in lieu
159    * of dots) of the Throwable to create and raise.
160    * @param message The message text of the Throwable.
161    */
162   static void raiseThrowable(const char *name, const char *message);
163 
164   /**
165    * Creates and throws a JNIError.
166    *
167    * @param message The message text of the JNIError.
168    */
throwError(const char * message)169   static void throwError(const char *message)
170     {
171       raiseThrowable(JAVAHL_CLASS("/JNIError"), message);
172     }
173 
174   static apr_pool_t *getPool();
175   static bool JNIInit(JNIEnv *env);
176   static bool initializeJNIRuntime();
177   enum { noLog, errorLog, exceptionLog, entryLog } LogLevel;
178 
179   /**
180    * Mutex that secures the global configuration object.
181    */
182   static JNIMutex *g_configMutex;
183 
184  private:
185   friend bool initialize_jni_util(JNIEnv *env);
186   static bool JNIGlobalInit(JNIEnv *env);
187 
188   static jthrowable wrappedCreateClientException(svn_error_t *err,
189                                                  jthrowable jcause);
190   static void putErrorsInTrace(svn_error_t *err,
191                                std::vector<jobject> &stackTrace);
192 
193   /**
194    * The log level of this module.
195    */
196   static int g_logLevel;
197 
198   /**
199    * Global master pool.  All other pool are subpools of this pool.
200    */
201   static apr_pool_t *g_pool;
202 
203   /**
204    * List of objects finalized, where the C++ peer has not yet be
205    * deleted.
206    */
207   static std::list<SVNBase*> g_finalizedObjects;
208 
209   /**
210    * Mutex to secure the g_finalizedObjects list.
211    */
212   static JNIMutex *g_finalizedObjectsMutex;
213 
214   /**
215    * Mutex to secure the access to the log file.
216    */
217   static JNIMutex *g_logMutex;
218 
219   /**
220    * Flag, that an exception occurred during our initialization.
221    */
222   static bool g_initException;
223 
224   /**
225    * The stream to write log messages to.
226    */
227   static std::ofstream g_logStream;
228 };
229 
230 /**
231  * A statement macro used for checking NULL pointers, in the style of
232  * SVN_ERR().
233  *
234  * Evaluate @a expr.  If it equals NULL, throw an NullPointerException with
235  * the value @a str, and return the @a ret_val.  Otherwise, continue.
236  *
237  * Note that if the enclosing function returns <tt>void</tt>, @a ret_val may
238  * be blank.
239  */
240 
241 #define SVN_JNI_NULL_PTR_EX(expr, str, ret_val) \
242   if ((expr) == NULL) {                         \
243     JNIUtil::throwNullPointerException(str);    \
244     return ret_val;                             \
245   }
246 
247 /**
248  * A statement macro used for checking for errors, in the style of
249  * SVN_ERR().
250  *
251  * Evalute @a expr.  If it yields an error, handle the JNI error, and
252  * return @a ret_val.  Otherwise, continue.
253  *
254  * Note that if the enclosing function returns <tt>void</tt>, @a ret_val may
255  * be blank.
256  */
257 
258 #define SVN_JNI_ERR(expr, ret_val)                      \
259   do {                                                  \
260     svn_error_t *svn_jni_err__temp = (expr);            \
261     if (svn_jni_err__temp != SVN_NO_ERROR) {            \
262       JNIUtil::handleSVNError(svn_jni_err__temp);       \
263       return ret_val;                                   \
264     }                                                   \
265   } while (0)
266 
267 /**
268  * The initial capacity of a create local reference frame.
269  */
270 #define LOCAL_FRAME_SIZE            16
271 
272 /**
273  * A statement macro use to pop the reference frame and return NULL
274  */
275 #define POP_AND_RETURN(ret_val)         \
276   do                                    \
277     {                                   \
278       env->PopLocalFrame(NULL);         \
279       return ret_val;                   \
280     }                                   \
281   while (0)
282 
283 /**
284  * A statement macro use to pop the reference frame and return
285  */
286 #define POP_AND_RETURN_NOTHING()        \
287   do                                    \
288     {                                   \
289       env->PopLocalFrame(NULL);         \
290       return;                           \
291     }                                   \
292   while (0)
293 
294 #define POP_AND_RETURN_EXCEPTION_AS_SVNERROR()                            \
295   do                                                                      \
296     {                                                                     \
297       svn_error_t *svn__err_for_exception = JNIUtil::wrapJavaException(); \
298                                                                           \
299       env->PopLocalFrame(NULL);                                           \
300       return svn__err_for_exception;                                      \
301     }                                                                     \
302   while (0)
303 
304 
305 /**
306  * A useful macro.
307  */
308 #define POP_AND_RETURN_NULL             POP_AND_RETURN(NULL)
309 
310 #define CPPADDR_NULL_PTR(expr, ret_val)                 \
311   do {                                                  \
312     if ((expr) == NULL) {                               \
313       JNIUtil::throwError(_("bad C++ this"));           \
314       return ret_val;                                   \
315     }                                                   \
316   } while (0)
317 
318 #define SVN_JNI_CATCH(statement, errorcode)             \
319   do {                                                  \
320     do { statement; } while(0);                         \
321     SVN_ERR(JNIUtil::checkJavaException((errorcode)));  \
322   } while(0)
323 
324 #define SVN_JNI_CATCH_VOID(statement)                   \
325   do {                                                  \
326     do { statement; } while(0);                         \
327     if (JNIUtil::getEnv()->ExceptionCheck()) {          \
328       JNIUtil::getEnv()->ExceptionClear();              \
329       return;                                           \
330     }                                                   \
331   } while(0)
332 
333 /**
334  * If there's an exception pending, temporarily stash it away, then
335  * re-throw again in destructor. The goal is to allow some Java calls
336  * to be made despite a pending exception. For example, doing some
337  * necessary cleanup.
338  */
339 class StashException
340 {
341  public:
342   /*
343    * Works like stashException().
344    */
345    StashException(JNIEnv* env);
346 
347   /**
348    * If there's an exception stashed, re-throws it.
349    */
350   ~StashException();
351 
352   /**
353    * Check for a pending exception.
354    * If present, stash it away until this class's destructor.
355    * If another exception is already stashed, forget the _new_ one. The
356    * reason behind it is that usually the first exception is the most
357    * informative.
358    */
359   void stashException();
360 
361  private:
362    JNIEnv* m_env;
363    jthrowable m_stashed;
364 };
365 
366 #endif  // JNIUTIL_H
367