diff options
Diffstat (limited to 'jvmfwk/source/framework.cxx')
-rw-r--r-- | jvmfwk/source/framework.cxx | 749 |
1 files changed, 749 insertions, 0 deletions
diff --git a/jvmfwk/source/framework.cxx b/jvmfwk/source/framework.cxx new file mode 100644 index 000000000..8aa85082b --- /dev/null +++ b/jvmfwk/source/framework.cxx @@ -0,0 +1,749 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <cassert> +#include <memory> + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <osl/diagnose.h> +#ifdef _WIN32 +#include <osl/file.hxx> +#include <osl/process.h> +#endif +#include <osl/thread.hxx> +#include <jvmfwk/framework.hxx> +#include <vendorbase.hxx> +#include <vendorplugin.hxx> +#include <vector> +#include <algorithm> +#include "framework.hxx" +#include <fwkutil.hxx> +#include <elements.hxx> +#include <fwkbase.hxx> + +namespace { + +bool g_bEnabledSwitchedOn = false; + +JavaVM * g_pJavaVM = nullptr; + +bool areEqualJavaInfo( + JavaInfo const * pInfoA,JavaInfo const * pInfoB) +{ + return jfw_areEqualJavaInfo(pInfoA, pInfoB); +} + +} + +javaFrameworkError jfw_findAllJREs(std::vector<std::unique_ptr<JavaInfo>> *pparInfo) +{ + assert(pparInfo != nullptr); + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + + jfw::VendorSettings aVendorSettings; + std::vector<std::unique_ptr<JavaInfo>> vecInfo; + + //Use all plug-in libraries to get Java installations. + std::vector<std::unique_ptr<JavaInfo>> arInfos; + std::vector<rtl::Reference<jfw_plugin::VendorBase>> infos; + javaPluginError plerr = jfw_plugin_getAllJavaInfos( + true, + aVendorSettings, + & arInfos, + infos); + + if (plerr != javaPluginError::NONE) + return JFW_E_ERROR; + + for (auto & j: arInfos) + vecInfo.push_back(std::move(j)); + + // direct mode disregards Java settings, so only retrieve + // JREs from settings when application mode is used + if (jfw::getMode() == jfw::JFW_MODE_APPLICATION) + { + //get the list of paths to jre locations which have been + //added manually + const jfw::MergedSettings settings; + const std::vector<OUString> vecJRELocations = + settings.getJRELocations(); + //Check if any plugin can detect JREs at the location + // of the paths added by jfw_addJRELocation + //Check every manually added location + for (auto const & ii: vecJRELocations) + { + std::unique_ptr<JavaInfo> aInfo; + plerr = jfw_plugin_getJavaInfoByPath( + ii, + aVendorSettings, + &aInfo); + if (plerr == javaPluginError::NoJre) + continue; + if (plerr == javaPluginError::FailedVersion) + continue; + if (plerr == javaPluginError::WrongArch) + continue; + else if (plerr != javaPluginError::NONE) + return JFW_E_ERROR; + + // Was this JRE already added? + if (std::none_of( + vecInfo.begin(), vecInfo.end(), + [&aInfo](std::unique_ptr<JavaInfo> const & info) { + return areEqualJavaInfo( + info.get(), aInfo.get()); + })) + { + vecInfo.push_back(std::move(aInfo)); + } + } + } + + *pparInfo = std::move(vecInfo); + + return JFW_E_NONE; + } + catch (const jfw::FrameworkException& e) + { + SAL_WARN( "jfw", e.message); + return e.errorCode; + } +} + +javaFrameworkError jfw_startVM( + JavaInfo const * pInfo, std::vector<OUString> const & arOptions, + JavaVM ** ppVM, JNIEnv ** ppEnv) +{ + assert(ppVM != nullptr); + javaFrameworkError errcode = JFW_E_NONE; + + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + + //We keep this pointer so we can determine if a VM has already + //been created. + if (g_pJavaVM != nullptr) + return JFW_E_RUNNING_JVM; + + std::vector<OString> vmParams; + OString sUserClassPath; + std::unique_ptr<JavaInfo> aInfo; + if (pInfo == nullptr) + { + jfw::JFW_MODE mode = jfw::getMode(); + if (mode == jfw::JFW_MODE_APPLICATION) + { + const jfw::MergedSettings settings; + if (!settings.getEnabled()) + return JFW_E_JAVA_DISABLED; + aInfo = settings.createJavaInfo(); + //check if a Java has ever been selected + if (!aInfo) + return JFW_E_NO_SELECT; + + //check if the javavendors.xml has changed after a Java was selected + OString sVendorUpdate = jfw::getElementUpdated(); + + if (sVendorUpdate != settings.getJavaInfoAttrVendorUpdate()) + return JFW_E_INVALID_SETTINGS; + + //check if JAVA is disabled + //If Java is enabled, but it was disabled when this process was started + // then no preparational work, such as setting the LD_LIBRARY_PATH, was + //done. Therefore if a JRE needs it, it must not be started. + if (g_bEnabledSwitchedOn && + (aInfo->nRequirements & JFW_REQUIRE_NEEDRESTART)) + return JFW_E_NEED_RESTART; + + //Check if the selected Java was set in this process. If so it + //must not have the requirements flag JFW_REQUIRE_NEEDRESTART + if ((aInfo->nRequirements & JFW_REQUIRE_NEEDRESTART) + && jfw::wasJavaSelectedInSameProcess()) + return JFW_E_NEED_RESTART; + + vmParams = settings.getVmParametersUtf8(); + sUserClassPath = jfw::makeClassPathOption(settings.getUserClassPath()); + } // end mode FWK_MODE_OFFICE + else if (mode == jfw::JFW_MODE_DIRECT) + { + errcode = jfw_getSelectedJRE(&aInfo); + if (errcode != JFW_E_NONE) + return errcode; + //In direct mode the options are specified by bootstrap variables + //of the form UNO_JAVA_JFW_PARAMETER_1 .. UNO_JAVA_JFW_PARAMETER_n + vmParams = jfw::BootParams::getVMParameters(); + auto const cp = jfw::BootParams::getClasspath(); + if (!cp.isEmpty()) + { + sUserClassPath = + "-Djava.class.path=" + cp; + } + } + else + OSL_ASSERT(false); + pInfo = aInfo.get(); + } + assert(pInfo != nullptr); + +#ifdef _WIN32 + // Alternative JREs (AdoptOpenJDK, Azul Zulu) are missing the bin/ folder in + // java.library.path. Somehow setting java.library.path accordingly doesn't work, + // but the PATH gets picked up, so add it there. + // Without this hack, some features don't work in alternative JREs. + OUString sPATH; + osl_getEnvironment(OUString("PATH").pData, &sPATH.pData); + OUString sJRELocation; + osl::FileBase::getSystemPathFromFileURL(pInfo->sLocation + "/bin", sJRELocation); + if (sPATH.isEmpty()) + sPATH = sJRELocation; + else + sPATH = sJRELocation + OUStringChar(SAL_PATHSEPARATOR) + sPATH; + osl_setEnvironment(OUString("PATH").pData, sPATH.pData); +#endif // _WIN32 + + // create JavaVMOptions array that is passed to the plugin + // it contains the classpath and all options set in the + //options dialog + std::unique_ptr<JavaVMOption[]> sarJOptions( + new JavaVMOption[ + arOptions.size() + (sUserClassPath.isEmpty() ? 1 : 2) + vmParams.size()]); + JavaVMOption * arOpt = sarJOptions.get(); + if (! arOpt) + return JFW_E_ERROR; + + //The first argument is the classpath + int index = 0; + if (!sUserClassPath.isEmpty()) { + arOpt[index].optionString= const_cast<char*>(sUserClassPath.getStr()); + arOpt[index].extraInfo = nullptr; + ++index; + } + // Set a flag that this JVM has been created via the JNI Invocation API + // (used, for example, by UNO remote bridges to share a common thread pool + // factory among Java and native bridge implementations): + arOpt[index].optionString = const_cast<char *>("-Dorg.openoffice.native="); + arOpt[index].extraInfo = nullptr; + ++index; + + //add the options set by options dialog + for (auto const & vmParam : vmParams) + { + arOpt[index].optionString = const_cast<char*>(vmParam.getStr()); + arOpt[index].extraInfo = nullptr; + index ++; + } + //add all options of the arOptions argument + std::vector<OString> convertedOptions; + for (auto const & ii: arOptions) + { + OString conv = OUStringToOString(ii, osl_getThreadTextEncoding()); + convertedOptions.push_back(conv); + // keep conv.getStr() alive until after the call to + // jfw_plugin_startJavaVirtualMachine below + arOpt[index].optionString = const_cast<char *>(conv.getStr()); + arOpt[index].extraInfo = nullptr; + index++; + } + + //start Java + JavaVM *pVm = nullptr; + SAL_INFO("jfw", "Starting Java"); + javaPluginError plerr = jfw_plugin_startJavaVirtualMachine(pInfo, arOpt, index, & pVm, ppEnv); + if (plerr == javaPluginError::VmCreationFailed) + { + errcode = JFW_E_VM_CREATION_FAILED; + } + else if (plerr != javaPluginError::NONE ) + { + errcode = JFW_E_ERROR; + } + else + { + g_pJavaVM = pVm; + *ppVM = pVm; + } + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message); + } + + return errcode; +} + +/** We do not use here jfw_findAllJREs and then check if a JavaInfo + meets the requirements, because that means using all plug-ins, which + may take quite a while. The implementation first inspects JAVA_HOME and + PATH environment variables. If no suitable JavaInfo is found there, it + inspects all JavaInfos found by the jfw_plugin_get* functions. + */ +javaFrameworkError jfw_findAndSelectJRE(std::unique_ptr<JavaInfo> *pInfo) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + std::unique_ptr<JavaInfo> aCurrentInfo; + + + // 'bInfoFound' indicates whether a Java installation has been found + bool bInfoFound = false; + + // get list of vendors for Java installations + jfw::VendorSettings aVendorSettings; + + std::vector<rtl::Reference<jfw_plugin::VendorBase>> infos; + + // first inspect Java installation that the JAVA_HOME + // environment variable points to (if it is set) + if (jfw_plugin_getJavaInfoFromJavaHome( + aVendorSettings, &aCurrentInfo, infos) + == javaPluginError::NONE) + { + bInfoFound = true; + } + + // if no Java installation was detected by using JAVA_HOME, + // query PATH for Java installations + if (!bInfoFound) + { + std::vector<std::unique_ptr<JavaInfo>> vecJavaInfosFromPath; + if (jfw_plugin_getJavaInfosFromPath( + aVendorSettings, vecJavaInfosFromPath, infos) + == javaPluginError::NONE) + { + assert(!vecJavaInfosFromPath.empty()); + aCurrentInfo = std::move(vecJavaInfosFromPath[0]); + bInfoFound = true; + } + } + + + // if no suitable Java installation has been found yet: + // first use jfw_plugin_getAllJavaInfos to find a suitable Java installation, + // then try paths that have been added manually + if (!bInfoFound) + { + //get all installations + std::vector<std::unique_ptr<JavaInfo>> arInfos; + javaPluginError plerr = jfw_plugin_getAllJavaInfos( + false, + aVendorSettings, + & arInfos, + infos); + + if (plerr == javaPluginError::NONE && !arInfos.empty()) + { + aCurrentInfo = std::move(arInfos[0]); + } + + if (!aCurrentInfo) + {//The plug-ins did not find a suitable Java. Now try the paths which have been + //added manually. + //get the list of paths to jre locations which have been added manually + const jfw::MergedSettings settings; + //node.loadFromSettings(); + const std::vector<OUString> & vecJRELocations = + settings.getJRELocations(); + //use all plug-ins to determine the JavaInfo objects + for (auto const & JRELocation : vecJRELocations) + { + std::unique_ptr<JavaInfo> aInfo; + javaPluginError err = jfw_plugin_getJavaInfoByPath( + JRELocation, + aVendorSettings, + &aInfo); + if (err == javaPluginError::NoJre) + continue; + if (err == javaPluginError::FailedVersion) + continue; + else if (err !=javaPluginError::NONE) + return JFW_E_ERROR; + + if (aInfo) + { + aCurrentInfo = std::move(aInfo); + break; + } + }//end iterate over paths + } + } + if (aCurrentInfo) + { + jfw::NodeJava javaNode(jfw::NodeJava::USER); + javaNode.setJavaInfo(aCurrentInfo.get(),true); + javaNode.write(); + //remember that this JRE was selected in this process + jfw::setJavaSelected(); + + if (pInfo !=nullptr) + { + *pInfo = std::move(aCurrentInfo); + } + } + else + { + errcode = JFW_E_NO_JAVA_FOUND; + } + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + + return errcode; +} + +bool jfw_areEqualJavaInfo(JavaInfo const * pInfoA,JavaInfo const * pInfoB) +{ + if (pInfoA == pInfoB) + return true; + if (pInfoA == nullptr || pInfoB == nullptr) + return false; + if (pInfoA->sVendor == pInfoB->sVendor + && pInfoA->sLocation == pInfoB->sLocation + && pInfoA->sVersion == pInfoB->sVersion + && pInfoA->nRequirements == pInfoB->nRequirements + && pInfoA->arVendorData == pInfoB->arVendorData) + { + return true; + } + return false; +} + +javaFrameworkError jfw_getSelectedJRE(std::unique_ptr<JavaInfo> *ppInfo) +{ + assert(ppInfo != nullptr); + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + { + if ((errcode = jfw_getJavaInfoByPath( + jfw::BootParams::getJREHome(), ppInfo)) + != JFW_E_NONE) + throw jfw::FrameworkException( + JFW_E_CONFIGURATION, + "[Java framework] The JRE specified by the bootstrap " + "variable UNO_JAVA_JFW_JREHOME or UNO_JAVA_JFW_ENV_JREHOME " + " could not be recognized. Check the values and make sure that you " + "use a plug-in library that can recognize that JRE."); + + return JFW_E_NONE; + } + + const jfw::MergedSettings settings; + *ppInfo = settings.createJavaInfo(); + if (!*ppInfo) + { + return JFW_E_NONE; + } + //If the javavendors.xml has changed, then the current selected + //Java is not valid anymore + // /java/javaInfo/@vendorUpdate != javaSelection/updated (javavendors.xml) + OString sUpdated = jfw::getElementUpdated(); + + if (sUpdated != settings.getJavaInfoAttrVendorUpdate()) + { + ppInfo->reset(); + return JFW_E_INVALID_SETTINGS; + } + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} + +bool jfw_isVMRunning() +{ + osl::MutexGuard guard(jfw::FwkMutex()); + return g_pJavaVM != nullptr; +} + +javaFrameworkError jfw_getJavaInfoByPath(OUString const & pPath, std::unique_ptr<JavaInfo> *ppInfo) +{ + assert(ppInfo != nullptr); + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + + jfw::VendorSettings aVendorSettings; + + //ask all plugins if this is a JRE. + //If so check if it meets the version requirements. + //Only if it does return a JavaInfo + javaPluginError plerr = jfw_plugin_getJavaInfoByPath( + pPath, + aVendorSettings, + ppInfo); + + if(plerr == javaPluginError::FailedVersion) + {//found JRE but it has the wrong version + ppInfo->reset(); + errcode = JFW_E_FAILED_VERSION; + } + OSL_ASSERT(plerr == javaPluginError::NONE || plerr == javaPluginError::NoJre); + if (!*ppInfo && errcode != JFW_E_FAILED_VERSION) + errcode = JFW_E_NOT_RECOGNIZED; + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + + return errcode; +} + + +javaFrameworkError jfw_setSelectedJRE(JavaInfo const *pInfo) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + //check if pInfo is the selected JRE + std::unique_ptr<JavaInfo> currentInfo; + errcode = jfw_getSelectedJRE( & currentInfo); + if (errcode != JFW_E_NONE && errcode != JFW_E_INVALID_SETTINGS) + return errcode; + + if (!jfw_areEqualJavaInfo(currentInfo.get(), pInfo)) + { + jfw::NodeJava node(jfw::NodeJava::USER); + node.setJavaInfo(pInfo, false); + node.write(); + //remember that the JRE was selected in this process + jfw::setJavaSelected(); + } + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} +javaFrameworkError jfw_setEnabled(bool bEnabled) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + + if (!g_bEnabledSwitchedOn && bEnabled) + { + //When the process started then Enabled was false. + //This is first time enabled is set to true. + //That means, no preparational work has been done, such as setting the + //LD_LIBRARY_PATH, etc. + + //check if Enabled is false; + const jfw::MergedSettings settings; + if (!settings.getEnabled()) + g_bEnabledSwitchedOn = true; + } + jfw::NodeJava node(jfw::NodeJava::USER); + node.setEnabled(bEnabled); + node.write(); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} + +javaFrameworkError jfw_getEnabled(bool *pbEnabled) +{ + assert(pbEnabled != nullptr); + javaFrameworkError errcode = JFW_E_NONE; + try + { + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + osl::MutexGuard guard(jfw::FwkMutex()); + jfw::MergedSettings settings; + *pbEnabled = settings.getEnabled(); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} + + +javaFrameworkError jfw_setVMParameters(std::vector<OUString> const & arOptions) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + jfw::NodeJava node(jfw::NodeJava::USER); + node.setVmParameters(arOptions); + node.write(); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + + return errcode; +} + +javaFrameworkError jfw_getVMParameters(std::vector<OUString> * parOptions) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + + const jfw::MergedSettings settings; + settings.getVmParametersArray(parOptions); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} + +javaFrameworkError jfw_setUserClassPath(OUString const & pCp) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + jfw::NodeJava node(jfw::NodeJava::USER); + node.setUserClassPath(pCp); + node.write(); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} + +javaFrameworkError jfw_getUserClassPath(OUString * ppCP) +{ + assert(ppCP != nullptr); + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + const jfw::MergedSettings settings; + *ppCP = settings.getUserClassPath(); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + return errcode; +} + +javaFrameworkError jfw_addJRELocation(OUString const & sLocation) +{ + javaFrameworkError errcode = JFW_E_NONE; + try + { + osl::MutexGuard guard(jfw::FwkMutex()); + if (jfw::getMode() == jfw::JFW_MODE_DIRECT) + return JFW_E_DIRECT_MODE; + jfw::NodeJava node(jfw::NodeJava::USER); + node.load(); + node.addJRELocation(sLocation); + node.write(); + } + catch (const jfw::FrameworkException& e) + { + errcode = e.errorCode; + SAL_WARN( "jfw", e.message ); + } + + return errcode; + +} + +javaFrameworkError jfw_existJRE(const JavaInfo *pInfo, bool *exist) +{ + javaPluginError plerr = jfw_plugin_existJRE(pInfo, exist); + + javaFrameworkError ret = JFW_E_NONE; + switch (plerr) + { + case javaPluginError::NONE: + ret = JFW_E_NONE; + break; + case javaPluginError::Error: + ret = JFW_E_ERROR; + break; + default: + ret = JFW_E_ERROR; + } + return ret; +} + +void jfw_lock() +{ + jfw::FwkMutex().acquire(); +} + +void jfw_unlock() +{ + jfw::FwkMutex().release(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |