diff options
Diffstat (limited to 'stoc/source/javavm')
-rw-r--r-- | stoc/source/javavm/interact.cxx | 108 | ||||
-rw-r--r-- | stoc/source/javavm/interact.hxx | 67 | ||||
-rw-r--r-- | stoc/source/javavm/javavm.component | 28 | ||||
-rw-r--r-- | stoc/source/javavm/javavm.cxx | 1416 | ||||
-rw-r--r-- | stoc/source/javavm/javavm.hxx | 147 | ||||
-rw-r--r-- | stoc/source/javavm/jvmargs.cxx | 32 | ||||
-rw-r--r-- | stoc/source/javavm/jvmargs.hxx | 58 |
7 files changed, 1856 insertions, 0 deletions
diff --git a/stoc/source/javavm/interact.cxx b/stoc/source/javavm/interact.cxx new file mode 100644 index 0000000000..49f223383a --- /dev/null +++ b/stoc/source/javavm/interact.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + +#include "interact.hxx" + +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionRetry.hpp> +#include <cppuhelper/implbase.hxx> +#include <mutex> +#include <utility> + +namespace com::sun::star::task { class XInteractionContinuation; } + +using stoc_javavm::InteractionRequest; + +namespace { + +class AbortContinuation: + public cppu::WeakImplHelper<css::task::XInteractionAbort> +{ +public: + AbortContinuation() {} + AbortContinuation(const AbortContinuation&) = delete; + AbortContinuation& operator=(const AbortContinuation&)= delete; + + virtual void SAL_CALL select() override {} + +private: + virtual ~AbortContinuation() override {} +}; + +} + +class InteractionRequest::RetryContinuation: + public cppu::WeakImplHelper<css::task::XInteractionRetry> +{ +public: + RetryContinuation(): m_bSelected(false) {} + RetryContinuation(const RetryContinuation&) = delete; + RetryContinuation& operator=(const RetryContinuation&) = delete; + + virtual void SAL_CALL select() override; + + bool isSelected() const; + +private: + virtual ~RetryContinuation() override {} + + mutable std::mutex m_aMutex; + bool m_bSelected; +}; + +void SAL_CALL InteractionRequest::RetryContinuation::select() +{ + std::scoped_lock aGuard(m_aMutex); + m_bSelected = true; +} + +bool InteractionRequest::RetryContinuation::isSelected() const +{ + std::scoped_lock aGuard(m_aMutex); + return m_bSelected; +} + +InteractionRequest::InteractionRequest(css::uno::Any aRequest): + m_aRequest(std::move(aRequest)) +{ + m_xRetryContinuation = new RetryContinuation; + m_aContinuations = { new AbortContinuation, m_xRetryContinuation }; +} + +css::uno::Any SAL_CALL InteractionRequest::getRequest() +{ + return m_aRequest; +} + +css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > +SAL_CALL InteractionRequest::getContinuations() +{ + return m_aContinuations; +} + +bool InteractionRequest::retry() const +{ + return m_xRetryContinuation.is() && m_xRetryContinuation->isSelected(); +} + +InteractionRequest::~InteractionRequest() +{} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/interact.hxx b/stoc/source/javavm/interact.hxx new file mode 100644 index 0000000000..42fa373a33 --- /dev/null +++ b/stoc/source/javavm/interact.hxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX + +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +namespace com::sun::star::task { + class XInteractionContinuation; +} + +namespace stoc_javavm { + +class InteractionRequest: + public cppu::WeakImplHelper< css::task::XInteractionRequest > +{ +public: + explicit InteractionRequest(css::uno::Any aRequest); + + virtual css::uno::Any SAL_CALL getRequest() override; + + virtual css::uno::Sequence< css::uno::Reference< + css::task::XInteractionContinuation > > SAL_CALL + getContinuations() override; + + bool retry() const; + +private: + class RetryContinuation; + + InteractionRequest(InteractionRequest const &) = delete; + void operator =(const InteractionRequest&) = delete; + + virtual ~InteractionRequest() override; + + css::uno::Any m_aRequest; + css::uno::Sequence< css::uno::Reference< + css::task::XInteractionContinuation > > m_aContinuations; + rtl::Reference< RetryContinuation > m_xRetryContinuation; +}; + +} + +#endif // INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/javavm.component b/stoc/source/javavm/javavm.component new file mode 100644 index 0000000000..71379052b8 --- /dev/null +++ b/stoc/source/javavm/javavm.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.JavaVirtualMachine" + constructor="stoc_JavaVM_get_implementation" + single-instance="true"> + <service name="com.sun.star.java.JavaVirtualMachine"/> + <singleton name="com.sun.star.java.theJavaVirtualMachine"/> + </implementation> +</component> diff --git a/stoc/source/javavm/javavm.cxx b/stoc/source/javavm/javavm.cxx new file mode 100644 index 0000000000..a1fedd1d2d --- /dev/null +++ b/stoc/source/javavm/javavm.cxx @@ -0,0 +1,1416 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + +#include "javavm.hxx" + +#include "interact.hxx" +#include "jvmargs.hxx" + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/java/JavaNotFoundException.hpp> +#include <com/sun/star/java/InvalidJavaSettingsException.hpp> +#include <com/sun/star/java/RestartRequiredException.hpp> +#include <com/sun/star/java/JavaDisabledException.hpp> +#include <com/sun/star/java/JavaVMCreationFailureException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <comphelper/propertysequence.hxx> +#include <comphelper/SetFlagContextHelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <jvmaccess/classpath.hxx> +#include <jvmaccess/unovirtualmachine.hxx> +#include <jvmaccess/virtualmachine.hxx> +#include <rtl/process.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <sal/log.hxx> +#include <uno/current_context.hxx> +#include <jvmfwk/framework.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <jni.h> + +#include <stack> +#include <string.h> +#include <time.h> +#include <memory> +#include <utility> +#include <vector> + +// Properties of the javavm can be put +// as a comma separated list in this +// environment variable +#ifdef UNIX +#define TIMEZONE "MEZ" +#else +#define TIMEZONE "MET" +#endif + +#ifdef MACOSX +#include <premac.h> +#include <CoreFoundation/CoreFoundation.h> +#include <postmac.h> +#endif + +/* Within this implementation of the com.sun.star.java.JavaVirtualMachine + * service and com.sun.star.java.theJavaVirtualMachine singleton, the method + * com.sun.star.java.XJavaVM.getJavaVM relies on the following: + * 1 The string "$URE_INTERNAL_JAVA_DIR/" is expanded via the + * com.sun.star.util.theMacroExpander singleton into an internal (see the + * com.sun.star.uri.ExternalUriReferenceTranslator service), hierarchical URI + * reference relative to which the URE JAR files can be addressed. + * 2 The string "$URE_INTERNAL_JAVA_CLASSPATH" is either not expandable via the + * com.sun.star.util.theMacroExpander singleton + * (com.sun.star.lang.IllegalArgumentException), or is expanded via the + * com.sun.star.util.theMacroExpander singleton into a list of zero or more + * internal (see the com.sun.star.uri.ExternalUriReferenceTranslator service) + * URIs, where any space characters (U+0020) are ignored (and, in particular, + * separate adjacent URIs). + * If either of these requirements is not met, getJavaVM raises a + * com.sun.star.uno.RuntimeException. + */ + +using stoc_javavm::JavaVirtualMachine; + +namespace { + + +class NoJavaIniException: public css::uno::Exception +{ +}; + +typedef std::stack< jvmaccess::VirtualMachine::AttachGuard * > GuardStack; + +extern "C" { + +static void destroyAttachGuards(void * pData) +{ + GuardStack * pStack = static_cast< GuardStack * >(pData); + if (pStack != nullptr) + { + while (!pStack->empty()) + { + delete pStack->top(); + pStack->pop(); + } + delete pStack; + } +} + +} + +bool askForRetry(css::uno::Any const & rException) +{ + if (comphelper::IsContextFlagActive("DontEnableJava")) + return false; + + css::uno::Reference< css::uno::XCurrentContext > xContext( + css::uno::getCurrentContext()); + if (xContext.is()) + { + css::uno::Reference< css::task::XInteractionHandler > xHandler; + xContext->getValueByName("java-vm.interaction-handler") + >>= xHandler; + if (xHandler.is()) + { + rtl::Reference< stoc_javavm::InteractionRequest > xRequest( + new stoc_javavm::InteractionRequest(rException)); + xHandler->handle(xRequest); + return xRequest->retry(); + } + } + return false; +} + +// Only gets the properties if the "Proxy Server" entry in the option dialog is +// set to manual (i.e. not to none) +/// @throws css::uno::Exception +void getINetPropsFromConfig(stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx ) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = xSMgr->createInstanceWithContext( + "com.sun.star.configuration.ConfigurationRegistry", + xCtx ); + if(!xConfRegistry.is()) throw css::uno::RuntimeException("javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple(xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open("org.openoffice.Inet", true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = xConfRegistry_simple->getRootKey(); + +// if ooInetProxyType is not 0 then read the settings + css::uno::Reference<css::registry::XRegistryKey> proxyEnable= xRegistryRootKey->openKey("Settings/ooInetProxyType"); + if( proxyEnable.is() && 0 != proxyEnable->getLongValue()) + { + // read http proxy name + css::uno::Reference<css::registry::XRegistryKey> httpProxy_name = xRegistryRootKey->openKey("Settings/ooInetHTTPProxyName"); + if(httpProxy_name.is() && !httpProxy_name->getStringValue().isEmpty()) { + OUString httpHost = "http.proxyHost=" + httpProxy_name->getStringValue(); + + // read http proxy port + css::uno::Reference<css::registry::XRegistryKey> httpProxy_port = xRegistryRootKey->openKey("Settings/ooInetHTTPProxyPort"); + if(httpProxy_port.is() && httpProxy_port->getLongValue()) { + OUString httpPort = "http.proxyPort=" + OUString::number(httpProxy_port->getLongValue()); + + pjvm->pushProp(httpHost); + pjvm->pushProp(httpPort); + } + } + + // read https proxy name + css::uno::Reference<css::registry::XRegistryKey> httpsProxy_name = xRegistryRootKey->openKey("Settings/ooInetHTTPSProxyName"); + if(httpsProxy_name.is() && !httpsProxy_name->getStringValue().isEmpty()) { + OUString httpsHost = "https.proxyHost=" + httpsProxy_name->getStringValue(); + + // read https proxy port + css::uno::Reference<css::registry::XRegistryKey> httpsProxy_port = xRegistryRootKey->openKey("Settings/ooInetHTTPSProxyPort"); + if(httpsProxy_port.is() && httpsProxy_port->getLongValue()) { + OUString httpsPort = "https.proxyPort=" + OUString::number(httpsProxy_port->getLongValue()); + + pjvm->pushProp(httpsHost); + pjvm->pushProp(httpsPort); + } + } + + // read nonProxyHosts + css::uno::Reference<css::registry::XRegistryKey> nonProxies_name = xRegistryRootKey->openKey("Settings/ooInetNoProxy"); + if(nonProxies_name.is() && !nonProxies_name->getStringValue().isEmpty()) { + OUString value = nonProxies_name->getStringValue(); + // replace the separator ";" by "|" + value = value.replace(';', '|'); + + OUString httpNonProxyHosts = "http.nonProxyHosts=" + value; + + pjvm->pushProp(httpNonProxyHosts); + } + } + xConfRegistry_simple->close(); +} + +/// @throws css::uno::Exception +void getDefaultLocaleFromConfig( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx ) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = + xSMgr->createInstanceWithContext( "com.sun.star.configuration.ConfigurationRegistry", xCtx ); + if(!xConfRegistry.is()) + throw css::uno::RuntimeException( + "javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple( + xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open("org.openoffice.Setup", true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = xConfRegistry_simple->getRootKey(); + + // Since 1.7 Java knows DISPLAY and FORMAT locales, which match our UI and + // system locale. See + // http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/569b1b644416/src/share/classes/java/util/Locale.java + // https://docs.oracle.com/javase/tutorial/i18n/locale/scope.html + // https://docs.oracle.com/javase/7/docs/api/java/util/Locale.html + + // Read UI language/locale. + css::uno::Reference<css::registry::XRegistryKey> xUILocale = xRegistryRootKey->openKey("L10N/ooLocale"); + if(xUILocale.is() && !xUILocale->getStringValue().isEmpty()) { + LanguageTag aLanguageTag( xUILocale->getStringValue()); + OUString language; + OUString script; + OUString country; + // Java knows nothing but plain old ISO codes, unless Locale.Builder or + // Locale.forLanguageTag() are used, or non-standardized variant field + // content which we ignore. + aLanguageTag.getIsoLanguageScriptCountry( language, script, country); + + if(!language.isEmpty()) { + OUString prop = "user.language=" + language; + pjvm->pushProp(prop); + } + + // As of Java 7 also script is supported. + if(!script.isEmpty()) { + OUString prop = "user.script=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country=" + country; + pjvm->pushProp(prop); + } + + // Java 7 DISPLAY category is our UI language/locale. + if(!language.isEmpty()) { + OUString prop = "user.language.display=" + language; + pjvm->pushProp(prop); + } + + if(!script.isEmpty()) { + OUString prop = "user.script.display=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country.display=" + country; + pjvm->pushProp(prop); + } + } + + // Read system locale. + css::uno::Reference<css::registry::XRegistryKey> xLocale = xRegistryRootKey->openKey("L10N/ooSetupSystemLocale"); + if(xLocale.is() && !xLocale->getStringValue().isEmpty()) { + LanguageTag aLanguageTag( xLocale->getStringValue()); + OUString language; + OUString script; + OUString country; + // Java knows nothing but plain old ISO codes, unless Locale.Builder or + // Locale.forLanguageTag() are used, or non-standardized variant field + // content which we ignore. + aLanguageTag.getIsoLanguageScriptCountry( language, script, country); + + // Java 7 FORMAT category is our system locale. + if(!language.isEmpty()) { + OUString prop = "user.language.format=" + language; + pjvm->pushProp(prop); + } + + if(!script.isEmpty()) { + OUString prop = "user.script.format=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country.format=" + country; + pjvm->pushProp(prop); + } + } + + xConfRegistry_simple->close(); +} + +/// @throws css::uno::Exception +void getJavaPropsFromSafetySettings( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = + xSMgr->createInstanceWithContext( + "com.sun.star.configuration.ConfigurationRegistry", + xCtx); + if(!xConfRegistry.is()) + throw css::uno::RuntimeException( + "javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple( + xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open( + "org.openoffice.Office.Java", + true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = + xConfRegistry_simple->getRootKey(); + + if (xRegistryRootKey.is()) + { + css::uno::Reference<css::registry::XRegistryKey> key_NetAccess= xRegistryRootKey->openKey("VirtualMachine/NetAccess"); + if (key_NetAccess.is()) + { + sal_Int32 val= key_NetAccess->getLongValue(); + OUString sVal; + switch( val) + { + case 0: sVal = "host"; + break; + case 1: sVal = "unrestricted"; + break; + case 3: sVal = "none"; + break; + } + OUString sProperty = "appletviewer.security.mode=" + sVal; + pjvm->pushProp(sProperty); + } + css::uno::Reference<css::registry::XRegistryKey> key_CheckSecurity= xRegistryRootKey->openKey( + "VirtualMachine/Security"); + if( key_CheckSecurity.is()) + { + bool val = static_cast<bool>(key_CheckSecurity->getLongValue()); + OUString sProperty("stardiv.security.disableSecurity="); + if( val) + sProperty += "false"; + else + sProperty += "true"; + pjvm->pushProp( sProperty); + } + } + xConfRegistry_simple->close(); +} + +void setTimeZone(stoc_javavm::JVM * pjvm) noexcept { + /* A Bug in the Java function + ** struct Hjava_util_Properties * java_lang_System_initProperties( + ** struct Hjava_lang_System *this, + ** struct Hjava_util_Properties *props); + ** This function doesn't detect MEZ, MET or "W. Europe Standard Time" + */ + struct tm *tmData; + time_t clock = time(nullptr); + tzset(); + tmData = localtime(&clock); +#ifdef MACOSX + char * p = tmData->tm_zone; +#elif defined(_MSC_VER) + char * p = _tzname[0]; + (void)tmData; +#else + char * p = tzname[0]; + (void)tmData; +#endif + + if (!strcmp(TIMEZONE, p)) + pjvm->pushProp("user.timezone=ECT"); +} + +/// @throws css::uno::Exception +void initVMConfiguration( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext > &xCtx) +{ + stoc_javavm::JVM jvm; + try { + getINetPropsFromConfig(&jvm, xSMgr, xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "can not get INETProps because of " << exception); + } + + try { + getDefaultLocaleFromConfig(&jvm, xSMgr,xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "can not get locale because of " << exception); + } + + try + { + getJavaPropsFromSafetySettings(&jvm, xSMgr, xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "couldn't get safety settings because of " << exception); + } + + *pjvm= jvm; + + // rhbz#1285356, native look will be gtk2, which crashes + // when gtk3 is already loaded. Until there is a solution + // java-side force look and feel to something that doesn't + // crash when we are using gtk3 + if (getenv("STOC_FORCE_SYSTEM_LAF")) + pjvm->pushProp("swing.systemlaf=javax.swing.plaf.metal.MetalLookAndFeel"); + + setTimeZone(pjvm); +} + +class DetachCurrentThread { +public: + explicit DetachCurrentThread(JavaVM * jvm): m_jvm(jvm) {} + + ~DetachCurrentThread() { +#ifdef MACOSX + // tdf#101376 don't detach thread if it is the main thread on macOS + // On macOS, many AWT classes do their work on the main thread + // deep in native methods in the java.awt.* classes. The problem + // is that Oracle's and OpenJDK's JVMs don't bracket their + // "perform on main thread" native calls with "attach/detach + // current thread" calls to the JVM. + if (CFRunLoopGetCurrent() != CFRunLoopGetMain()) +#endif + if (m_jvm->DetachCurrentThread() != 0) { + OSL_ASSERT(false); + } + } + + DetachCurrentThread(const DetachCurrentThread&) = delete; + DetachCurrentThread& operator=(const DetachCurrentThread&) = delete; + +private: + JavaVM * m_jvm; +}; + +} + +JavaVirtualMachine::JavaVirtualMachine( + css::uno::Reference< css::uno::XComponentContext > xContext): + WeakComponentImplHelper(m_aMutex), + m_xContext(std::move(xContext)), + m_bDisposed(false), + m_pJavaVm(nullptr), + m_aAttachGuards(destroyAttachGuards) // TODO check for validity +{} + +void SAL_CALL +JavaVirtualMachine::initialize(css::uno::Sequence< css::uno::Any > const & + rArguments) +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + if (m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "bad call to initialize", + getXWeak()); + css::beans::NamedValue val; + if (rArguments.getLength() == 1 && (rArguments[0] >>= val) && val.Name == "UnoVirtualMachine" ) + { + OSL_ENSURE( + sizeof (sal_Int64) >= sizeof (jvmaccess::UnoVirtualMachine *), + "Pointer cannot be represented as sal_Int64"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::UnoVirtualMachine * >(nullptr)); + val.Value >>= nPointer; + m_xUnoVirtualMachine = + reinterpret_cast< jvmaccess::UnoVirtualMachine * >(nPointer); + } else { + OSL_ENSURE( + sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *), + "Pointer cannot be represented as sal_Int64"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::VirtualMachine * >(nullptr)); + if (rArguments.getLength() == 1) + rArguments[0] >>= nPointer; + rtl::Reference< jvmaccess::VirtualMachine > vm( + reinterpret_cast< jvmaccess::VirtualMachine * >(nPointer)); + if (vm.is()) { + try { + m_xUnoVirtualMachine = new jvmaccess::UnoVirtualMachine(vm, nullptr); + } catch (jvmaccess::UnoVirtualMachine::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::UnoVirtualMachine::CreationException", + getXWeak(), anyEx ); + } + } + } + if (!m_xUnoVirtualMachine.is()) { + throw css::lang::IllegalArgumentException( + "sequence of exactly one any containing either (a) a" + " com.sun.star.beans.NamedValue with Name" + " \"UnoVirtualMachine\" and Value a hyper representing a" + " non-null pointer to a jvmaccess:UnoVirtualMachine, or (b)" + " a hyper representing a non-null pointer to a" + " jvmaccess::VirtualMachine required", + getXWeak(), 0); + } + m_xVirtualMachine = m_xUnoVirtualMachine->getVirtualMachine(); +} + +OUString SAL_CALL JavaVirtualMachine::getImplementationName() +{ + return "com.sun.star.comp.stoc.JavaVirtualMachine"; +} + +sal_Bool SAL_CALL +JavaVirtualMachine::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL +JavaVirtualMachine::getSupportedServiceNames() +{ + return { "com.sun.star.java.JavaVirtualMachine" }; +} + +css::uno::Any SAL_CALL +JavaVirtualMachine::getJavaVM(css::uno::Sequence< sal_Int8 > const & rProcessId) +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + css::uno::Sequence< sal_Int8 > aId(16); + rtl_getGlobalProcessId(reinterpret_cast< sal_uInt8 * >(aId.getArray())); + enum ReturnType { + RETURN_JAVAVM, RETURN_VIRTUALMACHINE, RETURN_UNOVIRTUALMACHINE }; + ReturnType returnType = + rProcessId.getLength() == 17 && rProcessId[16] == 0 + ? RETURN_VIRTUALMACHINE + : rProcessId.getLength() == 17 && rProcessId[16] == 1 + ? RETURN_UNOVIRTUALMACHINE + : RETURN_JAVAVM; + css::uno::Sequence< sal_Int8 > aProcessId(rProcessId); + if (returnType != RETURN_JAVAVM) + aProcessId.realloc(16); + if (aId != aProcessId) + return css::uno::Any(); + + std::unique_ptr<JavaInfo> info; + while (!m_xVirtualMachine.is()) // retry until successful + { + stoc_javavm::JVM aJvm; + initVMConfiguration(&aJvm, m_xContext->getServiceManager(), + m_xContext); + const std::vector<OUString> & props = aJvm.getProperties(); + std::vector<OUString> options; + options.reserve(props.size()); + for (auto const& i : props) + { + options.push_back(i.startsWith("-") ? i : "-D" + i); + } + + JNIEnv * pMainThreadEnv = nullptr; + javaFrameworkError errcode; + + if (getenv("STOC_FORCE_NO_JRE")) + errcode = JFW_E_NO_SELECT; + else + errcode = jfw_startVM(info.get(), options, & m_pJavaVm, + & pMainThreadEnv); + + bool bStarted = false; + switch (errcode) + { + case JFW_E_NONE: bStarted = true; break; + case JFW_E_NO_SELECT: + { + // No Java configured. We silently run the Java configuration + info.reset(); + javaFrameworkError errFind; + if (getenv("STOC_FORCE_NO_JRE")) + errFind = JFW_E_NO_JAVA_FOUND; + else + errFind = jfw_findAndSelectJRE(&info); + if (errFind == JFW_E_NONE) + { + continue; + } + else if (errFind == JFW_E_NO_JAVA_FOUND) + { + + //Warning MessageBox: + //%PRODUCTNAME requires a Java runtime environment (JRE) to perform this task. + //Please install a JRE and restart %PRODUCTNAME. + css::java::JavaNotFoundException exc( + "JavaVirtualMachine::getJavaVM failed because" + " No suitable JRE found!", + getXWeak()); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + else + { + //An unexpected error occurred + throw css::uno::RuntimeException( + "[JavaVirtualMachine]:An unexpected error occurred" + " while searching for a Java, " + OUString::number(errFind), nullptr); + } + } + case JFW_E_INVALID_SETTINGS: + { + //Warning MessageBox: + // The %PRODUCTNAME configuration has been changed. Under Tools + // - Options - %PRODUCTNAME - Java, select the Java runtime environment + // you want to have used by %PRODUCTNAME. + css::java::InvalidJavaSettingsException exc( + "JavaVirtualMachine::getJavaVM failed because" + " Java settings have changed!", + getXWeak()); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + case JFW_E_JAVA_DISABLED: + { + //QueryBox: + //%PRODUCTNAME requires a Java runtime environment (JRE) to perform + //this task. However, use of a JRE has been disabled. Do you want to + //enable the use of a JRE now? + css::java::JavaDisabledException exc( + "JavaVirtualMachine::getJavaVM failed because Java is disabled!", + getXWeak()); + if( ! askForRetry(css::uno::Any(exc))) + return css::uno::Any(); + continue; + } + case JFW_E_VM_CREATION_FAILED: + { + //If the creation failed because the JRE has been uninstalled then + //we search another one. As long as there is a javaldx, we should + //never come into this situation. javaldx checks always if the JRE + //still exist. + std::unique_ptr<JavaInfo> aJavaInfo; + if (JFW_E_NONE == jfw_getSelectedJRE(&aJavaInfo)) + { + bool bExist = false; + if (JFW_E_NONE == jfw_existJRE(aJavaInfo.get(), &bExist)) + { + if (!bExist + && ! (aJavaInfo->nRequirements & JFW_REQUIRE_NEEDRESTART)) + { + info.reset(); + javaFrameworkError errFind = jfw_findAndSelectJRE( + &info); + if (errFind == JFW_E_NONE) + { + continue; + } + } + } + } + + //Error: %PRODUCTNAME requires a Java + //runtime environment (JRE) to perform this task. The selected JRE + //is defective. Please select another version or install a new JRE + //and select it under Tools - Options - %PRODUCTNAME - Java. + css::java::JavaVMCreationFailureException exc( + "JavaVirtualMachine::getJavaVM failed because Java is defective!", + getXWeak(), 0); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + case JFW_E_RUNNING_JVM: + { + //This service should make sure that we do not start java twice. + OSL_ASSERT(false); + break; + } + case JFW_E_NEED_RESTART: + { + //Error: + //For the selected Java runtime environment to work properly, + //%PRODUCTNAME must be restarted. Please restart %PRODUCTNAME now. + css::java::RestartRequiredException exc( + "JavaVirtualMachine::getJavaVM failed because " + "Office must be restarted before Java can be used!", + getXWeak()); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + default: + //RuntimeException: error is somewhere in the java framework. + //An unexpected error occurred + throw css::uno::RuntimeException( + "[JavaVirtualMachine]:An unexpected error occurred" + " while starting Java!", nullptr); + } + + if (bStarted) + { + { + DetachCurrentThread detach(m_pJavaVm); + // necessary to make debugging work; this thread will be + // suspended when the destructor of detach returns + m_xVirtualMachine = new jvmaccess::VirtualMachine( + m_pJavaVm, JNI_VERSION_1_2, true, pMainThreadEnv); + setUpUnoVirtualMachine(pMainThreadEnv); + } + // Listen for changes in the configuration (e.g. proxy settings): + // TODO this is done too late; changes to the configuration done + // after the above call to initVMConfiguration are lost + registerConfigChangesListener(); + + break; + } + } + if (!m_xUnoVirtualMachine.is()) { + try { + jvmaccess::VirtualMachine::AttachGuard guard(m_xVirtualMachine); + setUpUnoVirtualMachine(guard.getEnvironment()); + } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException occurred", + getXWeak(), anyEx ); + } + } + switch (returnType) { + default: // RETURN_JAVAVM + if (m_pJavaVm == nullptr) { + throw css::uno::RuntimeException( + "JavaVirtualMachine service was initialized in a way" + " that the requested JavaVM pointer is not available", + getXWeak()); + } + return css::uno::Any(reinterpret_cast< sal_IntPtr >(m_pJavaVm)); + case RETURN_VIRTUALMACHINE: + OSL_ASSERT(sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *)); + return css::uno::Any( + reinterpret_cast< sal_Int64 >( + m_xUnoVirtualMachine->getVirtualMachine().get())); + case RETURN_UNOVIRTUALMACHINE: + OSL_ASSERT(sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *)); + return css::uno::Any( + reinterpret_cast< sal_Int64 >(m_xUnoVirtualMachine.get())); + } +} + +sal_Bool SAL_CALL JavaVirtualMachine::isVMStarted() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), getXWeak()); + return m_xUnoVirtualMachine.is(); +} + +sal_Bool SAL_CALL JavaVirtualMachine::isVMEnabled() +{ + { + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), getXWeak()); + } +// stoc_javavm::JVM aJvm; +// initVMConfiguration(&aJvm, m_xContext->getServiceManager(), m_xContext); +// return aJvm.isEnabled(); + //ToDo + bool bEnabled = false; + if (jfw_getEnabled( & bEnabled) != JFW_E_NONE) + throw css::uno::RuntimeException(); + return bEnabled; +} + +sal_Bool SAL_CALL JavaVirtualMachine::isThreadAttached() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), getXWeak()); + // TODO isThreadAttached only returns true if the thread was attached via + // registerThread: + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + return pStack != nullptr && !pStack->empty(); +} + +void SAL_CALL JavaVirtualMachine::registerThread() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + if (!m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::registerThread: null VirtualMachine", + getXWeak()); + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + if (pStack == nullptr) + { + pStack = new GuardStack; + m_aAttachGuards.setData(pStack); + } + try + { + pStack->push( + new jvmaccess::VirtualMachine::AttachGuard( + m_xUnoVirtualMachine->getVirtualMachine())); + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "JavaVirtualMachine::registerThread: jvmaccess::" + "VirtualMachine::AttachGuard::CreationException", + getXWeak(), anyEx ); + } +} + +void SAL_CALL JavaVirtualMachine::revokeThread() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", getXWeak()); + if (!m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::revokeThread: null VirtualMachine", + getXWeak()); + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + if (pStack == nullptr || pStack->empty()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::revokeThread: no matching registerThread", + getXWeak()); + delete pStack->top(); + pStack->pop(); +} + +void SAL_CALL +JavaVirtualMachine::disposing(css::lang::EventObject const & rSource) +{ + osl::MutexGuard aGuard(m_aMutex); + if (rSource.Source == m_xInetConfiguration) + m_xInetConfiguration.clear(); + if (rSource.Source == m_xJavaConfiguration) + m_xJavaConfiguration.clear(); +} + +void SAL_CALL JavaVirtualMachine::elementInserted( + css::container::ContainerEvent const &) +{} + +void SAL_CALL JavaVirtualMachine::elementRemoved( + css::container::ContainerEvent const &) +{} + +// If a user changes the setting, for example for proxy settings, then this +// function will be called from the configuration manager. Even if the .xml +// file does not contain an entry yet and that entry has to be inserted, this +// function will be called. We call java.lang.System.setProperty for the new +// values. +void SAL_CALL JavaVirtualMachine::elementReplaced( + css::container::ContainerEvent const & rEvent) +{ + // TODO Using the new value stored in rEvent is wrong here. If two threads + // receive different elementReplaced calls in quick succession, it is + // unspecified which changes the JVM's system properties last. A correct + // solution must atomically (i.e., protected by a mutex) read the latest + // value from the configuration and set it as a system property at the JVM. + + OUString aAccessor; + rEvent.Accessor >>= aAccessor; + OUString aPropertyName; + OUString aPropertyValue; + bool bSecurityChanged = false; + if ( aAccessor == "ooInetProxyType" ) + { + // Proxy none, manually + sal_Int32 value = 0; + rEvent.Element >>= value; + setINetSettingsInVM(value != 0); + return; + } + else if ( aAccessor == "ooInetHTTPProxyName" ) + { + aPropertyName = "http.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetHTTPProxyPort" ) + { + aPropertyName = "http.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetHTTPSProxyName" ) + { + aPropertyName = "https.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetHTTPSProxyPort" ) + { + aPropertyName = "https.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetNoProxy" ) + { + aPropertyName = "http.nonProxyHosts"; + rEvent.Element >>= aPropertyValue; + aPropertyValue = aPropertyValue.replace(';', '|'); + } + else if ( aAccessor == "NetAccess" ) + { + aPropertyName = "appletviewer.security.mode"; + sal_Int32 n = 0; + if (rEvent.Element >>= n) + switch (n) + { + case 0: + aPropertyValue = "host"; + break; + case 1: + aPropertyValue = "unrestricted"; + break; + case 3: + aPropertyValue = "none"; + break; + } + else + return; + bSecurityChanged = true; + } + else if ( aAccessor == "Security" ) + { + aPropertyName = "stardiv.security.disableSecurity"; + bool b; + if (rEvent.Element >>= b) + if (b) + aPropertyValue = "false"; + else + aPropertyValue = "true"; + else + return; + bSecurityChanged = true; + } + else + return; + + rtl::Reference< jvmaccess::VirtualMachine > xVirtualMachine; + { + osl::MutexGuard aGuard(m_aMutex); + if (m_xUnoVirtualMachine.is()) { + xVirtualMachine = m_xUnoVirtualMachine->getVirtualMachine(); + } + } + if (!xVirtualMachine.is()) + return; + + try + { + jvmaccess::VirtualMachine::AttachGuard aAttachGuard( + xVirtualMachine); + JNIEnv * pJNIEnv = aAttachGuard.getEnvironment(); + + // call java.lang.System.setProperty + // String setProperty( String key, String value) + jclass jcSystem= pJNIEnv->FindClass("java/lang/System"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/lang/System", nullptr); + jmethodID jmSetProps= pJNIEnv->GetStaticMethodID( jcSystem, "setProperty","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.setProperty", nullptr); + + jstring jsPropName= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyName.getStr()), aPropertyName.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + + // remove the property if it does not have a value ( user left the dialog field empty) + // or if the port is set to 0 + aPropertyValue= aPropertyValue.trim(); + if( aPropertyValue.isEmpty() || + ((aPropertyName == "http.proxyPort" /*|| aPropertyName == "socksProxyPort"*/) && aPropertyValue == "0") + ) + { + // call java.lang.System.getProperties + jmethodID jmGetProps= pJNIEnv->GetStaticMethodID( jcSystem, "getProperties","()Ljava/util/Properties;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getProperties", nullptr); + jobject joProperties= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetProps); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.getProperties", nullptr); + // call java.util.Properties.remove + jclass jcProperties= pJNIEnv->FindClass("java/util/Properties"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/util/Properties", nullptr); + jmethodID jmRemove= pJNIEnv->GetMethodID( jcProperties, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.util.Properties.remove", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsPropName); + } + else + { + // Change the Value of the property + jstring jsPropValue= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyValue.getStr()), aPropertyValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsPropName, jsPropValue); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + + // If the settings for Security and NetAccess changed then we have to notify the SandboxSecurity + // SecurityManager + // call System.getSecurityManager() + if (bSecurityChanged) + { + jmethodID jmGetSecur= pJNIEnv->GetStaticMethodID( jcSystem,"getSecurityManager","()Ljava/lang/SecurityManager;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getSecurityManager", nullptr); + jobject joSecur= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetSecur); + if (joSecur != nullptr) + { + // Make sure the SecurityManager is our SandboxSecurity + // FindClass("com.sun.star.lib.sandbox.SandboxSecurityManager" only worked at the first time + // this code was executed. Maybe it is a security feature. However, all attempts to debug the + // SandboxSecurity class (maybe the VM invokes checkPackageAccess) failed. +// jclass jcSandboxSec= pJNIEnv->FindClass("com.sun.star.lib.sandbox.SandboxSecurity"); +// if(pJNIEnv->ExceptionOccurred()) throw RuntimeException("JNI:FindClass com.sun.star.lib.sandbox.SandboxSecurity"); +// jboolean bIsSand= pJNIEnv->IsInstanceOf( joSecur, jcSandboxSec); + // The SecurityManagers class Name must be com.sun.star.lib.sandbox.SandboxSecurity + jclass jcSec= pJNIEnv->GetObjectClass( joSecur); + jclass jcClass= pJNIEnv->FindClass("java/lang/Class"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java.lang.Class", nullptr); + jmethodID jmName= pJNIEnv->GetMethodID( jcClass,"getName","()Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.lang.Class.getName", nullptr); + jstring jsClass= static_cast<jstring>(pJNIEnv->CallObjectMethod( jcSec, jmName)); + const jchar* jcharName= pJNIEnv->GetStringChars( jsClass, nullptr); + OUString sName(reinterpret_cast<sal_Unicode const *>(jcharName)); + bool bIsSandbox; + bIsSandbox = sName == "com.sun.star.lib.sandbox.SandboxSecurity"; + pJNIEnv->ReleaseStringChars( jsClass, jcharName); + + if (bIsSandbox) + { + // call SandboxSecurity.reset + jmethodID jmReset= pJNIEnv->GetMethodID( jcSec,"reset","()V"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID com.sun.star.lib.sandbox.SandboxSecurity.reset", nullptr); + pJNIEnv->CallVoidMethod( joSecur, jmReset); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallVoidMethod com.sun.star.lib.sandbox.SandboxSecurity.reset", nullptr); + } + } + } + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException", + getXWeak(), anyEx ); + } +} + +JavaVirtualMachine::~JavaVirtualMachine() +{ + if (m_xInetConfiguration.is()) + // We should never get here, but just in case... + try + { + m_xInetConfiguration->removeContainerListener(this); + } + catch (css::uno::Exception &) + { + OSL_FAIL("com.sun.star.uno.Exception caught"); + } + if (m_xJavaConfiguration.is()) + // We should never get here, but just in case... + try + { + m_xJavaConfiguration->removeContainerListener(this); + } + catch (css::uno::Exception &) + { + OSL_FAIL("com.sun.star.uno.Exception caught"); + } +} + +void SAL_CALL JavaVirtualMachine::disposing() +{ + css::uno::Reference< css::container::XContainer > xContainer1; + css::uno::Reference< css::container::XContainer > xContainer2; + { + osl::MutexGuard aGuard(m_aMutex); + m_bDisposed = true; + xContainer1 = m_xInetConfiguration; + m_xInetConfiguration.clear(); + xContainer2 = m_xJavaConfiguration; + m_xJavaConfiguration.clear(); + } + if (xContainer1.is()) + xContainer1->removeContainerListener(this); + if (xContainer2.is()) + xContainer2->removeContainerListener(this); +} + +/*We listen to changes in the configuration. For example, the user changes the proxy + settings in the options dialog (menu tools). Then we are notified of this change and + if the java vm is already running we change the properties (System.lang.System.setProperties) + through JNI. + To receive notifications this class implements XContainerListener. +*/ +void JavaVirtualMachine::registerConfigChangesListener() +{ + try + { + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider( + m_xContext->getValueByName( + "/singletons/com.sun.star.configuration.theDefaultProvider"), + css::uno::UNO_QUERY); + + if (xConfigProvider.is()) + { + // We register this instance as listener to changes in org.openoffice.Inet/Settings + // arguments for ConfigurationAccess + css::uno::Sequence<css::uno::Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", css::uno::Any(OUString("org.openoffice.Inet/Settings"))}, + {"depth", css::uno::Any(sal_Int32(-1))} + })); + m_xInetConfiguration.set( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments), + css::uno::UNO_QUERY); + + if (m_xInetConfiguration.is()) + m_xInetConfiguration->addContainerListener(this); + + // now register as listener to changes in org.openoffice.Java/VirtualMachine + css::uno::Sequence<css::uno::Any> aArguments2(comphelper::InitAnyPropertySequence( + { + {"nodepath", css::uno::Any(OUString("org.openoffice.Office.Java/VirtualMachine"))}, + {"depth", css::uno::Any(sal_Int32(-1))} // depth: -1 means unlimited + })); + m_xJavaConfiguration.set( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments2), + css::uno::UNO_QUERY); + + if (m_xJavaConfiguration.is()) + m_xJavaConfiguration->addContainerListener(this); + } + }catch(const css::uno::Exception & e) + { + SAL_INFO("stoc", "could not set up listener for Configuration because of >" << e << "<"); + } +} + +// param true: all Inet setting are set as Java Properties on a live VM. +// false: the Java net properties are set to empty value. +void JavaVirtualMachine::setINetSettingsInVM(bool set_reset) +{ + osl::MutexGuard aGuard(m_aMutex); + try + { + if (m_xUnoVirtualMachine.is()) + { + jvmaccess::VirtualMachine::AttachGuard aAttachGuard( + m_xUnoVirtualMachine->getVirtualMachine()); + JNIEnv * pJNIEnv = aAttachGuard.getEnvironment(); + + // The Java Properties + OUString sHttpProxyHost("http.proxyHost"); + OUString sHttpProxyPort("http.proxyPort"); + OUString sHttpNonProxyHosts("http.nonProxyHosts"); + + // create Java Properties as JNI strings + jstring jsHttpProxyHost= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpProxyHost.getStr()), sHttpProxyHost.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpProxyPort= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpProxyPort.getStr()), sHttpProxyPort.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpNonProxyHosts= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpNonProxyHosts.getStr()), sHttpNonProxyHosts.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + + // prepare java.lang.System.setProperty + jclass jcSystem= pJNIEnv->FindClass("java/lang/System"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/lang/System", nullptr); + jmethodID jmSetProps= pJNIEnv->GetStaticMethodID( jcSystem, "setProperty","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.setProperty", nullptr); + + // call java.lang.System.getProperties + jmethodID jmGetProps= pJNIEnv->GetStaticMethodID( jcSystem, "getProperties","()Ljava/util/Properties;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getProperties", nullptr); + jobject joProperties= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetProps); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.getProperties", nullptr); + // prepare java.util.Properties.remove + jclass jcProperties= pJNIEnv->FindClass("java/util/Properties"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/util/Properties", nullptr); + + if (set_reset) + { + // Set all network properties with the VM + JVM jvm; + getINetPropsFromConfig( &jvm, m_xContext->getServiceManager(), m_xContext); + const ::std::vector< OUString> & Props = jvm.getProperties(); + + for( auto& prop : Props) + { + sal_Int32 index= prop.indexOf( '='); + std::u16string_view propName= prop.subView( 0, index); + OUString propValue= prop.copy( index + 1); + + if (propName == sHttpProxyHost) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpProxyHost, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpProxyPort) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpProxyPort, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpNonProxyHosts) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpNonProxyHosts, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + } + } + else + { + // call java.util.Properties.remove + jmethodID jmRemove= pJNIEnv->GetMethodID( jcProperties, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.util.Property.remove", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpProxyHost); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpProxyPort); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpNonProxyHosts); + } + } + } + catch (css::uno::RuntimeException &) + { + OSL_FAIL("RuntimeException"); + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + OSL_FAIL("jvmaccess::VirtualMachine::AttachGuard::CreationException"); + } +} + +void JavaVirtualMachine::setUpUnoVirtualMachine(JNIEnv * environment) { + css::uno::Reference< css::util::XMacroExpander > exp = css::util::theMacroExpander::get(m_xContext); + OUString baseUrl; + try { + baseUrl = exp->expandMacros("$URE_INTERNAL_JAVA_DIR/"); + } catch (css::lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "css::lang::IllegalArgumentException", + getXWeak(), anyEx ); + } + OUString classPath; + try { + classPath = exp->expandMacros("$URE_INTERNAL_JAVA_CLASSPATH"); + } catch (css::lang::IllegalArgumentException &) {} + jclass class_URLClassLoader = environment->FindClass( + "java/net/URLClassLoader"); + if (class_URLClassLoader == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URLClassLoader = environment->GetMethodID( + class_URLClassLoader, "<init>", "([Ljava/net/URL;)V"); + if (ctor_URLClassLoader == nullptr) { + handleJniException(environment); + } + jclass class_URL = environment->FindClass("java/net/URL"); + if (class_URL == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URL_1 = environment->GetMethodID( + class_URL, "<init>", "(Ljava/lang/String;)V"); + if (ctor_URL_1 == nullptr) { + handleJniException(environment); + } + jvalue args[3]; + args[0].l = environment->NewString( + reinterpret_cast< jchar const * >(baseUrl.getStr()), + static_cast< jsize >(baseUrl.getLength())); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jobject base = environment->NewObjectA(class_URL, ctor_URL_1, args); + if (base == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URL_2 = environment->GetMethodID( + class_URL, "<init>", "(Ljava/net/URL;Ljava/lang/String;)V"); + if (ctor_URL_2 == nullptr) { + handleJniException(environment); + } + jobjectArray classpath = jvmaccess::ClassPath::translateToUrls( + m_xContext, environment, classPath); + if (classpath == nullptr) { + handleJniException(environment); + } + args[0].l = base; + args[1].l = environment->NewStringUTF("unoloader.jar"); + if (args[1].l == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewObjectA(class_URL, ctor_URL_2, args); + if (args[0].l == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewObjectArray(1, class_URL, args[0].l); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jobject cl1 = environment->NewObjectA( + class_URLClassLoader, ctor_URLClassLoader, args); + if (cl1 == nullptr) { + handleJniException(environment); + } + jmethodID method_loadClass = environment->GetMethodID( + class_URLClassLoader, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + if (method_loadClass == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewStringUTF( + "com.sun.star.lib.unoloader.UnoClassLoader"); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jclass class_UnoClassLoader = static_cast< jclass >( + environment->CallObjectMethodA(cl1, method_loadClass, args)); + if (class_UnoClassLoader == nullptr) { + handleJniException(environment); + } + jmethodID ctor_UnoClassLoader = environment->GetMethodID( + class_UnoClassLoader, "<init>", + "(Ljava/net/URL;[Ljava/net/URL;Ljava/lang/ClassLoader;)V"); + if (ctor_UnoClassLoader == nullptr) { + handleJniException(environment); + } + args[0].l = base; + args[1].l = classpath; + args[2].l = cl1; + jobject cl2 = environment->NewObjectA( + class_UnoClassLoader, ctor_UnoClassLoader, args); + if (cl2 == nullptr) { + handleJniException(environment); + } + try { + m_xUnoVirtualMachine = new jvmaccess::UnoVirtualMachine( + m_xVirtualMachine, cl2); + } catch (jvmaccess::UnoVirtualMachine::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::UnoVirtualMachine::CreationException", + getXWeak(), anyEx ); + } +} + +void JavaVirtualMachine::handleJniException(JNIEnv * environment) { +#if defined DBG_UTIL + environment->ExceptionDescribe(); +#else + environment->ExceptionClear(); +#endif + throw css::uno::RuntimeException( + "JNI exception occurred", + getXWeak()); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_JavaVM_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new JavaVirtualMachine(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/javavm.hxx b/stoc/source/javavm/javavm.hxx new file mode 100644 index 0000000000..b4f79d1f9f --- /dev/null +++ b/stoc/source/javavm/javavm.hxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/java/XJavaThreadRegister_11.hpp> +#include <com/sun/star/java/XJavaVM.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <osl/thread.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace container { class XContainer; } + namespace uno { class XComponentContext; } +} +namespace jvmaccess { + class UnoVirtualMachine; + class VirtualMachine; +} + +namespace stoc_javavm { + +class JavaVirtualMachine: + private cppu::BaseMutex, + public cppu::WeakComponentImplHelper< + css::lang::XInitialization, css::lang::XServiceInfo, css::java::XJavaVM, + css::java::XJavaThreadRegister_11, css::container::XContainerListener> +{ +public: + explicit JavaVirtualMachine( + css::uno::Reference< + css::uno::XComponentContext > xContext); + + // XInitialization + virtual void SAL_CALL + initialize(css::uno::Sequence< css::uno::Any > const & + rArguments) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL + supportsService(OUString const & rServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + // XJavaVM + virtual css::uno::Any SAL_CALL + getJavaVM(css::uno::Sequence< sal_Int8 > const & rProcessId) override; + + virtual sal_Bool SAL_CALL isVMStarted() override; + + virtual sal_Bool SAL_CALL isVMEnabled() override; + + // XJavaThreadRegister_11 + virtual sal_Bool SAL_CALL isThreadAttached() override; + + virtual void SAL_CALL registerThread() override; + + virtual void SAL_CALL revokeThread() override; + + // XContainerListener + virtual void SAL_CALL + disposing(css::lang::EventObject const & rSource) override; + + virtual void SAL_CALL + elementInserted(css::container::ContainerEvent const & rEvent) override; + + virtual void SAL_CALL + elementRemoved(css::container::ContainerEvent const & rEvent) override; + + virtual void SAL_CALL + elementReplaced(css::container::ContainerEvent const & rEvent) override; + +private: + JavaVirtualMachine(JavaVirtualMachine const &) = delete; + void operator =(const JavaVirtualMachine&) = delete; + + virtual ~JavaVirtualMachine() override; + + virtual void SAL_CALL disposing() override; + + void registerConfigChangesListener(); + + void setINetSettingsInVM(bool set_reset); + + void setUpUnoVirtualMachine(JNIEnv * environment); + + void handleJniException(JNIEnv * environment); + + css::uno::Reference< css::uno::XComponentContext > + m_xContext; + + // the following are controlled by BaseMutex::m_aMutex: + bool m_bDisposed; + rtl::Reference< jvmaccess::VirtualMachine > m_xVirtualMachine; + rtl::Reference< jvmaccess::UnoVirtualMachine > m_xUnoVirtualMachine; + JavaVM * m_pJavaVm; + // If the first creation of Java failed and this flag is set then the + // next call to getJavaVM throws a RuntimException. This is useful when + // the second attempt to create Java might cause a crash. + css::uno::Reference< css::container::XContainer > + m_xInetConfiguration; + css::uno::Reference< css::container::XContainer > + m_xJavaConfiguration; // for Java settings + + osl::ThreadData m_aAttachGuards; +}; + +} + +#endif // INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/jvmargs.cxx b/stoc/source/javavm/jvmargs.cxx new file mode 100644 index 0000000000..f98f22ec0c --- /dev/null +++ b/stoc/source/javavm/jvmargs.cxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "jvmargs.hxx" +#include <rtl/ustring.hxx> + +namespace stoc_javavm +{ +JVM::JVM() noexcept //: _enabled(sal_False) +{ +} + +void JVM::pushProp(const OUString& property) { _props.push_back(property); } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/jvmargs.hxx b/stoc/source/javavm/jvmargs.hxx new file mode 100644 index 0000000000..adc41ffa15 --- /dev/null +++ b/stoc/source/javavm/jvmargs.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_JVMARGS_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_JVMARGS_HXX + + +#include <vector> +#include <rtl/ustring.hxx> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +extern "C" { + typedef jint JNICALL JNI_InitArgs_Type(void *); + typedef jint JNICALL JNI_CreateVM_Type(JavaVM **, JNIEnv **, void *); + +} + +namespace stoc_javavm { + + class JVM { + ::std::vector<OUString> _props; + + public: + JVM() noexcept; + + void pushProp(const OUString & uString); + const ::std::vector< OUString> & getProperties() const { return _props;} + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |