diff options
Diffstat (limited to 'subversion/bindings/javahl/native')
127 files changed, 18025 insertions, 2110 deletions
diff --git a/subversion/bindings/javahl/native/AuthnCallback.cpp b/subversion/bindings/javahl/native/AuthnCallback.cpp new file mode 100644 index 0000000..3a25e04 --- /dev/null +++ b/subversion/bindings/javahl/native/AuthnCallback.cpp @@ -0,0 +1,365 @@ +/** + * @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 + */ + +#include "svn_base64.h" +#include "svn_x509.h" + +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_exception.hpp" +#include "jniwrapper/jni_string.hpp" +#include "jniwrapper/jni_array.hpp" +#include "jniwrapper/jni_list.hpp" + +#include "AuthnCallback.hpp" + +#include "svn_private_config.h" + +namespace JavaHL { + +// Class JavaHL::AuthnCallback +const char* const AuthnCallback::m_class_name = + JAVAHL_CLASS("/callback/AuthnCallback"); + +AuthnCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_username_prompt( + env.GetMethodID(cls, "usernamePrompt", + "(Ljava/lang/String;Z)" + JAVAHL_ARG("/callback/AuthnCallback") + "$UsernameResult;")), + m_mid_user_password_prompt( + env.GetMethodID(cls, "userPasswordPrompt", + "(Ljava/lang/String;Ljava/lang/String;Z)" + JAVAHL_ARG("/callback/AuthnCallback") + "$UserPasswordResult;")), + m_mid_ssl_server_trust_prompt( + env.GetMethodID(cls, "sslServerTrustPrompt", + "(Ljava/lang/String;" + JAVAHL_ARG("/callback/AuthnCallback") + "$SSLServerCertFailures;" + JAVAHL_ARG("/callback/AuthnCallback") + "$SSLServerCertInfo;" + "Z)" + JAVAHL_ARG("/callback/AuthnCallback") + "$SSLServerTrustResult;")), + m_mid_ssl_client_cert_prompt( + env.GetMethodID(cls, "sslClientCertPrompt", + "(Ljava/lang/String;Z)" + JAVAHL_ARG("/callback/AuthnCallback") + "$SSLClientCertResult;")), + m_mid_ssl_client_cert_passphrase_prompt( + env.GetMethodID(cls, "sslClientCertPassphrasePrompt", + "(Ljava/lang/String;Z)" + JAVAHL_ARG("/callback/AuthnCallback") + "$SSLClientCertPassphraseResult;")), + m_mid_allow_store_plaintext_password( + env.GetMethodID(cls, "allowStorePlaintextPassword", + "(Ljava/lang/String;)Z")), + m_mid_allow_store_plaintext_passphrase( + env.GetMethodID(cls, "allowStorePlaintextPassphrase", + "(Ljava/lang/String;)Z")) +{} + +AuthnCallback::ClassImpl::~ClassImpl() {} + +jobject AuthnCallback::username_prompt(const ::Java::String& realm, + bool may_save) +{ + return m_env.CallObjectMethod(m_jthis, impl().m_mid_username_prompt, + realm.get(), jboolean(may_save)); +} + + +jobject AuthnCallback::user_password_prompt(const ::Java::String& realm, + const ::Java::String& username, + bool may_save) +{ + return m_env.CallObjectMethod(m_jthis, impl().m_mid_user_password_prompt, + realm.get(), username.get(), + jboolean(may_save)); +} + +jobject AuthnCallback::ssl_server_trust_prompt( + const ::Java::String& realm, + const SSLServerCertFailures& failures, + const SSLServerCertInfo& info, + bool may_save) +{ + return m_env.CallObjectMethod(m_jthis, impl().m_mid_ssl_server_trust_prompt, + realm.get(), failures.get(), info.get(), + jboolean(may_save)); +} + +jobject AuthnCallback::ssl_client_cert_prompt(const ::Java::String& + realm, bool may_save) +{ + return m_env.CallObjectMethod(m_jthis, impl().m_mid_ssl_client_cert_prompt, + realm.get(), jboolean(may_save)); +} + +jobject AuthnCallback::ssl_client_cert_passphrase_prompt( + const ::Java::String& realm, + bool may_save) +{ + return m_env.CallObjectMethod(m_jthis, + impl().m_mid_ssl_client_cert_passphrase_prompt, + realm.get(), jboolean(may_save)); +} + +bool AuthnCallback::allow_store_plaintext_password(const ::Java::String& realm) +{ + return m_env.CallBooleanMethod(m_jthis, + impl().m_mid_allow_store_plaintext_password, + realm.get()); +} + +bool AuthnCallback::allow_store_plaintext_passphrase(const ::Java::String& realm) +{ + return m_env.CallBooleanMethod(m_jthis, + impl().m_mid_allow_store_plaintext_passphrase, + realm.get()); +} + + +// Class JavaHL::AuthnCallback::AuthnResult +const char* const AuthnCallback::AuthnResult::m_class_name = + JAVAHL_CLASS("/callback/AuthnCallback$AuthnResult"); + +AuthnCallback::AuthnResult::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_fid_save(env.GetFieldID(cls, "save", "Z")), + m_fid_trust(env.GetFieldID(cls, "trust", "Z")), + m_fid_identity(env.GetFieldID(cls, "identity", "Ljava/lang/String;")), + m_fid_secret(env.GetFieldID(cls, "secret", "Ljava/lang/String;")) +{} + +AuthnCallback::AuthnResult::ClassImpl::~ClassImpl() {} + +// Class JavaHL::AuthnCallback::SSLServerCertFailures +const char* const AuthnCallback::SSLServerCertFailures::m_class_name = + JAVAHL_CLASS("/callback/AuthnCallback$SSLServerCertFailures"); + +AuthnCallback::SSLServerCertFailures::ClassImpl::ClassImpl( + ::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_ctor(env.GetMethodID(cls, "<init>", "(I)V")) +{} + +AuthnCallback::SSLServerCertFailures::ClassImpl::~ClassImpl() {} + +AuthnCallback::SSLServerCertFailures::SSLServerCertFailures( + ::Java::Env env, jint failures) + : ::Java::Object(env, + ::Java::ClassCache::get_authn_ssl_server_cert_failures(env)) +{ + set_this(env.NewObject(get_class(), impl().m_mid_ctor, failures)); +} + + +// Class JavaHL::AuthnCallback::SSLServerCertInfo +const char* const AuthnCallback::SSLServerCertInfo::m_class_name = + JAVAHL_CLASS("/callback/AuthnCallback$SSLServerCertInfo"); + +AuthnCallback::SSLServerCertInfo::ClassImpl::ClassImpl( + ::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_ctor(env.GetMethodID(cls, "<init>", + "(Ljava/lang/String;" + "Ljava/lang/String;JJ[B" + "Ljava/util/List;" + "Ljava/lang/String;)V")) +{} + +AuthnCallback::SSLServerCertInfo::ClassImpl::~ClassImpl() {} + +AuthnCallback::SSLServerCertInfo::SSLServerCertInfo( + ::Java::Env env, const char* ascii_cert) + : ::Java::Object(env, + ::Java::ClassCache::get_authn_ssl_server_cert_info(env)) +{ + SVN::Pool pool; + + /* Convert header-less PEM to DER by undoing base64 encoding. */ + const svn_string_t cert_string = { ascii_cert, strlen(ascii_cert) }; + const svn_string_t* der = svn_base64_decode_string(&cert_string, + pool.getPool()); + + svn_x509_certinfo_t *certinfo; + SVN_JAVAHL_CHECK(env, svn_x509_parse_cert(&certinfo, der->data, der->len, + pool.getPool(), pool.getPool())); + + const ::Java::String subject( + env, svn_x509_certinfo_get_subject(certinfo, pool.getPool())); + const ::Java::String issuer( + env, svn_x509_certinfo_get_issuer(certinfo, pool.getPool())); + const ::Java::String cert(env, ascii_cert); + const jlong valid_from = + (jlong(svn_x509_certinfo_get_valid_from(certinfo)) + 500) / 1000; + const jlong valid_to = + (jlong(svn_x509_certinfo_get_valid_to(certinfo)) + 500) / 1000; + + const svn_checksum_t* digest = svn_x509_certinfo_get_digest(certinfo); + jsize digest_size; + switch (digest->kind) + { + case svn_checksum_sha1: + digest_size = 160 / 8; + break; + + case svn_checksum_md5: + digest_size = 128 / 8; + break; + + default: + digest_size = 0; // Initialize this to avoid compiler warnings + ::Java::IllegalArgumentException(env).raise( + _("Unknown certificate digest type")); + } + const ::Java::ByteArray fingerprint(env, digest->digest, digest_size); + + jobject jhostnames = NULL; + const apr_array_header_t* hostnames = + svn_x509_certinfo_get_hostnames(certinfo); + if (hostnames) + { + ::Java::List< ::Java::String> hn(env, hostnames->nelts); + for (int i = 0; i < hostnames->nelts; ++i) + hn.add(::Java::String(env, APR_ARRAY_IDX(hostnames, i, const char*))); + jhostnames = hn.get(); + } + + set_this(env.NewObject(get_class(), impl().m_mid_ctor, + subject.get(), issuer.get(), + valid_from, valid_to, + fingerprint.get(), + jhostnames, + cert.get())); +} + + +// Class JavaHL::UserPasswordCallback +const char* const UserPasswordCallback::m_class_name = + JAVAHL_CLASS("/callback/UserPasswordCallback"); + +UserPasswordCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_ask_trust_ssl_server( + env.GetMethodID(cls, "askTrustSSLServer", + "(Ljava/lang/String;Z)I")), + m_mid_prompt_2arg( + env.GetMethodID(cls, "prompt", + "(Ljava/lang/String;Ljava/lang/String;)Z")), + m_mid_ask_yes_no( + env.GetMethodID(cls, "askYesNo", + "(Ljava/lang/String;Ljava/lang/String;Z)Z")), + m_mid_ask_question_3arg( + env.GetMethodID(cls, "askQuestion", + "(Ljava/lang/String;Ljava/lang/String;Z)" + "Ljava/lang/String;")), + m_mid_get_username( + env.GetMethodID(cls, "getUsername", + "()Ljava/lang/String;")), + m_mid_get_password( + env.GetMethodID(cls, "getPassword", + "()Ljava/lang/String;")), + m_mid_prompt( + env.GetMethodID(cls, "prompt", + "(Ljava/lang/String;Ljava/lang/String;Z)Z")), + m_mid_ask_question( + env.GetMethodID(cls, "askQuestion", + "(Ljava/lang/String;Ljava/lang/String;ZZ)" + "Ljava/lang/String;")), + m_mid_user_allowed_save( + env.GetMethodID(cls, "userAllowedSave", "()Z")) +{} + +UserPasswordCallback::ClassImpl::~ClassImpl() {} + +jint UserPasswordCallback::ask_trust_ssl_server(const ::Java::String& info, + bool allow_permanently) +{ + return m_env.CallIntMethod(m_jthis, impl().m_mid_ask_trust_ssl_server, + info.get(), jboolean(allow_permanently)); +} + +bool UserPasswordCallback::prompt(const ::Java::String& realm, + const ::Java::String& username) +{ + return m_env.CallBooleanMethod(m_jthis, impl().m_mid_prompt_2arg, + realm.get(), username.get()); +} + +bool UserPasswordCallback::ask_yes_no(const ::Java::String& realm, + const ::Java::String& question, + bool yes_is_default) +{ + return m_env.CallBooleanMethod(m_jthis, impl().m_mid_ask_yes_no, + realm.get(), question.get(), + jboolean(yes_is_default)); +} + +jstring UserPasswordCallback::ask_question(const ::Java::String& realm, + const ::Java::String& question, + bool show_answer) +{ + return jstring(m_env.CallObjectMethod(m_jthis, + impl().m_mid_ask_question_3arg, + realm.get(), question.get(), + jboolean(show_answer))); +} + +jstring UserPasswordCallback::get_username() +{ + return jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_get_username)); +} + +jstring UserPasswordCallback::get_password() +{ + return jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_get_password)); +} + +bool UserPasswordCallback::prompt(const ::Java::String& realm, + const ::Java::String& username, + bool may_save) +{ + return m_env.CallBooleanMethod(m_jthis, impl().m_mid_prompt, + realm.get(), username.get(), + jboolean(may_save)); +} + +jstring UserPasswordCallback::ask_question(const ::Java::String& realm, + const ::Java::String& question, + bool show_answer, bool may_save) +{ + return jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_ask_question, + realm.get(), question.get(), + jboolean(show_answer), + jboolean(may_save))); +} + +bool UserPasswordCallback::user_allowed_save() +{ + return m_env.CallBooleanMethod(m_jthis, impl().m_mid_user_allowed_save); +} + +} // namespace JavaHL diff --git a/subversion/bindings/javahl/native/AuthnCallback.hpp b/subversion/bindings/javahl/native/AuthnCallback.hpp new file mode 100644 index 0000000..f30d004 --- /dev/null +++ b/subversion/bindings/javahl/native/AuthnCallback.hpp @@ -0,0 +1,376 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_AUTHN_CALLBACK_HPP +#define SVN_JAVAHL_AUTHN_CALLBACK_HPP + +#include "svn_auth.h" + +#include "Pool.h" + +#include "jniwrapper/jni_object.hpp" +#include "jniwrapper/jni_string.hpp" + +namespace JavaHL { + +/** + * Object wrapper for @c org.apache.subversion.javahl.callback.AuthnCallback. + * + * @since New in 1.9. + */ +class AuthnCallback : public ::Java::Object +{ +public: + /** + * Object wrapper for @c ...AuthnCallback$AuthnResult. + */ + class AuthnResult : public ::Java::Object + { + public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit AuthnResult(::Java::Env env, jobject jthis) + : ::Java::Object(env, ::Java::ClassCache::get_authn_result(env), jthis) + {} + + bool save() const + { + return (0 != m_env.GetBooleanField(m_jthis, impl().m_fid_save)); + } + + bool trust() const + { + return (0 != m_env.GetBooleanField(m_jthis, impl().m_fid_trust)); + } + + jstring identity() const + { + return jstring(m_env.GetObjectField(m_jthis, impl().m_fid_identity)); + } + + jstring secret() const + { + return jstring(m_env.GetObjectField(m_jthis, impl().m_fid_secret)); + } + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::FieldID m_fid_save; + const ::Java::FieldID m_fid_trust; + const ::Java::FieldID m_fid_identity; + const ::Java::FieldID m_fid_secret; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + }; + + + /** + * Object wrapper for @c ...AuthnCallback$SSLServerCertFailures. + */ + class SSLServerCertFailures : public ::Java::Object + { + public: + /** + * Creates and initializes a wrapped object; + * @a failures is a set of flags. + */ + explicit SSLServerCertFailures(::Java::Env env, jint failures); + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_ctor; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + }; + + + /** + * Object wrapper for @c ...AuthnCallback$SSLServerCertInfo. + */ + class SSLServerCertInfo : public ::Java::Object + { + public: + /** + * Creates and initializes a wrapped object; + */ + explicit SSLServerCertInfo(::Java::Env env, const char* ascii_cert); + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_ctor; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + }; + + + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit AuthnCallback(::Java::Env env, jobject jthis) + : ::Java::Object(env, ::Java::ClassCache::get_authn_cb(env), jthis) + {} + + /** + * Invokes the Java method AuthnCallback.usernamePrompt(). + */ + jobject username_prompt(const ::Java::String& realm, bool may_save); + + + /** + * Invokes the Java method AuthnCallback.userPasswordPrompt(). + */ + jobject user_password_prompt(const ::Java::String& realm, + const ::Java::String& username, + bool may_save); + + /** + * Invokes the Java method AuthnCallback.sslServerTrustPrompt(). + */ + jobject ssl_server_trust_prompt(const ::Java::String& realm, + const SSLServerCertFailures& failures, + const SSLServerCertInfo& info, + bool may_save); + + /** + * Invokes the Java method AuthnCallback.sslClientCertPrompt(). + */ + jobject ssl_client_cert_prompt(const ::Java::String& realm, bool may_save); + + /** + * Invokes the Java method AuthnCallback.sslClientCertPassphrasePrompt(). + */ + jobject ssl_client_cert_passphrase_prompt(const ::Java::String& realm, + bool may_save); + + /** + * Invokes the Java method AuthnCallback.allowStorePlaintextPassword(). + */ + bool allow_store_plaintext_password(const ::Java::String& realm); + + /** + * Invokes the Java method AuthnCallback.allowStorePlaintextPassphrase(). + */ + bool allow_store_plaintext_passphrase(const ::Java::String& realm); + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_username_prompt; + const ::Java::MethodID m_mid_user_password_prompt; + const ::Java::MethodID m_mid_ssl_server_trust_prompt; + const ::Java::MethodID m_mid_ssl_client_cert_prompt; + const ::Java::MethodID m_mid_ssl_client_cert_passphrase_prompt; + const ::Java::MethodID m_mid_allow_store_plaintext_password; + const ::Java::MethodID m_mid_allow_store_plaintext_passphrase; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + + +/** + * Object wrapper for the deprecated interface + * @c org.apache.subversion.javahl.callback.UserPasswordCallback. + * + * @since New in 1.9. + */ +class UserPasswordCallback : public ::Java::Object +{ +public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit UserPasswordCallback(::Java::Env env, jobject jthis) + : ::Java::Object(env, ::Java::ClassCache::get_user_passwd_cb(env), jthis) + {} + + /** + * Invokes the Java method UserPasswordCallback.askTrustSSLServer(). + */ + jint ask_trust_ssl_server(const ::Java::String& info, + bool allow_permanently); + + /** + * Invokes the Java method UserPasswordCallback.prompt(). + */ + bool prompt(const ::Java::String& realm, + const ::Java::String& username); + + /** + * Invokes the Java method UserPasswordCallback.askYesNo(). + */ + bool ask_yes_no(const ::Java::String& realm, + const ::Java::String& question, + bool yes_is_default); + + /** + * Invokes the Java method UserPasswordCallback.askQuestion(). + */ + jstring ask_question(const ::Java::String& realm, + const ::Java::String& question, + bool show_answer); + + /** + * Invokes the Java method UserPasswordCallback.getUsername(). + */ + jstring get_username(); + + /** + * Invokes the Java method UserPasswordCallback.getPassword(). + */ + jstring get_password(); + + /** + * Invokes the Java method UserPasswordCallback.prompt(). + */ + bool prompt(const ::Java::String& realm, + const ::Java::String& username, + bool may_save); + + /** + * Invokes the Java method UserPasswordCallback.askQuestion(). + */ + jstring ask_question(const ::Java::String& realm, + const ::Java::String& question, + bool show_answer, bool may_save); + + /** + * Invokes the Java method UserPasswordCallback.userAllowedSave(). + */ + bool user_allowed_save(); + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_ask_trust_ssl_server; + const ::Java::MethodID m_mid_prompt_2arg; + const ::Java::MethodID m_mid_ask_yes_no; + const ::Java::MethodID m_mid_ask_question_3arg; + const ::Java::MethodID m_mid_get_username; + const ::Java::MethodID m_mid_get_password; + const ::Java::MethodID m_mid_prompt; + const ::Java::MethodID m_mid_ask_question; + const ::Java::MethodID m_mid_user_allowed_save; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace JavaHL + +#endif // SVN_JAVAHL_AUTHN_CALLBACK_HPP diff --git a/subversion/bindings/javahl/native/BlameCallback.cpp b/subversion/bindings/javahl/native/BlameCallback.cpp index 5973707..10a8ff7 100644 --- a/subversion/bindings/javahl/native/BlameCallback.cpp +++ b/subversion/bindings/javahl/native/BlameCallback.cpp @@ -92,7 +92,7 @@ BlameCallback::singleLine(svn_revnum_t start_revnum, svn_revnum_t end_revnum, static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/BlameCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/BlameCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); @@ -104,14 +104,14 @@ BlameCallback::singleLine(svn_revnum_t start_revnum, svn_revnum_t end_revnum, } // convert the parameters to their Java relatives - jobject jrevProps = CreateJ::PropertyMap(revProps); + jobject jrevProps = CreateJ::PropertyMap(revProps, pool); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); jobject jmergedRevProps = NULL; if (mergedRevProps != NULL) { - jmergedRevProps = CreateJ::PropertyMap(mergedRevProps); + jmergedRevProps = CreateJ::PropertyMap(mergedRevProps, pool); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); } @@ -128,8 +128,6 @@ BlameCallback::singleLine(svn_revnum_t start_revnum, svn_revnum_t end_revnum, env->CallVoidMethod(m_callback, mid, (jlong)line_no, (jlong)revision, jrevProps, (jlong)mergedRevision, jmergedRevProps, jmergedPath, jline, (jboolean)localChange); - // No need to check for an exception here, because we return anyway. - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } diff --git a/subversion/bindings/javahl/native/ChangelistCallback.cpp b/subversion/bindings/javahl/native/ChangelistCallback.cpp index d04ba32..621de3d 100644 --- a/subversion/bindings/javahl/native/ChangelistCallback.cpp +++ b/subversion/bindings/javahl/native/ChangelistCallback.cpp @@ -78,7 +78,7 @@ ChangelistCallback::doChangelist(const char *path, const char *changelist, // it can be cached. if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ChangelistCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ChangelistCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NOTHING(); diff --git a/subversion/bindings/javahl/native/ClientContext.cpp b/subversion/bindings/javahl/native/ClientContext.cpp index f1babc9..c9f327b 100644 --- a/subversion/bindings/javahl/native/ClientContext.cpp +++ b/subversion/bindings/javahl/native/ClientContext.cpp @@ -26,7 +26,6 @@ #include "svn_client.h" #include "private/svn_wc_private.h" -#include "svn_private_config.h" #include "ClientContext.h" #include "JNIUtil.h" @@ -37,39 +36,13 @@ #include "EnumMapper.h" #include "CommitMessage.h" +#include "svn_private_config.h" ClientContext::ClientContext(jobject jsvnclient, SVN::Pool &pool) - : m_prompter(NULL), - m_cancelOperation(false) + : OperationContext(pool) { - JNIEnv *env = JNIUtil::getEnv(); - - /* Grab a global reference to the Java object embedded in the parent Java - object. */ static jfieldID ctxFieldID = 0; - if (ctxFieldID == 0) - { - jclass clazz = env->GetObjectClass(jsvnclient); - if (JNIUtil::isJavaExceptionThrown()) - return; - - ctxFieldID = env->GetFieldID(clazz, "clientContext", - "L"JAVA_PACKAGE"/SVNClient$ClientContext;"); - if (JNIUtil::isJavaExceptionThrown() || ctxFieldID == 0) - return; - - env->DeleteLocalRef(clazz); - } - - jobject jctx = env->GetObjectField(jsvnclient, ctxFieldID); - if (JNIUtil::isJavaExceptionThrown()) - return; - - m_jctx = env->NewGlobalRef(jctx); - if (JNIUtil::isJavaExceptionThrown()) - return; - - env->DeleteLocalRef(jctx); + attachJavaObject(jsvnclient, JAVAHL_ARG("/SVNClient$ClientContext;"), "clientContext", &ctxFieldID); SVN_JNI_ERR(svn_client_create_context2(&m_context, NULL, pool.getPool()), @@ -96,18 +69,36 @@ ClientContext::ClientContext(jobject jsvnclient, SVN::Pool &pool) m_context->conflict_func2 = resolve; m_context->conflict_baton2 = m_jctx; - m_context->client_name = "javahl"; - m_pool = &pool; + m_context->client_name = getClientName(); + + if (m_jtunnelcb) + { + m_context->check_tunnel_func = checkTunnel; + m_context->open_tunnel_func = openTunnel; + m_context->tunnel_baton = m_jtunnelcb; + } } ClientContext::~ClientContext() { - delete m_prompter; - - JNIEnv *env = JNIUtil::getEnv(); - env->DeleteGlobalRef(m_jctx); } +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->tunnel_baton = m_jtunnelcb; + } + else + { + m_context->check_tunnel_func = NULL; + m_context->open_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() @@ -140,7 +131,6 @@ svn_client_ctx_t * ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool) { apr_pool_t *pool = in_pool.getPool(); - svn_auth_baton_t *ab; svn_client_ctx_t *ctx = m_context; /* Make a temporary copy of ctx to restore at pool cleanup to avoid @@ -157,122 +147,17 @@ ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool) apr_pool_cleanup_register(in_pool.getPool(), bt, clear_ctx_ptrs, clear_ctx_ptrs); - if (!ctx->config) { - const char *configDir = m_configDir.c_str(); - if (m_configDir.empty()) - configDir = NULL; - SVN_JNI_ERR(svn_config_get_config(&(ctx->config), configDir, - m_pool->getPool()), - NULL); + apr_hash_t * configData = getConfigData(); + ctx->config = configData; bt->backup->config = ctx->config; } - svn_config_t *config = - reinterpret_cast<svn_config_t *>(apr_hash_get(ctx->config, - SVN_CONFIG_CATEGORY_CONFIG, - APR_HASH_KEY_STRING)); - - - /* The whole list of registered providers */ - apr_array_header_t *providers; - - /* Populate the registered providers with the platform-specific providers */ - SVN_JNI_ERR(svn_auth_get_platform_specific_client_providers(&providers, - config, - pool), - NULL); - - /* Use the prompter (if available) to prompt for password and cert - * caching. */ - svn_auth_plaintext_prompt_func_t plaintext_prompt_func = NULL; - void *plaintext_prompt_baton = NULL; - svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func; - void *plaintext_passphrase_prompt_baton = NULL; - - if (m_prompter != NULL) - { - plaintext_prompt_func = Prompter::plaintext_prompt; - plaintext_prompt_baton = m_prompter; - plaintext_passphrase_prompt_func = Prompter::plaintext_passphrase_prompt; - plaintext_passphrase_prompt_baton = m_prompter; - } - - /* The main disk-caching auth providers, for both - * 'username/password' creds and 'username' creds. */ - svn_auth_provider_object_t *provider; - - svn_auth_get_simple_provider2(&provider, plaintext_prompt_func, - plaintext_prompt_baton, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - svn_auth_get_username_provider(&provider, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - /* The server-cert, client-cert, and client-cert-password providers. */ - SVN_JNI_ERR(svn_auth_get_platform_specific_provider(&provider, - "windows", - "ssl_server_trust", - pool), - NULL); - - if (provider) - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - svn_auth_get_ssl_server_trust_file_provider(&provider, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - svn_auth_get_ssl_client_cert_file_provider(&provider, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, - plaintext_passphrase_prompt_func, - plaintext_passphrase_prompt_baton, pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - if (m_prompter != NULL) - { - /* Two basic prompt providers: username/password, and just username.*/ - provider = m_prompter->getProviderSimple(in_pool); - - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - provider = m_prompter->getProviderUsername(in_pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - /* Three ssl prompt providers, for server-certs, client-certs, - * and client-cert-passphrases. */ - provider = m_prompter->getProviderServerSSLTrust(in_pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - provider = m_prompter->getProviderClientSSL(in_pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - - provider = m_prompter->getProviderClientSSLPassword(in_pool); - APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; - } - /* Build an authentication baton to give to libsvn_client. */ - svn_auth_open(&ab, providers, pool); - - /* Place any default --username or --password credentials into the - * auth_baton's run-time parameter hash. ### Same with --no-auth-cache? */ - if (!m_userName.empty()) - svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, - apr_pstrdup(in_pool.getPool(), - m_userName.c_str())); - if (!m_passWord.empty()) - svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, - apr_pstrdup(in_pool.getPool(), - m_passWord.c_str())); - /* Store where to retrieve authentication data? */ - if (!m_configDir.empty()) - svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR, - apr_pstrdup(in_pool.getPool(), - m_configDir.c_str())); - - ctx->auth_baton = ab; + ctx->auth_baton = getAuthBaton(in_pool); ctx->log_msg_baton3 = message; - m_cancelOperation = false; + resetCancelRequest(); SVN_JNI_ERR(svn_wc_context_create(&ctx->wc_ctx, NULL, in_pool.getPool(), in_pool.getPool()), @@ -282,60 +167,6 @@ ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool) } void -ClientContext::username(const char *pi_username) -{ - m_userName = (pi_username == NULL ? "" : pi_username); -} - -void -ClientContext::password(const char *pi_password) -{ - m_passWord = (pi_password == NULL ? "" : pi_password); -} - -void -ClientContext::setPrompt(Prompter *prompter) -{ - delete m_prompter; - m_prompter = prompter; -} - -void -ClientContext::setConfigDirectory(const char *configDir) -{ - // A change to the config directory may necessitate creation of - // the config templates. - SVN::Pool requestPool; - SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.getPool()), ); - - m_configDir = (configDir == NULL ? "" : configDir); - m_context->config = NULL; -} - -const char * -ClientContext::getConfigDirectory() const -{ - return m_configDir.c_str(); -} - -void -ClientContext::cancelOperation() -{ - m_cancelOperation = true; -} - -svn_error_t * -ClientContext::checkCancel(void *cancelBaton) -{ - ClientContext *that = static_cast<ClientContext *>(cancelBaton); - if (that->m_cancelOperation) - return svn_error_create(SVN_ERR_CANCELLED, NULL, - _("Operation cancelled")); - else - return SVN_NO_ERROR; -} - -void ClientContext::notify(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool) @@ -351,7 +182,7 @@ ClientContext::notify(void *baton, return; mid = env->GetMethodID(clazz, "onNotify", - "(L"JAVA_PACKAGE"/ClientNotifyInformation;)V"); + "(" JAVAHL_ARG("/ClientNotifyInformation;") ")V"); if (JNIUtil::isJavaExceptionThrown() || mid == 0) return; @@ -369,54 +200,6 @@ ClientContext::notify(void *baton, env->DeleteLocalRef(jInfo); } -void -ClientContext::progress(apr_off_t progressVal, apr_off_t total, - void *baton, apr_pool_t *pool) -{ - jobject jctx = (jobject) baton; - JNIEnv *env = JNIUtil::getEnv(); - - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return; - - static jmethodID mid = 0; - if (mid == 0) - { - jclass clazz = env->GetObjectClass(jctx); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); - - mid = env->GetMethodID(clazz, "onProgress", - "(L"JAVA_PACKAGE"/ProgressEvent;)V"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN_NOTHING(); - } - - static jmethodID midCT = 0; - jclass clazz = env->FindClass(JAVA_PACKAGE"/ProgressEvent"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); - - if (midCT == 0) - { - midCT = env->GetMethodID(clazz, "<init>", "(JJ)V"); - if (JNIUtil::isJavaExceptionThrown() || midCT == 0) - POP_AND_RETURN_NOTHING(); - } - - // Call the Java method. - jobject jevent = env->NewObject(clazz, midCT, - (jlong) progressVal, (jlong) total); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); - - env->CallVoidMethod(jctx, mid, jevent); - - POP_AND_RETURN_NOTHING(); -} - svn_error_t * ClientContext::resolve(svn_wc_conflict_result_t **result, const svn_wc_conflict_description2_t *desc, @@ -440,8 +223,8 @@ ClientContext::resolve(svn_wc_conflict_result_t **result, POP_AND_RETURN(SVN_NO_ERROR); mid = env->GetMethodID(clazz, "resolve", - "(L"JAVA_PACKAGE"/ConflictDescriptor;)" - "L"JAVA_PACKAGE"/ConflictResult;"); + "(" JAVAHL_ARG("/ConflictDescriptor;") ")" + JAVAHL_ARG("/ConflictResult;")); if (JNIUtil::isJavaExceptionThrown() || mid == 0) POP_AND_RETURN(SVN_NO_ERROR); } @@ -493,7 +276,7 @@ ClientContext::javaResultToC(jobject jresult, apr_pool_t *pool) jclass clazz = NULL; if (getChoice == 0 || getMergedPath == 0) { - clazz = env->FindClass(JAVA_PACKAGE "/ConflictResult"); + clazz = env->FindClass(JAVAHL_CLASS("/ConflictResult")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; } @@ -501,7 +284,7 @@ ClientContext::javaResultToC(jobject jresult, apr_pool_t *pool) if (getChoice == 0) { getChoice = env->GetMethodID(clazz, "getChoice", - "()L"JAVA_PACKAGE"/ConflictResult$Choice;"); + "()" JAVAHL_ARG("/ConflictResult$Choice;")); if (JNIUtil::isJavaExceptionThrown() || getChoice == 0) POP_AND_RETURN_NULL; } diff --git a/subversion/bindings/javahl/native/ClientContext.h b/subversion/bindings/javahl/native/ClientContext.h index de3ece1..55ae9ff 100644 --- a/subversion/bindings/javahl/native/ClientContext.h +++ b/subversion/bindings/javahl/native/ClientContext.h @@ -29,6 +29,8 @@ #include <string> +#include "OperationContext.h" + #include "svn_types.h" #include "svn_client.h" @@ -36,7 +38,6 @@ #include "Pool.h" #include "JNIStringHolder.h" -class Prompter; class CommitMessage; /** @@ -44,25 +45,14 @@ class CommitMessage; * and implements the functions read & close of svn_stream_t. * */ -class ClientContext +class ClientContext : public OperationContext { private: svn_client_ctx_t *m_context; - const SVN::Pool *m_pool; - jobject m_jctx; - - std::string m_userName; - std::string m_passWord; - std::string m_configDir; - - Prompter *m_prompter; - bool m_cancelOperation; protected: static void notify(void *baton, const svn_wc_notify_t *notify, apr_pool_t *pool); - static void progress(apr_off_t progressVal, apr_off_t total, - void *baton, apr_pool_t *pool); static svn_error_t *resolve(svn_wc_conflict_result_t **result, const svn_wc_conflict_description2_t *desc, void *baton, @@ -73,24 +63,10 @@ class ClientContext public: ClientContext(jobject jsvnclient, SVN::Pool &pool); - ~ClientContext(); - - static svn_error_t *checkCancel(void *cancelBaton); + virtual ~ClientContext(); + virtual void setTunnelCallback(jobject jtunnelcb); svn_client_ctx_t *getContext(CommitMessage *message, SVN::Pool &in_pool); - - void username(const char *pi_username); - void password(const char *pi_password); - void setPrompt(Prompter *prompter); - void cancelOperation(); - const char *getConfigDirectory() const; - - /** - * Set the configuration directory, taking the usual steps to - * ensure that Subversion's config file templates exist in the - * specified location. - */ - void setConfigDirectory(const char *configDir); }; #endif // CLIENTCONTEXT_H diff --git a/subversion/bindings/javahl/native/CommitCallback.cpp b/subversion/bindings/javahl/native/CommitCallback.cpp index 2ffe326..2139312 100644 --- a/subversion/bindings/javahl/native/CommitCallback.cpp +++ b/subversion/bindings/javahl/native/CommitCallback.cpp @@ -37,9 +37,8 @@ * @param jcallback the Java callback object. */ CommitCallback::CommitCallback(jobject jcallback) -{ - m_callback = jcallback; -} + : m_callback(jcallback) +{} /** * Destroy a CommitCallback object @@ -81,13 +80,13 @@ CommitCallback::commitInfo(const svn_commit_info_t *commit_info, static jmethodID sm_mid = 0; if (sm_mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/CommitCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/CommitCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); sm_mid = env->GetMethodID(clazz, "commitInfo", - "(L"JAVA_PACKAGE"/CommitInfo;)V"); + "(" JAVAHL_ARG("/CommitInfo;") ")V"); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); } @@ -97,8 +96,17 @@ CommitCallback::commitInfo(const svn_commit_info_t *commit_info, POP_AND_RETURN(SVN_NO_ERROR); env->CallVoidMethod(m_callback, sm_mid, jcommitInfo); - // No need to check for an exception here, because we return anyway. - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); +} + + +PersistentCommitCallback::PersistentCommitCallback(jobject jcallback) + : CommitCallback(JNIUtil::getEnv()->NewGlobalRef(jcallback)) +{} + +PersistentCommitCallback::~PersistentCommitCallback() +{ + if (m_callback) + JNIUtil::getEnv()->DeleteGlobalRef(m_callback); } diff --git a/subversion/bindings/javahl/native/CommitCallback.h b/subversion/bindings/javahl/native/CommitCallback.h index d08219f..0c4fe0f 100644 --- a/subversion/bindings/javahl/native/CommitCallback.h +++ b/subversion/bindings/javahl/native/CommitCallback.h @@ -47,11 +47,26 @@ class CommitCallback svn_error_t *commitInfo(const svn_commit_info_t *commit_info, apr_pool_t *pool); - private: /** * This a local reference to the Java object. */ jobject m_callback; }; +/** + * Like CommitCallback, but maintains a reference to the Java object + * across JNI calls. + */ +class PersistentCommitCallback : protected CommitCallback +{ + public: + PersistentCommitCallback(jobject jcallback); + ~PersistentCommitCallback(); + static svn_error_t *callback(const svn_commit_info_t *commit_info, + void *baton, apr_pool_t *pool) + { + return CommitCallback::callback(commit_info, baton, pool); + } +}; + #endif // COMMITCALLBACK_H diff --git a/subversion/bindings/javahl/native/CommitEditor.cpp b/subversion/bindings/javahl/native/CommitEditor.cpp new file mode 100644 index 0000000..0a550d8 --- /dev/null +++ b/subversion/bindings/javahl/native/CommitEditor.cpp @@ -0,0 +1,618 @@ +/** + * @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 CommitEditor.cpp + * @brief Implementation of the class CommitEditor + */ + +#include "CommitEditor.h" +#include "EnumMapper.h" +#include "InputStream.h" +#include "Iterator.h" +#include "JNIByteArray.h" +#include "LockTokenTable.h" +#include "PropertyTable.h" +#include "RemoteSession.h" + +#include <apr_tables.h> +#include "svn_checksum.h" +#include "private/svn_editor.h" +#include "private/svn_ra_private.h" +#include "svn_private_config.h" + +#include "EditorCallbacks.hpp" +#include "jniwrapper/jni_string.hpp" +#include "jniwrapper/jni_stack.hpp" + +CommitEditor* +CommitEditor::getCppObject(jobject jthis) +{ + static jfieldID fid = 0; + jlong cppAddr = SVNBase::findCppAddrForJObject( + jthis, &fid, JAVAHL_CLASS("/remote/CommitEditor")); + return (cppAddr == 0 ? NULL : reinterpret_cast<CommitEditor*>(cppAddr)); +} + +jlong +CommitEditor::createInstance(jobject jsession, + jobject jrevprops, + jobject jcommit_callback, + jobject jlock_tokens, + jboolean jkeep_locks, + jobject jget_base_cb, + jobject jget_props_cb, + jobject jget_kind_cb) +{ + RemoteSession* session = RemoteSession::getCppObject(jsession); + CPPADDR_NULL_PTR(session, 0); + + CommitEditor* editor = new CommitEditor(session, + jrevprops, jcommit_callback, + jlock_tokens, jkeep_locks, + jget_base_cb, jget_props_cb, + jget_kind_cb); + if (JNIUtil::isJavaExceptionThrown()) + { + delete editor; + return 0; + } + return editor->getCppAddr(); +} + +CommitEditor::CommitEditor(RemoteSession* session, + jobject jrevprops, jobject jcommit_callback, + jobject jlock_tokens, jboolean jkeep_locks, + jobject jget_base_cb, jobject jget_props_cb, + jobject jget_kind_cb) + + : m_valid(false), + m_callback(jcommit_callback), + m_session(session), + m_editor(NULL), + m_get_base_cb(Java::Env(), jget_base_cb), + m_get_props_cb(Java::Env(), jget_props_cb), + m_get_kind_cb(Java::Env(), jget_kind_cb), + m_callback_session(NULL), + m_callback_session_url(NULL), + m_callback_session_uuid(NULL) +{ + // Store the repository root identity from the current session as we + // may need it to open another session in get_copysrc_kind_cb. + SVN_JNI_ERR(svn_ra_get_repos_root2(session->m_session, + &m_callback_session_url, + pool.getPool()),); + SVN_JNI_ERR(svn_ra_get_uuid2(session->m_session, + &m_callback_session_uuid, + pool.getPool()),); + + PropertyTable revprops(jrevprops, true, true); + if (JNIUtil::isJavaExceptionThrown()) + return; + LockTokenTable lock_tokens(jlock_tokens); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + SVN_JNI_ERR(svn_ra__get_commit_ev2( + &m_editor, + session->m_session, + revprops.hash(subPool), + m_callback.callback, &m_callback, + lock_tokens.hash(subPool, true), + bool(jkeep_locks), + this->provide_base_cb, + this->provide_props_cb, + this->get_copysrc_kind_cb, this, + pool.getPool(), // result pool + subPool.getPool()), // scratch pool + ); + m_valid = true; +} + +CommitEditor::~CommitEditor() {} + +void CommitEditor::dispose(jobject jthis) +{ + if (m_valid) + abort(); + + static jfieldID fid = 0; + SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/remote/CommitEditor")); +} + +namespace { +void throw_editor_inactive() +{ + JNIUtil::raiseThrowable("java/lang/IllegalStateException", + _("The editor is not active")); +} + +void throw_not_implemented(const char* fname) +{ + std::string msg = _("Not implemented: "); + msg += "CommitEditor."; + msg += fname; + JNIUtil::raiseThrowable("java/lang/RuntimeException", msg.c_str()); +} + +const apr_array_header_t* +build_children(const Iterator& iter, SVN::Pool& pool) +{ + apr_pool_t* result_pool = pool.getPool(); + apr_array_header_t* children = apr_array_make( + result_pool, 0, sizeof(const char*)); + while (iter.hasNext()) + { + JNIStringHolder path((jstring)iter.next()); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + APR_ARRAY_PUSH(children, const char*) = path.pstrdup(result_pool); + } + return children; +} + +svn_checksum_t +build_checksum(jobject jchecksum, SVN::Pool& pool) +{ + apr_pool_t* result_pool = pool.getPool(); + svn_checksum_t checksum = { 0 }; + if (jchecksum) + { + JNIEnv *env = JNIUtil::getEnv(); + + static jmethodID digest_mid = 0; + static jmethodID kind_mid = 0; + + if (0 == digest_mid || 0 == kind_mid) + { + jclass cls = env->FindClass(JAVAHL_CLASS("/types/Checksum")); + if (JNIUtil::isJavaExceptionThrown()) + return checksum; + + digest_mid = env->GetMethodID(cls, "getDigest", "()[B"); + if (JNIUtil::isJavaExceptionThrown()) + return checksum; + kind_mid = env->GetMethodID(cls, "getKind", "()L" + JAVAHL_CLASS("/types/Checksum$Kind;")); + if (JNIUtil::isJavaExceptionThrown()) + return checksum; + } + + jobject jdigest = env->CallObjectMethod(jchecksum, digest_mid); + if (JNIUtil::isJavaExceptionThrown()) + return checksum; + jobject jkind = env->CallObjectMethod(jchecksum, kind_mid); + if (JNIUtil::isJavaExceptionThrown()) + return checksum; + JNIByteArray bdigest((jbyteArray)jdigest, true); + if (JNIUtil::isJavaExceptionThrown()) + return checksum; + + void* digest = apr_palloc(result_pool, bdigest.getLength()); + memcpy(digest, bdigest.getBytes(), bdigest.getLength()); + checksum.digest = static_cast<const unsigned char*>(digest); + checksum.kind = EnumMapper::toChecksumKind(jkind); + } + + return checksum; +} +} // anonymous namespace + + +void CommitEditor::addDirectory(jstring jrelpath, + jobject jchildren, jobject jproperties, + jlong jreplaces_revision) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + Iterator children(jchildren); + if (JNIUtil::isJavaExceptionThrown()) + return; + PropertyTable properties(jproperties, true, true); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + Relpath relpath(jrelpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(relpath.error_occurred(),); + + SVN_JNI_ERR(svn_editor_add_directory(m_editor, relpath.c_str(), + build_children(children, subPool), + properties.hash(subPool), + svn_revnum_t(jreplaces_revision)),); +} + +void CommitEditor::addFile(jstring jrelpath, + jobject jchecksum, jobject jcontents, + jobject jproperties, + jlong jreplaces_revision) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + InputStream contents(jcontents); + PropertyTable properties(jproperties, true, true); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + Relpath relpath(jrelpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(relpath.error_occurred(),); + + svn_checksum_t checksum = build_checksum(jchecksum, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + SVN_JNI_ERR(svn_editor_add_file(m_editor, relpath.c_str(), + &checksum, contents.getStream(subPool), + properties.hash(subPool), + svn_revnum_t(jreplaces_revision)),); +} + +void CommitEditor::addSymlink(jstring jrelpath, + jstring jtarget, jobject jproperties, + jlong jreplaces_revision) +{ + throw_not_implemented("addSymlink"); +} + +void CommitEditor::addAbsent(jstring jrelpath, jobject jkind, + jlong jreplaces_revision) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + SVN::Pool subPool(pool); + Relpath relpath(jrelpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(relpath.error_occurred(),); + + SVN_JNI_ERR(svn_editor_add_absent(m_editor, relpath.c_str(), + EnumMapper::toNodeKind(jkind), + svn_revnum_t(jreplaces_revision)),); +} + +void CommitEditor::alterDirectory(jstring jrelpath, jlong jrevision, + jobject jchildren, jobject jproperties) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + Iterator children(jchildren); + if (JNIUtil::isJavaExceptionThrown()) + return; + PropertyTable properties(jproperties, true, false); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + Relpath relpath(jrelpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(relpath.error_occurred(),); + + SVN_JNI_ERR(svn_editor_alter_directory( + m_editor, relpath.c_str(), svn_revnum_t(jrevision), + (jchildren ? build_children(children, subPool) : NULL), + properties.hash(subPool)),); +} + +void CommitEditor::alterFile(jstring jrelpath, jlong jrevision, + jobject jchecksum, jobject jcontents, + jobject jproperties) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + InputStream contents(jcontents); + PropertyTable properties(jproperties, true, false); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + Relpath relpath(jrelpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(relpath.error_occurred(),); + + svn_checksum_t checksum = build_checksum(jchecksum, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + SVN_JNI_ERR(svn_editor_alter_file( + m_editor, relpath.c_str(), svn_revnum_t(jrevision), + (jcontents ? &checksum : NULL), + (jcontents ? contents.getStream(subPool) : NULL), + properties.hash(subPool)),); +} + +void CommitEditor::alterSymlink(jstring jrelpath, jlong jrevision, + jstring jtarget, jobject jproperties) +{ + throw_not_implemented("alterSymlink"); +} + +void CommitEditor::remove(jstring jrelpath, jlong jrevision) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + SVN::Pool subPool(pool); + Relpath relpath(jrelpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(relpath.error_occurred(),); + + SVN_JNI_ERR(svn_editor_delete(m_editor, relpath.c_str(), + svn_revnum_t(jrevision)),); +} + +void CommitEditor::copy(jstring jsrc_relpath, jlong jsrc_revision, + jstring jdst_relpath, jlong jreplaces_revision) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + SVN::Pool subPool(pool); + Relpath src_relpath(jsrc_relpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(src_relpath.error_occurred(),); + Relpath dst_relpath(jdst_relpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(dst_relpath.error_occurred(),); + + SVN_JNI_ERR(svn_editor_copy(m_editor, + src_relpath.c_str(), + svn_revnum_t(jsrc_revision), + dst_relpath.c_str(), + svn_revnum_t(jreplaces_revision)),); +} + +void CommitEditor::move(jstring jsrc_relpath, jlong jsrc_revision, + jstring jdst_relpath, jlong jreplaces_revision) +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + SVN::Pool subPool(pool); + Relpath src_relpath(jsrc_relpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(src_relpath.error_occurred(),); + Relpath dst_relpath(jdst_relpath, subPool); + if (JNIUtil::isExceptionThrown()) + return; + SVN_JNI_ERR(dst_relpath.error_occurred(),); + + SVN_JNI_ERR(svn_editor_move(m_editor, + src_relpath.c_str(), + svn_revnum_t(jsrc_revision), + dst_relpath.c_str(), + svn_revnum_t(jreplaces_revision)),); +} + +void CommitEditor::complete() +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + SVN_JNI_ERR(svn_editor_complete(m_editor),); + m_valid = false; +} + +void CommitEditor::abort() +{ + if (!m_valid) { throw_editor_inactive(); return; } + SVN_JNI_ERR(m_session->m_context->checkCancel(m_session->m_context),); + + SVN_JNI_ERR(svn_editor_abort(m_editor),); + m_valid = false; +} + + +namespace { +svn_error_t* open_callback_session(svn_ra_session_t*& session, + const char* url, const char* uuid, + RemoteSessionContext* context, + SVN::Pool& sessionPool) +{ + if (!session) + { + const char* corrected_url = NULL; + SVN_ERR(svn_ra_open4(&session, &corrected_url, url, uuid, + context->getCallbacks(), + context->getCallbackBaton(), + context->getConfigData(), + sessionPool.getPool())); + + if (corrected_url) + { + // This shouldn't happen -- the open session will give us + // the final redirected repository URL. There's an edge case + // where redirects might change while the session is open; + // but we'll just punt handling that to the caller. + return svn_error_createf( + SVN_ERR_RA_ILLEGAL_URL, NULL, + _("Repository URL changed while session was open.\n" + "Expected URL: %s\nApparent URL: %s"), + url, corrected_url); + } + } + return SVN_NO_ERROR; +} + +void +invoke_get_base_cb(svn_stream_t **contents, svn_revnum_t *revision, + Java::Env env, jobject get_base_cb, + const char *repos_relpath, apr_pool_t *result_pool) +{ + Java::String relpath(env, repos_relpath); + jobject jrv = + JavaHL::ProvideBaseCallback(env, get_base_cb)(relpath.get()); + JavaHL::ProvideBaseCallback::ReturnValue rv(env, jrv); + *contents = rv.get_global_stream(result_pool); + *revision = svn_revnum_t(rv.get_revision()); +} + +void +invoke_get_props_cb(apr_hash_t **props, svn_revnum_t *revision, + Java::Env env, jobject get_props_cb, + const char *repos_relpath, apr_pool_t *result_pool) +{ + Java::String relpath(env, repos_relpath); + jobject jrv = + JavaHL::ProvidePropsCallback(env, get_props_cb)(relpath.get()); + JavaHL::ProvidePropsCallback::ReturnValue rv(env, jrv); + *props = rv.get_property_hash(result_pool); + *revision = svn_revnum_t(rv.get_revision()); +} + +void +invoke_get_kind_cb(svn_node_kind_t *kind, + Java::Env env, jobject get_kind_cb, + const char *repos_relpath, svn_revnum_t revision) +{ + Java::String relpath(env, repos_relpath); + jobject jnode_kind = + JavaHL::GetNodeKindCallback(env, get_kind_cb)(relpath.get(), + jlong(revision)); + *kind = EnumMapper::toNodeKind(jnode_kind); +} +} // anonymous namespace + + +svn_error_t* +CommitEditor::provide_base_cb(svn_stream_t **contents, + svn_revnum_t *revision, + void *baton, + const char *repos_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + CommitEditor* editor = static_cast<CommitEditor*>(baton); + if (editor->m_get_base_cb.get()) + { + const Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_BASE, + invoke_get_base_cb(contents, revision, env, + editor->m_get_base_cb.get(), + repos_relpath, + result_pool)); + } + else + { + *contents = NULL; + *revision = SVN_INVALID_REVNUM; + } + return SVN_NO_ERROR; +} + +svn_error_t* +CommitEditor::provide_props_cb(apr_hash_t **props, + svn_revnum_t *revision, + void *baton, + const char *repos_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + CommitEditor* editor = static_cast<CommitEditor*>(baton); + if (editor->m_get_props_cb.get()) + { + const Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_BASE, + invoke_get_props_cb(props, revision, env, + editor->m_get_props_cb.get(), + repos_relpath, + result_pool)); + } + else + { + SVN_ERR(open_callback_session(editor->m_callback_session, + editor->m_callback_session_url, + editor->m_callback_session_uuid, + editor->m_session->m_context, + editor->pool)); + + svn_node_kind_t kind = svn_node_unknown; + SVN_ERR(svn_ra_check_path(editor->m_callback_session, + repos_relpath, SVN_INVALID_REVNUM, &kind, + scratch_pool)); + + // FIXME: Getting properties from the youngest revision is in + // fact not such a bright idea, as the path may have been moved + // or deleted in the repository. On the other hand, if that + // happens, the commit would fail due to a conflict anyway. + if (kind == svn_node_file) + return svn_ra_get_file(editor->m_callback_session, + repos_relpath, SVN_INVALID_REVNUM, + NULL, revision, props, scratch_pool); + else if (kind == svn_node_dir) + return svn_ra_get_dir2(editor->m_callback_session, NULL, revision, + props, repos_relpath, SVN_INVALID_REVNUM, 0, + scratch_pool); + else + return svn_error_createf( + SVN_ERR_NODE_UNEXPECTED_KIND, NULL, + _("Expected node kind '%s' or '%s' but got '%s'"), + svn_node_kind_to_word(svn_node_file), + svn_node_kind_to_word(svn_node_dir), + svn_node_kind_to_word(kind)); + } + return SVN_NO_ERROR; +} + +svn_error_t* +CommitEditor::get_copysrc_kind_cb(svn_node_kind_t* kind, void* baton, + const char* repos_relpath, + svn_revnum_t src_revision, + apr_pool_t *scratch_pool) +{ + CommitEditor* editor = static_cast<CommitEditor*>(baton); + if (editor->m_get_kind_cb.get()) + { + const Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_BASE, + invoke_get_kind_cb(kind, env, + editor->m_get_kind_cb.get(), + repos_relpath, + src_revision)); + } + else + { + SVN_ERR(open_callback_session(editor->m_callback_session, + editor->m_callback_session_url, + editor->m_callback_session_uuid, + editor->m_session->m_context, + editor->pool)); + + return svn_ra_check_path(editor->m_callback_session, + repos_relpath, src_revision, kind, + scratch_pool); + } + return SVN_NO_ERROR; +} diff --git a/subversion/bindings/javahl/native/CommitEditor.h b/subversion/bindings/javahl/native/CommitEditor.h new file mode 100644 index 0000000..4160b6c --- /dev/null +++ b/subversion/bindings/javahl/native/CommitEditor.h @@ -0,0 +1,132 @@ +/** + * @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 CommitEditor.h + * @brief Interface of the class CommitEditor + */ + +#ifndef JAVAHL_COMMIT_EDITOR_H +#define JAVAHL_COMMIT_EDITOR_H + +#include <string> +#include <jni.h> + +#include "svn_ra.h" + +#include "JNIUtil.h" +#include "SVNBase.h" +#include "CommitCallback.h" + +#include "jniwrapper/jni_globalref.hpp" + +class RemoteSession; + +// Forward-declare the currently private EV2 editor struct. +struct svn_editor_t; + +/* + * This class wraps an EV2 commit editor. + */ +class CommitEditor : public SVNBase +{ +public: + static CommitEditor* getCppObject(jobject jthis); + static jlong createInstance(jobject jsession, + jobject jrevprops, + jobject jcommit_callback, + jobject jlock_tokens, + jboolean jkeep_locks, + jobject jget_base_cb, + jobject jget_props_cb, + jobject jget_kind_cb); + virtual ~CommitEditor(); + + virtual void dispose(jobject jthis); + + void addDirectory(jstring jrelpath, + jobject jchildren, jobject jproperties, + jlong jreplaces_revision); + void addFile(jstring jrelpath, + jobject jchecksum, jobject jcontents, + jobject jproperties, + jlong jreplaces_revision); + void addSymlink(jstring jrelpath, + jstring jtarget, jobject jproperties, + jlong jreplaces_revision); + void addAbsent(jstring jrelpath, + jobject jkind, jlong jreplaces_revision); + void alterDirectory(jstring jrelpath, jlong jrevision, + jobject jchildren, jobject jproperties); + void alterFile(jstring jrelpath, jlong jrevision, + jobject jchecksum, jobject jcontents, + jobject jproperties); + void alterSymlink(jstring jrelpath, jlong jrevision, + jstring jtarget, jobject jproperties); + void remove(jstring jrelpath, jlong jrevision); + void copy(jstring jsrc_relpath, jlong jsrc_revision, + jstring jdst_relpath, jlong jreplaces_revision); + void move(jstring jsrc_relpath, jlong jsrc_revision, + jstring jdst_relpath, jlong jreplaces_revision); + void complete(); + void abort(); + +private: + CommitEditor(RemoteSession* session, + jobject jrevprops, jobject jcommit_callback, + jobject jlock_tokens, jboolean jkeep_locks, + jobject jget_base_cb, jobject jget_props_cb, + jobject jget_kind_cb); + + // This is our private callbacks for the commit editor. + static svn_error_t* provide_base_cb(svn_stream_t **contents, + svn_revnum_t *revision, + void *baton, + const char *repos_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + static svn_error_t* provide_props_cb(apr_hash_t **props, + svn_revnum_t *revision, + void *baton, + const char *repos_relpath, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); + static svn_error_t* get_copysrc_kind_cb(svn_node_kind_t* kind, void* baton, + const char* repos_relpath, + svn_revnum_t src_revision, + apr_pool_t *scratch_pool); + + bool m_valid; + PersistentCommitCallback m_callback; + RemoteSession* m_session; + svn_editor_t* m_editor; + + Java::GlobalObject m_get_base_cb; + Java::GlobalObject m_get_props_cb; + Java::GlobalObject m_get_kind_cb; + + // Temporary, while EV2 shims are in place + svn_ra_session_t* m_callback_session; + const char* m_callback_session_url; + const char* m_callback_session_uuid; +}; + +#endif // JAVAHL_COMMIT_EDITOR_H diff --git a/subversion/bindings/javahl/native/CommitMessage.cpp b/subversion/bindings/javahl/native/CommitMessage.cpp index a947f97..51bd83d 100644 --- a/subversion/bindings/javahl/native/CommitMessage.cpp +++ b/subversion/bindings/javahl/native/CommitMessage.cpp @@ -65,6 +65,7 @@ CommitMessage::getCommitMessage(const char **log_msg, const apr_array_header_t *commit_items, apr_pool_t *pool) { + *log_msg = NULL; *tmp_file = NULL; JNIEnv *env = JNIUtil::getEnv(); @@ -72,7 +73,7 @@ CommitMessage::getCommitMessage(const char **log_msg, static jmethodID midCallback = 0; if (midCallback == 0) { - jclass clazz2 = env->FindClass(JAVA_PACKAGE"/callback/CommitMessageCallback"); + jclass clazz2 = env->FindClass(JAVAHL_CLASS("/callback/CommitMessageCallback")); if (JNIUtil::isJavaExceptionThrown()) return SVN_NO_ERROR; @@ -93,6 +94,9 @@ CommitMessage::getCommitMessage(const char **log_msg, jobject jitem = CreateJ::CommitItem(item); + if (!jitem) + return SVN_NO_ERROR; /* Exception thrown */ + // store the Java object into the array jitems.push_back(jitem); } @@ -102,7 +106,7 @@ CommitMessage::getCommitMessage(const char **log_msg, midCallback, CreateJ::Set(jitems)); if (JNIUtil::isJavaExceptionThrown()) - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); if (jmessage != NULL) { diff --git a/subversion/bindings/javahl/native/CopySources.cpp b/subversion/bindings/javahl/native/CopySources.cpp index 0ae471d..a1d93f4 100644 --- a/subversion/bindings/javahl/native/CopySources.cpp +++ b/subversion/bindings/javahl/native/CopySources.cpp @@ -60,7 +60,7 @@ CopySources::makeJCopySource(const char *path, svn_revnum_t rev, SVN::Pool &pool if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE "/types/CopySource"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/CopySource")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -69,8 +69,8 @@ CopySources::makeJCopySource(const char *path, svn_revnum_t rev, SVN::Pool &pool { ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;" - "L" JAVA_PACKAGE "/types/Revision;" - "L" JAVA_PACKAGE "/types/Revision;)V"); + JAVAHL_ARG("/types/Revision;") + JAVAHL_ARG("/types/Revision;") ")V"); if (JNIUtil::isExceptionThrown()) POP_AND_RETURN_NULL; } @@ -88,7 +88,7 @@ CopySources::array(SVN::Pool &pool) apr_pool_t *p = pool.getPool(); JNIEnv *env = JNIUtil::getEnv(); - jclass clazz = env->FindClass(JAVA_PACKAGE "/types/CopySource"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/CopySource")); if (JNIUtil::isJavaExceptionThrown()) return NULL; @@ -132,7 +132,7 @@ CopySources::array(SVN::Pool &pool) if (getRevision == 0) { getRevision = env->GetMethodID(clazz, "getRevision", - "()L"JAVA_PACKAGE"/types/Revision;"); + "()" JAVAHL_ARG("/types/Revision;")); if (JNIUtil::isJavaExceptionThrown() || getRevision == 0) return NULL; } @@ -151,7 +151,7 @@ CopySources::array(SVN::Pool &pool) if (getPegRevision == 0) { getPegRevision = env->GetMethodID(clazz, "getPegRevision", - "()L"JAVA_PACKAGE"/types/Revision;"); + "()" JAVAHL_ARG("/types/Revision;")); if (JNIUtil::isJavaExceptionThrown() || getPegRevision == 0) return NULL; } diff --git a/subversion/bindings/javahl/native/CreateJ.cpp b/subversion/bindings/javahl/native/CreateJ.cpp index 9b40ce7..d9dffa2 100644 --- a/subversion/bindings/javahl/native/CreateJ.cpp +++ b/subversion/bindings/javahl/native/CreateJ.cpp @@ -30,12 +30,14 @@ #include "JNIStringHolder.h" #include "EnumMapper.h" #include "RevisionRange.h" +#include "RevisionRangeList.h" #include "CreateJ.h" #include "../include/org_apache_subversion_javahl_types_Revision.h" #include "../include/org_apache_subversion_javahl_CommitItemStateFlags.h" #include "svn_path.h" #include "svn_props.h" +#include "svn_mergeinfo.h" #include "private/svn_wc_private.h" jobject @@ -52,7 +54,7 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc) return NULL; // Create an instance of the conflict descriptor. - jclass clazz = env->FindClass(JAVA_PACKAGE "/ConflictDescriptor"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/ConflictDescriptor")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -60,16 +62,17 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc) if (ctor == 0) { ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;" - "L"JAVA_PACKAGE"/ConflictDescriptor$Kind;" - "L"JAVA_PACKAGE"/types/NodeKind;" + JAVAHL_ARG("/ConflictDescriptor$Kind;") + JAVAHL_ARG("/types/NodeKind;") "Ljava/lang/String;ZLjava/lang/String;" - "L"JAVA_PACKAGE"/ConflictDescriptor$Action;" - "L"JAVA_PACKAGE"/ConflictDescriptor$Reason;" - "L"JAVA_PACKAGE"/ConflictDescriptor$Operation;" + JAVAHL_ARG("/ConflictDescriptor$Action;") + JAVAHL_ARG("/ConflictDescriptor$Reason;") + JAVAHL_ARG("/ConflictDescriptor$Operation;") "Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/ConflictVersion;" - "L"JAVA_PACKAGE"/types/ConflictVersion;)V"); + JAVAHL_ARG("/types/ConflictVersion;") + JAVAHL_ARG("/types/ConflictVersion;") + "Ljava/lang/String;[B[B[B[B)V"); if (JNIUtil::isJavaExceptionThrown() || ctor == 0) POP_AND_RETURN_NULL; } @@ -116,6 +119,33 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc) jobject joperation = EnumMapper::mapOperation(desc->operation); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; + jstring jpropRejectAbspath = JNIUtil::makeJString(desc->prop_reject_abspath); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + jbyteArray jpropValueBase = ( + !desc->prop_value_base ? NULL + :JNIUtil::makeJByteArray(desc->prop_value_base->data, + int(desc->prop_value_base->len))); + if (JNIUtil::isExceptionThrown()) + POP_AND_RETURN_NULL; + jbyteArray jpropValueWorking = ( + !desc->prop_value_working ? NULL + :JNIUtil::makeJByteArray(desc->prop_value_working->data, + int(desc->prop_value_working->len))); + if (JNIUtil::isExceptionThrown()) + POP_AND_RETURN_NULL; + jbyteArray jpropValueIncomingOld = ( + !desc->prop_value_incoming_old ? NULL + :JNIUtil::makeJByteArray(desc->prop_value_incoming_old->data, + int(desc->prop_value_incoming_old->len))); + if (JNIUtil::isExceptionThrown()) + POP_AND_RETURN_NULL; + jbyteArray jpropValueIncomingNew = ( + !desc->prop_value_incoming_new ? NULL + :JNIUtil::makeJByteArray(desc->prop_value_incoming_new->data, + int(desc->prop_value_incoming_new->len))); + if (JNIUtil::isExceptionThrown()) + POP_AND_RETURN_NULL; // Instantiate the conflict descriptor. jobject jdesc = env->NewObject(clazz, ctor, jpath, jconflictKind, @@ -123,7 +153,10 @@ CreateJ::ConflictDescriptor(const svn_wc_conflict_description2_t *desc) (jboolean) desc->is_binary, jmimeType, jconflictAction, jconflictReason, joperation, jbasePath, jreposPath, juserPath, - jmergedPath, jsrcLeft, jsrcRight); + jmergedPath, jsrcLeft, jsrcRight, + jpropRejectAbspath, jpropValueBase, + jpropValueWorking, jpropValueIncomingOld, + jpropValueIncomingNew); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -144,7 +177,7 @@ CreateJ::ConflictVersion(const svn_wc_conflict_version_t *version) return NULL; // Create an instance of the conflict version. - jclass clazz = env->FindClass(JAVA_PACKAGE "/types/ConflictVersion"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/ConflictVersion")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -154,7 +187,7 @@ CreateJ::ConflictVersion(const svn_wc_conflict_version_t *version) ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;" "Ljava/lang/String;J" "Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/NodeKind;" + JAVAHL_ARG("/types/NodeKind;") ")V"); if (JNIUtil::isJavaExceptionThrown() || ctor == 0) POP_AND_RETURN_NULL; @@ -195,7 +228,7 @@ CreateJ::Checksum(const svn_checksum_t *checksum) if (JNIUtil::isJavaExceptionThrown()) return NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Checksum"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Checksum")); if (JNIUtil::isExceptionThrown()) POP_AND_RETURN_NULL; @@ -205,7 +238,7 @@ CreateJ::Checksum(const svn_checksum_t *checksum) { midConstructor = env->GetMethodID(clazz, "<init>", "([B" - "L"JAVA_PACKAGE"/types/Checksum$Kind;" + JAVAHL_ARG("/types/Checksum$Kind;") ")V"); if (JNIUtil::isExceptionThrown()) POP_AND_RETURN_NULL; @@ -230,6 +263,61 @@ CreateJ::Checksum(const svn_checksum_t *checksum) } jobject +CreateJ::DirEntry(const char *path, const char *absPath, + const svn_dirent_t *dirent) +{ + JNIEnv *env = JNIUtil::getEnv(); + + // Create a local frame for our references + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return SVN_NO_ERROR; + + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/DirEntry")); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + static jmethodID mid = 0; + if (mid == 0) + { + mid = env->GetMethodID(clazz, "<init>", + "(Ljava/lang/String;Ljava/lang/String;" + JAVAHL_ARG("/types/NodeKind;") + "JZJJLjava/lang/String;)V"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + } + + jstring jPath = JNIUtil::makeJString(path); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + jstring jAbsPath = JNIUtil::makeJString(absPath); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + jobject jNodeKind = EnumMapper::mapNodeKind(dirent->kind); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + jlong jSize = dirent->size; + jboolean jHasProps = (dirent->has_props? JNI_TRUE : JNI_FALSE); + jlong jLastChangedRevision = dirent->created_rev; + jlong jLastChanged = dirent->time; + jstring jLastAuthor = JNIUtil::makeJString(dirent->last_author); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + jobject ret = env->NewObject(clazz, mid, jPath, jAbsPath, jNodeKind, + jSize, jHasProps, jLastChangedRevision, + jLastChanged, jLastAuthor); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + return env->PopLocalFrame(ret); +} + +jobject CreateJ::Info(const char *path, const svn_client_info2_t *info) { JNIEnv *env = JNIUtil::getEnv(); @@ -239,7 +327,7 @@ CreateJ::Info(const char *path, const svn_client_info2_t *info) if (JNIUtil::isJavaExceptionThrown()) return NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE "/types/Info"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Info")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -249,15 +337,16 @@ CreateJ::Info(const char *path, const svn_client_info2_t *info) mid = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;J" - "L"JAVA_PACKAGE"/types/NodeKind;" + JAVAHL_ARG("/types/NodeKind;") "Ljava/lang/String;Ljava/lang/String;" "JJLjava/lang/String;" - "L"JAVA_PACKAGE"/types/Lock;Z" - "L"JAVA_PACKAGE"/types/Info$ScheduleKind;" + JAVAHL_ARG("/types/Lock;Z") + JAVAHL_ARG("/types/Info$ScheduleKind;") "Ljava/lang/String;JJ" - "L"JAVA_PACKAGE"/types/Checksum;" + JAVAHL_ARG("/types/Checksum;") "Ljava/lang/String;JJ" - "L"JAVA_PACKAGE"/types/Depth;Ljava/util/Set;)V"); + JAVAHL_ARG("/types/Depth;Ljava/util/Set;") + ")V"); if (mid == 0 || JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; } @@ -384,7 +473,7 @@ CreateJ::Lock(const svn_lock_t *lock) if (JNIUtil::isJavaExceptionThrown()) return NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Lock"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Lock")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -423,6 +512,72 @@ CreateJ::Lock(const svn_lock_t *lock) } jobject +CreateJ::LockMap(const apr_hash_t *locks, apr_pool_t *pool) +{ + JNIEnv *env = JNIUtil::getEnv(); + + if (locks == NULL) + return NULL; + + // Create a local frame for our references + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + jclass clazz = env->FindClass("java/util/HashMap"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + static jmethodID init_mid = 0; + if (init_mid == 0) + { + init_mid = env->GetMethodID(clazz, "<init>", "()V"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + } + + static jmethodID put_mid = 0; + if (put_mid == 0) + { + put_mid = env->GetMethodID(clazz, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)" + "Ljava/lang/Object;"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + } + + jobject map = env->NewObject(clazz, init_mid); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + apr_hash_index_t *hi; + int i = 0; + for (hi = apr_hash_first(pool, (apr_hash_t *) locks); hi; + hi = apr_hash_next(hi), ++i) + { + const char *key = (const char *) apr_hash_this_key(hi); + const svn_lock_t *lock = (const svn_lock_t *) apr_hash_this_val(hi); + + jstring jpath = JNIUtil::makeJString(key); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + jobject jlock = Lock(lock); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + env->CallObjectMethod(map, put_mid, jpath, jlock); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + env->DeleteLocalRef(jpath); + env->DeleteLocalRef(jlock); + } + + return env->PopLocalFrame(map); +} + +jobject CreateJ::ChangedPath(const char *path, svn_log_changed_path2_t *log_item) { JNIEnv *env = JNIUtil::getEnv(); @@ -432,7 +587,7 @@ CreateJ::ChangedPath(const char *path, svn_log_changed_path2_t *log_item) if (JNIUtil::isJavaExceptionThrown()) return NULL; - jclass clazzCP = env->FindClass(JAVA_PACKAGE"/types/ChangePath"); + jclass clazzCP = env->FindClass(JAVAHL_CLASS("/types/ChangePath")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -442,10 +597,11 @@ CreateJ::ChangedPath(const char *path, svn_log_changed_path2_t *log_item) midCP = env->GetMethodID(clazzCP, "<init>", "(Ljava/lang/String;JLjava/lang/String;" - "L"JAVA_PACKAGE"/types/ChangePath$Action;" - "L"JAVA_PACKAGE"/types/NodeKind;" - "L"JAVA_PACKAGE"/types/Tristate;" - "L"JAVA_PACKAGE"/types/Tristate;)V"); + JAVAHL_ARG("/types/ChangePath$Action;") + JAVAHL_ARG("/types/NodeKind;") + JAVAHL_ARG("/types/Tristate;") + JAVAHL_ARG("/types/Tristate;") + ")V"); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; } @@ -493,7 +649,7 @@ CreateJ::Status(svn_wc_context_t *wc_ctx, if (JNIUtil::isJavaExceptionThrown()) return NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Status"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Status")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -502,15 +658,18 @@ CreateJ::Status(svn_wc_context_t *wc_ctx, { mid = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/NodeKind;" + JAVAHL_ARG("/types/NodeKind;") "JJJLjava/lang/String;" - "L"JAVA_PACKAGE"/types/Status$Kind;" - "L"JAVA_PACKAGE"/types/Status$Kind;" - "L"JAVA_PACKAGE"/types/Status$Kind;" - "L"JAVA_PACKAGE"/types/Status$Kind;" - "ZZZZZL"JAVA_PACKAGE"/types/Lock;" - "L"JAVA_PACKAGE"/types/Lock;" - "JJL"JAVA_PACKAGE"/types/NodeKind;" + JAVAHL_ARG("/types/Status$Kind;") + JAVAHL_ARG("/types/Status$Kind;") + JAVAHL_ARG("/types/Status$Kind;") + JAVAHL_ARG("/types/Status$Kind;") + JAVAHL_ARG("/types/Status$Kind;") + JAVAHL_ARG("/types/Status$Kind;") + "ZZ" JAVAHL_ARG("/types/Depth;") + "ZZZ" JAVAHL_ARG("/types/Lock;") + JAVAHL_ARG("/types/Lock;") + "JJ" JAVAHL_ARG("/types/NodeKind;") "Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;)V"); if (JNIUtil::isJavaExceptionThrown()) @@ -534,27 +693,16 @@ CreateJ::Status(svn_wc_context_t *wc_ctx, jstring jMovedFromAbspath = NULL; jstring jMovedToAbspath = NULL; - enum svn_wc_status_kind text_status = status->node_status; - - /* Avoid using values that might come from prop changes */ - if (text_status == svn_wc_status_modified - || text_status == svn_wc_status_conflicted) - text_status = status->text_status; - - enum svn_wc_status_kind repos_text_status = status->repos_node_status; - - if (repos_text_status == svn_wc_status_modified - || repos_text_status == svn_wc_status_conflicted) - repos_text_status = status->repos_text_status; - - jboolean jIsConflicted = (status->conflicted == 1) ? JNI_TRUE : JNI_FALSE; - jobject jTextType = EnumMapper::mapStatusKind(text_status); + jobject jNodeType = EnumMapper::mapStatusKind(status->node_status); + jobject jTextType = EnumMapper::mapStatusKind(status->text_status); jobject jPropType = EnumMapper::mapStatusKind(status->prop_status); - jobject jRepositoryTextType = EnumMapper::mapStatusKind(repos_text_status); - jobject jRepositoryPropType = EnumMapper::mapStatusKind( - status->repos_prop_status); - jboolean jIsCopied = (status->copied == 1) ? JNI_TRUE: JNI_FALSE; + jobject jRpNodeType = EnumMapper::mapStatusKind(status->repos_node_status); + jobject jRpTextType = EnumMapper::mapStatusKind(status->repos_text_status); + jobject jRpPropType = EnumMapper::mapStatusKind(status->repos_prop_status); + jobject jDepth = EnumMapper::mapDepth(status->depth); jboolean jIsLocked = (status->wc_is_locked == 1) ? JNI_TRUE: JNI_FALSE; + jboolean jIsCopied = (status->copied == 1) ? JNI_TRUE: JNI_FALSE; + jboolean jIsConflicted = (status->conflicted == 1) ? JNI_TRUE : JNI_FALSE; jboolean jIsSwitched = (status->switched == 1) ? JNI_TRUE: JNI_FALSE; jboolean jIsFileExternal = (status->file_external == 1) ? JNI_TRUE : JNI_FALSE; @@ -620,9 +768,10 @@ CreateJ::Status(svn_wc_context_t *wc_ctx, jobject ret = env->NewObject(clazz, mid, jPath, jUrl, jNodeKind, jRevision, jLastChangedRevision, jLastChangedDate, - jLastCommitAuthor, jTextType, jPropType, - jRepositoryTextType, jRepositoryPropType, - jIsLocked, jIsCopied, jIsConflicted, + jLastCommitAuthor, + jNodeType, jTextType, jPropType, + jRpNodeType, jRpTextType, jRpPropType, + jIsLocked, jIsCopied, jDepth, jIsConflicted, jIsSwitched, jIsFileExternal, jLocalLock, jReposLock, jOODLastCmtRevision, jOODLastCmtDate, @@ -643,7 +792,7 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify) return NULL; static jmethodID midCT = 0; - jclass clazz = env->FindClass(JAVA_PACKAGE"/ClientNotifyInformation"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/ClientNotifyInformation")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -651,16 +800,17 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify) { midCT = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;" - "L"JAVA_PACKAGE"/ClientNotifyInformation$Action;" - "L"JAVA_PACKAGE"/types/NodeKind;" - "Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/Lock;" + JAVAHL_ARG("/ClientNotifyInformation$Action;") + JAVAHL_ARG("/types/NodeKind;") "Ljava/lang/String;" - "L"JAVA_PACKAGE"/ClientNotifyInformation$Status;" - "L"JAVA_PACKAGE"/ClientNotifyInformation$Status;" - "L"JAVA_PACKAGE"/ClientNotifyInformation$LockStatus;" + JAVAHL_ARG("/types/Lock;") + "Ljava/lang/String;Ljava/util/List;" + JAVAHL_ARG("/ClientNotifyInformation$Status;") + JAVAHL_ARG("/ClientNotifyInformation$Status;") + JAVAHL_ARG("/ClientNotifyInformation$LockStatus;") "JLjava/lang/String;" - "L"JAVA_PACKAGE"/types/RevisionRange;" + JAVAHL_ARG("/types/RevisionRange;") + "Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;" "Ljava/util/Map;JJJJJJI)V"); if (JNIUtil::isJavaExceptionThrown() || midCT == 0) @@ -688,7 +838,9 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify) if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; - jstring jErr = JNIUtil::makeSVNErrorMessage(wcNotify->err); + jstring jErr; + jobject jErrStack; + JNIUtil::makeSVNErrorMessage(wcNotify->err, &jErr, &jErrStack); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -716,6 +868,10 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify) POP_AND_RETURN_NULL; } + jstring jUrl = JNIUtil::makeJString(wcNotify->url); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + jstring jpathPrefix = JNIUtil::makeJString(wcNotify->path_prefix); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -735,7 +891,7 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify) jlong jhunkModifiedLength = wcNotify->hunk_modified_length; jlong jhunkMatchedLine = wcNotify->hunk_matched_line; jint jhunkFuzz = static_cast<jint>(wcNotify->hunk_fuzz); - if (jhunkFuzz != wcNotify->hunk_fuzz) + if (jhunkFuzz < 0 || jhunkFuzz != wcNotify->hunk_fuzz) { env->ThrowNew(env->FindClass("java.lang.ArithmeticException"), "Overflow converting C svn_linenum_t to Java int"); @@ -744,10 +900,10 @@ CreateJ::ClientNotifyInformation(const svn_wc_notify_t *wcNotify) // call the Java method jobject jInfo = env->NewObject(clazz, midCT, jPath, jAction, - jKind, jMimeType, jLock, jErr, + jKind, jMimeType, jLock, jErr, jErrStack, jContentState, jPropState, jLockState, (jlong) wcNotify->revision, jChangelistName, - jMergeRange, jpathPrefix, jpropName, + jMergeRange, jUrl, jpathPrefix, jpropName, jrevProps, joldRevision, jhunkOriginalStart, jhunkOriginalLength, jhunkModifiedStart, jhunkModifiedLength, @@ -769,16 +925,16 @@ CreateJ::ReposNotifyInformation(const svn_repos_notify_t *reposNotify) return NULL; static jmethodID midCT = 0; - jclass clazz = env->FindClass(JAVA_PACKAGE"/ReposNotifyInformation"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/ReposNotifyInformation")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; if (midCT == 0) { midCT = env->GetMethodID(clazz, "<init>", - "(L"JAVA_PACKAGE"/ReposNotifyInformation$Action;" + "(" JAVAHL_ARG("/ReposNotifyInformation$Action;") "JLjava/lang/String;JJJ" - "L"JAVA_PACKAGE"/ReposNotifyInformation$NodeAction;" + JAVAHL_ARG("/ReposNotifyInformation$NodeAction;") "Ljava/lang/String;)V"); if (JNIUtil::isJavaExceptionThrown() || midCT == 0) POP_AND_RETURN_NULL; @@ -827,7 +983,7 @@ CreateJ::CommitItem(svn_client_commit_item3_t *item) if (JNIUtil::isJavaExceptionThrown()) return NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE"/CommitItem"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/CommitItem")); if (JNIUtil::isExceptionThrown()) POP_AND_RETURN_NULL; @@ -837,7 +993,7 @@ CreateJ::CommitItem(svn_client_commit_item3_t *item) { midConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/NodeKind;" + JAVAHL_ARG("/types/NodeKind;") "ILjava/lang/String;" "Ljava/lang/String;J" "Ljava/lang/String;)V"); @@ -909,7 +1065,7 @@ CreateJ::CommitInfo(const svn_commit_info_t *commit_info) return NULL; static jmethodID midCT = 0; - jclass clazz = env->FindClass(JAVA_PACKAGE"/CommitInfo"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/CommitInfo")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -951,82 +1107,129 @@ CreateJ::CommitInfo(const svn_commit_info_t *commit_info) } jobject -CreateJ::RevisionRangeList(svn_rangelist_t *ranges) +CreateJ::StringSet(const apr_array_header_t *strings) { + std::vector<jobject> jstrs; + + for (int i = 0; i < strings->nelts; ++i) + { + const char *str = APR_ARRAY_IDX(strings, i, const char *); + jstring jstr = JNIUtil::makeJString(str); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + jstrs.push_back(jstr); + } + + return CreateJ::Set(jstrs); +} + +namespace { +void fill_property_map(jobject map, + apr_hash_t* prop_hash, apr_array_header_t* prop_diffs, + apr_pool_t* scratch_pool, jmethodID put_mid) +{ + SVN_ERR_ASSERT_NO_RETURN(!(prop_hash && prop_diffs)); + + if (!map || (prop_hash == NULL && prop_diffs == NULL)) + return; + JNIEnv *env = JNIUtil::getEnv(); // Create a local frame for our references env->PushLocalFrame(LOCAL_FRAME_SIZE); if (JNIUtil::isJavaExceptionThrown()) - return NULL; - - jclass clazz = env->FindClass("java/util/ArrayList"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + return; - static jmethodID init_mid = 0; - if (init_mid == 0) + // The caller may not know the concrete class of the map, so + // determine the "put" method identifier here. + if (put_mid == 0) { - init_mid = env->GetMethodID(clazz, "<init>", "()V"); + put_mid = env->GetMethodID(env->GetObjectClass(map), "put", + "(Ljava/lang/Object;Ljava/lang/Object;)" + "Ljava/lang/Object;"); if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + POP_AND_RETURN_NOTHING(); } - static jmethodID add_mid = 0; - if (add_mid == 0) + struct body + { + void operator()(const char* key, const svn_string_t* val) + { + jstring jpropName = JNIUtil::makeJString(key); + if (JNIUtil::isJavaExceptionThrown()) + return; + + jbyteArray jpropVal = (val ? JNIUtil::makeJByteArray(val) : NULL); + if (JNIUtil::isJavaExceptionThrown()) + return; + + jobject ret = m_env->CallObjectMethod(m_map, m_put_mid, + jpropName, jpropVal); + if (JNIUtil::isJavaExceptionThrown()) + return; + + m_env->DeleteLocalRef(ret); + m_env->DeleteLocalRef(jpropVal); + m_env->DeleteLocalRef(jpropName); + } + + JNIEnv*& m_env; + jmethodID& m_put_mid; + jobject& m_map; + + body(JNIEnv*& xenv, jmethodID& xput_mid, jobject& xmap) + : m_env(xenv), m_put_mid(xput_mid), m_map(xmap) + {} + } loop_body(env, put_mid, map); + + if (prop_hash) { - add_mid = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - } + if (!scratch_pool) + scratch_pool = apr_hash_pool_get(prop_hash); - jobject jranges = env->NewObject(clazz, init_mid); + apr_hash_index_t *hi; + for (hi = apr_hash_first(scratch_pool, prop_hash); + hi; hi = apr_hash_next(hi)) + { + const char* key; + svn_string_t* val; - for (int i = 0; i < ranges->nelts; ++i) - { - // Convert svn_merge_range_t *'s to Java RevisionRange objects. - svn_merge_range_t *range = - APR_ARRAY_IDX(ranges, i, svn_merge_range_t *); + const void* v_key; + void* v_val; - jobject jrange = RevisionRange::makeJRevisionRange(range); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + apr_hash_this(hi, &v_key, NULL, &v_val); + key = static_cast<const char*>(v_key); + val = static_cast<svn_string_t*>(v_val); - env->CallBooleanMethod(jranges, add_mid, jrange); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - env->DeleteLocalRef(jrange); + loop_body(key, val); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + } } - - return env->PopLocalFrame(jranges); -} - -jobject -CreateJ::StringSet(apr_array_header_t *strings) -{ - std::vector<jobject> jstrs; - - for (int i = 0; i < strings->nelts; ++i) + else { - const char *str = APR_ARRAY_IDX(strings, i, const char *); - jstring jstr = JNIUtil::makeJString(str); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; - - jstrs.push_back(jstr); + for (int i = 0; i < prop_diffs->nelts; ++i) + { + svn_prop_t* prop = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t); + loop_body(prop->name, prop->value); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + } } - - return CreateJ::Set(jstrs); + POP_AND_RETURN_NOTHING(); } -jobject CreateJ::PropertyMap(apr_hash_t *prop_hash) +jobject property_map(apr_hash_t *prop_hash, apr_array_header_t* prop_diffs, + apr_pool_t* scratch_pool) { - JNIEnv *env = JNIUtil::getEnv(); + SVN_ERR_ASSERT_NO_RETURN(!(prop_hash && prop_diffs)); - if (prop_hash == NULL) + if (prop_hash == NULL && prop_diffs == NULL) return NULL; + JNIEnv *env = JNIUtil::getEnv(); + // Create a local frame for our references env->PushLocalFrame(LOCAL_FRAME_SIZE); if (JNIUtil::isJavaExceptionThrown()) @@ -1058,35 +1261,35 @@ jobject CreateJ::PropertyMap(apr_hash_t *prop_hash) if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; - apr_hash_index_t *hi; - for (hi = apr_hash_first(apr_hash_pool_get(prop_hash), prop_hash); - hi; hi = apr_hash_next(hi)) - { - const char *key; - svn_string_t *val; - - apr_hash_this(hi, - reinterpret_cast<const void **>(&key), - NULL, - reinterpret_cast<void **>(&val)); + fill_property_map(map, prop_hash, prop_diffs, scratch_pool, put_mid); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; - jstring jpropName = JNIUtil::makeJString(key); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + return env->PopLocalFrame(map); +} +} // anonymous namespace - jbyteArray jpropVal = JNIUtil::makeJByteArray(val); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; +jobject CreateJ::PropertyMap(apr_hash_t *prop_hash, apr_pool_t* scratch_pool) +{ + return property_map(prop_hash, NULL, scratch_pool); +} - env->CallObjectMethod(map, put_mid, jpropName, jpropVal); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; +jobject CreateJ::PropertyMap(apr_array_header_t* prop_diffs, + apr_pool_t* scratch_pool) +{ + return property_map(NULL, prop_diffs, scratch_pool); +} - env->DeleteLocalRef(jpropName); - env->DeleteLocalRef(jpropVal); - } +void CreateJ::FillPropertyMap(jobject map, apr_hash_t* prop_hash, + apr_pool_t* scratch_pool, jmethodID put_mid) +{ + fill_property_map(map, prop_hash, NULL, scratch_pool, put_mid); +} - return env->PopLocalFrame(map); +void CreateJ::FillPropertyMap(jobject map, apr_array_header_t* prop_diffs, + apr_pool_t* scratch_pool, jmethodID put_mid) +{ + fill_property_map(map, NULL, prop_diffs, scratch_pool, put_mid); } jobject CreateJ::InheritedProps(apr_array_header_t *iprops) @@ -1123,7 +1326,7 @@ jobject CreateJ::InheritedProps(apr_array_header_t *iprops) } jclass item_cls = env->FindClass( - JAVA_PACKAGE"/callback/InheritedProplistCallback$InheritedItem"); + JAVAHL_CLASS("/callback/InheritedProplistCallback$InheritedItem")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NULL; @@ -1169,6 +1372,64 @@ jobject CreateJ::InheritedProps(apr_array_header_t *iprops) return env->PopLocalFrame(array); } +jobject CreateJ::Mergeinfo(svn_mergeinfo_t mergeinfo, apr_pool_t* scratch_pool) +{ + if (mergeinfo == NULL) + return NULL; + + // Transform mergeinfo into Java Mergeinfo object. + JNIEnv *env = JNIUtil::getEnv(); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Mergeinfo")); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + static jmethodID ctor = 0; + if (ctor == 0) + { + ctor = env->GetMethodID(clazz, "<init>", "()V"); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + } + + static jmethodID addRevisions = 0; + if (addRevisions == 0) + { + addRevisions = env->GetMethodID(clazz, "addRevisions", + "(Ljava/lang/String;" + "Ljava/util/List;)V"); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + } + + jobject jmergeinfo = env->NewObject(clazz, ctor); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + apr_hash_index_t *hi; + for (hi = apr_hash_first(scratch_pool, mergeinfo); + hi; + hi = apr_hash_next(hi)) + { + const void *path; + void *val; + apr_hash_this(hi, &path, NULL, &val); + + jstring jpath = + JNIUtil::makeJString(static_cast<const char*>(path)); + jobject jranges = + RevisionRangeList(static_cast<svn_rangelist_t*>(val)).toList(); + + env->CallVoidMethod(jmergeinfo, addRevisions, jpath, jranges); + + env->DeleteLocalRef(jranges); + env->DeleteLocalRef(jpath); + } + + env->DeleteLocalRef(clazz); + + return jmergeinfo; +} + jobject CreateJ::Set(std::vector<jobject> &objects) { diff --git a/subversion/bindings/javahl/native/CreateJ.h b/subversion/bindings/javahl/native/CreateJ.h index 7fe5363..8f4f36e 100644 --- a/subversion/bindings/javahl/native/CreateJ.h +++ b/subversion/bindings/javahl/native/CreateJ.h @@ -31,6 +31,7 @@ #include "svn_wc.h" #include "svn_repos.h" #include "svn_client.h" +#include "svn_mergeinfo.h" #include <vector> @@ -49,12 +50,19 @@ class CreateJ Checksum(const svn_checksum_t *checksum); static jobject + DirEntry(const char *path, const char *absPath, + const svn_dirent_t *dirent); + + static jobject Info(const char *path, const svn_client_info2_t *info); static jobject Lock(const svn_lock_t *lock); static jobject + LockMap(const apr_hash_t *locks, apr_pool_t *pool); + + static jobject ChangedPath(const char *path, svn_log_changed_path2_t *log_item); static jobject @@ -74,17 +82,30 @@ class CreateJ CommitInfo(const svn_commit_info_t *info); static jobject - RevisionRangeList(svn_rangelist_t *ranges); + StringSet(const apr_array_header_t *strings); static jobject - StringSet(apr_array_header_t *strings); + PropertyMap(apr_hash_t *prop_hash, apr_pool_t* scratch_pool = NULL); static jobject - PropertyMap(apr_hash_t *prop_hash); + PropertyMap(apr_array_header_t* prop_diffs, apr_pool_t* scratch_pool = NULL); + + static void + FillPropertyMap(jobject map, apr_hash_t* prop_hash, + apr_pool_t* scratch_pool, + jmethodID put_method_id = 0); + + static void + FillPropertyMap(jobject map, apr_array_header_t* prop_diffs, + apr_pool_t* scratch_pool, + jmethodID put_method_id = 0); static jobject InheritedProps(apr_array_header_t *inherited_props); + static jobject + Mergeinfo(svn_mergeinfo_t mergeinfo, apr_pool_t* scratch_pool); + /* This creates a set of Objects. It derefs the members of the vector * after putting them in the set, so they caller doesn't need to. */ static jobject diff --git a/subversion/bindings/javahl/native/Credential.cpp b/subversion/bindings/javahl/native/Credential.cpp new file mode 100644 index 0000000..8791de3 --- /dev/null +++ b/subversion/bindings/javahl/native/Credential.cpp @@ -0,0 +1,85 @@ +/** + * @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 + */ + +#include "Credential.hpp" + +#include "JNIUtil.h" + +namespace JavaHL { + +// Class JavaHL::Credential +const char* const Credential::m_class_name = + JAVAHL_CLASS("/SVNUtil$Credential"); + +Credential::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_ctor( + env.GetMethodID(cls, "<init>", + "(" JAVAHL_ARG("/SVNUtil$Credential$Kind;") + "Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Ljava/lang/String;" + JAVAHL_ARG("/callback/AuthnCallback$SSLServerCertInfo;") + JAVAHL_ARG("/callback/AuthnCallback$SSLServerCertFailures;") + "Ljava/lang/String;)V")) +{} + +Credential::ClassImpl::~ClassImpl() {} + +Credential::Credential(::Java::Env env, jobject kind, + const ::Java::String& realm, + const ::Java::String& store, + const ::Java::String& username, + const ::Java::String& password, + jobject info, jobject failures, + const ::Java::String& passphrase) + : ::Java::Object(env, ::Java::ClassCache::get_credential(env)) +{ + set_this(env.NewObject(get_class(), impl().m_mid_ctor, + kind, realm.get(), store.get(), + username.get(), password.get(), + info, failures, passphrase.get())); +} + +// Enum JavaHL::Credential::Kind +const char* const Credential::Kind::m_class_name = + JAVAHL_CLASS("/SVNUtil$Credential$Kind"); + +Credential::Kind::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_static_mid_from_string( + env.GetStaticMethodID(cls, "fromString", + "(Ljava/lang/String;)" + JAVAHL_ARG("/SVNUtil$Credential$Kind;"))) +{} + +Credential::Kind::ClassImpl::~ClassImpl() {} + +Credential::Kind::Kind(::Java::Env env, + const ::Java::String& value) + : ::Java::Object(env, ::Java::ClassCache::get_credential_kind(env)) +{ + set_this(env.CallStaticObjectMethod( + get_class(), impl().m_static_mid_from_string, value.get())); +} + +} // namespace JavaHL diff --git a/subversion/bindings/javahl/native/Credential.hpp b/subversion/bindings/javahl/native/Credential.hpp new file mode 100644 index 0000000..5c0c66d --- /dev/null +++ b/subversion/bindings/javahl/native/Credential.hpp @@ -0,0 +1,126 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_CREDENTIAL_HPP +#define SVN_JAVAHL_CREDENTIAL_HPP + +#include "jniwrapper/jni_object.hpp" +#include "jniwrapper/jni_string.hpp" + +#include "AuthnCallback.hpp" + +namespace JavaHL { + +/** + * Object wrapper for @c org.apache.subversion.javahl.SVNUtil.Credential. + * + * @since New in 1.9. + */ +class Credential : public ::Java::Object +{ +public: + /** + * Object wrapper for @c ...Credential$Kind. + */ + class Kind : public ::Java::Object + { + public: + /** + * Constructs a and wraps a new Credential$Kind object. + */ + explicit Kind(::Java::Env env, const ::Java::String& value); + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_static_mid_from_string; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + }; + + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit Credential(::Java::Env env, jobject jthis) + : ::Java::Object(env, ::Java::ClassCache::get_credential(env), jthis) + {} + + /** + * Constructs and wraps a new Credential object + */ + explicit Credential(::Java::Env env, jobject kind, + const ::Java::String& realm, + const ::Java::String& store, + const ::Java::String& username, + const ::Java::String& password, + jobject info, jobject failures, + const ::Java::String& passphrase); + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_ctor; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace JavaHL + +#endif // SVN_JAVAHL_CREDENTIAL_HPP diff --git a/subversion/bindings/javahl/native/DiffOptions.cpp b/subversion/bindings/javahl/native/DiffOptions.cpp index 8d7a8eb..f666f3a 100644 --- a/subversion/bindings/javahl/native/DiffOptions.cpp +++ b/subversion/bindings/javahl/native/DiffOptions.cpp @@ -71,5 +71,29 @@ apr_array_header_t *DiffOptions::optionsArray(SVN::Pool &resultPool) const if (flags & SHOW_C_FUNCTION) APR_ARRAY_PUSH(opt, const char*) = "--show-c-function"; + /* TODO: Support -U (context size) */ + + return opt; +} + +svn_diff_file_options_t *DiffOptions::fileOptions(SVN::Pool &resultPool) const +{ + svn_diff_file_options_t *opt; + + opt = svn_diff_file_options_create(resultPool.getPool()); + + if (flags & IGNORE_ALL_SPACE) + opt->ignore_space = svn_diff_file_ignore_space_all; + else if (flags & IGNORE_SPACE_CHANGE) + opt->ignore_eol_style = svn_diff_file_ignore_space_change; + + if (flags & IGNORE_EOL_STYLE) + opt->ignore_eol_style = TRUE; + + if (flags & SHOW_C_FUNCTION) + opt->show_c_function = TRUE; + + /* TODO: Support context size */ + return opt; } diff --git a/subversion/bindings/javahl/native/DiffOptions.h b/subversion/bindings/javahl/native/DiffOptions.h index e59b2f9..0dc4d9a 100644 --- a/subversion/bindings/javahl/native/DiffOptions.h +++ b/subversion/bindings/javahl/native/DiffOptions.h @@ -29,6 +29,7 @@ #include <apr_tables.h> #include "svn_types.h" +#include "svn_diff.h" #include "Pool.h" #include "JNIUtil.h" @@ -38,6 +39,7 @@ class DiffOptions DiffOptions(jobject joptions); apr_array_header_t *optionsArray(SVN::Pool &resultPool) const; + svn_diff_file_options_t *fileOptions(SVN::Pool &resultPool) const; svn_boolean_t useGitDiffFormat() const { diff --git a/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp b/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp index f4cf052..8ad3a91 100644 --- a/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp +++ b/subversion/bindings/javahl/native/DiffSummaryReceiver.cpp @@ -69,12 +69,12 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff, if (callback == 0) { // Initialize the method ID. - clazz = env->FindClass(JAVA_PACKAGE "/callback/DiffSummaryCallback"); + clazz = env->FindClass(JAVAHL_CLASS("/callback/DiffSummaryCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); callback = env->GetMethodID(clazz, "onSummary", - "(L"JAVA_PACKAGE"/DiffSummary;)V"); + "(" JAVAHL_ARG("/DiffSummary;") ")V"); if (JNIUtil::isJavaExceptionThrown() || callback == 0) POP_AND_RETURN(SVN_NO_ERROR); } @@ -82,7 +82,7 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff, // Do some prep work for tranforming the DIFF parameter into a // Java equivalent. static jmethodID ctor = 0; - clazz = env->FindClass(JAVA_PACKAGE "/DiffSummary"); + clazz = env->FindClass(JAVAHL_CLASS("/DiffSummary")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); @@ -90,8 +90,8 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff, { ctor = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;" - "L"JAVA_PACKAGE"/DiffSummary$DiffKind;Z" - "L"JAVA_PACKAGE"/types/NodeKind;)V"); + JAVAHL_ARG("/DiffSummary$DiffKind;") "Z" + JAVAHL_ARG("/types/NodeKind;") ")V"); if (JNIUtil::isJavaExceptionThrown() || ctor == 0) POP_AND_RETURN(SVN_NO_ERROR); } @@ -117,8 +117,5 @@ DiffSummaryReceiver::onSummary(const svn_client_diff_summarize_t *diff, // Invoke the Java DiffSummaryReceiver callback. env->CallVoidMethod(m_receiver, callback, jDiffSummary); - // We return whether an exception was thrown or not. - - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } diff --git a/subversion/bindings/javahl/native/EditorCallbacks.cpp b/subversion/bindings/javahl/native/EditorCallbacks.cpp new file mode 100644 index 0000000..703f6bd --- /dev/null +++ b/subversion/bindings/javahl/native/EditorCallbacks.cpp @@ -0,0 +1,124 @@ +/** + * @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 + */ + +#include "EditorCallbacks.hpp" + +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_string.hpp" +#include "jniwrapper/jni_io_stream.hpp" + +#include "Utility.hpp" + +namespace JavaHL { + +// class JavaHL::ProvideBaseCallback + +const char* const ProvideBaseCallback::m_class_name = + JAVAHL_CLASS("/ISVNEditor$ProvideBaseCallback"); + +ProvideBaseCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_get_contents( + env.GetMethodID( + cls, "getContents", + "(Ljava/lang/String;)" + JAVAHL_ARG("/ISVNEditor$ProvideBaseCallback$ReturnValue;"))) +{} + +ProvideBaseCallback::ClassImpl::~ClassImpl() {} + + +const char* const ProvideBaseCallback::ReturnValue::m_class_name = + JAVAHL_CLASS("/ISVNEditor$ProvideBaseCallback$ReturnValue"); + +ProvideBaseCallback::ReturnValue::ClassImpl::ClassImpl( + ::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_fid_contents(env.GetFieldID(cls, "contents", "Ljava/io/InputStream;")), + m_fid_revision(env.GetFieldID(cls, "revision", "J")) +{} + +ProvideBaseCallback::ReturnValue::ClassImpl::~ClassImpl() {} + + +svn_stream_t* +ProvideBaseCallback::ReturnValue::get_global_stream(apr_pool_t* pool) const +{ + jobject jstream = m_env.GetObjectField(m_jthis, impl().m_fid_contents); + return ::Java::InputStream::get_global_stream(m_env, jstream, pool); +} + + +// class JavaHL::ProvidePropsCallback + +const char* const ProvidePropsCallback::m_class_name = + JAVAHL_CLASS("/ISVNEditor$ProvidePropsCallback"); + +ProvidePropsCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_get_props( + env.GetMethodID( + cls, "getProperties", + "(Ljava/lang/String;)" + JAVAHL_ARG("/ISVNEditor$ProvidePropsCallback$ReturnValue;"))) +{} + +ProvidePropsCallback::ClassImpl::~ClassImpl() {} + + +const char* const ProvidePropsCallback::ReturnValue::m_class_name = + JAVAHL_CLASS("/ISVNEditor$ProvidePropsCallback$ReturnValue"); + +ProvidePropsCallback::ReturnValue::ClassImpl::ClassImpl( + ::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_fid_properties(env.GetFieldID(cls, "properties", "Ljava/util/Map;")), + m_fid_revision(env.GetFieldID(cls, "revision", "J")) +{} + +ProvidePropsCallback::ReturnValue::ClassImpl::~ClassImpl() {} + +apr_hash_t* +ProvidePropsCallback::ReturnValue::get_property_hash(apr_pool_t* pool) const +{ + jobject jproperties = m_env.GetObjectField(m_jthis, impl().m_fid_properties); + return Util::make_property_hash(m_env, jproperties, pool); +} + + +// class JavaHL::GetNodeKindCallback + +const char* const GetNodeKindCallback::m_class_name = + JAVAHL_CLASS("/ISVNEditor$GetNodeKindCallback"); + +GetNodeKindCallback::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_get_kind( + env.GetMethodID(cls, + "getKind", + "(Ljava/lang/String;J)" + JAVAHL_ARG("/types/NodeKind;"))) +{} + +GetNodeKindCallback::ClassImpl::~ClassImpl() {} +} // namespace JavaHL diff --git a/subversion/bindings/javahl/native/EditorCallbacks.hpp b/subversion/bindings/javahl/native/EditorCallbacks.hpp new file mode 100644 index 0000000..8918ee3 --- /dev/null +++ b/subversion/bindings/javahl/native/EditorCallbacks.hpp @@ -0,0 +1,328 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_EDITOR_CALLBACKS_HPP +#define SVN_JAVAHL_EDITOR_CALLBACKS_HPP + +#include "svn_io.h" + +#include "Pool.h" + +#include "jniwrapper/jni_object.hpp" + +namespace JavaHL { + +/** + * Object wrapper for the base contents callback interface in + * @c org.apache.subversion.javahl.ISVNEditor. + * + * @since New in 1.9. + */ +class ProvideBaseCallback : public ::Java::Object +{ +public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit ProvideBaseCallback(::Java::Env env, jobject jthis) + : ::Java::Object(env, + ::Java::ClassCache::get_editor_provide_base_cb(env), + jthis) + {} + + /** + * Invokes the callback. + */ + jobject operator()(jstring relpath) const + { + return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_contents, + relpath); + } + + class ReturnValue : public ::Java::Object + { + public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit ReturnValue(::Java::Env env, jobject jthis) + : Java::Object(env, + ::Java::ClassCache::get_editor_provide_base_cb_ret(env), + jthis) + {} + + /** + * Returns an @c svn_stream_t for the contents stream in the + * wrapped return value, allocated from @a pool. The wrapped Java + * stream will live as long as @a pool. + */ + svn_stream_t* get_global_stream(apr_pool_t* pool) const; + + /** + * Returns an @c svn_stream_t for the contents stream in the + * wrapped return value, allocated from @a pool. The wrapped Java + * stream will live as long as @a pool. + */ + svn_stream_t* get_global_stream(const SVN::Pool& pool) const + { + return get_global_stream(pool.getPool()); + } + + /** + * Returns the revision in the wrapped return value. + */ + jlong get_revision() const + { + return m_env.GetLongField(m_jthis, impl().m_fid_revision); + } + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::FieldID m_fid_contents; + const ::Java::FieldID m_fid_revision; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + }; + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_get_contents; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + + +/** + * Object wrapper for the props callback interface in + * @c org.apache.subversion.javahl.ISVNEditor. + * + * @since New in 1.9. + */ +class ProvidePropsCallback : public ::Java::Object +{ +public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit ProvidePropsCallback(::Java::Env env, jobject jthis) + : ::Java::Object(env, + ::Java::ClassCache::get_editor_provide_props_cb(env), + jthis) + {} + + /** + * Invokes the callback. + */ + jobject operator()(jstring relpath) const + { + return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_props, relpath); + } + + class ReturnValue : public ::Java::Object + { + public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit ReturnValue(::Java::Env env, jobject jthis) + : Java::Object(env, + ::Java::ClassCache::get_editor_provide_props_cb_ret(env), + jthis) + {} + + /** + * Returns an @c apr_hash_t of the node properties in the wrapped + * return value, allocated from @a pool. + */ + apr_hash_t* get_property_hash(apr_pool_t* pool) const; + + /** + * Returns an @c apr_hash_t of the node properties in the wrapped + * return value, allocated from @a pool. + */ + apr_hash_t* get_property_hash(const SVN::Pool& pool) const + { + return get_property_hash(pool.getPool()); + } + + /** + * Returns the revision in the wrapped return value. + */ + jlong get_revision() const + { + return m_env.GetLongField(m_jthis, impl().m_fid_revision); + } + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::FieldID m_fid_properties; + const ::Java::FieldID m_fid_revision; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + }; + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_get_props; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + + +/** + * Object wrapper for the kind callback interface in + * @c org.apache.subversion.javahl.ISVNEditor. + * + * @since New in 1.9. + */ +class GetNodeKindCallback : public ::Java::Object +{ +public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit GetNodeKindCallback(::Java::Env env, jobject jthis) + : ::Java::Object(env, + ::Java::ClassCache::get_editor_get_kind_cb(env), + jthis) + {} + + /** + * Invokes the callback. + */ + jobject operator()(jstring relpath, jlong revision) const + { + return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_kind, + relpath, revision); + } + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_get_kind; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace JavaHL + +#endif // SVN_JAVAHL_EDITOR_CALLBACKS_HPP diff --git a/subversion/bindings/javahl/native/EditorProxy.cpp b/subversion/bindings/javahl/native/EditorProxy.cpp new file mode 100644 index 0000000..8d9d65e --- /dev/null +++ b/subversion/bindings/javahl/native/EditorProxy.cpp @@ -0,0 +1,566 @@ +/** + * @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 EditorProxy.h + * @brief Interface of all editor proxy classes + */ +#include <apr_pools.h> + +#include "JNIUtil.h" +#include "JNIStackElement.h" +#include "EditorProxy.h" +#include "CreateJ.h" +#include "EnumMapper.h" + +// Newstyle: stream wrapper +#include <memory> +#include "NativeStream.hpp" +#include "jniwrapper/jni_stack.hpp" + +#include "svn_error.h" +#include "svn_private_config.h" + +EditorProxy::EditorProxy(jobject jeditor, apr_pool_t* edit_pool, + const char* repos_root_url, const char* base_relpath, + svn_cancel_func_t cancel_func, void* cancel_baton, + const EditorProxyCallbacks& callbacks) + : m_valid(false), + m_jeditor(JNIUtil::getEnv()->NewGlobalRef(jeditor)), + m_edit_pool(edit_pool), + m_repos_root_url(NULL), + m_base_relpath(NULL), + m_found_paths(false), + m_editor(NULL), + m_delta_editor(NULL), + m_delta_baton(NULL), + m_proxy_callbacks(callbacks) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::EditorProxy(...)\n"); + + static const svn_editor_cb_many_t editor_many_cb = { + cb_add_directory, cb_add_file, cb_add_symlink, cb_add_absent, + cb_alter_directory, cb_alter_file, cb_alter_symlink, + cb_delete, cb_copy, cb_move, + cb_complete, cb_abort + }; + + SVN::Pool scratchPool(edit_pool); + apr_pool_t* scratch_pool = scratchPool.getPool(); + + svn_error_t* err = svn_editor_create(&m_editor, this, + cancel_func, cancel_baton, + edit_pool, scratch_pool); + if (!err) + err = svn_editor_setcb_many(m_editor, &editor_many_cb, scratch_pool); + if (!err) + { + m_repos_root_url = + static_cast<const char*>(apr_pstrdup(edit_pool, repos_root_url)); + m_base_relpath = + static_cast<const char*>(apr_pstrdup(edit_pool, base_relpath)); + + svn_boolean_t found_paths; + err = svn_delta__delta_from_editor(&m_delta_editor, + &m_delta_baton, + m_editor, + m_proxy_callbacks.m_unlock_func, + m_proxy_callbacks.m_baton, + &found_paths, + repos_root_url, base_relpath, + m_proxy_callbacks.m_fetch_props_func, + m_proxy_callbacks.m_baton, + m_proxy_callbacks.m_fetch_base_func, + m_proxy_callbacks.m_baton, + &m_proxy_callbacks.m_extra_baton, + edit_pool); + m_found_paths = found_paths; + } + + if (err) + JNIUtil::handleSVNError(err); + else + m_valid = true; +} + +EditorProxy::~EditorProxy() +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::~EditorProxy()\n"); + + if (m_jeditor) + JNIUtil::getEnv()->DeleteGlobalRef(m_jeditor); +} + +namespace { +inline svn_error_t* invalid_editor() +{ + return svn_error_create(SVN_ERR_RA_SVN_EDIT_ABORTED, NULL, + _("The editor is not valid")); +} + +svn_error_t* +get_editor_method(jmethodID& mid, const char* name, const char* sig) +{ + if (0 != mid) + return SVN_NO_ERROR; // Already known. + + JNIEnv* env = JNIUtil::getEnv(); + jclass cls = env->FindClass(JAVAHL_CLASS("/ISVNEditor")); + SVN_JNI_CATCH(,SVN_ERR_RA_SVN_EDIT_ABORTED); + SVN_JNI_CATCH(mid = env->GetMethodID(cls, name, sig), + SVN_ERR_RA_SVN_EDIT_ABORTED); + return SVN_NO_ERROR; +} + +jobject wrap_input_stream(svn_stream_t* stream) +{ + std::auto_ptr<JavaHL::NativeInputStream> + wrapped(new JavaHL::NativeInputStream()); + apr_pool_t* const wrapped_pool = wrapped->get_pool().getPool(); + wrapped->set_stream(svn_stream_disown(stream, wrapped_pool)); + const jobject jstream = wrapped->create_java_wrapper(); + wrapped.release(); + return jstream; +} +} // anonymous namespace + +svn_error_t* +EditorProxy::cb_add_directory(void *baton, + const char *relpath, + const apr_array_header_t *children, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_directory('%s')\n", relpath); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "addDirectory", + "(Ljava/lang/String;" + "Ljava/lang/Iterable;" + "Ljava/util/Map;J)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jchildren = (!children ? NULL : CreateJ::StringSet(children)); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jprops = CreateJ::PropertyMap(props, scratch_pool); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jchildren, jprops, + jlong(replaces_rev)); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_add_file(void *baton, + const char *relpath, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_file('%s')\n", relpath); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "addFile", + "(Ljava/lang/String;" + JAVAHL_ARG("/types/Checksum;") + "Ljava/io/InputStream;" + "Ljava/util/Map;J)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jchecksum = CreateJ::Checksum(checksum); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jprops = CreateJ::PropertyMap(props, scratch_pool); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + jobject jcontents = NULL; + if (contents != NULL) + jcontents = wrap_input_stream(contents); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jchecksum, jcontents, + jprops, jlong(replaces_rev)); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_add_symlink(void *baton, + const char *relpath, + const char *target, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_symlink('%s')\n", relpath); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "addSymlink", + "(Ljava/lang/String;" + "Ljava/lang/String;" + "Ljava/util/Map;J)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jstring jtarget = JNIUtil::makeJString(target); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jprops = CreateJ::PropertyMap(props, scratch_pool); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jtarget, jprops, + jlong(replaces_rev)); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_add_absent(void *baton, + const char *relpath, + svn_node_kind_t kind, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_add_absent('%s')\n", relpath); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "addAbsent", + "(Ljava/lang/String;" + JAVAHL_ARG("/types/NodeKind;") + "J)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jkind = EnumMapper::mapNodeKind(kind); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jkind, + jlong(replaces_rev)); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_alter_directory(void *baton, + const char *relpath, + svn_revnum_t revision, + const apr_array_header_t *children, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_alter_directory('%s', r%lld)\n", + //DEBUG: relpath, static_cast<long long>(revision)); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "alterDirectory", + "(Ljava/lang/String;J" + "Ljava/lang/Iterable;" + "Ljava/util/Map;)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jchildren = (!children ? NULL : CreateJ::StringSet(children)); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jprops = CreateJ::PropertyMap(props, scratch_pool); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jlong(revision), + jchildren, jprops); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_alter_file(void *baton, + const char *relpath, + svn_revnum_t revision, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_alter_file('%s', r%lld)\n", + //DEBUG: relpath, static_cast<long long>(revision)); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "alterFile", + "(Ljava/lang/String;J" + JAVAHL_ARG("/types/Checksum;") + "Ljava/io/InputStream;" + "Ljava/util/Map;)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jchecksum = CreateJ::Checksum(checksum); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jprops = CreateJ::PropertyMap(props, scratch_pool); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + jobject jcontents = NULL; + if (contents != NULL) + jcontents = wrap_input_stream(contents); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jlong(revision), + jchecksum, jcontents, jprops); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_alter_symlink(void *baton, + const char *relpath, + svn_revnum_t revision, + const char *target, + apr_hash_t *props, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_alter_symlink('%s', r%lld)\n", + //DEBUG: relpath, static_cast<long long>(revision)); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "alterSymlink", + "(Ljava/lang/String;J" + "Ljava/lang/String;" + "Ljava/util/Map;)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jstring jtarget = JNIUtil::makeJString(target); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jobject jprops = CreateJ::PropertyMap(props, scratch_pool); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jrelpath, jlong(revision), + jtarget, jprops); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_delete(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_delete('%s', r%lld)\n", + //DEBUG: relpath, static_cast<long long>(revision)); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "delete", + "(Ljava/lang/String;J)V")); + + jstring jrelpath = JNIUtil::makeJString(relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, jrelpath); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_copy(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_copy('%s', r%lld, '%s')\n", + //DEBUG: src_relpath, static_cast<long long>(src_revision), dst_relpath); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "copy", + "(Ljava/lang/String;J" + "Ljava/lang/String;J)V")); + + jstring jsrc_relpath = JNIUtil::makeJString(src_relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jstring jdst_relpath = JNIUtil::makeJString(dst_relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jsrc_relpath, jlong(src_revision), + jdst_relpath, jlong(replaces_rev)); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_move(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_move('%s', r%lld, '%s')\n", + //DEBUG: src_relpath, static_cast<long long>(src_revision), dst_relpath); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "move", + "(Ljava/lang/String;J" + "Ljava/lang/String;J)V")); + + jstring jsrc_relpath = JNIUtil::makeJString(src_relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + jstring jdst_relpath = JNIUtil::makeJString(dst_relpath); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + env.CallVoidMethod(ep->m_jeditor, mid, + jsrc_relpath, jlong(src_revision), + jdst_relpath, jlong(replaces_rev)); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_complete(void *baton, apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_complete()\n"); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + ep->m_valid = false; + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "complete", "()V")); + + env.CallVoidMethod(ep->m_jeditor, mid); + }); + return SVN_NO_ERROR; +} + +svn_error_t* +EditorProxy::cb_abort(void *baton, apr_pool_t *scratch_pool) +{ + //DEBUG:fprintf(stderr, " (n) EditorProxy::cb_abort()\n"); + const ::Java::Env env; + SVN_JAVAHL_CATCH(env, SVN_ERR_RA_SVN_EDIT_ABORTED, + { + ::Java::LocalFrame frame(env); + + EditorProxy* const ep = static_cast<EditorProxy*>(baton); + if (!ep || !ep->m_valid) + return invalid_editor(); + ep->m_valid = false; + + static jmethodID mid = 0; + SVN_ERR(get_editor_method(mid, "abort", "()V")); + + env.CallVoidMethod(ep->m_jeditor, mid); + }); + return SVN_NO_ERROR; +} diff --git a/subversion/bindings/javahl/native/EditorProxy.h b/subversion/bindings/javahl/native/EditorProxy.h new file mode 100644 index 0000000..3ca005c --- /dev/null +++ b/subversion/bindings/javahl/native/EditorProxy.h @@ -0,0 +1,153 @@ +/** + * @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 EditorProxy.h + * @brief Interface of all editor proxy classes + */ + +#ifndef JAVAHL_EDITOR_PROXY_H +#define JAVAHL_EDITOR_PROXY_H + +#include "svn_delta.h" +#include "private/svn_editor.h" +#include "private/svn_delta_private.h" + +/** + * These callbacks are needed by the delta-to-Ev2 shims. + */ +struct EditorProxyCallbacks +{ + svn_delta__unlock_func_t m_unlock_func; + svn_delta_fetch_props_func_t m_fetch_props_func; + svn_delta_fetch_base_func_t m_fetch_base_func; + struct svn_delta__extra_baton m_extra_baton; + void* m_baton; +}; + +/** + * This is a proxy object that translates Ev2 operations (possibly + * implemented through shims) into calls to a Java editor + * implementation. + */ +class EditorProxy +{ +public: + EditorProxy(jobject jeditor, apr_pool_t* edit_pool, + const char* repos_root_url, const char* base_relpath, + svn_cancel_func_t cancel_func, void* cancel_baton, + const EditorProxyCallbacks& callbacks); + ~EditorProxy(); + + const svn_delta_editor_t* delta_editor() const + { + return m_delta_editor; + } + + void* delta_baton() const + { + return m_delta_baton; + } + +private: + EditorProxy(const EditorProxy&); // noncopyable + + static svn_error_t* cb_add_directory(void *baton, + const char *relpath, + const apr_array_header_t *children, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool); + static svn_error_t* cb_add_file(void *baton, + const char *relpath, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool); + static svn_error_t* cb_add_symlink(void *baton, + const char *relpath, + const char *target, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool); + static svn_error_t* cb_add_absent(void *baton, + const char *relpath, + svn_node_kind_t kind, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool); + static svn_error_t* cb_alter_directory(void *baton, + const char *relpath, + svn_revnum_t revision, + const apr_array_header_t *children, + apr_hash_t *props, + apr_pool_t *scratch_pool); + static svn_error_t* cb_alter_file(void *baton, + const char *relpath, + svn_revnum_t revision, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + apr_pool_t *scratch_pool); + static svn_error_t* cb_alter_symlink(void *baton, + const char *relpath, + svn_revnum_t revision, + const char *target, + apr_hash_t *props, + apr_pool_t *scratch_pool); + static svn_error_t* cb_delete(void *baton, + const char *relpath, + svn_revnum_t revision, + apr_pool_t *scratch_pool); + static svn_error_t* cb_copy(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool); + static svn_error_t* cb_move(void *baton, + const char *src_relpath, + svn_revnum_t src_revision, + const char *dst_relpath, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool); + static svn_error_t* cb_complete(void *baton, + apr_pool_t *scratch_pool); + static svn_error_t* cb_abort(void *baton, + apr_pool_t *scratch_pool); + +private: + bool m_valid; + jobject m_jeditor; ///< Reference to Java editor implementation + apr_pool_t* m_edit_pool; + + const char* m_repos_root_url; ///< The root of the repository + const char* m_base_relpath; ///< The root of the session within the repo + bool m_found_paths; ///< Returned paths are absolute + + svn_editor_t* m_editor; + const svn_delta_editor_t* m_delta_editor; + void* m_delta_baton; + EditorProxyCallbacks m_proxy_callbacks; +}; + + +#endif // JAVAHL_EDITOR_PROXY_H diff --git a/subversion/bindings/javahl/native/EnumMapper.cpp b/subversion/bindings/javahl/native/EnumMapper.cpp index 089ef6f..8e8ca50 100644 --- a/subversion/bindings/javahl/native/EnumMapper.cpp +++ b/subversion/bindings/javahl/native/EnumMapper.cpp @@ -32,44 +32,18 @@ #include "JNIStringHolder.h" #include "../include/org_apache_subversion_javahl_CommitItemStateFlags.h" -/** - * Map a C commit state flag constant to the Java constant. - * @param state the C commit state flage constant - * @returns the Java constant - */ -jint EnumMapper::mapCommitMessageStateFlags(apr_byte_t flags) -{ - jint jstateFlags = 0; - if (flags & SVN_CLIENT_COMMIT_ITEM_ADD) - jstateFlags |= - org_apache_subversion_javahl_CommitItemStateFlags_Add; - if (flags & SVN_CLIENT_COMMIT_ITEM_DELETE) - jstateFlags |= - org_apache_subversion_javahl_CommitItemStateFlags_Delete; - if (flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) - jstateFlags |= - org_apache_subversion_javahl_CommitItemStateFlags_TextMods; - if (flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) - jstateFlags |= - org_apache_subversion_javahl_CommitItemStateFlags_PropMods; - if (flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) - jstateFlags |= - org_apache_subversion_javahl_CommitItemStateFlags_IsCopy; - return jstateFlags; -} - jobject EnumMapper::mapChangePathAction(const char action) { switch (action) { case 'A': - return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 0); + return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 0); case 'D': - return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 1); + return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 1); case 'R': - return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 2); + return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 2); case 'M': - return mapEnum(JAVA_PACKAGE"/types/ChangePath$Action", 3); + return mapEnum(JAVAHL_CLASS("/types/ChangePath$Action"), 3); default: return NULL; } @@ -81,7 +55,7 @@ jobject EnumMapper::mapChangePathAction(const char action) jobject EnumMapper::mapNotifyState(svn_wc_notify_state_t state) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ClientNotifyInformation$Status", + return mapEnum(JAVAHL_CLASS("/ClientNotifyInformation$Status"), static_cast<int>(state)); } @@ -91,14 +65,14 @@ jobject EnumMapper::mapNotifyState(svn_wc_notify_state_t state) jobject EnumMapper::mapNotifyAction(svn_wc_notify_action_t action) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ClientNotifyInformation$Action", + return mapEnum(JAVAHL_CLASS("/ClientNotifyInformation$Action"), static_cast<int>(action)); } jobject EnumMapper::mapReposNotifyNodeAction(svn_node_action action) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ReposNotifyInformation$NodeAction", + return mapEnum(JAVAHL_CLASS("/ReposNotifyInformation$NodeAction"), static_cast<int>(action)); } @@ -108,7 +82,7 @@ jobject EnumMapper::mapReposNotifyNodeAction(svn_node_action action) jobject EnumMapper::mapReposNotifyAction(svn_repos_notify_action_t action) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ReposNotifyInformation$Action", + return mapEnum(JAVAHL_CLASS("/ReposNotifyInformation$Action"), static_cast<int>(action)); } @@ -118,7 +92,7 @@ jobject EnumMapper::mapReposNotifyAction(svn_repos_notify_action_t action) jobject EnumMapper::mapNodeKind(svn_node_kind_t nodeKind) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/types/NodeKind", + return mapEnum(JAVAHL_CLASS("/types/NodeKind"), static_cast<int>(nodeKind)); } @@ -128,7 +102,7 @@ jobject EnumMapper::mapNodeKind(svn_node_kind_t nodeKind) jobject EnumMapper::mapNotifyLockState(svn_wc_notify_lock_state_t state) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ClientNotifyInformation$LockStatus", + return mapEnum(JAVAHL_CLASS("/ClientNotifyInformation$LockStatus"), static_cast<int>(state)); } @@ -138,7 +112,7 @@ jobject EnumMapper::mapNotifyLockState(svn_wc_notify_lock_state_t state) jobject EnumMapper::mapScheduleKind(svn_wc_schedule_t schedule) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/types/Info$ScheduleKind", + return mapEnum(JAVAHL_CLASS("/types/Info$ScheduleKind"), static_cast<int>(schedule)); } @@ -149,91 +123,121 @@ jobject EnumMapper::mapStatusKind(svn_wc_status_kind svnKind) { // We're assuming a valid value for the C enum above // The offset here is +1 - return mapEnum(JAVA_PACKAGE"/types/Status$Kind", + return mapEnum(JAVAHL_CLASS("/types/Status$Kind"), static_cast<int>(svnKind) - 1); } jobject EnumMapper::mapChecksumKind(svn_checksum_kind_t kind) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/types/Checksum$Kind", + return mapEnum(JAVAHL_CLASS("/types/Checksum$Kind"), static_cast<int>(kind)); } jobject EnumMapper::mapConflictKind(svn_wc_conflict_kind_t kind) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Kind", + return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Kind"), static_cast<int>(kind)); } jobject EnumMapper::mapConflictAction(svn_wc_conflict_action_t action) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Action", + return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Action"), static_cast<int>(action)); } jobject EnumMapper::mapConflictReason(svn_wc_conflict_reason_t reason) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Reason", + return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Reason"), static_cast<int>(reason)); } int EnumMapper::toMergeinfoLogKind(jobject jLogKind) { - return getOrdinal(JAVA_PACKAGE"/types/Mergeinfo$LogKind", jLogKind); + return getOrdinal(JAVAHL_CLASS("/types/Mergeinfo$LogKind"), jLogKind); } int EnumMapper::toLogLevel(jobject jLogLevel) { - return getOrdinal(JAVA_PACKAGE"/SVNClient$ClientLogLevel", jLogLevel); + return getOrdinal(JAVAHL_CLASS("/SVNClient$ClientLogLevel"), jLogLevel); +} + +svn_node_kind_t EnumMapper::toNodeKind(jobject jNodeKind) +{ + return svn_node_kind_t( + getOrdinal(JAVAHL_CLASS("/types/NodeKind"), jNodeKind)); +} + +svn_checksum_kind_t EnumMapper::toChecksumKind(jobject jChecksumKind) +{ + return svn_checksum_kind_t( + getOrdinal(JAVAHL_CLASS("/types/Checksum$Kind"), jChecksumKind)); +} + +svn_tristate_t EnumMapper::toTristate(jobject jTristate) +{ + switch (getOrdinal(JAVAHL_CLASS("/types/Tristate"), jTristate)) + { + case 1: return svn_tristate_false; + case 2: return svn_tristate_true; + default: return svn_tristate_unknown; + } } svn_depth_t EnumMapper::toDepth(jobject jdepth) { // The offset for depths is -2 - return static_cast<svn_depth_t>(getOrdinal(JAVA_PACKAGE"/types/Depth", jdepth) - 2); + return static_cast<svn_depth_t>(getOrdinal(JAVAHL_CLASS("/types/Depth"), jdepth) - 2); } +svn_mergeinfo_inheritance_t +EnumMapper::toMergeinfoInheritance(jobject jInheritance) +{ + return static_cast<svn_mergeinfo_inheritance_t>( + getOrdinal(JAVAHL_CLASS("/types/Mergeinfo$Inheritance"), jInheritance)); +} + + jobject EnumMapper::mapDepth(svn_depth_t depth) { // We're assuming a valid value for the C enum above // The offset for depths is -2 - return mapEnum(JAVA_PACKAGE"/types/Depth", static_cast<int>(depth) + 2); + return mapEnum(JAVAHL_CLASS("/types/Depth"), static_cast<int>(depth) + 2); } jobject EnumMapper::mapOperation(svn_wc_operation_t operation) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/ConflictDescriptor$Operation", + return mapEnum(JAVAHL_CLASS("/ConflictDescriptor$Operation"), static_cast<int>(operation)); } jobject EnumMapper::mapTristate(svn_tristate_t tristate) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/types/Tristate", + return mapEnum(JAVAHL_CLASS("/types/Tristate"), static_cast<int>(tristate - svn_tristate_false)); } svn_wc_conflict_choice_t EnumMapper::toConflictChoice(jobject jchoice) { return static_cast<svn_wc_conflict_choice_t> - (getOrdinal(JAVA_PACKAGE"/ConflictResult$Choice", jchoice)); + (getOrdinal(JAVAHL_CLASS("/ConflictResult$Choice"), jchoice)); } svn_opt_revision_kind EnumMapper::toRevisionKind(jobject jkind) { return static_cast<svn_opt_revision_kind> - (getOrdinal(JAVA_PACKAGE"/types/Revision$Kind", jkind)); + (getOrdinal(JAVAHL_CLASS("/types/Revision$Kind"), jkind)); } jobject EnumMapper::mapSummarizeKind(svn_client_diff_summarize_kind_t sKind) { // We're assuming a valid value for the C enum above - return mapEnum(JAVA_PACKAGE"/DiffSummary$DiffKind", + return mapEnum(JAVAHL_CLASS("/DiffSummary$DiffKind"), static_cast<int>(sKind)); } diff --git a/subversion/bindings/javahl/native/EnumMapper.h b/subversion/bindings/javahl/native/EnumMapper.h index cb38bd9..3aa0423 100644 --- a/subversion/bindings/javahl/native/EnumMapper.h +++ b/subversion/bindings/javahl/native/EnumMapper.h @@ -48,9 +48,13 @@ class EnumMapper static svn_wc_conflict_choice_t toConflictChoice(jobject jchoice); static int toMergeinfoLogKind(jobject jLogKind); static int toLogLevel(jobject jLogLevel); + static svn_node_kind_t toNodeKind(jobject jNodeKind); + static svn_checksum_kind_t toChecksumKind(jobject jChecksumKind); + static svn_tristate_t toTristate(jobject jTristate); + static svn_mergeinfo_inheritance_t + toMergeinfoInheritance(jobject jInheritance); /* Converting from C enum's */ - static jint mapCommitMessageStateFlags(apr_byte_t flags); static jobject mapChangePathAction(const char action); static jobject mapNotifyState(svn_wc_notify_state_t state); static jobject mapNotifyAction(svn_wc_notify_action_t action); diff --git a/subversion/bindings/javahl/native/ExternalItem.cpp b/subversion/bindings/javahl/native/ExternalItem.cpp new file mode 100644 index 0000000..acfa1fb --- /dev/null +++ b/subversion/bindings/javahl/native/ExternalItem.cpp @@ -0,0 +1,176 @@ +/** + * @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 + */ + +#include "jniwrapper/jni_stack.hpp" + +#include "ExternalItem.hpp" +#include "Revision.h" + +namespace JavaHL { + +const char* const ExternalItem::m_class_name = + JAVAHL_CLASS("/types/ExternalItem"); + +ExternalItem::ClassImpl::ClassImpl(::Java::Env env, jclass cls) + : ::Java::Object::ClassImpl(env, cls), + m_mid_ctor( + env.GetMethodID(cls, "<init>", + "(ZLjava/lang/String;Ljava/lang/String;" + JAVAHL_ARG("/types/Revision;") + JAVAHL_ARG("/types/Revision;") + ")V")), + m_fid_target_dir(env.GetFieldID(cls, "targetDir", "Ljava/lang/String;")), + m_fid_url(env.GetFieldID(cls, "url", "Ljava/lang/String;")), + m_fid_revision(env.GetFieldID(cls, "revision", + JAVAHL_ARG("/types/Revision;"))), + m_fid_peg_revision(env.GetFieldID(cls, "pegRevision", + JAVAHL_ARG("/types/Revision;"))) +{} + +ExternalItem::ClassImpl::~ClassImpl() {} + +namespace { +inline jstring +get_string_field(::Java::Env env, jobject jthis, + const ::Java::FieldID& fid) +{ + return jstring(env.GetObjectField(jthis, fid)); +} + +inline svn_opt_revision_t +get_revision_field(::Java::Env env, jobject jthis, + const ::Java::FieldID& fid) +{ + const jobject rev = env.GetObjectField(jthis, fid); + return *Revision(rev).revision(); +} + +inline jobject +make_external_item(::Java::Env env, + jclass cls, const ::Java::MethodID& mid_ctor, + const char* target_dir, + const char* url, + const svn_opt_revision_t* revision, + const svn_opt_revision_t* peg_revision) +{ + return env.NewObject(cls, mid_ctor, + JNI_FALSE, + env.NewStringUTF(target_dir), + env.NewStringUTF(url), + Revision::makeJRevision(*revision), + Revision::makeJRevision(*peg_revision)); +} +} // anonymous namespace + +ExternalItem::ExternalItem(::Java::Env env, jobject jthis) + : Object(env, ::Java::ClassCache::get_external_item(env), jthis), + m_target_dir(env, get_string_field(env, jthis, impl().m_fid_target_dir)), + m_url(env, get_string_field(env, jthis, impl().m_fid_url)), + m_revision(get_revision_field(env, jthis, impl().m_fid_revision)), + m_peg_revision(get_revision_field(env, jthis, impl().m_fid_peg_revision)) +{} + +ExternalItem::ExternalItem(::Java::Env env, + const char* target_dir, + const char* url, + const svn_opt_revision_t* revision, + const svn_opt_revision_t* peg_revision) + : Object(env, ::Java::ClassCache::get_external_item(env)), + m_target_dir(env, target_dir), + m_url(env, url), + m_revision(*revision), + m_peg_revision(*peg_revision) +{ + set_this(make_external_item(env, get_class(), impl().m_mid_ctor, + target_dir, url, revision, peg_revision)); +} + +svn_wc_external_item2_t* +ExternalItem::get_external_item(SVN::Pool& svnpool) const +{ + svn_wc_external_item2_t* item; + apr_pool_t* const pool = svnpool.getPool(); + SVN_JAVAHL_CHECK(m_env, svn_wc_external_item2_create(&item, pool)); + + item->target_dir = apr_pstrdup( + pool, Java::String::Contents(m_target_dir).c_str()); + item->url = apr_pstrdup( + pool, Java::String::Contents(m_url).c_str()); + item->revision = m_revision; + item->peg_revision = m_peg_revision; + return item; +} + +} // namespace JavaHL + + +// FIXME: Should be in another source file, but Revision.cpp is +// old-style, so we'll put this implementation here for now. +namespace { +inline jobject get_static_revision(::Java::Env env, jclass cls, + const char* field_name) +{ + return env.GetStaticObjectField( + cls, env.GetStaticFieldID(cls, field_name, + JAVAHL_ARG("/types/Revision;"))); +} +} // anonymous namespace + +jobject Revision::makeJRevision(const svn_opt_revision_t& rev) +{ + if (rev.kind == svn_opt_revision_number) + return Revision::makeJRevision(rev.value.number); + + const ::Java::Env env; + + if (rev.kind == svn_opt_revision_date) + { + const jclass cls = env.FindClass( + JAVAHL_CLASS("/types/Revision$DateSpec")); + return env.NewObject(cls, env.GetMethodID(cls, "<init>", "(J)V"), + jlong(rev.value.date / 1000)); + } + + const jclass cls = env.FindClass(JAVAHL_CLASS("/types/Revision")); + switch (rev.kind) + { + case svn_opt_revision_committed: + return get_static_revision(env, cls, "COMMITTED"); + + case svn_opt_revision_previous: + return get_static_revision(env, cls, "PREVIOUS"); + + case svn_opt_revision_base: + return get_static_revision(env, cls, "BASE"); + + case svn_opt_revision_working: + return get_static_revision(env, cls, "WORKING"); + + case svn_opt_revision_head: + return get_static_revision(env, cls, "HEAD"); + + case svn_opt_revision_unspecified: + default: + return get_static_revision(env, cls, "UNSPECIFIED"); + } +} diff --git a/subversion/bindings/javahl/native/ExternalItem.hpp b/subversion/bindings/javahl/native/ExternalItem.hpp new file mode 100644 index 0000000..75827ff --- /dev/null +++ b/subversion/bindings/javahl/native/ExternalItem.hpp @@ -0,0 +1,139 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_EXTERNAL_ITEM_HPP +#define SVN_JAVAHL_EXTERNAL_ITEM_HPP + +#include <string> + +#include "svn_opt.h" +#include "svn_wc.h" + +#include "Pool.h" + +#include "jniwrapper/jni_object.hpp" +#include "jniwrapper/jni_string.hpp" + +namespace JavaHL { + +/** + * Object wrapper for @c org.apache.subversion.javahl.types.ExternalItem. + * + * @since New in 1.9. + */ +class ExternalItem : public ::Java::Object +{ +public: + /** + * Constructs a wrapper around @a jthis. + * The constructor does not verify the class of the wrapped object. + */ + explicit ExternalItem(::Java::Env env, jobject jthis); + + /** + * Constructs a new @c ExternalItem object and wrapper. + */ + explicit ExternalItem(::Java::Env env, + const char* target_dir, + const char* url, + const svn_opt_revision_t* revision, + const svn_opt_revision_t* peg_revision); + + /** + * Returns the value of the wrapped object's @c targetDir member. + */ + std::string target_dir() const + { + const ::Java::String::Contents contents(m_target_dir); + return std::string(contents.c_str()); + } + + /** + * Returns the value of the wrapped object's @c url member. + */ + std::string url() const + { + const ::Java::String::Contents contents(m_url); + return std::string(contents.c_str()); + } + + /** + * Returns the value of the wrapped object's @c revision member. + */ + const svn_opt_revision_t* revision() const + { + return &m_revision; + } + + /** + * Returns the value of the wrapped object's @c pegRevision member. + */ + const svn_opt_revision_t* peg_revision() const + { + return &m_peg_revision; + } + + /** + * Returns an @c svn_wc_external_item2_t allocated from @a pool and + * filled in with this object's values. + */ + svn_wc_external_item2_t* get_external_item(SVN::Pool& pool) const; + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ::Java::ClassCacheImpl; + + protected: + explicit ClassImpl(::Java::Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const ::Java::MethodID m_mid_ctor; + const ::Java::FieldID m_fid_target_dir; + const ::Java::FieldID m_fid_url; + const ::Java::FieldID m_fid_revision; + const ::Java::FieldID m_fid_peg_revision; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; + + ::Java::String m_target_dir; + ::Java::String m_url; + svn_opt_revision_t m_revision; + svn_opt_revision_t m_peg_revision; +}; + +} // namespace JavaHL + +#endif // SVN_JAVAHL_EXTERNAL_ITEM_HPP diff --git a/subversion/bindings/javahl/native/GlobalConfig.h b/subversion/bindings/javahl/native/GlobalConfig.h new file mode 100644 index 0000000..b7c561d --- /dev/null +++ b/subversion/bindings/javahl/native/GlobalConfig.h @@ -0,0 +1,38 @@ +/** + * @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 GlobalConfig.h + * @brief Interface of the class GlobalConfig + */ + +#ifndef JAVAHL_GLOBAL_CONFIG_H +#define JAVAHL_GLOBAL_CONFIG_H + +#include "JNIUtil.h" + +class GlobalConfig +{ + public: + static bool useNativeCredentialsStore(); +}; + +#endif // JAVAHL_GLOBAL_CONFIG_H diff --git a/subversion/bindings/javahl/native/ImportFilterCallback.cpp b/subversion/bindings/javahl/native/ImportFilterCallback.cpp index 33d7e8b..3c4a4c5 100644 --- a/subversion/bindings/javahl/native/ImportFilterCallback.cpp +++ b/subversion/bindings/javahl/native/ImportFilterCallback.cpp @@ -83,13 +83,14 @@ ImportFilterCallback::doImportFilter(svn_boolean_t *filtered, static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ImportFilterCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ImportFilterCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); mid = env->GetMethodID(clazz, "filter", "(Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/NodeKind;Z)Z"); + JAVAHL_ARG("/types/NodeKind;") + "Z)Z"); if (JNIUtil::isJavaExceptionThrown() || mid == 0) POP_AND_RETURN(SVN_NO_ERROR); } diff --git a/subversion/bindings/javahl/native/InfoCallback.cpp b/subversion/bindings/javahl/native/InfoCallback.cpp index 8a9e375..ee8fb69 100644 --- a/subversion/bindings/javahl/native/InfoCallback.cpp +++ b/subversion/bindings/javahl/native/InfoCallback.cpp @@ -80,12 +80,12 @@ InfoCallback::singleInfo(const char *path, static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/InfoCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/InfoCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); mid = env->GetMethodID(clazz, "singleInfo", - "(L"JAVA_PACKAGE"/types/Info;)V"); + "(" JAVAHL_ARG("/types/Info;") ")V"); if (JNIUtil::isJavaExceptionThrown() || mid == 0) POP_AND_RETURN(SVN_NO_ERROR); } @@ -95,8 +95,6 @@ InfoCallback::singleInfo(const char *path, POP_AND_RETURN(SVN_NO_ERROR); env->CallVoidMethod(m_callback, mid, jinfo2); - // Return SVN_NO_ERROR here regardless of an exception or not. - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } diff --git a/subversion/bindings/javahl/native/InfoCallback.h b/subversion/bindings/javahl/native/InfoCallback.h index b65f203..a1975d2 100644 --- a/subversion/bindings/javahl/native/InfoCallback.h +++ b/subversion/bindings/javahl/native/InfoCallback.h @@ -30,8 +30,6 @@ #include <jni.h> #include "svn_client.h" -struct info_entry; - /** * This class holds a Java callback object, which will receive every line of * the file for which the callback information is requested. diff --git a/subversion/bindings/javahl/native/InputStream.cpp b/subversion/bindings/javahl/native/InputStream.cpp index 1c73ba2..ac10f9e 100644 --- a/subversion/bindings/javahl/native/InputStream.cpp +++ b/subversion/bindings/javahl/native/InputStream.cpp @@ -54,7 +54,8 @@ svn_stream_t *InputStream::getStream(const SVN::Pool &pool) // Create a stream with this as the baton and set the read and // close functions. svn_stream_t *ret = svn_stream_create(this, pool.getPool()); - svn_stream_set_read(ret, InputStream::read); + svn_stream_set_read2(ret, InputStream::read, + NULL /* only partial read support */); svn_stream_set_close(ret, InputStream::close); return ret; } @@ -68,6 +69,9 @@ svn_stream_t *InputStream::getStream(const SVN::Pool &pool) */ svn_error_t *InputStream::read(void *baton, char *buffer, apr_size_t *len) { + if (0 == *len) + return SVN_NO_ERROR; + JNIEnv *env = JNIUtil::getEnv(); // An object of our class is passed in as the baton. InputStream *that = static_cast<InputStream *>(baton); diff --git a/subversion/bindings/javahl/native/Iterator.cpp b/subversion/bindings/javahl/native/Iterator.cpp new file mode 100644 index 0000000..bd8aee0 --- /dev/null +++ b/subversion/bindings/javahl/native/Iterator.cpp @@ -0,0 +1,115 @@ +/** + * @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 Iterator.cpp + * @brief Implementation of the class Iterator + */ + +#include "Iterator.h" + +namespace { +jobject init_iterator(jobject jiterable, bool persistent) +{ + // A null iterable is allowed, we'll leave the iterator null and + // nothing will happen in hasNext() and next(). + if (!jiterable) + return NULL; + + JNIEnv* env = JNIUtil::getEnv(); + + static jmethodID iterator_mid = 0; + if (0 == iterator_mid) + { + jclass cls = env->FindClass("java/lang/Iterable"); + if (JNIUtil::isExceptionThrown()) + return NULL; + iterator_mid = env->GetMethodID(cls, "iterator", + "()Ljava/util/Iterator;"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + jobject jiterator = env->CallObjectMethod(jiterable, iterator_mid); + if (JNIUtil::isExceptionThrown()) + return NULL; + return (persistent ? env->NewGlobalRef(jiterator) : jiterator); +} +} // anonymous namespace + + +Iterator::Iterator(jobject jiterable) + : m_persistent(false), + m_jiterator(init_iterator(jiterable, false)) +{} + +Iterator::Iterator(jobject jiterable, bool) + : m_persistent(true), + m_jiterator(init_iterator(jiterable, true)) +{} + +Iterator::~Iterator() +{ + if (m_persistent && m_jiterator) + JNIUtil::getEnv()->DeleteGlobalRef(m_jiterator); +} + +bool Iterator::hasNext() const +{ + if (!m_jiterator) + return false; + + JNIEnv* env = JNIUtil::getEnv(); + + static jmethodID hasNext_mid = 0; + if (0 == hasNext_mid) + { + jclass cls = env->FindClass("java/util/Iterator"); + if (JNIUtil::isExceptionThrown()) + return false; + hasNext_mid = env->GetMethodID(cls, "hasNext", "()Z"); + if (JNIUtil::isExceptionThrown()) + return false; + } + + return bool(env->CallBooleanMethod(m_jiterator, hasNext_mid)); +} + +jobject Iterator::next() const +{ + if (!m_jiterator) + return NULL; + + JNIEnv* env = JNIUtil::getEnv(); + + static jmethodID next_mid = 0; + if (0 == next_mid) + { + jclass cls = env->FindClass("java/util/Iterator"); + if (JNIUtil::isExceptionThrown()) + return NULL; + next_mid = env->GetMethodID(cls, "next", "()Ljava/lang/Object;"); + if (JNIUtil::isExceptionThrown()) + return NULL; + } + + return env->CallObjectMethod(m_jiterator, next_mid); +} diff --git a/subversion/bindings/javahl/native/Iterator.h b/subversion/bindings/javahl/native/Iterator.h new file mode 100644 index 0000000..c116ab3 --- /dev/null +++ b/subversion/bindings/javahl/native/Iterator.h @@ -0,0 +1,64 @@ +/** + * @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 Iterator.cpp + * @brief Interface of the class Iterator + */ + +#ifndef JAVAHL_ITERATOR_H +#define JAVAHL_ITERATOR_H + +#include "JNIUtil.h" + +/** + * Encapsulates an immutable java.lang.Iterator implementation. + */ +class Iterator +{ +public: + Iterator(jobject jiterable); + ~Iterator(); + bool hasNext() const; + jobject next() const; + +protected: + Iterator(jobject jiterable, bool); + +private: + bool m_persistent; + jobject m_jiterator; +}; + + +/** + * Like Iterator, but the implementation will hold a global reference + * to the internal iterator object to protect it across JNI calls. + */ +class PersistentIterator : protected Iterator +{ +public: + PersistentIterator(jobject jiterable) : Iterator(jiterable, true) {} + bool hasNext() const { return Iterator::hasNext(); } + jobject next() const { return Iterator::next(); } +}; + +#endif // JAVAHL_ITERATOR_H diff --git a/subversion/bindings/javahl/native/JNIByteArray.cpp b/subversion/bindings/javahl/native/JNIByteArray.cpp index 34ce96c..cedb1bb 100644 --- a/subversion/bindings/javahl/native/JNIByteArray.cpp +++ b/subversion/bindings/javahl/native/JNIByteArray.cpp @@ -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); diff --git a/subversion/bindings/javahl/native/JNIByteArray.h b/subversion/bindings/javahl/native/JNIByteArray.h index b43673b..e988bda 100644 --- a/subversion/bindings/javahl/native/JNIByteArray.h +++ b/subversion/bindings/javahl/native/JNIByteArray.h @@ -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(); }; diff --git a/subversion/bindings/javahl/native/JNICriticalSection.h b/subversion/bindings/javahl/native/JNICriticalSection.h index 685b049..d628c7e 100644 --- a/subversion/bindings/javahl/native/JNICriticalSection.h +++ b/subversion/bindings/javahl/native/JNICriticalSection.h @@ -32,7 +32,7 @@ class JNIMutex; /** * This class holds a mutex which will be locked during the constructor and * released during the destructor. If the object is created on the stack, this - * garanties that the mutex will be released all the time if the block is left. + * guaranties that the mutex will be released all the time if the block is left. * Only one thread can enter all the critrical sections secured by the same * mutex. */ diff --git a/subversion/bindings/javahl/native/JNIStackElement.cpp b/subversion/bindings/javahl/native/JNIStackElement.cpp index 2ca6c6e..df9163d 100644 --- a/subversion/bindings/javahl/native/JNIStackElement.cpp +++ b/subversion/bindings/javahl/native/JNIStackElement.cpp @@ -27,7 +27,6 @@ #include "JNIStackElement.h" #include "JNIUtil.h" #include "JNIStringHolder.h" -#include "JNIThreadData.h" #include <apr_strings.h> /** @@ -74,7 +73,7 @@ JNIStackElement::JNIStackElement(JNIEnv *env, const char *clazz, // Copy the result to a buffer. JNIStringHolder name(reinterpret_cast<jstring>(oStr)); - strncat(m_objectID, name, JNIUtil::formatBufferSize -1); + strncat(m_objectID, name, sizeof(m_objectID) - 1); env->DeleteLocalRef(oStr); } @@ -86,8 +85,8 @@ JNIStackElement::JNIStackElement(JNIEnv *env, const char *clazz, m_method = method; // Generate the log message. - char *buffer = JNIUtil::getFormatBuffer(); - apr_snprintf(buffer, JNIUtil::formatBufferSize, + char buffer[2048]; + apr_snprintf(buffer, sizeof(buffer), "entry class %s method %s object %s", m_clazz, m_method, m_objectID); JNIUtil::logMessage(buffer); @@ -110,11 +109,10 @@ JNIStackElement::~JNIStackElement() if (m_clazz != NULL) { // Generate a log message. - char *buffer = JNIUtil::getFormatBuffer(); - apr_snprintf(buffer, JNIUtil::formatBufferSize, + char buffer[2048]; + apr_snprintf(buffer, sizeof(buffer), "exit class %s method %s object %s", m_clazz, m_method, m_objectID); JNIUtil::logMessage(buffer); } - JNIThreadData::popThreadData(); } diff --git a/subversion/bindings/javahl/native/JNIStackElement.h b/subversion/bindings/javahl/native/JNIStackElement.h index 99dab62..61708c3 100644 --- a/subversion/bindings/javahl/native/JNIStackElement.h +++ b/subversion/bindings/javahl/native/JNIStackElement.h @@ -73,7 +73,7 @@ class JNIStackElement * A buffer for the result for jthis.toString to identify the * object. */ - char m_objectID[JNIUtil::formatBufferSize]; + char m_objectID[2048]; }; #endif // JNISTACKELEMENT_H diff --git a/subversion/bindings/javahl/native/JNIStringHolder.h b/subversion/bindings/javahl/native/JNIStringHolder.h index 7d36bed..c1bbf72 100644 --- a/subversion/bindings/javahl/native/JNIStringHolder.h +++ b/subversion/bindings/javahl/native/JNIStringHolder.h @@ -36,6 +36,7 @@ class JNIStringHolder JNIStringHolder(jstring jtext); ~JNIStringHolder(); operator const char *() { return m_str; } + const char* c_str() const { return m_str; } const char *pstrdup(apr_pool_t *pool); protected: diff --git a/subversion/bindings/javahl/native/JNIThreadData.cpp b/subversion/bindings/javahl/native/JNIThreadData.cpp deleted file mode 100644 index e3ce334..0000000 --- a/subversion/bindings/javahl/native/JNIThreadData.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/** - * @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 JNIThreadData.cpp - * @brief Implementation of the class JNIThreadData - */ - -#include "JNIThreadData.h" -#include <apr_strings.h> -#include <apr_tables.h> -#include <apr_general.h> -#include <apr_lib.h> -#include <apr_thread_proc.h> -#include "JNIUtil.h" - -apr_threadkey_t *JNIThreadData::g_key; - -/** - * Create and initialize a new object. - */ -JNIThreadData::JNIThreadData() -{ - m_env = NULL; - m_exceptionThrown = false; - m_previous = NULL; -} - -JNIThreadData::~JNIThreadData() -{ -} - -/** - * Initialize the thread local storage. - * @return success or failure - */ -bool JNIThreadData::initThreadData() -{ - // If already initialized -> nothing to do. - if (g_key != NULL) - return false; - - // Request a key for the thread local storage from the global pool - // and register a callback function called when the thread is - // deleted. - apr_status_t apr_err = apr_threadkey_private_create(&g_key, - del, - JNIUtil::getPool()); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_create"); - return false; - } - - return true; -} - -/** - * Get the thread local storage for this thread. - * @return thread local storage - */ -JNIThreadData *JNIThreadData::getThreadData() -{ - // We should never be called before initThreadData - if (g_key == NULL) - return NULL; - - // Retrieve the thread local storage from APR. - JNIThreadData *data = NULL; - apr_status_t apr_err = apr_threadkey_private_get - (reinterpret_cast<void**>(&data), g_key); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get"); - return NULL; - } - - // Not already allocated. - if (data == NULL) - { - // Allocate and store to APR. - data = new JNIThreadData; - apr_err = apr_threadkey_private_set (data, g_key); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set"); - return NULL; - } - } - return data; -} - -/** - * Allocate a new ThreadData for the current call from Java and push - * it on the stack - */ -void JNIThreadData::pushNewThreadData() -{ - JNIThreadData *data = NULL; - apr_status_t apr_err = apr_threadkey_private_get - (reinterpret_cast<void**>(&data), g_key); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get"); - return; - } - JNIThreadData *newData = new JNIThreadData(); - newData->m_previous =data; - apr_err = apr_threadkey_private_set(newData, g_key); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set"); - return; - } -} - -/** - * Pop the current ThreadData from the stack, because the call - * completed. - */ -void JNIThreadData::popThreadData() -{ - JNIThreadData *data = NULL; - apr_status_t apr_err = apr_threadkey_private_get - (reinterpret_cast<void**>(&data), g_key); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_get"); - return; - } - if (data == NULL) - return; - - JNIThreadData *oldData = data->m_previous; - delete data; - apr_err = apr_threadkey_private_set(oldData, g_key); - if (apr_err) - { - JNIUtil::handleAPRError(apr_err, "apr_threadkey_private_set"); - return; - } -} - -/** - * Callback called by APR when the thread dies. Deletes the thread - * local storage. - */ -void JNIThreadData::del(void *p) -{ - delete reinterpret_cast<JNIThreadData*>(p); -} diff --git a/subversion/bindings/javahl/native/JNIUtil.cpp b/subversion/bindings/javahl/native/JNIUtil.cpp index e52e975..2adcf2f 100644 --- a/subversion/bindings/javahl/native/JNIUtil.cpp +++ b/subversion/bindings/javahl/native/JNIUtil.cpp @@ -24,6 +24,13 @@ * @brief Implementation of the class JNIUtil */ +/* Include apr.h first, or INT64_C won't be defined properly on some C99 + compilers, when other headers include <stdint.h> before defining some + macros. + + See apr.h for the ugly details */ +#include <apr.h> + #include "JNIUtil.h" #include "Array.h" @@ -35,8 +42,11 @@ #include <apr_tables.h> #include <apr_general.h> #include <apr_lib.h> +#include <apr_file_info.h> +#include <apr_time.h> #include "svn_pools.h" +#include "svn_error.h" #include "svn_fs.h" #include "svn_ra.h" #include "svn_utf.h" @@ -44,37 +54,43 @@ #include "svn_dso.h" #include "svn_path.h" #include "svn_cache_config.h" -#include <apr_file_info.h> +#include "private/svn_atomic.h" +#include "private/svn_utf_private.h" #include "svn_private_config.h" -#ifdef WIN32 -/* FIXME: We're using an internal APR header here, which means we - have to build Subversion with APR sources. This being Win32-only, - that should be fine for now, but a better solution must be found in - combination with issue #850. */ -extern "C" { -#include <arch/win32/apr_arch_utf8.h> -}; -#endif #include "SVNBase.h" #include "JNIMutex.h" #include "JNICriticalSection.h" -#include "JNIThreadData.h" #include "JNIStringHolder.h" #include "Pool.h" + +#include "jniwrapper/jni_env.hpp" + // Static members of JNIUtil are allocated here. apr_pool_t *JNIUtil::g_pool = NULL; std::list<SVNBase*> JNIUtil::g_finalizedObjects; JNIMutex *JNIUtil::g_finalizedObjectsMutex = NULL; JNIMutex *JNIUtil::g_logMutex = NULL; +JNIMutex *JNIUtil::g_configMutex = NULL; bool JNIUtil::g_initException; -bool JNIUtil::g_inInit; -JNIEnv *JNIUtil::g_initEnv; -char JNIUtil::g_initFormatBuffer[formatBufferSize]; int JNIUtil::g_logLevel = JNIUtil::noLog; std::ofstream JNIUtil::g_logStream; +/* The error code we will use to signal a Java exception */ +static const apr_status_t +SVN_ERR_JAVAHL_WRAPPED = SVN_ERR_MALFUNC_CATEGORY_START + + SVN_ERR_CATEGORY_SIZE - 10; + +/** + * Return the JNI environment to use + * @return the JNI environment + */ +JNIEnv *JNIUtil::getEnv() +{ + return Java::Env().get(); +} + /** * Initialize the environment for all requests. * @param env the JNI environment for this request @@ -84,9 +100,6 @@ bool JNIUtil::JNIInit(JNIEnv *env) // Clear all standing exceptions. env->ExceptionClear(); - // Remember the env parameter for the remainder of the request. - setEnv(env); - // Lock the list of finalized objects. JNICriticalSection cs(*g_finalizedObjectsMutex) ; if (isExceptionThrown()) @@ -104,49 +117,48 @@ bool JNIUtil::JNIInit(JNIEnv *env) return true; } +/* Forwarder for calling JNIGlobalInit from JNI_OnLoad(). */ +bool initialize_jni_util(JNIEnv *env) +{ + return JNIUtil::JNIGlobalInit(env); +} + +namespace { + +volatile svn_atomic_t *gentle_crash_write_loc = NULL; + +svn_error_t * +gently_crash_the_jvm(svn_boolean_t can_return, + const char *file, int line, const char *expr) +{ + if (!can_return) + { + // Try not to abort; aborting prevents the JVM from creating + // a crash log, which is oh so useful for debugging. + // We can't just raise a SEGV signal, either, because it will + // be not be caught in the context that we're interested in + // getting the stack trace from. + + // Try reading from and writing to the zero page + const svn_atomic_t zeropage = svn_atomic_read(gentle_crash_write_loc); + svn_atomic_set(gentle_crash_write_loc, zeropage); + } + + // Forward to the standard malfunction handler, which does call + // abort when !can_return; this will only happen if the write to the + // zero page did not cause a SEGV. + return svn_error_raise_on_malfunction(can_return, file, line, expr); +} +} // Anonymous namespace + /** * Initialize the environment for all requests. + * This method must be called in a single-threaded context. * @param env the JNI environment for this request */ bool JNIUtil::JNIGlobalInit(JNIEnv *env) { - // This method has to be run only once during the run a program. - static bool run = false; svn_error_t *err; - if (run) // already run - return true; - - run = true; - - // Do not run this part more than one time. This leaves a small - // time window when two threads create their first SVNClient and - // SVNAdmin at the same time, but I do not see a better option - // without APR already initialized - if (g_inInit) - return false; - - g_inInit = true; - g_initEnv = env; - - apr_status_t status; - - - - /* Initialize the APR subsystem, and register an atexit() function - * to Uninitialize that subsystem at program exit. */ - status = apr_initialize(); - if (status) - { - if (stderr) - { - char buf[1024]; - apr_strerror(status, buf, sizeof(buf) - 1); - fprintf(stderr, - "%s: error: cannot initialize APR: %s\n", - "svnjavahl", buf); - } - return FALSE; - } /* This has to happen before any pools are created. */ if ((err = svn_dso_initialize2())) @@ -158,16 +170,8 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env) return FALSE; } - if (0 > atexit(apr_terminate)) - { - if (stderr) - fprintf(stderr, - "%s: error: atexit registration failed\n", - "svnjavahl"); - return FALSE; - } - - /* Create our top-level pool. */ + /* Create our top-level pool. + N.B.: APR was initialized by JNI_OnLoad. */ g_pool = svn_pool_create(NULL); apr_allocator_t* allocator = apr_pool_allocator_get(g_pool); @@ -180,15 +184,25 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env) } svn_utf_initialize2(FALSE, g_pool); /* Optimize character conversions */ - svn_fs_initialize(g_pool); /* Avoid some theoretical issues */ - svn_ra_initialize(g_pool); - /* We shouldn't fill the JVMs memory with FS cache data unless explictly - requested. */ + // Initialize the libraries we use + err = svn_fs_initialize(g_pool); + if (!err) + err = svn_ra_initialize(g_pool); + if (err) + { + if (stderr && err->message) + fprintf(stderr, "%s", err->message); + + svn_error_clear(err); + return FALSE; + } + + /* We shouldn't fill the JVMs memory with FS cache data unless + explicitly requested. And we don't either, because the caches get + allocated outside the JVM heap. Duh. */ { svn_cache_config_t settings = *svn_cache_config_get(); - settings.cache_size = 0; - settings.file_handle_count = 0; settings.single_threaded = FALSE; svn_cache_config_set(&settings); } @@ -197,32 +211,24 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env) #ifdef WIN32 { WCHAR ucs2_path[MAX_PATH]; - char *utf8_path; + const char *utf8_path; const char *internal_path; - apr_pool_t *pool; - apr_status_t apr_err; - apr_size_t inwords, outbytes; - unsigned int outlength; + svn_error_t *err; + apr_pool_t *pool = svn_pool_create(g_pool); - pool = svn_pool_create(g_pool); /* get dll name - our locale info will be in '../share/locale' */ - inwords = sizeof(ucs2_path) / sizeof(ucs2_path[0]); HINSTANCE moduleHandle = GetModuleHandle("libsvnjavahl-1"); - GetModuleFileNameW(moduleHandle, ucs2_path, inwords); - inwords = lstrlenW(ucs2_path); - outbytes = outlength = 3 * (inwords + 1); - utf8_path = reinterpret_cast<char *>(apr_palloc(pool, outlength)); - apr_err = apr_conv_ucs2_to_utf8((const apr_wchar_t *) ucs2_path, - &inwords, utf8_path, &outbytes); - if (!apr_err && (inwords > 0 || outbytes == 0)) - apr_err = APR_INCOMPLETE; - if (apr_err) + GetModuleFileNameW(moduleHandle, ucs2_path, + sizeof(ucs2_path) / sizeof(ucs2_path[0])); + err = svn_utf__win32_utf16_to_utf8(&utf8_path, ucs2_path, NULL, pool); + if (err) { if (stderr) - fprintf(stderr, "Can't convert module path to UTF-8"); - return FALSE; + svn_handle_error2(err, stderr, false, "svn: "); + svn_error_clear(err); + return false; } - utf8_path[outlength - outbytes] = '\0'; + internal_path = svn_dirent_internal_style(utf8_path, pool); /* get base path name */ internal_path = svn_dirent_dirname(internal_path, pool); @@ -268,16 +274,14 @@ bool JNIUtil::JNIGlobalInit(JNIEnv *env) if (isExceptionThrown()) return false; - // initialized the thread local storage - if (!JNIThreadData::initThreadData()) - return false; - - setEnv(env); + g_configMutex = new JNIMutex(g_pool); if (isExceptionThrown()) return false; - g_initEnv = NULL; - g_inInit = false; + // Set a malfunction handler that tries not to call abort, because + // that would prevent the JVM from creating a crash and stack log file. + svn_error_set_malfunction_handler(gently_crash_the_jvm); + return true; } @@ -304,18 +308,6 @@ void JNIUtil::raiseThrowable(const char *name, const char *message) return; env->ThrowNew(clazz, message); - setExceptionThrown(); - env->DeleteLocalRef(clazz); -} - -jstring JNIUtil::makeSVNErrorMessage(svn_error_t *err) -{ - if (err == NULL) - return NULL; - std::string buffer; - assembleErrorMessage(err, 0, APR_SUCCESS, buffer); - jstring jmessage = makeJString(buffer.c_str()); - return jmessage; } void @@ -418,10 +410,174 @@ JNIUtil::putErrorsInTrace(svn_error_t *err, env->DeleteLocalRef(jfileName); } -void JNIUtil::wrappedHandleSVNError(svn_error_t *err) +namespace { +struct MessageStackItem { - std::string msg; - assembleErrorMessage(svn_error_purge_tracing(err), 0, APR_SUCCESS, msg); + apr_status_t m_code; + std::string m_message; + bool m_generic; + + MessageStackItem(apr_status_t code, const char* message, + bool generic = false) + : m_code(code), + m_message(message), + m_generic(generic) + {} +}; +typedef std::vector<MessageStackItem> ErrorMessageStack; + +/* + * Build the error message from the svn error into buffer. This + * method iterates through all the chained errors + * + * @param err the subversion error + * @param buffer the buffer where the formated error message will + * be stored + * @return An array of error codes and messages + */ +ErrorMessageStack assemble_error_message( + svn_error_t *err, std::string &result) +{ + // buffer for a single error message + char errbuf[1024]; + apr_status_t parent_apr_err = 0; + ErrorMessageStack message_stack; + + /* Pretty-print the error */ + /* Note: we can also log errors here someday. */ + + for (int depth = 0; err; + ++depth, parent_apr_err = err->apr_err, err = err->child) + { + /* When we're recursing, don't repeat the top-level message if its + * the same as before. */ + if ((depth == 0 || err->apr_err != parent_apr_err) + && err->apr_err != SVN_ERR_JAVAHL_WRAPPED) + { + const char *message; + /* Is this a Subversion-specific error code? */ + if ((err->apr_err > APR_OS_START_USEERR) + && (err->apr_err <= APR_OS_START_CANONERR)) + message = svn_strerror(err->apr_err, errbuf, sizeof(errbuf)); + /* Otherwise, this must be an APR error code. */ + else + { + /* Messages coming from apr_strerror are in the native + encoding, it's a good idea to convert them to UTF-8. */ + apr_strerror(err->apr_err, errbuf, sizeof(errbuf)); + svn_error_t* utf8_err = + svn_utf_cstring_to_utf8(&message, errbuf, err->pool); + if (utf8_err) + { + /* Use fuzzy transliteration instead. */ + svn_error_clear(utf8_err); + message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool); + } + } + + message_stack.push_back( + MessageStackItem(err->apr_err, message, true)); + } + if (err->message) + { + message_stack.push_back( + MessageStackItem(err->apr_err, err->message)); + } + } + + for (ErrorMessageStack::const_iterator it = message_stack.begin(); + it != message_stack.end(); ++it) + { + if (!it->m_generic) + result += "svn: "; + result += it->m_message; + result += '\n'; + } + return message_stack; +} + +jobject construct_Jmessage_stack(const ErrorMessageStack& message_stack) +{ + JNIEnv *env = JNIUtil::getEnv(); + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + jclass list_clazz = env->FindClass("java/util/ArrayList"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + jmethodID mid = env->GetMethodID(list_clazz, "<init>", "(I)V"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + jmethodID add_mid = env->GetMethodID(list_clazz, "add", + "(Ljava/lang/Object;)Z"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + jobject jlist = env->NewObject(list_clazz, mid, jint(message_stack.size())); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + jclass clazz = env->FindClass(JAVAHL_CLASS("/ClientException$ErrorMessage")); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + mid = env->GetMethodID(clazz, "<init>", + "(ILjava/lang/String;Z)V"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + for (ErrorMessageStack::const_iterator it = message_stack.begin(); + it != message_stack.end(); ++it) + { + jobject jmessage = JNIUtil::makeJString(it->m_message.c_str()); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + jobject jitem = env->NewObject(clazz, mid, + jint(it->m_code), jmessage, + jboolean(it->m_generic)); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + env->CallBooleanMethod(jlist, add_mid, jitem); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + env->DeleteLocalRef(jmessage); + env->DeleteLocalRef(jitem); + } + return env->PopLocalFrame(jlist); +} +} // anonymous namespace + +std::string JNIUtil::makeSVNErrorMessage(svn_error_t *err, + jstring *jerror_message, + jobject *jmessage_stack) +{ + if (jerror_message) + *jerror_message = NULL; + if (jmessage_stack) + *jmessage_stack = NULL; + + std::string buffer; + err = svn_error_purge_tracing(err); + if (err == NULL || err->apr_err == 0 + || !(jerror_message || jmessage_stack)) + return buffer; + + ErrorMessageStack message_stack = assemble_error_message(err, buffer); + if (jerror_message) + *jerror_message = makeJString(buffer.c_str()); + if (jmessage_stack) + *jmessage_stack = construct_Jmessage_stack(message_stack); + return buffer; +} + +jthrowable JNIUtil::wrappedCreateClientException(svn_error_t *err, jthrowable jcause) +{ + jstring jmessage; + jobject jstack; + std::string msg = makeSVNErrorMessage(err, &jmessage, &jstack); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + const char *source = NULL; #ifdef SVN_DEBUG #ifndef SVN_ERR__TRACING @@ -436,6 +592,9 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err) #endif #endif + if (!jcause) + jcause = JNIUtil::unwrapJavaException(err); + // Much of the following is stolen from throwNativeException(). As much as // we'd like to call that function, we need to do some manual stack // unrolling, so it isn't feasible. @@ -445,11 +604,11 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err) // Create a local frame for our references env->PushLocalFrame(LOCAL_FRAME_SIZE); if (JNIUtil::isJavaExceptionThrown()) - return; + return NULL; - jclass clazz = env->FindClass(JAVA_PACKAGE "/ClientException"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/ClientException")); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; if (getLogLevel() >= exceptionLog) { @@ -463,23 +622,24 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err) g_logStream << std::endl; } if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; - jstring jmessage = makeJString(msg.c_str()); - if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); jstring jsource = makeJString(source); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; jmethodID mid = env->GetMethodID(clazz, "<init>", - "(Ljava/lang/String;Ljava/lang/String;I)V"); + "(Ljava/lang/String;" + "Ljava/lang/Throwable;" + "Ljava/lang/String;I" + "Ljava/util/List;)V"); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); - jobject nativeException = env->NewObject(clazz, mid, jmessage, jsource, - static_cast<jint>(err->apr_err)); + POP_AND_RETURN_NULL; + jobject nativeException = env->NewObject(clazz, mid, jmessage, jcause, + jsource, jint(err->apr_err), + jstack); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; #ifdef SVN_ERR__TRACING // Add all the C error stack trace information to the Java Exception @@ -491,7 +651,7 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err) mid_gst = env->GetMethodID(clazz, "getStackTrace", "()[Ljava/lang/StackTraceElement;"); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; } Array stackTraceArray((jobjectArray) env->CallObjectMethod(nativeException, mid_gst)); @@ -510,18 +670,18 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err) jclass stClazz = env->FindClass("java/lang/StackTraceElement"); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; const jsize stSize = static_cast<jsize>(newStackTrace.size()); - if (stSize != newStackTrace.size()) + if (stSize < 0 || stSize != newStackTrace.size()) { env->ThrowNew(env->FindClass("java.lang.ArithmeticException"), "Overflow converting C size_t to JNI jsize"); - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; } jobjectArray jStackTrace = env->NewObjectArray(stSize, stClazz, NULL); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; int i = 0; for (std::vector<jobject>::const_iterator it = newStackTrace.begin(); @@ -538,25 +698,34 @@ void JNIUtil::wrappedHandleSVNError(svn_error_t *err) mid_sst = env->GetMethodID(clazz, "setStackTrace", "([Ljava/lang/StackTraceElement;)V"); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; } env->CallVoidMethod(nativeException, mid_sst, jStackTrace); if (isJavaExceptionThrown()) - POP_AND_RETURN_NOTHING(); + POP_AND_RETURN_NULL; #endif - env->Throw(static_cast<jthrowable>(env->PopLocalFrame(nativeException))); + return static_cast<jthrowable>(env->PopLocalFrame(nativeException)); } -void JNIUtil::handleSVNError(svn_error_t *err) +jthrowable JNIUtil::createClientException(svn_error_t *err, jthrowable jcause) { + jthrowable jexc = NULL; try { - wrappedHandleSVNError(err); + jexc = wrappedCreateClientException(err, jcause); } catch (...) { svn_error_clear(err); throw; } svn_error_clear(err); + return jexc; +} + +void JNIUtil::handleSVNError(svn_error_t *err, jthrowable jcause) +{ + jthrowable jexc = createClientException(err, jcause); + if (jexc) + getEnv()->Throw(jexc); } void JNIUtil::putFinalizedClient(SVNBase *object) @@ -578,120 +747,82 @@ void JNIUtil::enqueueForDeletion(SVNBase *object) */ void JNIUtil::handleAPRError(int error, const char *op) { - char *buffer = getFormatBuffer(); - if (buffer == NULL) - return; + char buffer[2048]; - apr_snprintf(buffer, formatBufferSize, + apr_snprintf(buffer, sizeof(buffer), _("an error occurred in function %s with return value %d"), op, error); throwError(buffer); } -/** - * Return if an exception has been detected. - * @return a exception has been detected - */ -bool JNIUtil::isExceptionThrown() +namespace { +const char* known_exception_to_cstring(apr_pool_t* pool) { - // During init -> look in the global member. - if (g_inInit) - return g_initException; - - // Look in the thread local storage. - JNIThreadData *data = JNIThreadData::getThreadData(); - return data == NULL || data->m_exceptionThrown; -} + JNIEnv *env = JNIUtil::getEnv(); + jthrowable t = env->ExceptionOccurred(); + jclass cls = env->GetObjectClass(t); -/** - * Store the JNI environment for this request in the thread local - * storage. - * @param env the JNI environment - */ -void JNIUtil::setEnv(JNIEnv *env) -{ - JNIThreadData::pushNewThreadData(); - JNIThreadData *data = JNIThreadData::getThreadData(); - data->m_env = env; - data->m_exceptionThrown = false; -} + jstring jclass_name; + { + jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;"); + jobject clsobj = env->CallObjectMethod(t, mid); + jclass basecls = env->GetObjectClass(clsobj); + mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;"); + jclass_name = (jstring) env->CallObjectMethod(clsobj, mid); + } -/** - * Return the JNI environment to use - * @return the JNI environment - */ -JNIEnv *JNIUtil::getEnv() -{ - // During init -> look into the global variable. - if (g_inInit) - return g_initEnv; + jstring jmessage; + { + jmethodID mid = env->GetMethodID(cls, "getMessage", + "()Ljava/lang/String;"); + jmessage = (jstring) env->CallObjectMethod(t, mid); + } - // Look in the thread local storage. - JNIThreadData *data = JNIThreadData::getThreadData(); - return data->m_env; + JNIStringHolder class_name(jclass_name); + if (jmessage) + { + JNIStringHolder message(jmessage); + return apr_pstrcat(pool, class_name.c_str(), ": ", message.c_str(), NULL); + } + else + return class_name.pstrdup(pool); + // ### Conditionally add t.printStackTrace() to msg? } -/** - * Check if a Java exception has been thrown. - * @return is a Java exception has been thrown - */ -bool JNIUtil::isJavaExceptionThrown() +const char* exception_to_cstring(apr_pool_t* pool) { - JNIEnv *env = getEnv(); - if (env->ExceptionCheck()) + const char *msg; + if (JNIUtil::getEnv()->ExceptionCheck()) { - // Retrieving the exception removes it so we rethrow it here. - jthrowable exp = env->ExceptionOccurred(); - env->ExceptionDescribe(); - env->Throw(exp); - env->DeleteLocalRef(exp); - setExceptionThrown(); - return true; + msg = known_exception_to_cstring(pool); } - return false; + else + { + msg = NULL; + } + return msg; } +} // anonymous namespace const char * JNIUtil::thrownExceptionToCString(SVN::Pool &in_pool) { - const char *msg = NULL; - JNIEnv *env = getEnv(); - apr_pool_t *pool = in_pool.getPool(); - if (env->ExceptionCheck()) - { - jthrowable t = env->ExceptionOccurred(); - jclass cls = env->GetObjectClass(t); - - jstring jclass_name; - { - jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;"); - jobject clsobj = env->CallObjectMethod(t, mid); - jclass basecls = env->GetObjectClass(clsobj); - mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;"); - jclass_name = (jstring) env->CallObjectMethod(clsobj, mid); - } - - jstring jmessage; - { - jmethodID mid = env->GetMethodID(cls, "getMessage", - "()Ljava/lang/String;"); - jmessage = (jstring) env->CallObjectMethod(t, mid); - } + return exception_to_cstring(in_pool.getPool()); +} - JNIStringHolder class_name(jclass_name); - if (jmessage) - { - JNIStringHolder message(jmessage); - msg = apr_pstrcat(pool, - static_cast<const char*>(class_name), ": ", - static_cast<const char*>(message), NULL); - } - else - msg = class_name.pstrdup(pool); - // ### Conditionally add t.printStackTrace() to msg? - } - return msg; +svn_error_t* +JNIUtil::checkJavaException(apr_status_t errorcode) +{ + if (!getEnv()->ExceptionCheck()) + return SVN_NO_ERROR; + svn_error_t* err = svn_error_create(errorcode, NULL, NULL); + const char* const msg = known_exception_to_cstring(err->pool); + if (msg) + err->message = apr_psprintf(err->pool, _("Java exception: %s"), msg); + else + err->message = _("Java exception"); + return err; } /** @@ -710,25 +841,6 @@ jstring JNIUtil::makeJString(const char *txt) return env->NewStringUTF(txt); } -void -JNIUtil::setExceptionThrown(bool flag) -{ - if (g_inInit) - { - // During global initialization, store any errors that occur - // in a global variable (since thread-local storage may not - // yet be available). - g_initException = flag; - } - else - { - // When global initialization is complete, thread-local - // storage should be available, so store the error there. - JNIThreadData *data = JNIThreadData::getThreadData(); - data->m_exceptionThrown = flag; - } -} - /** * Initialite the log file. * @param level the log level @@ -752,23 +864,6 @@ void JNIUtil::initLogFile(int level, jstring path) } /** - * Returns a buffer to format error messages. - * @return a buffer for formating error messages - */ -char *JNIUtil::getFormatBuffer() -{ - if (g_inInit) // during init -> use the global buffer - return g_initFormatBuffer; - - // use the buffer in the thread local storage - JNIThreadData *data = JNIThreadData::getThreadData(); - if (data == NULL) // if that does not exists -> use the global buffer - return g_initFormatBuffer; - - return data->m_formatBuffer; -} - -/** * Returns the current log level. * @return the log level */ @@ -819,6 +914,31 @@ jobject JNIUtil::createDate(apr_time_t time) return ret; } +apr_time_t +JNIUtil::getDate(jobject jdate) +{ + JNIEnv *env = getEnv(); + jclass clazz = env->FindClass("java/util/Date"); + if (isJavaExceptionThrown()) + return 0; + + static jmethodID mid = 0; + if (mid == 0) + { + mid = env->GetMethodID(clazz, "getTime", "()J"); + if (isJavaExceptionThrown()) + return 0; + } + + jlong jmillis = env->CallLongMethod(jdate, mid); + if (isJavaExceptionThrown()) + return 0; + + env->DeleteLocalRef(clazz); + + return jmillis * 1000; +} + /** * Create a Java byte array from an array of characters. * @param data the character array @@ -826,11 +946,9 @@ jobject JNIUtil::createDate(apr_time_t time) */ jbyteArray JNIUtil::makeJByteArray(const void *data, int length) { - if (data == NULL) - { - // a NULL will create no Java array - return NULL; - } + // a NULL will create no Java array + if (!data) + return NULL; JNIEnv *env = getEnv(); @@ -861,62 +979,11 @@ jbyteArray JNIUtil::makeJByteArray(const void *data, int length) */ jbyteArray JNIUtil::makeJByteArray(const svn_string_t *str) { - return JNIUtil::makeJByteArray(str->data, static_cast<int>(str->len)); -} - -/** - * Build the error message from the svn error into buffer. This - * method calls itselft recursively for all the chained errors - * - * @param err the subversion error - * @param depth the depth of the call, used for formating - * @param parent_apr_err the apr of the previous level, used for formating - * @param buffer the buffer where the formated error message will - * be stored - */ -void JNIUtil::assembleErrorMessage(svn_error_t *err, int depth, - apr_status_t parent_apr_err, - std::string &buffer) -{ - // buffer for a single error message - char errbuf[256]; - - /* Pretty-print the error */ - /* Note: we can also log errors here someday. */ - - /* When we're recursing, don't repeat the top-level message if its - * the same as before. */ - if (depth == 0 || err->apr_err != parent_apr_err) - { - /* Is this a Subversion-specific error code? */ - if ((err->apr_err > APR_OS_START_USEERR) - && (err->apr_err <= APR_OS_START_CANONERR)) - buffer.append(svn_strerror(err->apr_err, errbuf, sizeof(errbuf))); - /* Otherwise, this must be an APR error code. */ - else - { - /* Messages coming from apr_strerror are in the native - encoding, it's a good idea to convert them to UTF-8. */ - const char* utf8_message; - apr_strerror(err->apr_err, errbuf, sizeof(errbuf)); - svn_error_t* utf8_err = svn_utf_cstring_to_utf8( - &utf8_message, errbuf, err->pool); - if (utf8_err) - { - /* Use fuzzy transliteration instead. */ - svn_error_clear(utf8_err); - utf8_message = svn_utf_cstring_from_utf8_fuzzy(errbuf, err->pool); - } - buffer.append(utf8_message); - } - buffer.append("\n"); - } - if (err->message) - buffer.append(_("svn: ")).append(err->message).append("\n"); - - if (err->child) - assembleErrorMessage(err->child, depth + 1, err->apr_err, buffer); + // a NULL will create no Java array + if (!str) + return NULL; + return JNIUtil::makeJByteArray(str->data, static_cast<int>(str->len)); } /** @@ -936,8 +1003,6 @@ void JNIUtil::throwNullPointerException(const char *message) return; env->ThrowNew(clazz, message); - setExceptionThrown(); - env->DeleteLocalRef(clazz); } svn_error_t *JNIUtil::preprocessPath(const char *&path, apr_pool_t *pool) @@ -996,3 +1061,91 @@ svn_error_t *JNIUtil::preprocessPath(const char *&path, apr_pool_t *pool) return NULL; } + +/* Tag to use on the apr_pool_t to store a WrappedException reference */ +static const char *WrapExceptionTag = "org.apache.subversion.JavaHL.svnerror"; + +class WrappedException +{ + JNIEnv *m_env; + jthrowable m_exception; +#ifdef SVN_DEBUG + bool m_fetched; +#endif +public: + WrappedException(JNIEnv *env) + { + m_env = env; + + // Fetch exception inside local frame + jthrowable exceptionObj = env->ExceptionOccurred(); + + // Now clear exception status + env->ExceptionClear(); + + // As adding a reference in exception state fails + m_exception = static_cast<jthrowable>(env->NewGlobalRef(exceptionObj)); + +#ifdef SVN_DEBUG + m_fetched = false; +#endif + } + + static jthrowable get_exception(apr_pool_t *pool) + { + void *data; + if (! apr_pool_userdata_get(&data, WrapExceptionTag, pool)) + { + WrappedException *we = reinterpret_cast<WrappedException *>(data); + + if (we) + { +#ifdef SVN_DEBUG + we->m_fetched = TRUE; +#endif + // Create reference in local frame, as the pool will be cleared + return static_cast<jthrowable>( + we->m_env->NewLocalRef(we->m_exception)); + } + } + return NULL; + } + +private: + ~WrappedException() + { +#ifdef SVN_DEBUG + if (!m_fetched) + SVN_DBG(("Cleared svn_error_t * before Java exception was fetched")); +#endif + m_env->DeleteGlobalRef(m_exception); + } +public: + static apr_status_t cleanup(void *data) + { + WrappedException *we = reinterpret_cast<WrappedException *>(data); + + delete we; + return APR_SUCCESS; + } +}; + +svn_error_t* JNIUtil::wrapJavaException() +{ + if (!isExceptionThrown()) + return SVN_NO_ERROR; + + svn_error_t *err = svn_error_create(SVN_ERR_JAVAHL_WRAPPED, NULL, + "Wrapped Java Exception"); + apr_pool_userdata_set(new WrappedException(getEnv()), WrapExceptionTag, + WrappedException::cleanup, err->pool); + return err; +} + +jthrowable JNIUtil::unwrapJavaException(const svn_error_t *err) +{ + if (!err) + return NULL; + return + WrappedException::get_exception(err->pool); +} diff --git a/subversion/bindings/javahl/native/JNIUtil.h b/subversion/bindings/javahl/native/JNIUtil.h index d353984..e05d1ef 100644 --- a/subversion/bindings/javahl/native/JNIUtil.h +++ b/subversion/bindings/javahl/native/JNIUtil.h @@ -37,11 +37,29 @@ class SVNBase; #include <fstream> #include <apr_time.h> #include <string> +#include <vector> + struct svn_error_t; +struct svn_string_t; -#define JAVA_PACKAGE "org/apache/subversion/javahl" +#include "svn_error.h" + + +/** + * The name of the package in which the JavaHL classes are defined. + */ +#define JAVAHL_PACKAGE "org/apache/subversion/javahl" + +/** + * Construct a JavaHL class name for JNIEnv::FindClass. + */ +#define JAVAHL_CLASS(name) JAVAHL_PACKAGE name + +/** + * Construct a JavaHL class parameter name for JNIEnv::GetMethodID & co. + */ +#define JAVAHL_ARG(name) "L" JAVAHL_PACKAGE name -struct svn_string_t; /** * Class to hold a number of JNI related utility methods. No Objects @@ -67,19 +85,24 @@ class JNIUtil static jbyteArray makeJByteArray(const void *data, int length); static jbyteArray makeJByteArray(const svn_string_t *str); static jobject createDate(apr_time_t time); + static apr_time_t getDate(jobject jdate); static void logMessage(const char *message); static int getLogLevel(); - static char *getFormatBuffer(); static void initLogFile(int level, jstring path); static jstring makeJString(const char *txt); - static bool isJavaExceptionThrown(); static JNIEnv *getEnv(); - static void setEnv(JNIEnv *); /** * @return Whether any Throwable has been raised. */ - static bool isExceptionThrown(); + static bool isExceptionThrown() { return isJavaExceptionThrown(); } + static bool isJavaExceptionThrown() + { + return getEnv()->ExceptionCheck(); + } + + static svn_error_t *wrapJavaException(); + static jthrowable unwrapJavaException(const svn_error_t *err); static void handleAPRError(int error, const char *op); @@ -107,12 +130,27 @@ class JNIUtil static const char *thrownExceptionToCString(SVN::Pool &in_pool); /** + * Check if a Java exception was thrown and convert it to a + * Subversion error, using @a errorcode as the generic error code. + */ + static svn_error_t* checkJavaException(apr_status_t errorcode); + + /** + * Create a Java exception corresponding to err, and run + * svn_error_clear() on err. + */ + static jthrowable createClientException(svn_error_t *err, + jthrowable jcause = NULL); + + /** * Throw a Java exception corresponding to err, and run * svn_error_clear() on err. */ - static void handleSVNError(svn_error_t *err); + static void handleSVNError(svn_error_t *err, jthrowable jcause = NULL); - static jstring makeSVNErrorMessage(svn_error_t *err); + static std::string makeSVNErrorMessage(svn_error_t *err, + jstring *jerror_message, + jobject *jmessage_stack); /** * Create and throw a java.lang.Throwable instance. @@ -130,28 +168,27 @@ class JNIUtil */ static void throwError(const char *message) { - raiseThrowable(JAVA_PACKAGE"/JNIError", message); + raiseThrowable(JAVAHL_CLASS("/JNIError"), message); } static apr_pool_t *getPool(); - static bool JNIGlobalInit(JNIEnv *env); static bool JNIInit(JNIEnv *env); static bool initializeJNIRuntime(); - enum { formatBufferSize = 2048 }; enum { noLog, errorLog, exceptionLog, entryLog } LogLevel; + /** + * Mutex that secures the global configuration object. + */ + static JNIMutex *g_configMutex; + private: - static void wrappedHandleSVNError(svn_error_t *err); - static void assembleErrorMessage(svn_error_t *err, int depth, - apr_status_t parent_apr_err, - std::string &buffer); + friend bool initialize_jni_util(JNIEnv *env); + static bool JNIGlobalInit(JNIEnv *env); + + static jthrowable wrappedCreateClientException(svn_error_t *err, + jthrowable jcause); static void putErrorsInTrace(svn_error_t *err, std::vector<jobject> &stackTrace); - /** - * Set the appropriate global or thread-local flag that an exception - * has been thrown to @a flag. - */ - static void setExceptionThrown(bool flag = true); /** * The log level of this module. @@ -185,22 +222,6 @@ class JNIUtil static bool g_initException; /** - * Flag, that one thread is in the init code. Cannot use mutex - * here since apr is not initialized yet. - */ - static bool g_inInit; - - /** - * The JNI environment used during initialization. - */ - static JNIEnv *g_initEnv; - - /** - * Fuffer the format error messages during initialization. - */ - static char g_initFormatBuffer[formatBufferSize]; - - /** * The stream to write log messages to. */ static std::ofstream g_logStream; @@ -270,6 +291,16 @@ class JNIUtil } \ while (0) +#define POP_AND_RETURN_EXCEPTION_AS_SVNERROR() \ + do \ + { \ + svn_error_t *svn__err_for_exception = JNIUtil::wrapJavaException(); \ + \ + env->PopLocalFrame(NULL); \ + return svn__err_for_exception; \ + } \ + while (0) + /** * A useful macro. @@ -284,4 +315,19 @@ class JNIUtil } \ } while (0) +#define SVN_JNI_CATCH(statement, errorcode) \ + do { \ + do { statement; } while(0); \ + SVN_ERR(JNIUtil::checkJavaException((errorcode))); \ + } while(0) + +#define SVN_JNI_CATCH_VOID(statement) \ + do { \ + do { statement; } while(0); \ + if (JNIUtil::getEnv()->ExceptionCheck()) { \ + JNIUtil::getEnv()->ExceptionClear(); \ + return; \ + } \ + } while(0) + #endif // JNIUTIL_H diff --git a/subversion/bindings/javahl/native/ListCallback.cpp b/subversion/bindings/javahl/native/ListCallback.cpp index 9759ff6..1cd089d 100644 --- a/subversion/bindings/javahl/native/ListCallback.cpp +++ b/subversion/bindings/javahl/native/ListCallback.cpp @@ -87,13 +87,13 @@ ListCallback::doList(const char *path, static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ListCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ListCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); mid = env->GetMethodID(clazz, "doEntry", - "(L"JAVA_PACKAGE"/types/DirEntry;" - "L"JAVA_PACKAGE"/types/Lock;)V"); + "(" JAVAHL_ARG("/types/DirEntry;") + JAVAHL_ARG("/types/Lock;") ")V"); if (JNIUtil::isJavaExceptionThrown() || mid == 0) POP_AND_RETURN(SVN_NO_ERROR); } @@ -113,10 +113,8 @@ ListCallback::doList(const char *path, // call the Java method env->CallVoidMethod(m_callback, mid, jdirentry, jlock); - // No need to check for exception here, because we'll just return anyway - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } /** @@ -126,53 +124,5 @@ jobject ListCallback::createJavaDirEntry(const char *path, const char *absPath, const svn_dirent_t *dirent) { - JNIEnv *env = JNIUtil::getEnv(); - - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return SVN_NO_ERROR; - - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/DirEntry"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - static jmethodID mid = 0; - if (mid == 0) - { - mid = env->GetMethodID(clazz, "<init>", - "(Ljava/lang/String;Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/NodeKind;" - "JZJJLjava/lang/String;)V"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - } - - jstring jPath = JNIUtil::makeJString(path); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - jstring jAbsPath = JNIUtil::makeJString(absPath); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - jobject jNodeKind = EnumMapper::mapNodeKind(dirent->kind); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - jlong jSize = dirent->size; - jboolean jHasProps = (dirent->has_props? JNI_TRUE : JNI_FALSE); - jlong jLastChangedRevision = dirent->created_rev; - jlong jLastChanged = dirent->time; - jstring jLastAuthor = JNIUtil::makeJString(dirent->last_author); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - jobject ret = env->NewObject(clazz, mid, jPath, jAbsPath, jNodeKind, - jSize, jHasProps, jLastChangedRevision, - jLastChanged, jLastAuthor); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - return env->PopLocalFrame(ret); + return CreateJ::DirEntry(path, absPath, dirent); } diff --git a/subversion/bindings/javahl/native/LockTokenTable.cpp b/subversion/bindings/javahl/native/LockTokenTable.cpp new file mode 100644 index 0000000..ebf50da --- /dev/null +++ b/subversion/bindings/javahl/native/LockTokenTable.cpp @@ -0,0 +1,114 @@ +/** + * @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 LockTokenTable.cpp + * @brief Implementation of the class LockTokenTable + */ + +#include "LockTokenTable.h" +#include "JNIUtil.h" +#include "JNIStringHolder.h" +#include "Array.h" +#include <apr_hash.h> + +LockTokenTable::~LockTokenTable() +{ + if (m_jlock_tokens) + JNIUtil::getEnv()->DeleteLocalRef(m_jlock_tokens); +} + +apr_hash_t* +LockTokenTable::hash(const SVN::Pool &pool, bool null_if_empty) +{ + if (m_lock_tokens.size() == 0 && null_if_empty) + return NULL; + + apr_pool_t* result_pool = pool.getPool(); + apr_hash_t* lock_table = apr_hash_make(result_pool); + + for (lock_tokens_t::const_iterator it = m_lock_tokens.begin(); + it != m_lock_tokens.end(); ++it) + { + const char *path = apr_pstrdup(result_pool, it->first.c_str()); + const char *token = apr_pstrdup(result_pool, it->second.c_str()); + apr_hash_set(lock_table, path, APR_HASH_KEY_STRING, token); + } + + return lock_table; +} + +LockTokenTable::LockTokenTable(jobject jlock_tokens) + : m_jlock_tokens(jlock_tokens) +{ + + if (jlock_tokens != NULL) + { + JNIEnv *env = JNIUtil::getEnv(); + + jclass lock_cls = env->FindClass(JAVAHL_CLASS("/types/Lock")); + if (JNIUtil::isExceptionThrown()) + return; + + static jmethodID getPath_mid = 0; + if (0 == getPath_mid) + { + getPath_mid = env->GetMethodID(lock_cls, "getPath", + "()Ljava/lang/String;"); + if (JNIUtil::isExceptionThrown()) + return; + } + + static jmethodID getToken_mid = 0; + if (0 == getToken_mid) + { + getToken_mid = env->GetMethodID(lock_cls, "getToken", + "()Ljava/lang/String;"); + if (JNIUtil::isExceptionThrown()) + return; + } + + std::vector<jobject> locks = Array(jlock_tokens).vector(); + for (std::vector<jobject>::const_iterator it = locks.begin(); + it != locks.end(); ++it) + { + jobject jpath = env->CallObjectMethod(*it, getPath_mid); + if (JNIUtil::isExceptionThrown()) + return; + jobject jtoken = env->CallObjectMethod(*it, getToken_mid); + if (JNIUtil::isExceptionThrown()) + return; + + JNIStringHolder path((jstring)jpath); + if (JNIUtil::isExceptionThrown()) + return; + JNIStringHolder token((jstring)jtoken); + if (JNIUtil::isExceptionThrown()) + return; + + m_lock_tokens[std::string(static_cast<const char *>(path))] = + std::string(static_cast<const char *>(token)); + + env->DeleteLocalRef(jpath); + env->DeleteLocalRef(jtoken); + } + } +} diff --git a/subversion/bindings/javahl/native/LockTokenTable.h b/subversion/bindings/javahl/native/LockTokenTable.h new file mode 100644 index 0000000..af8b32a --- /dev/null +++ b/subversion/bindings/javahl/native/LockTokenTable.h @@ -0,0 +1,50 @@ +/** + * @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 LockTokenTable.h + * @brief Interface of the class LockTokenTable + */ + +#ifndef JAVAHL_LOCK_TOKEN_TABLE_H +#define JAVAHL_LOCK_TOKEN_TABLE_H + +#include <jni.h> +#include "Pool.h" + +struct apr_hash_t; + +#include <map> +#include <string> + +class LockTokenTable +{ + private: + typedef std::map<std::string, std::string> lock_tokens_t; + lock_tokens_t m_lock_tokens; + jobject m_jlock_tokens; + public: + LockTokenTable(jobject jlock_tokens); + ~LockTokenTable(); + apr_hash_t *hash(const SVN::Pool &pool, bool null_if_empty = true); +}; + +#endif // JAVAHL_LOCK_TOKEN_TABLE_H diff --git a/subversion/bindings/javahl/native/LogMessageCallback.cpp b/subversion/bindings/javahl/native/LogMessageCallback.cpp index 237aabf..ffda075 100644 --- a/subversion/bindings/javahl/native/LogMessageCallback.cpp +++ b/subversion/bindings/javahl/native/LogMessageCallback.cpp @@ -81,7 +81,7 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool) static jmethodID sm_mid = 0; if (sm_mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/LogMessageCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/LogMessageCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); @@ -103,9 +103,9 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool) hi = apr_hash_next(hi)) { const char *path = - reinterpret_cast<const char *>(svn__apr_hash_index_key(hi)); + reinterpret_cast<const char *>(apr_hash_this_key(hi)); svn_log_changed_path2_t *log_item = - reinterpret_cast<svn_log_changed_path2_t *>(svn__apr_hash_index_val(hi)); + reinterpret_cast<svn_log_changed_path2_t *>(apr_hash_this_val(hi)); jobject cp = CreateJ::ChangedPath(path, log_item); @@ -117,7 +117,7 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool) jobject jrevprops = NULL; if (log_entry->revprops != NULL && apr_hash_count(log_entry->revprops) > 0) - jrevprops = CreateJ::PropertyMap(log_entry->revprops); + jrevprops = CreateJ::PropertyMap(log_entry->revprops, pool); env->CallVoidMethod(m_callback, sm_mid, @@ -125,8 +125,6 @@ LogMessageCallback::singleMessage(svn_log_entry_t *log_entry, apr_pool_t *pool) (jlong)log_entry->revision, jrevprops, (jboolean)log_entry->has_children); - // No need to check for an exception here, because we return anyway. - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } diff --git a/subversion/bindings/javahl/native/MessageReceiver.cpp b/subversion/bindings/javahl/native/MessageReceiver.cpp index 18f695d..330c527 100644 --- a/subversion/bindings/javahl/native/MessageReceiver.cpp +++ b/subversion/bindings/javahl/native/MessageReceiver.cpp @@ -58,7 +58,7 @@ void MessageReceiver::receiveMessage(const char *message) static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/ISVNAdmin$MessageReceiver"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/ISVNAdmin$MessageReceiver")); if (JNIUtil::isJavaExceptionThrown()) return; diff --git a/subversion/bindings/javahl/native/NativeStream.cpp b/subversion/bindings/javahl/native/NativeStream.cpp new file mode 100644 index 0000000..193c36f --- /dev/null +++ b/subversion/bindings/javahl/native/NativeStream.cpp @@ -0,0 +1,381 @@ +/** + * @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 + */ + +#include "NativeStream.hpp" + +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_exception.hpp" + +#include "svn_private_config.h" + +namespace JavaHL { + +// Class JavaHL::NativeInputStream + +const char* const NativeInputStream::m_class_name = + JAVAHL_CLASS("/types/NativeInputStream"); + +NativeInputStream::~NativeInputStream() {} + +void NativeInputStream::set_stream(svn_stream_t* stream) +{ + if (m_stream) + throw std::logic_error(_("Native input stream is already bound")); + m_stream = stream; +} + +NativeInputStream* +NativeInputStream::get_self_unsafe(::Java::Env env, jobject jthis) +{ + jfieldID fid_cppaddr = NULL; + const jlong cppaddr = + findCppAddrForJObject(jthis, &fid_cppaddr, m_class_name); + return reinterpret_cast<NativeInputStream*>(cppaddr); +} + +NativeInputStream* +NativeInputStream::get_self(::Java::Env env, jobject jthis) +{ + NativeInputStream* self = get_self_unsafe(env, jthis); + if (!self) + ::Java::NullPointerException(env).raise(_("this [C++]")); + return self; +} + +void NativeInputStream::close(::Java::Env env, jobject jthis) +{ + SVN_JAVAHL_CHECK(env, svn_stream_close(m_stream)); + dispose(jthis); +} + +bool NativeInputStream::mark_supported(::Java::Env env) const +{ + return svn_stream_supports_mark(m_stream); +} + +void NativeInputStream::mark(::Java::Env env) +{ + if (!svn_stream_supports_mark(m_stream)) + return; + SVN_JAVAHL_CHECK(env, svn_stream_mark(m_stream, &m_mark, pool.getPool())); +} + +void NativeInputStream::reset(::Java::Env env) +{ + if (!svn_stream_supports_mark(m_stream)) + return; + if (m_mark) + SVN_JAVAHL_CHECK(env, svn_stream_seek(m_stream, m_mark)); + else + ::Java::IOException(env).raise(_("Invalid seek on native stream")); + } + +jint NativeInputStream::read(::Java::Env env) +{ + apr_size_t len = 1; + char byte; + SVN_JAVAHL_CHECK(env, svn_stream_read_full(m_stream, &byte, &len)); + if (len == 0) + return -1; // EOF + if (len == 1) + return jint(byte & 0xff); + ::Java::IOException(env).raise(_("Read from native stream failed")); + return -1; +} + +jint NativeInputStream::read(::Java::Env env, + ::Java::ByteArray::MutableContents& dst, + jint offset, jint length) +{ + if (offset < 0 || length < 0 || offset + length > dst.length()) + ::Java::IndexOutOfBoundsException(env).raise(); + if (!dst.data()) + ::Java::NullPointerException(env).raise(); + + apr_size_t len = length; + if (svn_stream_supports_partial_read(m_stream)) + SVN_JAVAHL_CHECK(env, svn_stream_read2(m_stream, + dst.data() + offset, &len)); + else + SVN_JAVAHL_CHECK(env, svn_stream_read_full(m_stream, + dst.data() + offset, &len)); + if (len == 0) + return -1; // EOF + if (len <= length) + return jint(len); + ::Java::IOException(env).raise(_("Read from native stream failed")); + return -1; +} + +jlong NativeInputStream::skip(::Java::Env env, jlong count) +{ + const apr_size_t len = count; + SVN_JAVAHL_CHECK(env, svn_stream_skip(m_stream, len)); + return count; +} + +void NativeInputStream::dispose(jobject jthis) +{ + jfieldID fid_cppaddr = NULL; + SVNBase::dispose(jthis, &fid_cppaddr, m_class_name); +} + + +// Class JavaHL::NativeOutputStream + +const char* const NativeOutputStream::m_class_name = + JAVAHL_CLASS("/types/NativeOutputStream"); + +NativeOutputStream::~NativeOutputStream() {} + +void NativeOutputStream::set_stream(svn_stream_t* stream) +{ + if (m_stream) + throw std::logic_error(_("Native output stream is already bound")); + m_stream = stream; +} + +NativeOutputStream* +NativeOutputStream::get_self_unsafe(::Java::Env env, jobject jthis) +{ + jfieldID fid_cppaddr = NULL; + const jlong cppaddr = + findCppAddrForJObject(jthis, &fid_cppaddr, m_class_name); + return reinterpret_cast<NativeOutputStream*>(cppaddr); +} + +NativeOutputStream* +NativeOutputStream::get_self(::Java::Env env, jobject jthis) +{ + NativeOutputStream* self = get_self_unsafe(env, jthis); + if (!self) + ::Java::NullPointerException(env).raise(_("this [C++]")); + return self; +} + +void NativeOutputStream::close(::Java::Env env, jobject jthis) +{ + SVN_JAVAHL_CHECK(env, svn_stream_close(m_stream)); + dispose(jthis); +} + +void NativeOutputStream::write(::Java::Env env, jint byte) +{ + const char data = char(byte & 0xff); + apr_size_t len = 1; + SVN_JAVAHL_CHECK(env, svn_stream_write(m_stream, &data, &len)); + if (len != 1) + ::Java::IOException(env).raise(_("Write to native stream failed")); +} + +void NativeOutputStream::write(::Java::Env env, + const ::Java::ByteArray::Contents& src, + jint offset, jint length) +{ + if (offset < 0 || length < 0 || offset + length > src.length()) + ::Java::IndexOutOfBoundsException(env).raise(); + if (!src.data()) + ::Java::NullPointerException(env).raise(); + + apr_size_t len = length; + SVN_JAVAHL_CHECK(env, svn_stream_write(m_stream, src.data() + offset, &len)); + if (len != length) + ::Java::IOException(env).raise(_("Write to native stream failed")); +} + +void NativeOutputStream::dispose(jobject jthis) +{ + jfieldID fid_cppaddr = NULL; + SVNBase::dispose(jthis, &fid_cppaddr, m_class_name); +} + +} // namespace JavaHL + + +// Class JavaHL::NativeInputStream native method implementation +#include "../include/org_apache_subversion_javahl_types_NativeInputStream.h" + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_close( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, close) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + self->close(Java::Env(jenv), jthis); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); +} + +JNIEXPORT jboolean JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_markSupported( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, markSupported) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + self->mark_supported(Java::Env(jenv)); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); + return false; +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_mark( + JNIEnv* jenv, jobject jthis, jint) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, mark) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + self->mark(Java::Env(jenv)); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_reset( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, reset) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + self->reset(Java::Env(jenv)); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); +} + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_read__( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, read) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + return self->read(Java::Env(jenv)); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); + return 0; +} + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_read___3BII( + JNIEnv* jenv, jobject jthis, jbyteArray jdst, jint joffset, jint jlength) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, read) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + + const Java::Env env(jenv); + Java::ByteArray dst(env, jdst); + Java::ByteArray::MutableContents dst_contents(dst); + + return self->read(env, dst_contents, joffset, jlength); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); + return 0; +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_skip( + JNIEnv* jenv, jobject jthis, jlong jcount) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, skip) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeInputStream, self); + return self->skip(Java::Env(jenv), jcount); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); + return 0; +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeInputStream_finalize( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeInputStream, finalize) + { + JavaHL::NativeInputStream* native = + JavaHL::NativeInputStream::get_self_unsafe(Java::Env(jenv), jthis); + if (native != NULL) + native->finalize(); + } + SVN_JAVAHL_JNI_CATCH; +} + + +// Class JavaHL::NativeOutputStream native method implementation +#include "../include/org_apache_subversion_javahl_types_NativeOutputStream.h" + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeOutputStream_close( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeOutputStream, close) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeOutputStream, self); + self->close(Java::Env(jenv), jthis); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeOutputStream_write__I( + JNIEnv* jenv, jobject jthis, jint byte) +{ + SVN_JAVAHL_JNI_TRY(NativeOutputStream, write) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeOutputStream, self); + self->write(Java::Env(jenv), byte); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeOutputStream_write___3BII( + JNIEnv* jenv, jobject jthis, jbyteArray jsrc, jint joffset, jint jlength) +{ + SVN_JAVAHL_JNI_TRY(NativeOutputStream, write) + { + SVN_JAVAHL_GET_BOUND_OBJECT(JavaHL::NativeOutputStream, self); + + const Java::Env env(jenv); + const Java::ByteArray src(env, jsrc); + + self->write(env, Java::ByteArray::Contents(src), joffset, jlength); + } + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(Java::IOException); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_NativeOutputStream_finalize( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(NativeOutputStream, finalize) + { + JavaHL::NativeOutputStream* native = + JavaHL::NativeOutputStream::get_self_unsafe(Java::Env(jenv), jthis); + if (native != NULL) + native->finalize(); + } + SVN_JAVAHL_JNI_CATCH; +} diff --git a/subversion/bindings/javahl/native/NativeStream.hpp b/subversion/bindings/javahl/native/NativeStream.hpp new file mode 100644 index 0000000..57fd137 --- /dev/null +++ b/subversion/bindings/javahl/native/NativeStream.hpp @@ -0,0 +1,211 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_NATIVE_STREAM_HPP +#define SVN_JAVAHL_NATIVE_STREAM_HPP + +#include <stdexcept> + +#include "jniwrapper/jni_array.hpp" + +#include "SVNBase.h" + +#include "svn_io.h" + +namespace JavaHL { + +/** + * Object wrapper for @c org.apache.subversion.javahl.types.NativeInputStream. + * + * @since New in 1.9. + */ +class NativeInputStream : public ::SVNBase +{ +public: + /** + * Construcs the native instance that will be wrapped in a Java obejct. + * If @a stream is @c NULL, you must call #set_stream before creating + * the Java wrapper. + */ + explicit NativeInputStream(svn_stream_t* stream = NULL) + : m_stream(stream), + m_mark(NULL) + {} + + ~NativeInputStream(); + + /** + * Returns a reference to the pool owned by this wrapped object. + */ + const SVN::Pool& get_pool() const + { + return pool; + } + + /** + * Sets the @a stream that this object will own. + * Do not call this function if a stream was passed to the constructor. + */ + void set_stream(svn_stream_t* stream); + + /** + * Create the Java object that binds to this native object. + */ + jobject create_java_wrapper() + { + return createCppBoundObject(m_class_name); + } + + /** + * Retrieve the address of the native object from the bound Java object. + */ + static NativeInputStream* get_self(::Java::Env env, jobject jthis); + + static NativeInputStream* get_self_unsafe(::Java::Env env, jobject jthis); + +public: + /** + * Implements @c InputStream.close(). + * Also disposes the native object. + */ + void close(::Java::Env env, jobject jthis); + + /** + * Implements @c InputStream.markSupported(). + */ + bool mark_supported(::Java::Env env) const; + + /** + * Implements @c InputStream.mark(int). + * The @c readlimit parameter of the Java method is ignored. + */ + void mark(::Java::Env env); + + /** + * Implements @c InputStream.reset(). + */ + void reset(::Java::Env env); + + /** + * Implements @c InputStream.read(). + */ + jint read(::Java::Env env); + + /** + * Implements @c InputStream.read(byte[],int,int). + */ + jint read(::Java::Env env, + ::Java::ByteArray::MutableContents& dst, + jint offset, jint length); + + /** + * Implements @c InputStream.skip(long). + */ + jlong skip(::Java::Env env, jlong count); + +private: + virtual void dispose(jobject jthis); + + static const char* const m_class_name; + svn_stream_t* m_stream; + svn_stream_mark_t* m_mark; +}; + + +/** + * Object wrapper for @c org.apache.subversion.javahl.types.NativeOutputStream. + * + * @since New in 1.9. + */ +class NativeOutputStream : public ::SVNBase +{ +public: + /** + * Construcs the native instance that will be wrapped in a Java obejct. + * If @a stream is @c NULL, you must call #set_stream before creating + * the Java wrapper. + */ + explicit NativeOutputStream(svn_stream_t* stream = NULL) + : m_stream(stream) + {} + + ~NativeOutputStream(); + + /** + * Returns a reference to the pool owned by this wrapped object. + */ + const SVN::Pool& get_pool() const + { + return pool; + } + + /** + * Sets the @a stream that this object will own. + * Do not call this function if a stream was passed to the constructor. + */ + void set_stream(svn_stream_t* stream); + + /** + * Create the Java object that binds to this native object. + */ + jobject create_java_wrapper() + { + return createCppBoundObject(m_class_name); + } + + /** + * Retrieve the address of the native object from the bound Java object. + */ + static NativeOutputStream* get_self(::Java::Env env, jobject jthis); + + static NativeOutputStream* get_self_unsafe(::Java::Env env, jobject jthis); + +public: + /** + * Implements @c OutputStream.close(). + * Also disposes the native object. + */ + void close(::Java::Env env, jobject jthis); + + /** + * Implements @c OutputStream.write(int). + */ + void write(::Java::Env env, jint byte); + + /** + * Implements @c OutputStream.write(byte[],int,int). + */ + void write(::Java::Env env, + const ::Java::ByteArray::Contents& src, + jint offset, jint length); + +private: + virtual void dispose(jobject jthis); + + static const char* const m_class_name; + svn_stream_t* m_stream; +}; + +} // namespace JavaHL + +#endif // SVN_JAVAHL_NATIVE_STREAM_HPP diff --git a/subversion/bindings/javahl/native/OperationContext.cpp b/subversion/bindings/javahl/native/OperationContext.cpp new file mode 100644 index 0000000..b455c36 --- /dev/null +++ b/subversion/bindings/javahl/native/OperationContext.cpp @@ -0,0 +1,657 @@ +/** + * @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 OperationContext.cpp + * @brief Implementation of the class OperationContext + */ + +#include <apr_file_io.h> + +#include "GlobalConfig.h" +#include "OperationContext.h" +#include "JNIUtil.h" +#include "JNICriticalSection.h" + +#include "Prompter.h" +#include "CreateJ.h" +#include "EnumMapper.h" +#include "CommitMessage.h" + +#include "svn_client.h" +#include "private/svn_wc_private.h" +#include "private/svn_dep_compat.h" +#include "svn_private_config.h" + +OperationContext::OperationContext(SVN::Pool &pool) + : m_config(NULL), + m_prompter(NULL), + m_cancelOperation(0), + m_pool(&pool), + m_jctx(NULL), + m_jcfgcb(NULL), + m_jtunnelcb(NULL) +{} + +void +OperationContext::attachJavaObject( + jobject contextHolder, const char *contextClassType, + const char *contextFieldName, jfieldID * ctxFieldID) +{ + JNIEnv *env = JNIUtil::getEnv(); + + /* Grab a global reference to the Java object embedded in the parent + Java object. */ + if ((*ctxFieldID) == 0) + { + jclass clazz = env->GetObjectClass(contextHolder); + if (JNIUtil::isJavaExceptionThrown()) + return; + + (*ctxFieldID) = env->GetFieldID(clazz, contextFieldName, contextClassType); + if (JNIUtil::isJavaExceptionThrown() || (*ctxFieldID) == 0) + return; + + env->DeleteLocalRef(clazz); + } + + jobject jctx = env->GetObjectField(contextHolder, (*ctxFieldID)); + if (JNIUtil::isJavaExceptionThrown()) + return; + + m_jctx = env->NewGlobalRef(jctx); + if (JNIUtil::isJavaExceptionThrown()) + return; + + env->DeleteLocalRef(jctx); +} + +OperationContext::~OperationContext() +{ + JNIEnv *env = JNIUtil::getEnv(); + env->DeleteGlobalRef(m_jctx); + if (m_jcfgcb) + env->DeleteGlobalRef(m_jcfgcb); + if (m_jtunnelcb) + env->DeleteGlobalRef(m_jtunnelcb); +} + +apr_hash_t * +OperationContext::getConfigData() +{ + if(m_pool->getPool() == NULL) + { + JNIUtil::throwNullPointerException("pool is null"); + } + + if (m_config == NULL) + { + const char *configDir = m_configDir.c_str(); + if (m_configDir.empty()) + configDir = NULL; + SVN_JNI_ERR( + svn_config_get_config(&m_config, configDir, m_pool->getPool()), NULL); + notifyConfigLoad(); + } + + return m_config; +} + +svn_auth_baton_t * +OperationContext::getAuthBaton(SVN::Pool &in_pool) +{ + svn_auth_baton_t *ab; + apr_pool_t *pool = in_pool.getPool(); + + apr_hash_t * configData = getConfigData(); + + if (configData == NULL) + { + return NULL; + } + + svn_config_t *config = static_cast<svn_config_t *>(apr_hash_get(configData, + SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING)); + + const bool use_native_store = GlobalConfig::useNativeCredentialsStore(); + + /* The whole list of registered providers */ + apr_array_header_t *providers; + svn_auth_provider_object_t *provider; + + if (use_native_store) + { + /* Populate the registered providers with the platform-specific providers */ + SVN_JNI_ERR( + svn_auth_get_platform_specific_client_providers( + &providers, config, pool), + NULL); + + /* Use the prompter (if available) to prompt for password and cert + * caching. */ + svn_auth_plaintext_prompt_func_t plaintext_prompt_func; + void *plaintext_prompt_baton; + svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func; + void *plaintext_passphrase_prompt_baton; + + if (m_prompter.get()) + { + plaintext_prompt_func = Prompter::plaintext_prompt; + plaintext_prompt_baton = m_prompter.get(); + plaintext_passphrase_prompt_func = Prompter::plaintext_passphrase_prompt; + plaintext_passphrase_prompt_baton = m_prompter.get(); + } + else + { + plaintext_prompt_func = NULL; + plaintext_prompt_baton = NULL; + plaintext_passphrase_prompt_func = NULL; + plaintext_passphrase_prompt_baton = NULL; + } + + /* The main disk-caching auth providers, for both + * 'username/password' creds and 'username' creds. */ + + svn_auth_get_simple_provider2(&provider, plaintext_prompt_func, + plaintext_prompt_baton, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + svn_auth_get_username_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + svn_auth_get_ssl_server_trust_file_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + svn_auth_get_ssl_client_cert_file_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + svn_auth_get_ssl_client_cert_pw_file_provider2( + &provider, + plaintext_passphrase_prompt_func, plaintext_passphrase_prompt_baton, + pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + } + else + { + // Not using hte native credentials store, start with an empty + // providers array. + providers = apr_array_make(pool, 0, sizeof(svn_auth_provider_object_t *)); + } + + if (m_prompter.get()) + { + /* Two basic prompt providers: username/password, and just username.*/ + provider = m_prompter->get_provider_simple(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + provider = m_prompter->get_provider_username(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + /* Three ssl prompt providers, for server-certs, client-certs, + * and client-cert-passphrases. */ + provider = m_prompter->get_provider_server_ssl_trust(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + provider = m_prompter->get_provider_client_ssl(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + provider = m_prompter->get_provider_client_ssl_password(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + } + + /* Build an authentication baton to give to libsvn_client. */ + svn_auth_open(&ab, providers, pool); + + /* Place any default --username or --password credentials into the + * auth_baton's run-time parameter hash. ### Same with --no-auth-cache? */ + if (!m_userName.empty()) + svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, + apr_pstrdup(in_pool.getPool(), m_userName.c_str())); + if (!m_passWord.empty()) + svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, + apr_pstrdup(in_pool.getPool(), m_passWord.c_str())); + /* Store where to retrieve authentication data? */ + if (!m_configDir.empty()) + svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR, + apr_pstrdup(in_pool.getPool(), m_configDir.c_str())); + return ab; +} + +jobject OperationContext::getSelf() const +{ + return m_jctx; +} + +void +OperationContext::username(const char *pi_username) +{ + m_userName = (pi_username == NULL ? "" : pi_username); +} + +void +OperationContext::password(const char *pi_password) +{ + m_passWord = (pi_password == NULL ? "" : pi_password); +} + +void +OperationContext::setPrompt(Prompter::UniquePtr prompter) +{ + m_prompter = prompter; +} + +void +OperationContext::setConfigDirectory(const char *configDir) +{ + // A change to the config directory may necessitate creation of + // the config templates. + SVN::Pool requestPool; + SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.getPool()), ); + + m_configDir = (configDir == NULL ? "" : configDir); + + m_config = NULL; +} + +const char * +OperationContext::getConfigDirectory() const +{ + return (m_configDir.empty() ? NULL : m_configDir.c_str()); +} + +void OperationContext::setConfigEventHandler(jobject jcfgcb) +{ + JNIEnv *env = JNIUtil::getEnv(); + if (jcfgcb) + { + jcfgcb = env->NewGlobalRef(jcfgcb); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + if (m_jcfgcb) + env->DeleteGlobalRef(m_jcfgcb); + m_jcfgcb = jcfgcb; +} + +jobject OperationContext::getConfigEventHandler() const +{ + return m_jcfgcb; +} + +const char * +OperationContext::getUsername() const +{ + return (m_userName.empty() ? NULL : m_userName.c_str()); +} + +const char * +OperationContext::getPassword() const +{ + return (m_passWord.empty() ? NULL : m_passWord.c_str()); +} + +Prompter::UniquePtr OperationContext::clonePrompter() const +{ + if (m_prompter.get()) + return m_prompter->clone(); + return Prompter::UniquePtr(NULL); +} + +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() +{ + svn_atomic_set(&m_cancelOperation, 1); +} + +void +OperationContext::resetCancelRequest() +{ + svn_atomic_set(&m_cancelOperation, 0); +} + +bool +OperationContext::isCancelledOperation() +{ + return bool(svn_atomic_read(&m_cancelOperation)); +} + +svn_error_t * +OperationContext::checkCancel(void *cancelBaton) +{ + OperationContext *that = static_cast<OperationContext *>(cancelBaton); + if (that->isCancelledOperation()) + return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Operation cancelled")); + else if (JNIUtil::isJavaExceptionThrown()) + return svn_error_create(SVN_ERR_CANCELLED, JNIUtil::wrapJavaException(), + _("Operation cancelled")); + else + return SVN_NO_ERROR; +} + +void +OperationContext::progress(apr_off_t progressVal, apr_off_t total, void *baton, + apr_pool_t *pool) +{ + jobject jctx = (jobject) baton; + if (!jctx) + return; + + JNIEnv *env = JNIUtil::getEnv(); + + // Create a local frame for our references + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID mid = 0; + if (mid == 0) + { + jclass clazz = env->GetObjectClass(jctx); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + mid = env->GetMethodID(clazz, "onProgress", + "(" JAVAHL_ARG("/ProgressEvent;") ")V"); + if (JNIUtil::isJavaExceptionThrown() || mid == 0) + POP_AND_RETURN_NOTHING(); + } + + static jmethodID midCT = 0; + jclass clazz = env->FindClass(JAVAHL_CLASS("/ProgressEvent")); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + if (midCT == 0) + { + midCT = env->GetMethodID(clazz, "<init>", "(JJ)V"); + if (JNIUtil::isJavaExceptionThrown() || midCT == 0) + POP_AND_RETURN_NOTHING(); + } + + // Call the Java method. + jobject jevent = env->NewObject(clazz, midCT, (jlong) progressVal, + (jlong) total); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + env->CallVoidMethod(jctx, mid, jevent); + + POP_AND_RETURN_NOTHING(); +} + +const char * +OperationContext::getClientName() const +{ + return "javahl"; +} + +svn_error_t * +OperationContext::clientName(void *baton, const char **name, apr_pool_t *pool) +{ + OperationContext *that = (OperationContext *) baton; + + *name = that->getClientName(); + + return SVN_NO_ERROR; +} + +void +OperationContext::notifyConfigLoad() +{ + if (!m_jcfgcb) + return; + + JNIEnv *env = JNIUtil::getEnv(); + + static jmethodID onload_mid = 0; + if (0 == onload_mid) + { + jclass cls = env->FindClass(JAVAHL_CLASS("/callback/ConfigEvent")); + if (JNIUtil::isJavaExceptionThrown()) + return; + onload_mid = env->GetMethodID(cls, "onLoad", + "(" JAVAHL_ARG("/ISVNConfig;") ")V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jclass cfg_cls = env->FindClass(JAVAHL_CLASS("/util/ConfigImpl")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID ctor_mid = 0; + if (0 == ctor_mid) + { + ctor_mid = env->GetMethodID(cfg_cls, "<init>", "(J)V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + static jmethodID dispose_mid = 0; + if (0 == dispose_mid) + { + dispose_mid = env->GetMethodID(cfg_cls, "dispose", "()V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jobject jcbimpl = env->NewObject(cfg_cls, ctor_mid, + reinterpret_cast<jlong>(this)); + if (JNIUtil::isJavaExceptionThrown()) + return; + env->CallVoidMethod(m_jcfgcb, onload_mid, jcbimpl); + if (JNIUtil::isJavaExceptionThrown()) + return; + 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), + jclosecb(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); + } + + ~TunnelContext() + { + apr_file_close(request_out); + apr_file_close(response_in); + } + + 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 jclosecb; +}; + +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(JAVAHL_CLASS("/util/RequestChannel"), env, fd); +} +jobject create_ResponseChannel(JNIEnv *env, apr_file_t *fd) +{ + return create_Channel(JAVAHL_CLASS("/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(JAVAHL_CLASS("/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(svn_stream_t **request, svn_stream_t **response, + svn_ra_close_tunnel_func_t *close_func, + void **close_baton, + void *tunnel_baton, + const char *tunnel_name, const char *user, + const char *hostname, int port, + svn_cancel_func_t cancel_func, void *cancel_baton, + 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"))); + } + + *close_func = closeTunnel; + *close_baton = tc; + *request = svn_stream_from_aprfile2(tc->request_out, FALSE, pool); + *response = svn_stream_from_aprfile2(tc->response_in, FALSE, pool); + + 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(JAVAHL_CLASS("/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)" + JAVAHL_ARG("/callback/TunnelAgent$CloseTunnelCallback;")), + SVN_ERR_BASE); + } + + jobject jtunnelcb = jobject(tunnel_baton); + SVN_JNI_CATCH( + tc->jclosecb = env->CallObjectMethod( + jtunnelcb, mid, jrequest, jresponse, + jtunnel_name, juser, jhostname, jint(port)), + SVN_ERR_BASE); + + return SVN_NO_ERROR; +} + +void +OperationContext::closeTunnel(void *tunnel_context, void *) +{ + TunnelContext* tc = static_cast<TunnelContext*>(tunnel_context); + jobject jclosecb = tc->jclosecb; + delete tc; + + if (!jclosecb) + return; + + JNIEnv *env = JNIUtil::getEnv(); + + static jmethodID mid = 0; + if (0 == mid) + { + jclass cls; + SVN_JNI_CATCH_VOID( + cls= env->FindClass( + JAVAHL_CLASS("/callback/TunnelAgent$CloseTunnelCallback"))); + SVN_JNI_CATCH_VOID(mid = env->GetMethodID(cls, "closeTunnel", "()V")); + } + SVN_JNI_CATCH_VOID(env->CallVoidMethod(jclosecb, mid)); +} diff --git a/subversion/bindings/javahl/native/OperationContext.h b/subversion/bindings/javahl/native/OperationContext.h new file mode 100644 index 0000000..b0d82c2 --- /dev/null +++ b/subversion/bindings/javahl/native/OperationContext.h @@ -0,0 +1,128 @@ +/** + * @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 OperationContext.h + * @brief Interface of the class OperationContext + */ + +#ifndef JAVAHL_OPERATION_CONTEXT_H +#define JAVAHL_OPERATION_CONTEXT_H + +#include <string> +#include <memory> + +#include "svn_types.h" +#include "svn_client.h" +#include "private/svn_atomic.h" + +#include <jni.h> +#include "Pool.h" +#include "JNIStringHolder.h" + +class Prompter; + +/** + * This class contains a Java objects implementing the interface RaSharedContext + */ +class OperationContext +{ + private: + std::string m_userName; + std::string m_passWord; + std::string m_configDir; + + apr_hash_t * m_config; + + std::auto_ptr<Prompter> m_prompter; + svn_atomic_t m_cancelOperation; + + protected: + SVN::Pool *m_pool; + + 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( + svn_stream_t **request, svn_stream_t **response, + svn_ra_close_tunnel_func_t *close_func, void **close_baton, + void *tunnel_baton, + const char *tunnel_name, const char *user, + const char *hostname, int port, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *pool); + + static void closeTunnel( + void *tunnel_context, void *tunnel_baton); + + public: + OperationContext(SVN::Pool &pool); + void attachJavaObject(jobject contextHolder, const char *contextClassType, const char *contextFieldName, jfieldID * ctxFieldID); + virtual ~OperationContext(); + + static svn_error_t *checkCancel(void *cancelBaton); + + virtual void username(const char *pi_username); + virtual void password(const char *pi_password); + virtual void setPrompt(std::auto_ptr<Prompter> prompter); + svn_auth_baton_t *getAuthBaton(SVN::Pool &in_pool); + + void cancelOperation(); + void resetCancelRequest(); + virtual bool isCancelledOperation(); + jobject getSelf() const; + const char *getConfigDirectory() const; + const char *getUsername() const; + const char *getPassword() const; + std::auto_ptr<Prompter> clonePrompter() const; + + /** + * Set the configuration directory, taking the usual steps to + * ensure that Subversion's config file templates exist in the + * specified location. + */ + void setConfigDirectory(const char *configDir); + + /** + * Return configuration data for the context. + * Read it from config directory if necessary + */ + apr_hash_t *getConfigData(); + + void setConfigEventHandler(jobject jcfgcb); + jobject getConfigEventHandler() const; + + 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 diff --git a/subversion/bindings/javahl/native/PatchCallback.cpp b/subversion/bindings/javahl/native/PatchCallback.cpp index 0cff577..ac1b3d4 100644 --- a/subversion/bindings/javahl/native/PatchCallback.cpp +++ b/subversion/bindings/javahl/native/PatchCallback.cpp @@ -80,7 +80,7 @@ PatchCallback::singlePatch(svn_boolean_t *filtered, static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/PatchCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/PatchCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); @@ -106,7 +106,7 @@ PatchCallback::singlePatch(svn_boolean_t *filtered, jboolean jfiltered = env->CallBooleanMethod(m_callback, mid, jcanonPath, jpatchAbsPath, jrejectAbsPath); if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(SVN_NO_ERROR); + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); *filtered = (jfiltered ? TRUE : FALSE); diff --git a/subversion/bindings/javahl/native/PatchCallback.h b/subversion/bindings/javahl/native/PatchCallback.h index d10a309..61ec883 100644 --- a/subversion/bindings/javahl/native/PatchCallback.h +++ b/subversion/bindings/javahl/native/PatchCallback.h @@ -30,8 +30,6 @@ #include <jni.h> #include "svn_client.h" -struct info_entry; - /** * This class holds a Java callback object, which will receive every line of * the file for which the callback information is requested. diff --git a/subversion/bindings/javahl/native/Path.cpp b/subversion/bindings/javahl/native/Path.cpp index b9c69ea..c1a1fec 100644 --- a/subversion/bindings/javahl/native/Path.cpp +++ b/subversion/bindings/javahl/native/Path.cpp @@ -27,18 +27,24 @@ #include <jni.h> #include "Path.h" #include "svn_path.h" +#include "svn_dirent_uri.h" #include "JNIUtil.h" +#include "JNIStringHolder.h" #include "Pool.h" +#include "svn_private_config.h" /** * Constructor * - * @see Path::Path(const std::string &) + * @see PathBase::PathBase(const std::string &) * @param path Path string */ -Path::Path(const char *pi_path, SVN::Pool &in_pool) +PathBase::PathBase(const char *pi_path, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool) + : m_error_occurred(NULL) { - init(pi_path, in_pool); + init(pi_path, initfunc, in_pool); } /** @@ -48,19 +54,26 @@ Path::Path(const char *pi_path, SVN::Pool &in_pool) * * @param path Path string */ -Path::Path(const std::string &pi_path, SVN::Pool &in_pool) +PathBase::PathBase(const std::string &pi_path, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool) + : m_error_occurred(NULL) { - init(pi_path.c_str(), in_pool); + init(pi_path.c_str(), initfunc, in_pool); } /** - * Copy constructor - * - * @param path Path to be copied + * Constructor from a Java string. */ -Path::Path(const Path &pi_path, SVN::Pool &in_pool) +PathBase::PathBase(jstring jpath, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool) + : m_error_occurred(NULL) { - init(pi_path.c_str(), in_pool); + JNIStringHolder path(jpath); + if (JNIUtil::isJavaExceptionThrown()) + return; + init(path, initfunc, in_pool); } /** @@ -69,17 +82,13 @@ Path::Path(const Path &pi_path, SVN::Pool &in_pool) * @param path Path string */ void -Path::init(const char *pi_path, SVN::Pool &in_pool) +PathBase::init(const char *pi_path, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool) { - if (*pi_path == 0) - { - m_error_occurred = NULL; - m_path = ""; - } - else + if (pi_path && *pi_path) { - m_error_occurred = JNIUtil::preprocessPath(pi_path, in_pool.getPool()); - + m_error_occurred = initfunc(pi_path, in_pool); m_path = pi_path; } } @@ -88,7 +97,7 @@ Path::init(const char *pi_path, SVN::Pool &in_pool) * @return Path string */ const std::string & -Path::path() const +PathBase::path() const { return m_path; } @@ -97,7 +106,7 @@ Path::path() const * @return Path string as a C string */ const char * -Path::c_str() const +PathBase::c_str() const { return m_path.c_str(); } @@ -105,8 +114,8 @@ Path::c_str() const /** * Assignment operator */ -Path& -Path::operator=(const Path &pi_path) +PathBase& +PathBase::operator=(const PathBase &pi_path) { m_error_occurred = NULL; m_path = pi_path.m_path; @@ -114,12 +123,12 @@ Path::operator=(const Path &pi_path) return *this; } - svn_error_t *Path::error_occurred() const +svn_error_t *PathBase::error_occurred() const { return m_error_occurred; } -jboolean Path::isValid(const char *p) +jboolean PathBase::isValid(const char *p) { if (p == NULL) return JNI_FALSE; @@ -136,3 +145,25 @@ jboolean Path::isValid(const char *p) return JNI_FALSE; } } + +svn_error_t* +Path::initfunc(const char*& path, SVN::Pool& pool) +{ + return JNIUtil::preprocessPath(path, pool.getPool()); +} + +svn_error_t* +URL::initfunc(const char*& path, SVN::Pool& pool) +{ + if (svn_path_is_url(path)) + return JNIUtil::preprocessPath(path, pool.getPool()); + return svn_error_createf(SVN_ERR_BAD_URL, NULL, + _("Not an URL: %s"), path); +} + +svn_error_t* +Relpath::initfunc(const char*& path, SVN::Pool& pool) +{ + path = svn_relpath__internal_style(path, pool.getPool()); + return SVN_NO_ERROR; +} diff --git a/subversion/bindings/javahl/native/Path.h b/subversion/bindings/javahl/native/Path.h index d23261e..5684efb 100644 --- a/subversion/bindings/javahl/native/Path.h +++ b/subversion/bindings/javahl/native/Path.h @@ -24,18 +24,19 @@ * @brief Interface of the C++ class Path. */ -#ifndef PATH_H -#define PATH_H +#ifndef JAVAHL_PATH_H +#define JAVAHL_PATH_H #include <string> #include <jni.h> #include "Pool.h" struct svn_error_t; + /** * Encapsulation for Subversion Path handling */ -class Path +class PathBase { private: // The path to be stored. @@ -48,9 +49,11 @@ class Path * * @param pi_path Path string */ - void init(const char *pi_path, SVN::Pool &in_pool); + void init(const char *pi_path, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool); - public: + protected: /** * Constructor that takes a string as parameter. * The string is converted to subversion internal @@ -58,27 +61,31 @@ class Path * * @param pi_path Path string */ - Path(const std::string &pi_path, SVN::Pool &in_pool); + PathBase(const std::string &pi_path, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool); /** * Constructor * - * @see Path::Path (const std::string &) + * @see PathBase::PathBase (const std::string &) * @param pi_path Path string */ - Path(const char *pi_path, SVN::Pool &in_pool); + PathBase(const char *pi_path, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool); /** - * Copy constructor - * - * @param pi_path Path to be copied + * Constructor from a Java string. */ - Path(const Path &pi_path, SVN::Pool &in_pool); + PathBase(jstring jpath, + svn_error_t* initfunc(const char*&, SVN::Pool&), + SVN::Pool &in_pool); /** * Assignment operator */ - Path &operator=(const Path&); + PathBase &operator=(const PathBase&); /** * @return Path string @@ -92,6 +99,7 @@ class Path svn_error_t *error_occurred() const; +public: /** * Returns whether @a path is non-NULL and passes the @c * svn_path_check_valid() test. @@ -101,4 +109,113 @@ class Path static jboolean isValid(const char *path); }; -#endif // PATH_H + +/** + * Dirent or URI + */ +class Path : protected PathBase +{ + public: + Path(const std::string &pi_path, SVN::Pool &in_pool) + : PathBase(pi_path, initfunc, in_pool) + {} + + Path(const char *pi_path, SVN::Pool &in_pool) + : PathBase(pi_path, initfunc, in_pool) + {} + + Path(jstring jpath, SVN::Pool &in_pool) + : PathBase(jpath, initfunc, in_pool) + {} + + Path& operator=(const Path& that) + { + PathBase::operator=(that); + return *this; + } + + const std::string &path() const { return PathBase::path(); } + const char *c_str() const { return PathBase::c_str(); } + + svn_error_t *error_occurred() const + { + return PathBase::error_occurred(); + } + + private: + static svn_error_t* initfunc(const char*&, SVN::Pool&); +}; + +/** + * URL + */ +class URL : protected PathBase +{ + public: + URL(const std::string &pi_path, SVN::Pool &in_pool) + : PathBase(pi_path, initfunc, in_pool) + {} + + URL(const char *pi_path, SVN::Pool &in_pool) + : PathBase(pi_path, initfunc, in_pool) + {} + + URL(jstring jpath, SVN::Pool &in_pool) + : PathBase(jpath, initfunc, in_pool) + {} + + URL& operator=(const URL& that) + { + PathBase::operator=(that); + return *this; + } + + const std::string &path() const { return PathBase::path(); } + const char *c_str() const { return PathBase::c_str(); } + + svn_error_t *error_occurred() const + { + return PathBase::error_occurred(); + } + + private: + static svn_error_t* initfunc(const char*&, SVN::Pool&); +}; + +/** + * Relative path + */ +class Relpath : protected PathBase +{ + public: + Relpath(const std::string &pi_path, SVN::Pool &in_pool) + : PathBase(pi_path, initfunc, in_pool) + {} + + Relpath(const char *pi_path, SVN::Pool &in_pool) + : PathBase(pi_path, initfunc, in_pool) + {} + + Relpath(jstring jpath, SVN::Pool &in_pool) + : PathBase(jpath, initfunc, in_pool) + {} + + Relpath& operator=(const Relpath& that) + { + PathBase::operator=(that); + return *this; + } + + const std::string &path() const { return PathBase::path(); } + const char *c_str() const { return PathBase::c_str(); } + + svn_error_t *error_occurred() const + { + return PathBase::error_occurred(); + } + + private: + static svn_error_t* initfunc(const char*&, SVN::Pool&); +}; + +#endif // JAVAHL_PATH_H diff --git a/subversion/bindings/javahl/native/Prompter.cpp b/subversion/bindings/javahl/native/Prompter.cpp index 14bd312..bad7467 100644 --- a/subversion/bindings/javahl/native/Prompter.cpp +++ b/subversion/bindings/javahl/native/Prompter.cpp @@ -25,468 +25,532 @@ */ #include "Prompter.h" -#include "Pool.h" +#include "AuthnCallback.hpp" + #include "JNIUtil.h" #include "JNIStringHolder.h" #include "../include/org_apache_subversion_javahl_callback_UserPasswordCallback.h" + #include <apr_strings.h> -#include "svn_auth.h" #include "svn_error.h" #include "svn_error_codes.h" #include "svn_private_config.h" -/** - * Constructor - * @param jprompter a global reference to the Java callback object - */ -Prompter::Prompter(jobject jprompter) -{ - m_prompter = jprompter; -} -Prompter::~Prompter() +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_string.hpp" + +// Class Prompter + +Prompter::UniquePtr Prompter::create(jobject jprompter) { - if (m_prompter!= NULL) + if (!jprompter) + return UniquePtr(NULL); + + // Make sure no C++ exceptions are propagated from here. + const ::Java::Env jenv; + try { - // Since the reference to the Java object is a global one, it - // has to be deleted. - JNIEnv *env = JNIUtil::getEnv(); - env->DeleteGlobalRef(m_prompter); + const jclass cls = ::Java::ClassCache::get_authn_cb(jenv)->get_class(); + if (!jenv.IsInstanceOf(jprompter, cls)) + return UniquePtr(NULL); + + return UniquePtr(new Prompter(jenv, jprompter)); } + SVN_JAVAHL_JNI_CATCH; + return UniquePtr(NULL); } -/** - * Create a C++ peer object for the Java callback object - * - * @param jprompter Java callback object - * @return C++ peer object - */ -Prompter *Prompter::makeCPrompter(jobject jprompter) +Prompter::UniquePtr Prompter::clone() const { - // If we have no Java object, we need no C++ object. - if (jprompter == NULL) - return NULL; - - JNIEnv *env = JNIUtil::getEnv(); - - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; + return create(m_prompter.get()); +} - // Sanity check that the Java object implements UserPasswordCallback. - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; +Prompter::Prompter(::Java::Env env, jobject jprompter) + : m_prompter(env, jprompter) +{} - if (!env->IsInstanceOf(jprompter, clazz)) - POP_AND_RETURN_NULL; +Prompter::~Prompter() {} - // Create a new global ref for the Java object, because it is - // longer used that this call. - jobject myPrompt = env->NewGlobalRef(jprompter); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - env->PopLocalFrame(NULL); +svn_auth_provider_object_t * +Prompter::get_provider_simple(SVN::Pool &in_pool) +{ + apr_pool_t *pool = in_pool.getPool(); + svn_auth_provider_object_t *provider; + svn_auth_get_simple_prompt_provider(&provider, + simple_prompt, + this, + 2, /* retry limit */ + pool); - // Create the C++ peer. - return new Prompter(myPrompt); + return provider; } -/** - * Retrieve the username from the Java object - * @return Java string for the username or NULL - */ -jstring Prompter::username() +svn_auth_provider_object_t * +Prompter::get_provider_username(SVN::Pool &in_pool) { - JNIEnv *env = JNIUtil::getEnv(); - - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; - - // The method id will not change during the time this library is - // loaded, so it can be cached. - static jmethodID mid = 0; - - if (mid == 0) - { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + apr_pool_t *pool = in_pool.getPool(); + svn_auth_provider_object_t *provider; + svn_auth_get_username_prompt_provider(&provider, + username_prompt, + this, + 2, /* retry limit */ + pool); - mid = env->GetMethodID(clazz, "getUsername", "()Ljava/lang/String;"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN_NULL; - } + return provider; +} - jstring ret = static_cast<jstring>(env->CallObjectMethod(m_prompter, mid)); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; +svn_auth_provider_object_t *Prompter:: +get_provider_server_ssl_trust(SVN::Pool &in_pool) +{ + apr_pool_t *pool = in_pool.getPool(); + svn_auth_provider_object_t *provider; + svn_auth_get_ssl_server_trust_prompt_provider + (&provider, ssl_server_trust_prompt, this, pool); - return (jstring) env->PopLocalFrame(ret); + return provider; } -/** - * Retrieve the password from the Java object - * @return Java string for the password or NULL - */ -jstring Prompter::password() +svn_auth_provider_object_t *Prompter:: +get_provider_client_ssl(SVN::Pool &in_pool) { - JNIEnv *env = JNIUtil::getEnv(); + apr_pool_t *pool = in_pool.getPool(); + svn_auth_provider_object_t *provider; + svn_auth_get_ssl_client_cert_prompt_provider(&provider, + ssl_client_cert_prompt, + this, + 2 /* retry limit */, + pool); - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; + return provider; +} - // The method id will not change during the time this library is - // loaded, so it can be cached. - static jmethodID mid = 0; +svn_auth_provider_object_t * +Prompter::get_provider_client_ssl_password(SVN::Pool &in_pool) +{ + apr_pool_t *pool = in_pool.getPool(); + svn_auth_provider_object_t *provider; + svn_auth_get_ssl_client_cert_pw_prompt_provider + (&provider, ssl_client_cert_pw_prompt, this, 2 /* retry limit */, + pool); - if (mid == 0) - { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + return provider; +} - mid = env->GetMethodID(clazz, "getPassword", "()Ljava/lang/String;"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN_NULL; - } +svn_error_t *Prompter::simple_prompt( + svn_auth_cred_simple_t **cred_p, + void *baton, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_simple_prompt( + env, cred_p, realm, username, may_save, pool)); + return err; +} - jstring ret = static_cast<jstring>(env->CallObjectMethod(m_prompter, mid)); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; +svn_error_t *Prompter::username_prompt( + svn_auth_cred_username_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_username_prompt( + env, cred_p, realm, may_save, pool)); + return err; +} - return (jstring) env->PopLocalFrame(ret); +svn_error_t *Prompter::ssl_server_trust_prompt( + svn_auth_cred_ssl_server_trust_t **cred_p, + void *baton, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_ssl_server_trust_prompt( + env, cred_p, realm, failures, cert_info, may_save, pool)); + return err; } -/** - * Ask the user a question, which can be answered by yes/no. - * @param realm the server realm, for which this question is asked - * @param question the question to ask the user - * @param yesIsDefault flag if the yes-button should be the default button - * @return flag who the user answered the question - */ -bool Prompter::askYesNo(const char *realm, const char *question, - bool yesIsDefault) + +svn_error_t *Prompter::ssl_client_cert_prompt( + svn_auth_cred_ssl_client_cert_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) { - JNIEnv *env = JNIUtil::getEnv(); + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_ssl_client_cert_prompt( + env, cred_p, realm, may_save, pool)); + return err; +} - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return false; +svn_error_t *Prompter::ssl_client_cert_pw_prompt( + svn_auth_cred_ssl_client_cert_pw_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_ssl_client_cert_pw_prompt( + env, cred_p, realm, may_save, pool)); + return err; +} - // The method id will not change during the time this library is - // loaded, so it can be cached. - static jmethodID mid = 0; +svn_error_t *Prompter::plaintext_prompt( + svn_boolean_t *may_save_plaintext, + const char *realmstring, + void *baton, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_plaintext_prompt( + env, may_save_plaintext, realmstring, pool)); + return err; +} - if (mid == 0) - { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); - - mid = env->GetMethodID(clazz, "askYesNo", - "(Ljava/lang/String;Ljava/lang/String;Z)Z"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN(false); - } +svn_error_t *Prompter::plaintext_passphrase_prompt( + svn_boolean_t *may_save_plaintext, + const char *realmstring, + void *baton, + apr_pool_t *pool) +{ + const ::Java::Env env; + svn_error_t *err; + SVN_JAVAHL_CATCH( + env, SVN_ERR_RA_NOT_AUTHORIZED, + err = static_cast<Prompter*>(baton)->dispatch_plaintext_passphrase_prompt( + env, may_save_plaintext, realmstring, pool)); + return err; +} - // convert the texts to Java strings - jstring jrealm = JNIUtil::makeJString(realm); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); - jstring jquestion = JNIUtil::makeJString(question); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); +svn_error_t *Prompter::dispatch_simple_prompt( + ::Java::Env env, + svn_auth_cred_simple_t **cred_p, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); + + ::JavaHL::AuthnCallback::AuthnResult result( + env, + authn.user_password_prompt(::Java::String(env, realm), + ::Java::String(env, username), + may_save)); + if (!result.get()) + return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("User canceled dialog")); - // execute the callback - jboolean ret = env->CallBooleanMethod(m_prompter, mid, jrealm, jquestion, - yesIsDefault ? JNI_TRUE : JNI_FALSE); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); + ::Java::String user(env, result.identity()); + ::Java::String pass(env, result.secret()); + svn_auth_cred_simple_t *cred = + static_cast<svn_auth_cred_simple_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->username = user.strdup(pool); + cred->password = pass.strdup(pool); + cred->may_save = result.save(); + *cred_p = cred; - env->PopLocalFrame(NULL); - return ret ? true:false; + return SVN_NO_ERROR; } -const char *Prompter::askQuestion(const char *realm, const char *question, - bool showAnswer, bool maySave) +svn_error_t *Prompter::dispatch_username_prompt( + ::Java::Env env, + svn_auth_cred_username_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) { - JNIEnv *env = JNIUtil::getEnv(); - - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); - static jmethodID mid = 0; - static jmethodID mid2 = 0; - if (mid == 0) - { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - - mid = env->GetMethodID(clazz, "askQuestion", - "(Ljava/lang/String;Ljava/lang/String;" - "ZZ)Ljava/lang/String;"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN_NULL; - - mid2 = env->GetMethodID(clazz, "userAllowedSave", "()Z"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN_NULL; - } + ::JavaHL::AuthnCallback::AuthnResult result( + env, + authn.username_prompt(::Java::String(env, realm), may_save)); + if (!result.get()) + return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("User canceled dialog")); - jstring jrealm = JNIUtil::makeJString(realm); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + ::Java::String user(env, result.identity()); + svn_auth_cred_username_t *cred = + static_cast<svn_auth_cred_username_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->username = user.strdup(pool); + cred->may_save = result.save(); + *cred_p = cred; - jstring jquestion = JNIUtil::makeJString(question); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; + return SVN_NO_ERROR; +} - jstring janswer = static_cast<jstring>( - env->CallObjectMethod(m_prompter, mid, jrealm, - jquestion, - showAnswer ? JNI_TRUE : JNI_FALSE, - maySave ? JNI_TRUE : JNI_FALSE)); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; +svn_error_t *Prompter::dispatch_ssl_server_trust_prompt( + ::Java::Env env, + svn_auth_cred_ssl_server_trust_t **cred_p, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); + + ::JavaHL::AuthnCallback::AuthnResult result( + env, + authn.ssl_server_trust_prompt( + ::Java::String(env, realm), + ::JavaHL::AuthnCallback::SSLServerCertFailures(env, jint(failures)), + ::JavaHL::AuthnCallback::SSLServerCertInfo(env, cert_info->ascii_cert), + may_save)); + if (!result.get()) + return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("User canceled dialog")); - JNIStringHolder answer(janswer); - if (answer != NULL) - { - m_answer = answer; - m_maySave = env->CallBooleanMethod(m_prompter, mid2) ? true: false; - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN_NULL; - } - else + const bool trust = result.trust(); + if (!trust) { - m_answer = ""; - m_maySave = false; + *cred_p = NULL; + return SVN_NO_ERROR; } - env->PopLocalFrame(NULL); - return m_answer.c_str(); -} + const bool save = result.save(); + svn_auth_cred_ssl_server_trust_t *cred = + static_cast<svn_auth_cred_ssl_server_trust_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->may_save = save; + if (save) + cred->accepted_failures = failures; + *cred_p = cred; -int Prompter::askTrust(const char *question, bool maySave) -{ - static jmethodID mid = 0; - JNIEnv *env = JNIUtil::getEnv(); - - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return -1; - - if (mid == 0) - { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(-1); - - mid = env->GetMethodID(clazz, "askTrustSSLServer", - "(Ljava/lang/String;Z)I"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN(-1); - } - jstring jquestion = JNIUtil::makeJString(question); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(-1); - - jint ret = env->CallIntMethod(m_prompter, mid, jquestion, - maySave ? JNI_TRUE : JNI_FALSE); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(-1); - - env->PopLocalFrame(NULL); - return ret; + return SVN_NO_ERROR; } -bool Prompter::prompt(const char *realm, const char *pi_username, bool maySave) +svn_error_t *Prompter::dispatch_ssl_client_cert_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) { - JNIEnv *env = JNIUtil::getEnv(); - jboolean ret; + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); - // Create a local frame for our references - env->PushLocalFrame(LOCAL_FRAME_SIZE); - if (JNIUtil::isJavaExceptionThrown()) - return false; + ::JavaHL::AuthnCallback::AuthnResult result( + env, + authn.ssl_client_cert_prompt(::Java::String(env, realm), may_save)); + if (!result.get()) + return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("User canceled dialog")); - static jmethodID mid = 0; - static jmethodID mid2 = 0; - if (mid == 0) - { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/UserPasswordCallback"); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); - - mid = env->GetMethodID(clazz, "prompt", - "(Ljava/lang/String;Ljava/lang/String;Z)Z"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN(false); - - mid2 = env->GetMethodID(clazz, "userAllowedSave", "()Z"); - if (JNIUtil::isJavaExceptionThrown() || mid == 0) - POP_AND_RETURN(false); - } + ::Java::String path(env, result.identity()); + svn_auth_cred_ssl_client_cert_t *cred = + static_cast<svn_auth_cred_ssl_client_cert_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->cert_file = path.strdup(pool); + cred->may_save = result.save(); + *cred_p = cred; - jstring jrealm = JNIUtil::makeJString(realm); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); + return SVN_NO_ERROR; +} - jstring jusername = JNIUtil::makeJString(pi_username); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); +svn_error_t *Prompter::dispatch_ssl_client_cert_pw_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_pw_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) +{ + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); - ret = env->CallBooleanMethod(m_prompter, mid, jrealm, jusername, - maySave ? JNI_TRUE: JNI_FALSE); - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); + ::JavaHL::AuthnCallback::AuthnResult result( + env, + authn.ssl_client_cert_passphrase_prompt( + ::Java::String(env, realm), may_save)); + if (!result.get()) + return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, + _("User canceled dialog")); - m_maySave = env->CallBooleanMethod(m_prompter, mid2) ? true : false; - if (JNIUtil::isJavaExceptionThrown()) - POP_AND_RETURN(false); + ::Java::String passphrase(env, result.secret()); + svn_auth_cred_ssl_client_cert_pw_t *cred = + static_cast<svn_auth_cred_ssl_client_cert_pw_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->password = passphrase.strdup(pool); + cred->may_save = result.save(); + *cred_p = cred; - env->PopLocalFrame(NULL); - return ret ? true:false; + return SVN_NO_ERROR; } -svn_auth_provider_object_t *Prompter::getProviderSimple(SVN::Pool &in_pool) +svn_error_t *Prompter::dispatch_plaintext_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool) { - apr_pool_t *pool = in_pool.getPool(); - svn_auth_provider_object_t *provider; - svn_auth_get_simple_prompt_provider(&provider, - simple_prompt, - this, - 2, /* retry limit */ - pool); - - return provider; + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); + *may_save_plaintext = + authn.allow_store_plaintext_password(::Java::String(env, realmstring)); + return SVN_NO_ERROR; } -svn_auth_provider_object_t *Prompter::getProviderUsername(SVN::Pool &in_pool) +svn_error_t *Prompter::dispatch_plaintext_passphrase_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool) { - apr_pool_t *pool = in_pool.getPool(); - svn_auth_provider_object_t *provider; - svn_auth_get_username_prompt_provider(&provider, - username_prompt, - this, - 2, /* retry limit */ - pool); - - return provider; + ::JavaHL::AuthnCallback authn(env, m_prompter.get()); + *may_save_plaintext = + authn.allow_store_plaintext_passphrase(::Java::String(env, realmstring)); + return SVN_NO_ERROR; } -svn_auth_provider_object_t *Prompter::getProviderServerSSLTrust(SVN::Pool &in_pool) + +// Class CompatPrompter + +Prompter::UniquePtr CompatPrompter::create(jobject jprompter) { - apr_pool_t *pool = in_pool.getPool(); - svn_auth_provider_object_t *provider; - svn_auth_get_ssl_server_trust_prompt_provider - (&provider, ssl_server_trust_prompt, this, pool); + if (!jprompter) + return UniquePtr(NULL); - return provider; + // Make sure no C++ exceptions are propagated from here. + const ::Java::Env jenv; + try + { + const jclass cls = + ::Java::ClassCache::get_user_passwd_cb(jenv)->get_class(); + if (!jenv.IsInstanceOf(jprompter, cls)) + return UniquePtr(NULL); + + return UniquePtr(new CompatPrompter(jenv, jprompter)); + } + SVN_JAVAHL_JNI_CATCH; + return UniquePtr(NULL); } -svn_auth_provider_object_t *Prompter::getProviderClientSSL(SVN::Pool &in_pool) +Prompter::UniquePtr CompatPrompter::clone() const { - apr_pool_t *pool = in_pool.getPool(); - svn_auth_provider_object_t *provider; - svn_auth_get_ssl_client_cert_prompt_provider(&provider, - ssl_client_cert_prompt, - this, - 2 /* retry limit */, - pool); - - return provider; + return create(m_prompter.get()); } -svn_auth_provider_object_t *Prompter::getProviderClientSSLPassword(SVN::Pool &in_pool) +CompatPrompter::CompatPrompter(::Java::Env env, jobject jprompter) + : Prompter(env, jprompter) +{} + +CompatPrompter::~CompatPrompter() {} + +namespace { +jstring compat_ask_question( + bool& allowed_save, + ::Java::Env env, + ::JavaHL::UserPasswordCallback& authn, + const char *realm, const char *question, + bool show_answer, bool may_save) { - apr_pool_t *pool = in_pool.getPool(); - svn_auth_provider_object_t *provider; - svn_auth_get_ssl_client_cert_pw_prompt_provider - (&provider, ssl_client_cert_pw_prompt, this, 2 /* retry limit */, - pool); + const jstring janswer = + authn.ask_question(::Java::String(env, realm), + ::Java::String(env, question), + show_answer, may_save); - return provider; -} + if (janswer) + allowed_save = authn.user_allowed_save(); + else + allowed_save = false; -svn_error_t *Prompter::simple_prompt(svn_auth_cred_simple_t **cred_p, - void *baton, - const char *realm, const char *username, - svn_boolean_t may_save, - apr_pool_t *pool) + return janswer; +} +} // anonymous namespace + +svn_error_t *CompatPrompter::dispatch_simple_prompt( + ::Java::Env env, + svn_auth_cred_simple_t **cred_p, + const char *realm, const char *username, + svn_boolean_t may_save, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); - svn_auth_cred_simple_t *ret = - reinterpret_cast<svn_auth_cred_simple_t*>(apr_pcalloc(pool, sizeof(*ret))); - if (!that->prompt(realm, username, may_save ? true : false)) - return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, - _("User canceled dialog")); - jstring juser = that->username(); - JNIStringHolder user(juser); - if (user == NULL) + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); + + if (!authn.prompt(::Java::String(env, realm), + ::Java::String(env, username), + may_save)) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("User canceled dialog")); - ret->username = apr_pstrdup(pool,user); - jstring jpass = that->password(); - JNIStringHolder pass(jpass); - if (pass == NULL) + const ::Java::String user(env, authn.get_username()); + const ::Java::String pass(env, authn.get_password()); + + if (!user.get() || !pass.get()) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("User canceled dialog")); - else - { - ret->password = apr_pstrdup(pool, pass); - ret->may_save = that->m_maySave; - } - *cred_p = ret; + + svn_auth_cred_simple_t *cred = + static_cast<svn_auth_cred_simple_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->username = user.strdup(pool); + cred->password = pass.strdup(pool); + cred->may_save = authn.user_allowed_save(); + *cred_p = cred; return SVN_NO_ERROR; } -svn_error_t *Prompter::username_prompt(svn_auth_cred_username_t **cred_p, - void *baton, - const char *realm, - svn_boolean_t may_save, - apr_pool_t *pool) +svn_error_t *CompatPrompter::dispatch_username_prompt( + ::Java::Env env, + svn_auth_cred_username_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); - svn_auth_cred_username_t *ret = - reinterpret_cast<svn_auth_cred_username_t*>(apr_pcalloc(pool, sizeof(*ret))); - const char *user = that->askQuestion(realm, _("Username: "), true, - may_save ? true : false); - if (user == NULL) + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); + + bool allowed_save; + const ::Java::String user( + env, + compat_ask_question(allowed_save, env, authn, + realm, _("Username: "), true, may_save)); + if (!user.get()) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("User canceled dialog")); - ret->username = apr_pstrdup(pool,user); - ret->may_save = that->m_maySave; - *cred_p = ret; + + svn_auth_cred_username_t *cred = + static_cast<svn_auth_cred_username_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->username = user.strdup(pool); + cred->may_save = allowed_save; + *cred_p = cred; return SVN_NO_ERROR; } svn_error_t * -Prompter::ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred_p, - void *baton, - const char *realm, - apr_uint32_t failures, - const svn_auth_ssl_server_cert_info_t *cert_info, - svn_boolean_t may_save, - apr_pool_t *pool) +CompatPrompter::dispatch_ssl_server_trust_prompt( + ::Java::Env env, + svn_auth_cred_ssl_server_trust_t **cred_p, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); - svn_auth_cred_ssl_server_trust_t *ret = - reinterpret_cast<svn_auth_cred_ssl_server_trust_t*>(apr_pcalloc(pool, sizeof(*ret))); + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); std::string question = _("Error validating server certificate for "); question += realm; @@ -526,16 +590,20 @@ Prompter::ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred_p, question += "\n"; } - switch(that->askTrust(question.c_str(), may_save ? true : false)) + svn_auth_cred_ssl_server_trust_t *cred = + static_cast<svn_auth_cred_ssl_server_trust_t*>(apr_pcalloc(pool, sizeof(*cred))); + + switch (authn.ask_trust_ssl_server(::Java::String(env, question), may_save)) { case org_apache_subversion_javahl_callback_UserPasswordCallback_AcceptTemporary: - *cred_p = ret; - ret->may_save = FALSE; + cred->may_save = FALSE; + cred->accepted_failures = failures; + *cred_p = cred; break; case org_apache_subversion_javahl_callback_UserPasswordCallback_AcceptPermanently: - *cred_p = ret; - ret->may_save = TRUE; - ret->accepted_failures = failures; + cred->may_save = TRUE; + cred->accepted_failures = failures; + *cred_p = cred; break; default: *cred_p = NULL; @@ -544,79 +612,91 @@ Prompter::ssl_server_trust_prompt(svn_auth_cred_ssl_server_trust_t **cred_p, } svn_error_t * -Prompter::ssl_client_cert_prompt(svn_auth_cred_ssl_client_cert_t **cred_p, - void *baton, - const char *realm, - svn_boolean_t may_save, - apr_pool_t *pool) +CompatPrompter::dispatch_ssl_client_cert_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); - svn_auth_cred_ssl_client_cert_t *ret = - reinterpret_cast<svn_auth_cred_ssl_client_cert_t*>(apr_pcalloc(pool, sizeof(*ret))); - const char *cert_file = - that->askQuestion(realm, _("client certificate filename: "), true, - may_save ? true : false); - if (cert_file == NULL) + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); + + bool allowed_save; + const ::Java::String path( + env, + compat_ask_question(allowed_save, env, authn, realm, + _("Client certificate filename: "), + true, may_save)); + if (!path.get()) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("User canceled dialog")); - ret->cert_file = apr_pstrdup(pool, cert_file); - ret->may_save = that->m_maySave; - *cred_p = ret; + + svn_auth_cred_ssl_client_cert_t *cred = + static_cast<svn_auth_cred_ssl_client_cert_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->cert_file = path.strdup(pool); + cred->may_save = allowed_save; + *cred_p = cred; return SVN_NO_ERROR; } svn_error_t * -Prompter::ssl_client_cert_pw_prompt(svn_auth_cred_ssl_client_cert_pw_t **cred_p, - void *baton, - const char *realm, - svn_boolean_t may_save, - apr_pool_t *pool) +CompatPrompter::dispatch_ssl_client_cert_pw_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_pw_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); - svn_auth_cred_ssl_client_cert_pw_t *ret = - reinterpret_cast<svn_auth_cred_ssl_client_cert_pw_t*>(apr_pcalloc(pool, sizeof(*ret))); - const char *info = that->askQuestion(realm, - _("client certificate passphrase: "), - false, may_save ? true : false); - if (info == NULL) + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); + + bool allowed_save; + const ::Java::String info( + env, + compat_ask_question(allowed_save, env, authn, realm, + _("Client certificate passphrase: "), + false, may_save)); + if (!info.get()) return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("User canceled dialog")); - ret->password = apr_pstrdup(pool, info); - ret->may_save = that->m_maySave; - *cred_p = ret; + + svn_auth_cred_ssl_client_cert_pw_t *cred = + static_cast<svn_auth_cred_ssl_client_cert_pw_t*>(apr_pcalloc(pool, sizeof(*cred))); + cred->password = info.strdup(pool); + cred->may_save = allowed_save; + *cred_p = cred; return SVN_NO_ERROR; } svn_error_t * -Prompter::plaintext_prompt(svn_boolean_t *may_save_plaintext, - const char *realmstring, - void *baton, - apr_pool_t *pool) +CompatPrompter::dispatch_plaintext_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); - bool result = that->askYesNo(realmstring, - _("Store password unencrypted?"), - false); - - *may_save_plaintext = (result ? TRUE : FALSE); + *may_save_plaintext = authn.ask_yes_no( + ::Java::String(env, realmstring), + ::Java::String(env, _("Store password unencrypted?")), + false); return SVN_NO_ERROR; } svn_error_t * -Prompter::plaintext_passphrase_prompt(svn_boolean_t *may_save_plaintext, - const char *realmstring, - void *baton, - apr_pool_t *pool) +CompatPrompter::dispatch_plaintext_passphrase_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool) { - Prompter *that = static_cast<Prompter *>(baton); - - bool result = that->askYesNo(realmstring, - _("Store passphrase unencrypted?"), - false); + ::JavaHL::UserPasswordCallback authn(env, m_prompter.get()); - *may_save_plaintext = (result ? TRUE : FALSE); + *may_save_plaintext = authn.ask_yes_no( + ::Java::String(env, realmstring), + ::Java::String(env, _("Store passphrase unencrypted?")), + false); return SVN_NO_ERROR; } diff --git a/subversion/bindings/javahl/native/Prompter.h b/subversion/bindings/javahl/native/Prompter.h index 722abce..06a7f2d 100644 --- a/subversion/bindings/javahl/native/Prompter.h +++ b/subversion/bindings/javahl/native/Prompter.h @@ -24,93 +24,219 @@ * @brief Interface of the class Prompter */ -#ifndef PROMPTER_H -#define PROMPTER_H +#ifndef SVN_JAVAHL_PROMPTER_H +#define SVN_JAVAHL_PROMPTER_H + +#include <memory> -#include <jni.h> #include "svn_auth.h" -#include <string> + #include "Pool.h" -/** - * This class requests username/password and informations about - * ssl-certificates from the user. - */ + +#include "jniwrapper/jni_globalref.hpp" + class Prompter { - private: +public: + typedef ::std::auto_ptr<Prompter> UniquePtr; + /** - * The Java callback object. + * Factory method; @a prompter is a local reference to the Java + * callback object. */ - jobject m_prompter; + static Prompter::UniquePtr create(jobject jprompter); /** - * Tntermediate storage for an answer. + * Return a clone of the current object, referring to the same Java + * prompter object. */ - std::string m_answer; + virtual Prompter::UniquePtr clone() const; + + virtual ~Prompter(); + + svn_auth_provider_object_t *get_provider_username(SVN::Pool &in_pool); + svn_auth_provider_object_t *get_provider_simple(SVN::Pool &in_pool); + svn_auth_provider_object_t *get_provider_server_ssl_trust(SVN::Pool &in_pool); + svn_auth_provider_object_t *get_provider_client_ssl(SVN::Pool &in_pool); + svn_auth_provider_object_t *get_provider_client_ssl_password(SVN::Pool &in_pool); + +protected: + explicit Prompter(::Java::Env env, jobject jprompter); /** - * Flag is the user allowed, that the last answer is stored in the - * configuration. + * The Java callback object. */ - bool m_maySave; - - Prompter(jobject jprompter); - bool prompt(const char *realm, const char *pi_username, bool maySave); - bool askYesNo(const char *realm, const char *question, bool yesIsDefault); - const char *askQuestion(const char *realm, const char *question, - bool showAnswer, bool maySave); - int askTrust(const char *question, bool maySave); - jstring password(); - jstring username(); - static svn_error_t *simple_prompt(svn_auth_cred_simple_t **cred_p, - void *baton, const char *realm, - const char *username, - svn_boolean_t may_save, - apr_pool_t *pool); - static svn_error_t *username_prompt - (svn_auth_cred_username_t **cred_p, - void *baton, - const char *realm, - svn_boolean_t may_save, - apr_pool_t *pool); - static svn_error_t *ssl_server_trust_prompt - (svn_auth_cred_ssl_server_trust_t **cred_p, - void *baton, - const char *realm, - apr_uint32_t failures, - const svn_auth_ssl_server_cert_info_t *cert_info, - svn_boolean_t may_save, - apr_pool_t *pool); - static svn_error_t *ssl_client_cert_prompt - (svn_auth_cred_ssl_client_cert_t **cred_p, - void *baton, - const char *realm, - svn_boolean_t may_save, - apr_pool_t *pool); - static svn_error_t *ssl_client_cert_pw_prompt - (svn_auth_cred_ssl_client_cert_pw_t **cred_p, - void *baton, - const char *realm, - svn_boolean_t may_save, - apr_pool_t *pool); - public: - static Prompter *makeCPrompter(jobject jprompter); - ~Prompter(); - svn_auth_provider_object_t *getProviderUsername(SVN::Pool &in_pool); - svn_auth_provider_object_t *getProviderSimple(SVN::Pool &in_pool); - svn_auth_provider_object_t *getProviderServerSSLTrust(SVN::Pool &in_pool); - svn_auth_provider_object_t *getProviderClientSSL(SVN::Pool &in_pool); - svn_auth_provider_object_t *getProviderClientSSLPassword(SVN::Pool &in_pool); - - static svn_error_t *plaintext_prompt(svn_boolean_t *may_save_plaintext, - const char *realmstring, - void *baton, - apr_pool_t *pool); + ::Java::GlobalObject m_prompter; + + virtual svn_error_t *dispatch_simple_prompt( + ::Java::Env env, + svn_auth_cred_simple_t **cred_p, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool); + + static svn_error_t *simple_prompt( + svn_auth_cred_simple_t **cred_p, + void *baton, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_username_prompt( + ::Java::Env env, + svn_auth_cred_username_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + static svn_error_t *username_prompt( + svn_auth_cred_username_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_ssl_server_trust_prompt( + ::Java::Env env, + svn_auth_cred_ssl_server_trust_t **cred_p, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool); + + static svn_error_t *ssl_server_trust_prompt( + svn_auth_cred_ssl_server_trust_t **cred_p, + void *baton, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_ssl_client_cert_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + static svn_error_t *ssl_client_cert_prompt( + svn_auth_cred_ssl_client_cert_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_ssl_client_cert_pw_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_pw_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + static svn_error_t *ssl_client_cert_pw_prompt( + svn_auth_cred_ssl_client_cert_pw_t **cred_p, + void *baton, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + +protected: + virtual svn_error_t *dispatch_plaintext_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool); + +public: + static svn_error_t *plaintext_prompt( + svn_boolean_t *may_save_plaintext, + const char *realmstring, + void *baton, + apr_pool_t *pool); + +protected: + virtual svn_error_t *dispatch_plaintext_passphrase_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool); + +public: static svn_error_t *plaintext_passphrase_prompt( - svn_boolean_t *may_save_plaintext, - const char *realmstring, - void *baton, - apr_pool_t *pool); + svn_boolean_t *may_save_plaintext, + const char *realmstring, + void *baton, + apr_pool_t *pool); +}; + + +/** + * This class requests username/password and informations about + * ssl-certificates from the user. + */ +class CompatPrompter : public Prompter +{ +public: + static Prompter::UniquePtr create(jobject jprompter); + virtual Prompter::UniquePtr clone() const; + virtual ~CompatPrompter(); + +protected: + explicit CompatPrompter(::Java::Env env, jobject jprompter); + + virtual svn_error_t *dispatch_simple_prompt( + ::Java::Env env, + svn_auth_cred_simple_t **cred_p, + const char *realm, + const char *username, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_username_prompt( + ::Java::Env env, + svn_auth_cred_username_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_ssl_server_trust_prompt( + ::Java::Env env, + svn_auth_cred_ssl_server_trust_t **cred_p, + const char *realm, + apr_uint32_t failures, + const svn_auth_ssl_server_cert_info_t *cert_info, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_ssl_client_cert_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_ssl_client_cert_pw_prompt( + ::Java::Env env, + svn_auth_cred_ssl_client_cert_pw_t **cred_p, + const char *realm, + svn_boolean_t may_save, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_plaintext_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool); + + virtual svn_error_t *dispatch_plaintext_passphrase_prompt( + ::Java::Env env, + svn_boolean_t *may_save_plaintext, + const char *realmstring, + apr_pool_t *pool); }; -#endif // PROMPTER_H +#endif // SVN_JAVAHL_PROMPTER_H diff --git a/subversion/bindings/javahl/native/RevpropTable.cpp b/subversion/bindings/javahl/native/PropertyTable.cpp index 5da74dd..0a44f0c 100644 --- a/subversion/bindings/javahl/native/RevpropTable.cpp +++ b/subversion/bindings/javahl/native/PropertyTable.cpp @@ -20,13 +20,14 @@ * ==================================================================== * @endcopyright * - * @file RevpropTable.cpp - * @brief Implementation of the class RevpropTable + * @file PropertyTable.cpp + * @brief Implementation of the class PropertyTable */ -#include "RevpropTable.h" +#include "PropertyTable.h" #include "JNIUtil.h" #include "JNIStringHolder.h" +#include "JNIByteArray.h" #include "Array.h" #include <apr_tables.h> #include <apr_strings.h> @@ -35,15 +36,15 @@ #include "svn_props.h" #include <iostream> -RevpropTable::~RevpropTable() +PropertyTable::~PropertyTable() { if (m_revpropTable != NULL) JNIUtil::getEnv()->DeleteLocalRef(m_revpropTable); } -const apr_hash_t *RevpropTable::hash(const SVN::Pool &pool) +apr_hash_t *PropertyTable::hash(const SVN::Pool &pool) { - if (m_revprops.size() == 0) + if (!m_revpropTable && !m_empty_if_null) return NULL; apr_hash_t *revprop_table = apr_hash_make(pool.getPool()); @@ -57,13 +58,14 @@ const apr_hash_t *RevpropTable::hash(const SVN::Pool &pool) const char *msg = apr_psprintf(pool.getPool(), "Invalid property name: '%s'", propname); - JNIUtil::throwNativeException(JAVA_PACKAGE "/ClientException", msg, + JNIUtil::throwNativeException(JAVAHL_CLASS("/ClientException"), msg, NULL, SVN_ERR_CLIENT_PROPERTY_NAME); return NULL; } - svn_string_t *propval = svn_string_create(it->second.c_str(), - pool.getPool()); + svn_string_t *propval = svn_string_ncreate(it->second.c_str(), + it->second.size(), + pool.getPool()); apr_hash_set(revprop_table, propname, APR_HASH_KEY_STRING, propval); } @@ -71,7 +73,10 @@ const apr_hash_t *RevpropTable::hash(const SVN::Pool &pool) return revprop_table; } -RevpropTable::RevpropTable(jobject jrevpropTable) +PropertyTable::PropertyTable(jobject jrevpropTable, bool bytearray_values, + bool empty_if_null) + : m_revpropTable(jrevpropTable), + m_empty_if_null(empty_if_null) { m_revpropTable = jrevpropTable; @@ -116,12 +121,27 @@ RevpropTable::RevpropTable(jobject jrevpropTable) if (JNIUtil::isExceptionThrown()) return; - JNIStringHolder propval((jstring)jpropval); - if (JNIUtil::isExceptionThrown()) - return; - - m_revprops[std::string(static_cast<const char *>(propname))] - = std::string(static_cast<const char *>(propval)); + std::string pv; + if (bytearray_values) + { + JNIByteArray propval((jbyteArray)jpropval); + if (JNIUtil::isExceptionThrown()) + return; + if (!propval.isNull()) + pv = std::string( + reinterpret_cast<const char*>(propval.getBytes()), + propval.getLength()); + } + else + { + JNIStringHolder propval((jstring)jpropval); + if (JNIUtil::isExceptionThrown()) + return; + if (NULL != static_cast<const char *>(propval)) + pv = static_cast<const char *>(propval); + } + + m_revprops[std::string(static_cast<const char *>(propname))] = pv; JNIUtil::getEnv()->DeleteLocalRef(jpropval); } diff --git a/subversion/bindings/javahl/native/RevpropTable.h b/subversion/bindings/javahl/native/PropertyTable.h index ee5f14f..8a9a335 100644 --- a/subversion/bindings/javahl/native/RevpropTable.h +++ b/subversion/bindings/javahl/native/PropertyTable.h @@ -20,8 +20,8 @@ * ==================================================================== * @endcopyright * - * @file RevpropTable.h - * @brief Interface of the class RevpropTable + * @file PropertyTable.h + * @brief Interface of the class PropertyTable */ #ifndef REVPROPTABLE_H @@ -36,15 +36,18 @@ struct apr_hash_t; #include <map> #include <string> -class RevpropTable +class PropertyTable { private: std::map<std::string, std::string> m_revprops; jobject m_revpropTable; + bool m_empty_if_null; public: - RevpropTable(jobject jrevpropTable); - ~RevpropTable(); - const apr_hash_t *hash(const SVN::Pool &pool); + PropertyTable(jobject jrevpropTable, + bool bytearray_values, + bool empty_if_null); + ~PropertyTable(); + apr_hash_t *hash(const SVN::Pool &pool); }; #endif // REVPROPTABLE_H diff --git a/subversion/bindings/javahl/native/ProplistCallback.cpp b/subversion/bindings/javahl/native/ProplistCallback.cpp index be46cf5..85d28c5 100644 --- a/subversion/bindings/javahl/native/ProplistCallback.cpp +++ b/subversion/bindings/javahl/native/ProplistCallback.cpp @@ -89,7 +89,7 @@ svn_error_t *ProplistCallback::singlePath(const char *path, static volatile jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ProplistCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ProplistCallback")); if (JNIUtil::isJavaExceptionThrown()) return SVN_NO_ERROR; @@ -104,7 +104,7 @@ svn_error_t *ProplistCallback::singlePath(const char *path, if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); - jobject jmap = CreateJ::PropertyMap(prop_hash); + jobject jmap = CreateJ::PropertyMap(prop_hash, pool); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); @@ -112,9 +112,7 @@ svn_error_t *ProplistCallback::singlePath(const char *path, env->CallVoidMethod(m_callback, mid, jpath, jmap); // We return whether an exception was thrown or not. - env->PopLocalFrame(NULL); - - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } @@ -144,7 +142,7 @@ svn_error_t *ProplistCallback::singlePath( static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/InheritedProplistCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/InheritedProplistCallback")); if (JNIUtil::isJavaExceptionThrown()) return SVN_NO_ERROR; @@ -159,7 +157,7 @@ svn_error_t *ProplistCallback::singlePath( if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); - jobject jmap = CreateJ::PropertyMap(prop_hash); + jobject jmap = CreateJ::PropertyMap(prop_hash, pool); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); 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); +} diff --git a/subversion/bindings/javahl/native/RemoteSession.h b/subversion/bindings/javahl/native/RemoteSession.h new file mode 100644 index 0000000..26ca402 --- /dev/null +++ b/subversion/bindings/javahl/native/RemoteSession.h @@ -0,0 +1,128 @@ +/** + * @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.h + * @brief Interface of the class RemoteSession + */ + +#ifndef JAVAHL_REMOTE_SESSION_H +#define JAVAHL_REMOTE_SESSION_H + +#include <jni.h> + +#include "svn_ra.h" + +#include "SVNBase.h" +#include "RemoteSessionContext.h" +#include "Prompter.h" + +class CommitEditor; + +/* + * This class wraps Ra based operations from svn_ra.h + */ +class RemoteSession : public SVNBase +{ + public: + static RemoteSession* getCppObject(jobject jthis); + static jobject open(jint jretryAttempts, + jstring jurl, jstring juuid, + jstring jconfigDirectory, + jstring jusername, jstring jpassword, + jobject jprompter, jobject jdeprecatedPrompter, + jobject jprogress, jobject jcfgcb, jobject jtunnelcb); + static jobject open(jint jretryAttempts, + const char* url, const char* uuid, + const char* configDirectory, + const char* username, const char* password, + Prompter::UniquePtr prompter, jobject jprogress, + jobject jcfgcb, jobject jtunnelcb); + ~RemoteSession(); + + void cancelOperation() const { m_context->cancelOperation(); } + + virtual void dispose(jobject jthis); + + void reparent(jstring jurl); + jstring getSessionUrl(); + jstring getSessionRelativePath(jstring jurl); + jstring getReposRelativePath(jstring jurl); + jstring getReposUUID(); + jstring getReposRootUrl(); + jlong getLatestRevision(); + jlong getRevisionByTimestamp(jlong jtimestamp); + void changeRevisionProperty(jlong jrevision, jstring jname, + jbyteArray jold_value, + jbyteArray jvalue); + jobject getRevisionProperties(jlong jrevision); + jbyteArray getRevisionProperty(jlong jrevision, jstring jname); + jlong getFile(jlong jrevision, jstring jpath, + jobject jcontents, jobject jproperties); + jlong getDirectory(jlong jrevision, jstring jpath, jint jdirent_fields, + jobject jdirents, jobject jproperties); + jobject getMergeinfo(jobject jpaths, jlong jrevision, jobject jinherit, + jboolean jinclude_descendants); + // TODO: update + // TODO: switch + void status(jobject jthis, jstring jstatus_target, + jlong jrevision, jobject jdepth, + jobject jstatus_editor, jobject jreporter); + // TODO: diff + void 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); + jobject checkPath(jstring jpath, jlong jrevision); + jobject stat(jstring jpath, jlong jrevision); + jobject getLocations(jstring jpath, jlong jpeg_revision, + jobject jlocation_revisions); + void getLocationSegments(jstring jpath, jlong jpeg_revision, + jlong jstart_revision, jlong jend_revision, + jobject jcallback); + void getFileRevisions(jstring jpath, + jlong jstart_revision, jlong jend_revision, + jboolean jinclude_merged_revisions, + jobject jcallback); + // TODO: getFileRevisions + // TODO: lock + // TODO: unlock + // TODO: getLock + jobject getLocks(jstring jpath, jobject jdepth); + // TODO: replayRange + // TODO: replay + // TODO: getDeletedRevision + // TODO: getInheritedProperties + jboolean hasCapability(jstring capability); + + private: + friend class CommitEditor; + 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); + + svn_ra_session_t* m_session; + RemoteSessionContext* m_context; +}; + +#endif // JAVAHL_REMOTE_SESSION_H diff --git a/subversion/bindings/javahl/native/RemoteSessionContext.cpp b/subversion/bindings/javahl/native/RemoteSessionContext.cpp new file mode 100644 index 0000000..e4b86e1 --- /dev/null +++ b/subversion/bindings/javahl/native/RemoteSessionContext.cpp @@ -0,0 +1,125 @@ +/** + * @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 RemoteSessionContext.cpp + * @brief Implementation of the class RemoteSessionContext + */ + + +#include "RemoteSessionContext.h" +#include "JNIUtil.h" +#include "Prompter.h" + + +RemoteSessionContext::RemoteSessionContext( + SVN::Pool &pool, const char* configDirectory, + const char* usernameStr, const char* passwordStr, + Prompter::UniquePtr prompter, jobject jcfgcb, jobject jtunnelcb) + : OperationContext(pool), m_raCallbacks(NULL) +{ + setConfigDirectory(configDirectory); + if (usernameStr != NULL) + username(usernameStr); + + if (passwordStr != NULL) + password(passwordStr); + + setPrompt(prompter); + setConfigEventHandler(jcfgcb); + setTunnelCallback(jtunnelcb); + + /* + * Setup callbacks + */ + SVN_JNI_ERR(svn_ra_create_callbacks(&m_raCallbacks, m_pool->getPool()), ); + + m_raCallbacks->auth_baton = getAuthBaton(pool); + m_raCallbacks->cancel_func = checkCancel; + m_raCallbacks->get_client_string = clientName; + m_raCallbacks->progress_baton = NULL; + m_raCallbacks->progress_func = progress; + + /* + * JNI RA layer does not work with WC so all WC callbacks are set to NULL + */ + m_raCallbacks->get_wc_prop = NULL; + m_raCallbacks->invalidate_wc_props = NULL; + m_raCallbacks->push_wc_prop = NULL; + m_raCallbacks->set_wc_prop = NULL; + + /* + * 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->tunnel_baton = m_jtunnelcb; + } +} + +RemoteSessionContext::~RemoteSessionContext() +{ +} + +void RemoteSessionContext::activate(jobject jremoteSession, jobject jprogress) +{ + /* + * Attach session context java object + */ + static jfieldID ctxFieldID = 0; + attachJavaObject(jremoteSession, + JAVAHL_ARG("/remote/RemoteSession$RemoteSessionContext;"), + "sessionContext", &ctxFieldID); + + /* + * Set the progress callback + */ + JNIEnv *env = JNIUtil::getEnv(); + + jclass clazz = env->GetObjectClass(m_jctx); + if (JNIUtil::isJavaExceptionThrown()) + return; + + jmethodID mid = env->GetMethodID( + clazz, "setProgressCallback", + "(" JAVAHL_ARG("/callback/ProgressCallback;") ")V"); + if (JNIUtil::isJavaExceptionThrown() || mid == 0) + return; + + env->CallVoidMethod(m_jctx, mid, jprogress); + m_raCallbacks->progress_baton = m_jctx; +} + +void * +RemoteSessionContext::getCallbackBaton() +{ + return this; +} + +svn_ra_callbacks2_t * +RemoteSessionContext::getCallbacks() +{ + return m_raCallbacks; +} diff --git a/subversion/bindings/javahl/native/RemoteSessionContext.h b/subversion/bindings/javahl/native/RemoteSessionContext.h new file mode 100644 index 0000000..8c569a1 --- /dev/null +++ b/subversion/bindings/javahl/native/RemoteSessionContext.h @@ -0,0 +1,51 @@ +/** + * @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 RemoteSessionContext.h + * @brief Interface of the class RemoteSessionContext + */ + +#ifndef JAVAHL_REMOTE_SESSION_CONTEXT_H +#define JAVAHL_REMOTE_SESSION_CONTEXT_H + +#include "svn_ra.h" + +#include "OperationContext.h" + +class RemoteSessionContext : public OperationContext +{ + public: + RemoteSessionContext(SVN::Pool &pool, + const char* jconfigDirectory, + const char* jusername, const char* jpassword, + std::auto_ptr<Prompter> prompter, + jobject jcfgcb, jobject jtunnelcb); + virtual ~RemoteSessionContext(); + void activate(jobject jremoteSession, jobject jprogress); + void * getCallbackBaton(); + svn_ra_callbacks2_t* getCallbacks(); + + private: + svn_ra_callbacks2_t* m_raCallbacks; +}; + +#endif /* JAVAHL_REMOTE_SESSION_CONTEXT_H */ diff --git a/subversion/bindings/javahl/native/ReposFreezeAction.cpp b/subversion/bindings/javahl/native/ReposFreezeAction.cpp index 8c1cb7e..78f307f 100644 --- a/subversion/bindings/javahl/native/ReposFreezeAction.cpp +++ b/subversion/bindings/javahl/native/ReposFreezeAction.cpp @@ -39,7 +39,7 @@ svn_error_t* ReposFreezeAction::invoke() static volatile jmethodID mid = 0; if (!mid) { - jclass cls = env->FindClass(JAVA_PACKAGE"/callback/ReposFreezeAction"); + jclass cls = env->FindClass(JAVAHL_CLASS("/callback/ReposFreezeAction")); if (!JNIUtil::isJavaExceptionThrown()) mid = env->GetMethodID(cls, "invoke", "()V"); } diff --git a/subversion/bindings/javahl/native/ReposNotifyCallback.cpp b/subversion/bindings/javahl/native/ReposNotifyCallback.cpp index a716653..1237d13 100644 --- a/subversion/bindings/javahl/native/ReposNotifyCallback.cpp +++ b/subversion/bindings/javahl/native/ReposNotifyCallback.cpp @@ -69,12 +69,12 @@ ReposNotifyCallback::onNotify(const svn_repos_notify_t *wcNotify, static jmethodID mid = 0; if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/ReposNotifyCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ReposNotifyCallback")); if (JNIUtil::isJavaExceptionThrown()) return; mid = env->GetMethodID(clazz, "onNotify", - "(L"JAVA_PACKAGE"/ReposNotifyInformation;)V"); + "(" JAVAHL_ARG("/ReposNotifyInformation;") ")V"); if (JNIUtil::isJavaExceptionThrown() || mid == 0) return; diff --git a/subversion/bindings/javahl/native/ReposVerifyCallback.cpp b/subversion/bindings/javahl/native/ReposVerifyCallback.cpp new file mode 100644 index 0000000..1fb6ecd --- /dev/null +++ b/subversion/bindings/javahl/native/ReposVerifyCallback.cpp @@ -0,0 +1,88 @@ +/** + * @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 ReposVerifyCallback.cpp + * @brief Implementation of the class ReposVerifyCallback + */ + +#include "ReposVerifyCallback.h" +#include "JNIUtil.h" + +ReposVerifyCallback::ReposVerifyCallback(jobject jverify_cb) + : m_jverify_cb(jverify_cb) +{} + +ReposVerifyCallback::~ReposVerifyCallback() +{ + // Don't need to destroy the reference, since it was given us by Java +} + +svn_error_t * +ReposVerifyCallback::callback(void *baton, + svn_revnum_t revision, + svn_error_t *verify_err, + apr_pool_t *scratch_pool) +{ + if (!baton) + return SVN_NO_ERROR; + + static_cast<ReposVerifyCallback*>(baton) + ->onVerifyError(revision, verify_err, scratch_pool); + if (JNIUtil::isJavaExceptionThrown()) + return JNIUtil::wrapJavaException(); + return SVN_NO_ERROR; +} + +void +ReposVerifyCallback::onVerifyError(svn_revnum_t revision, + svn_error_t *verify_err, + apr_pool_t *scratch_pool) +{ + JNIEnv *env = JNIUtil::getEnv(); + + // Java method id will not change during the time this library is + // loaded, so it can be cached. + static jmethodID mid = 0; + if (mid == 0) + { + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/ReposVerifyCallback")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + mid = env->GetMethodID(clazz, "onVerifyError", + "(J" JAVAHL_ARG("/ClientException;") ")V"); + if (JNIUtil::isJavaExceptionThrown() || mid == 0) + return; + + env->DeleteLocalRef(clazz); + } + + jthrowable jverify_err = NULL; + if (verify_err) + jverify_err = JNIUtil::createClientException(svn_error_dup(verify_err), NULL); + if (JNIUtil::isJavaExceptionThrown()) + return; + + env->CallVoidMethod(m_jverify_cb, mid, jlong(revision), jverify_err); + if (verify_err) + env->DeleteLocalRef(jverify_err); +} diff --git a/subversion/bindings/javahl/native/ReposVerifyCallback.h b/subversion/bindings/javahl/native/ReposVerifyCallback.h new file mode 100644 index 0000000..a89d204 --- /dev/null +++ b/subversion/bindings/javahl/native/ReposVerifyCallback.h @@ -0,0 +1,75 @@ +/** + * @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 ReposVerifyCallback.h + * @brief Interface of the class ReposVerifyCallback + */ + +#ifndef SVN_JAVAHL_REPOS_VERIFY_CALLBACK_H +#define SVN_JAVAHL_REPOS_VERIFY_CALLBACK_H + +#include <jni.h> +#include "svn_repos.h" + +/** + * This class passes notification from subversion to a Java object + * (1.2 version). + */ +class ReposVerifyCallback +{ + private: + /** + * The local reference to the Java object. + */ + jobject m_jverify_cb; + + public: + /** + * Create a new object and store the Java object. + * @param verify_cb global reference to the Java object + */ + ReposVerifyCallback(jobject jverify_cb); + + ~ReposVerifyCallback(); + + /** + * Implementation of the svn_repos_verify_callback_t API. + * + * @param baton Notification instance is passed using this parameter + * @param revision The revision that the error was emitted for + * @param verify_err The emitted error + * @param scratch_pool An APR pool from which to allocate memory. + */ + static svn_error_t * callback(void *baton, + svn_revnum_t revision, + svn_error_t *verify_err, + apr_pool_t *scratch_pool); + + /** + * Handler for Subversion notifications. + */ + void onVerifyError(svn_revnum_t revision, + svn_error_t *verify_err, + apr_pool_t *scratch_pool); +}; + +#endif // SVN_JAVAHL_REPOS_VERIFY_CALLBACK_H diff --git a/subversion/bindings/javahl/native/Revision.cpp b/subversion/bindings/javahl/native/Revision.cpp index 7ba2094..d9e52f8 100644 --- a/subversion/bindings/javahl/native/Revision.cpp +++ b/subversion/bindings/javahl/native/Revision.cpp @@ -57,12 +57,12 @@ Revision::Revision(jobject jthis, bool headIfUnspecified, static jfieldID fid = 0; if (fid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Revision"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Revision")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NOTHING(); fid = env->GetFieldID(clazz, "revKind", - "L"JAVA_PACKAGE"/types/Revision$Kind;"); + JAVAHL_ARG("/types/Revision$Kind;")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NOTHING(); } @@ -81,7 +81,7 @@ Revision::Revision(jobject jthis, bool headIfUnspecified, if (fidNum == 0) { jclass clazz = - env->FindClass(JAVA_PACKAGE"/types/Revision$Number"); + env->FindClass(JAVAHL_CLASS("/types/Revision$Number")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NOTHING(); @@ -99,7 +99,7 @@ Revision::Revision(jobject jthis, bool headIfUnspecified, if (fidDate == 0) { jclass clazz = - env->FindClass(JAVA_PACKAGE"/types/Revision$DateSpec"); + env->FindClass(JAVAHL_CLASS("/types/Revision$DateSpec")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN_NOTHING(); @@ -158,7 +158,7 @@ jobject Revision::makeJRevision(svn_revnum_t rev) { JNIEnv *env = JNIUtil::getEnv(); - jclass clazz = env->FindClass(JAVA_PACKAGE "/types/Revision"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Revision")); if (JNIUtil::isJavaExceptionThrown()) return NULL; @@ -166,7 +166,7 @@ Revision::makeJRevision(svn_revnum_t rev) if (getInstance == 0) { getInstance = env->GetStaticMethodID(clazz, "getInstance", - "(J)L" JAVA_PACKAGE "/types/Revision;"); + "(J)" JAVAHL_ARG("/types/Revision;")); if (JNIUtil::isExceptionThrown()) return NULL; } diff --git a/subversion/bindings/javahl/native/Revision.h b/subversion/bindings/javahl/native/Revision.h index bef87a9..d98a868 100644 --- a/subversion/bindings/javahl/native/Revision.h +++ b/subversion/bindings/javahl/native/Revision.h @@ -50,6 +50,7 @@ class Revision * Make a Revision Java object. */ static jobject makeJRevision(svn_revnum_t rev); + static jobject makeJRevision(const svn_opt_revision_t& rev); }; #endif // REVISION_H diff --git a/subversion/bindings/javahl/native/RevisionRange.cpp b/subversion/bindings/javahl/native/RevisionRange.cpp index 677c7d5..d1c8c64 100644 --- a/subversion/bindings/javahl/native/RevisionRange.cpp +++ b/subversion/bindings/javahl/native/RevisionRange.cpp @@ -44,60 +44,117 @@ RevisionRange::~RevisionRange() // explicitly destroyed. } -const svn_opt_revision_range_t *RevisionRange::toRange(SVN::Pool &pool) const +namespace { +void get_range_info(jobject jrange, + svn_opt_revision_t* range_start, + svn_opt_revision_t* range_end, + svn_boolean_t* range_inheritable) { JNIEnv *env = JNIUtil::getEnv(); - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/RevisionRange"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/RevisionRange")); if (JNIUtil::isExceptionThrown()) - return NULL; + return; - static jmethodID fmid = 0; - if (fmid == 0) + if (range_start) { - fmid = env->GetMethodID(clazz, "getFromRevision", - "()L"JAVA_PACKAGE"/types/Revision;"); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; + + static jmethodID fmid = 0; + if (fmid == 0) + { + fmid = env->GetMethodID(clazz, "getFromRevision", + "()" JAVAHL_ARG("/types/Revision;")); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jobject jstartRevision = env->CallObjectMethod(jrange, fmid); + if (JNIUtil::isExceptionThrown()) + return; + + Revision startRevision(jstartRevision); + if (JNIUtil::isExceptionThrown()) + return; + + *range_start = *startRevision.revision(); + if (JNIUtil::isExceptionThrown()) + return; } - static jmethodID tmid = 0; - if (tmid == 0) + if (range_end) { - tmid = env->GetMethodID(clazz, "getToRevision", - "()L"JAVA_PACKAGE"/types/Revision;"); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; + static jmethodID tmid = 0; + if (tmid == 0) + { + tmid = env->GetMethodID(clazz, "getToRevision", + "()" JAVAHL_ARG("/types/Revision;")); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jobject jendRevision = env->CallObjectMethod(jrange, tmid); + if (JNIUtil::isExceptionThrown()) + return; + + Revision endRevision(jendRevision); + if (JNIUtil::isExceptionThrown()) + return; + + *range_end = *endRevision.revision(); + if (JNIUtil::isExceptionThrown()) + return; } - jobject jstartRevision = env->CallObjectMethod(m_range, fmid); - if (JNIUtil::isExceptionThrown()) - return NULL; + if (range_inheritable) + { + static jmethodID imid = 0; + if (imid == 0 && range_inheritable) + { + imid = env->GetMethodID(clazz, "isInheritable", "()Z"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jboolean inheritable = env->CallBooleanMethod(jrange, imid); + if (JNIUtil::isExceptionThrown()) + return; + *range_inheritable = inheritable; + } +} +} // anonymous namespace - Revision startRevision(jstartRevision); +svn_merge_range_t* RevisionRange::toMergeRange(SVN::Pool &pool) const +{ + svn_opt_revision_t range_start, range_end; + svn_boolean_t range_inheritable; + get_range_info(m_range, &range_start, &range_end, &range_inheritable); if (JNIUtil::isExceptionThrown()) return NULL; - jobject jendRevision = env->CallObjectMethod(m_range, tmid); - if (JNIUtil::isExceptionThrown()) - return NULL; + if (range_start.kind != svn_opt_revision_number + || range_end.kind != svn_opt_revision_number) + JNIUtil::raiseThrowable("java.lang.InvalidStateException", + "Revsision ranges must contain revision numbers"); - Revision endRevision(jendRevision); - if (JNIUtil::isExceptionThrown()) - return NULL; + svn_merge_range_t* range = + static_cast<svn_merge_range_t*> + (apr_palloc(pool.getPool(), sizeof(*range))); + + range->start = range_start.value.number; + range->end = range_end.value.number; + range->inheritable = range_inheritable; + return range; +} +svn_opt_revision_range_t *RevisionRange::toRange(SVN::Pool &pool) const +{ svn_opt_revision_range_t *range = - reinterpret_cast<svn_opt_revision_range_t *> + static_cast<svn_opt_revision_range_t *> (apr_palloc(pool.getPool(), sizeof(*range))); - range->start = *startRevision.revision(); - if (JNIUtil::isExceptionThrown()) - return NULL; - - range->end = *endRevision.revision(); + get_range_info(m_range, &range->start, &range->end, NULL); if (JNIUtil::isExceptionThrown()) - return NULL; - + range = NULL; return range; } @@ -106,19 +163,20 @@ RevisionRange::makeJRevisionRange(svn_merge_range_t *range) { JNIEnv *env = JNIUtil::getEnv(); - jclass rangeClazz = env->FindClass(JAVA_PACKAGE "/types/RevisionRange"); + jclass rangeClazz = env->FindClass(JAVAHL_CLASS("/types/RevisionRange")); if (JNIUtil::isJavaExceptionThrown()) return NULL; static jmethodID rangeCtor = 0; if (rangeCtor == 0) { - rangeCtor = env->GetMethodID(rangeClazz, "<init>", "(JJ)V"); + rangeCtor = env->GetMethodID(rangeClazz, "<init>", "(JJZ)V"); if (JNIUtil::isJavaExceptionThrown()) return NULL; } jobject jrange = env->NewObject(rangeClazz, rangeCtor, - (jlong) range->start, - (jlong) range->end); + jlong(range->start), + jlong(range->end), + jboolean(range->inheritable)); if (JNIUtil::isJavaExceptionThrown()) return NULL; diff --git a/subversion/bindings/javahl/native/RevisionRange.h b/subversion/bindings/javahl/native/RevisionRange.h index f932c1b..9934894 100644 --- a/subversion/bindings/javahl/native/RevisionRange.h +++ b/subversion/bindings/javahl/native/RevisionRange.h @@ -29,6 +29,7 @@ #include <jni.h> #include "svn_types.h" +#include "svn_opt.h" #include "Pool.h" @@ -53,7 +54,12 @@ class RevisionRange /** * Return an svn_opt_revision_range_t. */ - const svn_opt_revision_range_t *toRange(SVN::Pool &pool) const; + svn_opt_revision_range_t *toRange(SVN::Pool &pool) const; + + /** + * Return an svn_merge_range_t. + */ + svn_merge_range_t* toMergeRange(SVN::Pool &pool) const; /** * Make a (single) RevisionRange Java object. diff --git a/subversion/bindings/javahl/native/RevisionRangeList.cpp b/subversion/bindings/javahl/native/RevisionRangeList.cpp new file mode 100644 index 0000000..3759e5f --- /dev/null +++ b/subversion/bindings/javahl/native/RevisionRangeList.cpp @@ -0,0 +1,128 @@ +/** + * @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 RevisionRangeList.cpp + * @brief Implementat of the class RevisionRangeList + */ + +#include "JNIUtil.h" +#include "Iterator.h" +#include "RevisionRange.h" +#include "RevisionRangeList.h" + +RevisionRangeList::RevisionRangeList(jobject jrangelist, SVN::Pool &pool) + : m_rangelist(NULL) +{ + if (!jrangelist) + return; + + Iterator iter(jrangelist); + if (JNIUtil::isJavaExceptionThrown()) + return; + + m_rangelist = apr_array_make(pool.getPool(), 0, sizeof(svn_merge_range_t*)); + while (iter.hasNext()) + { + svn_merge_range_t* range = RevisionRange(iter.next()).toMergeRange(pool); + if (JNIUtil::isExceptionThrown()) + return; + APR_ARRAY_PUSH(m_rangelist, svn_merge_range_t*) = range; + } +} + +RevisionRangeList RevisionRangeList::create(jobject jthis, SVN::Pool &pool) +{ + jobject jrangelist = NULL; + + if (jthis) + { + JNIEnv *env = JNIUtil::getEnv(); + + jmethodID mid = 0; + if (mid == 0) + { + jclass cls = env->FindClass(JAVAHL_CLASS("/types/RevisionRangeList")); + if (JNIUtil::isJavaExceptionThrown()) + return RevisionRangeList(NULL, pool); + + mid = env->GetMethodID(cls, "getRanges", "()Ljava/util/List;"); + if (JNIUtil::isJavaExceptionThrown()) + return RevisionRangeList(NULL, pool); + } + + jrangelist = env->CallObjectMethod(jthis, mid); + if (JNIUtil::isJavaExceptionThrown()) + return RevisionRangeList(NULL, pool); + } + return RevisionRangeList(jrangelist, pool); +} + +jobject RevisionRangeList::toList() const +{ + JNIEnv *env = JNIUtil::getEnv(); + + // Create a local frame for our references + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + jclass clazz = env->FindClass("java/util/ArrayList"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + static jmethodID init_mid = 0; + if (init_mid == 0) + { + init_mid = env->GetMethodID(clazz, "<init>", "()V"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + } + + static jmethodID add_mid = 0; + if (add_mid == 0) + { + add_mid = env->GetMethodID(clazz, "add", "(Ljava/lang/Object;)Z"); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + } + + jobject jranges = env->NewObject(clazz, init_mid); + + for (int i = 0; i < m_rangelist->nelts; ++i) + { + // Convert svn_merge_range_t *'s to Java RevisionRange objects. + svn_merge_range_t *range = + APR_ARRAY_IDX(m_rangelist, i, svn_merge_range_t *); + + jobject jrange = RevisionRange::makeJRevisionRange(range); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + env->CallBooleanMethod(jranges, add_mid, jrange); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NULL; + + env->DeleteLocalRef(jrange); + } + + return env->PopLocalFrame(jranges); +} diff --git a/subversion/bindings/javahl/native/JNIThreadData.h b/subversion/bindings/javahl/native/RevisionRangeList.h index 55d4141..116ff0d 100644 --- a/subversion/bindings/javahl/native/JNIThreadData.h +++ b/subversion/bindings/javahl/native/RevisionRangeList.h @@ -20,58 +20,53 @@ * ==================================================================== * @endcopyright * - * @file JNIThreadData.h - * @brief Interface of the class JNIThreadData + * @file RevisionRangeList.h + * @brief Interface of the class RevisionRangeList */ -#ifndef JNITHREADDATA_H -#define JNITHREADDATA_H +#ifndef REVISION_RANGE_LIST_H +#define REVISION_RANGE_LIST_H #include <jni.h> -#include "JNIUtil.h" +#include "svn_mergeinfo.h" -struct apr_threadkey_t; +#include "Pool.h" /** - * This class implements thread local storage for JNIUtil. + * A wrapper for svn_rangelist_t */ -class JNIThreadData +class RevisionRangeList { public: - static void del(void *); - static JNIThreadData *getThreadData(); - static bool initThreadData(); - static void pushNewThreadData(); - static void popThreadData(); - JNIThreadData(); - ~JNIThreadData(); - /** - * The current JNI environment. + * Create a RevisionRangeList object from a Java list of revision ranges. */ - JNIEnv *m_env; + RevisionRangeList(jobject jrangelist, SVN::Pool &pool); /** - * Flag that a Java execption has been detected. + * Create a RevisionRangeList object from a Java RevisionRangeList. */ - bool m_exceptionThrown; + static RevisionRangeList create(jobject jthis, SVN::Pool &pool); /** - * A buffer used for formating messages. + * Wrap an svn_rangelist_t. */ - char m_formatBuffer[JNIUtil::formatBufferSize]; + explicit RevisionRangeList(svn_rangelist_t* ranges) + : m_rangelist(ranges) + {} - private: /** - * Pointer to previous thread information to enable reentrent - * calls. + * Return an svn_rangelist_t. */ - JNIThreadData *m_previous; + const svn_rangelist_t* get() const { return m_rangelist; } /** - * The key to address this thread local storage. + * Make a Java list of reivison ranges. */ - static apr_threadkey_t *g_key; + jobject toList() const; + + private: + svn_rangelist_t* m_rangelist; }; -#endif // JNITHREADDATA_H +#endif // REVISION_RANGE_LIST_H diff --git a/subversion/bindings/javahl/native/SVNBase.cpp b/subversion/bindings/javahl/native/SVNBase.cpp index bbabc11..5e20f93 100644 --- a/subversion/bindings/javahl/native/SVNBase.cpp +++ b/subversion/bindings/javahl/native/SVNBase.cpp @@ -97,3 +97,29 @@ inline void SVNBase::findCppAddrFieldID(jfieldID *fid, const char *className, } } } + +jobject SVNBase::createCppBoundObject(const char *clazzName) +{ + JNIEnv *env = JNIUtil::getEnv(); + + // Create java session object + jclass clazz = env->FindClass(clazzName); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + static jmethodID ctor = 0; + if (ctor == 0) + { + ctor = env->GetMethodID(clazz, "<init>", "(J)V"); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + } + + jlong cppAddr = this->getCppAddr(); + + jobject jself = env->NewObject(clazz, ctor, cppAddr); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return jself; +} diff --git a/subversion/bindings/javahl/native/SVNBase.h b/subversion/bindings/javahl/native/SVNBase.h index f47e3c0..9b3d41a 100644 --- a/subversion/bindings/javahl/native/SVNBase.h +++ b/subversion/bindings/javahl/native/SVNBase.h @@ -82,6 +82,11 @@ class SVNBase */ void dispose(jobject jthis, jfieldID *fid, const char *className); + /** + * Instantiates java object attached to this base object + */ + jobject createCppBoundObject(const char *clazzName); + private: /** * If the value pointed to by @a fid is zero, find the @c jfieldID diff --git a/subversion/bindings/javahl/native/SVNClient.cpp b/subversion/bindings/javahl/native/SVNClient.cpp index 4945cc7..5bae852 100644 --- a/subversion/bindings/javahl/native/SVNClient.cpp +++ b/subversion/bindings/javahl/native/SVNClient.cpp @@ -24,12 +24,18 @@ * @brief: Implementation of the SVNClient class */ +#include <vector> +#include <iostream> +#include <sstream> +#include <string> + #include "SVNClient.h" #include "JNIUtil.h" #include "CopySources.h" #include "DiffSummaryReceiver.h" #include "ClientContext.h" #include "Prompter.h" +#include "RemoteSession.h" #include "Pool.h" #include "Targets.h" #include "Revision.h" @@ -50,9 +56,11 @@ #include "CommitMessage.h" #include "EnumMapper.h" #include "StringArray.h" -#include "RevpropTable.h" +#include "PropertyTable.h" #include "DiffOptions.h" #include "CreateJ.h" +#include "JNIStringHolder.h" + #include "svn_auth.h" #include "svn_dso.h" #include "svn_types.h" @@ -62,14 +70,17 @@ #include "svn_diff.h" #include "svn_config.h" #include "svn_io.h" +#include "svn_hash.h" #include "svn_dirent_uri.h" #include "svn_path.h" #include "svn_utf.h" +#include "private/svn_subr_private.h" #include "svn_private_config.h" -#include "JNIStringHolder.h" -#include <vector> -#include <iostream> -#include <sstream> + +#include "ExternalItem.hpp" +#include "jniwrapper/jni_list.hpp" +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_string_map.hpp" SVNClient::SVNClient(jobject jthis_in) @@ -85,21 +96,21 @@ SVNClient *SVNClient::getCppObject(jobject jthis) { static jfieldID fid = 0; jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid, - JAVA_PACKAGE"/SVNClient"); + JAVAHL_CLASS("/SVNClient")); return (cppAddr == 0 ? NULL : reinterpret_cast<SVNClient *>(cppAddr)); } void SVNClient::dispose(jobject jthis) { static jfieldID fid = 0; - SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNClient"); + SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/SVNClient")); } jobject SVNClient::getVersionExtended(bool verbose) { JNIEnv *const env = JNIUtil::getEnv(); - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/VersionExtended"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/VersionExtended")); if (JNIUtil::isJavaExceptionThrown()) return NULL; @@ -184,8 +195,9 @@ void SVNClient::list(const char *url, Revision &revision, void SVNClient::status(const char *path, svn_depth_t depth, - bool onServer, bool getAll, bool noIgnore, - bool ignoreExternals, StringArray &changelists, + bool onServer, bool onDisk, bool getAll, + bool noIgnore, bool ignoreExternals, + bool depthAsSticky, StringArray &changelists, StatusCallback *callback) { SVN::Pool subPool(pool); @@ -204,11 +216,10 @@ SVNClient::status(const char *path, svn_depth_t depth, rev.kind = svn_opt_revision_unspecified; - SVN_JNI_ERR(svn_client_status5(&youngest, ctx, checkedPath.c_str(), - &rev, - depth, - getAll, onServer, noIgnore, ignoreExternals, - FALSE, + SVN_JNI_ERR(svn_client_status6(&youngest, ctx, checkedPath.c_str(), + &rev, depth, + getAll, onServer, onDisk, + noIgnore, ignoreExternals, depthAsSticky, changelists.array(subPool), StatusCallback::callback, callback, subPool.getPool()), ); @@ -227,23 +238,23 @@ rev_range_vector_to_apr_array(std::vector<RevisionRange> &revRanges, std::vector<RevisionRange>::const_iterator it; for (it = revRanges.begin(); it != revRanges.end(); ++it) { - if (it->toRange(subPool)->start.kind - == svn_opt_revision_unspecified - && it->toRange(subPool)->end.kind - == svn_opt_revision_unspecified) + const svn_opt_revision_range_t *range = it->toRange(subPool); + + if (range->start.kind == svn_opt_revision_unspecified + && range->end.kind == svn_opt_revision_unspecified) { - svn_opt_revision_range_t *range = + svn_opt_revision_range_t *full = reinterpret_cast<svn_opt_revision_range_t *> (apr_pcalloc(subPool.getPool(), sizeof(*range))); - range->start.kind = svn_opt_revision_number; - range->start.value.number = 1; - range->end.kind = svn_opt_revision_head; - APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = range; + full->start.kind = svn_opt_revision_number; + full->start.value.number = 1; + full->end.kind = svn_opt_revision_head; + full->end.value.number = 0; + APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = full; } else { - APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = - it->toRange(subPool); + APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = range; } if (JNIUtil::isExceptionThrown()) return NULL; @@ -255,7 +266,7 @@ void SVNClient::logMessages(const char *path, Revision &pegRevision, std::vector<RevisionRange> &logRanges, bool stopOnCopy, bool discoverPaths, bool includeMergedRevisions, StringArray &revProps, - long limit, LogMessageCallback *callback) + int limit, LogMessageCallback *callback) { SVN::Pool subPool(pool); @@ -317,7 +328,7 @@ jlong SVNClient::checkout(const char *moduleName, const char *destPath, } void SVNClient::remove(Targets &targets, CommitMessage *message, bool force, - bool keep_local, RevpropTable &revprops, + bool keep_local, PropertyTable &revprops, CommitCallback *callback) { SVN::Pool subPool(pool); @@ -334,23 +345,24 @@ void SVNClient::remove(Targets &targets, CommitMessage *message, bool force, ctx, subPool.getPool()), ); } -void SVNClient::revert(const char *path, svn_depth_t depth, - StringArray &changelists) +void SVNClient::revert(StringArray &paths, svn_depth_t depth, + StringArray &changelists, + bool clear_changelists, + bool metadata_only) { SVN::Pool subPool(pool); - SVN_JNI_NULL_PTR_EX(path, "path", ); - svn_client_ctx_t *ctx = context.getContext(NULL, subPool); if (ctx == NULL) return; - Targets target(path, subPool); - const apr_array_header_t *targets = target.array(subPool); - SVN_JNI_ERR(target.error_occurred(), ); - SVN_JNI_ERR(svn_client_revert2(targets, depth, - changelists.array(subPool), ctx, - subPool.getPool()), ); + Targets targets(paths, subPool); + SVN_JNI_ERR(targets.error_occurred(), ); + SVN_JNI_ERR(svn_client_revert3(targets.array(subPool), depth, + changelists.array(subPool), + clear_changelists, + metadata_only, + ctx, subPool.getPool()), ); } void SVNClient::add(const char *path, @@ -417,7 +429,7 @@ jlongArray SVNClient::update(Targets &targets, Revision &revision, void SVNClient::commit(Targets &targets, CommitMessage *message, svn_depth_t depth, bool noUnlock, bool keepChangelist, - StringArray &changelists, RevpropTable &revprops, + StringArray &changelists, PropertyTable &revprops, CommitCallback *callback) { SVN::Pool subPool(pool); @@ -439,20 +451,82 @@ void SVNClient::commit(Targets &targets, CommitMessage *message, ); } + +namespace { +typedef Java::ImmutableList<JavaHL::ExternalItem> PinList; +typedef Java::ImmutableMap<PinList> PinMap; + +struct PinListFunctor +{ + explicit PinListFunctor(const Java::Env& env, SVN::Pool& pool, int refs_len) + : m_pool(pool), + m_refs(apr_array_make(pool.getPool(), refs_len, + sizeof(svn_wc_external_item2_t*))) + {} + + void operator()(const JavaHL::ExternalItem& item) + { + APR_ARRAY_PUSH(m_refs, svn_wc_external_item2_t*) = + item.get_external_item(m_pool); + } + + SVN::Pool& m_pool; + apr_array_header_t *m_refs; +}; + +struct PinMapFunctor +{ + explicit PinMapFunctor(const Java::Env& env, SVN::Pool& pool) + : m_env(env), + m_pool(pool), + m_pin_set(svn_hash__make(pool.getPool())) + {} + + void operator()(const std::string& path, const PinList& refs) + { + PinListFunctor lf(m_env, m_pool, refs.length()); + refs.for_each(lf); + const char* key = static_cast<const char*>( + apr_pmemdup(m_pool.getPool(), path.c_str(), path.size() + 1)); + svn_hash_sets(m_pin_set, key, lf.m_refs); + } + + const Java::Env& m_env; + SVN::Pool& m_pool; + apr_hash_t *m_pin_set; +}; + +apr_hash_t *get_externals_to_pin(jobject jexternalsToPin, SVN::Pool& pool) +{ + if (!jexternalsToPin) + return NULL; + + const Java::Env env; + JNIEnv *jenv = env.get(); + + try + { + PinMap pin_map(env, jexternalsToPin); + PinMapFunctor mf(env, pool); + pin_map.for_each(mf); + return mf.m_pin_set; + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} +} // anonymous namespace + void SVNClient::copy(CopySources ©Sources, const char *destPath, CommitMessage *message, bool copyAsChild, bool makeParents, bool ignoreExternals, - RevpropTable &revprops, CommitCallback *callback) + bool metadataOnly, + bool pinExternals, jobject jexternalsToPin, + PropertyTable &revprops, CommitCallback *callback) { SVN::Pool subPool(pool); apr_array_header_t *srcs = copySources.array(subPool); - if (srcs == NULL) - { - JNIUtil::throwNativeException(JAVA_PACKAGE "/ClientException", - "Invalid copy sources"); - return; - } + SVN_JNI_NULL_PTR_EX(srcs, "sources", ); SVN_JNI_NULL_PTR_EX(destPath, "destPath", ); Path destinationPath(destPath, subPool); SVN_JNI_ERR(destinationPath.error_occurred(), ); @@ -461,17 +535,21 @@ void SVNClient::copy(CopySources ©Sources, const char *destPath, if (ctx == NULL) return; - SVN_JNI_ERR(svn_client_copy6(srcs, destinationPath.c_str(), - copyAsChild, makeParents, ignoreExternals, - revprops.hash(subPool), - CommitCallback::callback, callback, + apr_hash_t *pin_set = get_externals_to_pin(jexternalsToPin, subPool); + if (!JNIUtil::isJavaExceptionThrown()) + SVN_JNI_ERR(svn_client_copy7(srcs, destinationPath.c_str(), + copyAsChild, makeParents, ignoreExternals, + metadataOnly, + pinExternals, pin_set, + revprops.hash(subPool), + CommitCallback::callback, callback, ctx, subPool.getPool()), ); } void SVNClient::move(Targets &srcPaths, const char *destPath, CommitMessage *message, bool force, bool moveAsChild, bool makeParents, bool metadataOnly, bool allowMixRev, - RevpropTable &revprops, CommitCallback *callback) + PropertyTable &revprops, CommitCallback *callback) { SVN::Pool subPool(pool); @@ -496,7 +574,7 @@ void SVNClient::move(Targets &srcPaths, const char *destPath, } void SVNClient::mkdir(Targets &targets, CommitMessage *message, - bool makeParents, RevpropTable &revprops, + bool makeParents, PropertyTable &revprops, CommitCallback *callback) { SVN::Pool subPool(pool); @@ -513,7 +591,12 @@ void SVNClient::mkdir(Targets &targets, CommitMessage *message, ctx, subPool.getPool()), ); } -void SVNClient::cleanup(const char *path) +void SVNClient::cleanup(const char *path, + bool break_locks, + bool fix_recorded_timestamps, + bool clear_dav_cache, + bool remove_unused_pristines, + bool include_externals) { SVN::Pool subPool(pool); SVN_JNI_NULL_PTR_EX(path, "path", ); @@ -524,7 +607,13 @@ void SVNClient::cleanup(const char *path) if (ctx == NULL) return; - SVN_JNI_ERR(svn_client_cleanup(intPath.c_str(), ctx, subPool.getPool()),); + SVN_JNI_ERR(svn_client_cleanup2(intPath.c_str(), + break_locks, + fix_recorded_timestamps, + clear_dav_cache, + remove_unused_pristines, + include_externals, + ctx, subPool.getPool()),); } void SVNClient::resolve(const char *path, svn_depth_t depth, @@ -545,6 +634,7 @@ void SVNClient::resolve(const char *path, svn_depth_t depth, jlong SVNClient::doExport(const char *srcPath, const char *destPath, Revision &revision, Revision &pegRevision, bool force, bool ignoreExternals, + bool ignoreKeywords, svn_depth_t depth, const char *nativeEOL) { SVN::Pool subPool(pool); @@ -563,7 +653,7 @@ jlong SVNClient::doExport(const char *srcPath, const char *destPath, destinationPath.c_str(), pegRevision.revision(), revision.revision(), force, - ignoreExternals, FALSE, + ignoreExternals, ignoreKeywords, depth, nativeEOL, ctx, subPool.getPool()), @@ -613,7 +703,7 @@ void SVNClient::doImport(const char *path, const char *url, CommitMessage *message, svn_depth_t depth, bool noIgnore, bool noAutoProps, bool ignoreUnknownNodeTypes, - RevpropTable &revprops, + PropertyTable &revprops, ImportFilterCallback *ifCallback, CommitCallback *commitCallback) { @@ -658,7 +748,7 @@ void SVNClient::merge(const char *path1, Revision &revision1, const char *path2, Revision &revision2, const char *localPath, bool forceDelete, svn_depth_t depth, bool ignoreMergeinfo, bool diffIgnoreAncestry, - bool dryRun, bool recordOnly) + bool dryRun, bool allowMixedRev, bool recordOnly) { SVN::Pool subPool(pool); SVN_JNI_NULL_PTR_EX(path1, "path1", ); @@ -683,14 +773,15 @@ void SVNClient::merge(const char *path1, Revision &revision1, depth, ignoreMergeinfo, diffIgnoreAncestry, forceDelete, recordOnly, dryRun, - TRUE, NULL, ctx, subPool.getPool()), ); + allowMixedRev, NULL, ctx, + subPool.getPool()), ); } void SVNClient::merge(const char *path, Revision &pegRevision, std::vector<RevisionRange> *rangesToMerge, const char *localPath, bool forceDelete, svn_depth_t depth, bool ignoreMergeinfo, bool diffIgnoreAncestry, - bool dryRun, bool recordOnly) + bool dryRun, bool allowMixedRev, bool recordOnly) { SVN::Pool subPool(pool); SVN_JNI_NULL_PTR_EX(path, "path", ); @@ -718,39 +809,16 @@ void SVNClient::merge(const char *path, Revision &pegRevision, depth, ignoreMergeinfo, diffIgnoreAncestry, forceDelete, recordOnly, - dryRun, TRUE, NULL, ctx, + dryRun, allowMixedRev, NULL, ctx, subPool.getPool()), ); } -void SVNClient::mergeReintegrate(const char *path, Revision &pegRevision, - const char *localPath, bool dryRun) -{ - SVN::Pool subPool(pool); - SVN_JNI_NULL_PTR_EX(path, "path", ); - SVN_JNI_NULL_PTR_EX(localPath, "localPath", ); - Path intLocalPath(localPath, subPool); - SVN_JNI_ERR(intLocalPath.error_occurred(), ); - - Path srcPath(path, subPool); - SVN_JNI_ERR(srcPath.error_occurred(), ); - - svn_client_ctx_t *ctx = context.getContext(NULL, subPool); - if (ctx == NULL) - return; - - SVN_JNI_ERR(svn_client_merge_reintegrate(srcPath.c_str(), - pegRevision.revision(), - intLocalPath.c_str(), - dryRun, NULL, ctx, - subPool.getPool()), ); -} +/* SVNClient::mergeReintegrate is implemented in deprecated.cpp. */ jobject SVNClient::getMergeinfo(const char *target, Revision &pegRevision) { SVN::Pool subPool(pool); - JNIEnv *env = JNIUtil::getEnv(); - svn_client_ctx_t *ctx = context.getContext(NULL, subPool); if (ctx == NULL) return NULL; @@ -765,57 +833,7 @@ SVNClient::getMergeinfo(const char *target, Revision &pegRevision) NULL); if (mergeinfo == NULL) return NULL; - - // Transform mergeinfo into Java Mergeinfo object. - jclass clazz = env->FindClass(JAVA_PACKAGE "/types/Mergeinfo"); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; - - static jmethodID ctor = 0; - if (ctor == 0) - { - ctor = env->GetMethodID(clazz, "<init>", "()V"); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; - } - - static jmethodID addRevisions = 0; - if (addRevisions == 0) - { - addRevisions = env->GetMethodID(clazz, "addRevisions", - "(Ljava/lang/String;" - "Ljava/util/List;)V"); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; - } - - jobject jmergeinfo = env->NewObject(clazz, ctor); - if (JNIUtil::isJavaExceptionThrown()) - return NULL; - - apr_hash_index_t *hi; - for (hi = apr_hash_first(subPool.getPool(), mergeinfo); - hi; - hi = apr_hash_next(hi)) - { - const void *path; - void *val; - apr_hash_this(hi, &path, NULL, &val); - - jstring jpath = - JNIUtil::makeJString(reinterpret_cast<const char *>(path)); - jobject jranges = - CreateJ::RevisionRangeList(reinterpret_cast<svn_rangelist_t *>(val)); - - env->CallVoidMethod(jmergeinfo, addRevisions, jpath, jranges); - - env->DeleteLocalRef(jranges); - env->DeleteLocalRef(jpath); - } - - env->DeleteLocalRef(clazz); - - return jmergeinfo; + return CreateJ::Mergeinfo(mergeinfo, subPool.getPool()); } void SVNClient::getMergeinfoLog(int type, const char *pathOrURL, @@ -954,7 +972,7 @@ void SVNClient::propertySetRemote(const char *path, long base_rev, const char *name, CommitMessage *message, JNIByteArray &value, bool force, - RevpropTable &revprops, + PropertyTable &revprops, CommitCallback *callback) { SVN::Pool subPool(pool); @@ -1155,23 +1173,31 @@ SVNClient::diffSummarize(const char *target, Revision &pegRevision, subPool.getPool()), ); } -void SVNClient::streamFileContent(const char *path, Revision &revision, - Revision &pegRevision, - OutputStream &outputStream) +apr_hash_t *SVNClient::streamFileContent(const char *path, + Revision &revision, + Revision &pegRevision, + bool expand_keywords, + bool return_props, + OutputStream &outputStream) { SVN::Pool subPool(pool); - SVN_JNI_NULL_PTR_EX(path, "path", ); + SVN_JNI_NULL_PTR_EX(path, "path", NULL); Path intPath(path, subPool); - SVN_JNI_ERR(intPath.error_occurred(), ); + SVN_JNI_ERR(intPath.error_occurred(), NULL); svn_client_ctx_t *ctx = context.getContext(NULL, subPool); if (ctx == NULL) - return; + return NULL; - SVN_JNI_ERR(svn_client_cat2(outputStream.getStream(subPool), - intPath.c_str(), pegRevision.revision(), - revision.revision(), ctx, subPool.getPool()), - ); + apr_hash_t *props = NULL; + SVN_JNI_ERR(svn_client_cat3((return_props ? &props : NULL), + outputStream.getStream(subPool), + intPath.c_str(), + pegRevision.revision(), revision.revision(), + expand_keywords, ctx, + subPool.getPool(), subPool.getPool()), + NULL); + return props; } jbyteArray SVNClient::revProperty(const char *path, @@ -1239,7 +1265,7 @@ void SVNClient::relocate(const char *from, const char *to, const char *path, void SVNClient::blame(const char *path, Revision &pegRevision, Revision &revisionStart, Revision &revisionEnd, bool ignoreMimeType, bool includeMergedRevisions, - BlameCallback *callback) + BlameCallback *callback, DiffOptions const& options) { SVN::Pool subPool(pool); SVN_JNI_NULL_PTR_EX(path, "path", ); @@ -1253,7 +1279,7 @@ void SVNClient::blame(const char *path, Revision &pegRevision, SVN_JNI_ERR(svn_client_blame5( intPath.c_str(), pegRevision.revision(), revisionStart.revision(), revisionEnd.revision(), - svn_diff_file_options_create(subPool.getPool()), ignoreMimeType, + options.fileOptions(subPool), ignoreMimeType, includeMergedRevisions, BlameCallback::callback, callback, ctx, subPool.getPool()), ); @@ -1288,15 +1314,17 @@ void SVNClient::removeFromChangelists(Targets &srcPaths, svn_depth_t depth, } void SVNClient::getChangelists(const char *rootPath, - StringArray &changelists, + StringArray *changelists, svn_depth_t depth, ChangelistCallback *callback) { SVN::Pool subPool(pool); svn_client_ctx_t *ctx = context.getContext(NULL, subPool); - SVN_JNI_ERR(svn_client_get_changelists(rootPath, - changelists.array(subPool), + const apr_array_header_t *cl_array = (!changelists ? NULL + : changelists->array(subPool)); + + SVN_JNI_ERR(svn_client_get_changelists(rootPath, cl_array, depth, ChangelistCallback::callback, callback, ctx, subPool.getPool()), ); @@ -1392,10 +1420,10 @@ jstring SVNClient::getVersionInfo(const char *path, const char *trailUrl, } else { - char *message = JNIUtil::getFormatBuffer(); - apr_snprintf(message, JNIUtil::formatBufferSize, + char buffer[2048]; + apr_snprintf(buffer, sizeof(buffer), _("'%s' not versioned, and not exported\n"), path); - return JNIUtil::makeJString(message); + return JNIUtil::makeJString(buffer); } } @@ -1465,19 +1493,15 @@ jobject SVNClient::revProperties(const char *path, Revision &revision) &set_rev, ctx, subPool.getPool()), NULL); - return CreateJ::PropertyMap(props); + return CreateJ::PropertyMap(props, subPool.getPool()); } -struct info_baton -{ - std::vector<info_entry> infoVect; - apr_pool_t *pool; -}; - void -SVNClient::info2(const char *path, Revision &revision, Revision &pegRevision, - svn_depth_t depth, StringArray &changelists, - InfoCallback *callback) +SVNClient::info(const char *path, + Revision &revision, Revision &pegRevision, svn_depth_t depth, + svn_boolean_t fetchExcluded, svn_boolean_t fetchActualOnly, + svn_boolean_t includeExternals, + StringArray &changelists, InfoCallback *callback) { SVN_JNI_NULL_PTR_EX(path, "path", ); @@ -1489,10 +1513,11 @@ SVNClient::info2(const char *path, Revision &revision, Revision &pegRevision, Path checkedPath(path, subPool); SVN_JNI_ERR(checkedPath.error_occurred(), ); - SVN_JNI_ERR(svn_client_info3(checkedPath.c_str(), + SVN_JNI_ERR(svn_client_info4(checkedPath.c_str(), pegRevision.revision(), - revision.revision(), - depth, FALSE, TRUE, + revision.revision(), depth, + fetchExcluded, fetchActualOnly, + includeExternals, changelists.array(subPool), InfoCallback::callback, callback, ctx, subPool.getPool()), ); @@ -1525,6 +1550,87 @@ SVNClient::patch(const char *patchPath, const char *targetPath, bool dryRun, ctx, subPool.getPool()), ); } +void SVNClient::vacuum(const char *path, + bool remove_unversioned_items, + bool remove_ignored_items, + bool fix_recorded_timestamps, + bool remove_unused_pristines, + bool include_externals) +{ + SVN_JNI_NULL_PTR_EX(path, "path", ); + + SVN::Pool subPool(pool); + svn_client_ctx_t *ctx = context.getContext(NULL, subPool); + if (ctx == NULL) + return; + + Path checkedPath(path, subPool); + SVN_JNI_ERR(checkedPath.error_occurred(),); + + SVN_JNI_ERR(svn_client_vacuum(checkedPath.c_str(), + remove_unversioned_items, + remove_ignored_items, + fix_recorded_timestamps, + remove_unused_pristines, + include_externals, + ctx, subPool.getPool()), ); +} + +jobject +SVNClient::openRemoteSession(const char* path, int retryAttempts) +{ + static const svn_opt_revision_t HEAD = { svn_opt_revision_head, {0}}; + static const svn_opt_revision_t NONE = { svn_opt_revision_unspecified, {0}}; + + SVN_JNI_NULL_PTR_EX(path, "path", NULL); + + SVN::Pool subPool(pool); + svn_client_ctx_t *ctx = context.getContext(NULL, subPool); + if (ctx == NULL) + return NULL; + + Path checkedPath(path, subPool); + SVN_JNI_ERR(checkedPath.error_occurred(), NULL); + + struct PathInfo + { + std::string url; + std::string uuid; + static svn_error_t *callback(void *baton, + const char *, + const svn_client_info2_t *info, + apr_pool_t *) + { + PathInfo* const pi = static_cast<PathInfo*>(baton); + pi->url = info->URL; + pi->uuid = info->repos_UUID; + return SVN_NO_ERROR; + } + } path_info; + + SVN_JNI_ERR(svn_client_info4( + checkedPath.c_str(), &NONE, + (svn_path_is_url(checkedPath.c_str()) ? &HEAD : &NONE), + svn_depth_empty, FALSE, TRUE, FALSE, NULL, + PathInfo::callback, &path_info, + ctx, subPool.getPool()), + NULL); + + /* Decouple the RemoteSession's context from SVNClient's context + by creating a copy of the prompter here. */ + + jobject jremoteSession = RemoteSession::open( + retryAttempts, path_info.url.c_str(), path_info.uuid.c_str(), + context.getConfigDirectory(), + context.getUsername(), context.getPassword(), + context.clonePrompter(), context.getSelf(), + context.getConfigEventHandler(), context.getTunnelCallback()); + if (JNIUtil::isJavaExceptionThrown()) + jremoteSession = NULL; + + return jremoteSession; +} + ClientContext & SVNClient::getClientContext() { diff --git a/subversion/bindings/javahl/native/SVNClient.h b/subversion/bindings/javahl/native/SVNClient.h index 8d85d9b..6661bae 100644 --- a/subversion/bindings/javahl/native/SVNClient.h +++ b/subversion/bindings/javahl/native/SVNClient.h @@ -53,7 +53,7 @@ class PatchCallback; class ChangelistCallback; class CommitMessage; class StringArray; -class RevpropTable; +class PropertyTable; class DiffOptions; #include "svn_types.h" #include "svn_client.h" @@ -62,12 +62,19 @@ class DiffOptions; class SVNClient :public SVNBase { public: + jobject openRemoteSession(const char* path, int); + void vacuum(const char *path, + bool remove_unversioned_items, bool remove_ignored_items, + bool fix_recorded_timestamps, bool remove_unused_pristines, + bool include_externals); void patch(const char *patchPath, const char *targetPath, bool dryRun, int stripCount, bool reverse, bool ignoreWhitespace, bool removeTempfiles, PatchCallback *callback); - void info2(const char *path, Revision &revision, Revision &pegRevision, - svn_depth_t depth, StringArray &changelists, - InfoCallback *callback); + void info(const char *path, + Revision &revision, Revision &pegRevision, svn_depth_t depth, + svn_boolean_t fetchExcluded, svn_boolean_t fetchActualOnly, + svn_boolean_t includeExternals, + StringArray &changelists, InfoCallback *callback); void unlock(Targets &targets, bool force); void lock(Targets &targets, const char *comment, bool force); jobject revProperties(const char *path, Revision &revision); @@ -75,18 +82,20 @@ class SVNClient :public SVNBase void blame(const char *path, Revision &pegRevision, Revision &revisionStart, Revision &revisionEnd, bool ignoreMimeType, bool includeMergedRevisions, - BlameCallback *callback); + BlameCallback *callback, DiffOptions const& options); void relocate(const char *from, const char *to, const char *path, bool ignoreExternals); - void streamFileContent(const char *path, Revision &revision, - Revision &pegRevision, OutputStream &outputStream); + apr_hash_t *streamFileContent(const char *path, + Revision &revision, Revision &pegRevision, + bool expand_keywords, bool return_props, + OutputStream &outputStream); void propertySetLocal(Targets &targets, const char *name, JNIByteArray &value, svn_depth_t depth, StringArray &changelists, bool force); void propertySetRemote(const char *path, long base_rev, const char *name, CommitMessage *message, JNIByteArray &value, bool force, - RevpropTable &revprops, CommitCallback *callback); + PropertyTable &revprops, CommitCallback *callback); void properties(const char *path, Revision &revision, Revision &pegRevision, svn_depth_t depth, StringArray &changelists, ProplistCallback *callback); @@ -103,18 +112,18 @@ class SVNClient :public SVNBase const char *path2, Revision &revision2, const char *localPath, bool forceDelete, svn_depth_t depth, bool ignoreMergeinfo, bool diffIgnoreAncestry, - bool dryRun, bool recordOnly); + bool dryRun, bool allowMixedRev, bool recordOnly); void merge(const char *path, Revision &pegRevision, std::vector<RevisionRange> *rangesToMerge, const char *localPath, bool forceDelete, svn_depth_t depth, bool ignoreMergeinfo, bool diffIgnoreAncestry, - bool dryRun, bool recordOnly); + bool dryRun, bool allowMixedRev, bool recordOnly); void mergeReintegrate(const char *path, Revision &pegRevision, const char *localPath, bool dryRun); void doImport(const char *path, const char *url, CommitMessage *message, svn_depth_t depth, bool noIgnore, bool noAutoProps, bool ignoreUnknownNodeTypes, - RevpropTable &revprops, ImportFilterCallback *ifCallback, + PropertyTable &revprops, ImportFilterCallback *ifCallback, CommitCallback *commitCallback); jlong doSwitch(const char *path, const char *url, Revision &revision, Revision &pegRevision, svn_depth_t depth, @@ -122,33 +131,40 @@ class SVNClient :public SVNBase bool allowUnverObstructions, bool ignoreAncestry); jlong doExport(const char *srcPath, const char *destPath, Revision &revision, Revision &pegRevision, bool force, - bool ignoreExternals, svn_depth_t depth, - const char *nativeEOL); + bool ignoreExternals, bool ignoreKeywords, + svn_depth_t depth, const char *nativeEOL); void resolve(const char *path, svn_depth_t depth, svn_wc_conflict_choice_t choice); - void cleanup(const char *path); + void cleanup(const char *path, + bool break_locks, + bool fix_recorded_timestamps, + bool clear_dav_cache, + bool remove_unused_pristines, + bool include_externals); void mkdir(Targets &targets, CommitMessage *message, bool makeParents, - RevpropTable &revprops, CommitCallback *callback); + PropertyTable &revprops, CommitCallback *callback); void move(Targets &srcPaths, const char *destPath, CommitMessage *message, bool force, bool moveAsChild, bool makeParents, bool metadataOnly, bool allowMixRev, - RevpropTable &revprops, CommitCallback *callback); + PropertyTable &revprops, CommitCallback *callback); void copy(CopySources ©Sources, const char *destPath, CommitMessage *message, bool copyAsChild, bool makeParents, - bool ignoreExternals, RevpropTable &revprops, - CommitCallback *callback); + bool ignoreExternals, bool metaDataOnly, bool pinExternals, + jobject jexternalsToPin, + PropertyTable &revprops, CommitCallback *callback); void commit(Targets &targets, CommitMessage *message, svn_depth_t depth, bool noUnlock, bool keepChangelist, - StringArray &changelists, RevpropTable &revprops, + StringArray &changelists, PropertyTable &revprops, CommitCallback *callback); jlongArray update(Targets &targets, Revision &revision, svn_depth_t depth, bool depthIsSticky, bool makeParents, bool ignoreExternals, bool allowUnverObstructions); void add(const char *path, svn_depth_t depth, bool force, bool no_ignore, bool no_autoprops, bool add_parents); - void revert(const char *path, svn_depth_t depth, StringArray &changelists); + void revert(StringArray &paths, svn_depth_t depth, StringArray &changelists, + bool clear_changelists, bool metadata_only); void remove(Targets &targets, CommitMessage *message, bool force, - bool keep_local, RevpropTable &revprops, + bool keep_local, PropertyTable &revprops, CommitCallback *callback); jlong checkout(const char *moduleName, const char *destPath, Revision &revision, Revision &pegRevsion, svn_depth_t depth, @@ -157,7 +173,7 @@ class SVNClient :public SVNBase std::vector<RevisionRange> &ranges, bool stopOnCopy, bool discoverPaths, bool includeMergedRevisions, StringArray &revProps, - long limit, LogMessageCallback *callback); + int limit, LogMessageCallback *callback); jobject getVersionExtended(bool verbose); jstring getAdminDirectoryName(); jboolean isAdminDirectory(const char *name); @@ -165,11 +181,13 @@ class SVNClient :public SVNBase svn_depth_t depth, StringArray &changelists); void removeFromChangelists(Targets &srcPaths, svn_depth_t depth, StringArray &changelists); - void getChangelists(const char *path, StringArray &changelists, + void getChangelists(const char *path, StringArray *changelists, svn_depth_t depth, ChangelistCallback *callback); - void status(const char *path, svn_depth_t depth, bool onServer, - bool getAll, bool noIgnore, bool ignoreExternals, - StringArray &changelists, StatusCallback *callback); + void status(const char *path, svn_depth_t depth, + bool onServer, bool onDisk, bool getAll, + bool noIgnore, bool ignoreExternals, + bool depthAsSticky, StringArray &changelists, + StatusCallback *callback); void list(const char *url, Revision &revision, Revision &pegRevision, svn_depth_t depth, int direntFields, bool fetchLocks, ListCallback *callback); diff --git a/subversion/bindings/javahl/native/SVNRepos.cpp b/subversion/bindings/javahl/native/SVNRepos.cpp index 7fdf63c..0ee87e6 100644 --- a/subversion/bindings/javahl/native/SVNRepos.cpp +++ b/subversion/bindings/javahl/native/SVNRepos.cpp @@ -26,7 +26,6 @@ #include "SVNRepos.h" #include "CreateJ.h" -#include "ReposNotifyCallback.h" #include "JNIUtil.h" #include "svn_error_codes.h" #include "svn_repos.h" @@ -50,14 +49,14 @@ SVNRepos *SVNRepos::getCppObject(jobject jthis) { static jfieldID fid = 0; jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid, - JAVA_PACKAGE"/SVNRepos"); + JAVAHL_CLASS("/SVNRepos")); return (cppAddr == 0 ? NULL : reinterpret_cast<SVNRepos *>(cppAddr)); } void SVNRepos::dispose(jobject jthis) { static jfieldID fid = 0; - SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNRepos"); + SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/SVNRepos")); } void SVNRepos::cancelOperation() @@ -124,8 +123,10 @@ void SVNRepos::deltify(File &path, Revision &revStart, Revision &revEnd) return; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), + requestPool.getPool()), ); fs = svn_repos_fs(repos); SVN_JNI_ERR(svn_fs_youngest_rev(&youngest, fs, requestPool.getPool()), ); @@ -193,8 +194,9 @@ void SVNRepos::dump(File &path, OutputStream &dataOut, return; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); fs = svn_repos_fs(repos); SVN_JNI_ERR(svn_fs_youngest_rev(&youngest, fs, requestPool.getPool()), ); @@ -248,7 +250,8 @@ void SVNRepos::dump(File &path, OutputStream &dataOut, } void SVNRepos::hotcopy(File &path, File &targetPath, - bool cleanLogs, bool incremental) + bool cleanLogs, bool incremental, + ReposNotifyCallback *notifyCallback) { SVN::Pool requestPool; @@ -264,9 +267,13 @@ void SVNRepos::hotcopy(File &path, File &targetPath, return; } - SVN_JNI_ERR(svn_repos_hotcopy2(path.getInternalStyle(requestPool), + SVN_JNI_ERR(svn_repos_hotcopy3(path.getInternalStyle(requestPool), targetPath.getInternalStyle(requestPool), cleanLogs, incremental, + notifyCallback != NULL + ? ReposNotifyCallback::notify + : NULL, + notifyCallback, checkCancel, this /* cancel callback/baton */, requestPool.getPool()), ); @@ -321,6 +328,8 @@ void SVNRepos::load(File &path, bool forceUUID, bool usePreCommitHook, bool usePostCommitHook, + bool validateProps, + bool ignoreDates, const char *relativePath, ReposNotifyCallback *notifyCallback) { @@ -353,13 +362,14 @@ void SVNRepos::load(File &path, _("First revision cannot be higher than second")), ); } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); - SVN_JNI_ERR(svn_repos_load_fs4(repos, dataIn.getStream(requestPool), + SVN_JNI_ERR(svn_repos_load_fs5(repos, dataIn.getStream(requestPool), lower, upper, uuid_action, relativePath, usePreCommitHook, usePostCommitHook, - FALSE, + validateProps, ignoreDates, notifyCallback != NULL ? ReposNotifyCallback::notify : NULL, @@ -380,8 +390,9 @@ void SVNRepos::lstxns(File &path, MessageReceiver &messageReceiver) return; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); fs = svn_repos_fs(repos); SVN_JNI_ERR(svn_fs_list_transactions(&txns, fs, requestPool.getPool()), ); @@ -417,8 +428,9 @@ jlong SVNRepos::recover(File &path, ReposNotifyCallback *notifyCallback) /* Since db transactions may have been replayed, it's nice to tell * people what the latest revision is. It also proves that the * recovery actually worked. */ - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), -1); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), -1); SVN_JNI_ERR(svn_fs_youngest_rev(&youngest_rev, svn_repos_fs(repos), requestPool.getPool()), -1); @@ -463,8 +475,9 @@ void SVNRepos::rmtxns(File &path, StringArray &transactions) return; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); fs = svn_repos_fs(repos); args = transactions.array(requestPool); @@ -520,8 +533,9 @@ void SVNRepos::setRevProp(File &path, Revision &revision, /* Open the filesystem. */ svn_repos_t *repos; - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); /* If we are bypassing the hooks system, we just hit the filesystem * directly. */ @@ -579,7 +593,9 @@ SVNRepos::getRevnum(svn_revnum_t *revnum, const svn_opt_revision_t *revision, void SVNRepos::verify(File &path, Revision &revisionStart, Revision &revisionEnd, - ReposNotifyCallback *notifyCallback) + bool checkNormalization, bool metadataOnly, + ReposNotifyCallback *notifyCallback, + ReposVerifyCallback *verifyCallback) { SVN::Pool requestPool; svn_repos_t *repos; @@ -594,8 +610,9 @@ SVNRepos::verify(File &path, Revision &revisionStart, Revision &revisionEnd, /* This whole process is basically just a dump of the repository * with no interest in the output. */ - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); SVN_JNI_ERR(svn_fs_youngest_rev(&youngest, svn_repos_fs(repos), requestPool.getPool()), ); @@ -621,11 +638,15 @@ SVNRepos::verify(File &path, Revision &revisionStart, Revision &revisionEnd, (SVN_ERR_INCORRECT_PARAMS, NULL, _("Start revision cannot be higher than end revision")), ); - SVN_JNI_ERR(svn_repos_verify_fs2(repos, lower, upper, - notifyCallback != NULL - ? ReposNotifyCallback::notify - : NULL, + SVN_JNI_ERR(svn_repos_verify_fs3(repos, lower, upper, + checkNormalization, + metadataOnly, + (!notifyCallback ? NULL + : ReposNotifyCallback::notify), notifyCallback, + (!verifyCallback ? NULL + : ReposVerifyCallback::callback), + verifyCallback, checkCancel, this /* cancel callback/baton */, requestPool.getPool()), ); } @@ -641,8 +662,9 @@ void SVNRepos::pack(File &path, ReposNotifyCallback *notifyCallback) return; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); SVN_JNI_ERR(svn_repos_fs_pack2(repos, notifyCallback != NULL @@ -686,15 +708,16 @@ jobject SVNRepos::lslocks(File &path, svn_depth_t depth) return NULL; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), NULL); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), NULL); /* Fetch all locks on or below the root directory. */ SVN_JNI_ERR(svn_repos_fs_get_locks2(&locks, repos, "/", depth, NULL, NULL, requestPool.getPool()), NULL); JNIEnv *env = JNIUtil::getEnv(); - jclass clazz = env->FindClass(JAVA_PACKAGE"/types/Lock"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/types/Lock")); if (JNIUtil::isJavaExceptionThrown()) return NULL; @@ -730,8 +753,9 @@ void SVNRepos::rmlocks(File &path, StringArray &locks) return; } - SVN_JNI_ERR(svn_repos_open2(&repos, path.getInternalStyle(requestPool), - NULL, requestPool.getPool()), ); + SVN_JNI_ERR(svn_repos_open3(&repos, + path.getInternalStyle(requestPool), NULL, + requestPool.getPool(), requestPool.getPool()), ); fs = svn_repos_fs(repos); const char *username = NULL; diff --git a/subversion/bindings/javahl/native/SVNRepos.h b/subversion/bindings/javahl/native/SVNRepos.h index b22a4c4..4fdc48d 100644 --- a/subversion/bindings/javahl/native/SVNRepos.h +++ b/subversion/bindings/javahl/native/SVNRepos.h @@ -35,6 +35,7 @@ #include "InputStream.h" #include "MessageReceiver.h" #include "ReposNotifyCallback.h" +#include "ReposVerifyCallback.h" #include "ReposFreezeAction.h" #include "StringArray.h" #include "File.h" @@ -45,7 +46,9 @@ class SVNRepos : public SVNBase void rmlocks(File &path, StringArray &locks); jobject lslocks(File &path, svn_depth_t depth); void verify(File &path, Revision &revisionStart, Revision &revisionEnd, - ReposNotifyCallback *notifyCallback); + bool checkNormalization, bool metadataOnly, + ReposNotifyCallback *notifyCallback, + ReposVerifyCallback *verifyCallback); void setRevProp(File &path, Revision &revision, const char *propName, const char *propValue, bool usePreRevPropChangeHook, @@ -58,11 +61,13 @@ class SVNRepos : public SVNBase Revision &revsionStart, Revision &revisionEnd, bool ignoreUUID, bool forceUUID, bool usePreCommitHook, bool usePostCommitHook, + bool validateProps, bool ignoreDates, const char *relativePath, ReposNotifyCallback *notifyCallback); void listUnusedDBLogs(File &path, MessageReceiver &messageReceiver); void listDBLogs(File &path, MessageReceiver &messageReceiver); - void hotcopy(File &path, File &targetPath, bool cleanLogs, bool incremental); + void hotcopy(File &path, File &targetPath, bool cleanLogs, bool incremental, + ReposNotifyCallback *notifyCallback); void dump(File &path, OutputStream &dataOut, Revision &revsionStart, Revision &RevisionEnd, bool incremental, bool useDeltas, ReposNotifyCallback *notifyCallback); diff --git a/subversion/bindings/javahl/native/StateReporter.cpp b/subversion/bindings/javahl/native/StateReporter.cpp new file mode 100644 index 0000000..a0c05ed --- /dev/null +++ b/subversion/bindings/javahl/native/StateReporter.cpp @@ -0,0 +1,191 @@ +/** + * @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 StateReporter.cpp + * @brief Implementation of the class StateReporter + */ + +#include <jni.h> + +#include "JNIUtil.h" +#include "JNIStringHolder.h" +#include "StateReporter.h" +#include "EnumMapper.h" +#include "Path.h" + +#include "svn_private_config.h" + +StateReporter::StateReporter() + : m_valid(false), + m_raw_reporter(NULL), + m_report_baton(NULL), + m_editor(NULL), + m_target_revision(SVN_INVALID_REVNUM) +{} + +StateReporter::~StateReporter() +{ + delete m_editor; +} + +StateReporter* +StateReporter::getCppObject(jobject jthis) +{ + static jfieldID fid = 0; + jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid, + JAVAHL_CLASS("/remote/StateReporter")); + return (cppAddr == 0 ? NULL : reinterpret_cast<StateReporter*>(cppAddr)); +} + +void +StateReporter::dispose(jobject jthis) +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::dispose()\n"); + + if (m_valid) + abortReport(); + + static jfieldID fid = 0; + SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/remote/StateReporter")); +} + +namespace { +void throw_reporter_inactive() +{ + JNIUtil::raiseThrowable("java/lang/IllegalStateException", + _("The reporter is not active")); +} +} // anonymous namespace + +void +StateReporter::setPath(jstring jpath, jlong jrevision, jobject jdepth, + jboolean jstart_empty, jstring jlock_token) +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::setPath()\n"); + + if (!m_valid) { throw_reporter_inactive(); return; } + + JNIStringHolder lock_token(jlock_token); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + svn_depth_t depth = EnumMapper::toDepth(jdepth); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN_JNI_ERR(m_raw_reporter->set_path(m_report_baton, path.c_str(), + svn_revnum_t(jrevision), depth, + bool(jstart_empty), lock_token.c_str(), + subPool.getPool()),); +} + +void +StateReporter::deletePath(jstring jpath) +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::deletePath()\n"); + + if (!m_valid) { throw_reporter_inactive(); return; } + + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN_JNI_ERR(m_raw_reporter->delete_path(m_report_baton, path.c_str(), + subPool.getPool()),); +} + +void +StateReporter::linkPath(jstring jurl, jstring jpath, + jlong jrevision, jobject jdepth, + jboolean jstart_empty, jstring jlock_token) +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::linkPath()\n"); + + if (!m_valid) { throw_reporter_inactive(); return; } + + JNIStringHolder lock_token(jlock_token); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN::Pool subPool(pool); + Relpath path(jpath, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + URL url(jurl, subPool); + if (JNIUtil::isJavaExceptionThrown()) + return; + svn_depth_t depth = EnumMapper::toDepth(jdepth); + if (JNIUtil::isJavaExceptionThrown()) + return; + + SVN_JNI_ERR(m_raw_reporter->link_path(m_report_baton, path.c_str(), + url.c_str(), svn_revnum_t(jrevision), + depth, bool(jstart_empty), + lock_token.c_str(), + subPool.getPool()),); +} + +jlong +StateReporter::finishReport() +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::finishReport()\n"); + + if (!m_valid) { throw_reporter_inactive(); return SVN_INVALID_REVNUM; } + + SVN::Pool subPool(pool); + SVN_JNI_ERR(m_raw_reporter->finish_report(m_report_baton, + subPool.getPool()), + SVN_INVALID_REVNUM); + m_valid = false; + return jlong(m_target_revision); +} + +void +StateReporter::abortReport() +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::abortReport()\n"); + + if (!m_valid) { throw_reporter_inactive(); return; } + + SVN::Pool subPool(pool); + SVN_JNI_ERR(m_raw_reporter->abort_report(m_report_baton, + subPool.getPool()),); + m_valid = false; +} + +void +StateReporter::set_reporter_data(const svn_ra_reporter3_t* raw_reporter, + void* report_baton, + EditorProxy* editor) +{ + //DEBUG:fprintf(stderr, " (n) StateReporter::set_reporter_data()\n"); + + m_editor = editor; + m_raw_reporter = raw_reporter; + m_report_baton = report_baton; + m_valid = true; +} diff --git a/subversion/bindings/javahl/native/StateReporter.h b/subversion/bindings/javahl/native/StateReporter.h new file mode 100644 index 0000000..f16ddbf --- /dev/null +++ b/subversion/bindings/javahl/native/StateReporter.h @@ -0,0 +1,74 @@ +/** + * @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 UpdateReporter.h + * @brief Interface of the class UpdateReporter + */ + +#ifndef JAVAHL_UPDATE_REPORTER_H +#define JAVAHL_UPDATE_REPORTER_H + +#include <jni.h> + +#include "svn_ra.h" +#include "SVNBase.h" +#include "EditorProxy.h" + +class RemoteSession; + +/* + * This class wraps the update/status/switch/diff reporter in svn_ra.h + */ +class StateReporter : public SVNBase +{ +public: + StateReporter(); + virtual ~StateReporter(); + + static StateReporter* getCppObject(jobject jreporter); + + virtual void dispose(jobject jthis); + + void setPath(jstring jpath, jlong jrevision, jobject jdepth, + jboolean jstart_empty, jstring jlock_token); + void deletePath(jstring jpath); + void linkPath(jstring jurl, jstring jpath, jlong jrevision, jobject jdepth, + jboolean jstart_empty, jstring jlock_token); + jlong finishReport(); + void abortReport(); + +private: + + bool m_valid; + const svn_ra_reporter3_t* m_raw_reporter; + void* m_report_baton; + EditorProxy* m_editor; + + friend class RemoteSession; + apr_pool_t* get_report_pool() const { return pool.getPool(); } + void set_reporter_data(const svn_ra_reporter3_t* raw_reporter, + void* report_baton, + EditorProxy* editor); + svn_revnum_t m_target_revision; +}; + +#endif // JAVAHL_UPDATE_REPORTER_H diff --git a/subversion/bindings/javahl/native/StatusCallback.cpp b/subversion/bindings/javahl/native/StatusCallback.cpp index cc0d67a..94ed676 100644 --- a/subversion/bindings/javahl/native/StatusCallback.cpp +++ b/subversion/bindings/javahl/native/StatusCallback.cpp @@ -81,13 +81,13 @@ StatusCallback::doStatus(const char *local_abspath, // it can be cached. if (mid == 0) { - jclass clazz = env->FindClass(JAVA_PACKAGE"/callback/StatusCallback"); + jclass clazz = env->FindClass(JAVAHL_CLASS("/callback/StatusCallback")); if (JNIUtil::isJavaExceptionThrown()) POP_AND_RETURN(SVN_NO_ERROR); mid = env->GetMethodID(clazz, "doStatus", "(Ljava/lang/String;" - "L"JAVA_PACKAGE"/types/Status;)V"); + JAVAHL_ARG("/types/Status;") ")V"); if (JNIUtil::isJavaExceptionThrown() || mid == 0) POP_AND_RETURN(SVN_NO_ERROR); } @@ -101,11 +101,8 @@ StatusCallback::doStatus(const char *local_abspath, POP_AND_RETURN(SVN_NO_ERROR); env->CallVoidMethod(m_callback, mid, jPath, jStatus); - // We return here regardless of whether an exception is thrown or not, - // so we do not need to explicitly check for one. - env->PopLocalFrame(NULL); - return SVN_NO_ERROR; + POP_AND_RETURN_EXCEPTION_AS_SVNERROR(); } void diff --git a/subversion/bindings/javahl/native/libsvnjavahl.la.c b/subversion/bindings/javahl/native/SubversionException.cpp index 0cbbd2e..b0b5286 100644 --- a/subversion/bindings/javahl/native/libsvnjavahl.la.c +++ b/subversion/bindings/javahl/native/SubversionException.cpp @@ -19,19 +19,14 @@ * under the License. * ==================================================================== * @endcopyright - * - * @file libsvnjavahl.c - * @brief mandatory file for building with libtool */ -#include <jni.h> -JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *vm, void *reserved) -{ - return JNI_VERSION_1_2; -} +#include "SubversionException.hpp" +#include "JNIUtil.h" + +namespace JavaHL { + +const char* const SubversionException::m_class_name = + JAVAHL_CLASS("/SubversionException"); -JNIEXPORT void JNICALL -JNI_OnUnload(JavaVM *vm, void *reserved) -{ -} +} // namespace JavaHL diff --git a/subversion/bindings/javahl/native/SubversionException.hpp b/subversion/bindings/javahl/native/SubversionException.hpp new file mode 100644 index 0000000..5c71107 --- /dev/null +++ b/subversion/bindings/javahl/native/SubversionException.hpp @@ -0,0 +1,54 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_SUBVERSION_EXCEPTION_HPP +#define SVN_JAVAHL_SUBVERSION_EXCEPTION_HPP + +#include "jniwrapper/jni_exception.hpp" + +namespace JavaHL { + +/** + * Generator class for exceptions of type + * @c org.apache.subversion.javahl.SubversionException. + * + * The associated JNI class reference is stored for the lifetime of + * the JVM in the global class cache. + * + * @since New in 1.9. + */ +class SubversionException : public ::Java::Exception +{ +public: + explicit SubversionException(::Java::Env env) + : Java::Exception(env, ::Java::ClassCache::get_subversion_exception(env)) + {} + +private: + friend class ::Java::ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace JavaHL + +#endif // SVN_JAVAHL_SUBVERSION_EXCEPTION_HPP diff --git a/subversion/bindings/javahl/native/Utility.cpp b/subversion/bindings/javahl/native/Utility.cpp new file mode 100644 index 0000000..9e30386 --- /dev/null +++ b/subversion/bindings/javahl/native/Utility.cpp @@ -0,0 +1,96 @@ +/** + * @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 + */ + +#include <string> +#include <apr_strings.h> + +#include "svn_string.h" + +#include "jniwrapper/jni_array.hpp" +#include "jniwrapper/jni_string.hpp" +#include "jniwrapper/jni_string_map.hpp" + +#include "Utility.hpp" + + +namespace JavaHL { +namespace Util { + +namespace { +class MapToHashIteration +{ +public: + explicit MapToHashIteration(const svn_string_t* default_value, + apr_pool_t* pool) + : m_pool(pool), + m_hash(apr_hash_make(pool)), + m_default(default_value) + {} + + void operator()(const std::string& key, const Java::ByteArray& value) + { + const char* const safe_key = + apr_pstrmemdup(m_pool, key.c_str(), key.size() + 1); + if (!value.get()) + { + if (m_default != NULL) + apr_hash_set(m_hash, safe_key, key.size(), m_default); + } + else + { + Java::ByteArray::Contents val(value); + apr_hash_set(m_hash, safe_key, key.size(), val.get_string(m_pool)); + } + } + + apr_hash_t* get() const + { + return m_hash; + } + +private: + apr_pool_t* const m_pool; + apr_hash_t* const m_hash; + const svn_string_t* const m_default; +}; + +typedef ::Java::ImmutableMap< ::Java::ByteArray, jbyteArray> ImmutableByteArrayMap; +} // anonymous namespace + +apr_hash_t* +make_keyword_hash(::Java::Env env, jobject jkeywords, apr_pool_t* pool) +{ + const svn_string_t* const empty = svn_string_create_empty(pool); + const ImmutableByteArrayMap keywords(env, jkeywords); + return keywords.for_each(MapToHashIteration(empty, pool)).get(); +} + +apr_hash_t* +make_property_hash(::Java::Env env, jobject jproperties, apr_pool_t* pool) +{ + const ImmutableByteArrayMap props(env, jproperties); + return props.for_each(MapToHashIteration(NULL, pool)).get(); +} + +} // namespace Util +} // namespace JavaHL diff --git a/subversion/bindings/javahl/native/Utility.hpp b/subversion/bindings/javahl/native/Utility.hpp new file mode 100644 index 0000000..7d91c91 --- /dev/null +++ b/subversion/bindings/javahl/native/Utility.hpp @@ -0,0 +1,90 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_UTILITY_HPP +#define SVN_JAVAHL_UTILITY_HPP + +#include <apr_hash.h> + +#include "Pool.h" + +namespace JavaHL { +namespace Util { + +/** + * Converts keyword/valuue pairs in the the Java map @a jkeywords to + * an APR hash table allocated in @a pool. The keys in the resulting + * table are @c const @c char*, the values are @c svn_string_t*. Null + * values in the Java map are converted to empty strings. + * + * @since New in 1.9. + */ +apr_hash_t* +make_keyword_hash(::Java::Env env, jobject jkeywords, apr_pool_t* pool); + +/** + * Converts keyword/valuue pairs in the the Java map @a jkeywords to + * an APR hash table allocated in @a pool. The keys in the resulting + * table are @c const @c char*, the values are @c svn_string_t*. Null + * values in the Java map are converted to empty strings. + * + * @since New in 1.9. + */ +inline apr_hash_t* +make_keyword_hash(::Java::Env env, jobject jkeywords, + const ::SVN::Pool& pool) +{ + return make_keyword_hash(env, jkeywords, pool.getPool()); +} + + +/** + * Converts property/value pairs the Java map @a jproperties to an APR + * hash table allocated in @a pool. The keys in the resulting table + * are @c const @c char*, the values are @c svn_string_t*. Null values + * in the Java map will not appear in the converted map. + * + * @since New in 1.9. + */ +apr_hash_t* +make_property_hash(::Java::Env env, jobject jproperties, apr_pool_t* pool); + +/** + * Converts property/value pairs the Java map @a jproperties to an APR + * hash table allocated in @a pool. The keys in the resulting table + * are @c const @c char*, the values are @c svn_string_t*. Null values + * in the Java map will not appear in the converted map. + * + * @since New in 1.9. + */ +inline apr_hash_t* +make_property_hash(::Java::Env env, jobject jproperties, + const ::SVN::Pool& pool) +{ + return make_property_hash(env, jproperties, pool.getPool()); +} + +} // namespace Util +} // namespace JavaHL + +#endif // SVN_JAVAHL_JNI_UTILITY_HPP diff --git a/subversion/bindings/javahl/native/VersionExtended.cpp b/subversion/bindings/javahl/native/VersionExtended.cpp index 1dc47db..7202778 100644 --- a/subversion/bindings/javahl/native/VersionExtended.cpp +++ b/subversion/bindings/javahl/native/VersionExtended.cpp @@ -27,7 +27,7 @@ #include "JNIUtil.h" #include "VersionExtended.h" -const VersionExtended * +VersionExtended * VersionExtended::getCppObject(jobject jthis) { if (!jthis) @@ -35,7 +35,7 @@ VersionExtended::getCppObject(jobject jthis) static jfieldID fid = 0; jlong cppAddr = SVNBase::findCppAddrForJObject( - jthis, &fid, JAVA_PACKAGE"/types/VersionExtended"); + jthis, &fid, JAVAHL_CLASS("/types/VersionExtended")); return (cppAddr == 0 ? NULL : reinterpret_cast<VersionExtended *>(cppAddr)); } @@ -46,7 +46,7 @@ static jobject getWrapperAddress(jobject jthat, volatile jfieldID *fid) if (!*fid) { *fid = env->GetFieldID(env->GetObjectClass(jthat), "wrapper", - "L"JAVA_PACKAGE"/types/VersionExtended;"); + JAVAHL_ARG("/types/VersionExtended;")); if (JNIUtil::isJavaExceptionThrown()) { *fid = 0; @@ -94,5 +94,5 @@ VersionExtended::~VersionExtended() {} void VersionExtended::dispose(jobject jthis) { static jfieldID fid = 0; - SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/types/VersionExtended"); + SVNBase::dispose(jthis, &fid, JAVAHL_CLASS("/types/VersionExtended")); } diff --git a/subversion/bindings/javahl/native/VersionExtended.h b/subversion/bindings/javahl/native/VersionExtended.h index 8df81f3..4b5ff48 100644 --- a/subversion/bindings/javahl/native/VersionExtended.h +++ b/subversion/bindings/javahl/native/VersionExtended.h @@ -33,7 +33,7 @@ class VersionExtended : public SVNBase { public: - static const VersionExtended *getCppObject(jobject jthis); + static VersionExtended *getCppObject(jobject jthis); static const VersionExtended *getCppObjectFromLinkedLib(jobject jthat); static const VersionExtended *getCppObjectFromLoadedLib(jobject jthat); static const VersionExtended *getCppObjectFromLinkedLibIterator(jobject jthat); diff --git a/subversion/bindings/javahl/native/deprecated.cpp b/subversion/bindings/javahl/native/deprecated.cpp new file mode 100644 index 0000000..2ba5e10 --- /dev/null +++ b/subversion/bindings/javahl/native/deprecated.cpp @@ -0,0 +1,60 @@ +/** + * @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 deprecated.cpp + * @brief: Implementation of methods that intentionally use deprecated + * Subversion APIs. + */ + +/* We define this here to remove any further warnings about the usage of + deprecated functions in this file. */ +#define SVN_DEPRECATED + +#include "SVNClient.h" +#include "JNIUtil.h" +#include "Path.h" +#include "Revision.h" + +#include "svn_client.h" + +void SVNClient::mergeReintegrate(const char *path, Revision &pegRevision, + const char *localPath, bool dryRun) +{ + SVN::Pool subPool(pool); + SVN_JNI_NULL_PTR_EX(path, "path", ); + SVN_JNI_NULL_PTR_EX(localPath, "localPath", ); + Path intLocalPath(localPath, subPool); + SVN_JNI_ERR(intLocalPath.error_occurred(), ); + + Path srcPath(path, subPool); + SVN_JNI_ERR(srcPath.error_occurred(), ); + + svn_client_ctx_t *ctx = context.getContext(NULL, subPool); + if (ctx == NULL) + return; + + SVN_JNI_ERR(svn_client_merge_reintegrate(srcPath.c_str(), + pegRevision.revision(), + intLocalPath.c_str(), + dryRun, NULL, ctx, + subPool.getPool()), ); +} diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_array.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_array.hpp new file mode 100644 index 0000000..0c92e7a --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_array.hpp @@ -0,0 +1,267 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_ARRAY_HPP +#define SVN_JAVAHL_JNIWRAPPER_ARRAY_HPP + +#include <cstring> +#include <string> + +#include "svn_string.h" + +#include "jni_env.hpp" +#include "../Pool.h" + +namespace Java { + +/** + * Wrapper class for Java byte arrays. + * + * @since New in 1.9. + */ +class ByteArray +{ +public: + /** + * Constructs a wrapper around an existing @a array. + */ + explicit ByteArray(Env env, jbyteArray array) + : m_env(env), + m_length(array ? m_env.GetArrayLength(array) : 0), + m_array(array) + {} + + /** + * Constructs a new, uninitialized array of size @a length. + */ + explicit ByteArray(Env env, jsize length) + : m_env(env), + m_length(length), + m_array(m_env.NewByteArray(m_length)) + {} + + /** + * Constructs a new array and wrapper from @a text. + */ + explicit ByteArray(Env env, const char* text) + : m_env(env), + m_length(jsize(::std::strlen(text))), + m_array(m_env.NewByteArray(m_length)) + { + ByteArray::MutableContents contents(*this); + ::memcpy(contents.data(), text, m_length); + } + + /** + * Constructs a new array and wrapper of size @a length + * from @a data. + */ + explicit ByteArray(Env env, const void* data, jsize length) + : m_env(env), + m_length(length), + m_array(m_env.NewByteArray(m_length)) + { + ByteArray::MutableContents contents(*this); + ::memcpy(contents.data(), data, m_length); + } + + /** + * Constructs a new array and wrapper from @a text. + */ + explicit ByteArray(Env env, const std::string& text) + : m_env(env), + m_length(jsize(text.size())), + m_array(m_env.NewByteArray(m_length)) + { + ByteArray::MutableContents contents(*this); + ::memcpy(contents.data(), text.c_str(), m_length); + } + + /** + * Returns the wrapped native array reference. + */ + jbyteArray get() const + { + return m_array; + } + + /** + * Returns the size of the wrapped array. + */ + jsize length() const + { + return m_length; + } + + /** + * Accessor class for the contents of the byte array. + * + * Objects of this class should be created within the scope where + * the raw data stored in the array must be manipulated. They will + * create an immutable mirror of the array contents. + */ + class Contents + { + public: + /** + * Constructs an immutable array contents accessor. + * + * Whilst the returned contents are themselves mutable, the + * destructor will discard any changes. + */ + explicit Contents(const ByteArray& array) + : m_array(array), + m_data(!array.m_array ? NULL + : array.m_env.GetByteArrayElements(array.m_array, NULL)) + {} + + /** + * Releases the array contents. + */ + ~Contents() + { + if (m_data) + { + const Env& env = m_array.m_env; + env.ReleaseByteArrayElements(m_array.m_array, m_data, JNI_ABORT); + } + } + + /** + * Returns the address of the immutable array contents. + * @note The data will @b not be NUL-terminated! + */ + const char* data() const + { + return reinterpret_cast<const char*>(m_data); + } + + /** + * Returns the size of the array contents. + */ + jsize length() const + { + return m_array.m_length; + } + + /** + * Copies the array contents to a NUL-terminated string allocated + * from @a result_pool. + */ + svn_string_t* get_string(apr_pool_t* result_pool) const + { + if (m_data) + return svn_string_ncreate(data(), m_array.m_length, result_pool); + return NULL; + } + + /** + * Copies the array contents to a NUL-terminated string allocated + * from @a result_pool. + */ + svn_string_t* get_string(const ::SVN::Pool& result_pool) const + { + return get_string(result_pool.getPool()); + } + + protected: + const ByteArray& m_array; + jbyte* m_data; + }; + + /** + * Accessor class for the contents of the byte array. + * + * Behaves like the #Contents class, but the mirrored contents are + * considered mutable and any changes made to them will be committed + * to the JVM. + */ + class MutableContents : protected Contents + { + public: + /** + * Constructs a mutable array contents accessor. + */ + explicit MutableContents(ByteArray& array) + : Contents(array) + {} + + /** + * Releases the array contents, committing changes to the JVM. + */ + ~MutableContents() + { + if (m_data) + { + // Prevent double-release by the Contents desctuctor + jbyte* const data = m_data; + m_data = NULL; + m_array.m_env.ReleaseByteArrayElements(m_array.m_array, data, 0); + } + } + /** + * Returns the mutable address of the array contents. + * @note The data will @b not be NUL-terminated! + */ + char* data() + { + return const_cast<char*>(Contents::data()); + } + + /** + * Returns the size of the array contents. + */ + jsize length() const + { + return Contents::length(); + } + + /** + * Copies the array contents to a NUL-terminated string allocated + * from @a result_pool. + */ + svn_string_t* get_string(apr_pool_t* result_pool) const + { + return Contents::get_string(result_pool); + } + + /** + * Copies the array contents to a NUL-terminated string allocated + * from @a result_pool. + */ + svn_string_t* get_string(const ::SVN::Pool& result_pool) const + { + return Contents::get_string(result_pool.getPool()); + } + }; + +private: + friend class Contents; + const Env m_env; + const jsize m_length; + const jbyteArray m_array; +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_ARRAY_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp new file mode 100644 index 0000000..e3671a2 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp @@ -0,0 +1,382 @@ +/** + * @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 + */ + +#include <cstring> +#include <memory> +#include <apr.h> + +#include "jni_env.hpp" +#include "jni_globalref.hpp" +#include "jni_exception.hpp" +#include "jni_object.hpp" +#include "jni_string.hpp" +#include "jni_array.hpp" +#include "jni_stack.hpp" + +#include "../JNIUtil.h" +bool initialize_jni_util(JNIEnv *env); + +#include "svn_private_config.h" + + +// Global library initializaiton + +/** + * Initializer function, called just after the JVM loads the native + * library. Stores the global JVM reference and creates the global + * class cache. + */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM* jvm, void*) +{ + ::Java::Env::static_init(jvm); + const ::Java::Env env; + + const apr_status_t status = apr_initialize(); + if (!status) + ::Java::ClassCache::create(); + else + { + char buf[2048]; + std::strcpy(buf, "Could not initialize APR: "); + const std::size_t offset = std::strlen(buf); + apr_strerror(status, buf + offset, sizeof(buf) - offset - 1); + env.ThrowNew(env.FindClass("java/lang/Error"), buf); + } + + // Initialize the old-style JavaHL infrastructure. + if (!initialize_jni_util(env.get()) && !env.ExceptionCheck()) + { + env.ThrowNew(env.FindClass("java/lang/LinkageError"), + "Native library initialization failed"); + } + + return JNI_VERSION_1_2; +} + +/** + * Cleanup function, called just before the JVM unloads the native + * library. Destroys the global class cache. + */ +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM*, void*) +{ + ::Java::ClassCache::destroy(); + apr_terminate(); +} + + +namespace Java { + +// class Java::Env + +::JavaVM* Env::m_jvm = NULL; +void Env::static_init(::JavaVM* jvm) +{ + m_jvm = jvm; +} + +const char* Env::error_create_global_reference() throw() +{ + return _("Could not create global reference"); +} + +const char* Env::error_get_contents_string() throw() +{ + return _("Could not get contents of Java String"); +} + +const char* Env::error_release_null_string() throw() +{ + return _("Could not release contents of a null String"); +} + +const char* Env::error_create_object_array() throw() +{ + return _("Could not create Object array"); +} + +namespace { +// The typed array error messages are always fatal, so allocating +// the buffer on the heap and never releasing it does not really +// constitute a memory leak. +const char* make_typed_error(const char* fmt, const char* type) throw() +{ + const apr_size_t bufsize = 512; + char *msg = new(std::nothrow) char[bufsize]; + apr_snprintf(msg, bufsize, fmt, type); + return msg; +} +} // anonymous namespace + +const char* Env::error_create_array(const char* type) throw() +{ + return make_typed_error(_("Could not create %sArray"), type); +} + +const char* Env::error_get_contents_array(const char* type) throw() +{ + return make_typed_error(_("Could not get %s array contents"), type); +} + +const char* Env::error_release_null_array(const char* type) throw() +{ + return make_typed_error( + _("Could not release contents of a null %sArray"), type); +} + +::JNIEnv* Env::env_from_jvm() +{ + if (m_jvm) + { + void *penv; + switch (m_jvm->GetEnv(&penv, JNI_VERSION_1_2)) + { + case JNI_OK: + return static_cast<JNIEnv*>(penv); + + case JNI_EDETACHED: + throw std::runtime_error( + _("Native thread is not attached to a Java VM")); + + case JNI_EVERSION: + throw std::runtime_error(_("Unsupported JNI version")); + + default: + throw std::runtime_error(_("Invalid JNI environment")); + } + } + throw std::logic_error(_("JavaVM instance was not initialized")); +} + +void Env::throw_java_out_of_memory(const char* message) const +{ + OutOfMemoryError(*this).raise(message); +} + +// class Java::LocalFrame + +const jint LocalFrame::DEFAULT_CAPACITY = 16; + + +// class Java::GlobalObject + +GlobalObject& GlobalObject::operator=(jobject that) +{ + this->~GlobalObject(); + return *new(this) GlobalObject(Env(), that); +} + +GlobalObject::~GlobalObject() +{ + if (m_obj) + Env().DeleteGlobalRef(m_obj); +} + + +// class Java::GlobalClass + +GlobalClass& GlobalClass::operator=(jclass that) +{ + this->~GlobalClass(); + return *new(this) GlobalClass(Env(), that); +} + + +// Class Java::Object + +const char* const Object::m_class_name = "java/lang/Object"; +Object::ClassImpl::~ClassImpl() {} + + +// Class Java::Class + +const char* const Class::m_class_name = "java/lang/Class"; +Class::ClassImpl::~ClassImpl() {} + +MethodID Class::m_mid_get_class; +MethodID Class::m_mid_get_name; + +void Class::static_init(Env env, jclass cls) +{ + m_mid_get_class = env.GetMethodID( + ClassCache::get_object(env)->get_class(), + "getClass", "()Ljava/lang/Class;"); + m_mid_get_name = env.GetMethodID( + cls, "getName", "()Ljava/lang/String;"); +} + +namespace{ +jobject get_class_of_object(Env env, jobject obj, jmethodID mid_get_class) +{ + if (!obj) + return NULL; + return env.CallObjectMethod(obj, mid_get_class); +} +} // anonymous namespace + +Class::Class(Env env, jobject obj) + : m_env(env), + m_jthis(get_class_of_object(env, obj, m_mid_get_class)) +{} + +Class::Class(const Object& obj) + : m_env(obj.get_env()), + m_jthis(get_class_of_object(obj.get_env(), obj.get(), m_mid_get_class)) +{} + +jstring Class::get_name() const +{ + if (!m_jthis) + return NULL; + return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_name)); +} + + +// Class Java::String + +const char* const String::m_class_name = "java/lang/String"; +String::ClassImpl::~ClassImpl() {} + +const char* String::strdup(apr_pool_t* pool) const +{ + return apr_pstrdup(pool, String::Contents(*this).c_str()); +} + +void String::MutableContents::set_value(const char* new_text) +{ + if (!m_new_text) + throw std::invalid_argument( + _("Cannot set String contents to null")); + if (m_text) + { + m_new_text = new_text; + m_length = jsize(::std::strlen(new_text)); + } + else + throw std::logic_error( + _("Cannot change the contents of a null String")); +} + +// class Java::Exception + +void Exception::throw_java_exception() const +{ + if (instantiated() + ? m_env.Throw(m_jthis) + : m_env.ThrowNew(m_class, NULL)) + throw std::runtime_error(_("Could not throw Java exception")); +} + +void Exception::throw_java_exception(const char* message) const +{ + if (m_env.ThrowNew(m_class, message)) + throw std::runtime_error(_("Could not throw Java exception")); +} + +jstring Exception::get_message() const +{ + if (instantiated()) + return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_message)); + throw std::logic_error(_("Could not get exception message:" + " Exception instance is not available")); +} + +const char* const Exception::m_class_name = "java/lang/Throwable"; +Exception::ClassImpl::~ClassImpl() {} + +MethodID Exception::m_mid_get_message; +void Exception::static_init(Env env, jclass cls) +{ + m_mid_get_message = env.GetMethodID( + cls, "getMessage", "()Ljava/lang/String;"); +} + +// Other exception class initializers + +const char* const RuntimeException::m_class_name = + "java/lang/RuntimeException"; + +const char* const NullPointerException::m_class_name = + "java/lang/NullPointerException"; + +const char* const OutOfMemoryError::m_class_name = + "java/lang/OutOfMemoryError"; + +const char* const IndexOutOfBoundsException::m_class_name = + "java/lang/IndexOutOfBoundsException"; +IndexOutOfBoundsException::ClassImpl::~ClassImpl() {} + +const char* const IOException::m_class_name = + "java/io/IOException"; + +const char* const IllegalArgumentException::m_class_name = + "java/lang/IllegalArgumentException"; + +const char *const NoSuchElementException::m_class_name = + "java/util/NoSuchElementException"; +NoSuchElementException::ClassImpl::~ClassImpl() {} + +// Implementation of jni_stack.hpp + +void handle_svn_error(Env env, ::svn_error_t* err) +{ + jthrowable cause = NULL; + + // If the exception being currently thrown was generated by the + // JavaHL bindings, then assume the error was propagated through + // native code and do not re-throw it. + if (env.ExceptionCheck()) + { + cause = env.ExceptionOccurred(); + if (env.IsInstanceOf( + cause, ClassCache::get_subversion_exception(env)->get_class())) + { + // XXX FIXME: Should really have a special error code + // specifically for propagating Java exceptions from + // callbacks through native code. + svn_error_clear(err); + throw SignalExceptionThrown(); + } + } + + // Make sure there's only a single exception in the environment. + if (cause) + env.ExceptionClear(); + + ::JNIUtil::handleSVNError(err, cause); + throw SignalExceptionThrown(); +} + +const char* unknown_cxx_exception_message() throw() +{ + return _("Caught unknown C++ exception"); +} + +svn_error_t* caught_java_exception_error(apr_status_t status) throw() +{ + return svn_error_create(status, JNIUtil::wrapJavaException(), + _("Java exception")); +} + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp new file mode 100644 index 0000000..a3d41b2 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_channel.cpp @@ -0,0 +1,257 @@ +/** + * @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 + */ + +#include <stdexcept> + +#include "jni_object.hpp" +#include "jni_array.hpp" +#include "jni_channel.hpp" + +#include "svn_private_config.h" + +namespace Java { + +namespace { +// Get the ByteBuffer's internal array. +jbyteArray get_array(Env env, jobject buffer, + const MethodID& mid_has_array, + const MethodID& mid_get_array) +{ + if (!env.CallBooleanMethod(buffer, mid_has_array)) + return NULL; + return jbyteArray(env.CallObjectMethod(buffer, mid_get_array)); +} + +// Get the offset in the ByteBuffer's array. NEVER call this function +// unless the buffer actually has an accessible array. +jint get_array_offset(Env env, jobject buffer, + const MethodID& mid_get_array_offset) +{ + return env.CallIntMethod(buffer, mid_get_array_offset); +} + +// Get the remaining space in a ByteBuffer. +jint get_remaining(Env env, jobject buffer, + const MethodID& mid_get_remaining) +{ + return env.CallIntMethod(buffer, mid_get_remaining); +} + +// Get the current position of a ByteBuffer. +jint get_position(Env env, jobject buffer, + const MethodID& mid_get_position) +{ + return env.CallIntMethod(buffer, mid_get_position); +} + +// Set the new position of a ByteBuffer. +void set_position(Env env, jobject buffer, + const MethodID& mid_set_position, + jint new_position) +{ + env.CallObjectMethod(buffer, mid_set_position, new_position); +} + +// Get byte array contents from a ByteBuffer. +void get_bytearray(Env env, jobject buffer, + const MethodID& mid_get_bytearray, + ByteArray& array, jint length = -1, jint offset = 0) +{ + env.CallObjectMethod( + buffer, mid_get_bytearray, array.get(), offset, + (length >= 0 ? length : (array.length() - offset))); +} + +// Put byte array contents into a ByteBuffer. +void put_bytearray(Env env, jobject buffer, + const MethodID& mid_put_bytearray, + ByteArray& array, jint length = -1, jint offset = 0) +{ + env.CallObjectMethod(buffer, mid_put_bytearray, + array.get(), offset, + (length >= 0 ? length : (array.length() - offset))); +} + +struct BadReaderWriter : public ChannelReader, ChannelWriter +{ + BadReaderWriter() {} + + virtual jint operator()(Env, void*, jint) + { + throw std::logic_error(_("Reading from write-only channel")); + } + + virtual jint operator()(Env, const void*, jint) + { + throw std::logic_error(_("Writing to read-only channel")); + } +} bad_reader_writer; + +} // anonymous namespace + + +ChannelReader& ByteChannel::m_null_reader = bad_reader_writer; +ChannelWriter& ByteChannel::m_null_writer = bad_reader_writer; + +const char* const ByteChannel::ByteBuffer::m_class_name = + "java/nio/ByteBuffer"; + +ByteChannel::ByteBuffer::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_has_array(env.GetMethodID(cls, "hasArray", "()Z")), + m_mid_get_array(env.GetMethodID(cls, "array", "()[B")), + m_mid_get_array_offset(env.GetMethodID(cls, "arrayOffset", "()I")), + m_mid_get_remaining(env.GetMethodID(cls, "remaining", "()I")), + m_mid_get_position(env.GetMethodID(cls, "position", "()I")), + m_mid_set_position(env.GetMethodID(cls, "position", + "(I)Ljava/nio/Buffer;")), + m_mid_get_bytearray(env.GetMethodID(cls, "get", + "([BII)Ljava/nio/ByteBuffer;")), + m_mid_put_bytearray(env.GetMethodID(cls, "put", + "([BII)Ljava/nio/ByteBuffer;")) +{} + +ByteChannel::ByteBuffer::ClassImpl::~ClassImpl() {} + +jint ByteChannel::read(jobject destination) +{ + const ByteBuffer::ClassImpl& bufimpl = ByteBuffer::impl(m_env); + + const jint remaining = get_remaining(m_env, destination, + bufimpl.m_mid_get_remaining); + if (!remaining) + { + // No space in the buffer; don't try to read anything. + return 0; + } + + const jint position = get_position(m_env, destination, + bufimpl.m_mid_get_position); + + jint bytes_read = 0; + void* data = m_env.GetDirectBufferAddress(destination); + if (data) + { + data = static_cast<char*>(data) + position; + bytes_read = m_reader(m_env, data, remaining); + } + else + { + // It was not a direct buffer ... see if it has an array. + jbyteArray raw_array = get_array(m_env, destination, + bufimpl.m_mid_has_array, + bufimpl.m_mid_get_array); + if (raw_array) + { + const jint array_offset = get_array_offset( + m_env, destination, + bufimpl.m_mid_get_array_offset); + ByteArray array(m_env, raw_array); + ByteArray::MutableContents contents(array); + data = contents.data(); + data = static_cast<char*>(data) + position + array_offset; + bytes_read = m_reader(m_env, data, remaining); + } + } + if (data) + { + if (bytes_read > 0) + set_position(m_env, destination, + bufimpl.m_mid_set_position, + position + bytes_read); + return bytes_read; + } + + // No accessible array, either. Oh well. Create a byte array and + // push it into the buffer. + ByteArray array(m_env, remaining); + ByteArray::MutableContents contents(array); + bytes_read = m_reader(m_env, contents.data(), contents.length()); + if (bytes_read > 0) + put_bytearray(m_env, destination, + bufimpl.m_mid_put_bytearray, + array, bytes_read); + return bytes_read; +} + +jint ByteChannel::write(jobject source) +{ + const ByteBuffer::ClassImpl& bufimpl = ByteBuffer::impl(m_env); + + const jint remaining = get_remaining(m_env, source, + bufimpl.m_mid_get_remaining); + if (!remaining) + { + // No data in the buffer; don't try to write anything. + return 0; + } + + const jint position = get_position(m_env, source, + bufimpl.m_mid_get_position); + + jint bytes_written = 0; + const void* data = m_env.GetDirectBufferAddress(source); + if (data) + { + data = static_cast<const char*>(data) + position; + bytes_written = m_writer(m_env, data, remaining); + } + else + { + // It was not a direct buffer ... see if it has an array. + jbyteArray raw_array = get_array(m_env, source, + bufimpl.m_mid_has_array, + bufimpl.m_mid_get_array); + if (raw_array) + { + const jint array_offset = get_array_offset( + m_env, source, + bufimpl.m_mid_get_array_offset); + const ByteArray array(m_env, raw_array); + ByteArray::Contents contents(array); + data = contents.data(); + data = static_cast<const char*>(data) + position + array_offset; + bytes_written = m_writer(m_env, data, remaining); + } + } + if (data) + { + if (bytes_written > 0) + set_position(m_env, source, + bufimpl.m_mid_set_position, + position + bytes_written); + return bytes_written; + } + + // No accessible array, either. Oh well. Get an array from the + // buffer and read data from that. + ByteArray array(m_env, remaining); + get_bytearray(m_env, source, + bufimpl.m_mid_get_bytearray, + array); + ByteArray::Contents contents(array); + bytes_written = m_writer(m_env, contents.data(), contents.length()); + return bytes_written; +} + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp new file mode 100644 index 0000000..7af5783 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_channel.hpp @@ -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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_CHANNEL_HPP +#define SVN_JAVAHL_JNIWRAPPER_CHANNEL_HPP + +#include "jni_env.hpp" + +namespace Java { + +/** + * Abstract base class for implementing channel read method internals. + * + * @since New in 1.9. + */ +struct ChannelReader +{ + /** + * Reads at most @a length bytes into @a buffer, returning the + * number of bytes read (which may be zero) or -1 if at + * end-of-stream. + */ + virtual jint operator()(Env env, void* buffer, jint length) = 0; +}; + +/** + * Abstract base class for implementing channel write method internals. + * + * @since New in 1.9. + */ +struct ChannelWriter +{ + /** + * Writes at exactly @a length bytes from @a buffer, returning the + * number of bytes written (which may be zero). + */ + virtual jint operator()(Env env, const void* buffer, jint length) = 0; +}; + + +/** + * Wrapper for @c java.nio.channels.ByteChannel. Unlike most wrappers, + * this one does not actually represent a ByteChannel object. The + * assumption is that the native implementation will want to implement + * the read and write methods, not invoke them. + * + * Also serves as the (protected) base of the Readable- and + * WritableByteChannel interfaces; this is for purposes of code + * sharing only. We're not interested in replicating Java's class + * hierarchy here. + * + * @since New in 1.9. + */ +class ByteChannel +{ +public: + /** + * Constructs a wrapper for @a channel with @a reader and @a writer + * as the read and write method implementations. + */ + explicit ByteChannel(Env env, ChannelReader& reader, ChannelWriter& writer) + : m_env(env), + m_reader(reader), + m_writer(writer) + {} + + /** + * Reads bytes into @a destination, which must be a + * @c java.nio.ByteBuffer instance, from #m_reader. + * @return the number of bytes read, or -1 if at end-of-stream. + */ + jint read(jobject destination); + + /** + * Writes bytes from @a source, which must be a + * @c java.nio.ByteBuffer instance, to #m_writer. + * @return the number of bytes written. + */ + jint write(jobject source); + +protected: + /** + * Constructor used by read-only subclasses. + */ + explicit ByteChannel(Env env, ChannelReader& reader) + : m_env(env), + m_reader(reader), + m_writer(m_null_writer) + {} + + /** + * Constructor used by write-only subclasses. + */ + explicit ByteChannel(Env env, ChannelWriter& writer) + : m_env(env), + m_reader(m_null_reader), + m_writer(writer) + {} + +private: + Env m_env; + ChannelReader& m_reader; + ChannelWriter& m_writer; + + static ChannelReader& m_null_reader; + static ChannelWriter& m_null_writer; + + friend class ClassCacheImpl; + + // Private references for the java.nio.ByteBuffer class. + struct ByteBuffer + { + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_has_array; + const MethodID m_mid_get_array; + const MethodID m_mid_get_array_offset; + const MethodID m_mid_get_remaining; + const MethodID m_mid_get_position; + const MethodID m_mid_set_position; + const MethodID m_mid_get_bytearray; + const MethodID m_mid_put_bytearray; + }; + + static const char* const m_class_name; + static const ClassImpl& impl(Env env) + { + return *dynamic_cast<const ClassImpl*>( + ClassCache::get_byte_buffer(env)); + } + }; +}; + + +/** + * Wrapper for @c java.nio.channels.ReadableByteChannel. + * + * @since New in 1.9. + */ +class ReadableByteChannel : protected ByteChannel +{ +public: + /** + * Constructs a wrapper for @a channel with @a reader the read + * method implementation. + */ + explicit ReadableByteChannel(Env env, ChannelReader& reader) + : ByteChannel(env, reader) + {} + + /** + * Reads bytes into @a destination, which must be a + * @c java.nio.ByteBuffer instance, from #m_reader. + * @return the number of bytes read, or -1 if at end-of-stream. + */ + jint read(jobject destination) + { + return ByteChannel::read(destination); + } +}; + + +/** + * Wrapper @c java.nio.channels.WritableByteChannel. + * + * @since New in 1.9. + */ +class WritableByteChannel : protected ByteChannel +{ +public: + /** + * Constructs a wrapper for @a channel with @a writer as the write + * method implementation. + */ + explicit WritableByteChannel(Env env, ChannelWriter& writer) + : ByteChannel(env, writer) + {} + + /** + * Writes bytes from @a source, which must be a + * @c java.nio.ByteBuffer instance, to #m_writer. + * @return the number of bytes written. + */ + jint write(jobject source) + { + return ByteChannel::write(source); + } +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_CHANNEL_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp new file mode 100644 index 0000000..9e533db --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_class_cache.cpp @@ -0,0 +1,326 @@ +/** + * @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 + */ + +#include <stdexcept> + +#include <apr_atomic.h> + +#define SVN_JAVAHL_JNIWRAPPER_LOG(expr) +#include "jni_env.hpp" +#include "jni_globalref.hpp" +#include "jni_exception.hpp" +#include "jni_object.hpp" +#include "jni_string.hpp" + +#include "jni_channel.hpp" +#include "jni_io_stream.hpp" +#include "jni_list.hpp" +#include "jni_string_map.hpp" + +#include "../SubversionException.hpp" +#include "../AuthnCallback.hpp" +#include "../Credential.hpp" +#include "../ExternalItem.hpp" +#include "../EditorCallbacks.hpp" + +namespace +{ +/* This class behaves like a dumbed-down std:auto_ptr, but it + implements atomic access and modification of the wrapped + pointer. */ +class ClassImplPtr +{ + typedef ::Java::Object::ClassImpl ClassImpl; + + public: + /* Default constructor; initializes the wrapped pointer to NULL */ + explicit ClassImplPtr() + : m_ptr(NULL) + {} + + /* Initializing constructor; sets the wrapped pointer to PTR */ + explicit ClassImplPtr(ClassImpl* ptr) + : m_ptr(ptr) + {} + + /* Destructor deletes the object and resets the wrapped pointer to NULL. */ + ~ClassImplPtr() + { + delete static_cast<ClassImpl*>( + apr_atomic_casptr(&m_ptr, NULL, get())); + } + + /* Sets the wrapped pointer to PTR iff it is NULL, and returns the + old value. */ + ClassImpl* test_and_set(ClassImpl* ptr) + { + return static_cast<ClassImpl*>( + apr_atomic_casptr(&m_ptr, ptr, NULL)); + } + + /* Returns the current value of the the wrapped pointer. */ + ClassImpl* get() const + { + return static_cast<ClassImpl*>( + apr_atomic_casptr(&m_ptr, NULL, NULL)); + } + +private: + // Non-copyable + ClassImplPtr(const ClassImplPtr&); + ClassImplPtr& operator=(const ClassImplPtr&); + + mutable volatile void* m_ptr; +}; +} // anonymous namespace + + +namespace Java { + +class ClassCacheImpl +{ + + friend class ClassCache; + + // We only statically initialize a few of the common class wrappers. + explicit ClassCacheImpl(Env env) : + +#define JNIWRAPPER_INIT_CACHED_CLASS(M, C) \ + m_impl_##M(new C::ClassImpl(env, env.FindClass(C::m_class_name))) + + JNIWRAPPER_INIT_CACHED_CLASS(object, Object), + JNIWRAPPER_INIT_CACHED_CLASS(classtype, Class), + JNIWRAPPER_INIT_CACHED_CLASS(throwable, Exception), + JNIWRAPPER_INIT_CACHED_CLASS(string, String) +#undef JNIWRAPPER_INIT_CACHED_CLASS + {} + + // We can't do this in the constructor above, because the satic + // initializers will expect that ClassCache::m_impl is already set; + // that doesn't happen until the constructor returns. + void static_init(Env env) + { +#define JNIWRAPPER_STATIC_CACHED_CLASS(M, C) \ + C::static_init(env, m_impl_##M->get_class()) + + // No-op JNIWRAPPER_STATIC_CACHED_CLASS(object, Object); + JNIWRAPPER_STATIC_CACHED_CLASS(classtype, Class); + JNIWRAPPER_STATIC_CACHED_CLASS(throwable, Exception); + // No-op JNIWRAPPER_STATIC_CACHED_CLASS(string, String); +#undef JNIWRAPPER_STATIC_CACHED_CLASS + } + + // The statically initialized calss wrappers are always defined and + // therefore do not need atomic access. +#define JNIWRAPPER_DEFINE_CACHED_CLASS(M, C) \ + std::auto_ptr<Object::ClassImpl> m_impl_##M; \ + const Object::ClassImpl* get_##M(Env) \ + { \ + return m_impl_##M.get(); \ + } + + JNIWRAPPER_DEFINE_CACHED_CLASS(object, Object) + JNIWRAPPER_DEFINE_CACHED_CLASS(classtype, Class) + JNIWRAPPER_DEFINE_CACHED_CLASS(throwable, Exception) + JNIWRAPPER_DEFINE_CACHED_CLASS(string, String) +#undef JNIWRAPPER_DEFINE_CACHED_CLASS + + // All other class wrappers must be atomically initialized +#define JNIWRAPPER_DEFINE_CACHED_CLASS(M, C) \ + ClassImplPtr m_impl_##M; \ + const Object::ClassImpl* get_##M(Env env) \ + { \ + Object::ClassImpl* pimpl = m_impl_##M.get(); \ + if (!pimpl) \ + { \ + std::auto_ptr<Object::ClassImpl> tmp( \ + new C::ClassImpl( \ + env, env.FindClass(C::m_class_name))); \ + pimpl = m_impl_##M.test_and_set(tmp.get()); \ + if (!pimpl) \ + pimpl = tmp.release(); \ + } \ + return pimpl; \ + } + + JNIWRAPPER_DEFINE_CACHED_CLASS(exc_index_out_of_bounds, + IndexOutOfBoundsException); + JNIWRAPPER_DEFINE_CACHED_CLASS(exc_no_such_element, + NoSuchElementException); + + JNIWRAPPER_DEFINE_CACHED_CLASS(iterator, BaseIterator); + + JNIWRAPPER_DEFINE_CACHED_CLASS(list, BaseImmutableList); + JNIWRAPPER_DEFINE_CACHED_CLASS(array_list, BaseList); + + JNIWRAPPER_DEFINE_CACHED_CLASS(map, BaseImmutableMap); + JNIWRAPPER_DEFINE_CACHED_CLASS(set, BaseImmutableMap::Set); + JNIWRAPPER_DEFINE_CACHED_CLASS(map_entry, BaseImmutableMap::Entry); + JNIWRAPPER_DEFINE_CACHED_CLASS(hash_map, BaseMap); + + JNIWRAPPER_DEFINE_CACHED_CLASS(input_stream, InputStream); + JNIWRAPPER_DEFINE_CACHED_CLASS(output_stream, OutputStream); + + JNIWRAPPER_DEFINE_CACHED_CLASS(byte_buffer, + ByteChannel::ByteBuffer); + + JNIWRAPPER_DEFINE_CACHED_CLASS(subversion_exception, + ::JavaHL::SubversionException); + + JNIWRAPPER_DEFINE_CACHED_CLASS(authn_cb, + ::JavaHL::AuthnCallback); + JNIWRAPPER_DEFINE_CACHED_CLASS(authn_result, + ::JavaHL::AuthnCallback::AuthnResult); + JNIWRAPPER_DEFINE_CACHED_CLASS(authn_ssl_server_cert_failures, + ::JavaHL::AuthnCallback::SSLServerCertFailures); + JNIWRAPPER_DEFINE_CACHED_CLASS(authn_ssl_server_cert_info, + ::JavaHL::AuthnCallback::SSLServerCertInfo); + JNIWRAPPER_DEFINE_CACHED_CLASS(user_passwd_cb, + ::JavaHL::UserPasswordCallback); + + JNIWRAPPER_DEFINE_CACHED_CLASS(credential, + ::JavaHL::Credential); + JNIWRAPPER_DEFINE_CACHED_CLASS(credential_kind, + ::JavaHL::Credential::Kind); + + JNIWRAPPER_DEFINE_CACHED_CLASS(external_item, + ::JavaHL::ExternalItem); + + JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_base_cb, + ::JavaHL::ProvideBaseCallback); + JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_base_cb_ret, + ::JavaHL::ProvideBaseCallback::ReturnValue); + JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_props_cb, + ::JavaHL::ProvidePropsCallback); + JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_props_cb_ret, + ::JavaHL::ProvidePropsCallback::ReturnValue); + JNIWRAPPER_DEFINE_CACHED_CLASS(editor_get_kind_cb, + ::JavaHL::GetNodeKindCallback); +#undef JNIWRAPPER_DEFINE_CACHED_CLASS +}; + + +ClassCacheImpl* ClassCache::m_impl = NULL; + +void ClassCache::create() +{ + const char* exception_message = NULL; + + try + { + const Env env; + m_impl = new ClassCacheImpl(env); + m_impl->static_init(env); + } + catch (const SignalExceptionThrown&) + {} + catch (const std::exception& ex) + { + exception_message = ex.what(); + } + catch (...) + { + exception_message = "Caught unknown C++ exception"; + } + + // Do not throw any more exceptions from here, so use the raw environment. + ::JNIEnv* const jenv = Env().get(); + if (exception_message || jenv->ExceptionCheck()) + { + jobject cause = jenv->ExceptionOccurred(); + if (cause) + jenv->ExceptionClear(); + + const jclass rtx = jenv->FindClass("java/lang/RuntimeException"); + const jmethodID ctor = jenv->GetMethodID(rtx, "<init>", + "(Ljava/lang/String;" + "Ljava/lang/Throwable;)V"); + if (!cause && exception_message) + { + const jstring msg = jenv->NewStringUTF(exception_message); + cause = jenv->NewObject(rtx, ctor, msg, jthrowable(0)); + } + const jstring reason = + jenv->NewStringUTF("JavaHL native library initialization failed"); + const jobject exception = jenv->NewObject(rtx, ctor, reason, cause); + jenv->Throw(jthrowable(exception)); + } +} + +void ClassCache::destroy() +{ + ClassCacheImpl* const pimpl = m_impl; + m_impl = NULL; + delete pimpl; +} + +#define JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(M) \ +const Object::ClassImpl* ClassCache::get_##M(Env env) \ +{ \ + return m_impl->get_##M(env); \ +} + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(object); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(classtype); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(throwable); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(string); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(exc_index_out_of_bounds); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(exc_no_such_element); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(list); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(array_list); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(map); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(set); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(iterator); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(map_entry); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(hash_map); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(input_stream); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(output_stream); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(byte_buffer); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(subversion_exception); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_cb); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_result); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_ssl_server_cert_failures); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_ssl_server_cert_info); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(user_passwd_cb); + + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(credential); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(credential_kind); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(external_item); + +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_base_cb); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_base_cb_ret); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_props_cb); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_props_cb_ret); +JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_get_kind_cb); +#undef JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp new file mode 100644 index 0000000..9d73653 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_env.hpp @@ -0,0 +1,682 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_ENV_HPP +#define SVN_JAVAHL_JNIWRAPPER_ENV_HPP + +#include <jni.h> +#include <cstdarg> +#include <stdexcept> + +#ifdef SVN_JAVAHL_DEBUG +# ifndef SVN_JAVAHL_JNIWRAPPER_LOG +# include <iostream> +# define SVN_JAVAHL_JNIWRAPPER_LOG(expr) \ + (std::cerr << expr << std::endl) +# endif // SVN_JAVAHL_JNIWRAPPER_LOG +#else +# define SVN_JAVAHL_JNIWRAPPER_LOG(expr) +#endif // SVN_JAVAHL_DEBUG + +namespace Java { + +/** + * A C++ exception object for signalling that a Java exception has + * been thrown. + * + * Thrown to unwind the stack while avoiding code clutter when a Java + * exception is detected in the JNI environment. + * + * @since New in 1.9. + */ +class SignalExceptionThrown {}; + +/** + * Auto-initializing proxy for the JNI method ID. + * + * Behaves like a @c jmethodID but automatically initializes to @c NULL. + * + * @since New in 1.9. + */ +class MethodID +{ +public: + MethodID() + : m_mid(NULL) + {} + + MethodID(jmethodID mid) + : m_mid(mid) + {} + + MethodID(const MethodID& that) + : m_mid(that.m_mid) + {} + + MethodID& operator=(jmethodID mid) + { + m_mid = mid; + return *this; + } + + MethodID& operator=(const MethodID& that) + { + m_mid = that.m_mid; + return *this; + } + + operator jmethodID() const + { + return m_mid; + } + + operator bool() const + { + return (NULL != m_mid); + } + +private: + jmethodID m_mid; +}; + + +/** + * Auto-initializing proxy for the JNI field ID. + * + * Behaves like a @c jfieldID but automatically initializes to @c NULL. + * + * @since New in 1.9. + */ +class FieldID +{ +public: + FieldID() + : m_fid(NULL) + {} + + FieldID(jfieldID mid) + : m_fid(mid) + {} + + FieldID(const FieldID& that) + : m_fid(that.m_fid) + {} + + FieldID& operator=(jfieldID fid) + { + m_fid = fid; + return *this; + } + + FieldID& operator=(const FieldID& that) + { + m_fid = that.m_fid; + return *this; + } + + operator jfieldID() const + { + return m_fid; + } + + operator bool() const + { + return (NULL != m_fid); + } + +private: + jfieldID m_fid; +}; + +/** + * Encapsulation of a JNI environment reference. + * + * This class wraps all (relevant) JNI functions and checks for thrown + * exceptions, so that call sites don't have to be cluttered with KNI + * exception checks. + * + * @since New in 1.9. + */ +class Env +{ +public: + /** + * Constructs an environment object, retrieving the JNI environment + * reference from the global JVM reference. + */ + explicit Env() + : m_env(env_from_jvm()) + {} + + /** + * Given a JNI renvironment reference, constructs an environment object. + */ + explicit Env(::JNIEnv* env) + : m_env(env) + {} + + /** + * Returns the wrapped JNI environment reference. + * + * This method is present for compatibility with the old-style + * native implmentation that needs the raw pointer, and will be + * removed presently. Do not use it in new-style code. + */ + ::JNIEnv* get() const + { + SVN_JAVAHL_JNIWRAPPER_LOG("Warning: Direct access to JNIEnv at " + << __FILE__ << ":" << __LINE__); + return m_env; + } + + /** Wrapped JNI function. */ + jobject NewGlobalRef(jobject obj) const + { + jobject ret = m_env->NewGlobalRef(obj); + check_java_exception(); + if (!ret) + throw_java_out_of_memory(error_create_global_reference()); + return ret; + } + + /** Wrapped JNI function. */ + void DeleteGlobalRef(jobject obj) const throw() + { + m_env->DeleteGlobalRef(obj); + } + + /** Wrapped JNI function. */ + void PushLocalFrame(jint capacity) const + { + if (0 > m_env->PushLocalFrame(capacity)) + throw_java_exception(); + } + + /** Wrapped JNI function. */ + void PopLocalFrame() const throw() + { + m_env->PopLocalFrame(NULL); + } + + /** Wrapped JNI function. */ + jint Throw(jthrowable exc) const throw() + { + return m_env->Throw(exc); + } + + /** Wrapped JNI function. */ + jint ThrowNew(jclass cls, const char* message) const throw() + { + return m_env->ThrowNew(cls, message); + } + + /** Wrapped JNI function. */ + jboolean ExceptionCheck() const throw() + { + return m_env->ExceptionCheck(); + } + + /** Wrapped JNI function. */ + jthrowable ExceptionOccurred() const throw() + { + return m_env->ExceptionOccurred(); + } + + /** Wrapped JNI function. */ + void ExceptionClear() const throw() + { + m_env->ExceptionClear(); + } + + /** Wrapped JNI function. */ + jclass FindClass(const char* name) const + { + jclass cls = m_env->FindClass(name); + check_java_exception(); + return cls; + } + + /** Wrapped JNI function. */ + jobject NewObject(jclass cls, jmethodID ctor, ...) const + { + std::va_list args; + va_start(args, ctor); + jobject obj = m_env->NewObjectV(cls, ctor, args); + va_end(args); + check_java_exception(); + return obj; + } + + /** Wrapped JNI function. */ + jclass GetObjectClass(jobject obj) const + { + jclass cls = m_env->GetObjectClass(obj); + check_java_exception(); + return cls; + } + + /** Wrapped JNI function. */ + jboolean IsInstanceOf(jobject obj, jclass cls) const throw() + { + return m_env->IsInstanceOf(obj, cls); + } + + /** Wrapped JNI function. */ + jmethodID GetMethodID(jclass cls, const char* name, const char* sig) const + { + jmethodID mid = m_env->GetMethodID(cls, name, sig); + check_java_exception(); + return mid; + } + + /** Wrapped JNI function. */ + jmethodID GetStaticMethodID(jclass cls, const char* name, + const char* sig) const + { + jmethodID mid = m_env->GetStaticMethodID(cls, name, sig); + check_java_exception(); + return mid; + } + + /** Wrapped JNI function. */ + jfieldID GetFieldID(jclass cls, const char* name, const char* sig) const + { + jfieldID fid = m_env->GetFieldID(cls, name, sig); + check_java_exception(); + return fid; + } + + /** Wrapped JNI function. */ + jfieldID GetStaticFieldID(jclass cls, const char* name, + const char* sig) const + { + jfieldID fid = m_env->GetStaticFieldID(cls, name, sig); + check_java_exception(); + return fid; + } + + /** Wrapped JNI function. */ + jstring NewStringUTF(const char* text) const + { + if (!text) + return NULL; + + jstring str = m_env->NewStringUTF(text); + check_java_exception(); + return str; + } + + /** Wrapped JNI function. */ + jsize GetStringLength(jstring str) const + { + jsize len = m_env->GetStringLength(str); + check_java_exception(); + return len; + } + + /** Wrapped JNI function. */ + jsize GetStringUTFLength(jstring str) const + { + jsize len = m_env->GetStringUTFLength(str); + check_java_exception(); + return len; + } + + /** Wrapped JNI function. */ + const char* GetStringUTFChars(jstring str, jboolean* is_copy) const + { + if (!str) + return NULL; + + const char* text = m_env->GetStringUTFChars(str, is_copy); + check_java_exception(); + if (!text) + throw_java_out_of_memory(error_get_contents_string()); + return text; + } + + /** Wrapped JNI function. */ + void ReleaseStringUTFChars(jstring str, const char* new_text) const + { + if (!str) + throw std::logic_error(error_release_null_string()); + m_env->ReleaseStringUTFChars(str, new_text); + } + + /** Wrapped JNI function. */ + void CallVoidMethod(jobject obj, jmethodID mid, ...) const + { + std::va_list args; + va_start(args, mid); + m_env->CallObjectMethodV(obj, mid, args); + va_end(args); + check_java_exception(); + } + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(T, N) \ + T Call##N##Method(jobject obj, jmethodID mid, ...) const \ + { \ + std::va_list args; \ + va_start(args, mid); \ + T ret = m_env->Call##N##MethodV(obj, mid, args); \ + va_end(args); \ + check_java_exception(); \ + return ret; \ + } + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jobject, Object) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jint, Int) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_CALL_X_METHOD + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(T, N) \ + T CallStatic##N##Method(jclass obj, jmethodID mid, ...) const \ + { \ + std::va_list args; \ + va_start(args, mid); \ + T ret = m_env->CallStatic##N##MethodV(obj, mid, args); \ + va_end(args); \ + check_java_exception(); \ + return ret; \ + } + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jobject, Object) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jint, Int) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_CALL_STATIC_X_METHOD + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(T, N) \ + T Get##N##Field(jobject obj, jfieldID fid) const \ + { \ + T ret = m_env->Get##N##Field(obj, fid); \ + check_java_exception(); \ + return ret; \ + } + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jobject, Object) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jint, Int) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_GET_X_FIELD + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(T, N) \ + void Set##N##Field(jobject obj, jfieldID fid, T val) const \ + { \ + m_env->Set##N##Field(obj, fid, val); \ + check_java_exception(); \ + } + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jobject, Object) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jint, Int) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_SET_X_FIELD + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(T, N) \ + T GetStatic##N##Field(jclass cls, jfieldID fid) const \ + { \ + T ret = m_env->GetStatic##N##Field(cls, fid); \ + check_java_exception(); \ + return ret; \ + } + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jobject, Object) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jint, Int) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_GET_STATIC_X_FIELD + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(T, N) \ + void SetStatic##N##Field(jclass cls, jfieldID fid, T val) const \ + { \ + m_env->SetStatic##N##Field(cls, fid, val); \ + check_java_exception(); \ + } + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jobject, Object) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jint, Int) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_SET_STATIC_X_FIELD + + /** Wrapped JNI function. */ + jsize GetArrayLength(jarray array) const + { + if (!array) + return 0; + return m_env->GetArrayLength(array); + } + + /** Wrapped JNI function. */ + jobjectArray NewObjectArray(jsize length, jclass cls, jobject init) const + { + jobjectArray array = m_env->NewObjectArray(length, cls, init); + if (!array) + throw_java_out_of_memory(error_create_object_array()); + return array; + } + + /** Wrapped JNI function. */ + jobject GetObjectArrayElement(jobjectArray array, jsize index) const + { + jobject obj = m_env->GetObjectArrayElement(array, index); + check_java_exception(); + return obj; + } + + /** Wrapped JNI function. */ + void SetObjectArrayElement(jobjectArray array, + jsize index, jobject value) const + { + m_env->SetObjectArrayElement(array, index, value); + check_java_exception(); + } + + /** Boilerplate generator for wrapped JNI functions. */ +#define SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(T, N) \ + T##Array New##N##Array(jsize length) const \ + { \ + T##Array array = m_env->New##N##Array(length); \ + if (!array) \ + throw_java_out_of_memory(error_create_array(#T)); \ + return array; \ + } \ + T* Get##N##ArrayElements(T##Array array, jboolean* is_copy) const \ + { \ + if (!array) \ + return NULL; \ + \ + T* data = m_env->Get##N##ArrayElements(array, is_copy); \ + check_java_exception(); \ + if (!data) \ + throw_java_out_of_memory(error_get_contents_array(#N)); \ + return data; \ + } \ + void Release##N##ArrayElements(T##Array array, T* data, jint mode) const \ + { \ + if (!array) \ + throw std::logic_error(error_release_null_array(#T)); \ + m_env->Release##N##ArrayElements(array, data, mode); \ + } + + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jboolean, Boolean) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jbyte, Byte) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jchar, Char) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jshort, Short) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jint, Int) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jlong, Long) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jfloat, Float) + SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY(jdouble, Double) +#undef SVN_JAVAHL_JNIWRAPPER_PRIMITIVE_TYPE_ARRAY + + /** Wrapped JNI function. */ + void* GetDirectBufferAddress(jobject buffer) const + { + void* const addr = m_env->GetDirectBufferAddress(buffer); + check_java_exception(); + return addr; + } + +private: + ::JNIEnv* m_env; + static ::JavaVM* m_jvm; + static ::JNIEnv* env_from_jvm(); + + void throw_java_exception() const + { + throw SignalExceptionThrown(); + } + + void check_java_exception() const + { + if (m_env->ExceptionCheck()) + throw SignalExceptionThrown(); + } + + void throw_java_out_of_memory(const char* message) const; + + // We cannont use svn_private_config.h in a header, so we move the + // actual message translations into the implementation file. + static const char* error_create_global_reference() throw(); + static const char* error_get_contents_string() throw(); + static const char* error_release_null_string() throw(); + + static const char* error_create_object_array() throw(); + static const char* error_create_array(const char* type) throw(); + static const char* error_get_contents_array(const char* type) throw(); + static const char* error_release_null_array(const char* type) throw(); + +public: + // This static initializer must only be called by JNI_OnLoad + static void static_init(::JavaVM*); +}; + + +/** + * Encapsulation of a JNI local frame. + * + * Used within loop bodies to limit the proliferation of local + * references, or anywhere else where such references should be + * pre-emptively discarded. + * + * @since New in 1.9. + */ +class LocalFrame +{ + static const jint DEFAULT_CAPACITY; + +public: + /** + * Constructs a local frame, retrieving the JNI environment + * reference from the global JVM reference. + */ + explicit LocalFrame() + : m_env(Env()) + { + m_env.PushLocalFrame(DEFAULT_CAPACITY); + } + + /** + * Given a JNI renvironment reference, constructs a local frame. + */ + explicit LocalFrame(Env env) + : m_env(env) + { + m_env.PushLocalFrame(DEFAULT_CAPACITY); + } + + /** + * Constructs a local frame with the given initial @a capacity, + * retrieving the JNI environment reference from the global JVM + * reference. + */ + explicit LocalFrame(jint capacity) + : m_env(Env()) + { + m_env.PushLocalFrame(capacity); + } + + /** + * Given a JNI renvironment reference, constructs a local frame with + * the given initial @a capacity. + */ + explicit LocalFrame(Env env, jint capacity) + : m_env(env) + { + m_env.PushLocalFrame(capacity); + } + + ~LocalFrame() + { + m_env.PopLocalFrame(); + } + + /** + * Returns the stored enviromnent object. + */ + Env get_env() const + { + return m_env; + } + +private: + const Env m_env; + LocalFrame(const LocalFrame&); + LocalFrame& operator=(const LocalFrame&); +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_ENV_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp new file mode 100644 index 0000000..2062275 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_exception.hpp @@ -0,0 +1,364 @@ +/** + * @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 + */ + + +#ifndef SVN_JAVAHL_JNIWRAPPER_EXCEPTION_HPP +#define SVN_JAVAHL_JNIWRAPPER_EXCEPTION_HPP + +#include "jni_env.hpp" +#include "jni_object.hpp" + +namespace Java { + +/** + * Base class for all exception generators, and generator class for + * exceptions of type @c java.lang.Throwable. + * + * The associated JNI class reference is stored for the lifetime of + * the JVM in the global class cache. + * + * @since New in 1.9. + */ +class Exception +{ +public: + /** + * Constructs a wrapper for the @c jthrowable object @a exc. + */ + explicit Exception(Env env, jthrowable exc) + : m_env(env), + m_jthis(exc), + m_class(env.GetObjectClass(exc)) + {} + + /** + * Raises a Java exception of the concrete class, and throws a + * native exception at the same time. + * + * It is an error to call this method if an existing @c jthrowable + * object was wrapped. + */ + void raise() const + { + throw_java_exception(); + throw SignalExceptionThrown(); + } + + /** + * Raises a Java exception of the concrete class with the givem + * @a message, and throws a native exception at the same time. + * + * It is an error to call this method if an existing @c jthrowable + * object was wrapped. + */ + void raise(const char* message) const + { + throw_java_exception(message); + throw SignalExceptionThrown(); + } + + /** + * Raises a Java exception of the concrete class, but does not throw + * a native exception. + * + * It is an error to call this method if an existing @c jthrowable + * object was wrapped. + */ + void throw_java_exception() const; + + /** + * Raises a Java exception of the concrete class with the given + * @a message, but does not throw a native exception. + * + * It is an error to call this method if an existing @c jthrowable + * object was wrapped. + */ + void throw_java_exception(const char* message) const; + + /** + * Checks if an existing @c jthrowable object was wrapped. + */ + bool instantiated() const + { + return (m_jthis != NULL); + } + + /** + * Returns the wrapped @c jthrowable object. + */ + jthrowable throwable() const + { + return m_jthis; + } + + /** + * Wrapper for the Java method @c getMessage(). + * Only valid if an existing @c jthrowable object was wrapped. + */ + jstring get_message() const; + + + /** + * Returns the wrapped exception instance. + */ + jobject get() const + { + return m_jthis; + } + + /** + * Returns the wrapped exception class. + */ + jclass get_class() const + { + return m_class; + } + + /** + * Returns the wrapped enviromnment reference. + */ + Env get_env() const + { + return m_env; + } + +protected: + /** + * Constructs an exception generator with the concrete class + * @a class_name. + */ + explicit Exception(Env env, const char* class_name) + : m_env(env), + m_jthis(NULL), + m_class(env.FindClass(class_name)) + {} + + /** + * Constructs an exception generator with the concrete class @a cls. + */ + explicit Exception(Env env, const Object::ClassImpl* impl) + : m_env(env), + m_jthis(NULL), + m_class(impl->get_class()) + {} + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls) + {} + + public: + virtual ~ClassImpl(); + }; + + const Env m_env; + jthrowable m_jthis; + jclass m_class; + + friend class ClassCacheImpl; + static void static_init(Env env, jclass cls); + static const char* const m_class_name; + static MethodID m_mid_get_message; +}; + +/** + * Generator class for exceptions of type @c java.lang.RuntimeException. + * + * @since New in 1.9. + */ +class RuntimeException : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit RuntimeException(Env env) + : Exception(env, m_class_name) + {} + +private: + static const char* const m_class_name; +}; + + +/** + * Generator class for exceptions of type @c java.lang.NullPointerException. + * + * @since New in 1.9. + */ +class NullPointerException : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit NullPointerException(Env env) + : Exception(env, m_class_name) + {} + +private: + static const char* const m_class_name; +}; + +/** + * Generator class for exceptions of type @c java.lang.OutOfMemoryError. + * + * @since New in 1.9. + */ +class OutOfMemoryError : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit OutOfMemoryError(Env env) + : Exception(env, m_class_name) + {} + +private: + static const char* const m_class_name; +}; + +/** + * Generator class for exceptions of type + * @c java.lang.IndexOutOfBoundsException. + * + * @since New in 1.9. + */ +class IndexOutOfBoundsException : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit IndexOutOfBoundsException(Env env) + : Exception(env, m_class_name) + {} + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls) + {} + + public: + virtual ~ClassImpl(); + }; + + friend class ClassCacheImpl; + static const char* const m_class_name; +}; + +/** + * Generator class for exceptions of type @c java.io.IOException. + * + * @since New in 1.9. + */ +class IOException : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit IOException(Env env) + : Exception(env, m_class_name) + {} + +private: + static const char* const m_class_name; +}; + +/** + * Generator class for exceptions of type @c java.lang.IllegalArgumentException. + * + * @since New in 1.9. + */ +class IllegalArgumentException : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit IllegalArgumentException(Env env) + : Exception(env, m_class_name) + {} + +private: + static const char* const m_class_name; +}; + +/** + * Generator class for exceptions of type + * @c java.util.NoSuchElementException. + * + * @since New in 1.9. + */ +class NoSuchElementException : public Exception +{ +public: + /** + * Constructs an exception generator object. + */ + explicit NoSuchElementException(Env env) + : Exception(env, m_class_name) + {} + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls) + {} + + public: + virtual ~ClassImpl(); + }; + + friend class ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_EXCEPTION_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp new file mode 100644 index 0000000..7fe88d0 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_globalref.hpp @@ -0,0 +1,90 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_GLOBALREF_HPP +#define SVN_JAVAHL_JNIWRAPPER_GLOBALREF_HPP + +#include <jni.h> + +#include "jni_env.hpp" + +namespace Java { + +/** + * Wrapper for a global object reference. The reference is held until + * the wrapper goes out of scope (i.e., until the destructor is called). + * + * @since New in 1.9. + */ +class GlobalObject +{ +public: + explicit GlobalObject(Env env, jobject obj) + : m_obj(obj ? env.NewGlobalRef(obj) : NULL) + {} + + ~GlobalObject(); + + GlobalObject& operator=(jobject that); + + jobject get() const + { + return m_obj; + } + +private: + GlobalObject(const GlobalObject&); + GlobalObject& operator=(const GlobalObject&); + + jobject m_obj; +}; + +/** + * Wrapper for a global class reference. Behaves just like the object + * reference wrapper, but provides a more type-safe interface for + * class references. + * + * @since New in 1.9. + */ +class GlobalClass : protected GlobalObject +{ +public: + explicit GlobalClass(Env env, jclass cls) + : GlobalObject(env, cls) + {} + + GlobalClass& operator=(jclass that); + + jclass get() const + { + return jclass(GlobalObject::get()); + } + +private: + GlobalClass(const GlobalClass&); + GlobalClass& operator=(const GlobalClass&); +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_GLOBALREF_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp new file mode 100644 index 0000000..3c055ca --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.cpp @@ -0,0 +1,294 @@ +/** + * @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 + */ + +#include "jni_globalref.hpp" +#include "jni_io_stream.hpp" +#include "jni_stack.hpp" + +#include "svn_private_config.h" + +// Stream-wrapper-specific mark object type +struct svn_stream_mark_t +{ + void* m_baton; +}; + +namespace Java { + +namespace { +svn_error_t* stream_close_input(void* baton) +{ + InputStream* const self = static_cast<InputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, self->close()); + return SVN_NO_ERROR; +} + +svn_error_t* stream_mark(void* baton, svn_stream_mark_t** mark, + apr_pool_t* result_pool) +{ + InputStream* const self = static_cast<InputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, + self->mark(16384)); // FIXME: invent better readlimit + + *mark = static_cast<svn_stream_mark_t*>( + apr_palloc(result_pool, sizeof(**mark))); + (*mark)->m_baton = baton; + return SVN_NO_ERROR; +} + +svn_error_t* stream_seek(void* baton, const svn_stream_mark_t* mark) +{ + if (mark->m_baton != baton) + return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, + NULL, _("Invalid mark")); + + InputStream* const self = static_cast<InputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, + self->reset()); + return SVN_NO_ERROR; +} + +svn_error_t* stream_read(void* baton, char* buffer, apr_size_t* len) +{ + if (0 == *len) + return SVN_NO_ERROR; + + jint length = jint(*len); + InputStream* const self = static_cast<InputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, + length = self->read(buffer, length)); + if (length < 0) + *len = 0; + else + *len = length; + return SVN_NO_ERROR; +} + +svn_error_t* stream_skip(void* baton, apr_size_t len) +{ + InputStream* const self = static_cast<InputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, self->skip(jlong(len))); + return SVN_NO_ERROR; +} + +svn_error_t* stream_close_output(void* baton) +{ + OutputStream* const self = static_cast<OutputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, self->close()); + return SVN_NO_ERROR; +} + +svn_error_t* stream_write(void* baton, const char* data, apr_size_t* len) +{ + OutputStream* const self = static_cast<OutputStream*>(baton); + SVN_JAVAHL_CATCH(self->get_env(), SVN_ERR_BASE, + self->write(data, jint(*len))); + return SVN_NO_ERROR; +} + + +svn_error_t* +global_stream_close_input(void* baton) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + InputStream stream(Env(), ref->get()); + return stream_close_input(&stream); +} + +svn_error_t* +global_stream_mark(void* baton, svn_stream_mark_t** mark, + apr_pool_t* result_pool) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + InputStream stream(Env(), ref->get()); + return stream_mark(&stream, mark, result_pool); +} + +svn_error_t* +global_stream_seek(void* baton, const svn_stream_mark_t* mark) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + InputStream stream(Env(), ref->get()); + return stream_seek(&stream, mark); +} + +svn_error_t* +global_stream_read(void* baton, char* buffer, apr_size_t* len) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + InputStream stream(Env(), ref->get()); + return stream_read(&stream, buffer, len); +} + +svn_error_t* +global_stream_skip(void* baton, apr_size_t len) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + InputStream stream(Env(), ref->get()); + return stream_skip(&stream, len); +} + +svn_error_t* +global_stream_close_output(void* baton) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + OutputStream stream(Env(), ref->get()); + return stream_close_output(&stream); +} + +svn_error_t* +global_stream_write(void* baton, const char* data, apr_size_t* len) +{ + GlobalObject* ref = static_cast<GlobalObject*>(baton); + OutputStream stream(Env(), ref->get()); + return stream_write(&stream, data, len); +} + +apr_status_t cleanup_global_object(void* baton) +{ + delete static_cast<GlobalObject*>(baton); + return APR_SUCCESS; +} +} // anonymous namespace + + +// Class Java::InputStream + +const char* const InputStream::m_class_name = "java/io/InputStream"; +InputStream::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_close(env.GetMethodID(cls, "close", "()V")), + m_mid_mark_supported(env.GetMethodID(cls, "markSupported", "()Z")), + m_mid_mark(env.GetMethodID(cls, "mark", "(I)V")), + m_mid_reset(env.GetMethodID(cls, "reset", "()V")), + m_mid_read_byte(env.GetMethodID(cls, "read", "()I")), + m_mid_read_bytearray(env.GetMethodID(cls, "read", "([BII)I")), + m_mid_skip(env.GetMethodID(cls, "skip", "(J)J")) +{} + +InputStream::ClassImpl::~ClassImpl() {} + +svn_stream_t* +InputStream::get_global_stream(Env env, jobject jstream, + const SVN::Pool& pool) +{ + if (!jstream) + return NULL; + + const bool has_mark = InputStream(env, jstream).mark_supported(); + + std::auto_ptr<GlobalObject> baton(new GlobalObject(env, jstream)); + + svn_stream_t* const stream = svn_stream_create(baton.get(), pool.getPool()); + svn_stream_set_read2(stream, global_stream_read, + NULL /* only partial read support */); + svn_stream_set_skip(stream, global_stream_skip); + svn_stream_set_close(stream, global_stream_close_input); + if (has_mark) + { + svn_stream_set_mark(stream, global_stream_mark); + svn_stream_set_seek(stream, global_stream_seek); + } + + apr_pool_cleanup_register(pool.getPool(), baton.release(), + cleanup_global_object, + apr_pool_cleanup_null); + return stream; +} + +svn_stream_t* InputStream::get_stream(const SVN::Pool& pool) +{ + if (!m_jthis) + return NULL; + + const bool has_mark = mark_supported(); + + svn_stream_t* const stream = svn_stream_create(this, pool.getPool()); + svn_stream_set_read2(stream, stream_read, + NULL /* only partial read support */); + svn_stream_set_skip(stream, stream_skip); + svn_stream_set_close(stream, stream_close_input); + if (has_mark) + { + svn_stream_set_mark(stream, stream_mark); + svn_stream_set_seek(stream, stream_seek); + } + return stream; +} + +jint InputStream::read(void* data, jint length) +{ + ByteArray array(m_env, length); + const jint size = read(array); + if (size > 0) + { + ByteArray::Contents contents(array); + ::memcpy(static_cast<char*>(data), contents.data(), size); + } + return size; +} + +// Class Java::OutputStream + +const char* const OutputStream::m_class_name = "java/io/OutputStream"; + +OutputStream::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_close(env.GetMethodID(cls, "close", "()V")), + m_mid_write_byte(env.GetMethodID(cls, "write", "(I)V")), + m_mid_write_bytearray(env.GetMethodID(cls, "write", "([BII)V")) +{} + +OutputStream::ClassImpl::~ClassImpl() {} + +svn_stream_t* +OutputStream::get_global_stream(Env env, jobject jstream, + const SVN::Pool& pool) +{ + if (!jstream) + return NULL; + + std::auto_ptr<GlobalObject> baton(new GlobalObject(env, jstream)); + + svn_stream_t* const stream = svn_stream_create(baton.get(), pool.getPool()); + svn_stream_set_write(stream, global_stream_write); + svn_stream_set_close(stream, global_stream_close_output); + + apr_pool_cleanup_register(pool.getPool(), baton.release(), + cleanup_global_object, + apr_pool_cleanup_null); + return stream; +} + +svn_stream_t* OutputStream::get_stream(const SVN::Pool& pool) +{ + if (!m_jthis) + return NULL; + + svn_stream_t* const stream = svn_stream_create(this, pool.getPool()); + svn_stream_set_write(stream, stream_write); + svn_stream_set_close(stream, stream_close_output); + return stream; +} + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp new file mode 100644 index 0000000..c0ee022 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_io_stream.hpp @@ -0,0 +1,281 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_IO_STREAM_HPP +#define SVN_JAVAHL_JNIWRAPPER_IO_STREAM_HPP + +#include <cstring> +#include <string> +#include <memory> + +#include "jni_object.hpp" +#include "jni_array.hpp" + +#include "../Pool.h" + +#include "svn_io.h" + +namespace Java { + +/** + * Object wrapper for @c java.io.InputStream. + * + * @since New in 1.9. + */ +class InputStream : public Object +{ +public: + /** + * Constructs a wrapper around an existing @c InputStream @a jstream. + */ + explicit InputStream(Env env, jobject jstream) + : Object(env, ClassCache::get_input_stream(env), jstream) + {} + + /** + * Creates a stand-alone @c svn_stream_t allocated from @a pool that + * contains a global reference to @a jstream. This stream can safely + * be used in long-lived bound objects. + */ + static svn_stream_t* + get_global_stream(Env env, jobject jstream, const SVN::Pool& pool); + + /** + * Creates an @c svn_stream_t allocated from @a pool. + * <b>Do not use the returned stream past the lifetime of the + * current JNI native frame.</b> + */ + svn_stream_t* get_stream(const SVN::Pool& pool); + +public: + /** + * Implements @c InputStream.close() + */ + void close() + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_close); + } + + /** + * Implements @c InputStream.markSupported() + */ + bool mark_supported() + { + return m_env.CallBooleanMethod(m_jthis, impl().m_mid_mark_supported); + } + + /** + * Implements @c InputStream.mark(int) + */ + void mark(jint readlimit) + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_mark, readlimit); + } + + /** + * Implements @c InputStream.reset() + */ + void reset() + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_reset); + } + + /** + * Implements @c InputStream.read() + */ + jint read() + { + return m_env.CallIntMethod(m_jthis, impl().m_mid_read_byte); + } + + /** + * Implements @c InputStream.read(byte[],int,int) + */ + jint read(ByteArray& dst, jint length = -1, jint offset = 0) + { + return m_env.CallIntMethod(m_jthis, impl().m_mid_read_bytearray, + dst.get(), offset, + (length >= 0 ? length + : dst.length() - offset)); + } + + /** + * Helper method to read data into a native buffer. + */ + jint read(void* data, jint length); + + /** + * Implements @c InputStream.skip(long) + */ + jlong skip(jlong count) + { + return m_env.CallLongMethod(m_jthis, impl().m_mid_skip, count); + } + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_close; + const MethodID m_mid_mark_supported; + const MethodID m_mid_mark; + const MethodID m_mid_reset; + const MethodID m_mid_read_byte; + const MethodID m_mid_read_bytearray; + const MethodID m_mid_skip; + }; + + friend class ClassCacheImpl; + static const char* const m_class_name; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } +}; + + +/** + * Object wrapper for @c java.io.OutputStream. + * + * @since New in 1.9. + */ +class OutputStream : public Object +{ +public: + /** + * Constructs a wrapper around an existing @c OutputStream @a jstream. + */ + explicit OutputStream(Env env, jobject jstream) + : Object(env, ClassCache::get_output_stream(env), jstream) + {} + + /** + * Creates a stand-alone @c svn_stream_t allocated from @a pool that + * contains a global reference to @a jstream. This stream can safely + * be used in long-lived bound objects. + */ + static svn_stream_t* + get_global_stream(Env env, jobject jstream, const SVN::Pool& pool); + + /** + * Creates an @c svn_stream_t allocated from @a pool. + * <b>Do not use the returned stream past the lifetime of the + * current JNI native frame.</b> + */ + svn_stream_t* get_stream(const SVN::Pool& pool); + +public: + /** + * Implements @c OuptutStream.close() + */ + void close() + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_close); + } + + /** + * Implements @c OuptutStream.write(int) + */ + void write(jint byte) + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_write_byte, byte); + } + + /** + * Implements @c OuptutStream.write(byte[],int,int) + */ + void write(const ByteArray& src, jint length = -1, jint offset = 0) + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_write_bytearray, + src.get(), offset, + (length >= 0 ? length + : src.length() - offset)); + } + + /** + * Helper method to write data from a native buffer. + */ + void write(const void* data, jint length, jint offset = 0) + { + write(ByteArray(m_env, data, length)); + } + + /** + * Helper method to write a C string to the stream. + */ + void write(const char* text) + { + write(ByteArray(m_env, text)); + } + + /** + * Helper method to write a C++ string to the stream. + */ + void write(const std::string& text) + { + write(ByteArray(m_env, text)); + } + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_close; + const MethodID m_mid_write_byte; + const MethodID m_mid_write_bytearray; + }; + + friend class ClassCacheImpl; + static void static_init(Env env); + static const char* const m_class_name; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_IO_STREAM_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp new file mode 100644 index 0000000..a6e3d1c --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.cpp @@ -0,0 +1,63 @@ +/** + * @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 + */ + +#include <stdexcept> + +#include "jni_iterator.hpp" + +#include "svn_private_config.h" + +namespace Java { + +// Class Java::BaseIterator + +const char* const BaseIterator::m_class_name = "java/util/Iterator"; + +BaseIterator::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_has_next(env.GetMethodID(cls, "hasNext", "()Z")), + m_mid_next(env.GetMethodID(cls, "next", "()Ljava/lang/Object;")) +{} + +BaseIterator::ClassImpl::~ClassImpl() {} + +jobject BaseIterator::next() +{ + try + { + return m_env.CallObjectMethod(m_jthis, impl().m_mid_next); + } + catch (const SignalExceptionThrown&) + { + // Just rethrow if it's not a NoSuchElementException. + if (!m_env.IsInstanceOf( + m_env.ExceptionOccurred(), + ClassCache::get_exc_no_such_element(m_env)->get_class())) + throw; + + m_env.ExceptionClear(); + throw std::range_error(_("Iterator out of bounds")); + } +} + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp new file mode 100644 index 0000000..19c7976 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_iterator.hpp @@ -0,0 +1,91 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_ITERATOR_HPP +#define SVN_JAVAHL_JNIWRAPPER_ITERATOR_HPP + +#include "jni_env.hpp" +#include "jni_object.hpp" + +namespace Java { + +/** + * Non-template base for a Java iterator. + * + * @since New in 1.9. + */ +class BaseIterator : public Object +{ +protected: + /** + * Constructs the iterator wrapper. + */ + explicit BaseIterator(Env env, jobject jiterator) + : Object(env, ClassCache::get_iterator(env), jiterator) + {} + +public: + /** + * Returns @c false at the end of the iteration. + */ + bool has_next() const + { + return m_env.CallBooleanMethod(m_jthis, impl().m_mid_has_next); + } + + /** + * Returns the next object in the iteration. + * @throw std::range_error if the next object is not available. + */ + jobject next(); + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_has_next; + const MethodID m_mid_next; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_ITERATOR_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp new file mode 100644 index 0000000..e7b8a41 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_list.cpp @@ -0,0 +1,85 @@ +/** + * @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 + */ + +#include <stdexcept> +#include <string> + +#include "jni_list.hpp" + +#include "svn_private_config.h" + +namespace Java { + +// Class Java::BaseImmutableList + +const char* const BaseImmutableList::m_class_name = "java/util/List"; + +BaseImmutableList::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_size(env.GetMethodID(cls, "size", "()I")), + m_mid_get(env.GetMethodID(cls, "get", "(I)Ljava/lang/Object;")), + m_mid_add(env.GetMethodID(cls, "add", "(Ljava/lang/Object;)Z")), + m_mid_clear(env.GetMethodID(cls, "clear", "()V")), + m_mid_iter(env.GetMethodID(cls, "listIterator", "()Ljava/util/ListIterator;")) +{} + +BaseImmutableList::ClassImpl::~ClassImpl() {} + +jobject BaseImmutableList::operator[](jint index) const +{ + try + { + return m_env.CallObjectMethod(m_jthis, impl().m_mid_get, index); + } + catch (const SignalExceptionThrown&) + { + // Just rethrow if it's not an IndexOutOfBoundsException. + if (!m_env.IsInstanceOf( + m_env.ExceptionOccurred(), + ClassCache::get_exc_index_out_of_bounds(m_env)->get_class())) + throw; + + m_env.ExceptionClear(); + std::string msg(_("List index out of bounds: ")); + msg += index; + throw std::out_of_range(msg.c_str()); + } +} + +BaseImmutableList::Iterator BaseImmutableList::get_iterator() const +{ + return Iterator(m_env, m_env.CallObjectMethod(m_jthis, impl().m_mid_iter)); +} + +// Class Java::BaseList + +const char* const BaseList::m_class_name = "java/util/ArrayList"; + +BaseList::ClassImpl::ClassImpl(Env env, jclass cls) + : BaseImmutableList::ClassImpl(env, cls), + m_mid_ctor(env.GetMethodID(cls, "<init>", "(I)V")) +{} + +BaseList::ClassImpl::~ClassImpl() {} + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp new file mode 100644 index 0000000..9a11fc1 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_list.hpp @@ -0,0 +1,307 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_LIST_HPP +#define SVN_JAVAHL_JNIWRAPPER_LIST_HPP + +#include "jni_env.hpp" +#include "jni_object.hpp" +#include "jni_iterator.hpp" + +namespace Java { + +/** + * Non-template base for an immutable type-safe Java list. + * + * @since New in 1.9. + */ +class BaseImmutableList : public Object +{ +public: + /** + * Returns the number of elements in the list. + */ + jint length() const + { + return m_env.CallIntMethod(m_jthis, impl().m_mid_size); + } + + /** + * Checks if the list is empty. + */ + bool is_empty() const + { + return (length() == 0); + } + +protected: + /** + * Constructs the list wrapper + */ + explicit BaseImmutableList(Env env, jobject jlist) + : Object(env, ClassCache::get_list(env), jlist) + {} + + /** + * Constructor used by BaseList + */ + explicit BaseImmutableList(Env env, const Object::ClassImpl* pimpl) + : Object(env, pimpl) + {} + + /** + * Clears the contents of the list. + */ + void clear() + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_clear); + } + + /** + * Appends @a obj to the end of the list. + */ + void add(jobject obj) + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_add, obj); + } + + /** + * Returns the object reference at @a index. + * @throw std::out_of_range if the index value is not valid. + */ + jobject operator[](jint index) const; + + /** + * Iterator used by subclasses. + */ + class Iterator : public BaseIterator + { + friend class BaseImmutableList; + explicit Iterator(Env env, jobject jiterator) + : BaseIterator(env, jiterator) + {} + }; + + /** + * Returns an iterator instance. + */ + Iterator get_iterator() const; + + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_size; + const MethodID m_mid_get; + const MethodID m_mid_add; + const MethodID m_mid_clear; + const MethodID m_mid_iter; + }; + +private: + friend class Iterator; + friend class ClassCacheImpl; + static const char* const m_class_name; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } +}; + +/** + * Template wrapper for an immutable type-safe Java list. + * + * @since New in 1.9. + */ +template <typename T, typename NativeT=jobject> +class ImmutableList : public BaseImmutableList +{ +public: + /** + * Constructs the list wrapper. + */ + explicit ImmutableList(Env env, jobject jlist) + : BaseImmutableList(env, jlist) + {} + + /** + * Returns a wrapper object for the object reference at @a index. + * @throw std::out_of_range if the index value is not valid. + */ + T operator[](jint index) const + { + return T(m_env, NativeT(BaseImmutableList::operator[](index))); + } + + /** + * Iterates over the items in the list, calling @a function for + * each item. + * @see std::for_each + */ + template<typename F> + F for_each(F function) const + { + Iterator iter(get_iterator()); + while (iter.has_next()) + function(T(m_env, NativeT(iter.next()))); + return function; + } +}; + +/** + * Non-template base for a mutable type-safe Java list. + * + * @since New in 1.9. + */ +class BaseList : public BaseImmutableList +{ +public: + /** + * Clears the contents of the list. + */ + void clear() + { + BaseImmutableList::clear(); + } + +protected: + /** + * Constructs the list wrapper, treating @a jlist as a @c java.util.List. + */ + explicit BaseList(Env env, jobject jlist) + : BaseImmutableList(env, jlist) + {} + + /** + * Constructs and wraps an empty list of type @c java.util.ArrayList + * with initial allocation size @a length. + */ + explicit BaseList(Env env, jint length) + : BaseImmutableList(env, ClassCache::get_array_list(env)) + { + set_this(env.NewObject(get_class(), impl().m_mid_ctor, length)); + } + + /** + * Appends @a obj to the end of the list. + */ + void add(jobject obj) + { + BaseImmutableList::add(obj); + } + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public BaseImmutableList::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_ctor; + }; + + friend class ClassCacheImpl; + static const char* const m_class_name; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } +}; + +/** + * Template wrapper for a mutable type-safe Java list. + * + * @since New in 1.9. + */ +template <typename T, typename NativeT=jobject> +class List : public BaseList +{ +public: + /** + * Constructs the list wrapper, deriving the class from @a jlist. + */ + explicit List(Env env, jobject jlist) + : BaseList(env, jlist) + {} + + /** + * Constructs and wraps an empty list of type @c java.util.ArrayList + * with initial allocation size @a length. + */ + explicit List(Env env, jint length_ = 0) + : BaseList(env, length_) + {} + + /** + * Returns a wrapper object for the object reference at @a index. + * @throw std::out_of_range if the index value is not valid. + */ + T operator[](jint index) const + { + return T(m_env, NativeT(BaseList::operator[](index))); + } + + /** + * Appends @a obj to the end of the list. + */ + void add(const T& obj) + { + BaseList::add(obj.get()); + } + + /** + * Iterates over the items in the list, calling @a function for + * each item. + * @see std::for_each + */ + template<typename F> + F for_each(F function) const + { + Iterator iter(get_iterator()); + while (iter.has_next()) + function(T(m_env, NativeT(iter.next()))); + return function; + } +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_LIST_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_object.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_object.hpp new file mode 100644 index 0000000..2e05a06 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_object.hpp @@ -0,0 +1,285 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_OBJECT_HPP +#define SVN_JAVAHL_JNIWRAPPER_OBJECT_HPP + +#include "jni_env.hpp" +#include "jni_globalref.hpp" + +namespace Java { + +/** + * An abstract wrapper for a @c java.lang.Object instance. + * + * This is the base class for all concrete object wrapper classes. It + * is self-contained in the sense that it heeps its own JVM + * environment, class and object reference; Java object methods are + * expected to be exposed as methods of derived classes. + * + * The associated JNI class reference is stored for the lifetime of + * the JVM in the global class cache. + * + * @since New in 1.9. + */ +class Object +{ +public: + /** + * Returns the wrapped JNI object reference. + */ + jobject get() const + { + return m_jthis; + } + + /** + * Returns the wrapped JNI class reference. + */ + jclass get_class() const + { + return m_impl->get_class(); + } + + /** + * Returns the wrapped enviromnment reference. + */ + Env get_env() const + { + return m_env; + } + + /** + * This object's implementation details. + */ + class ClassImpl + { + public: + jclass get_class() const + { + return m_class.get(); + } + + virtual ~ClassImpl(); + + protected: + explicit ClassImpl(Env env, jclass cls) + : m_class(env, cls) + {} + + private: + friend class ClassCacheImpl; + + GlobalClass m_class; ///< Class reference for this object wrapper + + // Non-copyable + ClassImpl(const ClassImpl&); + ClassImpl& operator=(const ClassImpl&); + }; + +protected: + /** + * constructs an object wrapper given the class @a impl and an + * object reference @a jthis. + */ + Object(Env env, const ClassImpl* impl, jobject jthis = NULL) + : m_env(env), + m_impl(impl), + m_jthis(jthis) + {} + + const Env m_env; ///< JVM environment wrapper + const ClassImpl* const m_impl; ///< Class implementation details + const jobject m_jthis; ///< @c this object reference + + /** + * Certain subclasses need a fully constructed base Object before + * they can create the wrapped JNI object. They can use this + * function to oveerride the constness of @c m_jthis, but only if + * they're changing a @c NULL @c m_jthis to a concrete value. + */ + void set_this(jobject jthis) + { + if (!m_jthis && jthis) + *const_cast<jobject*>(&m_jthis) = jthis; + } + +private: + friend class ClassCacheImpl; + static const char* const m_class_name; +}; + +// Forward declaration +class ClassCacheImpl; + +/** + * A singleton cache for global class references. + * + * The instance is created when the native library is loaded by the + * JVM, and destroyed when it is unloaded. It creates global + * references for a number of classes and calls said classes' + * single-threded static initializers, which usually find and store + * method and field IDs (which are usually only valid until the + * associated class is garbage-collected). + * + * Be aware that as long as the global references exist, these classes + * cannot be garbage-collected. The number of classes stored in this + * cache should therefore be kept to a reasonable minimum. + * + * @since New in 1.9. + */ +class ClassCache +{ + // Cannot create instances of this type. + ClassCache(); + ~ClassCache(); + + static ClassCacheImpl* m_impl; + +public: + /* This static initializer must only be called by JNI_OnLoad */ + static void create(); + + /* This static finalizer must only be called by JNI_OnUnload */ + static void destroy(); + +#define JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(M) \ + static const Object::ClassImpl* get_##M(Env env); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(object); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(classtype); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(throwable); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(string); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(exc_index_out_of_bounds); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(exc_no_such_element); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(list); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(array_list); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(map); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(set); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(iterator); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(map_entry); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(hash_map); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(input_stream); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(output_stream); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(byte_buffer); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(subversion_exception); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_cb); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_result); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_ssl_server_cert_failures); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(authn_ssl_server_cert_info); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(user_passwd_cb); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(credential); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(credential_kind); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(external_item); + + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_base_cb); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_base_cb_ret); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_props_cb); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_provide_props_cb_ret); + JNIWRAPPER_DECLARE_CACHED_CLASS_ACCESSOR(editor_get_kind_cb); +#undef JNIWRAPPER_DECLARE_CACHED_CLASS +}; + + +/** + * Object wrapper for @c java.lang.Class. + * + * The associated JNI class reference is stored for the lifetime of + * the JVM in the global class cache. + * + * @since New in 1.9. + */ +class Class +{ +public: + /** + * Constructs class instance wrapper for @a obj. + */ + explicit Class(Env env, jobject obj); + + /** + * Constructs class instance wrapper for @a obj. + */ + explicit Class(const Object& obj); + + /** + * Wrapper for the Java @c getName() method. + */ + jstring get_name() const; + + /** + * Returns the wrapped class instance. + */ + jobject get() const + { + return m_jthis; + } + + /** + * Returns the wrapped enviromnment reference. + */ + Env get_env() const + { + return m_env; + } + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls) + {} + + public: + virtual ~ClassImpl(); + }; + + const Env m_env; ///< JVM environment wrapper + const jobject m_jthis; ///< Class instance + + friend class ClassCacheImpl; + static const char* const m_class_name; + static void static_init(Env env, jclass class_type); + + static MethodID m_mid_get_class; + static MethodID m_mid_get_name; +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_OBJECT_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp new file mode 100644 index 0000000..13e35e3 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_stack.hpp @@ -0,0 +1,220 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_STACK_HPP +#define SVN_JAVAHL_JNIWRAPPER_STACK_HPP + +#ifdef SVN_JAVAHL_DEBUG +# ifndef SVN_JAVAHL_ASSERT_EXCEPTION_THROWN +# include <cassert> +# define SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(E) \ + assert((E).ExceptionCheck()) +# endif // SVN_JAVAHL_ASSERT_EXCEPTION_THROWN +#else +# define SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(E) +# endif // SVN_JAVAHL_DEBUG + +#include "../JNIStackElement.h" +#include "jni_env.hpp" +#include "jni_exception.hpp" + +#include "svn_error.h" + +/** + * Boilerplate for the native method implementation entry point. + * + * Every native method implementation should start by invoking this + * macro to initialize the logging stack element and begin the + * try/catch block of the function body. + * + * @param C The name of the Java class that declares this method. + * @param M The (Java) name of the method. + * + * This macro expects two additional parameters to be available + * (either as function arguments or local variables): + * @c JNIEnv* @a jenv and @c jobject @a jthis. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_JNI_TRY(C, M) \ + ::JNIStackElement st_ac_ke_le_me_nt_(jenv, #C, #M, jthis); \ + try + +/** + * Boilerplate for the native method implementation entry point. + * + * Initializes local variable named @a V as a pointer to an instance + * of the native-bound class @a C. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_GET_BOUND_OBJECT(C, V) \ + C* const V = C::get_self(::Java::Env(jenv), jthis) + +/** + * Boilerplate for the native method implementation entry point. + * + * Like #SVN_JAVAHL_JNI_TRY, but for static methods where the @c jthis + * argument is not available. + * + * This macro expects two additional parameters to be available + * (either as function arguments or local variables): + * @c JNIEnv* @a jenv and @c jclass @a jclazz. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_JNI_TRY_STATIC(C, M) \ + ::JNIStackElement st_ac_ke_le_me_nt_(jenv, #C, #M, jclazz); \ + try + + +/** + * Boilerplate for the native method implementation exit point. + * + * Every native method implementation should end by invoking this + * macro to close the try/catch block of the function body and handle + * any exceptions thrown by the method implementation. + * + * This boilerplate variant converts C++ exceptions to the Java + * exception type @a X, but retains exceptions that are already in + * progress. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(X) \ + catch (const ::Java::SignalExceptionThrown&) \ + { \ + SVN_JAVAHL_ASSERT_EXCEPTION_THROWN(::Java::Env(jenv)); \ + } \ + catch (const ::std::exception& ex) \ + { \ + X(::Java::Env(jenv)).throw_java_exception(ex.what()); \ + } \ + catch (...) \ + { \ + const char* const msg = Java::unknown_cxx_exception_message(); \ + X(::Java::Env(jenv)).throw_java_exception(msg); \ + } + +/** + * Boilerplate for the native method implementation exit point. + * + * Invokes #SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION to throw a + * @c RuntimeException. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_JNI_CATCH \ + SVN_JAVAHL_JNI_CATCH_TO_EXCEPTION(::Java::RuntimeException) + +/** + * Invocation wrapper for functions that return an @c svn_error_t *. + * + * @param E A wrapped environment (@c Java::Env) instance. + * @param S The statement to execute in the checked context. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_CHECK(E, S) \ + do { \ + svn_error_t* const ja_va_hl_err_te_mp_ = (S); \ + if (ja_va_hl_err_te_mp_) \ + ::Java::handle_svn_error((E), ja_va_hl_err_te_mp_); \ + } while(0) + +/** + * Invocation wrapper for calling Java methods that may throw an + * exception from within a native callback that is expected to return + * an @c svn_error_t*. + * + * @param E A wrapped environment (@c Java::Env) instance. + * @param C A Subversion or APR error code. + * @param S The statement to execute in the checked context. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_CATCH(E, C, S) \ + try \ + { \ + S; \ + } \ + catch (const ::Java::SignalExceptionThrown&) \ + { \ + SVN_JAVAHL_ASSERT_EXCEPTION_THROWN((E)); \ + return Java::caught_java_exception_error((C)); \ + } \ + catch (const ::std::exception& ex) \ + { \ + const char* const msg = ex.what(); \ + ::Java::RuntimeException((E)).throw_java_exception(msg); \ + return svn_error_create((C), NULL, msg); \ + } \ + catch (...) \ + { \ + const char* const msg = Java::unknown_cxx_exception_message(); \ + ::Java::RuntimeException((E)).throw_java_exception(msg); \ + return svn_error_create((C), NULL, msg); \ + } + +/** + * Exception checker for the oldstyle implementation that does not use + * the @c Java::Env environment wrapper. + * + * @param E A wrapped environment (@c Java::Env) instance. + * + * @since New in 1.9. + */ +#define SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(E) \ + do { \ + if ((E).ExceptionCheck()) \ + throw ::Java::SignalExceptionThrown(); \ + } while(0) + + +namespace Java { + +/** + * Handle an error @a err returned from a native function and throws + * an appropriate Java exception. + * + * @since New in 1.9. + */ +void handle_svn_error(Env env, svn_error_t* err); + +/** + * Return a localized error string for an unknown C++ exception. + * + * @since New in 1.9. + */ +const char* unknown_cxx_exception_message() throw(); + +/** + * Create an svn_error_t for a caught Java exception. + * + * @since New in 1.9. + */ +svn_error_t* caught_java_exception_error(apr_status_t status) throw(); + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_STACK_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_string.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_string.hpp new file mode 100644 index 0000000..7b874d0 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_string.hpp @@ -0,0 +1,251 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_STRING_HPP +#define SVN_JAVAHL_JNIWRAPPER_STRING_HPP + +#include <cstring> +#include <string> + +#include <apr_pools.h> + +#include "jni_object.hpp" + +namespace Java { + +/** + * Object wrapper for @c java.lang.String. + * + * The associated JNI class reference is stored for the lifetime of + * the JVM in the global class cache. + * + * @since New in 1.9. + */ +class String +{ +public: + /** + * Constructs a wrapper around an existing string @a str. + */ + explicit String(Env env, jstring str) + : m_env(env), + m_jthis(str) + {} + + /** + * Constructs a new string and wrapper from @a text. + */ + explicit String(Env env, const char* text) + : m_env(env), + m_jthis(env.NewStringUTF(text)) + {} + + /** + * Constructs a new string and wrapper from @a text. + */ + explicit String(Env env, const std::string& text) + : m_env(env), + m_jthis(env.NewStringUTF(text.c_str())) + {} + + /** + * Returns the wrapped JNI object reference. + */ + jstring get() const + { + return m_jthis; + } + + /** + * Returns the wrapped enviromnment reference. + */ + Env get_env() const + { + return m_env; + } + + /** + * Returns the number of Unicode characters in the string. + */ + jsize length() const + { + return m_env.GetStringLength(get()); + } + + /** + * Returns the length of the modified UTF-8 representation of the + * string. + */ + jsize utf8_length() const + { + return m_env.GetStringUTFLength(get()); + } + + /** + * Copies the contents of the modified UTF-8 representation of the + * string into @a pool. + */ + const char* strdup(apr_pool_t* pool) const; + + /** + * Accessor class for the contents of the string. + * + * Objects of this class should be created within the scope where + * the raw C string is required. They will create an immutable + * modified UTF-8 representation of the string contents. The data + * will be released by the destructor. + */ + class Contents + { + public: + /** + * Constructs an immutable string contents accessor. + */ + explicit Contents(const String& str) + : m_str(str), + m_text(!str.get() ? NULL + : str.m_env.GetStringUTFChars(str.get(), NULL)), + m_length(m_text ? jsize(::std::strlen(m_text)) : 0) + {} + + /** + * Releases the string contents, possibly committing changes to the JVM. + */ + ~Contents() + { + if (m_text) + m_str.m_env.ReleaseStringUTFChars(m_str.get(), NULL); + } + + /** + * Returns the C representation of the string contents. + */ + const char* c_str() const + { + return m_text; + } + + /** + * Returns the length of the C representation of the string. + */ + jsize utf8_length() const + { + return m_length; + } + + protected: + const String& m_str; + const char* m_text; + jsize m_length; + }; + + /** + * Accessor class for the contents of the string. + * + * Behaves like the #Contents class, but the representation is + * considered mutable and can be assigned a new value, which will be + * subsequently committed to the JVM. + */ + class MutableContents : protected Contents + { + public: + /** + * Constructs a mutable string contents accessor. + */ + explicit MutableContents(String& str) + : Contents(str), + m_new_text(NULL) + {} + + /** + * Releases the string contents, possibly committing changes to the JVM. + */ + ~MutableContents() + { + if (m_new_text) + { + // Prevent double-release by the Contents destructor. + m_text = NULL; + m_str.m_env.ReleaseStringUTFChars(m_str.get(), m_new_text); + } + } + + /** + * Returns the C representation of the string contents. + */ + const char* c_str() const + { + if (m_new_text) + return m_new_text; + return Contents::c_str(); + } + + /** + * Returns the length of the C representation of the string. + */ + jsize utf8_length() const + { + return Contents::utf8_length(); + } + + /** + * Sets a new value for the string, to be committed to the JVM + * when the accessor object is destroyed. + * @throw std::invalid_argument if the @a new_text is @c null + * @throw std::logic_error if this is a @c null or immutable string + */ + void set_value(const char* new_text); + + private: + const char* m_new_text; + }; + +private: + const Env m_env; + const jstring m_jthis; + + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls) + {} + + public: + virtual ~ClassImpl(); + }; + + friend class Contents; + friend class MutableContents; + friend class ClassCacheImpl; + static const char* const m_class_name; +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_STRING_HPP diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp new file mode 100644 index 0000000..369d956 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.cpp @@ -0,0 +1,110 @@ +/** + * @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 + */ + +#include <stdexcept> + +#include "jni_string.hpp" +#include "jni_string_map.hpp" + +#include "svn_private_config.h" + +namespace Java { + +// Class Java::BaseImmutableMap + +const char* const BaseImmutableMap::m_class_name = "java/util/Map"; + +BaseImmutableMap::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_put(env.GetMethodID(cls, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)" + "Ljava/lang/Object;")), + m_mid_clear(env.GetMethodID(cls, "clear", "()V")), + m_mid_has_key(env.GetMethodID(cls, "containsKey", + "(Ljava/lang/Object;)Z")), + m_mid_get(env.GetMethodID(cls, "get", + "(Ljava/lang/Object;)Ljava/lang/Object;")), + m_mid_size(env.GetMethodID(cls, "size", "()I")), + m_mid_entry_set(env.GetMethodID(cls, "entrySet", "()Ljava/util/Set;")) +{} + +BaseImmutableMap::ClassImpl::~ClassImpl() {} + +jobject BaseImmutableMap::operator[](const std::string& index) const +{ + const String key(m_env, index); + if (!m_env.CallBooleanMethod(m_jthis, impl().m_mid_has_key, key.get())) + { + std::string msg(_("Map does not contain key: ")); + msg += index; + throw std::out_of_range(msg.c_str()); + } + return m_env.CallObjectMethod(m_jthis, impl().m_mid_get, key.get()); +} + +BaseImmutableMap::Iterator BaseImmutableMap::get_iterator() const +{ + const jobject jentry_set = + m_env.CallObjectMethod(m_jthis, impl().m_mid_entry_set); + const jobject jiterator = + m_env.CallObjectMethod(jentry_set, Set::impl(m_env).m_mid_iterator); + return Iterator(m_env, jiterator); +} + +// Class Java::BaseImmutableMap::Entry + +const char* const BaseImmutableMap::Entry::m_class_name = "java/util/Map$Entry"; + +BaseImmutableMap::Entry::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_get_key(env.GetMethodID(cls, "getKey", "()Ljava/lang/Object;")), + m_mid_get_value(env.GetMethodID(cls, "getValue", "()Ljava/lang/Object;")) +{} + +BaseImmutableMap::Entry::ClassImpl::~ClassImpl() {} + +// Class Java::BaseImmutableMap::Set + +const char* const BaseImmutableMap::Set::m_class_name = "java/util/Set"; + +BaseImmutableMap::Set::ClassImpl::ClassImpl(Env env, jclass cls) + : Object::ClassImpl(env, cls), + m_mid_iterator(env.GetMethodID(cls, "iterator", + "()Ljava/util/Iterator;")) +{} + +BaseImmutableMap::Set::ClassImpl::~ClassImpl() {} + + +// Class Java::BaseMap + +const char* const BaseMap::m_class_name = "java/util/HashMap"; + +BaseMap::ClassImpl::ClassImpl(Env env, jclass cls) + : BaseImmutableMap::ClassImpl(env, cls), + m_mid_ctor(env.GetMethodID(cls, "<init>", "(I)V")) +{} + +BaseMap::ClassImpl::~ClassImpl() {} + +} // namespace Java diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp new file mode 100644 index 0000000..db05e28 --- /dev/null +++ b/subversion/bindings/javahl/native/jniwrapper/jni_string_map.hpp @@ -0,0 +1,384 @@ +/** + * @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 + */ + +#ifndef SVN_JAVAHL_JNIWRAPPER_STRING_MAP_HPP +#define SVN_JAVAHL_JNIWRAPPER_STRING_MAP_HPP + +#include <string> + +#include "jni_env.hpp" +#include "jni_object.hpp" +#include "jni_iterator.hpp" + +namespace Java { + +/** + * Non-template base for an immutable type-safe Java map with String keys. + * + * @since New in 1.9. + */ +class BaseImmutableMap : public Object +{ +public: + /** + * Returns the number of elements in the map. + */ + jint length() const + { + return m_env.CallIntMethod(m_jthis, impl().m_mid_size); + } + + /** + * Checks if the map is empty. + */ + bool is_empty() const + { + return (length() == 0); + } + +protected: + /** + * Constructs the map wrapper. + */ + explicit BaseImmutableMap(Env env, jobject jmap) + : Object(env, ClassCache::get_map(env), jmap) + {} + + /** + * Constructor used by BaseMap. + */ + explicit BaseImmutableMap(Env env, const Object::ClassImpl* pimpl) + : Object(env, pimpl) + {} + + /** + * Clears the contents of the map. + */ + void clear() + { + m_env.CallVoidMethod(m_jthis, impl().m_mid_clear); + } + + /** + * Inserts @a obj identified by @a key into the map. + */ + void put(const std::string& key, jobject obj) + { + m_env.CallObjectMethod(m_jthis, impl().m_mid_put, + String(m_env, key).get(), obj); + } + + /** + * Returns the object reference identified by @a index. + * @throw std::out_of_range if there is no such element. + */ + jobject operator[](const std::string& index) const; + + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_put; + const MethodID m_mid_clear; + const MethodID m_mid_has_key; + const MethodID m_mid_get; + const MethodID m_mid_size; + const MethodID m_mid_entry_set; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ClassCacheImpl; + static const char* const m_class_name; + + class Iterator : public BaseIterator + { + friend class BaseImmutableMap; + explicit Iterator(Env env, jobject jiterator) + : BaseIterator(env, jiterator) + {} + }; + + Iterator get_iterator() const; + + class Entry : public Object + { + public: + explicit Entry(Env env, jobject jentry) + : Object(env, ClassCache::get_map_entry(env), jentry) + {} + + const std::string key() const + { + const jstring jkey = + jstring(m_env.CallObjectMethod(m_jthis, impl().m_mid_get_key)); + const String::Contents key(String(m_env, jkey)); + return std::string(key.c_str()); + } + + jobject value() const + { + return m_env.CallObjectMethod(m_jthis, impl().m_mid_get_value); + } + + private: + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_get_key; + const MethodID m_mid_get_value; + }; + + friend class ClassCacheImpl; + static const char* const m_class_name; + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + }; + +private: + struct Set + { + /** + * This object's implementation details. + */ + class ClassImpl : public Object::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_iterator; + }; + + static const char* const m_class_name; + static const ClassImpl& impl(Env env) + { + return *dynamic_cast<const ClassImpl*>(ClassCache::get_set(env)); + } + }; +}; + +/** + * Template wrapper for an immutable type-safe Java map. + * + * @since New in 1.9. + */ +template <typename T, typename NativeT=jobject> +class ImmutableMap : public BaseImmutableMap +{ +public: + /** + * Constructs the map wrapper, converting the contents to an + * @c std::map. + */ + explicit ImmutableMap(Env env, jobject jmap) + : BaseImmutableMap(env, jmap) + {} + + /** + * Returns a wrapper object for the object reference identified by @a index. + * @throw std::out_of_range if there is no such element. + */ + T operator[](const std::string& index) const + { + return T(m_env, NativeT(BaseImmutableMap::operator[](index))); + } + + /** + * Iterates over the items in the map, calling @a function for + * each item. + * @see std::for_each + * @note Unlike std::for_each, which invokes the functor with a + * single @c value_type argument, this iterator calls + * @a function with separate @c const references to the key + * and value. + */ + template<typename F> + F for_each(F function) const + { + Iterator iter(get_iterator()); + while (iter.has_next()) + { + Entry entry(m_env, iter.next()); + const std::string& key(entry.key()); + function(key, T(m_env, NativeT(entry.value()))); + } + return function; + } +}; + +/** + * Non-template base for a mutable type-safe Java map with String keys. + * + * @since New in 1.9. + */ +class BaseMap : public BaseImmutableMap +{ +public: + /** + * Clears the contents of the map. + */ + void clear() + { + BaseImmutableMap::clear(); + } + +protected: + /** + * Constructs the map wrapper, treating @a jmap as a @c java.util.Map. + */ + explicit BaseMap(Env env, jobject jmap) + : BaseImmutableMap(env, jmap) + {} + + /** + * Constructs and wraps an empty map of type @c java.util.HashMap + * with initial allocation size @a length. + */ + explicit BaseMap(Env env, jint length) + : BaseImmutableMap(env, ClassCache::get_hash_map(env)) + { + set_this(env.NewObject(get_class(), impl().m_mid_ctor, length)); + } + +private: + /** + * This object's implementation details. + */ + class ClassImpl : public BaseImmutableMap::ClassImpl + { + friend class ClassCacheImpl; + + protected: + explicit ClassImpl(Env env, jclass cls); + + public: + virtual ~ClassImpl(); + + const MethodID m_mid_ctor; + }; + + const ClassImpl& impl() const + { + return *dynamic_cast<const ClassImpl*>(m_impl); + } + + friend class ClassCacheImpl; + static const char* const m_class_name; +}; + +/** + * Template wrapper for a mutable type-safe Java map. + * + * @since New in 1.9. + */ +template <typename T, typename NativeT=jobject> +class Map : public BaseMap +{ +public: + /** + * Constructs the map wrapper, deriving the class from @a jmap. + */ + explicit Map(Env env, jobject jmap) + : BaseMap(env, jmap) + {} + + /** + * Constructs and wraps an empty map of type @c java.util.HashMap + * with initial allocation size @a length. + */ + explicit Map(Env env, jint length = 0) + : BaseMap(env, length) + {} + + /** + * Inserts @a obj identified by @a key into the map. + */ + void put(const std::string& key, const T& obj) + { + BaseMap::put(key, obj.get()); + } + + /** + * Returns a wrapper object for the object reference identified by @a index. + * @throw std::out_of_range if there is no such element. + */ + T operator[](const std::string& index) const + { + return T(m_env, NativeT(BaseMap::operator[](index))); + } + + /** + * Iterates over the items in the map, calling @a function for + * each item. + * @see std::for_each + * @note Unlike std::for_each, which invokes the functor with a + * single @c value_type argument, this iterator calls + * @a function with separate @c const references to the key + * and value. + */ + template<typename F> + F for_each(F function) const + { + Iterator iter(get_iterator()); + while (iter.has_next()) + { + Entry entry(m_env, iter.next()); + const std::string& key(entry.key()); + function(key, T(m_env, NativeT(entry.value()))); + } + return function; + } +}; + +} // namespace Java + +#endif // SVN_JAVAHL_JNIWRAPPER_STRING_MAP_HPP diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp index 9705304..a6be60f 100644 --- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_NativeResources.cpp @@ -25,14 +25,11 @@ * NativeResources. */ -#include "JNIUtil.h" #include "../include/org_apache_subversion_javahl_NativeResources.h" JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_NativeResources_initNativeLibrary (JNIEnv *env, jclass jclazz) { - // No usual JNIEntry here, as the prerequisite native library - // initialization is performed here. - JNIUtil::JNIGlobalInit(env); + // No-op; see comment in NativeResources.java } diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp index e4fdd76..5f70a5a 100644 --- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNClient.cpp @@ -50,7 +50,8 @@ #include "ImportFilterCallback.h" #include "ChangelistCallback.h" #include "StringArray.h" -#include "RevpropTable.h" +#include "PropertyTable.h" +#include "CreateJ.h" #include "VersionExtended.h" #include "DiffOptions.h" #include "svn_version.h" @@ -184,8 +185,9 @@ Java_org_apache_subversion_javahl_SVNClient_list JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNClient_status (JNIEnv *env, jobject jthis, jstring jpath, jobject jdepth, - jboolean jonServer, jboolean jgetAll, jboolean jnoIgnore, - jboolean jignoreExternals, jobject jchangelists, + jboolean jonServer, jboolean jonDisk, jboolean jgetAll, + jboolean jnoIgnore, jboolean jignoreExternals, + jboolean jdepthAsSticky, jobject jchangelists, jobject jstatusCallback) { JNIEntry(SVNClient, status); @@ -203,9 +205,9 @@ Java_org_apache_subversion_javahl_SVNClient_status StatusCallback callback(jstatusCallback); cl->status(path, EnumMapper::toDepth(jdepth), - jonServer ? true:false, - jgetAll ? true:false, jnoIgnore ? true:false, - jignoreExternals ? true:false, changelists, &callback); + bool(jonServer), bool(jonDisk), bool(jgetAll), + bool(jnoIgnore), bool(jignoreExternals), + bool(jdepthAsSticky), changelists, &callback); } JNIEXPORT void JNICALL @@ -257,7 +259,7 @@ Java_org_apache_subversion_javahl_SVNClient_password } JNIEXPORT void JNICALL -Java_org_apache_subversion_javahl_SVNClient_setPrompt +Java_org_apache_subversion_javahl_SVNClient_setPrompt__Lorg_apache_subversion_javahl_callback_AuthnCallback_2 (JNIEnv *env, jobject jthis, jobject jprompter) { JNIEntry(SVNClient, setPrompt); @@ -267,7 +269,7 @@ Java_org_apache_subversion_javahl_SVNClient_setPrompt JNIUtil::throwError(_("bad C++ this")); return; } - Prompter *prompter = Prompter::makeCPrompter(jprompter); + Prompter::UniquePtr prompter(Prompter::create(jprompter)); if (JNIUtil::isExceptionThrown()) return; @@ -275,6 +277,39 @@ Java_org_apache_subversion_javahl_SVNClient_setPrompt } JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_SVNClient_setPrompt__Lorg_apache_subversion_javahl_callback_UserPasswordCallback_2 +(JNIEnv *env, jobject jthis, jobject jprompter) +{ + JNIEntry(SVNClient, setPrompt); + SVNClient *cl = SVNClient::getCppObject(jthis); + if (cl == NULL) + { + JNIUtil::throwError(_("bad C++ this")); + return; + } + Prompter::UniquePtr prompter(CompatPrompter::create(jprompter)); + if (JNIUtil::isExceptionThrown()) + return; + + cl->getClientContext().setPrompt(prompter); +} + +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, @@ -282,6 +317,14 @@ Java_org_apache_subversion_javahl_SVNClient_logMessages jobject jlogMessageCallback) { JNIEntry(SVNClient, logMessages); + + if (jlong(int(jlimit)) != jlimit) + { + JNIUtil::raiseThrowable("java/lang/IllegalArgumentException", + "The value of 'limit' is too large"); + return; + } + SVNClient *cl = SVNClient::getCppObject(jthis); if (cl == NULL) { @@ -323,7 +366,7 @@ Java_org_apache_subversion_javahl_SVNClient_logMessages cl->logMessages(path, pegRevision, revisionRanges, jstopOnCopy ? true: false, jdisoverPaths ? true : false, jincludeMergedRevisions ? true : false, - revProps, static_cast<long>(jlimit), &callback); + revProps, int(jlimit), &callback); } JNIEXPORT jlong JNICALL @@ -384,7 +427,7 @@ Java_org_apache_subversion_javahl_SVNClient_remove if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; @@ -395,8 +438,8 @@ Java_org_apache_subversion_javahl_SVNClient_remove JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNClient_revert -(JNIEnv *env, jobject jthis, jstring jpath, jobject jdepth, - jobject jchangelists) +(JNIEnv *env, jobject jthis, jobject jpaths, jobject jdepth, + jobject jchangelists, jboolean jclear_changelists, jboolean jmetadata_only) { JNIEntry(SVNClient, revert); SVNClient *cl = SVNClient::getCppObject(jthis); @@ -405,7 +448,9 @@ Java_org_apache_subversion_javahl_SVNClient_revert JNIUtil::throwError(_("bad C++ this")); return; } - JNIStringHolder path(jpath); + + SVN_JNI_NULL_PTR_EX(jpaths, "paths", ); + StringArray paths(jpaths); if (JNIUtil::isExceptionThrown()) return; @@ -413,7 +458,8 @@ Java_org_apache_subversion_javahl_SVNClient_revert if (JNIUtil::isExceptionThrown()) return; - cl->revert(path, EnumMapper::toDepth(jdepth), changelists); + cl->revert(paths, EnumMapper::toDepth(jdepth), + changelists, bool(jclear_changelists), bool(jmetadata_only)); } JNIEXPORT void JNICALL @@ -498,7 +544,7 @@ Java_org_apache_subversion_javahl_SVNClient_commit if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; @@ -513,6 +559,7 @@ JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNClient_copy (JNIEnv *env, jobject jthis, jobject jcopySources, jstring jdestPath, jboolean jcopyAsChild, jboolean jmakeParents, jboolean jignoreExternals, + jboolean jmetadataOnly, jboolean jpinExternals, jobject jexternalsToPin, jobject jrevpropTable, jobject jmessage, jobject jcallback) { JNIEntry(SVNClient, copy); @@ -539,13 +586,15 @@ Java_org_apache_subversion_javahl_SVNClient_copy if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; CommitCallback callback(jcallback); - cl->copy(copySources, destPath, &message, jcopyAsChild ? true : false, - jmakeParents ? true : false, jignoreExternals ? true : false, + cl->copy(copySources, destPath, &message, + bool(jcopyAsChild), bool(jmakeParents), + bool(jignoreExternals), bool(jmetadataOnly), + bool(jpinExternals), jexternalsToPin, revprops, jcallback ? &callback : NULL); } @@ -577,7 +626,7 @@ Java_org_apache_subversion_javahl_SVNClient_move if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; @@ -610,7 +659,7 @@ Java_org_apache_subversion_javahl_SVNClient_mkdir if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; @@ -621,7 +670,10 @@ Java_org_apache_subversion_javahl_SVNClient_mkdir JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNClient_cleanup -(JNIEnv *env, jobject jthis, jstring jpath) +(JNIEnv *env, jobject jthis, jstring jpath, + jboolean jbreakLocks, jboolean jfixRecordedTimestamps, + jboolean jclearDavCache, jboolean jremoveUnusedPristines, + jboolean jincludeExternals) { JNIEntry(SVNClient, cleanup); SVNClient *cl = SVNClient::getCppObject(jthis); @@ -634,7 +686,9 @@ Java_org_apache_subversion_javahl_SVNClient_cleanup if (JNIUtil::isExceptionThrown()) return; - cl->cleanup(path); + cl->cleanup(path, jbreakLocks, jfixRecordedTimestamps, + jclearDavCache, jremoveUnusedPristines, + jincludeExternals); } JNIEXPORT void JNICALL @@ -660,7 +714,8 @@ JNIEXPORT jlong JNICALL Java_org_apache_subversion_javahl_SVNClient_doExport (JNIEnv *env, jobject jthis, jstring jsrcPath, jstring jdestPath, jobject jrevision, jobject jpegRevision, jboolean jforce, - jboolean jignoreExternals, jobject jdepth, jstring jnativeEOL) + jboolean jignoreExternals, jboolean jignoreKeywords, + jobject jdepth, jstring jnativeEOL) { JNIEntry(SVNClient, doExport); SVNClient *cl = SVNClient::getCppObject(jthis); @@ -690,7 +745,9 @@ Java_org_apache_subversion_javahl_SVNClient_doExport return -1; return cl->doExport(srcPath, destPath, revision, pegRevision, - jforce ? true : false, jignoreExternals ? true : false, + jforce ? true : false, + jignoreExternals ? true : false, + jignoreKeywords ? true : false, EnumMapper::toDepth(jdepth), nativeEOL); } @@ -758,7 +815,7 @@ Java_org_apache_subversion_javahl_SVNClient_doImport if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; @@ -796,11 +853,11 @@ Java_org_apache_subversion_javahl_SVNClient_suggestMergeSources } JNIEXPORT void JNICALL -Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZ +Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZZ (JNIEnv *env, jobject jthis, jstring jpath1, jobject jrevision1, jstring jpath2, jobject jrevision2, jstring jlocalPath, jboolean jforceDelete, jobject jdepth, jboolean jignoreMergeinfo, jboolean jdiffIgnoreAncestry, - jboolean jdryRun, jboolean jrecordOnly) + jboolean jdryRun, jboolean jallowMixedRev, jboolean jrecordOnly) { JNIEntry(SVNClient, merge); SVNClient *cl = SVNClient::getCppObject(jthis); @@ -835,15 +892,16 @@ Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apach jignoreMergeinfo ? true:false, jdiffIgnoreAncestry ? true:false, jdryRun ? true:false, + jallowMixedRev ? true:false, jrecordOnly ? true:false); } JNIEXPORT void JNICALL -Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_util_List_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZ +Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apache_subversion_javahl_types_Revision_2Ljava_util_List_2Ljava_lang_String_2ZLorg_apache_subversion_javahl_types_Depth_2ZZZZZ (JNIEnv *env, jobject jthis, jstring jpath, jobject jpegRevision, jobject jranges, jstring jlocalPath, jboolean jforceDelete, jobject jdepth, jboolean jignoreMergeinfo, jboolean jdiffIgnoreAncestry, - jboolean jdryRun, jboolean jrecordOnly) + jboolean jdryRun, jboolean jallowMixedRev, jboolean jrecordOnly) { JNIEntry(SVNClient, merge); SVNClient *cl = SVNClient::getCppObject(jthis); @@ -894,6 +952,7 @@ Java_org_apache_subversion_javahl_SVNClient_merge__Ljava_lang_String_2Lorg_apach jignoreMergeinfo ? true:false, jdiffIgnoreAncestry ? true:false, jdryRun ? true:false, + jallowMixedRev ? true:false, jrecordOnly ? true:false); } @@ -1009,7 +1068,7 @@ Java_org_apache_subversion_javahl_SVNClient_propertySetRemote if (JNIUtil::isExceptionThrown()) return; - RevpropTable revprops(jrevpropTable); + PropertyTable revprops(jrevpropTable, false, false); if (JNIUtil::isExceptionThrown()) return; @@ -1439,35 +1498,44 @@ Java_org_apache_subversion_javahl_SVNClient_diffSummarize__Ljava_lang_String_2Lo jignoreAncestry ? true : false, receiver); } -JNIEXPORT void JNICALL +JNIEXPORT jobject JNICALL Java_org_apache_subversion_javahl_SVNClient_streamFileContent -(JNIEnv *env, jobject jthis, jstring jpath, jobject jrevision, - jobject jpegRevision, jobject jstream) +(JNIEnv *env, jobject jthis, jstring jpath, + jobject jrevision, jobject jpegRevision, + jboolean jexpand_keywords, jboolean jreturn_props, + jobject jstream) { JNIEntry(SVNClient, streamFileContent); SVNClient *cl = SVNClient::getCppObject(jthis); if (cl == NULL) { JNIUtil::throwError(_("bad C++ this")); - return; + return NULL; } JNIStringHolder path(jpath); if (JNIUtil::isExceptionThrown()) - return; + return NULL; Revision revision(jrevision); if (JNIUtil::isExceptionThrown()) - return; + return NULL; Revision pegRevision(jpegRevision); if (JNIUtil::isExceptionThrown()) - return; + return NULL; OutputStream dataOut(jstream); if (JNIUtil::isExceptionThrown()) - return; + return NULL; - cl->streamFileContent(path, revision, pegRevision, dataOut); + apr_hash_t* props = + cl->streamFileContent(path, revision, pegRevision, + bool(jexpand_keywords), bool(jreturn_props), + dataOut); + if (!jreturn_props || JNIUtil::isExceptionThrown()) + return NULL; + + return CreateJ::PropertyMap(props, SVN::Pool().getPool()); } JNIEXPORT jstring JNICALL @@ -1581,7 +1649,8 @@ JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNClient_blame (JNIEnv *env, jobject jthis, jstring jpath, jobject jpegRevision, jobject jrevisionStart, jobject jrevisionEnd, jboolean jignoreMimeType, - jboolean jincludeMergedRevisions, jobject jblameCallback) + jboolean jincludeMergedRevisions, jobject jblameCallback, + jobject jdiffOptions) { JNIEntry(SVNClient, blame); SVNClient *cl = SVNClient::getCppObject(jthis); @@ -1606,10 +1675,15 @@ Java_org_apache_subversion_javahl_SVNClient_blame if (JNIUtil::isExceptionThrown()) return; + DiffOptions options(jdiffOptions); + if (JNIUtil::isExceptionThrown()) + return; + BlameCallback callback(jblameCallback); cl->blame(path, pegRevision, revisionStart, revisionEnd, jignoreMimeType ? true : false, - jincludeMergedRevisions ? true : false, &callback); + jincludeMergedRevisions ? true : false, &callback, + options); } JNIEXPORT void JNICALL @@ -1648,6 +1722,36 @@ Java_org_apache_subversion_javahl_SVNClient_getConfigDirectory } JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_SVNClient_setConfigEventHandler( + JNIEnv* env, jobject jthis, jobject jcallback) +{ + JNIEntry(SVNClient, setConfigEventHandler); + SVNClient *cl = SVNClient::getCppObject(jthis); + if (cl == NULL) + { + JNIUtil::throwError(_("bad C++ this")); + return; + } + + cl->getClientContext().setConfigEventHandler(jcallback); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_SVNClient_getConfigEventHandler( + JNIEnv* env, jobject jthis) +{ + JNIEntry(SVNClient, getConfigEventHandler); + SVNClient *cl = SVNClient::getCppObject(jthis); + if (cl == NULL) + { + JNIUtil::throwError(_("bad C++ this")); + return NULL; + } + + return cl->getClientContext().getConfigEventHandler(); +} + +JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNClient_cancelOperation (JNIEnv *env, jobject jthis) { @@ -1738,8 +1842,9 @@ Java_org_apache_subversion_javahl_SVNClient_getChangelists return; ChangelistCallback callback(jchangelistCallback); - cl->getChangelists(root_path, changelists, EnumMapper::toDepth(jdepth), - &callback); + cl->getChangelists(root_path, + (jchangelists ? &changelists : NULL), + EnumMapper::toDepth(jdepth), &callback); } JNIEXPORT void JNICALL @@ -1790,12 +1895,14 @@ Java_org_apache_subversion_javahl_SVNClient_unlock } JNIEXPORT void JNICALL -Java_org_apache_subversion_javahl_SVNClient_info2 +Java_org_apache_subversion_javahl_SVNClient_info (JNIEnv *env, jobject jthis, jstring jpath, jobject jrevision, - jobject jpegRevision, jobject jdepth, jobject jchangelists, - jobject jinfoCallback) + jobject jpegRevision, jobject jdepth, + jboolean jfetchExcluded, jboolean jfetchActualOnly, + jboolean jincludeExternals, + jobject jchangelists, jobject jinfoCallback) { - JNIEntry(SVNClient, info2); + JNIEntry(SVNClient, info); SVNClient *cl = SVNClient::getCppObject(jthis); if (cl == NULL) { @@ -1819,8 +1926,10 @@ Java_org_apache_subversion_javahl_SVNClient_info2 return; InfoCallback callback(jinfoCallback); - cl->info2(path, revision, pegRevision, EnumMapper::toDepth(jdepth), - changelists, &callback); + cl->info(path, revision, pegRevision, EnumMapper::toDepth(jdepth), + svn_boolean_t(jfetchExcluded), svn_boolean_t(jfetchActualOnly), + svn_boolean_t(jincludeExternals), + changelists, &callback); } JNIEXPORT void JNICALL @@ -1851,3 +1960,44 @@ Java_org_apache_subversion_javahl_SVNClient_patch jreverse ? true : false, jignoreWhitespace ? true : false, jremoveTempfiles ? true : false, &callback); } + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_SVNClient_vacuum +(JNIEnv *env, jobject jthis, jstring jpath, + jboolean jremoveUnversionedItems, jboolean jremoveIgnoredItems, + jboolean jfixRecordedTimestamps, jboolean jremoveUnusedPristines, + jboolean jincludeExternals) +{ + JNIEntry(SVNClient, patch); + SVNClient *cl = SVNClient::getCppObject(jthis); + if (cl == NULL) + { + JNIUtil::throwError("bad C++ this"); + return; + } + + JNIStringHolder path(jpath); + cl->vacuum(path, + jremoveUnversionedItems, jremoveIgnoredItems, + jfixRecordedTimestamps, jremoveUnusedPristines, + jincludeExternals); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_SVNClient_nativeOpenRemoteSession +(JNIEnv *env, jobject jthis, jstring jpath, jint jretryAttempts) +{ + JNIEntry(SVNClient, openRemoteSession); + SVNClient *cl = SVNClient::getCppObject(jthis); + if (cl == NULL) + { + JNIUtil::throwError("bad C++ this"); + return NULL; + } + + JNIStringHolder path(jpath); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return cl->openRemoteSession(path, jretryAttempts); +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp index ee1c7c1..ae41bb3 100644 --- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_SVNRepos.cpp @@ -170,7 +170,7 @@ Java_org_apache_subversion_javahl_SVNRepos_dump JNIEXPORT void JNICALL Java_org_apache_subversion_javahl_SVNRepos_hotcopy (JNIEnv *env, jobject jthis, jobject jpath, jobject jtargetPath, - jboolean jcleanLogs, jboolean jincremental) + jboolean jcleanLogs, jboolean jincremental, jobject jnotifyCallback) { JNIEntry(SVNRepos, hotcopy); SVNRepos *cl = SVNRepos::getCppObject(jthis); @@ -188,8 +188,11 @@ Java_org_apache_subversion_javahl_SVNRepos_hotcopy if (JNIUtil::isExceptionThrown()) return; + ReposNotifyCallback notifyCallback(jnotifyCallback); + cl->hotcopy(path, targetPath, jcleanLogs ? true : false, - jincremental ? true : false); + jincremental ? true : false, + jnotifyCallback != NULL ? ¬ifyCallback : NULL); } JNIEXPORT void JNICALL @@ -239,11 +242,13 @@ Java_org_apache_subversion_javahl_SVNRepos_listUnusedDBLogs } JNIEXPORT void JNICALL -Java_org_apache_subversion_javahl_SVNRepos_load -(JNIEnv *env, jobject jthis, jobject jpath, jobject jinputData, - jobject jrevisionStart, jobject jrevisionEnd, - jboolean jignoreUUID, jboolean jforceUUID, jboolean jusePreCommitHook, - jboolean jusePostCommitHook, jstring jrelativePath, jobject jnotifyCallback) +Java_org_apache_subversion_javahl_SVNRepos_load( + JNIEnv *env, jobject jthis, jobject jpath, jobject jinputData, + jobject jrevisionStart, jobject jrevisionEnd, + jboolean jignoreUUID, jboolean jforceUUID, + jboolean jusePreCommitHook, jboolean jusePostCommitHook, + jboolean jvalidateProps, jboolean jignoreDates, + jstring jrelativePath, jobject jnotifyCallback) { JNIEntry(SVNRepos, load); SVNRepos *cl = SVNRepos::getCppObject(jthis); @@ -280,6 +285,8 @@ Java_org_apache_subversion_javahl_SVNRepos_load jforceUUID ? true : false, jusePreCommitHook ? true : false, jusePostCommitHook ? true : false, + jvalidateProps ? true : false, + jignoreDates ? true : false, relativePath, (jnotifyCallback != NULL ? ¬ifyCallback : NULL)); } @@ -409,9 +416,11 @@ Java_org_apache_subversion_javahl_SVNRepos_setRevProp } JNIEXPORT void JNICALL -Java_org_apache_subversion_javahl_SVNRepos_verify -(JNIEnv *env, jobject jthis, jobject jpath, jobject jrevisionStart, - jobject jrevisionEnd, jobject jcallback) +Java_org_apache_subversion_javahl_SVNRepos_verify( + JNIEnv *env, jobject jthis, jobject jpath, + jobject jrevisionStart, jobject jrevisionEnd, + jboolean jcheckNormalization, jboolean jmetadataOnly, + jobject jnotifyCallback, jobject jverifyCallback) { JNIEntry(SVNRepos, verify); SVNRepos *cl = SVNRepos::getCppObject(jthis); @@ -433,12 +442,18 @@ Java_org_apache_subversion_javahl_SVNRepos_verify if (JNIUtil::isExceptionThrown()) return; - ReposNotifyCallback callback(jcallback); + ReposNotifyCallback notify_cb(jnotifyCallback); + if (JNIUtil::isExceptionThrown()) + return; + + ReposVerifyCallback verify_cb(jverifyCallback); if (JNIUtil::isExceptionThrown()) return; cl->verify(path, revisionStart, revisionEnd, - jcallback != NULL ? &callback : NULL); + jcheckNormalization, jmetadataOnly, + jnotifyCallback != NULL ? ¬ify_cb : NULL, + jverifyCallback != NULL ? &verify_cb : NULL); } JNIEXPORT jobject JNICALL diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp new file mode 100644 index 0000000..e871b0c --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_CommitEditor.cpp @@ -0,0 +1,205 @@ +/** + * @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_remote_CommitEditor.cpp + * @brief Implementation of the native methods in the Java class CommitEditor + */ + +#include "../include/org_apache_subversion_javahl_remote_CommitEditor.h" + +#include "JNIStackElement.h" +#include "JNIUtil.h" +#include "CommitEditor.h" + +#include "svn_private_config.h" + + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_finalize( + JNIEnv *env, jobject jthis) +{ + JNIEntry(CommitEditor, finalize); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + if (editor != NULL) + editor->finalize(); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_nativeDispose( + JNIEnv *env, jobject jthis) +{ + JNIEntry(CommitEditor, nativeDispose); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + if (editor != NULL) + editor->dispose(jthis); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_nativeCreateInstance( + JNIEnv *env, jclass thisclass, jobject jsession, jobject jrevprops, + jobject jcommit_callback, jobject jlock_tokens, jboolean jkeep_locks, + jobject jget_base_cb, jobject jget_props_cb, jobject jget_kind_cb) +{ + jobject jthis = NULL; // Placeholder -- this is a static method + JNIEntry(CommitEditor, nativeCreateInstance); + + return CommitEditor::createInstance( + jsession, jrevprops, jcommit_callback, jlock_tokens, jkeep_locks, + jget_base_cb, jget_props_cb, jget_kind_cb); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_addDirectory( + JNIEnv* env, jobject jthis, + jstring jrelpath, jobject jchildren, jobject jproperties, + jlong jreplaces_revision) +{ + JNIEntry(CommitEditor, addDirectory); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->addDirectory(jrelpath, jchildren, jproperties, jreplaces_revision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_addFile( + JNIEnv* env, jobject jthis, + jstring jrelpath, jobject jchecksum, jobject jcontents, + jobject jproperties, jlong jreplaces_revision) +{ + JNIEntry(CommitEditor, addFile); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->addFile(jrelpath, jchecksum, jcontents, jproperties, + jreplaces_revision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_addSymlink( + JNIEnv* env, jobject jthis, + jstring jrelpath, jstring jtarget, jobject jproperties, + jlong jreplaces_revision) +{ + JNIEntry(CommitEditor, addSymlink); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->addSymlink(jrelpath, jtarget, jproperties, jreplaces_revision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_addAbsent( + JNIEnv* env, jobject jthis, + jstring jrelpath, jobject jkind, jlong jreplaces_revision) +{ + JNIEntry(CommitEditor, addAbsent); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->addAbsent(jrelpath, jkind, jreplaces_revision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_alterDirectory( + JNIEnv* env, jobject jthis, + jstring jrelpath, jlong jrevision, jobject jchildren, jobject jproperties) +{ + JNIEntry(CommitEditor, alterDirectory); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->alterDirectory(jrelpath, jrevision, jchildren, jproperties); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_alterFile( + JNIEnv* env, jobject jthis, + jstring jrelpath, jlong jrevision, jobject jchecksum, jobject jcontents, + jobject jproperties) +{ + JNIEntry(CommitEditor, alterFile); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->alterFile(jrelpath, jrevision, jchecksum, jcontents, jproperties); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_alterSymlink( + JNIEnv* env, jobject jthis, + jstring jrelpath, jlong jrevision, jstring jtarget, jobject jproperties) +{ + JNIEntry(CommitEditor, alterSymlink); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->alterSymlink(jrelpath, jrevision, jtarget, jproperties); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_delete( + JNIEnv* env, jobject jthis, + jstring jrelpath, jlong jrevision) +{ + JNIEntry(CommitEditor, delete); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->remove(jrelpath, jrevision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_copy( + JNIEnv* env, jobject jthis, + jstring jsrc_relpath, jlong jsrc_revision, jstring jdst_relpath, + jlong jreplaces_revision) +{ + JNIEntry(CommitEditor, copy); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->copy(jsrc_relpath, jsrc_revision, jdst_relpath, jreplaces_revision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_move( + JNIEnv* env, jobject jthis, + jstring jsrc_relpath, jlong jsrc_revision, jstring jdst_relpath, + jlong jreplaces_revision) +{ + JNIEntry(CommitEditor, move); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->move(jsrc_relpath, jsrc_revision, jdst_relpath, jreplaces_revision); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_complete( + JNIEnv* env, jobject jthis) +{ + JNIEntry(CommitEditor, complete); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->complete(); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_CommitEditor_abort( + JNIEnv* env, jobject jthis, jobject jsession) +{ + JNIEntry(CommitEditor, abort); + CommitEditor *editor = CommitEditor::getCppObject(jthis); + CPPADDR_NULL_PTR(editor,); + editor->abort(); +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp new file mode 100644 index 0000000..80c0c42 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteFactory.cpp @@ -0,0 +1,61 @@ +/** + * @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_remote_RemoteFactory.cpp + * @brief Implementation of the native methods in the Java class RemoteFactory + */ + +#include "../include/org_apache_subversion_javahl_remote_RemoteFactory.h" + +#include "JNIStackElement.h" +#include "JNIUtil.h" +#include "JNIStringHolder.h" + +#include "RemoteSession.h" + +#include "svn_private_config.h" + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteFactory_open( + JNIEnv *env, jclass jclazz, jint jretryAttempts, + jstring jurl, jstring juuid, + jstring jconfigDirectory, + jstring jusername, jstring jpassword, + jobject jprompter, jobject jdeprecatedPrompter, + jobject jprogress, jobject jcfgcb, jobject jtunnelcb) +{ + //JNI macros need jthis but this is a static call + JNIEntryStatic(RemoteFactory, open); + + /* + * Create RemoteSession C++ object and return its java wrapper to the caller + */ + jobject jremoteSession = RemoteSession::open( + jretryAttempts, jurl, juuid, + jconfigDirectory, jusername, jpassword, + jprompter, jdeprecatedPrompter, + jprogress, jcfgcb, jtunnelcb); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + return jremoteSession; +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp new file mode 100644 index 0000000..03079b6 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_RemoteSession.cpp @@ -0,0 +1,359 @@ +/** + * @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_remote_RemoteSession.cpp + * @brief Implementation of the native methods in the Java class RemoteSession + */ + +#include "../include/org_apache_subversion_javahl_remote_RemoteSession.h" + +#include "JNIStackElement.h" +#include "JNIUtil.h" +#include "Prompter.h" +#include "RemoteSession.h" +#include "Revision.h" +#include "EnumMapper.h" + +#include "svn_private_config.h" + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_finalize( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, finalize); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + if (ras != NULL) + ras->finalize(); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_nativeDispose( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, nativeDispose); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + if (ras != NULL) + ras->dispose(jthis); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_cancelOperation( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, cancelOperation); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, ); + + ras->cancelOperation(); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_reparent( + JNIEnv *env, jobject jthis, jstring jurl) +{ + JNIEntry(RemoteSession, getSessionUrl); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, ); + + ras->reparent(jurl); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getSessionRelativePath( + JNIEnv *env, jobject jthis, jstring jurl) +{ + JNIEntry(RemoteSession, getSessionUrl); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getSessionRelativePath(jurl); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getReposRelativePath( + JNIEnv *env, jobject jthis, jstring jurl) +{ + JNIEntry(RemoteSession, getSessionUrl); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getReposRelativePath(jurl); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getSessionUrl( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, getSessionUrl); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getSessionUrl(); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getReposUUID( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, geRepostUUID); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getReposUUID(); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getReposRootUrl( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, geRepostUUID); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getReposRootUrl(); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getLatestRevision( + JNIEnv *env, jobject jthis) +{ + JNIEntry(RemoteSession, getLatestRevision); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM); + + return ras->getLatestRevision(); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getRevisionByTimestamp( + JNIEnv *env, jobject jthis, jlong timestamp) +{ + JNIEntry(RemoteSession, getDatedRevision); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM); + + return ras->getRevisionByTimestamp(timestamp); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_nativeChangeRevisionProperty( + JNIEnv *env, jobject jthis, jlong jrevision, jstring jname, + jbyteArray jold_value, jbyteArray jvalue) +{ + JNIEntry(RemoteSession, nativeChangeRevisionProperty); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, ); + + return ras->changeRevisionProperty(jrevision, jname, jold_value, jvalue); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getRevisionProperties( + JNIEnv *env, jobject jthis, jlong jrevision) +{ + JNIEntry(SVNReposAccess, getRevisionProperties); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getRevisionProperties(jrevision); +} + +JNIEXPORT jbyteArray JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getRevisionProperty( + JNIEnv *env, jobject jthis, jlong jrevision, jstring jname) +{ + JNIEntry(SVNReposAccess, getRevisionProperty); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getRevisionProperty(jrevision, jname); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_nativeGetFile( + JNIEnv *env, jobject jthis, jlong jrevision, jstring jpath, + jobject jcontents, jobject jproperties) +{ + JNIEntry(SVNReposAccess, nativeGetFile); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM); + + return ras->getFile(jrevision, jpath, jcontents, jproperties); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_nativeGetDirectory( + JNIEnv *env, jobject jthis, jlong jrevision, jstring jpath, + jint jdirent_fields, jobject jdirents, jobject jproperties) +{ + JNIEntry(SVNReposAccess, nativeGetDirectory); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, SVN_INVALID_REVNUM); + + return ras->getDirectory(jrevision, jpath, + jdirent_fields, jdirents, jproperties); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getMergeinfo( + JNIEnv *env, jobject jthis, jobject jpaths, jlong jrevision, + jobject jinherit, jboolean jinclude_descendants) +{ + JNIEntry(SVNReposAccess, getMergeinfo); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getMergeinfo(jpaths, jrevision, jinherit, jinclude_descendants); +} + +// TODO: update +// TODO: switch + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_nativeStatus( + JNIEnv *env, jobject jthis, jstring jstatus_target, jlong jrevision, + jobject jdepth, jobject jstatus_editor, jobject jreporter) +{ + JNIEntry(SVNReposAccess, nativeStatus); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras,); + + ras->status(jthis, jstatus_target, jrevision, jdepth, + jstatus_editor, jreporter); +} + +// TODO: diff + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getLog( + JNIEnv *env, jobject jthis, 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) +{ + JNIEntry(SVNReposAccess, getLog); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras,); + + ras->getLog(jpaths, jstartrev, jendrev, jlimit, + jstrict_node_history, jdiscover_changed_paths, + jinclude_merged_revisions, + jrevprops, jlog_callback); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_checkPath( + JNIEnv *env, jobject jthis, jstring jpath, jlong jrevision) +{ + JNIEntry(SVNReposAccess, checkPath); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->checkPath(jpath, jrevision); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_stat( + JNIEnv *env, jobject jthis, jstring jpath, jlong jrevision) +{ + JNIEntry(SVNReposAccess, stat); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->stat(jpath, jrevision); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getLocations( + JNIEnv *env, jobject jthis, jstring jpath, jlong jpeg_revision, + jobject jlocation_revisions) +{ + JNIEntry(SVNReposAccess, getLocations); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getLocations(jpath, jpeg_revision, jlocation_revisions); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getLocationSegments( + JNIEnv *env, jobject jthis, jstring jpath, jlong jpeg_revision, + jlong jstart_revision, jlong jend_revision, jobject jcallback) +{ + JNIEntry(SVNReposAccess, getLocationSegments); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, ); + + if (jcallback == NULL) + JNIUtil::throwNullPointerException("handler"); + ras->getLocationSegments(jpath, jpeg_revision, + jstart_revision, jend_revision, + jcallback); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getFileRevisions( + JNIEnv *env, jobject jthis, jstring jpath, + jlong jstart_revision, jlong jend_revision, + jboolean jinclude_merged_revisions, jobject jcallback) +{ + JNIEntry(SVNReposAccess, getFileRevisions); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, ); + + if (jcallback == NULL) + JNIUtil::throwNullPointerException("handler"); + ras->getFileRevisions(jpath, jstart_revision, jend_revision, + jinclude_merged_revisions, jcallback); +} + +// TODO: lock +// TODO: unlock +// TODO: getLock + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_getLocks( + JNIEnv *env, jobject jthis, jstring jpath, jobject jdepth) +{ + JNIEntry(RemoteSession, getLocks); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, NULL); + + return ras->getLocks(jpath, jdepth); +} + +// TODO: replayRange +// TODO: replay +// TODO: getDeletedRevision +// TODO: getInheritedProperties + +JNIEXPORT jboolean JNICALL +Java_org_apache_subversion_javahl_remote_RemoteSession_nativeHasCapability( + JNIEnv *env, jobject jthis, jstring jcapability) +{ + JNIEntry(RemoteSession, nativeHasCapability); + RemoteSession *ras = RemoteSession::getCppObject(jthis); + CPPADDR_NULL_PTR(ras, false); + + return ras->hasCapability(jcapability); +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp new file mode 100644 index 0000000..f5c291b --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_remote_StateReporter.cpp @@ -0,0 +1,117 @@ +/** + * @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_remote_StateReporter.cpp + * @brief Implementation of the native methods in the Java class StateReporter + */ + +#include "../include/org_apache_subversion_javahl_remote_StateReporter.h" + +#include <jni.h> +#include "JNIStackElement.h" +#include "JNIUtil.h" +#include "StateReporter.h" + +#include "svn_private_config.h" + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_finalize( + JNIEnv *env, jobject jthis) +{ + JNIEntry(StateReporter, finalize); + StateReporter *reporter = StateReporter::getCppObject(jthis); + if (reporter != NULL) + reporter->finalize(); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_nativeDispose( + JNIEnv* env, jobject jthis) +{ + JNIEntry(StateReporter, nativeCreateInstance); + StateReporter* reporter = StateReporter::getCppObject(jthis); + CPPADDR_NULL_PTR(reporter,); + reporter->dispose(jthis); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_setPath( + JNIEnv* env, jobject jthis, + jstring jpath, jlong jrevision, jobject jdepth, + jboolean jstart_empty, jstring jlock_token) +{ + JNIEntry(StateReporter, setPath); + StateReporter* reporter = StateReporter::getCppObject(jthis); + CPPADDR_NULL_PTR(reporter,); + reporter->setPath(jpath, jrevision, jdepth, jstart_empty, jlock_token); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_deletePath( + JNIEnv* env, jobject jthis, jstring jpath) +{ + JNIEntry(StateReporter, deletePath); + StateReporter* reporter = StateReporter::getCppObject(jthis); + CPPADDR_NULL_PTR(reporter,); + reporter->deletePath(jpath); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_linkPath( + JNIEnv* env, jobject jthis, + jstring jurl, jstring jpath, jlong jrevision, jobject jdepth, + jboolean jstart_empty, jstring jlock_token) +{ + JNIEntry(StateReporter, linkPath); + StateReporter* reporter = StateReporter::getCppObject(jthis); + CPPADDR_NULL_PTR(reporter,); + reporter->linkPath(jurl, jpath, jrevision, jdepth, jstart_empty, jlock_token); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_finishReport( + JNIEnv* env, jobject jthis) +{ + JNIEntry(StateReporter, finishReport); + StateReporter* reporter = StateReporter::getCppObject(jthis); + CPPADDR_NULL_PTR(reporter, SVN_INVALID_REVNUM); + return reporter->finishReport(); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_abortReport( + JNIEnv* env, jobject jthis) +{ + JNIEntry(StateReporter, abortReport); + StateReporter* reporter = StateReporter::getCppObject(jthis); + CPPADDR_NULL_PTR(reporter,); + reporter->abortReport(); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_remote_StateReporter_nativeCreateInstance( + JNIEnv* env, jclass clazz) +{ + jobject jthis = NULL; + JNIEntry(StateReporter, nativeCreateInstance); + return reinterpret_cast<jlong>(new StateReporter); +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp new file mode 100644 index 0000000..4c54118 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RevisionRangeList.cpp @@ -0,0 +1,58 @@ +/** + * @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_types_RevisionRangeList.cpp + * @brief Implementation of the native methods in the Java class + * RevisionRangeList. + */ + + +#include "../include/org_apache_subversion_javahl_types_RevisionRangeList.h" +#include "JNIStackElement.h" +#include "RevisionRangeList.h" +#include "Pool.h" + +#include "svn_mergeinfo.h" +#include "svn_private_config.h" + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_types_RevisionRangeList_remove( + JNIEnv* env, jobject jthis, jobject jeraser, + jboolean jconsider_inheritance) +{ + JNIEntry(RevisionRangeList, remove); + + SVN::Pool request_pool; + + RevisionRangeList rangelist = RevisionRangeList::create(jthis, request_pool); + CPPADDR_NULL_PTR(rangelist.get(), NULL); + + RevisionRangeList eraser(jeraser, request_pool); + CPPADDR_NULL_PTR(eraser.get(), NULL); + + svn_rangelist_t *output; + SVN_JNI_ERR(svn_rangelist_remove(&output, eraser.get(), rangelist.get(), + bool(jconsider_inheritance), + request_pool.getPool()), + NULL); + return RevisionRangeList(output).toList(); +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp new file mode 100644 index 0000000..ed43fb7 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_RuntimeVersion.cpp @@ -0,0 +1,69 @@ +/** + * @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_types_RuntimeVersion.cpp + * @brief Implementation of the native methods in the Java class RuntimeVersion. + */ + +#include "../include/org_apache_subversion_javahl_types_RuntimeVersion.h" +#include "svn_client.h" +#include "svn_version.h" + +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_string.hpp" + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_types_RuntimeVersion_getMajor( + JNIEnv* jenv, jobject jthis) +{ + const svn_version_t* const version = svn_client_version(); + return jint(version->major); +} + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_types_RuntimeVersion_getMinor( + JNIEnv* jenv, jobject jthis) +{ + const svn_version_t* const version = svn_client_version(); + return jint(version->minor); +} + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_types_RuntimeVersion_getPatch( + JNIEnv* jenv, jobject jthis) +{ + const svn_version_t* const version = svn_client_version(); + return jint(version->patch); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_types_RuntimeVersion_getNumberTag( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(RuntimeVersion, getNumberTag) + { + const svn_version_t* const version = svn_client_version(); + return Java::String(Java::Env(jenv), version->tag).get(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp index 8fd8aee..72d52a3 100644 --- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_Version.cpp @@ -28,12 +28,19 @@ #include "JNIStackElement.h" #include "svn_version.h" +namespace { +const svn_version_t* javahl_version() +{ + SVN_VERSION_BODY; +} +} //anonymous namespace + JNIEXPORT jint JNICALL Java_org_apache_subversion_javahl_types_Version_getMajor(JNIEnv *env, jobject jthis) { JNIEntry(Version, getMajor); - return SVN_VER_MAJOR; + return javahl_version()->major; } JNIEXPORT jint JNICALL @@ -41,7 +48,7 @@ Java_org_apache_subversion_javahl_types_Version_getMinor(JNIEnv *env, jobject jthis) { JNIEntry(Version, getMinor); - return SVN_VER_MINOR; + return javahl_version()->minor; } JNIEXPORT jint JNICALL @@ -49,7 +56,7 @@ Java_org_apache_subversion_javahl_types_Version_getPatch(JNIEnv *env, jobject jthis) { JNIEntry(Version, getPatch); - return SVN_VER_PATCH; + return javahl_version()->patch; } JNIEXPORT jstring JNICALL diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp index e24801a..b1ecd72 100644 --- a/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_types_VersionExtended.cpp @@ -33,8 +33,24 @@ #include "JNIStackElement.h" #include <string> +#include "svn_private_config.h" + // VersionExtended native methods +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_types_VersionExtended_dispose( + JNIEnv *env, jobject jthis) +{ + JNIEntry(VersionExtended, dispose); + VersionExtended *const vx = VersionExtended::getCppObject(jthis); + if (vx == NULL) + { + JNIUtil::throwError(_("bad C++ this")); + return; + } + vx->dispose(jthis); +} + JNIEXPORT jstring JNICALL Java_org_apache_subversion_javahl_types_VersionExtended_getBuildDate( JNIEnv *env, jobject jthis) diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp new file mode 100644 index 0000000..5fa5c89 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigImpl_Category.cpp @@ -0,0 +1,314 @@ +/** + * @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_ConfigImpl_Category.cpp + * @brief Implementation of the native methods in the Java class + * util.ConfigImpl.Category. + */ + +#include <string> +#include <vector> + +#include "../include/org_apache_subversion_javahl_util_ConfigImpl_Category.h" +#include "JNIUtil.h" +#include "JNIStackElement.h" +#include "JNIStringHolder.h" +#include "OperationContext.h" +#include "CreateJ.h" +#include "EnumMapper.h" + +#include "svn_config.h" +#include "svn_hash.h" +#include "svn_private_config.h" + +namespace { +struct ImplContext +{ + ImplContext(JNIEnv* env, jobject jthis, + jstring jcategory, jlong jcontext, + jstring jsection, jstring joption) : m_config(NULL) + { + OperationContext* const context( + reinterpret_cast<OperationContext*>(jcontext)); + CPPADDR_NULL_PTR(context,); + + JNIStringHolder category(jcategory); + if (JNIUtil::isJavaExceptionThrown()) + return; + if (category.c_str()) + { + apr_hash_t* cfgdata = context->getConfigData(); + if (cfgdata) + m_config = static_cast<svn_config_t*>( + svn_hash_gets(cfgdata, category.c_str())); + else + JNIUtil::throwNullPointerException("getConfigData"); + } + if (!m_config) + JNIUtil::throwNullPointerException("category"); + + JNIStringHolder section(jsection); + if (JNIUtil::isJavaExceptionThrown()) + return; + if (section.c_str()) + m_section = section.c_str(); + + JNIStringHolder option(joption); + if (JNIUtil::isJavaExceptionThrown()) + return; + if (option.c_str()) + m_option = option.c_str(); + } + + svn_config_t* m_config; + std::string m_section; + std::string m_option; +}; +} // anonymous namespace + + + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1str( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jstring jdefault_value) +{ + JNIEntry(ConfigImpl$Category, get_str); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + JNIStringHolder default_value(jdefault_value); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + const char* value; + svn_config_get(ctx.m_config, &value, + ctx.m_section.c_str(), ctx.m_option.c_str(), + default_value.c_str()); + return JNIUtil::makeJString(value); +} + +JNIEXPORT jboolean JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1bool( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jboolean jdefault_value) +{ + JNIEntry(ConfigImpl$Category, get_bool); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + svn_boolean_t value; + SVN_JNI_ERR(svn_config_get_bool(ctx.m_config, &value, + ctx.m_section.c_str(), ctx.m_option.c_str(), + bool(jdefault_value)), + jdefault_value); + return jboolean(value); +} + +JNIEXPORT jlong JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1long( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jlong jdefault_value) +{ + JNIEntry(ConfigImpl$Category, get_long); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + apr_int64_t value; + SVN_JNI_ERR(svn_config_get_int64(ctx.m_config, &value, + ctx.m_section.c_str(), ctx.m_option.c_str(), + apr_int64_t(jdefault_value)), + jdefault_value); + return jlong(value); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1tri( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, + jstring junknown, jobject jdefault_value) +{ + JNIEntry(ConfigImpl$Category, get_tri); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + JNIStringHolder unknown(junknown); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + svn_tristate_t value; + SVN_JNI_ERR(svn_config_get_tristate(ctx.m_config, &value, + ctx.m_section.c_str(), + ctx.m_option.c_str(), + unknown.c_str(), + EnumMapper::toTristate(jdefault_value)), + NULL); + return EnumMapper::mapTristate(value); +} + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_get_1yna( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jstring jdefault_value) +{ + JNIEntry(ConfigImpl$Category, get_yna); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + JNIStringHolder default_value(jdefault_value); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + + const char* value; + SVN_JNI_ERR(svn_config_get_yes_no_ask( + ctx.m_config, &value, + ctx.m_section.c_str(), ctx.m_option.c_str(), + default_value.c_str()), + NULL); + return JNIUtil::makeJString(value); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_set_1str( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jstring jvalue) +{ + JNIEntry(ConfigImpl$Category, set_str); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + JNIStringHolder value(jvalue); + if (JNIUtil::isJavaExceptionThrown()) + return; + + svn_config_set(ctx.m_config, + ctx.m_section.c_str(), ctx.m_option.c_str(), + value.c_str()); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_set_1bool( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jboolean jvalue) +{ + JNIEntry(ConfigImpl$Category, set_bool); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + svn_config_set_bool(ctx.m_config, + ctx.m_section.c_str(), ctx.m_option.c_str(), + bool(jvalue)); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_set_1long( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jstring joption, jlong jvalue) +{ + JNIEntry(ConfigImpl$Category, set_long); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, joption); + + svn_config_set_int64(ctx.m_config, + ctx.m_section.c_str(), ctx.m_option.c_str(), + apr_int64_t(jvalue)); +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_sections( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext) +{ + JNIEntry(ConfigImpl$Category, sections); + const ImplContext ctx(env, jthis, jcategory, jcontext, NULL, NULL); + + struct enumerator_t + { + static svn_boolean_t process(const char* name, void* baton, + apr_pool_t *pool) + { + jstring jname = JNIUtil::makeJString(name); + if (JNIUtil::isJavaExceptionThrown()) + return false; + static_cast<enumerator_t*>(baton) + ->m_sections.push_back(jobject(jname)); + return true; + } + std::vector<jobject> m_sections; + } enumerator; + + SVN::Pool requestPool; + svn_config_enumerate_sections2(ctx.m_config, enumerator.process, &enumerator, + requestPool.getPool()); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + return CreateJ::Set(enumerator.m_sections); +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_ConfigImpl_00024Category_enumerate( + JNIEnv* env, jobject jthis, jstring jcategory, jlong jcontext, + jstring jsection, jobject jhandler) +{ + JNIEntry(ConfigImpl$Category, sections); + const ImplContext ctx(env, jthis, jcategory, jcontext, jsection, NULL); + + struct enumerator_t + { + static svn_boolean_t process(const char* name, const char* value, + void* baton, apr_pool_t *pool) + { + enumerator_t* enmr = static_cast<enumerator_t*>(baton); + JNIEnv* const e = enmr->m_env; + const jobject jh = enmr->m_jhandler;; + + static jmethodID mid = 0; + if (0 == mid) + { + jclass cls = e->FindClass(JAVAHL_CLASS("/ISVNConfig$Enumerator")); + if (JNIUtil::isJavaExceptionThrown()) + return false; + mid = e->GetMethodID(cls, "option", + "(Ljava/lang/String;Ljava/lang/String;)V"); + if (JNIUtil::isJavaExceptionThrown()) + return false; + } + + jstring jname = JNIUtil::makeJString(name); + if (JNIUtil::isJavaExceptionThrown()) + return false; + jstring jvalue = JNIUtil::makeJString(value); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + e->CallVoidMethod(jh, mid, jname, jvalue); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + e->DeleteLocalRef(jname); + e->DeleteLocalRef(jvalue); + return true; + } + + JNIEnv* m_env; + jobject m_jhandler; + } enumerator; + + enumerator.m_env = env; + enumerator.m_jhandler = jhandler; + + SVN::Pool requestPool; + svn_config_enumerate2(ctx.m_config, ctx.m_section.c_str(), + enumerator.process, &enumerator, + requestPool.getPool()); +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp new file mode 100644 index 0000000..0630098 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_ConfigLib.cpp @@ -0,0 +1,492 @@ +/** + * @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_ConfigLib.cpp + * @brief Implementation of the native methods in the Java class ConfigLib + */ + +#include "../include/org_apache_subversion_javahl_util_ConfigLib.h" + +#include <apr_fnmatch.h> +#include <apr_strings.h> + +#include "jniwrapper/jni_list.hpp" +#include "jniwrapper/jni_stack.hpp" + +#include "AuthnCallback.hpp" +#include "Credential.hpp" +#include "SubversionException.hpp" + +#include "GlobalConfig.h" +#include "JNIUtil.h" +#include "JNICriticalSection.h" + +#include "svn_auth.h" +#include "svn_base64.h" +#include "svn_config.h" +#include "svn_hash.h" +#include "svn_x509.h" + +#include "svn_private_config.h" + +namespace { +bool g_ignore_native_credentials = false; +} // anonymous namespace + +bool GlobalConfig::useNativeCredentialsStore() +{ + JNICriticalSection lock(*JNIUtil::g_configMutex); + return !g_ignore_native_credentials; +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_ConfigLib_enableNativeCredentialsStore( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(ConfigLib, enableNativeCredentialsStore) + { + JNICriticalSection lock(*JNIUtil::g_configMutex); + g_ignore_native_credentials = false; + } + SVN_JAVAHL_JNI_CATCH; +} + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_ConfigLib_disableNativeCredentialsStore( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(ConfigLib, disableNativeCredentialsStore) + { + JNICriticalSection lock(*JNIUtil::g_configMutex); + g_ignore_native_credentials = true; + } + SVN_JAVAHL_JNI_CATCH; +} + +JNIEXPORT jboolean JNICALL +Java_org_apache_subversion_javahl_util_ConfigLib_isNativeCredentialsStoreEnabled( + JNIEnv* jenv, jobject jthis) +{ + SVN_JAVAHL_JNI_TRY(ConfigLib, isNativeCredentialsStoreEnabled) + { + return jboolean(GlobalConfig::useNativeCredentialsStore()); + } + SVN_JAVAHL_JNI_CATCH; + return JNI_FALSE; +} + + +namespace { +jobject build_credential(Java::Env env, apr_hash_t* cred, + const char* cred_kind, const char* realm, + apr_pool_t* scratch_pool) +{ + svn_string_t* entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_REALMSTRING_KEY)); + if (!entry || !realm || 0 != strcmp(realm, entry->data)) + { + JavaHL::SubversionException(env).throw_java_exception( + apr_psprintf(scratch_pool, + "Unexpected realm; got: [%s], expected: [%s]", + (entry ? entry->data : "(null)"), + (realm ? realm : "(null)"))); + } + + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSTYPE_KEY)); + + const char* store = (entry ? entry->data : NULL); + const char* username = NULL; + const char* password = NULL; + jobject info = NULL; + jobject failures = NULL; + const char* passphrase = NULL; + + if (0 == strcmp(cred_kind, SVN_AUTH_CRED_USERNAME)) + { + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_USERNAME_KEY)); + if (entry) + username = entry->data; + } + else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SIMPLE)) + { + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_USERNAME_KEY)); + if (entry) + username = entry->data; + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSWORD_KEY)); + if (entry) + password = entry->data; + } + else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SSL_SERVER_TRUST)) + { + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_ASCII_CERT_KEY)); + const char* ascii_cert = (entry ? entry->data : NULL); + + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_FAILURES_KEY)); + jint failflags = (!entry ? 0 : jint(apr_atoi64(entry->data))); + + info = JavaHL::AuthnCallback::SSLServerCertInfo(env, ascii_cert).get(); + failures = JavaHL::AuthnCallback + ::SSLServerCertFailures(env, failflags).get(); + } + else if (0 == strcmp(cred_kind, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW)) + { + entry = static_cast<svn_string_t*>( + svn_hash_gets(cred, SVN_CONFIG_AUTHN_PASSPHRASE_KEY)); + if (entry) + passphrase = entry->data; + } + else + { + JavaHL::SubversionException(env).throw_java_exception( + apr_psprintf(scratch_pool, + "Invalid credential type: [%s]", + cred_kind)); + } + + return JavaHL::Credential( + env, + JavaHL::Credential::Kind(env, Java::String(env, cred_kind)).get(), + Java::String(env, realm), Java::String(env, store), + Java::String(env, username), Java::String(env, password), + info, failures, Java::String(env, passphrase)).get(); +} + +class WalkCredentialsCallback +{ +public: + static svn_error_t* walk_func(svn_boolean_t *delete_cred, + void *walk_baton, + const char *cred_kind, + const char *realmstring, + apr_hash_t *cred, + apr_pool_t *scratch_pool) + { + WalkCredentialsCallback& self = + *static_cast<WalkCredentialsCallback*>(walk_baton); + return self(delete_cred, cred_kind, realmstring, cred, scratch_pool); + } + + virtual svn_error_t* operator()(svn_boolean_t *delete_cred, + const char *cred_kind, + const char *realmstring, + apr_hash_t *cred_hash, + apr_pool_t *scratch_pool) = 0; +}; + +class SimpleSearchCallback : public WalkCredentialsCallback +{ + const ::Java::Env m_env; + const char* const m_cred_kind; + const char* const m_realm; + const bool m_delete_when_found; + jobject m_credential; + +public: + explicit SimpleSearchCallback(::Java::Env env, + const char* cred_kind, const char* realm, + bool delete_when_found) + : m_env(env), + m_cred_kind(cred_kind), + m_realm(realm), + m_delete_when_found(delete_when_found), + m_credential(NULL) + {} + + jobject credential() const { return m_credential; } + + virtual svn_error_t* operator()(svn_boolean_t *delete_cred, + const char *cred_kind, + const char *realmstring, + apr_hash_t *cred_hash, + apr_pool_t *scratch_pool) + { + if (0 == strcmp(cred_kind, m_cred_kind) + && 0 == strcmp(realmstring, m_realm)) + { + m_credential = build_credential(m_env, cred_hash, + cred_kind, realmstring, + scratch_pool); + *delete_cred = m_delete_when_found; + return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, ""); + } + + *delete_cred = false; + return SVN_NO_ERROR; + } +}; +} // anonymous namespace + + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_ConfigLib_nativeGetCredential( + JNIEnv* jenv, jobject jthis, + jstring jconfig_dir, jstring jcred_kind, jstring jrealm) +{ + SVN_JAVAHL_JNI_TRY(ConfigLib, nativeGetCredential) + { + if (!GlobalConfig::useNativeCredentialsStore()) + return NULL; + + const Java::Env env(jenv); + const Java::String config_dir(env, jconfig_dir); + const Java::String cred_kind(env, jcred_kind); + const Java::String realm(env, jrealm); + + // Using a "global" request pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool pool; + + SimpleSearchCallback cb(env, + cred_kind.strdup(pool.getPool()), + realm.strdup(pool.getPool()), + false); + + SVN_JAVAHL_CHECK(env, + svn_config_walk_auth_data( + Java::String::Contents(config_dir).c_str(), + cb.walk_func, &cb, pool.getPool())); + return cb.credential(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_ConfigLib_nativeRemoveCredential( + JNIEnv* jenv, jobject jthis, + jstring jconfig_dir, jstring jcred_kind, jstring jrealm) +{ + SVN_JAVAHL_JNI_TRY(ConfigLib, nativeRemoveCredential) + { + if (!GlobalConfig::useNativeCredentialsStore()) + return NULL; + + const Java::Env env(jenv); + const Java::String config_dir(env, jconfig_dir); + const Java::String cred_kind(env, jcred_kind); + const Java::String realm(env, jrealm); + + // Using a "global" request pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool pool; + + SimpleSearchCallback cb(env, + cred_kind.strdup(pool.getPool()), + realm.strdup(pool.getPool()), + true); + + SVN_JAVAHL_CHECK(env, + svn_config_walk_auth_data( + Java::String::Contents(config_dir).c_str(), + cb.walk_func, &cb, pool.getPool())); + return cb.credential(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_ConfigLib_nativeSearchCredentials( + JNIEnv* jenv, jobject jthis, + jstring jconfig_dir, jstring jcred_kind, + jstring jrealm_pattern, jstring jusername_pattern, + jstring jhostname_pattern, jstring jtext_pattern) +{ + SVN_JAVAHL_JNI_TRY(ConfigLib, iterateCredentials) + { + if (!GlobalConfig::useNativeCredentialsStore()) + return NULL; + + const Java::Env env(jenv); + const Java::String config_dir(env, jconfig_dir); + const Java::String cred_kind(env, jcred_kind); + const Java::String realm_pattern(env, jrealm_pattern); + const Java::String username_pattern(env, jusername_pattern); + const Java::String hostname_pattern(env, jhostname_pattern); + const Java::String text_pattern(env, jtext_pattern); + + // Using a "global" request pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool pool; + + class Callback : public WalkCredentialsCallback + { + const char* const m_cred_kind; + const char* const m_realm_pattern; + const char* const m_username_pattern; + const char* const m_hostname_pattern; + const char* const m_text_pattern; + + const ::Java::Env m_env; + ::Java::List<JavaHL::Credential> m_credentials; + + bool match_array(const char* pattern, + const apr_array_header_t* hostnames) + { + for (int i = 0; i < hostnames->nelts; ++i) + { + const char* const hostname = + APR_ARRAY_IDX(hostnames, i, const char*); + if (!apr_fnmatch(pattern, hostname, 0)) + return true; + } + return false; + } + + public: + explicit Callback(::Java::Env ctor_env, + const char* ctor_cred_kind, + const char* ctor_realm_pattern, + const char* ctor_username_pattern, + const char* ctor_hostname_pattern, + const char* ctor_text_pattern) + : m_cred_kind(ctor_cred_kind), + m_realm_pattern(ctor_realm_pattern), + m_username_pattern(ctor_username_pattern), + m_hostname_pattern(ctor_hostname_pattern), + m_text_pattern(ctor_text_pattern), + m_env(ctor_env), + m_credentials(ctor_env) + {} + + jobject credentials() const + { + if (m_credentials.is_empty()) + return NULL; + return m_credentials.get(); + } + + virtual svn_error_t* operator()(svn_boolean_t *delete_cred, + const char *cb_cred_kind, + const char *cb_realmstring, + apr_hash_t *cb_cred_hash, + apr_pool_t *cb_scratch_pool) + { + *delete_cred = false; + if (m_cred_kind && 0 != strcmp(cb_cred_kind, m_cred_kind)) + return SVN_NO_ERROR; + + svn_string_t* entry = static_cast<svn_string_t*>( + svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_USERNAME_KEY)); + const char* const username = (entry ? entry->data : NULL); + + entry = static_cast<svn_string_t*>( + svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_PASSTYPE_KEY)); + const char* const store = (entry ? entry->data : NULL); + + const svn_string_t* ascii_cert = static_cast<svn_string_t*>( + svn_hash_gets(cb_cred_hash, SVN_CONFIG_AUTHN_ASCII_CERT_KEY)); + + /* Parsed certificate data. */ + const char* subject = NULL; + const char* issuer = NULL; + const char* fingerprint = NULL; + const apr_array_header_t* hostnames = NULL; + + if (ascii_cert) + { + const svn_string_t* const der = + svn_base64_decode_string(ascii_cert, cb_scratch_pool); + svn_x509_certinfo_t* certinfo; + svn_error_t* err = svn_x509_parse_cert( + &certinfo, der->data, der->len, + cb_scratch_pool, cb_scratch_pool); + if (err) + { + // Ignore credentials that can't be parsed. + svn_error_clear(err); + return SVN_NO_ERROR; + } + else + { + subject = svn_x509_certinfo_get_subject( + certinfo, cb_scratch_pool); + issuer = svn_x509_certinfo_get_issuer( + certinfo, cb_scratch_pool); + fingerprint = svn_checksum_to_cstring_display( + svn_x509_certinfo_get_digest(certinfo), + cb_scratch_pool); + hostnames = svn_x509_certinfo_get_hostnames(certinfo); + } + } + + bool match = (m_realm_pattern + && !apr_fnmatch(m_realm_pattern, cb_realmstring, 0)); + + if (!match && m_username_pattern) + match = (match + || (username + && !apr_fnmatch(m_username_pattern, username, 0))); + + if (!match && m_hostname_pattern) + match = (match + || (hostnames + && match_array(m_hostname_pattern, hostnames))); + + if (!match && m_text_pattern) + match = (match + || (username + && !apr_fnmatch(m_text_pattern, username, 0)) + || (store + && !apr_fnmatch(m_text_pattern, store, 0)) + || (subject + && !apr_fnmatch(m_text_pattern, subject, 0)) + || (issuer + && !apr_fnmatch(m_text_pattern, issuer, 0)) + || (fingerprint + && !apr_fnmatch(m_text_pattern, fingerprint, 0)) + || (hostnames + && match_array(m_text_pattern, hostnames))); + + if (match) + m_credentials.add( + ::JavaHL::Credential(m_env, + build_credential(m_env, + cb_cred_hash, + cb_cred_kind, + cb_realmstring, + cb_scratch_pool))); + + return SVN_NO_ERROR; + } + } cb(env, + cred_kind.strdup(pool.getPool()), + realm_pattern.strdup(pool.getPool()), + username_pattern.strdup(pool.getPool()), + hostname_pattern.strdup(pool.getPool()), + text_pattern.strdup(pool.getPool())); + + SVN_JAVAHL_CHECK(env, + svn_config_walk_auth_data( + Java::String::Contents(config_dir).c_str(), + cb.walk_func, &cb, pool.getPool())); + return cb.credentials(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp new file mode 100644 index 0000000..c6d3d6e --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_DiffLib.cpp @@ -0,0 +1,206 @@ +/** + * @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_DiffLib.cpp + * @brief Implementation of the native methods in the Java class DiffLib + */ + +#include "../include/org_apache_subversion_javahl_util_DiffLib.h" + +#include "JNIStackElement.h" +#include "JNIStringHolder.h" +#include "JNIUtil.h" +#include "OutputStream.h" +#include "Path.h" +#include "Pool.h" + +#include "svn_diff.h" + +JNIEXPORT jboolean JNICALL +Java_org_apache_subversion_javahl_util_DiffLib_nativeFileDiff( + JNIEnv* env, jobject jthis, + jstring joriginal_file, + jstring jmodified_file, + + jint jignore_space_ordinal, + jboolean jignore_eol_style, + jboolean jshow_c_function, + jint jcontext_size, + + jstring joriginal_header, + jstring jmodified_header, + jstring jheader_encoding, + jstring jrelative_to_dir, + + jobject jresult_stream) +{ + JNIEntry(DiffLib, nativeFileDiff); + + // Using a "global" request pool since we don't keep a context with + // its own pool around for these functions. + SVN::Pool pool; + + Path original(joriginal_file, pool); + if (JNIUtil::isJavaExceptionThrown()) + return false; + SVN_JNI_ERR(original.error_occurred(), false); + + Path modified(jmodified_file, pool); + if (JNIUtil::isJavaExceptionThrown()) + return false; + SVN_JNI_ERR(modified.error_occurred(), false); + + svn_diff_t* diff; + svn_diff_file_options_t* diff_options = + svn_diff_file_options_create(pool.getPool()); + diff_options->ignore_space = + svn_diff_file_ignore_space_t(jignore_space_ordinal); + diff_options->ignore_eol_style = svn_boolean_t(jignore_eol_style); + diff_options->show_c_function = svn_boolean_t(jshow_c_function); + SVN_JNI_ERR(svn_diff_file_diff_2(&diff, + original.c_str(), + modified.c_str(), + diff_options, + pool.getPool()), + false); + + const jboolean diffs = svn_diff_contains_diffs(diff); + + JNIStringHolder original_header(joriginal_header); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + JNIStringHolder modified_header(jmodified_header); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + JNIStringHolder header_encoding(jheader_encoding); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + JNIStringHolder relative_to_dir(jrelative_to_dir); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + OutputStream result_stream(jresult_stream); + + SVN_JNI_ERR(svn_diff_file_output_unified4( + result_stream.getStream(pool), diff, + original.c_str(), modified.c_str(), + original_header.c_str(), modified_header.c_str(), + header_encoding.c_str(), relative_to_dir.c_str(), + diff_options->show_c_function, int(jcontext_size), + NULL, NULL, pool.getPool()), + false); + + return diffs; +} + +JNIEXPORT jboolean JNICALL +Java_org_apache_subversion_javahl_util_DiffLib_nativeFileMerge( + JNIEnv* env, jobject jthis, + jstring joriginal_file, + jstring jmodified_file, + jstring jlatest_file, + + jint jignore_space_ordinal, + jboolean jignore_eol_style, + jboolean jshow_c_function, + + jstring jconflict_original, + jstring jconflict_modified, + jstring jconflict_latest, + jstring jconflict_separator, + jint jconflict_style_ordinal, + + jobject jresult_stream) +{ + JNIEntry(DiffLib, nativeFileMerge); + + // Using a "global" request pool since we don't keep a context with + // its own pool around for these functions. + SVN::Pool pool; + + Path original(joriginal_file, pool); + if (JNIUtil::isJavaExceptionThrown()) + return false; + SVN_JNI_ERR(original.error_occurred(), false); + + Path modified(jmodified_file, pool); + if (JNIUtil::isJavaExceptionThrown()) + return false; + SVN_JNI_ERR(modified.error_occurred(), false); + + Path latest(jlatest_file, pool); + if (JNIUtil::isJavaExceptionThrown()) + return false; + SVN_JNI_ERR(latest.error_occurred(), false); + + svn_diff_t* diff; + svn_diff_file_options_t* diff_options = + svn_diff_file_options_create(pool.getPool()); + diff_options->ignore_space = + svn_diff_file_ignore_space_t(jignore_space_ordinal); + diff_options->ignore_eol_style = svn_boolean_t(jignore_eol_style); + diff_options->show_c_function = svn_boolean_t(jshow_c_function); + SVN_JNI_ERR(svn_diff_file_diff3_2(&diff, + original.c_str(), + modified.c_str(), + latest.c_str(), + diff_options, + pool.getPool()), + false); + + const jboolean conflicts = svn_diff_contains_conflicts(diff); + + JNIStringHolder conflict_original(jconflict_original); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + JNIStringHolder conflict_modified(jconflict_modified); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + JNIStringHolder conflict_latest(jconflict_latest); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + JNIStringHolder conflict_separator(jconflict_separator); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + OutputStream result_stream(jresult_stream); + + SVN_JNI_ERR(svn_diff_file_output_merge3( + result_stream.getStream(pool), diff, + original.c_str(), modified.c_str(), latest.c_str(), + conflict_original.c_str(), + conflict_modified.c_str(), + conflict_latest.c_str(), + conflict_separator.c_str(), + svn_diff_conflict_display_style_t(jconflict_style_ordinal), + NULL, NULL, + pool.getPool()), + false); + + return conflicts; +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp new file mode 100644 index 0000000..3456dea --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_PropLib.cpp @@ -0,0 +1,381 @@ +/** + * @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_PropLib.cpp + * @brief Implementation of the native methods in the Java class PropLib + */ + +#include <iostream> +#include <sstream> + +#include "../include/org_apache_subversion_javahl_util_PropLib.h" + +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_array.hpp" +#include "jniwrapper/jni_list.hpp" +#include "jniwrapper/jni_string.hpp" +#include "jniwrapper/jni_io_stream.hpp" +#include "ExternalItem.hpp" +#include "SubversionException.hpp" + +#include "EnumMapper.h" +#include "Pool.h" + +#include "svn_props.h" +#include "svn_time.h" +#include "svn_wc.h" + +#include "private/svn_wc_private.h" +#include "svn_private_config.h" + + +namespace { +class PropGetter +{ +public: + PropGetter(const char* mime_type, svn_stream_t* contents) + : m_mime_type(mime_type), + m_contents(contents) + {} + + static svn_error_t* callback(const svn_string_t** mime_type, + svn_stream_t* stream, void* baton, + apr_pool_t* pool) + { + PropGetter* self = static_cast<PropGetter*>(baton); + if (mime_type) + { + if (self->m_mime_type) + *mime_type = svn_string_create(self->m_mime_type, pool); + else + *mime_type = svn_string_create_empty(pool); + } + + if (stream && self->m_contents) + { + SVN_ERR(svn_stream_copy3(self->m_contents, + svn_stream_disown(stream, pool), + NULL, NULL, pool)); + } + + return SVN_NO_ERROR; + } + +private: + const char* m_mime_type; + svn_stream_t* m_contents; +}; + +struct FormatRevision +{ + explicit FormatRevision(const svn_opt_revision_t* const& revarg, + const SVN::Pool& poolarg) + : rev(revarg), pool(poolarg) + {} + + const svn_opt_revision_t* const& rev; + const SVN::Pool& pool; +}; + +std::ostream& operator<<(std::ostream& os, const FormatRevision& pr) +{ + switch (pr.rev->kind) + { + case svn_opt_revision_number: + os << pr.rev->value.number; + break; + case svn_opt_revision_date: + os << '{' + << svn_time_to_cstring(pr.rev->value.date, pr.pool.getPool()) + << '}'; + break; + default: + throw std::logic_error( + _("Invalid revision tag; must be a number or a date")); + } + return os; +} + +bool operator==(const svn_opt_revision_t& a, + const svn_opt_revision_t& b) +{ + if (a.kind != b.kind) + return false; + if (a.kind == svn_opt_revision_number + && a.value.number != b.value.number) + return false; + if (a.kind == svn_opt_revision_date + && a.value.date != b.value.date) + return false; + return true; +} + +inline bool operator!=(const svn_opt_revision_t& a, + const svn_opt_revision_t& b) +{ + return !(a == b); +} + +class UnparseFunctor +{ +public: + explicit UnparseFunctor(std::ostringstream& buffer, bool old_format, + SVN::Pool& iterpool) + : m_buffer(buffer), + m_old_format(old_format), + m_iterpool(iterpool) + {} + + void operator()(const JavaHL::ExternalItem& item) + { + m_iterpool.clear(); + + const Java::Env env(item.get_env()); + const Java::LocalFrame frame(env); + + if (!m_old_format) + { + if (item.revision()->kind != svn_opt_revision_head + && *item.revision() != *item.peg_revision()) + { + m_buffer << "-r" + << FormatRevision(item.revision(), m_iterpool) + << ' '; + } + if (item.peg_revision()->kind == svn_opt_revision_head) + m_buffer << item.url() << ' '; + else + { + m_buffer << item.url() << '@' + << FormatRevision(item.peg_revision(), m_iterpool) + << ' '; + } + m_buffer << item.target_dir() << '\n'; + } + else + { + // Sanity check: old format does not support peg revisions + if (item.peg_revision()->kind != svn_opt_revision_head + && *item.revision() != *item.peg_revision()) + { + JavaHL::SubversionException(env) + .raise(_("Clients older than Subversion 1.5" + " do not support peg revision syntax" + " in the svn:externals property")); + } + + // Sanity check: old format does not support relative URLs + const std::string url = item.url(); + if ( (url.size() >= 1 && (url[0] == '.' || url[0] == '/')) + || (url.size() >= 2 && (url[0] == '^' && url[1] == '/'))) + { + JavaHL::SubversionException(env) + .raise(_("Clients older than Subversion 1.5" + " do not support relative URLs" + " in the svn:externals property")); + } + + m_buffer << item.target_dir() << ' '; + if (item.revision()->kind != svn_opt_revision_head) + { + m_buffer << "-r" + << FormatRevision(item.revision(), m_iterpool) + << ' '; + } + m_buffer << url << '\n'; + } + } + +private: + std::ostringstream& m_buffer; + const bool m_old_format; + SVN::Pool& m_iterpool; +}; +} // anoymous namespace + + +JNIEXPORT jbyteArray JNICALL +Java_org_apache_subversion_javahl_util_PropLib_checkNodeProp( + JNIEnv* jenv, jobject jthis, + jstring jname, jbyteArray jvalue, jstring jpath, jobject jkind, + jstring jmime_type, jobject jfile_contents, + jboolean jskip_some_checks) +{ + SVN_JAVAHL_JNI_TRY(PropLib, checkLocalProp) + { + const Java::Env env(jenv); + + const svn_node_kind_t kind = EnumMapper::toNodeKind(jkind); + SVN_JAVAHL_OLDSTYLE_EXCEPTION_CHECK(env); + + const Java::String name_str(env, jname); + const Java::ByteArray value(env, jvalue); + const Java::String path_str(env, jpath); + const Java::String mime_type_str(env, jmime_type); + Java::InputStream file_contents(env, jfile_contents); + + // Using a "global" request pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool pool; + + const Java::String::Contents name(name_str); + const Java::String::Contents path(path_str); + const Java::String::Contents mime_type(mime_type_str); + PropGetter getter(mime_type.c_str(), file_contents.get_stream(pool)); + + const svn_string_t* canonval; + SVN_JAVAHL_CHECK(env, + svn_wc_canonicalize_svn_prop( + &canonval, name.c_str(), + Java::ByteArray::Contents(value).get_string(pool), + path.c_str(), kind, + svn_boolean_t(jskip_some_checks), + PropGetter::callback, &getter, + pool.getPool())); + return Java::ByteArray(env, canonval->data, jint(canonval->len)).get(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_PropLib_parseExternals( + JNIEnv* jenv, jobject jthis, + jbyteArray jdescription, jstring jparent_dir, jboolean jcanonicalize_url) +{ + SVN_JAVAHL_JNI_TRY(PropLib, parseExternals) + { + const Java::Env env(jenv); + + const Java::ByteArray description(env, jdescription); + const Java::String parent_dir(env, jparent_dir); + + // Using a "global" request pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool pool; + + apr_array_header_t* externals; + { + // There is no guarantee that the description contents are + // null-terminated. Copy them to an svn_string_t to make sure + // that they are. + svn_string_t* const description_contents = + Java::ByteArray::Contents(description).get_string(pool); + + SVN_JAVAHL_CHECK(env, + svn_wc_parse_externals_description3( + &externals, + Java::String::Contents(parent_dir).c_str(), + description_contents->data, + svn_boolean_t(jcanonicalize_url), + pool.getPool())); + } + + Java::List<JavaHL::ExternalItem> items(env, externals->nelts); + for (jint i = 0; i < externals->nelts; ++i) + { + // References to the newly created external items are stored + // in the list, so make sure the local reference in this + // frame get cleared on each iteration. + Java::LocalFrame frame; + + const svn_wc_external_item2_t* const item = + APR_ARRAY_IDX(externals, i, svn_wc_external_item2_t*); + items.add(JavaHL::ExternalItem(env, + item->target_dir, + item->url, + &item->revision, + &item->peg_revision)); + } + return items.get(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + + +JNIEXPORT jbyteArray JNICALL +Java_org_apache_subversion_javahl_util_PropLib_unparseExternals( + JNIEnv* jenv, jobject jthis, + jobject jitems, jstring jparent_dir, jboolean jold_format) +{ + SVN_JAVAHL_JNI_TRY(PropLib, unparseExternals) + { + const Java::Env env(jenv); + + const Java::ImmutableList<JavaHL::ExternalItem> items(env, jitems); + const Java::String parent_dir(env, jparent_dir); + + // Using a "global" iteration pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool iterpool; + + std::ostringstream buffer; + items.for_each(UnparseFunctor(buffer, jold_format, iterpool)); + const std::string description(buffer.str()); + + // Validate the result. Even though we generated the string + // ourselves, we did not validate the input paths and URLs. + SVN_JAVAHL_CHECK(env, + svn_wc_parse_externals_description3( + NULL, + Java::String::Contents(parent_dir).c_str(), + description.c_str(), + false, iterpool.getPool())); + return Java::ByteArray(env, description).get(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + + +JNIEXPORT jstring JNICALL +Java_org_apache_subversion_javahl_util_PropLib_resolveExternalsUrl( + JNIEnv* jenv, jobject jthis, + jobject jitem, jstring jrepos_root_url, jstring jparent_dir_url) +{ + SVN_JAVAHL_JNI_TRY(PropLib, unparseExternals) + { + const Java::Env env(jenv); + + const Java::String repos_root_url(env, jrepos_root_url); + const Java::String parent_dir_url(env, jparent_dir_url); + const JavaHL::ExternalItem item(env, jitem); + + // Using a "global" request pool since we don't keep a context + // with its own pool around for these functions. + SVN::Pool pool; + + const char* resolved_url; + SVN_JAVAHL_CHECK(env, + svn_wc__resolve_relative_external_url( + &resolved_url, + item.get_external_item(pool), + Java::String::Contents(repos_root_url).c_str(), + Java::String::Contents(parent_dir_url).c_str(), + pool.getPool(), pool.getPool())); + return Java::String(env, resolved_url).get(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp new file mode 100644 index 0000000..ce507e5 --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_SubstLib.cpp @@ -0,0 +1,219 @@ +/** + * @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_SubstLib.cpp + * @brief Implementation of the native methods in the Java class SubstLib + */ + +#include <memory> + +#include "../include/org_apache_subversion_javahl_util_SubstLib.h" + +#include "jniwrapper/jni_stack.hpp" +#include "jniwrapper/jni_array.hpp" +#include "jniwrapper/jni_string.hpp" +#include "jniwrapper/jni_string_map.hpp" +#include "jniwrapper/jni_io_stream.hpp" + +#include "JNIUtil.h" +#include "NativeStream.hpp" +#include "Utility.hpp" + +#include <apr_hash.h> + +#include "svn_subst.h" + +#include "svn_private_config.h" + + +namespace { +apr_hash_t* +build_keywords_common(Java::Env env, const SVN::Pool& pool, + jbyteArray jkeywords_value, jlong jrevision, + jstring jurl, jstring jrepos_root_url, + jobject jdate, jstring jauthor) +{ + const Java::ByteArray keywords_value(env, jkeywords_value); + const Java::String url(env, jurl); + const Java::String repos_root_url(env, jrepos_root_url); + const Java::String author(env, jauthor); + + const Java::ByteArray::Contents keywords_contents(keywords_value); + svn_string_t* keywords_string = keywords_contents.get_string(pool); + const char* revision = (jrevision < 0 ? NULL + : apr_psprintf(pool.getPool(), + "%" APR_UINT64_T_FMT, + apr_uint64_t(jrevision))); + const Java::String::Contents url_contents(url); + const Java::String::Contents root_url_contents(repos_root_url); + const Java::String::Contents author_contents(author); + + apr_hash_t* kw = NULL; + SVN_JAVAHL_CHECK(env, + svn_subst_build_keywords3( + &kw, + keywords_string->data, + revision, + url_contents.c_str(), + root_url_contents.c_str(), + (jdate ? JNIUtil::getDate(jdate) : 0), + author_contents.c_str(), + pool.getPool())); + return kw; +} + +svn_stream_t* +translate_stream_common(Java::Env env, const SVN::Pool& pool, + svn_stream_t* stream, + jbyteArray jeol_marker, jboolean jrepair_eol, + jobject jkeywords, jboolean juse_keywords, + jboolean jexpand_keywords, + jbyteArray jkeywords_value, jlong jrevision, + jstring jurl, jstring jrepos_root_url, + jobject jdate, jstring jauthor) +{ + apr_hash_t* const keywords = + (juse_keywords + ? JavaHL::Util::make_keyword_hash(env, jkeywords, pool) + : build_keywords_common( + env, pool, jkeywords_value, jrevision, + jurl, jrepos_root_url, jdate, jauthor)); + + const Java::ByteArray eol_marker(env, jeol_marker); + svn_string_t* const eol_str = + Java::ByteArray::Contents(eol_marker).get_string(pool); + return svn_subst_stream_translated(stream, + eol_str->data, + svn_boolean_t(jrepair_eol), + keywords, + svn_boolean_t(jexpand_keywords), + pool.getPool()); +} +} // anoymous namespace + + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_SubstLib_buildKeywords( + JNIEnv* jenv, jobject jthis, + jbyteArray jkeywords_value, jlong jrevision, + jstring jurl, jstring jrepos_root_url, + jobject jdate, jstring jauthor) +{ + typedef Java::Map<Java::ByteArray, jbyteArray> ByteArrayMap; + + SVN_JAVAHL_JNI_TRY(SubstLib, buildKeywords) + { + const Java::Env env(jenv); + + // Using a "global" request pool since we don't keep a context with + // its own pool around for these functions. + SVN::Pool pool; + + apr_hash_t* const kw = build_keywords_common( + env, pool, jkeywords_value, jrevision, + jurl, jrepos_root_url, jdate, jauthor); + + ByteArrayMap keywords(env, jint(apr_hash_count(kw))); + for (apr_hash_index_t* hi = apr_hash_first(pool.getPool(), kw); + hi; hi = apr_hash_next(hi)) + { + const void* rkey; + void* rval; + apr_hash_this(hi, &rkey, NULL, &rval); + + svn_string_t* const val = static_cast<svn_string_t*>(rval); + keywords.put(static_cast<const char*>(rkey), + Java::ByteArray(env, val->data, jsize(val->len))); + } + return keywords.get(); + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_SubstLib_translateInputStream( + JNIEnv* jenv, jobject jthis, + jobject jsource, jbyteArray jeol_marker, jboolean jrepair_eol, + jobject jkeywords, jboolean juse_keywords, jboolean jexpand_keywords, + jbyteArray jkeywords_value, jlong jrevision, + jstring jurl, jstring jrepos_root_url, + jobject jdate, jstring jauthor) +{ + SVN_JAVAHL_JNI_TRY(SubstLib, translateInputStream) + { + const Java::Env env(jenv); + + // We'll allocate the stream in the bound object's pool. + std::auto_ptr<JavaHL::NativeInputStream> + translated(new JavaHL::NativeInputStream()); + svn_stream_t* source = Java::InputStream::get_global_stream( + env, jsource, translated->get_pool()); + + translated->set_stream(translate_stream_common( + env, translated->get_pool(), source, + jeol_marker, jrepair_eol, + jkeywords, juse_keywords, jexpand_keywords, + jkeywords_value, jrevision, + jurl, jrepos_root_url, jdate, jauthor)); + const jobject jtranslated = translated->create_java_wrapper(); + translated.release(); + return jtranslated; + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} + + +JNIEXPORT jobject JNICALL +Java_org_apache_subversion_javahl_util_SubstLib_translateOutputStream( + JNIEnv* jenv, jobject jthis, + jobject jdestination, jbyteArray jeol_marker, jboolean jrepair_eol, + jobject jkeywords, jboolean juse_keywords, jboolean jexpand_keywords, + jbyteArray jkeywords_value, jlong jrevision, + jstring jurl, jstring jrepos_root_url, + jobject jdate, jstring jauthor) +{ + SVN_JAVAHL_JNI_TRY(SubstLib, translateInputStream) + { + const Java::Env env(jenv); + + // We'll allocate the stream in the bound object's pool. + std::auto_ptr<JavaHL::NativeOutputStream> + translated(new JavaHL::NativeOutputStream()); + svn_stream_t* destination = Java::OutputStream::get_global_stream( + env, jdestination, translated->get_pool()); + + translated->set_stream(translate_stream_common( + env, translated->get_pool(), destination, + jeol_marker, jrepair_eol, + jkeywords, juse_keywords, jexpand_keywords, + jkeywords_value, jrevision, + jurl, jrepos_root_url, jdate, jauthor)); + const jobject jtranslated = translated->create_java_wrapper(); + translated.release(); + return jtranslated; + } + SVN_JAVAHL_JNI_CATCH; + return NULL; +} diff --git a/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp new file mode 100644 index 0000000..b18f3fc --- /dev/null +++ b/subversion/bindings/javahl/native/org_apache_subversion_javahl_util_TunnelChannel.cpp @@ -0,0 +1,174 @@ +/** + * @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 <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 "jniwrapper/jni_exception.hpp" +#include "jniwrapper/jni_channel.hpp" +#include "jniwrapper/jni_stack.hpp" + +#include "svn_private_config.h" + +namespace { +apr_file_t* get_file_descriptor(Java::Env env, jlong jfd) +{ + apr_file_t* fd = reinterpret_cast<apr_file_t*>(jfd); + if (!fd) + Java::NullPointerException(env).raise("nativeChannel"); + return fd; +} + +void throw_IOException(Java::Env env, const char* message, + apr_status_t status) +{ + char buf[1024]; + std::string msg(message); + apr_strerror(status, buf, sizeof(buf) - 1); + msg += buf; + Java::IOException(env).raise(msg.c_str()); +} + +class TunnelReader : public Java::ChannelReader +{ +public: + explicit TunnelReader(Java::Env env, jlong jnative_channel) + : m_fd(get_file_descriptor(env, jnative_channel)) + {} + + virtual jint operator()(Java::Env env, void* buffer, jint length) + { + if (!length) + return 0; + + apr_size_t bytes_read = length; + const apr_status_t status = apr_file_read(m_fd, buffer, &bytes_read); + if (status && !APR_STATUS_IS_EOF(status)) + { + throw_IOException( + env, _("Error reading from native file handle: "), + status); + return -1; + } + if (APR_STATUS_IS_EOF(status)) + return -1; + return jint(bytes_read); + } + +private: + apr_file_t* const m_fd; +}; + +class TunnelWriter : public Java::ChannelWriter +{ +public: + explicit TunnelWriter(Java::Env env, jlong jnative_channel) + : m_fd(get_file_descriptor(env, jnative_channel)) + {} + + virtual jint operator()(Java::Env env, const void* buffer, jint length) + { + if (!length) + return 0; + + apr_size_t bytes_written; + const apr_status_t status = + apr_file_write_full(m_fd, buffer, length, &bytes_written); + if (status) + { + throw_IOException( + env, _("Error writing to native file handle: "), + status); + return -1; + } + return jint(bytes_written); + } + +private: + apr_file_t* const m_fd; +}; + +} // anonymous namespace + + +JNIEXPORT void JNICALL +Java_org_apache_subversion_javahl_util_TunnelChannel_nativeClose( + JNIEnv* jenv, jclass jclazz, jlong jnative_channel) +{ + SVN_JAVAHL_JNI_TRY_STATIC(TunnelChannel, close) + { + const Java::Env env(jenv); + + apr_file_t* const fd = get_file_descriptor(env, jnative_channel); + if (!fd) + return; + + const apr_status_t status = apr_file_close(fd); + if (status) + throw_IOException( + env, _("Error closing native file handle: "), + status); + } + SVN_JAVAHL_JNI_CATCH; +} + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_util_RequestChannel_nativeRead( + JNIEnv* jenv, jclass jclazz, jlong jnative_channel, jobject jdst_buffer) +{ + SVN_JAVAHL_JNI_TRY_STATIC(RequestChannel, read) + { + const Java::Env env(jenv); + + TunnelReader reader(env, jnative_channel); + Java::ReadableByteChannel channel(env, reader); + return channel.read(jdst_buffer); + } + SVN_JAVAHL_JNI_CATCH; + return -1; +} + +JNIEXPORT jint JNICALL +Java_org_apache_subversion_javahl_util_ResponseChannel_nativeWrite( + JNIEnv* jenv, jclass jclazz, jlong jnative_channel, jobject jsrc_buffer) +{ + SVN_JAVAHL_JNI_TRY_STATIC(ResponseChannel, write) + { + const Java::Env env(jenv); + + TunnelWriter writer(env, jnative_channel); + Java::WritableByteChannel channel(env, writer); + return channel.write(jsrc_buffer); + } + SVN_JAVAHL_JNI_CATCH; + return -1; +} |