1 /**
2 * @copyright
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 * ====================================================================
21 * @endcopyright
22 */
23
24 #include <cstring>
25 #include <memory>
26 #include <apr.h>
27
28 #include "jni_env.hpp"
29 #include "jni_globalref.hpp"
30 #include "jni_exception.hpp"
31 #include "jni_object.hpp"
32 #include "jni_string.hpp"
33 #include "jni_array.hpp"
34 #include "jni_stack.hpp"
35
36 #include "../JNIUtil.h"
37 bool initialize_jni_util(JNIEnv *env);
38
39 #include "svn_private_config.h"
40
41
42 // Global library initializaiton
43
44 /**
45 * Initializer function, called just after the JVM loads the native
46 * library. Stores the global JVM reference and creates the global
47 * class cache.
48 */
49 JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM * jvm,void *)50 JNI_OnLoad(JavaVM* jvm, void*)
51 {
52 ::Java::Env::static_init(jvm);
53 const ::Java::Env env;
54
55 const apr_status_t status = apr_initialize();
56 if (!status)
57 ::Java::ClassCache::create();
58 else
59 {
60 char buf[2048];
61 std::strcpy(buf, "Could not initialize APR: ");
62 const std::size_t offset = std::strlen(buf);
63 apr_strerror(status, buf + offset, sizeof(buf) - offset - 1);
64 env.ThrowNew(env.FindClass("java/lang/Error"), buf);
65 }
66
67 // Initialize the old-style JavaHL infrastructure.
68 if (!initialize_jni_util(env.get()) && !env.ExceptionCheck())
69 {
70 env.ThrowNew(env.FindClass("java/lang/LinkageError"),
71 "Native library initialization failed");
72 }
73
74 return JNI_VERSION_1_2;
75 }
76
77 /**
78 * Cleanup function, called just before the JVM unloads the native
79 * library. Destroys the global class cache.
80 */
81 JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *,void *)82 JNI_OnUnload(JavaVM*, void*)
83 {
84 ::Java::ClassCache::destroy();
85 apr_terminate();
86 }
87
88
89 namespace Java {
90
91 // class Java::Env
92
93 ::JavaVM* Env::m_jvm = NULL;
static_init(::JavaVM * jvm)94 void Env::static_init(::JavaVM* jvm)
95 {
96 m_jvm = jvm;
97 }
98
error_create_global_reference()99 const char* Env::error_create_global_reference() throw()
100 {
101 return _("Could not create global reference");
102 }
103
error_get_contents_string()104 const char* Env::error_get_contents_string() throw()
105 {
106 return _("Could not get contents of Java String");
107 }
108
error_release_null_string()109 const char* Env::error_release_null_string() throw()
110 {
111 return _("Could not release contents of a null String");
112 }
113
error_create_object_array()114 const char* Env::error_create_object_array() throw()
115 {
116 return _("Could not create Object array");
117 }
118
119 namespace {
120 // The typed array error messages are always fatal, so allocating
121 // the buffer on the heap and never releasing it does not really
122 // constitute a memory leak.
make_typed_error(const char * fmt,const char * type)123 const char* make_typed_error(const char* fmt, const char* type) throw()
124 {
125 const apr_size_t bufsize = 512;
126 char *msg = new(std::nothrow) char[bufsize];
127 apr_snprintf(msg, bufsize, fmt, type);
128 return msg;
129 }
130 } // anonymous namespace
131
error_create_array(const char * type)132 const char* Env::error_create_array(const char* type) throw()
133 {
134 return make_typed_error(_("Could not create %sArray"), type);
135 }
136
error_get_contents_array(const char * type)137 const char* Env::error_get_contents_array(const char* type) throw()
138 {
139 return make_typed_error(_("Could not get %s array contents"), type);
140 }
141
error_release_null_array(const char * type)142 const char* Env::error_release_null_array(const char* type) throw()
143 {
144 return make_typed_error(
145 _("Could not release contents of a null %sArray"), type);
146 }
147
env_from_jvm()148 ::JNIEnv* Env::env_from_jvm()
149 {
150 if (m_jvm)
151 {
152 void *penv;
153 switch (m_jvm->GetEnv(&penv, JNI_VERSION_1_2))
154 {
155 case JNI_OK:
156 return static_cast<JNIEnv*>(penv);
157
158 case JNI_EDETACHED:
159 throw std::runtime_error(
160 _("Native thread is not attached to a Java VM"));
161
162 case JNI_EVERSION:
163 throw std::runtime_error(_("Unsupported JNI version"));
164
165 default:
166 throw std::runtime_error(_("Invalid JNI environment"));
167 }
168 }
169 throw std::logic_error(_("JavaVM instance was not initialized"));
170 }
171
throw_java_out_of_memory(const char * message) const172 void Env::throw_java_out_of_memory(const char* message) const
173 {
174 OutOfMemoryError(*this).raise(message);
175 }
176
177 // class Java::LocalFrame
178
179 const jint LocalFrame::DEFAULT_CAPACITY = 16;
180
181
182 // Class Java::Object
183
184 const char* const Object::m_class_name = "java/lang/Object";
~ClassImpl()185 Object::ClassImpl::~ClassImpl() {}
186
187
188 // Class Java::Class
189
190 const char* const Class::m_class_name = "java/lang/Class";
~ClassImpl()191 Class::ClassImpl::~ClassImpl() {}
192
193 MethodID Class::m_mid_get_class;
194 MethodID Class::m_mid_get_name;
195
static_init(Env env,jclass cls)196 void Class::static_init(Env env, jclass cls)
197 {
198 m_mid_get_class = env.GetMethodID(
199 ClassCache::get_object(env)->get_class(),
200 "getClass", "()Ljava/lang/Class;");
201 m_mid_get_name = env.GetMethodID(
202 cls, "getName", "()Ljava/lang/String;");
203 }
204
205 namespace{
get_class_of_object(Env env,jobject obj,jmethodID mid_get_class)206 jobject get_class_of_object(Env env, jobject obj, jmethodID mid_get_class)
207 {
208 if (!obj)
209 return NULL;
210 return env.CallObjectMethod(obj, mid_get_class);
211 }
212 } // anonymous namespace
213
Class(Env env,jobject obj)214 Class::Class(Env env, jobject obj)
215 : m_env(env),
216 m_jthis(get_class_of_object(env, obj, m_mid_get_class))
217 {}
218
Class(const Object & obj)219 Class::Class(const Object& obj)
220 : m_env(obj.get_env()),
221 m_jthis(get_class_of_object(obj.get_env(), obj.get(), m_mid_get_class))
222 {}
223
get_name() const224 jstring Class::get_name() const
225 {
226 if (!m_jthis)
227 return NULL;
228 return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_name));
229 }
230
231
232 // Class Java::String
233
234 const char* const String::m_class_name = "java/lang/String";
~ClassImpl()235 String::ClassImpl::~ClassImpl() {}
236
strdup(apr_pool_t * pool) const237 const char* String::strdup(apr_pool_t* pool) const
238 {
239 return apr_pstrdup(pool, String::Contents(*this).c_str());
240 }
241
set_value(const char * new_text)242 void String::MutableContents::set_value(const char* new_text)
243 {
244 if (!m_new_text)
245 throw std::invalid_argument(
246 _("Cannot set String contents to null"));
247 if (m_text)
248 {
249 m_new_text = new_text;
250 m_length = jsize(::std::strlen(new_text));
251 }
252 else
253 throw std::logic_error(
254 _("Cannot change the contents of a null String"));
255 }
256
257 // class Java::Exception
258
throw_java_exception() const259 void Exception::throw_java_exception() const
260 {
261 if (instantiated()
262 ? m_env.Throw(m_jthis)
263 : m_env.ThrowNew(m_class, NULL))
264 throw std::runtime_error(_("Could not throw Java exception"));
265 }
266
throw_java_exception(const char * message) const267 void Exception::throw_java_exception(const char* message) const
268 {
269 if (m_env.ThrowNew(m_class, message))
270 throw std::runtime_error(_("Could not throw Java exception"));
271 }
272
get_message() const273 jstring Exception::get_message() const
274 {
275 if (instantiated())
276 return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_message));
277 throw std::logic_error(_("Could not get exception message:"
278 " Exception instance is not available"));
279 }
280
281 const char* const Exception::m_class_name = "java/lang/Throwable";
~ClassImpl()282 Exception::ClassImpl::~ClassImpl() {}
283
284 MethodID Exception::m_mid_get_message;
static_init(Env env,jclass cls)285 void Exception::static_init(Env env, jclass cls)
286 {
287 m_mid_get_message = env.GetMethodID(
288 cls, "getMessage", "()Ljava/lang/String;");
289 }
290
291 // Other exception class initializers
292
293 const char* const RuntimeException::m_class_name =
294 "java/lang/RuntimeException";
295
296 const char* const NullPointerException::m_class_name =
297 "java/lang/NullPointerException";
298
299 const char* const OutOfMemoryError::m_class_name =
300 "java/lang/OutOfMemoryError";
301
302 const char* const IndexOutOfBoundsException::m_class_name =
303 "java/lang/IndexOutOfBoundsException";
~ClassImpl()304 IndexOutOfBoundsException::ClassImpl::~ClassImpl() {}
305
306 const char* const IOException::m_class_name =
307 "java/io/IOException";
308
309 const char* const IllegalArgumentException::m_class_name =
310 "java/lang/IllegalArgumentException";
311
312 const char *const NoSuchElementException::m_class_name =
313 "java/util/NoSuchElementException";
~ClassImpl()314 NoSuchElementException::ClassImpl::~ClassImpl() {}
315
316 // Implementation of jni_stack.hpp
317
handle_svn_error(Env env,::svn_error_t * err)318 void handle_svn_error(Env env, ::svn_error_t* err)
319 {
320 jthrowable cause = NULL;
321
322 // If the exception being currently thrown was generated by the
323 // JavaHL bindings, then assume the error was propagated through
324 // native code and do not re-throw it.
325 if (env.ExceptionCheck())
326 {
327 cause = env.ExceptionOccurred();
328 if (env.IsInstanceOf(
329 cause, ClassCache::get_subversion_exception(env)->get_class()))
330 {
331 // XXX FIXME: Should really have a special error code
332 // specifically for propagating Java exceptions from
333 // callbacks through native code.
334 svn_error_clear(err);
335 throw SignalExceptionThrown();
336 }
337 }
338
339 // Make sure there's only a single exception in the environment.
340 if (cause)
341 env.ExceptionClear();
342
343 ::JNIUtil::handleSVNError(err, cause);
344 throw SignalExceptionThrown();
345 }
346
unknown_cxx_exception_message()347 const char* unknown_cxx_exception_message() throw()
348 {
349 return _("Caught unknown C++ exception");
350 }
351
caught_java_exception_error(apr_status_t status)352 svn_error_t* caught_java_exception_error(apr_status_t status) throw()
353 {
354 return svn_error_create(status, JNIUtil::wrapJavaException(),
355 _("Java exception"));
356 }
357
358 } // namespace Java
359