/** * @copyright * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * @endcopyright * * @file JNIUtil.h * @brief Interface of the class JNIUtil */ #ifndef JNIUTIL_H #define JNIUTIL_H #include #include #include "Pool.h" struct apr_pool_t; class JNIMutex; class SVNBase; #include #include #include #include #include struct svn_error_t; struct svn_string_t; #include "svn_error.h" /** * The name of the package in which the JavaHL classes are defined. */ #define JAVAHL_PACKAGE "org/apache/subversion/javahl" /** * Construct a JavaHL class name for JNIEnv::FindClass. */ #define JAVAHL_CLASS(name) JAVAHL_PACKAGE name /** * Construct a JavaHL class parameter name for JNIEnv::GetMethodID & co. */ #define JAVAHL_ARG(name) "L" JAVAHL_PACKAGE name /** * Class to hold a number of JNI related utility methods. No Objects * of this class are ever created. */ class JNIUtil { public: static svn_error_t *preprocessPath(const char *&path, apr_pool_t *pool); /** * Throw the Java NativeException instance named by * exceptionClassName. A NativeException sub-class must supply a * 3-arg constructor identical to that of NativeException. @a * source is any file name and line number information. */ static void throwNativeException(const char *exceptionClassName, const char *msg, const char *source = NULL, int aprErr = -1); static void throwNullPointerException(const char *message); static jbyteArray makeJByteArray(const void *data, int length); static jbyteArray makeJByteArray(const svn_string_t *str); static jobject createDate(apr_time_t time); static apr_time_t getDate(jobject jdate); static void logMessage(const char *message); static int getLogLevel(); static void initLogFile(int level, jstring path); static jstring makeJString(const char *txt); static JNIEnv *getEnv(); /** * @return Whether any Throwable has been raised. */ static bool isExceptionThrown() { return isJavaExceptionThrown(); } static bool isJavaExceptionThrown() { return getEnv()->ExceptionCheck(); } static svn_error_t *wrapJavaException(); static jthrowable unwrapJavaException(const svn_error_t *err); static void handleAPRError(int error, const char *op); /** * Put @a object in the list of finalized objects queued up to be * deleted (by another thread) during the next operation. * * @param object The C++ peer of the finalized (Java) object. * @since 1.4.0 */ static void enqueueForDeletion(SVNBase *object); /** * @deprecated Use the more appropriately named * enqueueForDeletion() instead. */ static void putFinalizedClient(SVNBase *cl); /** * Convert any exception that may have been thrown into a textual * representation. Return @c NULL if no exception has * occurred. Useful for converting Java @c Exceptions into @c * svn_error_t's. */ static const char *thrownExceptionToCString(SVN::Pool &in_pool); /** * Check if a Java exception was thrown and convert it to a * Subversion error, using @a errorcode as the generic error code. */ static svn_error_t* checkJavaException(apr_status_t errorcode); /** * Create a Java exception corresponding to err, and run * svn_error_clear() on err. */ static jthrowable createClientException(svn_error_t *err, jthrowable jcause = NULL); /** * Throw a Java exception corresponding to err, and run * svn_error_clear() on err. */ static void handleSVNError(svn_error_t *err, jthrowable jcause = NULL); static std::string makeSVNErrorMessage(svn_error_t *err, jstring *jerror_message, jobject *jmessage_stack); /** * Create and throw a java.lang.Throwable instance. * * @param name The class name (in path form, with slashes in lieu * of dots) of the Throwable to create and raise. * @param message The message text of the Throwable. */ static void raiseThrowable(const char *name, const char *message); /** * Creates and throws a JNIError. * * @param message The message text of the JNIError. */ static void throwError(const char *message) { raiseThrowable(JAVAHL_CLASS("/JNIError"), message); } static apr_pool_t *getPool(); static bool JNIInit(JNIEnv *env); static bool initializeJNIRuntime(); enum { noLog, errorLog, exceptionLog, entryLog } LogLevel; /** * Mutex that secures the global configuration object. */ static JNIMutex *g_configMutex; private: friend bool initialize_jni_util(JNIEnv *env); static bool JNIGlobalInit(JNIEnv *env); static jthrowable wrappedCreateClientException(svn_error_t *err, jthrowable jcause); static void putErrorsInTrace(svn_error_t *err, std::vector &stackTrace); /** * The log level of this module. */ static int g_logLevel; /** * Global master pool. All other pool are subpools of this pool. */ static apr_pool_t *g_pool; /** * List of objects finalized, where the C++ peer has not yet be * deleted. */ static std::list g_finalizedObjects; /** * Mutex to secure the g_finalizedObjects list. */ static JNIMutex *g_finalizedObjectsMutex; /** * Mutex to secure the access to the log file. */ static JNIMutex *g_logMutex; /** * Flag, that an exception occurred during our initialization. */ static bool g_initException; /** * The stream to write log messages to. */ static std::ofstream g_logStream; }; /** * A statement macro used for checking NULL pointers, in the style of * SVN_ERR(). * * Evaluate @a expr. If it equals NULL, throw an NullPointerException with * the value @a str, and return the @a ret_val. Otherwise, continue. * * Note that if the enclosing function returns void, @a ret_val may * be blank. */ #define SVN_JNI_NULL_PTR_EX(expr, str, ret_val) \ if ((expr) == NULL) { \ JNIUtil::throwNullPointerException(str); \ return ret_val; \ } /** * A statement macro used for checking for errors, in the style of * SVN_ERR(). * * Evalute @a expr. If it yields an error, handle the JNI error, and * return @a ret_val. Otherwise, continue. * * Note that if the enclosing function returns void, @a ret_val may * be blank. */ #define SVN_JNI_ERR(expr, ret_val) \ do { \ svn_error_t *svn_jni_err__temp = (expr); \ if (svn_jni_err__temp != SVN_NO_ERROR) { \ JNIUtil::handleSVNError(svn_jni_err__temp); \ return ret_val; \ } \ } while (0) /** * The initial capacity of a create local reference frame. */ #define LOCAL_FRAME_SIZE 16 /** * A statement macro use to pop the reference frame and return NULL */ #define POP_AND_RETURN(ret_val) \ do \ { \ env->PopLocalFrame(NULL); \ return ret_val; \ } \ while (0) /** * A statement macro use to pop the reference frame and return */ #define POP_AND_RETURN_NOTHING() \ do \ { \ env->PopLocalFrame(NULL); \ return; \ } \ while (0) #define POP_AND_RETURN_EXCEPTION_AS_SVNERROR() \ do \ { \ svn_error_t *svn__err_for_exception = JNIUtil::wrapJavaException(); \ \ env->PopLocalFrame(NULL); \ return svn__err_for_exception; \ } \ while (0) /** * A useful macro. */ #define POP_AND_RETURN_NULL POP_AND_RETURN(NULL) #define CPPADDR_NULL_PTR(expr, ret_val) \ do { \ if ((expr) == NULL) { \ JNIUtil::throwError(_("bad C++ this")); \ return ret_val; \ } \ } while (0) #define SVN_JNI_CATCH(statement, errorcode) \ do { \ do { statement; } while(0); \ SVN_ERR(JNIUtil::checkJavaException((errorcode))); \ } while(0) #define SVN_JNI_CATCH_VOID(statement) \ do { \ do { statement; } while(0); \ if (JNIUtil::getEnv()->ExceptionCheck()) { \ JNIUtil::getEnv()->ExceptionClear(); \ return; \ } \ } while(0) /** * If there's an exception pending, temporarily stash it away, then * re-throw again in destructor. The goal is to allow some Java calls * to be made despite a pending exception. For example, doing some * necessary cleanup. */ class StashException { public: /* * Works like stashException(). */ StashException(JNIEnv* env); /** * If there's an exception stashed, re-throws it. */ ~StashException(); /** * Check for a pending exception. * If present, stash it away until this class's destructor. * If another exception is already stashed, forget the _new_ one. The * reason behind it is that usually the first exception is the most * informative. */ void stashException(); private: JNIEnv* m_env; jthrowable m_stashed; }; #endif // JNIUTIL_H