1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_stoc.hxx"
26 
27 
28 #include <cstdarg>
29 #include <osl/diagnose.h>
30 #include <osl/process.h>
31 
32 #include <rtl/process.h>
33 #include <rtl/ustrbuf.hxx>
34 
35 #include <uno/environment.h>
36 #include <uno/mapping.hxx>
37 #include "com/sun/star/uno/RuntimeException.hpp"
38 
39 #include <cppuhelper/servicefactory.hxx>
40 
41 #ifdef LINUX
42 #undef minor
43 #undef major
44 #endif
45 
46 #include <com/sun/star/java/XJavaVM.hpp>
47 
48 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
49 
50 #include "jni.h"
51 
52 #include <cppuhelper/factory.hxx>
53 #include <cppuhelper/implementationentry.hxx>
54 
55 #include <cppuhelper/implbase2.hxx>
56 
57 #include <com/sun/star/loader/XImplementationLoader.hpp>
58 #include <com/sun/star/lang/IllegalArgumentException.hpp>
59 #include <com/sun/star/lang/XServiceInfo.hpp>
60 #include <com/sun/star/lang/XInitialization.hpp>
61 #include <com/sun/star/registry/XRegistryKey.hpp>
62 
63 #include "jvmaccess/unovirtualmachine.hxx"
64 #include "jvmaccess/virtualmachine.hxx"
65 
66 namespace css = com::sun::star;
67 
68 using namespace ::com::sun::star::java;
69 using namespace ::com::sun::star::lang;
70 using namespace ::com::sun::star::loader;
71 using namespace ::com::sun::star::uno;
72 using namespace ::com::sun::star::registry;
73 
74 using namespace ::cppu;
75 using namespace ::rtl;
76 using namespace ::osl;
77 
78 namespace stoc_javaloader {
79 
80 static Mutex & getInitMutex();
81 
82 static Sequence< OUString > loader_getSupportedServiceNames()
83 {
84     static Sequence < OUString > *pNames = 0;
85     if( ! pNames )
86     {
87         MutexGuard guard( Mutex::getGlobalMutex() );
88         if( !pNames )
89         {
90             static Sequence< OUString > seqNames(2);
91             seqNames.getArray()[0] = OUString(
92                 RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.loader.Java") );
93             seqNames.getArray()[1] = OUString(
94                 RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.loader.Java2") );
95             pNames = &seqNames;
96         }
97     }
98     return *pNames;
99 }
100 
101 static OUString loader_getImplementationName()
102 {
103     static OUString *pImplName = 0;
104     if( ! pImplName )
105     {
106         MutexGuard guard( Mutex::getGlobalMutex() );
107         if( ! pImplName )
108         {
109             static OUString implName(
110                 RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.stoc.JavaComponentLoader" ) );
111             pImplName = &implName;
112         }
113     }
114     return *pImplName;
115 }
116 
117 class JavaComponentLoader : public WeakImplHelper2<XImplementationLoader, XServiceInfo>
118 {
119     css::uno::Reference<XComponentContext> m_xComponentContext;
120     /** Do not use m_javaLoader directly. Instead use getJavaLoader.
121      */
122     css::uno::Reference<XImplementationLoader> m_javaLoader;
123     /** The retured Reference contains a null pointer if the office is not configured
124         to run java.
125 
126         @exception com::sun::star::uno::RuntimeException
127         If the Java implementation of the loader could not be obtained, for reasons other
128         then that java was not configured the RuntimeException is thrown.
129      */
130     const css::uno::Reference<XImplementationLoader> & getJavaLoader();
131 
132 
133 public:
134 	JavaComponentLoader(const css::uno::Reference<XComponentContext> & xCtx)
135         throw(RuntimeException);
136 	virtual ~JavaComponentLoader() throw();
137 
138 public:
139 	// XServiceInfo
140 	virtual OUString SAL_CALL getImplementationName() throw(RuntimeException);
141 	virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName)
142         throw(RuntimeException);
143 	virtual Sequence<OUString> SAL_CALL getSupportedServiceNames()
144         throw(RuntimeException);
145 
146 	// XImplementationLoader
147 	virtual css::uno::Reference<XInterface> SAL_CALL activate(
148         const OUString& implementationName, const OUString& implementationLoaderUrl,
149         const OUString& locationUrl, const css::uno::Reference<XRegistryKey>& xKey)
150         throw(CannotActivateFactoryException, RuntimeException);
151 	virtual sal_Bool SAL_CALL writeRegistryInfo(
152         const css::uno::Reference<XRegistryKey>& xKey,
153         const OUString& implementationLoaderUrl, const OUString& locationUrl)
154         throw(CannotRegisterImplementationException, RuntimeException);
155 };
156 
157 const css::uno::Reference<XImplementationLoader> & JavaComponentLoader::getJavaLoader()
158 {
159     MutexGuard aGuard(getInitMutex());
160 
161     if (m_javaLoader.is())
162         return m_javaLoader;
163 
164 	uno_Environment * pJava_environment = NULL;
165 	uno_Environment * pUno_environment = NULL;
166 	typelib_InterfaceTypeDescription * pType_XImplementationLoader = 0;
167 
168 	try {
169 		// get a java vm, where we can create a loader
170 		css::uno::Reference<XJavaVM> javaVM_xJavaVM(
171             m_xComponentContext->getValueByName(
172                 OUString(RTL_CONSTASCII_USTRINGPARAM(
173                              "/singletons/"
174                              "com.sun.star.java.theJavaVirtualMachine"))),
175             UNO_QUERY_THROW);
176 
177         // Use the special protocol of XJavaVM.getJavaVM:  If the passed in
178         // process ID has an extra 17th byte of value one, the returned any
179         // contains a pointer to a jvmaccess::UnoVirtualMachine, instead of the
180         // underlying JavaVM pointer:
181 		Sequence<sal_Int8> processID(17);
182 		rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8 *>(processID.getArray()));
183         processID[16] = 1;
184 
185         // We get a non-refcounted pointer to a jvmaccess::UnoVirtualMachine
186         // from the XJavaVM service (the pointer is guaranteed to be valid
187         // as long as our reference to the XJavaVM service lasts), and
188         // convert the non-refcounted pointer into a refcounted one
189         // immediately:
190         OSL_ENSURE(sizeof (sal_Int64)
191                         >= sizeof (jvmaccess::UnoVirtualMachine *),
192                     "Pointer cannot be represented as sal_Int64");
193         sal_Int64 nPointer = reinterpret_cast< sal_Int64 >(
194             static_cast< jvmaccess::UnoVirtualMachine * >(0));
195         javaVM_xJavaVM->getJavaVM(processID) >>= nPointer;
196         rtl::Reference< jvmaccess::UnoVirtualMachine > xVirtualMachine(
197             reinterpret_cast< jvmaccess::UnoVirtualMachine * >(nPointer));
198         if (!xVirtualMachine.is())
199             //throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
200             //   "javaloader error - JavaVirtualMachine service could not provide a VM")),
201             //   css::uno::Reference<XInterface>());
202             // We must not throw a RuntimeException, because this might end the applications.
203             // It is ok if java components
204             // are not working because the office can be installed without Java support.
205             return m_javaLoader; // null-ref
206 
207         try
208         {
209             jvmaccess::VirtualMachine::AttachGuard aGuard2(
210                 xVirtualMachine->getVirtualMachine());
211             JNIEnv * pJNIEnv = aGuard2.getEnvironment();
212 
213             // instantiate the java JavaLoader
214             jclass jcClassLoader = pJNIEnv->FindClass("java/lang/ClassLoader");
215             if(pJNIEnv->ExceptionOccurred())
216                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
217                     "javaloader error - could not find class java/lang/ClassLoader")),
218                     css::uno::Reference<XInterface>());
219             jmethodID jmLoadClass = pJNIEnv->GetMethodID(
220                 jcClassLoader, "loadClass",
221                 "(Ljava/lang/String;)Ljava/lang/Class;");
222             if(pJNIEnv->ExceptionOccurred())
223                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
224                     "javaloader error - could not find method java/lang/ClassLoader.loadClass")),
225                     css::uno::Reference<XInterface>());
226             jvalue arg;
227             arg.l = pJNIEnv->NewStringUTF(
228                 "com.sun.star.comp.loader.JavaLoader");
229             if(pJNIEnv->ExceptionOccurred())
230                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
231                     "javaloader error - could not create string")),
232                     css::uno::Reference<XInterface>());
233             jclass jcJavaLoader = static_cast< jclass >(
234                 pJNIEnv->CallObjectMethodA(
235                     static_cast< jobject >(xVirtualMachine->getClassLoader()),
236                     jmLoadClass, &arg));
237             if(pJNIEnv->ExceptionOccurred())
238                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
239                     "javaloader error - could not find class com/sun/star/comp/loader/JavaLoader")),
240                     css::uno::Reference<XInterface>());
241             jmethodID jmJavaLoader_init = pJNIEnv->GetMethodID(jcJavaLoader, "<init>", "()V");
242             if(pJNIEnv->ExceptionOccurred())
243                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
244                     "javaloader error - instantiation of com.sun.star.comp.loader.JavaLoader failed")),
245                     css::uno::Reference<XInterface>());
246             jobject joJavaLoader = pJNIEnv->NewObject(jcJavaLoader, jmJavaLoader_init);
247             if(pJNIEnv->ExceptionOccurred())
248                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
249                     "javaloader error - instantiation of com.sun.star.comp.loader.JavaLoader failed")),
250                     css::uno::Reference<XInterface>());
251 
252             // map the java JavaLoader to this environment
253             OUString sJava(RTL_CONSTASCII_USTRINGPARAM("java"));
254             uno_getEnvironment(&pJava_environment, sJava.pData,
255                                 xVirtualMachine.get());
256             if(!pJava_environment)
257                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
258                     "javaloader error - no Java environment available")), css::uno::Reference<XInterface>());
259 
260             // why is there no convinient contructor?
261             OUString sCppu_current_lb_name(RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME));
262             uno_getEnvironment(&pUno_environment, sCppu_current_lb_name.pData, NULL);
263             if(!pUno_environment)
264                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
265                     "javaloader error - no C++ environment available")), css::uno::Reference<XInterface>());
266 
267             Mapping java_curr(pJava_environment, pUno_environment);
268             if(!java_curr.is())
269                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
270                     "javaloader error - no mapping from java to C++ ")), css::uno::Reference<XInterface>());
271 
272             // release java environment
273             pJava_environment->release(pJava_environment);
274             pJava_environment = NULL;
275 
276             // release uno environment
277             pUno_environment->release(pUno_environment);
278             pUno_environment = NULL;
279 
280             getCppuType((css::uno::Reference<XImplementationLoader> *) 0).
281                 getDescription((typelib_TypeDescription **) & pType_XImplementationLoader);
282             if(!pType_XImplementationLoader)
283                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
284                     "javaloader error - no type information for XImplementationLoader")),
285                     css::uno::Reference<XInterface>());
286 
287             m_javaLoader = css::uno::Reference<XImplementationLoader>(reinterpret_cast<XImplementationLoader *>(
288                             java_curr.mapInterface(joJavaLoader, pType_XImplementationLoader)));
289             pJNIEnv->DeleteLocalRef( joJavaLoader );
290             if(!m_javaLoader.is())
291                 throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
292                     "javaloader error - mapping of java XImplementationLoader to c++ failed")),
293                     css::uno::Reference<XInterface>());
294 
295             typelib_typedescription_release(reinterpret_cast<typelib_TypeDescription *>(pType_XImplementationLoader));
296             pType_XImplementationLoader = NULL;
297         }
298         catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &)
299         {
300             throw RuntimeException(
301                 OUString(RTL_CONSTASCII_USTRINGPARAM(
302                                 "jvmaccess::VirtualMachine::AttachGuard"
303                                 "::CreationException")),0);
304         }
305 
306 		// set the service manager at the javaloader
307 		css::uno::Reference<XInitialization> javaLoader_XInitialization(m_javaLoader, UNO_QUERY);
308 		if(!javaLoader_XInitialization.is())
309             throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM(
310                 "javaloader error - initialization of java javaloader failed, no XInitialization")),
311                 css::uno::Reference<XInterface>());
312 
313 		Any any;
314 		any <<= css::uno::Reference<XMultiComponentFactory>(
315             m_xComponentContext->getServiceManager());
316 
317 		javaLoader_XInitialization->initialize(Sequence<Any>(&any, 1));
318 	}
319 	catch(RuntimeException &) {
320 		if(pJava_environment)
321 			pJava_environment->release(pJava_environment);
322 
323 		if(pUno_environment)
324 			pUno_environment->release(pUno_environment);
325 
326 		if(pType_XImplementationLoader)
327 			typelib_typedescription_release(
328                 reinterpret_cast<typelib_TypeDescription *>(pType_XImplementationLoader));
329 		throw;
330 	}
331 	OSL_TRACE("javaloader.cxx: mapped javaloader - 0x%x", m_javaLoader.get());
332     return m_javaLoader;
333 }
334 
335 JavaComponentLoader::JavaComponentLoader(const css::uno::Reference<XComponentContext> & xCtx) throw(RuntimeException) :
336     m_xComponentContext(xCtx)
337 
338 {
339 
340 }
341 
342 JavaComponentLoader::~JavaComponentLoader() throw()
343 {
344 }
345 
346 // XServiceInfo
347 OUString SAL_CALL JavaComponentLoader::getImplementationName()
348     throw(::com::sun::star::uno::RuntimeException)
349 {
350 	return loader_getImplementationName();
351 }
352 
353 sal_Bool SAL_CALL JavaComponentLoader::supportsService(const OUString & ServiceName)
354     throw(::com::sun::star::uno::RuntimeException)
355 {
356 	sal_Bool bSupport = sal_False;
357 
358 	Sequence<OUString> aSNL = getSupportedServiceNames();
359 	const OUString * pArray = aSNL.getArray();
360 	for(sal_Int32 i = 0; i < aSNL.getLength() && !bSupport; ++ i)
361 		bSupport = pArray[i] == ServiceName;
362 
363 	return bSupport;
364 }
365 
366 Sequence<OUString> SAL_CALL JavaComponentLoader::getSupportedServiceNames()
367     throw(::com::sun::star::uno::RuntimeException)
368 {
369 	return loader_getSupportedServiceNames();
370 }
371 
372 
373 
374 // XImplementationLoader
375 sal_Bool SAL_CALL JavaComponentLoader::writeRegistryInfo(
376     const css::uno::Reference<XRegistryKey> & xKey, const OUString & blabla,
377     const OUString & rLibName)
378 	throw(CannotRegisterImplementationException, RuntimeException)
379 {
380     const css::uno::Reference<XImplementationLoader> & loader = getJavaLoader();
381     if (loader.is())
382         return loader->writeRegistryInfo(xKey, blabla, rLibName);
383     else
384         throw CannotRegisterImplementationException(
385             OUString(RTL_CONSTASCII_USTRINGPARAM("Could not create Java implementation loader")), NULL);
386 }
387 
388 
389 css::uno::Reference<XInterface> SAL_CALL JavaComponentLoader::activate(
390     const OUString & rImplName, const OUString & blabla, const OUString & rLibName,
391     const css::uno::Reference<XRegistryKey> & xKey)
392     throw(CannotActivateFactoryException, RuntimeException)
393 {
394     const css::uno::Reference<XImplementationLoader> & loader = getJavaLoader();
395     if (loader.is())
396         return loader->activate(rImplName, blabla, rLibName, xKey);
397     else
398         throw CannotActivateFactoryException(
399             OUString(RTL_CONSTASCII_USTRINGPARAM("Could not create Java implementation loader")), NULL);
400 }
401 
402 static Mutex & getInitMutex()
403 {
404     static Mutex * pMutex = 0;
405     if( ! pMutex )
406     {
407         MutexGuard guard( Mutex::getGlobalMutex() );
408         if( ! pMutex )
409         {
410             static Mutex mutex;
411             pMutex = &mutex;
412         }
413     }
414     return *pMutex;
415 }
416 
417 css::uno::Reference<XInterface> SAL_CALL JavaComponentLoader_CreateInstance(const css::uno::Reference<XComponentContext> & xCtx) throw(Exception)
418 {
419     css::uno::Reference<XInterface> xRet;
420 
421     try {
422         MutexGuard guard( getInitMutex() );
423         // The javaloader is never destroyed and there can be only one!
424         // Note that the first context wins ....
425         static css::uno::Reference< XInterface > *pStaticRef = 0;
426         if( pStaticRef )
427         {
428             xRet = *pStaticRef;
429         }
430         else
431         {
432             xRet = *new JavaComponentLoader(xCtx);
433             pStaticRef = new css::uno::Reference< XInterface > ( xRet );
434         }
435     }
436     catch(RuntimeException & runtimeException) {
437         OString message = OUStringToOString(runtimeException.Message, RTL_TEXTENCODING_ASCII_US);
438         osl_trace("javaloader - could not init javaloader cause of %s", message.getStr());
439         throw;
440     }
441 
442     return xRet;
443 }
444 
445 } //end namespace
446 
447 
448 using namespace stoc_javaloader;
449 
450 static struct ImplementationEntry g_entries[] =
451 {
452 	{
453 		JavaComponentLoader_CreateInstance, loader_getImplementationName,
454 		loader_getSupportedServiceNames, createSingleComponentFactory,
455 		0 , 0
456 	},
457 	{ 0, 0, 0, 0, 0, 0 }
458 };
459 
460 extern "C"
461 {
462 // NOTE: component_canUnload is not exported, as the library cannot be unloaded.
463 
464 //==================================================================================================
465 void SAL_CALL component_getImplementationEnvironment(
466 	const sal_Char ** ppEnvTypeName, uno_Environment ** )
467 {
468 	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
469 }
470 //==================================================================================================
471 void * SAL_CALL component_getFactory(
472 	const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
473 {
474 	return component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries );
475 }
476 }
477