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 <stdexcept>
25 
26 #include <apr_atomic.h>
27 
28 #define SVN_JAVAHL_JNIWRAPPER_LOG(expr)
29 #include "jni_env.hpp"
30 #include "jni_globalref.hpp"
31 #include "jni_exception.hpp"
32 #include "jni_object.hpp"
33 #include "jni_string.hpp"
34 
35 #include "jni_channel.hpp"
36 #include "jni_io_stream.hpp"
37 #include "jni_list.hpp"
38 #include "jni_string_map.hpp"
39 
40 #include "../SubversionException.hpp"
41 #include "../AuthnCallback.hpp"
42 #include "../Credential.hpp"
43 #include "../ExternalItem.hpp"
44 #include "../EditorCallbacks.hpp"
45 #include "../CxxCompat.hpp"
46 
47 namespace
48 {
49 /* This class behaves like a dumbed-down std:unique_ptr, but it
50    implements atomic access and modification of the wrapped
51    pointer. */
52 class ClassImplPtr
53 {
54   typedef ::Java::Object::ClassImpl ClassImpl;
55 
56  public:
57   /* Default constructor; initializes the wrapped pointer to NULL */
ClassImplPtr()58   explicit ClassImplPtr()
59     : m_ptr(NULL)
60   {}
61 
62   /* Initializing constructor; sets the wrapped pointer to PTR */
ClassImplPtr(ClassImpl * ptr)63   explicit ClassImplPtr(ClassImpl* ptr)
64     : m_ptr(ptr)
65   {}
66 
67   /* Destructor deletes the object and resets the wrapped pointer to NULL. */
~ClassImplPtr()68   ~ClassImplPtr()
69     {
70       delete static_cast<ClassImpl*>(
71           apr_atomic_casptr(&m_ptr, NULL, get()));
72     }
73 
74   /* Sets the wrapped pointer to PTR iff it is NULL, and returns the
75      old value. */
test_and_set(ClassImpl * ptr)76   ClassImpl* test_and_set(ClassImpl* ptr)
77     {
78       return static_cast<ClassImpl*>(
79           apr_atomic_casptr(&m_ptr, ptr, NULL));
80     }
81 
82   /* Returns the current value of the the wrapped pointer. */
get() const83   ClassImpl* get() const
84     {
85       return static_cast<ClassImpl*>(
86           apr_atomic_casptr(&m_ptr, NULL, NULL));
87     }
88 
89 private:
90   // Non-copyable
91   ClassImplPtr(const ClassImplPtr&);
92   ClassImplPtr& operator=(const ClassImplPtr&);
93 
94   mutable volatile void* m_ptr;
95 };
96 } // anonymous namespace
97 
98 
99 namespace Java {
100 
101 class ClassCacheImpl
102 {
103 
104   friend class ClassCache;
105 
106   // We only statically initialize a few of the common class wrappers.
ClassCacheImpl(Env env)107   explicit ClassCacheImpl(Env env) :
108 
109 #define JNIWRAPPER_INIT_CACHED_CLASS(M, C)     \
110   m_impl_##M(new C::ClassImpl(env, env.FindClass(C::m_class_name)))
111 
112       JNIWRAPPER_INIT_CACHED_CLASS(object, Object),
113       JNIWRAPPER_INIT_CACHED_CLASS(classtype, Class),
114       JNIWRAPPER_INIT_CACHED_CLASS(throwable, Exception),
115       JNIWRAPPER_INIT_CACHED_CLASS(string, String)
116 #undef JNIWRAPPER_INIT_CACHED_CLASS
117     {}
118 
119   // We can't do this in the constructor above, because the satic
120   // initializers will expect that ClassCache::m_impl is already set;
121   // that doesn't happen until the constructor returns.
static_init(Env env)122   void static_init(Env env)
123     {
124 #define JNIWRAPPER_STATIC_CACHED_CLASS(M, C)            \
125       C::static_init(env, m_impl_##M->get_class())
126 
127       // No-op JNIWRAPPER_STATIC_CACHED_CLASS(object, Object);
128       JNIWRAPPER_STATIC_CACHED_CLASS(classtype, Class);
129       JNIWRAPPER_STATIC_CACHED_CLASS(throwable, Exception);
130       // No-op JNIWRAPPER_STATIC_CACHED_CLASS(string, String);
131 #undef JNIWRAPPER_STATIC_CACHED_CLASS
132     }
133 
134   // The statically initialized calss wrappers are always defined and
135   // therefore do not need atomic access.
136 #define JNIWRAPPER_DEFINE_CACHED_CLASS(M, C)                     \
137   JavaHL::cxx::owned_ptr<Object::ClassImpl> m_impl_##M;          \
138   const Object::ClassImpl* get_##M(Env)                          \
139     {                                                            \
140       return m_impl_##M.get();                                   \
141     }
142 
143   JNIWRAPPER_DEFINE_CACHED_CLASS(object, Object)
144   JNIWRAPPER_DEFINE_CACHED_CLASS(classtype, Class)
145   JNIWRAPPER_DEFINE_CACHED_CLASS(throwable, Exception)
146   JNIWRAPPER_DEFINE_CACHED_CLASS(string, String)
147 #undef JNIWRAPPER_DEFINE_CACHED_CLASS
148 
149   // All other class wrappers must be atomically initialized
150 #define JNIWRAPPER_DEFINE_CACHED_CLASS(M, C)                    \
151   ClassImplPtr m_impl_##M;                                      \
152   const Object::ClassImpl* get_##M(Env env)                     \
153     {                                                           \
154       Object::ClassImpl* pimpl = m_impl_##M.get();              \
155       if (!pimpl)                                               \
156         {                                                       \
157           JavaHL::cxx::owned_ptr<Object::ClassImpl> tmp(        \
158               new C::ClassImpl(                                 \
159                   env, env.FindClass(C::m_class_name)));        \
160           pimpl = m_impl_##M.test_and_set(tmp.get());           \
161           if (!pimpl)                                           \
162             pimpl = tmp.release();                              \
163         }                                                       \
164       return pimpl;                                             \
165     }
166 
167   JNIWRAPPER_DEFINE_CACHED_CLASS(exc_index_out_of_bounds,
168                                  IndexOutOfBoundsException);
169   JNIWRAPPER_DEFINE_CACHED_CLASS(exc_no_such_element,
170                                  NoSuchElementException);
171 
172   JNIWRAPPER_DEFINE_CACHED_CLASS(iterator, BaseIterator);
173 
174   JNIWRAPPER_DEFINE_CACHED_CLASS(list, BaseImmutableList);
175   JNIWRAPPER_DEFINE_CACHED_CLASS(array_list, BaseList);
176 
177   JNIWRAPPER_DEFINE_CACHED_CLASS(map, BaseImmutableMap);
178   JNIWRAPPER_DEFINE_CACHED_CLASS(set, BaseImmutableMap::Set);
179   JNIWRAPPER_DEFINE_CACHED_CLASS(map_entry, BaseImmutableMap::Entry);
180   JNIWRAPPER_DEFINE_CACHED_CLASS(hash_map, BaseMap);
181 
182   JNIWRAPPER_DEFINE_CACHED_CLASS(input_stream, InputStream);
183   JNIWRAPPER_DEFINE_CACHED_CLASS(output_stream, OutputStream);
184 
185   JNIWRAPPER_DEFINE_CACHED_CLASS(byte_buffer,
186                                  ByteChannel::ByteBuffer);
187 
188   JNIWRAPPER_DEFINE_CACHED_CLASS(subversion_exception,
189                                  ::JavaHL::SubversionException);
190 
191   JNIWRAPPER_DEFINE_CACHED_CLASS(authn_cb,
192                                  ::JavaHL::AuthnCallback);
193   JNIWRAPPER_DEFINE_CACHED_CLASS(authn_result,
194                                  ::JavaHL::AuthnCallback::AuthnResult);
195   JNIWRAPPER_DEFINE_CACHED_CLASS(authn_ssl_server_cert_failures,
196                                  ::JavaHL::AuthnCallback::SSLServerCertFailures);
197   JNIWRAPPER_DEFINE_CACHED_CLASS(authn_ssl_server_cert_info,
198                                  ::JavaHL::AuthnCallback::SSLServerCertInfo);
199   JNIWRAPPER_DEFINE_CACHED_CLASS(user_passwd_cb,
200                                  ::JavaHL::UserPasswordCallback);
201 
202   JNIWRAPPER_DEFINE_CACHED_CLASS(credential,
203                                  ::JavaHL::Credential);
204   JNIWRAPPER_DEFINE_CACHED_CLASS(credential_kind,
205                                  ::JavaHL::Credential::Kind);
206 
207   JNIWRAPPER_DEFINE_CACHED_CLASS(external_item,
208                                  ::JavaHL::ExternalItem);
209 
210   JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_base_cb,
211                                  ::JavaHL::ProvideBaseCallback);
212   JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_base_cb_ret,
213                                  ::JavaHL::ProvideBaseCallback::ReturnValue);
214   JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_props_cb,
215                                  ::JavaHL::ProvidePropsCallback);
216   JNIWRAPPER_DEFINE_CACHED_CLASS(editor_provide_props_cb_ret,
217                                  ::JavaHL::ProvidePropsCallback::ReturnValue);
218   JNIWRAPPER_DEFINE_CACHED_CLASS(editor_get_kind_cb,
219                                  ::JavaHL::GetNodeKindCallback);
220 #undef JNIWRAPPER_DEFINE_CACHED_CLASS
221 };
222 
223 
224 ClassCacheImpl* ClassCache::m_impl = NULL;
225 
create()226 void ClassCache::create()
227 {
228   const char* exception_message = NULL;
229 
230   try
231     {
232       const Env env;
233       m_impl = new ClassCacheImpl(env);
234       m_impl->static_init(env);
235     }
236   catch (const SignalExceptionThrown&)
237     {}
238   catch (const std::exception& ex)
239     {
240       exception_message = ex.what();
241     }
242   catch (...)
243     {
244       exception_message = "Caught unknown C++ exception";
245     }
246 
247   // Do not throw any more exceptions from here, so use the raw environment.
248   ::JNIEnv* const jenv = Env().get();
249   if (exception_message || jenv->ExceptionCheck())
250     {
251       jobject cause = jenv->ExceptionOccurred();
252       if (cause)
253         jenv->ExceptionClear();
254 
255       const jclass rtx = jenv->FindClass("java/lang/RuntimeException");
256       const jmethodID ctor = jenv->GetMethodID(rtx, "<init>",
257                                                "(Ljava/lang/String;"
258                                                "Ljava/lang/Throwable;)V");
259       if (!cause && exception_message)
260         {
261           const jstring msg = jenv->NewStringUTF(exception_message);
262           cause = jenv->NewObject(rtx, ctor, msg, jthrowable(0));
263         }
264       const jstring reason =
265         jenv->NewStringUTF("JavaHL native library initialization failed");
266       const jobject exception = jenv->NewObject(rtx, ctor, reason, cause);
267       jenv->Throw(jthrowable(exception));
268     }
269 }
270 
destroy()271 void ClassCache::destroy()
272 {
273   ClassCacheImpl* const pimpl = m_impl;
274   m_impl = NULL;
275   delete pimpl;
276 }
277 
278 #define JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(M)         \
279 const Object::ClassImpl* ClassCache::get_##M(Env env)   \
280 {                                                       \
281   return m_impl->get_##M(env);                          \
282 }
283 
284 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(object);
285 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(classtype);
286 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(throwable);
287 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(string);
288 
289 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(exc_index_out_of_bounds);
290 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(exc_no_such_element);
291 
292 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(list);
293 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(array_list);
294 
295 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(map);
296 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(set);
297 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(iterator);
298 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(map_entry);
299 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(hash_map);
300 
301 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(input_stream);
302 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(output_stream);
303 
304 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(byte_buffer);
305 
306 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(subversion_exception);
307 
308 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_cb);
309 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_result);
310 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_ssl_server_cert_failures);
311 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(authn_ssl_server_cert_info);
312 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(user_passwd_cb);
313 
314 
315 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(credential);
316 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(credential_kind);
317 
318 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(external_item);
319 
320 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_base_cb);
321 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_base_cb_ret);
322 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_props_cb);
323 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_provide_props_cb_ret);
324 JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR(editor_get_kind_cb);
325 #undef JNIWRAPPER_IMPL_CLASS_CACHE_ACCESSOR
326 
327 } // namespace Java
328