diff options
Diffstat (limited to 'subversion/bindings/javahl/native/RemoteSession.cpp')
-rw-r--r-- | subversion/bindings/javahl/native/RemoteSession.cpp | 1322 |
1 files changed, 1322 insertions, 0 deletions
diff --git a/subversion/bindings/javahl/native/RemoteSession.cpp b/subversion/bindings/javahl/native/RemoteSession.cpp new file mode 100644 index 0000000..42bfe75 --- /dev/null +++ b/subversion/bindings/javahl/native/RemoteSession.cpp @@ -0,0 +1,1322 @@ +/** + * @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 RemoteSession.cpp + * @brief Implementation of the class RemoteSession + */ + +#include <cstring> +#include <memory> +#include <set> + +#include "JNIByteArray.h" +#include "JNIStringHolder.h" +#include "JNIUtil.h" +#include "Path.h" + +#include "svn_ra.h" +#include "svn_string.h" +#include "svn_dirent_uri.h" +#include "svn_delta.h" + +#include "CreateJ.h" +#include "EnumMapper.h" +#include "Iterator.h" +#include "LogMessageCallback.h" +#include "OutputStream.h" +#include "Prompter.h" +#include "Revision.h" +#include "RemoteSession.h" +#include "EditorProxy.h" +#include "StateReporter.h" + +#include <apr_strings.h> +#include "svn_private_config.h" + +#define JAVA_CLASS_REMOTE_SESSION JAVAHL_CLASS("/remote/RemoteSession") + +RemoteSession * +RemoteSession::getCppObject(jobject jthis) +{ + static jfieldID fid = 0; + jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid, + JAVA_CLASS_REMOTE_SESSION); + return (cppAddr == 0 ? NULL : reinterpret_cast<RemoteSession *>(cppAddr)); +} + +jobject +RemoteSession::open(jint jretryAttempts, + jstring jurl, jstring juuid, + jstring jconfigDirectory, + jstring jusername, jstring jpassword, + jobject jprompter, jobject jdeprecatedPrompter, + jobject jprogress, jobject jcfgcb, jobject jtunnelcb) +{ + SVN_ERR_ASSERT_NO_RETURN(!(jprompter && jdeprecatedPrompter)); + + SVN::Pool requestPool; + URL url(jurl, requestPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(url.error_occurred(), NULL); + + JNIStringHolder uuid(juuid); + if (JNIUtil::isExceptionThrown()) + return NULL; + + Path configDirectory(jconfigDirectory, requestPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(configDirectory.error_occurred(), NULL); + + JNIStringHolder usernameStr(jusername); + if (JNIUtil::isExceptionThrown()) + return NULL; + + JNIStringHolder passwordStr(jpassword); + if (JNIUtil::isExceptionThrown()) + return NULL; + + Prompter::UniquePtr prompter(jprompter ? Prompter::create(jprompter) + : CompatPrompter::create(jdeprecatedPrompter)); + if (JNIUtil::isExceptionThrown()) + return NULL; + + jobject jremoteSession = open( + jretryAttempts, url.c_str(), uuid, + (jconfigDirectory ? configDirectory.c_str() : NULL), + usernameStr, passwordStr, prompter, jprogress, jcfgcb, jtunnelcb); + if (JNIUtil::isExceptionThrown() || !jremoteSession) + jremoteSession = NULL; + return jremoteSession; +} + +jobject +RemoteSession::open(jint jretryAttempts, + const char* url, const char* uuid, + const char* configDirectory, + const char* usernameStr, const char* passwordStr, + Prompter::UniquePtr prompter, jobject jprogress, + jobject jcfgcb, jobject jtunnelcb) +{ + RemoteSession* session = new RemoteSession( + jretryAttempts, url, uuid, configDirectory, + usernameStr, passwordStr, prompter, jcfgcb, jtunnelcb); + if (JNIUtil::isJavaExceptionThrown() || !session) + { + delete session; + return NULL; + } + + // Create java session object + JNIEnv *env = JNIUtil::getEnv(); + + jclass clazz = env->FindClass(JAVA_CLASS_REMOTE_SESSION); + if (JNIUtil::isJavaExceptionThrown()) + { + delete session; + return NULL; + } + + static jmethodID ctor = 0; + if (ctor == 0) + { + ctor = env->GetMethodID(clazz, "<init>", "(J)V"); + if (JNIUtil::isJavaExceptionThrown()) + { + delete session; + return NULL; + } + } + + jobject jremoteSession = env->NewObject(clazz, ctor, session->getCppAddr()); + if (JNIUtil::isJavaExceptionThrown()) + { + delete session; + return NULL; + } + + session->m_context->activate(jremoteSession, jprogress); + if (JNIUtil::isJavaExceptionThrown()) + { + delete session; + jremoteSession = NULL; + } + + return jremoteSession; +} + + +namespace{ + struct compare_c_strings + { + bool operator()(const char* a, const char* b) + { + return (0 < std::strcmp(a, b)); + } + }; + typedef std::set<const char*, compare_c_strings> attempt_set; + typedef std::pair<attempt_set::iterator, bool> attempt_insert; +} // anonymous namespace + +RemoteSession::RemoteSession(int retryAttempts, + const char* url, const char* uuid, + const char* configDirectory, + const char* username, const char* password, + Prompter::UniquePtr prompter, + jobject jcfgcb, jobject jtunnelcb) + : m_session(NULL), m_context(NULL) +{ + m_context = new RemoteSessionContext( + pool, configDirectory, username, password, prompter, jcfgcb, jtunnelcb); + if (JNIUtil::isJavaExceptionThrown()) + return; + + const char* corrected_url = NULL; + bool cycle_detected = false; + attempt_set attempted; + + while (retryAttempts-- >= 0) + { + SVN_JNI_ERR( + svn_ra_open4(&m_session, &corrected_url, + url, uuid, m_context->getCallbacks(), + m_context->getCallbackBaton(), + m_context->getConfigData(), + pool.getPool()), + ); + + if (!corrected_url) + break; + + attempt_insert result = attempted.insert(corrected_url); + if (!result.second) + { + cycle_detected = true; + break; + } + + url = corrected_url; + corrected_url = NULL; + } + + if (cycle_detected) + { + JNIEnv *env = JNIUtil::getEnv(); + + jstring exmsg = JNIUtil::makeJString( + apr_psprintf(pool.getPool(), + _("Redirect cycle detected for URL '%s'"), + corrected_url)); + + jclass excls = env->FindClass( + JAVAHL_CLASS("/SubversionException")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID exctor = 0; + if (exctor == 0) + { + exctor = env->GetMethodID(excls, "<init>", "(Ljava/lang/String;)V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jobject ex = env->NewObject(excls, exctor, exmsg); + env->Throw(static_cast<jthrowable>(ex)); + return; + } + + if (corrected_url) + { + JNIEnv *env = JNIUtil::getEnv(); + + jstring exmsg = JNIUtil::makeJString(_("Too many redirects")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + jstring exurl = JNIUtil::makeJString(corrected_url); + if (JNIUtil::isJavaExceptionThrown()) + return; + + jclass excls = env->FindClass( + JAVAHL_CLASS("/remote/RetryOpenSession")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID exctor = 0; + if (exctor == 0) + { + exctor = env->GetMethodID(excls, "<init>", "(JJ)V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jobject ex = env->NewObject(excls, exctor, exmsg, exurl); + env->Throw(static_cast<jthrowable>(ex)); + return; + } +} + +RemoteSession::~RemoteSession() +{ + delete m_context; +} + +void +RemoteSession::dispose(jobject jthis) +{ + static jfieldID fid = 0; + SVNBase::dispose(jthis, &fid, JAVA_CLASS_REMOTE_SESSION); +} + +void RemoteSession::reparent(jstring jurl) +{ + SVN::Pool subPool(pool); + URL url(jurl, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(url.error_occurred(),); + + SVN_JNI_ERR(svn_ra_reparent(m_session, url.c_str(), subPool.getPool()), ); +} + +jstring +RemoteSession::getSessionUrl() +{ + SVN::Pool subPool(pool); + const char* url; + SVN_JNI_ERR(svn_ra_get_session_url(m_session, &url, subPool.getPool()), NULL); + + jstring jurl = JNIUtil::makeJString(url); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return jurl; +} + +jstring +RemoteSession::getSessionRelativePath(jstring jurl) +{ + SVN::Pool subPool(pool); + URL url(jurl, subPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(url.error_occurred(), NULL); + + const char* rel_path; + SVN_JNI_ERR(svn_ra_get_path_relative_to_session( + m_session, &rel_path, url.c_str(), subPool.getPool()), + NULL); + jstring jrel_path = JNIUtil::makeJString(rel_path); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return jrel_path; +} + +jstring +RemoteSession::getReposRelativePath(jstring jurl) +{ + SVN::Pool subPool(pool); + URL url(jurl, subPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(url.error_occurred(), NULL); + + const char* rel_path; + SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &rel_path, + url.c_str(), + subPool.getPool()), + NULL); + + jstring jrel_path = JNIUtil::makeJString(rel_path); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return jrel_path; +} + +jstring +RemoteSession::getReposUUID() +{ + SVN::Pool subPool(pool); + const char * uuid; + SVN_JNI_ERR(svn_ra_get_uuid2(m_session, &uuid, subPool.getPool()), NULL); + + jstring juuid = JNIUtil::makeJString(uuid); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return juuid; +} + +jstring +RemoteSession::getReposRootUrl() +{ + SVN::Pool subPool(pool); + const char* url; + SVN_JNI_ERR(svn_ra_get_repos_root2(m_session, &url, subPool.getPool()), + NULL); + + jstring jurl = JNIUtil::makeJString(url); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return jurl; +} + +jlong +RemoteSession::getLatestRevision() +{ + SVN::Pool subPool(pool); + svn_revnum_t rev; + SVN_JNI_ERR(svn_ra_get_latest_revnum(m_session, &rev, subPool.getPool()), + SVN_INVALID_REVNUM); + return rev; +} + +jlong +RemoteSession::getRevisionByTimestamp(jlong timestamp) +{ + SVN::Pool subPool(pool); + svn_revnum_t rev; + SVN_JNI_ERR(svn_ra_get_dated_revision(m_session, &rev, + apr_time_t(timestamp), + subPool.getPool()), + SVN_INVALID_REVNUM); + return rev; +} + +namespace { +svn_string_t* +byte_array_to_svn_string(JNIByteArray& ary, SVN::Pool& scratch_pool) +{ + if (ary.isNull()) + return NULL; + return svn_string_ncreate(reinterpret_cast<const char*>(ary.getBytes()), + ary.getLength(), scratch_pool.getPool()); +} +} // anonymous namespace + +void +RemoteSession::changeRevisionProperty( + jlong jrevision, jstring jname, + jbyteArray jold_value, jbyteArray jvalue) +{ + JNIStringHolder name(jname); + if (JNIUtil::isExceptionThrown()) + return; + + JNIByteArray old_value(jold_value); + if (JNIUtil::isExceptionThrown()) + return; + + JNIByteArray value(jvalue); + if (JNIUtil::isExceptionThrown()) + return; + + SVN::Pool subPool(pool); + svn_string_t* const* p_old_value = NULL; + svn_string_t* const str_old_value = + byte_array_to_svn_string(old_value, subPool); + if (str_old_value) + p_old_value = &str_old_value; + + SVN_JNI_ERR(svn_ra_change_rev_prop2(m_session, + svn_revnum_t(jrevision), + name, p_old_value, + byte_array_to_svn_string(value, subPool), + subPool.getPool()), ); +} + +jobject +RemoteSession::getRevisionProperties(jlong jrevision) +{ + SVN::Pool subPool(pool); + apr_hash_t *props; + SVN_JNI_ERR(svn_ra_rev_proplist(m_session, svn_revnum_t(jrevision), + &props, subPool.getPool()), + NULL); + + return CreateJ::PropertyMap(props, subPool.getPool()); +} + +jbyteArray +RemoteSession::getRevisionProperty(jlong jrevision, jstring jname) +{ + JNIStringHolder name(jname); + if (JNIUtil::isExceptionThrown()) + return NULL; + + SVN::Pool subPool(pool); + svn_string_t *propval; + SVN_JNI_ERR(svn_ra_rev_prop(m_session, svn_revnum_t(jrevision), + name, &propval, subPool.getPool()), + NULL); + + return JNIUtil::makeJByteArray(propval); +} + +jlong +RemoteSession::getFile(jlong jrevision, jstring jpath, + jobject jcontents, jobject jproperties) +{ + OutputStream contents_proxy(jcontents); + if (JNIUtil::isExceptionThrown()) + return SVN_INVALID_REVNUM; + + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return SVN_INVALID_REVNUM; + SVN_JNI_ERR(path.error_occurred(), SVN_INVALID_REVNUM); + + apr_hash_t* props = NULL; + svn_revnum_t fetched_rev = svn_revnum_t(jrevision); + svn_stream_t* contents = (!jcontents ? NULL + : contents_proxy.getStream(subPool)); + + SVN_JNI_ERR(svn_ra_get_file(m_session, path.c_str(), fetched_rev, + contents, &fetched_rev, + (jproperties ? &props : NULL), + subPool.getPool()), + SVN_INVALID_REVNUM); + + if (jproperties) + { + CreateJ::FillPropertyMap(jproperties, props, subPool.getPool()); + if (JNIUtil::isExceptionThrown()) + return SVN_INVALID_REVNUM; + } + + return fetched_rev; +} + +namespace { +void fill_dirents(const char* base_url, const char* base_relpath, + jobject jdirents, apr_hash_t* dirents, + apr_pool_t* scratch_pool) +{ + if (!dirents) + return; + + base_url = apr_pstrcat(scratch_pool, base_url, "/", base_relpath, NULL); + base_url = svn_uri_canonicalize(base_url, scratch_pool); + svn_stringbuf_t* abs_path = svn_stringbuf_create(base_url, scratch_pool); + svn_stringbuf_appendbyte(abs_path, '/'); + const apr_size_t base_len = abs_path->len; + + JNIEnv *env = JNIUtil::getEnv(); + + // Create a local frame for our references + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return; + + // We have no way of knowing the exact type of `dirents' in advance + // so we cannot remember the "put" method ID across calls. + jmethodID put_mid = + env->GetMethodID(env->GetObjectClass(jdirents), "put", + "(Ljava/lang/Object;Ljava/lang/Object;)" + "Ljava/lang/Object;"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + static jfieldID path_fid = 0; + if (path_fid == 0) + { + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/DirEntry")); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + path_fid = env->GetFieldID(clazz, "path", "Ljava/lang/String;"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + } + + for (apr_hash_index_t* hi = apr_hash_first(scratch_pool, dirents); + hi; hi = apr_hash_next(hi)) + { + const char* path; + svn_dirent_t* dirent; + + const void *v_key; + void *v_val; + + apr_hash_this(hi, &v_key, NULL, &v_val); + path = static_cast<const char*>(v_key); + dirent = static_cast<svn_dirent_t*>(v_val); + abs_path->len = base_len; + svn_stringbuf_appendcstr(abs_path, path); + + jobject jdirent = CreateJ::DirEntry(path, abs_path->data, dirent); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + // Use the existing DirEntry.path field as the key + jstring jpath = jstring(env->GetObjectField(jdirent, path_fid)); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + env->CallObjectMethod(jdirents, put_mid, jpath, jdirent); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + env->DeleteLocalRef(jdirent); + } + + POP_AND_RETURN_NOTHING(); +} +} // anonymous namespace + +jlong +RemoteSession::getDirectory(jlong jrevision, jstring jpath, + jint jdirent_fields, jobject jdirents, + jobject jproperties) +{ + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return SVN_INVALID_REVNUM; + SVN_JNI_ERR(path.error_occurred(), SVN_INVALID_REVNUM); + + apr_hash_t* props = NULL; + apr_hash_t* dirents = NULL; + svn_revnum_t fetched_rev = svn_revnum_t(jrevision); + + SVN_JNI_ERR(svn_ra_get_dir2(m_session, (jdirents ? &dirents : NULL), + &fetched_rev, (jproperties ? &props : NULL), + path.c_str(), fetched_rev, + apr_uint32_t(jdirent_fields), + subPool.getPool()), + SVN_INVALID_REVNUM); + + if (jdirents) + { + // We will construct the absolute path in the DirEntry objects + // from the session URL and directory relpath. + const char* base_url; + SVN_JNI_ERR(svn_ra_get_session_url(m_session, &base_url, + subPool.getPool()), + SVN_INVALID_REVNUM); + fill_dirents(base_url, path.c_str(), jdirents, dirents, + subPool.getPool()); + if (JNIUtil::isExceptionThrown()) + return SVN_INVALID_REVNUM; + } + + if (jproperties) + { + CreateJ::FillPropertyMap(jproperties, props, subPool.getPool()); + if (JNIUtil::isExceptionThrown()) + return SVN_INVALID_REVNUM; + } + + return fetched_rev; +} + +namespace { +const apr_array_header_t* +build_string_array(const Iterator& iter, + bool contains_relpaths, SVN::Pool& pool) +{ + apr_pool_t* result_pool = pool.getPool(); + apr_array_header_t* array = apr_array_make( + result_pool, 0, sizeof(const char*)); + while (iter.hasNext()) + { + const char* element; + jstring jitem = (jstring)iter.next(); + if (contains_relpaths) + { + Relpath item(jitem, pool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(item.error_occurred(), NULL); + element = apr_pstrdup(result_pool, item.c_str()); + } + else + { + JNIStringHolder item(jitem); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + element = item.pstrdup(result_pool); + } + APR_ARRAY_PUSH(array, const char*) = element; + } + return array; +} +} + +jobject +RemoteSession::getMergeinfo(jobject jpaths, jlong jrevision, jobject jinherit, + jboolean jinclude_descendants) +{ + Iterator paths_iter(jpaths); + if (JNIUtil::isExceptionThrown()) + return NULL; + + SVN::Pool subPool(pool); + const apr_array_header_t* paths = + build_string_array(paths_iter, true, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + svn_mergeinfo_catalog_t catalog; + SVN_JNI_ERR(svn_ra_get_mergeinfo( + m_session, &catalog, paths, svn_revnum_t(jrevision), + EnumMapper::toMergeinfoInheritance(jinherit), + bool(jinclude_descendants), + subPool.getPool()), + NULL); + if (catalog == NULL) + return NULL; + + JNIEnv* env = JNIUtil::getEnv(); + jclass cls = env->FindClass("java/util/HashMap"); + if (JNIUtil::isExceptionThrown()) + return NULL; + + static jmethodID ctor_mid = 0; + if (0 == ctor_mid) + { + ctor_mid = env->GetMethodID(cls, "<init>", "()V"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + static jmethodID put_mid = 0; + if (0 == put_mid) + { + put_mid = env->GetMethodID(cls, "put", + "(Ljava/lang/Object;" + "Ljava/lang/Object;)" + "Ljava/lang/Object;"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + jobject jcatalog = env->NewObject(cls, ctor_mid); + if (JNIUtil::isExceptionThrown()) + return NULL; + + for (apr_hash_index_t* hi = apr_hash_first(subPool.getPool(), catalog); + hi; hi = apr_hash_next(hi)) + { + const void *v_key; + void *v_val; + apr_hash_this(hi, &v_key, NULL, &v_val); + const char* key = static_cast<const char*>(v_key); + svn_mergeinfo_t val = static_cast<svn_mergeinfo_t>(v_val); + + jstring jpath = JNIUtil::makeJString(key); + if (JNIUtil::isExceptionThrown()) + return NULL; + jobject jmergeinfo = CreateJ::Mergeinfo(val, subPool.getPool()); + if (JNIUtil::isExceptionThrown()) + return NULL; + + env->CallObjectMethod(jcatalog, put_mid, jpath, jmergeinfo); + if (JNIUtil::isExceptionThrown()) + return NULL; + + env->DeleteLocalRef(jpath); + env->DeleteLocalRef(jmergeinfo); + } + + return jcatalog; +} + +// TODO: update +// TODO: switch + +namespace { +svn_error_t* +status_unlock_func(void* baton, const char* path, apr_pool_t* scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) status_unlock_func('%s')\n", path); + return SVN_NO_ERROR; +} + +svn_error_t* +status_fetch_props_func(apr_hash_t **props, void *baton, + const char *path, svn_revnum_t base_revision, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) status_fetch_props_func('%s', r%lld)\n", + //DEBUG: path, static_cast<long long>(base_revision)); + *props = apr_hash_make(result_pool); + return SVN_NO_ERROR; +} + +svn_error_t* +status_fetch_base_func(const char **filename, void *baton, + const char *path, svn_revnum_t base_revision, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) status_fetch_base_func('%s', r%lld)\n", + //DEBUG: path, static_cast<long long>(base_revision)); + *filename = NULL; + return SVN_NO_ERROR; +} + +svn_error_t* +status_start_edit_func(void* baton, svn_revnum_t start_revision) +{ + //DEBUG:fprintf(stderr, " (n) status_start_edit_func(r%lld)\n", + //DEBUG: static_cast<long long>(start_revision)); + return SVN_NO_ERROR; +} + +svn_error_t* +status_target_revision_func(void* baton, svn_revnum_t target_revision, + apr_pool_t* scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) status_target_revision_func(r%lld)\n", + //DEBUG: static_cast<long long>(target_revision)); + *static_cast<svn_revnum_t*>(baton) = target_revision; + return SVN_NO_ERROR; +} + +const EditorProxyCallbacks template_status_editor_callbacks = { + status_unlock_func, + status_fetch_props_func, + status_fetch_base_func, + { status_start_edit_func, status_target_revision_func, NULL }, + NULL +}; +} // anonymous namespace + +void +RemoteSession::status(jobject jthis, jstring jstatus_target, + jlong jrevision, jobject jdepth, + jobject jstatus_editor, jobject jreporter) +{ + StateReporter *rp = StateReporter::getCppObject(jreporter); + CPPADDR_NULL_PTR(rp,); + + SVN::Pool scratchPool(rp->get_report_pool()); + Relpath status_target(jstatus_target, scratchPool); + if (JNIUtil::isExceptionThrown()) + return; + + apr_pool_t* scratch_pool = scratchPool.getPool(); + const char* repos_root_url; + SVN_JNI_ERR(svn_ra_get_repos_root2(m_session, &repos_root_url, + scratch_pool),); + const char* session_root_url; + SVN_JNI_ERR(svn_ra_get_session_url(m_session, &session_root_url, + scratch_pool),); + const char* base_relpath; + SVN_JNI_ERR(svn_ra_get_path_relative_to_root(m_session, &base_relpath, + session_root_url, + scratch_pool),); + + EditorProxyCallbacks proxy_callbacks = + template_status_editor_callbacks; + proxy_callbacks.m_extra_baton.baton = &rp->m_target_revision; + + apr_pool_t* report_pool = rp->get_report_pool(); + std::auto_ptr<EditorProxy> editor( + new EditorProxy(jstatus_editor, report_pool, + repos_root_url, base_relpath, + m_context->checkCancel, m_context, + proxy_callbacks)); + if (JNIUtil::isExceptionThrown()) + return; + + const svn_ra_reporter3_t* raw_reporter; + void* report_baton; + SVN_JNI_ERR(svn_ra_do_status2(m_session, + &raw_reporter, &report_baton, + status_target.c_str(), + svn_revnum_t(jrevision), + EnumMapper::toDepth(jdepth), + editor->delta_editor(), + editor->delta_baton(), + report_pool),); + rp->set_reporter_data(raw_reporter, report_baton, editor.release()); +} + +// TODO: diff + +void +RemoteSession::getLog(jobject jpaths, + jlong jstartrev, jlong jendrev, jint jlimit, + jboolean jstrict_node_history, + jboolean jdiscover_changed_paths, + jboolean jinclude_merged_revisions, + jobject jrevprops, jobject jlog_callback) +{ + Iterator pathiter(jpaths); + if (JNIUtil::isJavaExceptionThrown()) + return; + Iterator revpropiter(jrevprops); + if (JNIUtil::isJavaExceptionThrown()) + return; + LogMessageCallback receiver(jlog_callback); + + SVN::Pool subPool(pool); + const apr_array_header_t* paths = build_string_array(pathiter, + true, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + const apr_array_header_t* revprops = (jrevprops != NULL) + ? build_string_array(revpropiter, + false, subPool) + : NULL; + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN_JNI_ERR(svn_ra_get_log2(m_session, paths, + svn_revnum_t(jstartrev), svn_revnum_t(jendrev), + int(jlimit), + bool(jdiscover_changed_paths), + bool(jstrict_node_history), + bool(jinclude_merged_revisions), + revprops, + receiver.callback, &receiver, + subPool.getPool()),); +} + +jobject +RemoteSession::checkPath(jstring jpath, jlong jrevision) +{ + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(path.error_occurred(), NULL); + + svn_node_kind_t kind; + SVN_JNI_ERR(svn_ra_check_path(m_session, path.c_str(), + svn_revnum_t(jrevision), + &kind, subPool.getPool()), + NULL); + + return EnumMapper::mapNodeKind(kind); +} + +jobject +RemoteSession::stat(jstring jpath, jlong jrevision) +{ + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(path.error_occurred(), NULL); + + svn_dirent_t* dirent; + SVN_JNI_ERR(svn_ra_stat(m_session, path.c_str(), + svn_revnum_t(jrevision), + &dirent, subPool.getPool()), + NULL); + + if (dirent) + return CreateJ::DirEntry(path.c_str(), path.c_str(), dirent); + return NULL; +} + +namespace { +apr_array_header_t* +long_iterable_to_revnum_array(jobject jlong_iterable, apr_pool_t* pool) +{ + + JNIEnv* env = JNIUtil::getEnv(); + + jclass cls = env->FindClass("java/lang/Long"); + if (JNIUtil::isExceptionThrown()) + return NULL; + + static jmethodID mid = 0; + if (0 == mid) + { + mid = env->GetMethodID(cls, "longValue", "()J"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + apr_array_header_t* array = apr_array_make(pool, 0, sizeof(svn_revnum_t)); + Iterator iter(jlong_iterable); + while (iter.hasNext()) + { + const jlong entry = env->CallLongMethod(iter.next(), mid); + if (JNIUtil::isExceptionThrown()) + return NULL; + APR_ARRAY_PUSH(array, svn_revnum_t) = svn_revnum_t(entry); + } + return array; +} + +jobject +location_hash_to_map(apr_hash_t* locations, apr_pool_t* scratch_pool) +{ + JNIEnv* env = JNIUtil::getEnv(); + + jclass long_cls = env->FindClass("java/lang/Long"); + if (JNIUtil::isExceptionThrown()) + return NULL; + + static jmethodID long_ctor = 0; + if (0 == long_ctor) + { + long_ctor = env->GetMethodID(long_cls, "<init>", "(J)V"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + jclass hash_cls = env->FindClass("java/util/HashMap"); + if (JNIUtil::isExceptionThrown()) + return NULL; + + static jmethodID hash_ctor = 0; + if (0 == hash_ctor) + { + hash_ctor = env->GetMethodID(hash_cls, "<init>", "()V"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + static jmethodID hash_put = 0; + if (0 == hash_put) + { + hash_put = env->GetMethodID(hash_cls, "put", + "(Ljava/lang/Object;Ljava/lang/Object;" + ")Ljava/lang/Object;"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + jobject result = env->NewObject(hash_cls, hash_ctor); + if (JNIUtil::isExceptionThrown()) + return NULL; + + if (!locations) + return result; + + for (apr_hash_index_t* hi = apr_hash_first(scratch_pool, locations); + hi; hi = apr_hash_next(hi)) + { + const void* key; + void* val; + + apr_hash_this(hi, &key, NULL, &val); + + jobject jkey = env->NewObject( + long_cls, long_ctor, jlong(*static_cast<const svn_revnum_t*>(key))); + if (JNIUtil::isExceptionThrown()) + return NULL; + jstring jval = JNIUtil::makeJString(static_cast<const char*>(val)); + if (JNIUtil::isExceptionThrown()) + return NULL; + + env->CallObjectMethod(result, hash_put, jkey, jval); + if (JNIUtil::isExceptionThrown()) + return NULL; + + env->DeleteLocalRef(jkey); + env->DeleteLocalRef(jval); + } + + return result; +} +} // anonymous namespace + +jobject +RemoteSession::getLocations(jstring jpath, jlong jpeg_revision, + jobject jlocation_revisions) +{ + if (!jpath || !jlocation_revisions) + return NULL; + + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(path.error_occurred(), NULL); + + apr_array_header_t* location_revisions = + long_iterable_to_revnum_array(jlocation_revisions, subPool.getPool()); + if (!location_revisions) + return NULL; + + apr_hash_t* locations; + SVN_JNI_ERR(svn_ra_get_locations(m_session, &locations, + path.c_str(), svn_revnum_t(jpeg_revision), + location_revisions, subPool.getPool()), + NULL); + return location_hash_to_map(locations, subPool.getPool()); +} + +namespace { +class LocationSegmentHandler +{ +public: + static svn_error_t* callback(svn_location_segment_t* segment, + void* baton, apr_pool_t*) + { + LocationSegmentHandler* const self = + static_cast<LocationSegmentHandler*>(baton); + SVN_ERR_ASSERT(self->m_jcallback != NULL); + self->call(segment); + SVN_ERR(JNIUtil::checkJavaException(SVN_ERR_BASE)); + return SVN_NO_ERROR; + } + + LocationSegmentHandler(jobject jcallback) + : m_jcallback(jcallback), + m_call_mid(0) + { + JNIEnv* env = JNIUtil::getEnv(); + jclass cls = env->GetObjectClass(jcallback); + if (JNIUtil::isJavaExceptionThrown()) + return; + + m_call_mid = env->GetMethodID( + cls, "doSegment", "(" JAVAHL_ARG("/ISVNRemote$LocationSegment;") ")V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + +private: + void call(svn_location_segment_t* segment) + { + JNIEnv* env = JNIUtil::getEnv(); + jclass cls = env->FindClass(JAVAHL_CLASS("/ISVNRemote$LocationSegment")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID mid = 0; + if (mid == 0) + { + mid = env->GetMethodID(cls, "<init>", + "(Ljava/lang/String;JJ)V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jstring jpath = JNIUtil::makeJString(segment->path); + if (JNIUtil::isJavaExceptionThrown()) + return; + + env->CallVoidMethod(m_jcallback, m_call_mid, + env->NewObject(cls, mid, jpath, + jlong(segment->range_start), + jlong(segment->range_end))); + if (JNIUtil::isJavaExceptionThrown()) + return; + env->DeleteLocalRef(jpath); + } + + jobject m_jcallback; + jmethodID m_call_mid; +}; +} // anonymous namespace + +void +RemoteSession::getLocationSegments(jstring jpath, jlong jpeg_revision, + jlong jstart_revision, jlong jend_revision, + jobject jcallback) +{ + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(path.error_occurred(), ); + + LocationSegmentHandler handler(jcallback); + if (JNIUtil::isExceptionThrown()) + return ; + + SVN_JNI_ERR(svn_ra_get_location_segments(m_session, path.c_str(), + svn_revnum_t(jpeg_revision), + svn_revnum_t(jstart_revision), + svn_revnum_t(jend_revision), + handler.callback, &handler, + subPool.getPool()),); +} + +namespace { +class FileRevisionHandler +{ +public: + static svn_error_t* callback(void* baton, + const char* path, svn_revnum_t revision, + apr_hash_t* revision_props, + // We ignore the deltas as they're not + // exposed in the JavaHL API. + svn_boolean_t result_of_merge, + svn_txdelta_window_handler_t* delta_handler, + void** delta_handler_baton, + apr_array_header_t* prop_diffs, + apr_pool_t* scratch_pool) + { + if (delta_handler) + *delta_handler = svn_delta_noop_window_handler; + if (delta_handler_baton) + *delta_handler_baton = NULL; + + FileRevisionHandler* const self = + static_cast<FileRevisionHandler*>(baton); + SVN_ERR_ASSERT(self->m_jcallback != NULL); + self->call(path, revision, revision_props, + result_of_merge, prop_diffs, + (delta_handler != NULL), + scratch_pool); + SVN_ERR(JNIUtil::checkJavaException(SVN_ERR_BASE)); + return SVN_NO_ERROR; + } + + FileRevisionHandler(jobject jcallback) + : m_jcallback(jcallback), + m_call_mid(0) + { + JNIEnv* env = JNIUtil::getEnv(); + jclass cls = env->GetObjectClass(jcallback); + if (JNIUtil::isJavaExceptionThrown()) + return; + + m_call_mid = env->GetMethodID( + cls, "doRevision", "(" JAVAHL_ARG("/ISVNRemote$FileRevision;") ")V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + +private: + void call(const char* path, svn_revnum_t revision, + apr_hash_t* revision_props, + svn_boolean_t result_of_merge, + apr_array_header_t* prop_diffs, + svn_boolean_t has_text_delta, + apr_pool_t* scratch_pool) + { + JNIEnv* env = JNIUtil::getEnv(); + jclass cls = env->FindClass(JAVAHL_CLASS("/ISVNRemote$FileRevision")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID mid = 0; + if (mid == 0) + { + mid = env->GetMethodID(cls, "<init>", + "(Ljava/lang/String;JZ" + "Ljava/util/Map;Ljava/util/Map;Z)V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jstring jpath = JNIUtil::makeJString(path); + if (JNIUtil::isJavaExceptionThrown()) + return; + jobject jrevprops = CreateJ::PropertyMap(revision_props, scratch_pool); + if (JNIUtil::isJavaExceptionThrown()) + return; + jobject jpropdelta = CreateJ::PropertyMap(prop_diffs, scratch_pool); + if (JNIUtil::isJavaExceptionThrown()) + return; + + env->CallVoidMethod(m_jcallback, m_call_mid, + env->NewObject(cls, mid, jpath, jlong(revision), + jboolean(result_of_merge), + jrevprops, jpropdelta, + jboolean(has_text_delta))); + if (JNIUtil::isJavaExceptionThrown()) + return; + env->DeleteLocalRef(jpath); + env->DeleteLocalRef(jrevprops); + env->DeleteLocalRef(jpropdelta); + } + + jobject m_jcallback; + jmethodID m_call_mid; +}; +} // anonymous namespace + +void +RemoteSession::getFileRevisions(jstring jpath, + jlong jstart_revision, jlong jend_revision, + jboolean jinclude_merged_revisions, + jobject jcallback) +{ + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(path.error_occurred(), ); + + FileRevisionHandler handler(jcallback); + if (JNIUtil::isExceptionThrown()) + return; + + SVN_JNI_ERR(svn_ra_get_file_revs2(m_session, path.c_str(), + svn_revnum_t(jstart_revision), + svn_revnum_t(jend_revision), + bool(jinclude_merged_revisions), + handler.callback, &handler, + subPool.getPool()),); +} + +// TODO: lock +// TODO: unlock +// TODO: getLock + +jobject +RemoteSession::getLocks(jstring jpath, jobject jdepth) +{ + svn_depth_t depth = EnumMapper::toDepth(jdepth); + if (JNIUtil::isExceptionThrown()) + return NULL; + + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isExceptionThrown()) + return NULL; + SVN_JNI_ERR(path.error_occurred(), NULL); + + apr_hash_t *locks; + SVN_JNI_ERR(svn_ra_get_locks2(m_session, &locks, path.c_str(), depth, + subPool.getPool()), + NULL); + + return CreateJ::LockMap(locks, subPool.getPool()); +} + +// TODO: replayRange +// TODO: replay +// TODO: getDeletedRevision +// TODO: getInheritedProperties + +jboolean +RemoteSession::hasCapability(jstring jcapability) +{ + JNIStringHolder capability(jcapability); + if (JNIUtil::isExceptionThrown()) + return false; + + SVN::Pool subPool(pool); + svn_boolean_t has; + SVN_JNI_ERR(svn_ra_has_capability(m_session, &has, capability, + subPool.getPool()), + false); + + return jboolean(has); +} |