jthrowable JNIUtil::wrappedCreateClientException()

in subversion/bindings/javahl/native/JNIUtil.cpp [578:715]


jthrowable JNIUtil::wrappedCreateClientException(svn_error_t *err, jthrowable jcause)
{
  jstring jmessage;
  jobject jstack;
  std::string msg = makeSVNErrorMessage(err, &jmessage, &jstack);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  std::string source;
#ifdef SVN_DEBUG
#ifndef SVN_ERR__TRACING
  if (err->file)
    {
      source = err->file;
      if (err->line > 0)
        {
          source += ':';
          source += err->line;
        }
    }
#endif
#endif

  if (!jcause)
    jcause = JNIUtil::unwrapJavaException(err);

  // Much of the following is stolen from throwNativeException().  As much as
  // we'd like to call that function, we need to do some manual stack
  // unrolling, so it isn't feasible.

  JNIEnv *env = getEnv();

  // Create a local frame for our references
  env->PushLocalFrame(LOCAL_FRAME_SIZE);
  if (JNIUtil::isJavaExceptionThrown())
    return NULL;

  jclass clazz = env->FindClass(JAVAHL_CLASS("/ClientException"));
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;

  if (getLogLevel() >= exceptionLog)
    {
      JNICriticalSection cs(*g_logMutex);
      g_logStream << "Subversion JavaHL exception thrown, message:<";
      g_logStream << msg << ">";
      if (!source.empty())
        g_logStream << " source:<" << source << ">";
      if (err->apr_err != -1)
        g_logStream << " apr-err:<" << err->apr_err << ">";
      g_logStream << std::endl;
    }
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;

  jstring jsource = (source.empty() ? NULL : makeJString(source.c_str()));
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;

  jmethodID mid = env->GetMethodID(clazz, "<init>",
                                   "(Ljava/lang/String;"
                                   "Ljava/lang/Throwable;"
                                   "Ljava/lang/String;I"
                                   "Ljava/util/List;)V");
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;
  jobject nativeException = env->NewObject(clazz, mid, jmessage, jcause,
                                           jsource, jint(err->apr_err),
                                           jstack);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;

#ifdef SVN_ERR__TRACING
  // Add all the C error stack trace information to the Java Exception

  // Get the standard stack trace, and vectorize it using the Array class.
  static jmethodID mid_gst = 0;
  if (mid_gst == 0)
    {
      mid_gst = env->GetMethodID(clazz, "getStackTrace",
                                 "()[Ljava/lang/StackTraceElement;");
      if (isJavaExceptionThrown())
        POP_AND_RETURN_NULL;
    }
  Array stackTraceArray((jobjectArray) env->CallObjectMethod(nativeException,
                                                             mid_gst));
  std::vector<jobject> oldStackTrace = stackTraceArray.vector();

  // Build the new stack trace elements from the chained errors.
  std::vector<jobject> newStackTrace;
  putErrorsInTrace(err, newStackTrace);

  // Join the new elements with the old ones
  for (std::vector<jobject>::const_iterator it = oldStackTrace.begin();
            it < oldStackTrace.end(); ++it)
    {
      newStackTrace.push_back(*it);
    }

  jclass stClazz = env->FindClass("java/lang/StackTraceElement");
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;

  const jsize stSize = static_cast<jsize>(newStackTrace.size());
  if (stSize < 0 || stSize != newStackTrace.size())
    {
      env->ThrowNew(env->FindClass("java.lang.ArithmeticException"),
                    "Overflow converting C size_t to JNI jsize");
      POP_AND_RETURN_NULL;
    }
  jobjectArray jStackTrace = env->NewObjectArray(stSize, stClazz, NULL);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;

  int i = 0;
  for (std::vector<jobject>::const_iterator it = newStackTrace.begin();
       it < newStackTrace.end(); ++it)
    {
      env->SetObjectArrayElement(jStackTrace, i, *it);
      ++i;
    }

  // And put the entire trace back into the exception
  static jmethodID mid_sst = 0;
  if (mid_sst == 0)
    {
      mid_sst = env->GetMethodID(clazz, "setStackTrace",
                                 "([Ljava/lang/StackTraceElement;)V");
      if (isJavaExceptionThrown())
        POP_AND_RETURN_NULL;
    }
  env->CallVoidMethod(nativeException, mid_sst, jStackTrace);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NULL;
#endif

  return static_cast<jthrowable>(env->PopLocalFrame(nativeException));
}