Alien-SVN

 view release on metacpan or  search on metacpan

src/subversion/subversion/bindings/javahl/native/JNIUtil.cpp  view on Meta::CPAN

  // Build all mutexes.
  g_finalizedObjectsMutex = new JNIMutex(g_pool);
  if (isExceptionThrown())
    return false;

  g_logMutex = new JNIMutex(g_pool);
  if (isExceptionThrown())
    return false;

  // initialized the thread local storage
  if (!JNIThreadData::initThreadData())
    return false;

  setEnv(env);
  if (isExceptionThrown())
    return false;

  g_initEnv = NULL;
  g_inInit = false;
  return true;
}

/**
 * Returns the global (not request specific) pool.
 * @return global pool
 */
apr_pool_t *JNIUtil::getPool()
{
  return g_pool;
}

void JNIUtil::raiseThrowable(const char *name, const char *message)
{
  if (getLogLevel() >= errorLog)
    {
      JNICriticalSection cs(*g_logMutex);
      g_logStream << "Throwable raised <" << message << ">" << std::endl;
    }

  JNIEnv *env = getEnv();
  jclass clazz = env->FindClass(name);
  if (isJavaExceptionThrown())
    return;

  env->ThrowNew(clazz, message);
  setExceptionThrown();
  env->DeleteLocalRef(clazz);
}

jstring JNIUtil::makeSVNErrorMessage(svn_error_t *err)
{
  if (err == NULL)
    return NULL;
  std::string buffer;
  assembleErrorMessage(err, 0, APR_SUCCESS, buffer);
  jstring jmessage = makeJString(buffer.c_str());
  return jmessage;
}

void
JNIUtil::throwNativeException(const char *className, const char *msg,
                              const char *source, int aprErr)
{
  JNIEnv *env = getEnv();
  jclass clazz = env->FindClass(className);

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

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

  jstring jmessage = makeJString(msg);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();
  jstring jsource = makeJString(source);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();

  jmethodID mid = env->GetMethodID(clazz, "<init>",
                                   "(Ljava/lang/String;Ljava/lang/String;I)V");
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();
  jobject nativeException = env->NewObject(clazz, mid, jmessage, jsource,
                                           static_cast<jint>(aprErr));
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();

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

void
JNIUtil::putErrorsInTrace(svn_error_t *err,
                          std::vector<jobject> &stackTrace)
{
  if (!err)
    return;

  JNIEnv *env = getEnv();

  // First, put all our child errors in the stack trace
  putErrorsInTrace(err->child, stackTrace);

  // Now, put our own error in the stack trace
  jclass stClazz = env->FindClass("java/lang/StackTraceElement");
  if (isJavaExceptionThrown())
    return;

  static jmethodID ctor_mid = 0;
  if (ctor_mid == 0)
    {
      ctor_mid = env->GetMethodID(stClazz, "<init>",
                                  "(Ljava/lang/String;Ljava/lang/String;"
                                  "Ljava/lang/String;I)V");
      if (isJavaExceptionThrown())
        return;
    }

  jstring jdeclClass = makeJString("native");
  if (isJavaExceptionThrown())
    return;

  char *tmp_path;
  char *path = svn_dirent_dirname(err->file, err->pool);
  while ((tmp_path = strchr(path, '/')))
    *tmp_path = '.';

  jstring jmethodName = makeJString(path);
  if (isJavaExceptionThrown())
    return;

  jstring jfileName = makeJString(svn_dirent_basename(err->file, err->pool));
  if (isJavaExceptionThrown())
    return;

  jobject jelement = env->NewObject(stClazz, ctor_mid, jdeclClass, jmethodName,
                                    jfileName, (jint) err->line);

  stackTrace.push_back(jelement);

  env->DeleteLocalRef(stClazz);
  env->DeleteLocalRef(jdeclClass);
  env->DeleteLocalRef(jmethodName);
  env->DeleteLocalRef(jfileName);
}

void JNIUtil::wrappedHandleSVNError(svn_error_t *err)
{
  std::string msg;
  assembleErrorMessage(svn_error_purge_tracing(err), 0, APR_SUCCESS, msg);
  const char *source = NULL;
#ifdef SVN_DEBUG
#ifndef SVN_ERR__TRACING
  if (err->file)
    {
      std::ostringstream buf;
      buf << err->file;
      if (err->line > 0)
        buf << ':' << err->line;
      source = buf.str().c_str();
    }
#endif
#endif

  // 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;

  jclass clazz = env->FindClass(JAVA_PACKAGE "/ClientException");
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();

  if (getLogLevel() >= exceptionLog)
    {
      JNICriticalSection cs(*g_logMutex);
      g_logStream << "Subversion JavaHL exception thrown, message:<";
      g_logStream << msg << ">";
      if (source)
        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_NOTHING();

  jstring jmessage = makeJString(msg.c_str());
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();
  jstring jsource = makeJString(source);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();

  jmethodID mid = env->GetMethodID(clazz, "<init>",
                                   "(Ljava/lang/String;Ljava/lang/String;I)V");
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();
  jobject nativeException = env->NewObject(clazz, mid, jmessage, jsource,
                                           static_cast<jint>(err->apr_err));
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();

#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_NOTHING();
    }
  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_NOTHING();

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

  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_NOTHING();
    }
  env->CallVoidMethod(nativeException, mid_sst, jStackTrace);
  if (isJavaExceptionThrown())
    POP_AND_RETURN_NOTHING();
#endif

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

void JNIUtil::handleSVNError(svn_error_t *err)
{
  try {
    wrappedHandleSVNError(err);
  } catch (...) {
    svn_error_clear(err);
    throw;
  }
  svn_error_clear(err);
}

void JNIUtil::putFinalizedClient(SVNBase *object)
{
  enqueueForDeletion(object);
}

void JNIUtil::enqueueForDeletion(SVNBase *object)
{
  JNICriticalSection cs(*g_finalizedObjectsMutex);
  if (!isExceptionThrown())
    g_finalizedObjects.push_back(object);
}

/**
 * Handle an apr error (those are not expected) by throwing an error.
 * @param error the apr error number
 * @param op the apr function returning the error
 */
void JNIUtil::handleAPRError(int error, const char *op)
{
  char *buffer = getFormatBuffer();
  if (buffer == NULL)
    return;

  apr_snprintf(buffer, formatBufferSize,
               _("an error occurred in function %s with return value %d"),
               op, error);

  throwError(buffer);
}

/**
 * Return if an exception has been detected.
 * @return a exception has been detected
 */
bool JNIUtil::isExceptionThrown()
{
  // During init -> look in the global member.
  if (g_inInit)
    return g_initException;

  // Look in the thread local storage.
  JNIThreadData *data = JNIThreadData::getThreadData();
  return data == NULL || data->m_exceptionThrown;
}

/**
 * Store the JNI environment for this request in the thread local
 * storage.
 * @param env   the JNI environment
 */
void JNIUtil::setEnv(JNIEnv *env)
{
  JNIThreadData::pushNewThreadData();
  JNIThreadData *data = JNIThreadData::getThreadData();
  data->m_env = env;
  data->m_exceptionThrown = false;
}

/**
 * Return the JNI environment to use
 * @return the JNI environment
 */
JNIEnv *JNIUtil::getEnv()
{
  // During init -> look into the global variable.
  if (g_inInit)
    return g_initEnv;

  // Look in the thread local storage.
  JNIThreadData *data = JNIThreadData::getThreadData();
  return data->m_env;
}

/**
 * Check if a Java exception has been thrown.
 * @return is a Java exception has been thrown
 */
bool JNIUtil::isJavaExceptionThrown()
{
  JNIEnv *env = getEnv();
  if (env->ExceptionCheck())
    {
      // Retrieving the exception removes it so we rethrow it here.
      jthrowable exp = env->ExceptionOccurred();
      env->ExceptionDescribe();
      env->Throw(exp);
      env->DeleteLocalRef(exp);
      setExceptionThrown();
      return true;
    }
  return false;
}

const char *
JNIUtil::thrownExceptionToCString(SVN::Pool &in_pool)
{
  const char *msg = NULL;
  JNIEnv *env = getEnv();
  apr_pool_t *pool = in_pool.getPool();
  if (env->ExceptionCheck())
    {
      jthrowable t = env->ExceptionOccurred();
      jclass cls = env->GetObjectClass(t);

      jstring jclass_name;
      {
        jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;");
        jobject clsobj = env->CallObjectMethod(t, mid);
        jclass basecls = env->GetObjectClass(clsobj);
        mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;");
        jclass_name = (jstring) env->CallObjectMethod(clsobj, mid);
      }

      jstring jmessage;
      {
        jmethodID mid = env->GetMethodID(cls, "getMessage",
                                         "()Ljava/lang/String;");
        jmessage = (jstring) env->CallObjectMethod(t, mid);
      }

      JNIStringHolder class_name(jclass_name);
      if (jmessage)
        {
          JNIStringHolder message(jmessage);
          msg = apr_pstrcat(pool,
                            static_cast<const char*>(class_name), ": ",
                            static_cast<const char*>(message), NULL);
        }
      else
        msg = class_name.pstrdup(pool);
      // ### Conditionally add t.printStackTrace() to msg?
    }
  return msg;
}

/**
 * Create a Java string from a native UTF-8 string.
 * @param txt   native UTF-8 string
 * @return the Java string. It is a local reference, which should be deleted
 *         as soon a possible
 */
jstring JNIUtil::makeJString(const char *txt)
{
  if (txt == NULL)
    // A NULL pointer is equates to a null java.lang.String.
    return NULL;

  JNIEnv *env = getEnv();
  return env->NewStringUTF(txt);
}

void
JNIUtil::setExceptionThrown(bool flag)
{
  if (g_inInit)
    {
      // During global initialization, store any errors that occur
      // in a global variable (since thread-local storage may not
      // yet be available).
      g_initException = flag;
    }
  else

src/subversion/subversion/bindings/javahl/native/JNIUtil.cpp  view on Meta::CPAN

 * Build the error message from the svn error into buffer.  This
 * method calls itselft recursively for all the chained errors
 *
 * @param err               the subversion error
 * @param depth             the depth of the call, used for formating
 * @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
 */
void JNIUtil::assembleErrorMessage(svn_error_t *err, int depth,
                                   apr_status_t parent_apr_err,
                                   std::string &buffer)
{
  // buffer for a single error message
  char errbuf[256];

  /* Pretty-print the error */
  /* Note: we can also log errors here someday. */

  /* When we're recursing, don't repeat the top-level message if its
   * the same as before. */
  if (depth == 0 || err->apr_err != parent_apr_err)
    {
      /* 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)));
      /* 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);
          if (utf8_err)
            {
              /* Use fuzzy transliteration instead. */
              svn_error_clear(utf8_err);
              utf8_message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool);
            }
          buffer.append(utf8_message);
        }
      buffer.append("\n");
    }
  if (err->message)
    buffer.append(_("svn: ")).append(err->message).append("\n");

  if (err->child)
    assembleErrorMessage(err->child, depth + 1, err->apr_err, buffer);

}

/**
 * Throw a Java NullPointerException.  Used when input parameters
 * which should not be null are that.
 *
 * @param message   the name of the parameter that is null
 */
void JNIUtil::throwNullPointerException(const char *message)
{
  if (getLogLevel() >= errorLog)
    logMessage("NullPointerException thrown");

  JNIEnv *env = getEnv();
  jclass clazz = env->FindClass("java/lang/NullPointerException");
  if (isJavaExceptionThrown())
    return;

  env->ThrowNew(clazz, message);
  setExceptionThrown();
  env->DeleteLocalRef(clazz);
}

svn_error_t *JNIUtil::preprocessPath(const char *&path, apr_pool_t *pool)
{
  /* URLs and wc-paths get treated differently. */
  if (svn_path_is_url(path))
    {
      /* No need to canonicalize a URL's case or path separators. */

      /* Convert to URI. */
      path = svn_path_uri_from_iri(path, pool);

      /* Auto-escape some ASCII characters. */
      path = svn_path_uri_autoescape(path, pool);

      /* The above doesn't guarantee a valid URI. */
      if (! svn_path_is_uri_safe(path))
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 _("URL '%s' is not properly URI-encoded"),
                                 path);

      /* Verify that no backpaths are present in the URL. */
      if (svn_path_is_backpath_present(path))
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 _("URL '%s' contains a '..' element"),
                                 path);

      /* strip any trailing '/' */
      path = svn_uri_canonicalize(path, pool);
    }
  else  /* not a url, so treat as a path */
    {
      /* Normalize path to subversion internal style */

      /* ### In Subversion < 1.6 this method on Windows actually tried
         to lookup the path on disk to fix possible invalid casings in
         the passed path. (An extremely expensive operation; especially
         on network drives).

         This 'feature'is now removed as it penalizes every correct
         path passed, and also breaks behavior of e.g.
           'svn status .' returns '!' file, because there is only a "File"
             on disk.
            But when you then call 'svn status file', you get '? File'.

         As JavaHL is designed to be platform independent I assume users
         don't want this broken behavior on non round-trippable paths, nor
         the performance penalty.
       */

      path = svn_dirent_internal_style(path, pool);



( run in 1.084 second using v1.01-cache-2.11-cpan-3d66aa2751a )