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/10/15 06:29:52 UTC

svn commit: r1532182 - in /subversion/trunk: ./ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/ subversion/bindings/javahl/src/org/apa...

Author: brane
Date: Tue Oct 15 04:29:52 2013
New Revision: 1532182

URL: http://svn.apache.org/r1532182
Log:
Expose the tunnel management callbacks in JavaHL.

* build.conf (private-built-includes): Add new generated JavaHL headers.

[in subversion/bindings/javahl/src/org/apache/subversion/javahl]
* callback/TunnelAgent.java: New public interface.
* ISVNClient.java, SVNClient.java (ISVNClient.setTunnelAgent): New method.
* remote/RemoteFactory.java
  (RemoteFactory.RemoteFactory): New parameter tunnelAgent.
  (RemoteFactory.setTunnelAgent): New method.
  (RemoteFactory.tunnelAgent): New member.
  (RemoteFactory.open): New parameter tunnelAgent; all callers updated.
* util/TunnelChannel.java, util/RequestChannel.java, util/ResponseChannel.java:
   New private utility classes; implement byte channes for tunnels.

[in subversion/bindings/javahl/native]
* OperationContext.h
  (OperationContext::m_jtunnelcb): New class member.
  (OperationContext::checkTunnel,
   OperationContext::openTunnel,
   OperationContext::closeTUnnel): New static methods.
  (OperationContext::setTunnelCallback,
   OperationContext::getTunnelCallback): New methods.
* OperationContext.cpp
  (OperationContext::OperationContext): Initialise m_jtunnelcb.
  (OperationContext::~OperationContext): Release m_jtunnelcb.
  (OperationContext::checkTunnel, OperationContext::openTunnel,
   OperationContext::closeTUnnel, OperationContext::setTunnelCallback,
   OperationContext::getTunnelCallback): Implement.
  (TunnelContext): New local helper class.
  (create_Channel, create_RequestChannel,
   create_ResponseChannel): new local helper functions.

* ClientContext.h (ClientContext::setTunnelCallback): Override method.
* ClientContext.cpp (ClientContext::ClientContext): Set tunnel callbacks.
  (ClientContext::setTunnelCallback): Implement.

* RemoteSessionContext.h
  (RemoteSessionContext::RemoteSessionContext): New parameter jtunnelcb.
* RemoteSessionContext.cpp
  (RemoteSessionContext::RemoteSessionContext): Update signature and
   set tunnel callbacks.

* RemoteSession.h (RemoteSession::open, RemoteSession::RemoteSession):
   New parameter jtunnelcb.
* RemoteSession.cpp (RemoteSession::open, RemoteSession::RemoteSession):
   Update signature and implementation.
* SVNClient.cpp (SVNClient::openRemoteSession): Pass the tunnel callback
   from the context to the session factory.

* org_apache_subversion_javahl_SVNClient.cpp
  (Java_org_apache_subversion_javahl_SVNClient_setTunnelAgent):
   Implement native method.
* org_apache_subversion_javahl_remote_RemoteFactory.cpp
  (Java_org_apache_subversion_javahl_remote_RemoteFactory_open)
   Update native method.
* org_apache_subversion_javahl_util_TunnelChannel.cpp: New.
   Implementation of TunnelChannel, RequestChannel and ResponseChannel.

* JNIByteArray.h (JNIByteArray::m_abortOnRelease): New member.
  (JNIByteArray::JNIByteArray): New default parameter abortOnRelease.
* JNIByteArray.cpp (JNIByteArray::JNIByteArray): Update constructor.
  (JNIByteArray::~JNIByteArray): Optionally commit array changes.

[in subversion/bindings/javahl/tests/org/apache/subversion/javahl]
* SVNTests.java (SVNTests.DefaultProgressListener): Make protected.
* BasicTests.java (BasicTests.Tunnel): New helper class.
  (BasicTests.testTunnelAgent): New test case for tunnels.
* SVNRemoteTests.java
  (SVNRemoteTests.testSessionGC) Update factory construction.
  (SVNRemoteTests.testGetSession_ConfigConstructor): Likewise.

Added:
    subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/TunnelAgent.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.java
Modified:
    subversion/trunk/build.conf
    subversion/trunk/subversion/bindings/javahl/native/ClientContext.cpp
    subversion/trunk/subversion/bindings/javahl/native/ClientContext.h
    subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.cpp
    subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.h
    subversion/trunk/subversion/bindings/javahl/native/OperationContext.cpp
    subversion/trunk/subversion/bindings/javahl/native/OperationContext.h
    subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp
    subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h
    subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.cpp
    subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.h
    subversion/trunk/subversion/bindings/javahl/native/SVNClient.cpp
    subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp
    subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java
    subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java
    subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java
    subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
    subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java

Modified: subversion/trunk/build.conf
URL: http://svn.apache.org/viewvc/subversion/trunk/build.conf?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/build.conf (original)
+++ subversion/trunk/build.conf Tue Oct 15 04:29:52 2013
@@ -79,6 +79,9 @@ private-built-includes =
         subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigImpl_Category.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ConfigLib.h
         subversion/bindings/javahl/include/org_apache_subversion_javahl_util_DiffLib.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_util_TunnelChannel.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_util_RequestChannel.h
+        subversion/bindings/javahl/include/org_apache_subversion_javahl_util_ResponseChannel.h
 
 test-scripts =
         subversion/tests/cmdline/*_tests.py

Modified: subversion/trunk/subversion/bindings/javahl/native/ClientContext.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/ClientContext.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/ClientContext.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/ClientContext.cpp Tue Oct 15 04:29:52 2013
@@ -70,12 +70,38 @@ ClientContext::ClientContext(jobject jsv
     m_context->conflict_baton2 = m_jctx;
 
     m_context->client_name = getClientName();
+
+    if (m_jtunnelcb)
+      {
+        m_context->check_tunnel_func = checkTunnel;
+        m_context->open_tunnel_func = openTunnel;
+        m_context->close_tunnel_func = closeTunnel;
+        m_context->tunnel_baton = m_jtunnelcb;
+      }
 }
 
 ClientContext::~ClientContext()
 {
 }
 
+void ClientContext::setTunnelCallback(jobject jtunnelcb)
+{
+  OperationContext::setTunnelCallback(jtunnelcb);
+  if (m_jtunnelcb)
+    {
+      m_context->check_tunnel_func = checkTunnel;
+      m_context->open_tunnel_func = openTunnel;
+      m_context->close_tunnel_func = closeTunnel;
+      m_context->tunnel_baton = m_jtunnelcb;
+    }
+  else
+    {
+      m_context->check_tunnel_func = NULL;
+      m_context->open_tunnel_func = NULL;
+      m_context->close_tunnel_func = NULL;
+      m_context->tunnel_baton = NULL;
+    }
+}
 
 /* Helper function to make sure that we don't keep dangling pointers in ctx.
    Note that this function might be called multiple times if getContext()

Modified: subversion/trunk/subversion/bindings/javahl/native/ClientContext.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/ClientContext.h?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/ClientContext.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/ClientContext.h Tue Oct 15 04:29:52 2013
@@ -64,6 +64,7 @@ class ClientContext : public OperationCo
  public:
   ClientContext(jobject jsvnclient, SVN::Pool &pool);
   virtual ~ClientContext();
+  virtual void setTunnelCallback(jobject jtunnelcb);
 
   svn_client_ctx_t *getContext(CommitMessage *message, SVN::Pool &in_pool);
 };

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.cpp Tue Oct 15 04:29:52 2013
@@ -32,30 +32,24 @@
  * @param flag that the underlying byte array reference should be deleted at
  *        destruction
  */
-JNIByteArray::JNIByteArray(jbyteArray jba, bool deleteByteArray)
-{
-  m_array = jba;
-  m_deleteByteArray = deleteByteArray;
-  if (jba != NULL)
-    {
-      // Get the bytes.
-      JNIEnv *env = JNIUtil::getEnv();
-      m_data = env->GetByteArrayElements(jba, NULL);
-    }
-  else
-    {
-      m_data = NULL;
-    }
-}
+JNIByteArray::JNIByteArray(jbyteArray jba,
+                           bool deleteByteArray,
+                           bool abortOnRelease)
+  : m_array(jba),
+    m_data(!jba ? NULL
+           : JNIUtil::getEnv()->GetByteArrayElements(jba, NULL)),
+    m_deleteByteArray(deleteByteArray),
+    m_abortOnRelease(abortOnRelease)
+{}
 
 JNIByteArray::~JNIByteArray()
 {
   if (m_array != NULL)
     {
       // Release the bytes
-      JNIUtil::getEnv()->ReleaseByteArrayElements(m_array,
-                                                  m_data,
-                                                  JNI_ABORT);
+      JNIUtil::getEnv()->ReleaseByteArrayElements(
+          m_array, m_data,
+          (m_abortOnRelease ? JNI_ABORT: JNI_COMMIT));
       if (m_deleteByteArray)
         // And if needed the byte array.
         JNIUtil::getEnv()->DeleteLocalRef(m_array);

Modified: subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.h?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/JNIByteArray.h Tue Oct 15 04:29:52 2013
@@ -51,11 +51,18 @@ class JNIByteArray
    * at destruction.
    */
   bool m_deleteByteArray;
+
+  /**
+   * False if changes to the array should be committed to the Java VM.
+   */
+  bool m_abortOnRelease;
  public:
   bool isNull() const;
   const signed char *getBytes() const;
   int getLength();
-  JNIByteArray(jbyteArray jba, bool deleteByteArray = false);
+  JNIByteArray(jbyteArray jba,
+               bool deleteByteArray = false,
+               bool abortOnRelease = true);
   ~JNIByteArray();
 };
 

Modified: subversion/trunk/subversion/bindings/javahl/native/OperationContext.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/OperationContext.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/OperationContext.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/OperationContext.cpp Tue Oct 15 04:29:52 2013
@@ -44,7 +44,8 @@ OperationContext::OperationContext(SVN::
     m_cancelOperation(0),
     m_pool(&pool),
     m_jctx(NULL),
-    m_jcfgcb(NULL)
+    m_jcfgcb(NULL),
+    m_jtunnelcb(NULL)
 {}
 
 void
@@ -92,6 +93,8 @@ OperationContext::~OperationContext()
   env->DeleteGlobalRef(m_jctx);
   if (m_jcfgcb)
     env->DeleteGlobalRef(m_jcfgcb);
+  if (m_jtunnelcb)
+    env->DeleteGlobalRef(m_jtunnelcb);
 }
 
 apr_hash_t *
@@ -295,6 +298,26 @@ const Prompter& OperationContext::getPro
   return *m_prompter;
 }
 
+void OperationContext::setTunnelCallback(jobject jtunnelcb)
+{
+  JNIEnv *env = JNIUtil::getEnv();
+  if (jtunnelcb)
+    {
+      jtunnelcb = env->NewGlobalRef(jtunnelcb);
+      if (JNIUtil::isJavaExceptionThrown())
+        return;
+    }
+
+  if (m_jtunnelcb)
+    env->DeleteGlobalRef(m_jtunnelcb);
+  m_jtunnelcb = jtunnelcb;
+}
+
+jobject OperationContext::getTunnelCallback() const
+{
+  return m_jtunnelcb;
+}
+
 void
 OperationContext::cancelOperation()
 {
@@ -440,3 +463,177 @@ OperationContext::notifyConfigLoad()
   env->CallVoidMethod(jcbimpl, dispose_mid);
   env->DeleteLocalRef(jcbimpl);
 }
+
+namespace {
+class TunnelContext
+{
+public:
+  explicit TunnelContext(apr_pool_t *pool)
+    : request_in(NULL),
+      request_out(NULL),
+      response_in(NULL),
+      response_out(NULL)
+    {
+      status = apr_file_pipe_create_ex(&request_in, &request_out,
+                                       APR_FULL_BLOCK, pool);
+      if (!status)
+        status = apr_file_pipe_create_ex(&response_in, &response_out,
+                                         APR_FULL_BLOCK, pool);
+    }
+
+  apr_file_t *request_in;
+  apr_file_t *request_out;
+  apr_file_t *response_in;
+  apr_file_t *response_out;
+  apr_status_t status;
+};
+
+jobject create_Channel(const char *class_name, JNIEnv *env, apr_file_t *fd)
+{
+  jclass cls = env->FindClass(class_name);
+  if (JNIUtil::isJavaExceptionThrown())
+    return NULL;
+  jmethodID ctor = env->GetMethodID(cls, "<init>", "(J)V");
+  if (JNIUtil::isJavaExceptionThrown())
+    return NULL;
+  return env->NewObject(cls, ctor, reinterpret_cast<jlong>(fd));
+}
+
+jobject create_RequestChannel(JNIEnv *env, apr_file_t *fd)
+{
+  return create_Channel(JAVA_PACKAGE"/util/RequestChannel", env, fd);
+}
+jobject create_ResponseChannel(JNIEnv *env, apr_file_t *fd)
+{
+  return create_Channel(JAVA_PACKAGE"/util/ResponseChannel", env, fd);
+}
+} // anonymous namespace
+
+svn_boolean_t
+OperationContext::checkTunnel(void *tunnel_baton, const char *tunnel_name)
+{
+  JNIEnv *env = JNIUtil::getEnv();
+
+  jstring jtunnel_name = JNIUtil::makeJString(tunnel_name);
+  if (JNIUtil::isJavaExceptionThrown())
+    return false;
+
+  static jmethodID mid = 0;
+  if (0 == mid)
+    {
+      jclass cls = env->FindClass(JAVA_PACKAGE"/callback/TunnelAgent");
+      if (JNIUtil::isJavaExceptionThrown())
+        return false;
+      mid = env->GetMethodID(cls, "checkTunnel",
+                             "(Ljava/lang/String;)Z");
+        if (JNIUtil::isJavaExceptionThrown())
+          return false;
+    }
+
+  jobject jtunnelcb = jobject(tunnel_baton);
+  jboolean check = env->CallBooleanMethod(jtunnelcb, mid, jtunnel_name);
+  if (JNIUtil::isJavaExceptionThrown())
+    return false;
+
+  return svn_boolean_t(check);
+}
+
+svn_error_t *
+OperationContext::openTunnel(apr_file_t **request, apr_file_t **response,
+                             void **tunnel_context, void *tunnel_baton,
+                             const char *tunnel_name, const char *user,
+                             const char *hostname, int port,
+                             apr_pool_t *pool)
+{
+  TunnelContext *tc = new TunnelContext(pool);
+  if (tc->status)
+    {
+      delete tc;
+      return svn_error_trace(
+          svn_error_wrap_apr(tc->status, _("Could not open tunnel streams")));
+    }
+
+  *request = tc->request_out;
+  *response = tc->response_in;
+
+  JNIEnv *env = JNIUtil::getEnv();
+
+  jobject jrequest = create_RequestChannel(env, tc->request_in);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  jobject jresponse = create_ResponseChannel(env, tc->response_out);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  jstring jtunnel_name = JNIUtil::makeJString(tunnel_name);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  jstring juser = JNIUtil::makeJString(user);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  jstring jhostname = JNIUtil::makeJString(hostname);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  static jmethodID mid = 0;
+  if (0 == mid)
+    {
+      jclass cls = env->FindClass(JAVA_PACKAGE"/callback/TunnelAgent");
+      SVN_JNI_CATCH(, SVN_ERR_BASE);
+      SVN_JNI_CATCH(
+          mid = env->GetMethodID(cls, "openTunnel",
+                                 "(Ljava/nio/channels/ReadableByteChannel;"
+                                 "Ljava/nio/channels/WritableByteChannel;"
+                                 "Ljava/lang/String;"
+                                 "Ljava/lang/String;"
+                                 "Ljava/lang/String;I)V"),
+          SVN_ERR_BASE);
+    }
+
+  jobject jtunnelcb = jobject(tunnel_baton);
+  SVN_JNI_CATCH(
+      env->CallVoidMethod(jtunnelcb, mid, jrequest, jresponse,
+                          jtunnel_name, juser, jhostname, jint(port)),
+      SVN_ERR_BASE);
+
+  *tunnel_context = NULL;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+OperationContext::closeTunnel(void *tunnel_context, void *tunnel_baton,
+                              const char *tunnel_name, const char *user,
+                              const char *hostname, int port)
+{
+  delete static_cast<TunnelContext*>(tunnel_context);
+
+  jstring jtunnel_name = JNIUtil::makeJString(tunnel_name);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  jstring juser = JNIUtil::makeJString(user);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  jstring jhostname = JNIUtil::makeJString(hostname);
+  SVN_JNI_CATCH(, SVN_ERR_BASE);
+
+  JNIEnv *env = JNIUtil::getEnv();
+
+  static jmethodID mid = 0;
+  if (0 == mid)
+    {
+      jclass cls = env->FindClass(JAVA_PACKAGE"/callback/TunnelAgent");
+      SVN_JNI_CATCH(, SVN_ERR_BASE);
+      SVN_JNI_CATCH(
+          mid = env->GetMethodID(cls, "closeTunnel",
+                                 "(Ljava/lang/String;"
+                                 "Ljava/lang/String;"
+                                 "Ljava/lang/String;I)V"),
+          SVN_ERR_BASE);
+    }
+
+  jobject jtunnelcb = jobject(tunnel_baton);
+  SVN_JNI_CATCH(
+      env->CallVoidMethod(jtunnelcb, mid,
+                          jtunnel_name, juser, jhostname, jint(port)),
+      SVN_ERR_BASE);
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/bindings/javahl/native/OperationContext.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/OperationContext.h?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/OperationContext.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/OperationContext.h Tue Oct 15 04:29:52 2013
@@ -59,9 +59,27 @@ class OperationContext
 
   jobject m_jctx;
   jobject m_jcfgcb;
+  jobject m_jtunnelcb;
+
   static void progress(apr_off_t progressVal, apr_off_t total,
                        void *baton, apr_pool_t *pool);
   void notifyConfigLoad();
+
+  static svn_boolean_t checkTunnel(
+      void *tunnel_baton, const char *tunnel_name);
+
+  static svn_error_t *openTunnel(
+      apr_file_t **request, apr_file_t **response,
+      void **tunnel_context, void *tunnel_baton,
+      const char *tunnel_name, const char *user,
+      const char *hostname, int port,
+      apr_pool_t *pool);
+
+  static svn_error_t *closeTunnel(
+      void *tunnel_context, void *tunnel_baton,
+      const char *tunnel_name, const char *user,
+      const char *hostname, int port);
+
  public:
   OperationContext(SVN::Pool &pool);
   void attachJavaObject(jobject contextHolder, const char *contextClassType, const char *contextFieldName, jfieldID * ctxFieldID);
@@ -98,6 +116,9 @@ class OperationContext
 
   static svn_error_t * clientName(void *baton, const char **name, apr_pool_t *pool);
   virtual const char * getClientName() const;
+
+  virtual void setTunnelCallback(jobject jtunnelcb);
+  jobject getTunnelCallback() const;
 };
 
 #endif // JAVAHL_OPERATION_CONTEXT_H

Modified: subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/RemoteSession.cpp Tue Oct 15 04:29:52 2013
@@ -68,7 +68,7 @@ RemoteSession::open(jint jretryAttempts,
                     jstring jurl, jstring juuid,
                     jstring jconfigDirectory,
                     jstring jusername, jstring jpassword,
-                    jobject jprompter, jobject jprogress)
+                    jobject jprompter, jobject jprogress, jobject jtunnelcb)
 {
   JNIEnv *env = JNIUtil::getEnv();
 
@@ -111,7 +111,7 @@ RemoteSession::open(jint jretryAttempts,
   jobject jremoteSession = open(
       jretryAttempts, url.c_str(), uuid,
       (jconfigDirectory ? configDirectory.c_str() : NULL),
-      usernameStr, passwordStr, prompter, jprogress);
+      usernameStr, passwordStr, prompter, jprogress, jtunnelcb);
   if (JNIUtil::isExceptionThrown() || !jremoteSession)
     {
       delete prompter;
@@ -125,7 +125,7 @@ RemoteSession::open(jint jretryAttempts,
                     const char* url, const char* uuid,
                     const char* configDirectory,
                     const char*  usernameStr, const char*  passwordStr,
-                    Prompter*& prompter, jobject jprogress)
+                    Prompter*& prompter, jobject jprogress, jobject jtunnelcb)
 {
   /*
    * Initialize ra layer if we have not done so yet
@@ -139,7 +139,7 @@ RemoteSession::open(jint jretryAttempts,
 
   RemoteSession* session = new RemoteSession(
       jretryAttempts, url, uuid, configDirectory,
-      usernameStr, passwordStr, prompter);
+      usernameStr, passwordStr, prompter, jtunnelcb);
   if (JNIUtil::isJavaExceptionThrown() || !session)
     {
       delete session;
@@ -201,11 +201,11 @@ RemoteSession::RemoteSession(int retryAt
                              const char* url, const char* uuid,
                              const char* configDirectory,
                              const char*  username, const char*  password,
-                             Prompter*& prompter)
+                             Prompter*& prompter, jobject jtunnelcb)
   : m_session(NULL), m_context(NULL)
 {
   m_context = new RemoteSessionContext(
-      pool, configDirectory, username, password, prompter);
+      pool, configDirectory, username, password, prompter, jtunnelcb);
   if (JNIUtil::isJavaExceptionThrown())
     return;
 

Modified: subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/RemoteSession.h Tue Oct 15 04:29:52 2013
@@ -48,12 +48,14 @@ class RemoteSession : public SVNBase
                         jstring jurl, jstring juuid,
                         jstring jconfigDirectory,
                         jstring jusername, jstring jpassword,
-                        jobject jprompter, jobject jprogress);
+                        jobject jprompter, jobject jprogress,
+                        jobject jtunnelcb);
     static jobject open(jint jretryAttempts,
                         const char* url, const char* uuid,
                         const char* configDirectory,
                         const char* username, const char* password,
-                        Prompter*& prompter, jobject jprogress);
+                        Prompter*& prompter, jobject jprogress,
+                        jobject jtunnelcb);
     ~RemoteSession();
 
     void cancelOperation() const { m_context->cancelOperation(); }
@@ -117,7 +119,7 @@ class RemoteSession : public SVNBase
                   const char* url, const char* uuid,
                   const char* configDirectory,
                   const char* username, const char* password,
-                  Prompter*& prompter);
+                  Prompter*& prompter, jobject jtunnelcb);
 
     svn_ra_session_t* m_session;
     RemoteSessionContext* m_context;

Modified: subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.cpp Tue Oct 15 04:29:52 2013
@@ -33,7 +33,7 @@
 RemoteSessionContext::RemoteSessionContext(
     SVN::Pool &pool, const char* configDirectory,
     const char* usernameStr, const char* passwordStr,
-    Prompter* prompter)
+    Prompter* prompter, jobject jtunnelcb)
   : OperationContext(pool), m_raCallbacks(NULL)
 {
   setConfigDirectory(configDirectory);
@@ -44,6 +44,7 @@ RemoteSessionContext::RemoteSessionConte
     password(passwordStr);
 
   setPrompt(prompter);
+  setTunnelCallback(jtunnelcb);
 
   /*
    * Setup callbacks
@@ -68,6 +69,14 @@ RemoteSessionContext::RemoteSessionConte
    * Don't set deprecated callback
    */
   m_raCallbacks->open_tmp_file = NULL;
+
+  if (m_jtunnelcb)
+    {
+      m_raCallbacks->check_tunnel_func = checkTunnel;
+      m_raCallbacks->open_tunnel_func = openTunnel;
+      m_raCallbacks->close_tunnel_func = closeTunnel;
+      m_raCallbacks->tunnel_baton = m_jtunnelcb;
+    }
 }
 
 RemoteSessionContext::~RemoteSessionContext()

Modified: subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.h?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.h (original)
+++ subversion/trunk/subversion/bindings/javahl/native/RemoteSessionContext.h Tue Oct 15 04:29:52 2013
@@ -37,7 +37,7 @@ class RemoteSessionContext : public Oper
     RemoteSessionContext(SVN::Pool &pool,
                          const char* jconfigDirectory,
                          const char* jusername, const char* jpassword,
-                         Prompter* prompter);
+                         Prompter* prompter, jobject jtunnelcb);
     virtual ~RemoteSessionContext();
     void activate(jobject jremoteSession, jobject jprogress);
     void * getCallbackBaton();

Modified: subversion/trunk/subversion/bindings/javahl/native/SVNClient.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/SVNClient.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/SVNClient.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/SVNClient.cpp Tue Oct 15 04:29:52 2013
@@ -1511,10 +1511,6 @@ SVNClient::openRemoteSession(const char*
                     ctx, subPool.getPool()),
                 NULL);
 
-    jobject jctx = context.getSelf();
-    if (JNIUtil::isJavaExceptionThrown())
-        return NULL;
-
     /* Decouple the RemoteSession's context from SVNClient's context
        by creating a copy of the prompter here. */
     Prompter* prompter = new Prompter(context.getPrompter());
@@ -1525,7 +1521,7 @@ SVNClient::openRemoteSession(const char*
         retryAttempts, path_info.url.c_str(), path_info.uuid.c_str(),
         context.getConfigDirectory(),
         context.getUsername(), context.getPassword(),
-        prompter, jctx);
+        prompter, context.getSelf(), context.getTunnelCallback());
     if (JNIUtil::isJavaExceptionThrown())
       delete prompter;
 

Modified: subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp Tue Oct 15 04:29:52 2013
@@ -275,6 +275,21 @@ Java_org_apache_subversion_javahl_SVNCli
 }
 
 JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_SVNClient_setTunnelAgent
+(JNIEnv *env, jobject jthis, jobject jtunnelcb)
+{
+  JNIEntry(SVNClient, setPrompt);
+  SVNClient *cl = SVNClient::getCppObject(jthis);
+  if (cl == NULL)
+    {
+      JNIUtil::throwError(_("bad C++ this"));
+      return;
+    }
+
+  cl->getClientContext().setTunnelCallback(jtunnelcb);
+}
+
+JNIEXPORT void JNICALL
 Java_org_apache_subversion_javahl_SVNClient_logMessages
 (JNIEnv *env, jobject jthis, jstring jpath, jobject jpegRevision,
  jobject jranges, jboolean jstopOnCopy, jboolean jdisoverPaths,

Modified: subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp (original)
+++ subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp Tue Oct 15 04:29:52 2013
@@ -40,7 +40,8 @@ Java_org_apache_subversion_javahl_remote
     jstring jurl, jstring juuid,
     jstring jconfigDirectory,
     jstring jusername, jstring jpassword,
-    jobject jprompter, jobject jprogress)
+    jobject jprompter, jobject jprogress,
+    jobject jtunnelcb)
 {
   //JNI macros need jthis but this is a static call
   JNIEntryStatic(RemoteFactory, open);
@@ -50,7 +51,7 @@ Java_org_apache_subversion_javahl_remote
    */
   jobject jremoteSession = RemoteSession::open(
       jretryAttempts, jurl, juuid,
-      jconfigDirectory, jusername, jpassword, jprompter, jprogress);
+      jconfigDirectory, jusername, jpassword, jprompter, jprogress, jtunnelcb);
   if (JNIUtil::isJavaExceptionThrown())
     return NULL;
 

Added: subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp?rev=1532182&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp (added)
+++ subversion/trunk/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp Tue Oct 15 04:29:52 2013
@@ -0,0 +1,223 @@
+/**
+ * @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 org_apache_subversion_javahl_util_TunnelChannel.cpp
+ * @brief Implementation of the native methods in the Java classes
+ *        TunnelChannel, RequestChannel and ResponseChannel
+ */
+
+#include <assert.h>             // TEMPORARY until we handle weird byte arrays
+
+#include <string>
+
+#include <apr_file_io.h>
+
+#include "../include/org_apache_subversion_javahl_util_TunnelChannel.h"
+#include "../include/org_apache_subversion_javahl_util_RequestChannel.h"
+#include "../include/org_apache_subversion_javahl_util_ResponseChannel.h"
+
+#include "JNIUtil.h"
+#include "JNIStackElement.h"
+#include "JNIByteArray.h"
+
+#include "svn_private_config.h"
+
+namespace {
+apr_file_t* get_file_descriptor(jlong jfd)
+{
+  apr_file_t* fd = reinterpret_cast<apr_file_t*>(jfd);
+  if (!fd)
+    {
+      JNIUtil::throwNullPointerException("nativeChannel");
+      return NULL;
+    }
+  return fd;
+}
+
+void throw_IOException(const char* message, apr_status_t status)
+{
+  char buf[1024];
+  apr_strerror(status, buf, sizeof(buf) - 1);
+
+  std::string msg(message);
+  msg += ": ";
+  msg += buf;
+  JNIUtil::raiseThrowable("java/io/IOException", msg.c_str());
+}
+
+class ByteBufferProxy
+{
+public:
+  ByteBufferProxy(jobject buf, JNIEnv* env)
+    : m_buf(buf),
+      m_direct(env->GetDirectBufferAddress(buf)),
+      m_array(m_direct ? NULL : get_array(buf, env)),
+      m_array_offset(m_array ? get_array_offset(buf, env) : 0),
+      m_offset(get_position(buf, env)),
+      m_size(get_remaining(buf, env))
+    {}
+
+  jint read(apr_file_t* fd, JNIEnv* env)
+    {
+      if (!m_size)
+        return 0;
+
+      JNIByteArray arr(m_array, false, false);
+      apr_size_t bytes_read = m_size;
+      apr_status_t status = apr_file_read(
+          fd, get_base_address(arr), &bytes_read);
+      if (status && !APR_STATUS_IS_EOF(status))
+        {
+          throw_IOException(_("Error reading from native file handle"),
+                            status);
+          return 0;
+        }
+      update_position(bytes_read, env);
+      return jint(bytes_read);
+    }
+
+  jint write(apr_file_t* fd, JNIEnv* env)
+    {
+      if (!m_size)
+        return 0;
+
+      JNIByteArray arr(m_array);
+      apr_size_t bytes_written;
+      apr_status_t status = apr_file_write_full(
+          fd, get_base_address(arr), m_size, &bytes_written);
+      if (status)
+        {
+          throw_IOException(_("Error writing to native file handle"),
+                            status);
+          return 0;
+        }
+      update_position(bytes_written, env);
+      return jint(bytes_written);
+    }
+
+private:
+  void *get_base_address(JNIByteArray& arr)
+    {
+      void* base = (m_direct ? m_direct
+                    : const_cast<signed char*>(arr.getBytes()));
+      // FIXME: We do not currently support buffers that are nether
+      // direct, nor have an accessible array.
+      assert(base != 0);
+      return static_cast<char*>(base) + m_offset + m_array_offset;
+    }
+
+  void update_position(apr_size_t amount, JNIEnv* env)
+    {
+      jmethodID mid = env->GetMethodID(
+          env->GetObjectClass(m_buf), "position", "(I)Ljava/nio/Buffer;");
+      if (mid)
+        env->CallObjectMethod(m_buf, mid, jint(amount));
+    }
+
+  static jbyteArray get_array(jobject buf, JNIEnv* env)
+    {
+      jclass cls = env->GetObjectClass(buf);
+      jmethodID mid = env->GetMethodID(cls, "hasArray", "()Z");
+      if (!mid)
+        return NULL;
+
+      jboolean has_array = env->CallBooleanMethod(buf, mid);
+      if (!has_array)
+        return NULL;
+
+      mid = env->GetMethodID(cls, "array", "()[B");
+      if (mid)
+        return jbyteArray(env->CallObjectMethod(buf, mid));
+      return NULL;
+    }
+
+  static apr_size_t get_array_offset(jobject buf, JNIEnv* env)
+    {
+      jmethodID mid = env->GetMethodID(
+          env->GetObjectClass(buf), "arrayOffset", "()I");
+      if (mid)
+        return env->CallIntMethod(buf, mid);
+      return 0;
+    }
+
+  static apr_size_t get_position(jobject buf, JNIEnv* env)
+    {
+      jmethodID mid = env->GetMethodID(
+          env->GetObjectClass(buf), "position", "()I");
+      if (mid)
+        return env->CallIntMethod(buf, mid);
+      return 0;
+    }
+
+  static apr_size_t get_remaining(jobject buf, JNIEnv* env)
+    {
+      jmethodID mid = env->GetMethodID(
+          env->GetObjectClass(buf), "remaining", "()I");
+      if (mid)
+        return env->CallIntMethod(buf, mid);
+      return 0;
+    }
+
+  jobject m_buf;
+  void *m_direct;
+  jbyteArray m_array;
+  apr_size_t m_array_offset;
+  apr_size_t m_offset;
+  apr_size_t m_size;
+};
+} // anonymous namespace
+
+JNIEXPORT void JNICALL
+Java_org_apache_subversion_javahl_util_TunnelChannel_nativeClose(
+    JNIEnv* env, jclass jclazz, jlong nativeChannel)
+{
+  JNIEntryStatic(TunnelChannel, close);
+  apr_file_t* fd = reinterpret_cast<apr_file_t*>(nativeChannel);
+  if (!fd)
+    return;
+
+  apr_status_t status = apr_file_close(fd);
+  if (status)
+    throw_IOException(_("Error closing native file handle"), status);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_util_RequestChannel_nativeRead(
+    JNIEnv* env, jclass jclazz, jlong nativeChannel, jobject dst)
+{
+  JNIEntryStatic(RequestChannel, read);
+  apr_file_t* fd = get_file_descriptor(nativeChannel);
+  if (fd)
+    return ByteBufferProxy(dst, env).read(fd, env);
+  return -1;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_apache_subversion_javahl_util_ResponseChannel_nativeWrite(
+    JNIEnv* env, jclass jclazz, jlong nativeChannel, jobject src)
+{
+  JNIEntryStatic(ResponseChannel, write);
+  apr_file_t* fd = get_file_descriptor(nativeChannel);
+  if (fd)
+    return ByteBufferProxy(src, env).write(fd, env);
+  return -1;
+}

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNClient.java Tue Oct 15 04:29:52 2013
@@ -134,6 +134,12 @@ public interface ISVNClient
     void setPrompt(UserPasswordCallback prompt);
 
     /**
+     * Set callbacks for ra_svn tunnel handling.
+     * @since 1.9
+     */
+    void setTunnelAgent(TunnelAgent tunnelAgent);
+
+    /**
      * Retrieve the log messages for an item.
      * @param path          path or url to get the log message for.
      * @param pegRevision   revision to interpret path

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java Tue Oct 15 04:29:52 2013
@@ -132,6 +132,8 @@ public class SVNClient implements ISVNCl
 
     public native void setPrompt(UserPasswordCallback prompt);
 
+    public native void setTunnelAgent(TunnelAgent tunnelAgent);
+
     public native void logMessages(String path, Revision pegRevision,
                                    List<RevisionRange> revisionRanges,
                                    boolean stopOnCopy, boolean discoverPath,

Added: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/TunnelAgent.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/TunnelAgent.java?rev=1532182&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/TunnelAgent.java (added)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/callback/TunnelAgent.java Tue Oct 15 04:29:52 2013
@@ -0,0 +1,83 @@
+/**
+ * @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
+ */
+
+package org.apache.subversion.javahl.callback;
+
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * Callback interface for creating and managing tunnels for ra_svn
+ * connections.
+ *
+ * Note that tunnel agent implementations should run in a separate
+ * thread of control than the one that invokes an ISVNClient or
+ * RemoteSession method that requires a tunnel, otherwise the method
+ * will deadlock.
+ *
+ * @since 1.9
+ */
+public interface TunnelAgent
+{
+    /**
+     * This callback method is called before a tunnel is created, to
+     * determine whether to use this tunnel implementation, or revert
+     * to the default (native) tunnel implementation.
+     * @param name the name of the tunnel, as in
+     *     <tt>svn+</tt><em>name</em><tt>://...</tt>
+     * @return <code>false</code> to defer to the default implementation.
+     */
+    boolean checkTunnel(String name);
+
+    /**
+     * This callback method is called when a tunnel needs to be
+     * created and the request and response streams attached to it.
+     * @param request The request stream of the tunnel. The tunnel
+     *     agent implementation will read requests from this channel
+     *     and send them to the tunnel process.
+     * @param response The request stream of the tunnel. The tunnel
+     *     agent implementation will read requests from this channel
+     *     and send them to the tunnel process.
+     * @param name the name of the tunnel, as in
+     *     <tt>svn+</tt><em>name</em><tt>://...</tt>
+     * @param user the tunnel username
+     * @param hostname the host part of the svn+tunnel:// URL
+     * @param port the port part of the svn+tunnel:// URL
+     * @throws any exception will abort the connection
+     */
+    void openTunnel(ReadableByteChannel request, WritableByteChannel response,
+                    String name, String user, String hostname, int port)
+        throws Throwable;
+
+    /**
+     * This callback method is called when a tunnel needs to be closed
+     * and the request and response streams detached from it.
+     * @param name the name of the tunnel, as in
+     *                  <tt>svn+</tt><em>name</em><tt>://...</tt>
+     * @param user the tunnel username
+     * @param hostname the host part of the svn+tunnel:// URL
+     * @param port the port part of the svn+tunnel:// URL
+     */
+    void closeTunnel(String name, String user, String hostname, int port)
+        throws Throwable;
+}

Modified: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java (original)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java Tue Oct 15 04:29:52 2013
@@ -59,13 +59,15 @@ public class RemoteFactory
     public RemoteFactory(String configDirectory,
                          String username, String password,
                          UserPasswordCallback prompt,
-                         ProgressCallback progress)
+                         ProgressCallback progress,
+                         TunnelAgent tunnelAgent)
     {
         setConfigDirectory(configDirectory);
         setUsername(username);
         setPassword(password);
         setPrompt(prompt);
         setProgressCallback(progress);
+        setTunnelAgent(tunnelAgent);
     }
 
     /**
@@ -125,6 +127,14 @@ public class RemoteFactory
     }
 
     /**
+     * Set callbacks for ra_svn tunnel handling.
+     */
+    public void setTunnelAgent(TunnelAgent tunnelAgent)
+    {
+        this.tunnelAgent = tunnelAgent;
+    }
+
+    /**
      * Open a persistent session to a repository.
      * <p>
      * <b>Note:</b> The URL can point to a subtree of the repository.
@@ -141,7 +151,7 @@ public class RemoteFactory
             throws ClientException, SubversionException
     {
         return open(1, url, null, configDirectory,
-                    username, password, prompt, progress);
+                    username, password, prompt, progress, tunnelAgent);
     }
 
     /**
@@ -168,7 +178,7 @@ public class RemoteFactory
             throw new IllegalArgumentException(
                 "retryAttempts must be positive");
         return open(retryAttempts, url, null, configDirectory,
-                    username, password, prompt, progress);
+                    username, password, prompt, progress, tunnelAgent);
     }
 
     /**
@@ -195,7 +205,7 @@ public class RemoteFactory
         if (reposUUID == null)
             throw new IllegalArgumentException("reposUUID may not be null");
         return open(1, url, reposUUID, configDirectory,
-                    username, password, prompt, progress);
+                    username, password, prompt, progress, tunnelAgent);
     }
 
     /**
@@ -229,7 +239,7 @@ public class RemoteFactory
             throw new IllegalArgumentException(
                 "retryAttempts must be positive");
         return open(retryAttempts, url, reposUUID, configDirectory,
-                    username, password, prompt, progress);
+                    username, password, prompt, progress, tunnelAgent);
     }
 
     private String configDirectory;
@@ -237,6 +247,7 @@ public class RemoteFactory
     private String password;
     private UserPasswordCallback prompt;
     private ProgressCallback progress;
+    private TunnelAgent tunnelAgent;
 
     /* Native factory implementation. */
     private static native ISVNRemote open(int retryAttempts,
@@ -244,6 +255,7 @@ public class RemoteFactory
                                           String configDirectory,
                                           String username, String password,
                                           UserPasswordCallback prompt,
-                                          ProgressCallback progress)
+                                          ProgressCallback progress,
+                                          TunnelAgent tunnelAgent)
             throws ClientException, SubversionException;
 }

Added: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java?rev=1532182&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java (added)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/RequestChannel.java Tue Oct 15 04:29:52 2013
@@ -0,0 +1,49 @@
+/**
+ * @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
+ */
+
+package org.apache.subversion.javahl.util;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+/* The following channel subclasses are used by the native
+   implementation of the tunnel management code. */
+
+class RequestChannel
+    extends TunnelChannel
+    implements ReadableByteChannel
+{
+    private RequestChannel(long nativeChannel)
+    {
+        super(nativeChannel);
+    }
+
+    public int read(ByteBuffer dst) throws IOException
+    {
+        return nativeRead(nativeChannel, dst);
+    }
+
+    private static native int nativeRead(long nativeChannel, ByteBuffer dst)
+        throws IOException;
+}

Added: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java?rev=1532182&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java (added)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/ResponseChannel.java Tue Oct 15 04:29:52 2013
@@ -0,0 +1,49 @@
+/**
+ * @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
+ */
+
+package org.apache.subversion.javahl.util;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+
+/* The following channel subclasses are used by the native
+   implementation of the tunnel management code. */
+
+class ResponseChannel
+    extends TunnelChannel
+    implements WritableByteChannel
+{
+    private ResponseChannel(long nativeChannel)
+    {
+        super(nativeChannel);
+    }
+
+    public int write(ByteBuffer src) throws IOException
+    {
+        return nativeWrite(nativeChannel, src);
+    }
+
+    private static native int nativeWrite(long nativeChannel, ByteBuffer src)
+        throws IOException;
+}

Added: subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.java?rev=1532182&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.java (added)
+++ subversion/trunk/subversion/bindings/javahl/src/org/apache/subversion/javahl/util/TunnelChannel.java Tue Oct 15 04:29:52 2013
@@ -0,0 +1,55 @@
+/**
+ * @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
+ */
+
+package org.apache.subversion.javahl.util;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channel;
+
+/* The following channel subclasses are used by the native
+   implementation of the tunnel management code. */
+
+abstract class TunnelChannel implements Channel
+{
+    protected TunnelChannel(long nativeChannel)
+    {
+        this.nativeChannel = nativeChannel;
+    }
+
+    public boolean isOpen()
+    {
+        return (nativeChannel != 0);
+    }
+
+    public void close() throws IOException
+    {
+        nativeClose(nativeChannel);
+        nativeChannel = 0;
+    }
+
+    private native static void nativeClose(long nativeChannel)
+        throws IOException;
+
+    protected long nativeChannel;
+}

Modified: subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java (original)
+++ subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java Tue Oct 15 04:29:52 2013
@@ -33,6 +33,9 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
 import java.text.ParseException;
 import java.util.Collection;
 import java.util.Arrays;
@@ -3776,6 +3779,106 @@ public class BasicTests extends SVNTests
         assertEquals(1, result.size());
     }
 
+    private class Tunnel extends Thread implements TunnelAgent
+    {
+        public boolean checkTunnel(String name)
+        {
+            return name.equals("test");
+        }
+
+        public void openTunnel(ReadableByteChannel request,
+                               WritableByteChannel response,
+                               String name, String user,
+                               String hostname, int port)
+        {
+            this.request = request;
+            this.response = response;
+            start();
+        }
+
+        public void closeTunnel(String name, String user,
+                                String hostname, int port)
+            throws Throwable
+        {
+            request.close();
+            join();
+            response.close();
+        }
+
+        private ReadableByteChannel request;
+        private WritableByteChannel response;
+
+        public void run()
+        {
+
+            int index = 0;
+            byte[] raw_data = new byte[1024];
+            ByteBuffer data = ByteBuffer.wrap(raw_data);
+            while(index < commands.length && request.isOpen()) {
+                try {
+                    byte[] command = commands[index++];
+                    response.write(ByteBuffer.wrap(command));
+                } catch (IOException ex) {
+                    break;
+                }
+
+                try {
+                    data.clear();
+                    request.read(data);
+                } catch (Throwable ex) {}
+            }
+
+            try {
+                response.close();
+                request.close();
+            } catch (Throwable t) {}
+        }
+
+        private final byte[][] commands = new byte[][]{
+            // Initial capabilities negotiation
+            ("( success ( 2 2 ( ) " +
+             "( edit-pipeline svndiff1 absent-entries commit-revprops " +
+             "depth log-revprops atomic-revprops partial-replay " +
+             "inherited-props ephemeral-txnprops file-revs-reverse " +
+             ") ) ) ").getBytes(),
+
+            // Response for successful connection
+            ("( success ( ( ANONYMOUS EXTERNAL ) " +
+             "36:e3c8c113-03ba-4ec5-a8e6-8fc555e57b91 ) ) ").getBytes(),
+
+            // Response to authentication request
+            ("( success ( ) ) ( success ( " +
+             "36:e3c8c113-03ba-4ec5-a8e6-8fc555e57b91 " +
+             "24:svn+test://localhost/foo ( mergeinfo ) ) ) ").getBytes(),
+
+            // Response to revprop request
+            ("( success ( ( ) 0: ) ) ( success ( ( 4:fake ) ) ) ").getBytes()
+        };
+    }
+
+    /**
+     * Test tunnel handling.
+     */
+    public void testTunnelAgent() throws Throwable
+    {
+        byte[] revprop;
+        SVNClient cl = new SVNClient();
+        try {
+            cl.notification2(new MyNotifier());
+            cl.setPrompt(new DefaultPromptUserPassword());
+            cl.username(USERNAME);
+            cl.setProgressCallback(new DefaultProgressListener());
+            cl.setConfigDirectory(conf.getAbsolutePath());
+
+            cl.setTunnelAgent(new Tunnel());
+            revprop = cl.revProperty("svn+test://localhost/foo", "svn:log",
+                                     Revision.getInstance(0L));
+        } finally {
+            cl.dispose();
+        }
+        assertEquals("fake", new String(revprop));
+    }
+
     /**
      * @return <code>file</code> converted into a -- possibly
      * <code>canonical</code>-ized -- Subversion-internal path

Modified: subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java (original)
+++ subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java Tue Oct 15 04:29:52 2013
@@ -111,7 +111,8 @@ public class SVNRemoteTests extends SVNT
             session = new RemoteFactory(
                 super.conf.getAbsolutePath(),
                 USERNAME, PASSWORD,
-                new DefaultPromptUserPassword(), null)
+                new DefaultPromptUserPassword(),
+                null, null)
                 .openRemoteSession(getTestRepoUrl());
         }
         catch (ClientException ex)
@@ -138,7 +139,8 @@ public class SVNRemoteTests extends SVNT
                 new RemoteFactory(
                     super.conf.getAbsolutePath(),
                     USERNAME, PASSWORD,
-                    new DefaultPromptUserPassword(), null)
+                    new DefaultPromptUserPassword(),
+                    null, null)
                     .openRemoteSession(prefix + "repositorydoesnotexisthere");
             }
             finally

Modified: subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java?rev=1532182&r1=1532181&r2=1532182&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java (original)
+++ subversion/trunk/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNTests.java Tue Oct 15 04:29:52 2013
@@ -355,7 +355,7 @@ class SVNTests extends TestCase
         }
     }
 
-    private static class DefaultProgressListener implements ProgressCallback
+    protected static class DefaultProgressListener implements ProgressCallback
     {
 
         public void onProgress(ProgressEvent event)