You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2013/06/25 07:19:51 UTC

svn commit: r1496353 - in /subversion/trunk/subversion/bindings/javahl: native/JNIUtil.cpp native/JNIUtil.h src/org/apache/subversion/javahl/ClientException.java

Author: brane
Date: Tue Jun 25 05:19:50 2013
New Revision: 1496353

URL: http://svn.apache.org/r1496353
Log:
Extend the JavaHL native exception to provide the whole stack of error
messages that were generated by the native libraries.

[in subversion/bindings/javahl/native]
* JNIUtil.h (message_stack_item, error_message_stack_t): New types.
  (JNIUtil::assembleErrorMessage): Take an optional mesage_stack parameter.
* JNIUtil.cpp (construct_Jmessage_stack): New helper; converts an
   error_message_stack_t to a Java list.
  (JNIUtil::handleSVNError): Pass a message stack to assembleErrorMessage
   and send the converted Java list to the ClientException constructor.
  (JNIUtil::assembleErrorMessage): Optionally build the message stack.

[in subversion/bindings/javahl/src/org/apache/subversion/javahl]
* ClientException.java (ClientException): Update serialVersionUID.
  (ClientException.ErrorMessage): New nested class. This is the Java
   equivalent of the message_stack_item from JNIUtil.h.
  (ClientException::ClientException): New constructor accepts a message stack.
  (ClientException::getAllMessages): Return the current message stack.

Modified:
    subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp
    subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp?rev=1496353&r1=1496352&r2=1496353&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIUtil.cpp Tue Jun 25 05:19:50 2013
@@ -418,10 +418,66 @@ JNIUtil::putErrorsInTrace(svn_error_t *e
   env->DeleteLocalRef(jfileName);
 }
 
+namespace {
+jobject construct_Jmessage_stack(
+    const JNIUtil::error_message_stack_t& message_stack)
+{
+  JNIEnv *env = JNIUtil::getEnv();
+  env->PushLocalFrame(LOCAL_FRAME_SIZE);
+  if (JNIUtil::isJavaExceptionThrown())
+    return NULL;
+
+  jclass list_clazz = env->FindClass("java/util/ArrayList");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  jmethodID mid = env->GetMethodID(list_clazz, "<init>", "(I)V");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  jmethodID add_mid = env->GetMethodID(list_clazz, "add",
+                                       "(Ljava/lang/Object;)Z");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  jobject jlist = env->NewObject(list_clazz, mid, jint(message_stack.size()));
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+
+  jclass clazz = env->FindClass(JAVA_PACKAGE"/ClientException$ErrorMessage");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+  mid = env->GetMethodID(clazz, "<init>",
+                         "(ILjava/lang/String;Z)V");
+  if (JNIUtil::isJavaExceptionThrown())
+    POP_AND_RETURN_NULL;
+
+  for (JNIUtil::error_message_stack_t::const_iterator
+         it = message_stack.begin();
+       it != message_stack.end(); ++it)
+    {
+      jobject jmessage = JNIUtil::makeJString(it->m_message.c_str());
+      if (JNIUtil::isJavaExceptionThrown())
+        POP_AND_RETURN_NULL;
+      jobject jitem = env->NewObject(clazz, mid,
+                                     jint(it->m_code), jmessage,
+                                     jboolean(it->m_generic));
+      if (JNIUtil::isJavaExceptionThrown())
+        POP_AND_RETURN_NULL;
+      env->CallBooleanMethod(jlist, add_mid, jitem);
+      if (JNIUtil::isJavaExceptionThrown())
+        POP_AND_RETURN_NULL;
+
+      env->DeleteLocalRef(jmessage);
+      env->DeleteLocalRef(jitem);
+    }
+  return env->PopLocalFrame(jlist);
+}
+} // anonymous namespace
+
 void JNIUtil::handleSVNError(svn_error_t *err)
 {
   std::string msg;
-  assembleErrorMessage(svn_error_purge_tracing(err), 0, APR_SUCCESS, msg);
+  error_message_stack_t message_stack;
+  assembleErrorMessage(svn_error_purge_tracing(err),
+                       0, APR_SUCCESS, msg, &message_stack);
   const char *source = NULL;
 #ifdef SVN_DEBUG
 #ifndef SVN_ERR__TRACING
@@ -472,12 +528,18 @@ void JNIUtil::handleSVNError(svn_error_t
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
 
+  jobject jmessageStack = construct_Jmessage_stack(message_stack);
+  if (isJavaExceptionThrown())
+    POP_AND_RETURN_NOTHING();
+
   jmethodID mid = env->GetMethodID(clazz, "<init>",
-                                   "(Ljava/lang/String;Ljava/lang/String;I)V");
+                                   "(Ljava/lang/String;"
+                                   "Ljava/lang/String;I"
+                                   "Ljava/util/List;)V");
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
   jobject nativeException = env->NewObject(clazz, mid, jmessage, jsource,
-                                           static_cast<jint>(err->apr_err));
+                                           jint(err->apr_err), jmessageStack);
   if (isJavaExceptionThrown())
     POP_AND_RETURN_NOTHING();
 
@@ -875,13 +937,15 @@ jbyteArray JNIUtil::makeJByteArray(const
  * @param parent_apr_err    the apr of the previous level, used for formating
  * @param buffer            the buffer where the formated error message will
  *                          be stored
+ * @param message_stack     an array of error codes and messages
  */
 void JNIUtil::assembleErrorMessage(svn_error_t *err, int depth,
                                    apr_status_t parent_apr_err,
-                                   std::string &buffer)
+                                   std::string &buffer,
+                                   error_message_stack_t* message_stack)
 {
   // buffer for a single error message
-  char errbuf[256];
+  char errbuf[1024];
 
   /* Pretty-print the error */
   /* Note: we can also log errors here someday. */
@@ -890,35 +954,43 @@ void JNIUtil::assembleErrorMessage(svn_e
    * the same as before. */
   if (depth == 0 || err->apr_err != parent_apr_err)
     {
+      const char *message;
       /* Is this a Subversion-specific error code? */
       if ((err->apr_err > APR_OS_START_USEERR)
           && (err->apr_err <= APR_OS_START_CANONERR))
-        buffer.append(svn_strerror(err->apr_err, errbuf, sizeof(errbuf)));
+        message = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
       /* Otherwise, this must be an APR error code. */
       else
         {
           /* Messages coming from apr_strerror are in the native
              encoding, it's a good idea to convert them to UTF-8. */
-          const char* utf8_message;
           apr_strerror(err->apr_err, errbuf, sizeof(errbuf));
-          svn_error_t* utf8_err = svn_utf_cstring_to_utf8(
-              &utf8_message, errbuf, err->pool);
+          svn_error_t* utf8_err =
+            svn_utf_cstring_to_utf8(&message, errbuf, err->pool);
           if (utf8_err)
             {
               /* Use fuzzy transliteration instead. */
               svn_error_clear(utf8_err);
-              utf8_message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
+              message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
             }
-          buffer.append(utf8_message);
         }
+
+      if (message_stack)
+        message_stack->push_back(
+            message_stack_item(err->apr_err, message, true));
+      buffer.append(message);
       buffer.append("\n");
     }
   if (err->message)
-    buffer.append(_("svn: ")).append(err->message).append("\n");
+    {
+      if (message_stack)
+        message_stack->push_back(
+            message_stack_item(err->apr_err, err->message));
+      buffer.append(_("svn: ")).append(err->message).append("\n");
+    }
 
   if (err->child)
     assembleErrorMessage(err->child, depth + 1, err->apr_err, buffer);
-
 }
 
 /**

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h?rev=1496353&r1=1496352&r2=1496353&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIUtil.h Tue Jun 25 05:19:50 2013
@@ -37,6 +37,7 @@ class SVNBase;
 #include <fstream>
 #include <apr_time.h>
 #include <string>
+#include <vector>
 struct svn_error_t;
 
 #define JAVA_PACKAGE "org/apache/subversion/javahl"
@@ -141,10 +142,26 @@ class JNIUtil
   enum { formatBufferSize = 2048 };
   enum { noLog, errorLog, exceptionLog, entryLog } LogLevel;
 
+  struct message_stack_item
+  {
+    apr_status_t m_code;
+    std::string m_message;
+    bool m_generic;
+
+    message_stack_item(apr_status_t code, const char* message,
+                       bool generic = false)
+      : m_code(code),
+        m_message(message),
+        m_generic(generic)
+      {}
+  };
+  typedef std::vector<message_stack_item> error_message_stack_t;
+
  private:
   static void assembleErrorMessage(svn_error_t *err, int depth,
                                    apr_status_t parent_apr_err,
-                                   std::string &buffer);
+                                   std::string &buffer,
+                                   error_message_stack_t* message_stack = NULL);
   static void putErrorsInTrace(svn_error_t *err,
                                std::vector<jobject> &stackTrace);
   /**

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java?rev=1496353&r1=1496352&r2=1496353&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ClientException.java Tue Jun 25 05:19:50 2013
@@ -22,6 +22,7 @@
  */
 
 package org.apache.subversion.javahl;
+import java.util.List;
 
 /**
  * This exception is thrown whenever something goes wrong in the
@@ -36,7 +37,54 @@ public class ClientException extends Nat
     // http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf
     // http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html#6678
     // http://java.sun.com/javase/6/docs/platform/serialization/spec/version.html#6678
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L;
+
+    /**
+     * Describes a single error message in a stack of messages
+     * associated with this exception.
+     * @since 1.9
+     */
+    public static final class ErrorMessage
+    {
+        ErrorMessage(int code, String message, boolean generic)
+        {
+            this.code = code;
+            this.message = message;
+            this.generic = generic;
+        }
+
+        /** @return The APR error code associated with the message. */
+        public final int getCode() { return code; }
+
+        /** @return The error message text. */
+        public final String getMessage() { return message; }
+
+        /** @return A flag indicating whether this is a generic
+            message for the APR error code, or a more specific message
+            generated by the native libraries. */
+        public final boolean isGeneric() { return generic; }
+
+        private final int code;
+        private final String message;
+        private final boolean generic;
+    };
+
+    /**
+     * This constructor is only used by the native library.
+     *
+     * @param message A description of the problem.
+     * @param source The error's source.
+     * @param aprError Any associated APR error code for a wrapped
+     *        <code>svn_error_t</code>.
+     * @param messageStack The whole stack of error messages
+     * @since 1.9
+     */
+    ClientException(String message, String source, int aprError,
+                    List<ErrorMessage> messageStack)
+    {
+        super(message, source, aprError);
+        this.messageStack = messageStack;
+    }
 
     /**
      * This constructor is only used by the native library.
@@ -48,7 +96,12 @@ public class ClientException extends Nat
      */
     ClientException(String message, String source, int aprError)
     {
-        super(message, source, aprError);
+        this(message, source, aprError, null);
+    }
+
+    public List<ErrorMessage> getAllMessages()
+    {
+        return messageStack;
     }
 
     /**
@@ -68,4 +121,6 @@ public class ClientException extends Nat
             return new ClientException(t.getMessage(), null, -1);
         }
     }
+
+    private final List<ErrorMessage> messageStack;
 }