diff options
Diffstat (limited to '')
51 files changed, 8267 insertions, 0 deletions
diff --git a/jvmfwk/CppunitTest_jvmfwk_sunversion.mk b/jvmfwk/CppunitTest_jvmfwk_sunversion.mk new file mode 100644 index 000000000..8aa6a2c68 --- /dev/null +++ b/jvmfwk/CppunitTest_jvmfwk_sunversion.mk @@ -0,0 +1,40 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*- +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,jvmfwk_sunversion)) + +$(eval $(call gb_CppunitTest_add_exception_objects,jvmfwk_sunversion, \ + jvmfwk/qa/unit/sunversion \ +)) + +$(eval $(call gb_CppunitTest_set_include,jvmfwk_sunversion, \ + $$(INCLUDE) \ + -I$(SRCDIR) \ +)) + +$(eval $(call gb_CppunitTest_use_externals,jvmfwk_sunversion, \ + libxml2 \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,jvmfwk_sunversion, \ + sal \ + salhelper \ +)) + +$(eval $(call gb_CppunitTest_use_library_objects,jvmfwk_sunversion, \ + jvmfwk \ +)) + +ifeq ($(OS),MACOSX) +$(eval $(call gb_CppunitTest_use_system_darwin_frameworks,jvmfwk_sunversion, \ + Foundation \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/jvmfwk/CustomTarget_jreproperties.mk b/jvmfwk/CustomTarget_jreproperties.mk new file mode 100644 index 000000000..da509dbff --- /dev/null +++ b/jvmfwk/CustomTarget_jreproperties.mk @@ -0,0 +1,23 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_CustomTarget_CustomTarget,jvmfwk/jreproperties)) + +$(call gb_CustomTarget_get_target,jvmfwk/jreproperties) : $(call gb_CustomTarget_get_workdir,jvmfwk/jreproperties)/JREProperties.class + +$(call gb_CustomTarget_get_workdir,jvmfwk/jreproperties)/JREProperties.class : \ + $(SRCDIR)/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java \ + | $(call gb_CustomTarget_get_workdir,jvmfwk/jreproperties)/.dir + $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),JCS,1) + $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),JCS) + $(call gb_Helper_abbreviate_dirs, \ + cd $(dir $@) && $(call gb_JavaClassSet_JAVACCOMMAND,$(JAVA_TARGET_VER)) $(gb_JavaClassSet_JAVACDEBUG) -d $(dir $@) $^) + $(call gb_Trace_EndRange,$(subst $(WORKDIR)/,,$@),JCS) + +# vim:set shiftwidth=4 tabstop=4 noexpandtab: diff --git a/jvmfwk/CustomTarget_jvmfwk_jvmfwk3_ini.mk b/jvmfwk/CustomTarget_jvmfwk_jvmfwk3_ini.mk new file mode 100644 index 000000000..bd9621fd2 --- /dev/null +++ b/jvmfwk/CustomTarget_jvmfwk_jvmfwk3_ini.mk @@ -0,0 +1,38 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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 . +# + +$(eval $(call gb_CustomTarget_CustomTarget,jvmfwk/jvmfwk3_ini)) + +$(eval $(call gb_CustomTarget_register_targets,jvmfwk/jvmfwk3_ini, \ + $(call gb_Helper_get_rcfile,jvmfwk3) \ +)) + +$(call gb_CustomTarget_get_workdir,jvmfwk/jvmfwk3_ini)/$(call gb_Helper_get_rcfile,jvmfwk3): \ + $(SRCDIR)/jvmfwk/CustomTarget_jvmfwk_jvmfwk3_ini.mk + $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),ECH,1) + $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),ECH) + ( printf '[Bootstrap]\n' && \ + printf 'UNO_JAVA_JFW_VENDOR_SETTINGS=$(if $(filter MACOSX,$(OS)),$${ORIGIN}/../share/misc/,$${ORIGIN}/)javavendors.xml\n' && \ + printf 'UNO_JAVA_JFW_SHARED_DATA=$${URE_OVERRIDE_JAVA_JFW_SHARED_DATA}\n' && \ + printf 'UNO_JAVA_JFW_USER_DATA=$${URE_OVERRIDE_JAVA_JFW_USER_DATA}\n' && \ + printf 'UNO_JAVA_JFW_CLASSPATH_URLS=$${URE_MORE_JAVA_CLASSPATH_URLS}\n' \ + ) > $@ + $(call gb_Trace_EndRange,$(subst $(WORKDIR)/,,$@),ECH) + +# vim: set noet sw=4 ts=4: diff --git a/jvmfwk/Executable_javaldx.mk b/jvmfwk/Executable_javaldx.mk new file mode 100644 index 000000000..ab5ae8abe --- /dev/null +++ b/jvmfwk/Executable_javaldx.mk @@ -0,0 +1,21 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Executable_Executable,javaldx)) + +$(eval $(call gb_Executable_use_libraries,javaldx,\ + jvmfwk \ + sal \ +)) + +$(eval $(call gb_Executable_add_exception_objects,javaldx,\ + jvmfwk/plugins/sunmajor/javaenvsetup/javaldx \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/jvmfwk/IwyuFilter_jvmfwk.yaml b/jvmfwk/IwyuFilter_jvmfwk.yaml new file mode 100644 index 000000000..bfca82b3d --- /dev/null +++ b/jvmfwk/IwyuFilter_jvmfwk.yaml @@ -0,0 +1,36 @@ +--- +assumeFilename: jvmfwk/source/fwkbase.cxx +excludelist: + jvmfwk/inc/fwkutil.hxx: + # Needed on MACOSX + - config_folders.h + # Don't replace with URE impl. detail + - rtl/byteseq.hxx + jvmfwk/inc/vendorbase.hxx: + # OSL_BIGENDIAN is being checked + - osl/endian.h + jvmfwk/source/framework.cxx: + # Don't replace with URE impl. detail + - osl/thread.hxx + jvmfwk/source/fwkbase.cxx: + # Don't replace with URE impl. detail + - osl/thread.hxx + jvmfwk/source/fwkutil.cxx: + # Needed on WIN32 + - sal/log.hxx + jvmfwk/plugins/sunmajor/javaenvsetup/javaldx.cxx: + # Don't replace with URE impl. detail + - rtl/byteseq.hxx + jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx: + # Needed on WIN32 + - o3tl/char16_t2wchar_t.hxx + - string_view + # Don't replace with URE impl. detail + - osl/thread.hxx + # Don't replace with bits/types, needed for sig_atomic_t + - signal.h + jvmfwk/plugins/sunmajor/pluginlib/util.cxx: + # Needed on MACOSX + - config_folders.h + # Don't replace with URE impl. detail + - osl/module.hxx diff --git a/jvmfwk/Library_jvmfwk.mk b/jvmfwk/Library_jvmfwk.mk new file mode 100644 index 000000000..64a6b9d8e --- /dev/null +++ b/jvmfwk/Library_jvmfwk.mk @@ -0,0 +1,87 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,jvmfwk)) + +$(eval $(call gb_Library_add_defs,jvmfwk,\ + -DJVMFWK_DLLIMPLEMENTATION \ +)) + +ifneq ($(JVM_ONE_PATH_CHECK),) +$(eval $(call gb_Library_add_defs,jvmfwk,\ + -DJVM_ONE_PATH_CHECK=\"$(JVM_ONE_PATH_CHECK)\" \ +)) +endif + +$(eval $(call gb_Library_set_include,jvmfwk,\ + -I$(SRCDIR)/jvmfwk/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_use_api,jvmfwk,\ + udkapi \ +)) + +$(eval $(call gb_Library_use_libraries,jvmfwk,\ + cppu \ + cppuhelper \ + sal \ + salhelper \ +)) + +ifeq ($(OS),ANDROID) +$(eval $(call gb_Library_use_libraries,jvmfwk,\ + lo-bootstrap \ +)) +endif + +ifeq ($(OS),WNT) +$(eval $(call gb_Library_use_system_win32_libs,jvmfwk,\ + advapi32 \ +)) +endif + +$(eval $(call gb_Library_use_externals,jvmfwk,\ + boost_headers \ + libxml2 \ + valgrind \ +)) + +ifeq ($(OS),MACOSX) +$(eval $(call gb_Library_add_cxxflags,jvmfwk,\ + $(gb_OBJCXXFLAGS) \ +)) + +ifeq ($(CPUNAME),X86_64) +$(eval $(call gb_Library_add_objcxxobjects,jvmfwk,\ + jvmfwk/plugins/sunmajor/pluginlib/util_cocoa \ +)) +endif + +$(eval $(call gb_Library_add_libs,jvmfwk,\ + -framework Foundation \ +)) +endif + +$(eval $(call gb_Library_add_exception_objects,jvmfwk,\ + jvmfwk/plugins/sunmajor/pluginlib/otherjre \ + jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin \ + jvmfwk/plugins/sunmajor/pluginlib/sunjre \ + jvmfwk/plugins/sunmajor/pluginlib/sunversion \ + jvmfwk/plugins/sunmajor/pluginlib/util \ + jvmfwk/plugins/sunmajor/pluginlib/vendorbase \ + jvmfwk/plugins/sunmajor/pluginlib/vendorlist \ + jvmfwk/source/elements \ + jvmfwk/source/framework \ + jvmfwk/source/fwkbase \ + jvmfwk/source/fwkutil \ + jvmfwk/source/libxmlutil \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/jvmfwk/Makefile b/jvmfwk/Makefile new file mode 100644 index 000000000..0997e6284 --- /dev/null +++ b/jvmfwk/Makefile @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/jvmfwk/Module_jvmfwk.mk b/jvmfwk/Module_jvmfwk.mk new file mode 100644 index 000000000..802daed57 --- /dev/null +++ b/jvmfwk/Module_jvmfwk.mk @@ -0,0 +1,37 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Module_Module,jvmfwk)) + +ifeq ($(ENABLE_JAVA),TRUE) + +$(eval $(call gb_Module_add_targets,jvmfwk,\ + CustomTarget_jreproperties \ + CustomTarget_jvmfwk_jvmfwk3_ini \ + Library_jvmfwk \ + Package_jreproperties \ + Package_jvmfwk_jvmfwk3_ini \ + Package_rcfiles \ +)) + +ifneq (,$(filter-out MACOSX WNT,$(OS))) +ifneq (,$(filter DESKTOP,$(BUILD_TYPE))) +$(eval $(call gb_Module_add_targets,jvmfwk,\ + Executable_javaldx \ +)) +endif +endif + +$(eval $(call gb_Module_add_check_targets,jvmfwk, \ + CppunitTest_jvmfwk_sunversion \ +)) + +endif + +# vim:set noet sw=4 ts=4: diff --git a/jvmfwk/Package_jreproperties.mk b/jvmfwk/Package_jreproperties.mk new file mode 100644 index 000000000..cd12402c6 --- /dev/null +++ b/jvmfwk/Package_jreproperties.mk @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Package_Package,jvmfwk_jreproperties,$(call gb_CustomTarget_get_workdir,jvmfwk/jreproperties))) + +$(eval $(call gb_Package_add_file,jvmfwk_jreproperties,$(if $(filter MACOSX,$(OS)),$(LIBO_URE_SHARE_JAVA_FOLDER),$(LIBO_URE_LIB_FOLDER))/JREProperties.class,JREProperties.class)) + +# vim:set noet sw=4 ts=4: diff --git a/jvmfwk/Package_jvmfwk_jvmfwk3_ini.mk b/jvmfwk/Package_jvmfwk_jvmfwk3_ini.mk new file mode 100644 index 000000000..51a933cea --- /dev/null +++ b/jvmfwk/Package_jvmfwk_jvmfwk3_ini.mk @@ -0,0 +1,16 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Package_Package,jvmfwk_jvmfwk3_ini,$(call gb_CustomTarget_get_workdir,jvmfwk/jvmfwk3_ini))) + +$(eval $(call gb_Package_add_files,jvmfwk_jvmfwk3_ini,$(LIBO_URE_ETC_FOLDER), \ + $(call gb_Helper_get_rcfile,jvmfwk3) \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/jvmfwk/Package_rcfiles.mk b/jvmfwk/Package_rcfiles.mk new file mode 100644 index 000000000..e7010b970 --- /dev/null +++ b/jvmfwk/Package_rcfiles.mk @@ -0,0 +1,30 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Package_Package,jvmfwk_javavendors,$(SRCDIR)/jvmfwk)) + +ifeq ($(OS),WNT) +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_wnt.xml)) +else ifeq ($(OS),FREEBSD) +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_freebsd.xml)) +else ifeq ($(OS),MACOSX) +ifeq ($(CPUNAME),AARCH64) +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_macosx_aarch64.xml)) +else +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_macosx.xml)) +endif +else ifeq ($(OS),LINUX) +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_linux.xml)) +else ifeq ($(OS),AIX) +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_linux.xml)) +else +$(eval $(call gb_Package_add_file,jvmfwk_javavendors,$(LIBO_URE_MISC_FOLDER)/javavendors.xml,distributions/OpenOfficeorg/javavendors_unx.xml)) +endif + +# vim:set noet sw=4 ts=4: diff --git a/jvmfwk/README.md b/jvmfwk/README.md new file mode 100644 index 000000000..3f99d9dba --- /dev/null +++ b/jvmfwk/README.md @@ -0,0 +1,20 @@ +# JVM Framework Wrappers + +Wrappers so you can use all the Java Runtime Environments with their slightly incompatible APIs with more ease. + +Used to use an over-engineered "plugin" mechanism although there was only one +"plugin", called "sunmajor", that handles all possible JREs. + +IMPORTANT: The `<updated>` element in `vmfwk/distributions/OpenOfficeorg/javavendors_*.xml` files +should only be updated for incompatible changes, not for compatible ones. As stated in the commit +message of <https://gerrit.libreoffice.org/#/c/69730/> in LibreOffice gerrit: + +javavendors\_\*.xml <updated\> should not have been updated... + +Changing `<updated>` causes `jfw_startVM` and `jfw_getSelectedJRE` (both +`jvmfwk/source/framework.cxx`) to fail with `JFW_E_INVALID_SETTINGS`, which in turn causes functionality +that requires a JVM to issue a GUI error dialog stating that the user must select a new JRE in the +Options dialog. While that behavior makes sense if a JRE was selected that would no longer be +supported by the modified `javavendors_*.xml`, it is just annoying if an already selected JRE is still +supported. And a compatible change to `javavendors_*.xml` implies that an already selected JRE will +still be supported." diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors.xsd b/jvmfwk/distributions/OpenOfficeorg/javavendors.xsd new file mode 100644 index 000000000..fbc643f71 --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors.xsd @@ -0,0 +1,61 @@ +<?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 . +--> +<schema targetNamespace="http://openoffice.org/2004/java/framework/1.0" + xmlns:jf="http://openoffice.org/2004/java/framework/1.0" + xmlns="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified"> + <element name="javaSelection" type="jf:JavaSelectionType"> + <unique name="dummy3"> + <selector xpath="jf:vendorInfos/jf:vendor"/> + <field xpath="@name"/> + </unique> + </element> + + <complexType name="JavaSelectionType"> + <sequence> + <element name="updated" type="date"/> + <element name="vendorInfos" type="jf:VendorInfoType"/> + </sequence> + </complexType> + + + <complexType name="VendorInfoType"> + <sequence> + <element name="vendor" type="jf:VendorType" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + </complexType> + + <complexType name="VendorType"> + <sequence> + <element name="minVersion" type="string" minOccurs="0"/> + <element name="maxVersion" type="string" minOccurs="0"/> + <element name="excludeVersions" minOccurs="0"> + <complexType> + <sequence> + <element name="version" type="string" minOccurs="0" maxOccurs="unbounded"/> + </sequence> + </complexType> + </element> + </sequence> + <attribute name="name" use="required" type="string"/> + </complexType> + + + +</schema> diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors_freebsd.xml b/jvmfwk/distributions/OpenOfficeorg/javavendors_freebsd.xml new file mode 100644 index 000000000..b7776db58 --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors_freebsd.xml @@ -0,0 +1,32 @@ +<?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 . + --> +<javaSelection xmlns="http://openoffice.org/2004/java/framework/1.0"> + <updated>2019-07-26</updated> + <vendorInfos> + <vendor name="Oracle Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Sun Microsystems Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="The FreeBSD Foundation"> + <minVersion>1.8.0</minVersion> + </vendor> + </vendorInfos> +</javaSelection> diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors_linux.xml b/jvmfwk/distributions/OpenOfficeorg/javavendors_linux.xml new file mode 100644 index 000000000..09fcf3733 --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors_linux.xml @@ -0,0 +1,44 @@ +<?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 . + --> +<javaSelection xmlns="http://openoffice.org/2004/java/framework/1.0"> + <updated>2019-07-26</updated> + <vendorInfos> + <vendor name="Sun Microsystems Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Oracle Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="IBM Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Blackdown Java-Linux Team"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="BEA Systems, Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Azul Systems, Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Amazon.com Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + </vendorInfos> +</javaSelection> diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors_macosx.xml b/jvmfwk/distributions/OpenOfficeorg/javavendors_macosx.xml new file mode 100644 index 000000000..a10e366f5 --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors_macosx.xml @@ -0,0 +1,38 @@ +<?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 . + --> +<javaSelection xmlns="http://openoffice.org/2004/java/framework/1.0"> + <updated>2019-07-26</updated> + <vendorInfos> + <vendor name="Oracle Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Apple Computer, Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Apple Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Azul Systems, Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Amazon.com Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + </vendorInfos> +</javaSelection> diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors_macosx_aarch64.xml b/jvmfwk/distributions/OpenOfficeorg/javavendors_macosx_aarch64.xml new file mode 100644 index 000000000..329030ccb --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors_macosx_aarch64.xml @@ -0,0 +1,11 @@ +<?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/. + --> +<javaSelection xmlns="http://openoffice.org/2004/java/framework/1.0"> + <updated>2021-10-19</updated> +</javaSelection> diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors_unx.xml b/jvmfwk/distributions/OpenOfficeorg/javavendors_unx.xml new file mode 100644 index 000000000..9eab9d832 --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors_unx.xml @@ -0,0 +1,29 @@ +<?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 . + --> +<javaSelection xmlns="http://openoffice.org/2004/java/framework/1.0"> + <updated>2019-07-26</updated> + <vendorInfos> + <vendor name="Oracle Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Sun Microsystems Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + </vendorInfos> +</javaSelection> diff --git a/jvmfwk/distributions/OpenOfficeorg/javavendors_wnt.xml b/jvmfwk/distributions/OpenOfficeorg/javavendors_wnt.xml new file mode 100644 index 000000000..d37db0a16 --- /dev/null +++ b/jvmfwk/distributions/OpenOfficeorg/javavendors_wnt.xml @@ -0,0 +1,38 @@ +<?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 . + --> +<javaSelection xmlns="http://openoffice.org/2004/java/framework/1.0"> + <updated>2019-07-26</updated> + <vendorInfos> + <vendor name="Oracle Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Sun Microsystems Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="IBM Corporation"> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Azul Systems, Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + <vendor name="Amazon.com Inc."> + <minVersion>1.8.0</minVersion> + </vendor> + </vendorInfos> +</javaSelection> diff --git a/jvmfwk/inc/elements.hxx b/jvmfwk/inc/elements.hxx new file mode 100644 index 000000000..3fde096c4 --- /dev/null +++ b/jvmfwk/inc/elements.hxx @@ -0,0 +1,329 @@ +/* -*- 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_JVMFWK_SOURCE_ELEMENTS_HXX +#define INCLUDED_JVMFWK_SOURCE_ELEMENTS_HXX + +#include <sal/config.h> + +#include <memory> +#include <vector> +#include "fwkutil.hxx" +#include <rtl/ustring.hxx> +#include <rtl/byteseq.hxx> +#include <libxml/parser.h> +#include <optional> + +struct JavaInfo; + +#define NS_JAVA_FRAMEWORK "http://openoffice.org/2004/java/framework/1.0" +#define NS_SCHEMA_INSTANCE "http://www.w3.org/2001/XMLSchema-instance" + +namespace jfw +{ +/** gets the value of the updated element from the javavendors.xml. + */ +OString getElementUpdated(); + +/** create the child elements within the root structure for each platform. + + @param bNeedsSave + [out]If true then the respective structure of elements was added and the + document needs to be saved. + */ +void createSettingsStructure(xmlDoc* document, bool* bNeedsSave); + +/** represents the settings saved in the /java/javaInfo element. + It is used within class NodeJava which determines the settings + file. +*/ +class CNodeJavaInfo +{ +public: + CNodeJavaInfo(); + + /** if true, then javaInfo is empty. When writeToNode is called + then all child elements are deleted. + */ + bool m_bEmptyNode; + /** Contains the value of the <updated> element of + the javavendors.xml after loadFromNode was called. + It is not used, when the javaInfo node is written. + see writeToNode + */ + OString sAttrVendorUpdate; + /** contains the nil value of the /java/javaInfo@xsi:nil attribute. + Default is true; + */ + bool bNil; + /** contains the value of the /java/javaInfo@autoSelect attribute. + Default is true. If it is false then the user has modified the JRE + selection by actively choosing a JRE from the options dialog. That is, + the function jfw_setSelectedJRE was called. Contrary, the function + jfw_findAndSelectJRE sets the attribute to true. + */ + bool bAutoSelect; + OUString sVendor; + OUString sLocation; + OUString sVersion; + sal_uInt64 nRequirements; + ::rtl::ByteSequence arVendorData; + + /** reads the node /java/javaInfo. + If javaInfo@xsi:nil = true then member bNil is set to true + and no further elements are read. + */ + void loadFromNode(xmlDoc* pDoc, xmlNode* pJavaInfo); + /** The attribute nil will be set to false. The function gets the value + javaSettings/updated from the javavendors.xml and writes it to + javaInfo@vendorUpdate in javasettings.xml + */ + void writeToNode(xmlDoc* pDoc, xmlNode* pJavaInfo) const; + + /** returns NULL if javaInfo is nil. + */ + std::unique_ptr<JavaInfo> makeJavaInfo() const; +}; + +/** this class represents the java settings based on a particular + settings file. + + Which settings file is used is determined by the value passed into the + constructor and the values of the bootstrap parameters + UNO_JAVA_JFW_USER_DATA and UNO_JAVA_JFW_SHARED_DATA. + + The method load reads the data from the settings file. + The method write stores the data into the settings file. + */ +class NodeJava +{ +public: + enum Layer + { + USER, + SHARED + }; + +private: + /** creates settings file and fills it with default values. + + When this function is called then it creates the + settings file at the position determined by the bootstrap parameters + (UNO_JAVA_JFW_USER_DATA, UNO_JAVA_JFW_SHARED_DATA) and m_layer, unless + the file already exists (see createSettingsDocument). + + @return + JFW_E_CONFIG_READWRITE + */ + bool prepareSettingsDocument() const; + + /** helper function for prepareSettingsDocument. + */ + bool createSettingsDocument() const; + + /** returns the system path to the data file which is to be used. The value + depends on the member m_layer and the bootstrap parameters + UNO_JAVA_JFW_USER_DATA and UNO_JAVA_JFW_SHARED_DATA. + */ + OString getSettingsPath() const; + + /** returns the file URL to the data file which is to be used. See getSettingsPath. + */ + OUString getSettingsURL() const; + + /** Verifies if the respective settings file exist. + */ + static jfw::FileStatus checkSettingsFileStatus(OUString const& sURL); + + /** Determines the layer for which the instance the loads and writes the + data. + */ + Layer m_layer; + + /** User configurable option. /java/enabled + If /java/enabled@xsi:nil == true then the value will be uninitialized + after a call to load(). + */ + std::optional<sal_Bool> m_enabled; + + /** User configurable option. /java/userClassPath + If /java/userClassPath@xsi:nil == true then the value is uninitialized + after a call to load(). + */ + std::optional<OUString> m_userClassPath; + /** User configurable option. /java/javaInfo + If /java/javaInfo@xsi:nil == true then the value is uninitialized + after a call to load. + */ + std::optional<CNodeJavaInfo> m_javaInfo; + /** User configurable option. /java/vmParameters + If /java/vmParameters@xsi:nil == true then the value is uninitialized + after a call to load. + */ + std::optional<::std::vector<OUString>> m_vmParameters; + /** User configurable option. /java/jreLocations + If /java/jreLocations@xsi:nil == true then the value is uninitialized + after a call to load. + */ + std::optional<::std::vector<OUString>> m_JRELocations; + +public: + explicit NodeJava(Layer theLayer); + + /** sets m_enabled. + /java/enabled@xsi:nil will be set to false when write is called. + */ + void setEnabled(bool bEnabled); + + /** sets m_sUserClassPath. See setEnabled. + */ + void setUserClassPath(const OUString& sClassPath); + + /** sets m_aInfo. See setEnabled. + @param bAutoSelect + true- called by jfw_setSelectedJRE + false called by jfw_findAndSelectJRE + */ + void setJavaInfo(const JavaInfo* pInfo, bool bAutoSelect); + + /** sets the /java/vmParameters/param elements. + When this method all previous values are removed and replaced + by those in arParameters. + /java/vmParameters@xsi:nil will be set to true when write() is + called. + */ + void setVmParameters(std::vector<OUString> const& arParameters); + + /** adds a location to the already existing locations. + Note: call load() before, then add the location and then call write(). + */ + void addJRELocation(OUString const& sLocation); + + /** writes the data to user settings. + */ + void write() const; + + /** load the values of the settings file. + */ + void load(); + + /** returns the value of the element /java/enabled + */ + const std::optional<sal_Bool>& getEnabled() const { return m_enabled; } + /** returns the value of the element /java/userClassPath. + */ + const std::optional<OUString>& getUserClassPath() const { return m_userClassPath; } + + /** returns the value of the element /java/javaInfo. + */ + const std::optional<CNodeJavaInfo>& getJavaInfo() const { return m_javaInfo; } + + /** returns the parameters from the element /java/vmParameters/param. + */ + const std::optional<::std::vector<OUString>>& getVmParameters() const { return m_vmParameters; } + + /** returns the parameters from the element /java/jreLocations/location. + */ + const std::optional<::std::vector<OUString>>& getJRELocations() const { return m_JRELocations; } +}; + +/** merges the settings for shared, user and installation during construction. + The class uses a simple merge mechanism for the javasettings.xml files in share and + user. The following elements completely overwrite the corresponding elements + from share: + /java/enabled + /java/userClassPath + /java/vmParameters + /java/jreLocations + /java/javaInfo + + In case of an installation, the shared and user settings are completely + disregarded. + + The locations of the different settings files is obtained through the + bootstrap variables: + UNO_JAVA_JFW_USER_DATA + UNO_JAVA_JFW_SHARED_DATA + + The class also determines useful default values for settings which have not been made. +*/ +class MergedSettings final +{ +private: + MergedSettings& operator=(MergedSettings const&) = delete; + MergedSettings(MergedSettings const&) = delete; + + void merge(const NodeJava& share, const NodeJava& user); + + bool m_bEnabled; + + OUString m_sClassPath; + + ::std::vector<OUString> m_vmParams; + + ::std::vector<OUString> m_JRELocations; + + CNodeJavaInfo m_javaInfo; + +public: + MergedSettings(); + ~MergedSettings(); + + /** the default is true. + */ + bool getEnabled() const { return m_bEnabled; } + + const OUString& getUserClassPath() const { return m_sClassPath; } + + ::std::vector<OString> getVmParametersUtf8() const; + /** returns a JavaInfo structure representing the node + /java/javaInfo. Every time a new JavaInfo structure is created + which needs to be freed by the caller. + If both, user and share settings are nil, then NULL is returned. + */ + std::unique_ptr<JavaInfo> createJavaInfo() const; + + /** returns the value of the attribute /java/javaInfo[@vendorUpdate]. + */ + OString const& getJavaInfoAttrVendorUpdate() const { return m_javaInfo.sAttrVendorUpdate; } + +#ifdef _WIN32 + /** returns the javaInfo@autoSelect attribute. + Before calling this function loadFromSettings must be called. + It uses the javaInfo@autoSelect attribute to determine + the return value; + */ + bool getJavaInfoAttrAutoSelect() const; +#endif + + void getVmParametersArray(std::vector<OUString>* parParameters) const; + + const ::std::vector<OUString>& getJRELocations() const { return m_JRELocations; } +}; + +struct VersionInfo +{ + ::std::vector<OUString> vecExcludeVersions; + OUString sMinVersion; + OUString sMaxVersion; +}; + +} //end namespace +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/inc/fwkbase.hxx b/jvmfwk/inc/fwkbase.hxx new file mode 100644 index 000000000..61c3fa500 --- /dev/null +++ b/jvmfwk/inc/fwkbase.hxx @@ -0,0 +1,130 @@ +/* -*- 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_JVMFWK_INC_FWKBASE_HXX +#define INCLUDED_JVMFWK_INC_FWKBASE_HXX + +#include <sal/config.h> + +#include <string_view> +#include <vector> + +#include <rtl/ustring.hxx> +#include "libxmlutil.hxx" + +namespace jfw +{ +struct VersionInfo; + +class VendorSettings +{ + CXmlDocPtr m_xmlDocVendorSettings; + CXPathContextPtr m_xmlPathContextVendorSettings; + +public: + VendorSettings(); + + VersionInfo getVersionInformation(std::u16string_view sVendor) const; +}; + +/* The class offers functions to retrieve verified bootstrap parameters. + */ +namespace BootParams +{ +/* Gets the file URL to the JRE which has been determined by the + bootstrap parameter UNO_JAVA_JFW_JREHOME or UNO_JAVA_JFW_ENV_JREHOME. + + In direct mode either of them must be set. If not an exception is thrown. +*/ +OUString getJREHome(); + +::std::vector<OString> getVMParameters(); + +OUString getUserData(); + +OUString getSharedData(); + +/* returns the file URL to the vendor settings xml file. + */ +OUString getVendorSettings(); + +/* User the parameter UNO_JAVA_JFW_CLASSPATH and UNO_JAVA_JFW_ENV_CLASSPATH + to compose a classpath + */ +OString getClasspath(); + +OUString getClasspathUrls(); + +} //end namespace + +enum JFW_MODE +{ + JFW_MODE_APPLICATION, + + JFW_MODE_DIRECT +}; + +JFW_MODE getMode(); + +/** creates the -Djava.class.path option with the complete classpath, including + the paths which are set by UNO_JAVA_JFW_CLASSPATH_URLS. + */ +OString makeClassPathOption(std::u16string_view sUserClassPath); + +OString getSettingsPath(const OUString& sURL); + +/** Get the system path to the javasettings.xml + Converts the URL returned from getUserSettingsURL to a + Systempath. An empty string is returned if the file + does not exist. + @throws FrameworkException + */ +OString getUserSettingsPath(); + +/** Returns the system path of the share settings file. + Returns a valid string or throws an exception. + @throws FrameworkException + */ +OString getSharedSettingsPath(); + +/* returns a valid string or throws an exception. + @throws FrameworkException + */ +OString getVendorSettingsPath(); + +/** Called from writeJavaInfoData. It sets the process identifier. When +java is to be started, then the current id is compared to the one set by +this function. If they are identical then the Java was selected in the +same process. If that Java needs a prepared environment, such as a +LD_LIBRARY_PATH, then it must not be started in this process. +*/ +void setJavaSelected(); + +/** Determines if the currently selected Java was set in this process. + + @see setProcessId() + */ +bool wasJavaSelectedInSameProcess(); +/* Only for application mode. + */ +OUString getApplicationClassPath(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/inc/fwkutil.hxx b/jvmfwk/inc/fwkutil.hxx new file mode 100644 index 000000000..76d437ae7 --- /dev/null +++ b/jvmfwk/inc/fwkutil.hxx @@ -0,0 +1,77 @@ +/* -*- 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_JVMFWK_SOURCE_FWKUTIL_HXX +#define INCLUDED_JVMFWK_SOURCE_FWKUTIL_HXX + +#include <config_folders.h> + +#include <sal/config.h> +#include <rtl/bootstrap.hxx> +#include <rtl/byteseq.hxx> +#include <osl/mutex.hxx> + +namespace osl { class Mutex; } + +namespace jfw +{ + +/** Returns the file URL of the directory where the framework library + (this library) resides. +*/ +OUString getLibraryLocation(); + +/** provides a bootstrap class which already knows the values from the + jvmfkwrc file. +*/ +const rtl::Bootstrap * Bootstrap(); + +osl::Mutex& FwkMutex(); + +rtl::ByteSequence encodeBase16(const rtl::ByteSequence& rawData); +rtl::ByteSequence decodeBase16(const rtl::ByteSequence& data); + +OUString getDirFromFile(std::u16string_view usFilePath); + +enum FileStatus +{ + FILE_OK, + FILE_DOES_NOT_EXIST, + FILE_INVALID +}; + +/** checks if the URL is a file. + + If it is a link to a file than + it is resolved. Assuming that the argument + represents a relative URL then FILE_INVALID + is returned. + + + @return + one of the values of FileStatus. + + @exception + Errors occurred during determining if the file exists + */ +FileStatus checkFileURL(const OUString & path); + +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/inc/libxmlutil.hxx b/jvmfwk/inc/libxmlutil.hxx new file mode 100644 index 000000000..2599cff50 --- /dev/null +++ b/jvmfwk/inc/libxmlutil.hxx @@ -0,0 +1,101 @@ +/* -*- 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_JVMFWK_INC_LIBXMLUTIL_HXX +#define INCLUDED_JVMFWK_INC_LIBXMLUTIL_HXX + +#include <sal/config.h> + +#include <string_view> + +#include <libxml/parser.h> +#include <libxml/xpath.h> +#include <rtl/ustring.hxx> + +namespace jfw +{ +class CXPathObjectPtr +{ + xmlXPathObject* _object; + CXPathObjectPtr& operator=(const CXPathObjectPtr&) = delete; + CXPathObjectPtr(const CXPathObjectPtr&) = delete; + +public: + CXPathObjectPtr(); + CXPathObjectPtr(xmlXPathObject*); + ~CXPathObjectPtr(); + /** Takes ownership of xmlXPathObject + */ + CXPathObjectPtr& operator=(xmlXPathObject* pObj); + xmlXPathObject* operator->() { return _object; } + operator xmlXPathObject*() const { return _object; } +}; + +class CXPathContextPtr +{ + xmlXPathContext* _object; + + CXPathContextPtr(const jfw::CXPathContextPtr&) = delete; + CXPathContextPtr& operator=(const CXPathContextPtr&) = delete; + +public: + CXPathContextPtr(); + CXPathContextPtr(xmlXPathContextPtr aContext); + CXPathContextPtr& operator=(xmlXPathContextPtr pObj); + ~CXPathContextPtr(); + operator xmlXPathContext*() const { return _object; } +}; + +class CXmlDocPtr +{ + xmlDoc* _object; + + CXmlDocPtr(const CXmlDocPtr&) = delete; + +public: + CXmlDocPtr& operator=(const CXmlDocPtr&); + CXmlDocPtr(); + CXmlDocPtr(xmlDoc* aDoc); + /** Takes ownership of xmlDoc + */ + CXmlDocPtr& operator=(xmlDoc* pObj); + ~CXmlDocPtr(); + operator xmlDoc*() const { return _object; } +}; + +class CXmlCharPtr +{ + xmlChar* _object; + + CXmlCharPtr(const CXmlCharPtr&) = delete; + CXmlCharPtr& operator=(const CXmlCharPtr&) = delete; + +public: + CXmlCharPtr(); + CXmlCharPtr(xmlChar* aDoc); + explicit CXmlCharPtr(std::u16string_view); + ~CXmlCharPtr(); + CXmlCharPtr& operator=(xmlChar* pObj); + operator xmlChar*() const { return _object; } + operator OUString(); + operator OString() { return reinterpret_cast<char*>(_object); } +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/inc/vendorbase.hxx b/jvmfwk/inc/vendorbase.hxx new file mode 100644 index 000000000..779fc8f7a --- /dev/null +++ b/jvmfwk/inc/vendorbase.hxx @@ -0,0 +1,182 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_VENDORBASE_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_VENDORBASE_HXX + +#include <rtl/ustring.hxx> +#include <rtl/ref.hxx> +#include <osl/endian.h> +#include <salhelper/simplereferenceobject.hxx> +#include <vector> + +namespace jfw_plugin +{ +//Used by subclasses of VendorBase to build paths to Java runtime +#if defined(JAVA_ARCH) +#define JFW_PLUGIN_ARCH JAVA_ARCH +#elif defined SPARC64 +#define JFW_PLUGIN_ARCH "sparcv9" +#elif defined SPARC +#define JFW_PLUGIN_ARCH "sparc" +#elif defined X86_64 +#define JFW_PLUGIN_ARCH "amd64" +#elif defined ARM64 +#define JFW_PLUGIN_ARCH "arm64" +#elif defined INTEL +#define JFW_PLUGIN_ARCH "i386" +#elif defined POWERPC64 +#define JFW_PLUGIN_ARCH "ppc64" +#elif defined POWERPC +#define JFW_PLUGIN_ARCH "ppc" +#elif defined MIPS +#ifdef OSL_BIGENDIAN +#define JFW_PLUGIN_ARCH "mips" +#else +/* FIXME: do JDKs have some JDK-specific define? This is for +OpenJDK at least, but probably not true for Lemotes JDK */ +#define JFW_PLUGIN_ARCH "mipsel" +#endif +#elif defined MIPS64 +#ifdef OSL_BIGENDIAN +#define JFW_PLUGIN_ARCH "mips64" +#else +#define JFW_PLUGIN_ARCH "mips64el" +#endif +#elif defined S390X +#define JFW_PLUGIN_ARCH "s390x" +#elif defined S390 +#define JFW_PLUGIN_ARCH "s390" +#elif defined ARM +#define JFW_PLUGIN_ARCH "arm" +#elif defined IA64 +#define JFW_PLUGIN_ARCH "ia64" +#elif defined M68K +#define JFW_PLUGIN_ARCH "m68k" +#elif defined HPPA +#define JFW_PLUGIN_ARCH "parisc" +#elif defined AXP +#define JFW_PLUGIN_ARCH "alpha" +#elif defined AARCH64 +#define JFW_PLUGIN_ARCH "aarch64" +#else // SPARC, INTEL, POWERPC, MIPS, MIPS64, ARM, IA64, M68K, HPPA, ALPHA +#error unknown platform +#endif // SPARC, INTEL, POWERPC, MIPS, MIPS64, ARM, IA64, M68K, HPPA, ALPHA + +class MalformedVersionException final : public std::exception +{ +public: + virtual ~MalformedVersionException() override; +}; + +class VendorBase : public salhelper::SimpleReferenceObject +{ +public: + VendorBase(); + /* static char const* const * getJavaExePaths(int* size); + + returns relative paths to the java executable as + file URLs. + + For example "bin/java.exe". You need + to implement this function in a derived class, if + the paths differ. this implementation provides for + Windows "bin/java.exe" and for Unix "bin/java". + The paths are relative file URLs. That is, they always + contain '/' even on windows. The paths are relative + to the installation directory of a JRE. + + + The signature of this function must correspond to + getJavaExePaths_func. + */ + + /* static rtl::Reference<VendorBase> createInstance(); + + creates an instance of this class. MUST be overridden + in a derived class. + #################################################### + OVERRIDE in derived class + ################################################### + @param + Key - value pairs of the system properties of the JRE. + */ + + const OUString& getVendor() const; + const OUString& getVersion() const; + const OUString& getHome() const; + const OUString& getRuntimeLibrary() const; + const OUString& getLibraryPath() const; + bool isValidArch() const; + /* determines if prior to running java something has to be done, + like setting the LD_LIBRARY_PATH. This implementation checks + if an LD_LIBRARY_PATH (getLD_LIBRARY_PATH) needs to be set and + if so, needsRestart returns true. + */ + bool needsRestart() const; + + /* compares versions of this vendor. MUST be overridden + in a derived class. + #################################################### + OVERRIDE in derived class + ################################################### + @return + 0 this.version == sSecond + 1 this.version > sSecond + -1 this.version < sSEcond + + @throw + MalformedVersionException if the version string was not recognized. + */ + virtual int compareVersions(const OUString& sSecond) const = 0; + +protected: + /* called automatically on the instance created by createInstance. + + @return + true - the object could completely initialize. + false - the object could not completely initialize. In this case + it will be discarded by the caller. + */ + bool initialize(const std::vector<std::pair<OUString, OUString>>& props); + + /* returns relative file URLs to the runtime library. + For example "/bin/client/jvm.dll" + */ + virtual char const* const* getRuntimePaths(int* size) = 0; + + virtual char const* const* getLibraryPaths(int* size) = 0; + + OUString m_sVendor; + OUString m_sVersion; + OUString m_sHome; + OUString m_sRuntimeLibrary; + OUString m_sLD_LIBRARY_PATH; + OUString m_sArch; + + typedef rtl::Reference<VendorBase> (*createInstance_func)(); + friend rtl::Reference<VendorBase> + createInstance(createInstance_func pFunc, + const std::vector<std::pair<OUString, OUString>>& properties); +}; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/inc/vendorplugin.hxx b/jvmfwk/inc/vendorplugin.hxx new file mode 100644 index 000000000..4c24903ba --- /dev/null +++ b/jvmfwk/inc/vendorplugin.hxx @@ -0,0 +1,245 @@ +/* -*- 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 . + */ + +/** @HTML */ +#ifndef INCLUDED_JVMFWK_INC_VENDORPLUGIN_HXX +#define INCLUDED_JVMFWK_INC_VENDORPLUGIN_HXX + +#include <rtl/ustring.hxx> +#include <jni.h> + +#include <memory> +#include <vector> + +namespace jfw_plugin { class VendorBase; } +namespace rtl { template <class reference_type> class Reference; } + +struct JavaInfo; + +namespace jfw { class VendorSettings; } + +/** + @file + <p> + This API shall be implemented if one wants to support a Java Runtime + Environment (JRE) of a particular vendor. Because there is currently no + specification which rules the structure and location of JRE installations + and the format of version strings it is not possible to supply a general + implementation for all possible vendors. If an application determines exactly + what version a JRE must have then it relies on certain features and bug + fixes of that version. Because a version 1.4.2_1 from vendor X may contain + different fixes as the same version from vendor Y it is important to see + version and vendor as one entity. One without the other does not guarantee + the existence of a particular set of features or bug fixes. An implementation + of this API may support multiple vendors. </p> + */ + +enum class javaPluginError +{ + NONE, + Error, + InvalidArg, + WrongVersionFormat, + FailedVersion, + NoJre, + WrongArch, + VmCreationFailed +}; + + +/** obtains information about installations of Java Runtime Environments (JREs). + + <p>The function gathers information about available JREs. Only information + about those JREs which match the version requirements are returned. These + requirements are specified by the parameter <code>vendorSettings</code>. + </p> + <p> + The JavaInfo structures returned in <code>parJavaInfo</code> should be ordered + according to their version. The one, representing a JRE with the highest + version should be the first in the array. </p> + @param parJavaInfo + [out] if the function runs successfully then <code>parJavaInfo</code> contains + on return a vector of pointers to <code>JavaInfo</code> objects. + + @return + javaPluginError::NONE the function ran successfully.</br> + javaPluginError::Error an error occurred during execution.</br> + javaPluginError::WrongVersionFormat the version strings in + <code>vendorSettings</code> are not recognized as valid + version strings. + */ +javaPluginError jfw_plugin_getAllJavaInfos( + bool checkJavaHomeAndPath, + jfw::VendorSettings const & vendorSettings, + std::vector<std::unique_ptr<JavaInfo>> * parJavaInfo, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos); + +/** obtains information for a JRE at a given location. + + <p>If the given location belongs to a JRE + and the JRE has a version which meets the requirements as + specified by <code>vendorSettings</code> then + this function shall return a JavaInfo object for this JRE.</p> + + @param sLocation + [in] a file URL to the directory of the JRE. + @param ppInfo + [out] if the function runs successfully then <code>ppInfo</code> contains + on return a pointer to a <code>JavaInfo</code> object. + + @return + javaPluginError::NONE the function ran successfully.</br> + javaPluginError::Error an error occurred during execution.</br> + javaPluginError::InvalidArg an argument was not valid. For example, sLocation + is an empty string.</br> + javaPluginError::FailedVersion there is a JRE at the given location but it does not + meet the version requirements. + javaPluginError::NoJre no JRE could be detected at the given location. + */ +javaPluginError jfw_plugin_getJavaInfoByPath( + OUString const& sLocation, + jfw::VendorSettings const & vendorSettings, + std::unique_ptr<JavaInfo> * ppInfo); + + +/** obtains information for a JRE referenced by the JAVA_HOME environment variable. + + <p>If the JAVA_HOME environment variable is set and points to a JRE that + matches the requirements given by vendorSettings (i.e. + the version requirements, if any, for the vendor are met), + then this function shall return a JavaInfo object for this JRE.</p> + + @param ppInfo + [out] if the JAVA_HOME environment variable is set and points to a suitable + JRE, then <code>ppInfo</code> contains + on return a pointer to its <code>JavaInfo</code> object. + + @return + javaPluginError::NONE the function ran successfully.</br> + javaPluginError::NoJre no suitable JRE could be detected at the given location. However, that + does not mean necessarily that there is no JRE. There could be a JRE but + it does not + meet the version requirements. + */ +javaPluginError jfw_plugin_getJavaInfoFromJavaHome( + jfw::VendorSettings const & vendorSettings, + std::unique_ptr<JavaInfo> * ppInfo, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos); + + +/** obtains information about installations of Java Runtime Environments (JREs) + whose executable is in the PATH. + + <p>The function gathers information about available JREs which are on the PATH + (PATH environment variable) and meet the version requirements given by + <code>vendorSettings</code> (i.e. + the version requirements, if any, for the vendor are met). + </p> + <p> + The JavaInfo structures returned in <code>vecJavaInfosFromPath</code> should be ordered + according to their occurrence in the PATH. The one that is the first one on the PATH + is also the first element in the vector.</p> + @param vecJavaInfosFromPath + [out] if the function runs successfully then <code>vecJavaInfosFromPath</code> + contains on return a vector of pointers to <code>JavaInfo</code> objects. + On return of this function, <code>vecJavaInfosFromPath</code> references + a newly created vector rather than the same vector as before with + the <code>JavaInfo</code> objects inserted into the existing vector. + + @return + javaPluginError::NONE the function ran successfully and at least one JRE + that meets the requirements was found.</br> + javaPluginError::NoJre no JavaInfo that meets the version criteria was found + when inspecting the PATH + */ + +javaPluginError jfw_plugin_getJavaInfosFromPath( + jfw::VendorSettings const & vendorSettings, + std::vector<std::unique_ptr<JavaInfo>> & vecJavaInfosFromPath, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos); + +/** starts a Java Virtual Machine. + + <p>The caller should provide all essential JavaVMOptions, such as the + class path (-Djava.class.path=xxx). It is assumed that the caller + knows what JRE is used. Hence the implementation does not need to check + the options for validity. If a user configured the application to + use specific options, such as -X..., then it is in his responsibility to + ensure that the application works properly. The function may add or modify + properties. For example, it may add to the class path property. + <p> + The function must ensure, that the VM does not abort the process + during instantiation.</p> + <p> + The function receives a <code>JavaInfo</code> object that was created + by the functions <code>jfw_plugin_getJavaInfoByPath</code> or + <code>jfw_plugin_getAllJavaInfos</code> from the same library. This can be + guaranteed if an application uses exactly one library for one vendor. + Therefore the functions which create the <code>JavaInfo</code> can store all + necessary information which are needed for starting the VM into that + structure. </p> + + @param pInfo + [in] the JavaInfo object with information about the JRE. + @param arOptions + [in] the options which are passed into the JNI_CreateJavaVM function. + Can be NULL. + @param nSizeOptions + [in] the number of elements in <code>arOptions</code>. + @param ppVM + [out] the JavaVM pointer of the created VM. + @param ppEnv + [out] the JNIEnv pointer of the created VM. + + @return + javaPluginError::NONE the function ran successfully.</br> + javaPluginError::Error an error occurred during execution.</br> + JFW_PLUGIN_E_VM_CREATION_FAILED a VM could not be created. The error was caused + by the JRE. + */ +javaPluginError jfw_plugin_startJavaVirtualMachine( + const JavaInfo *pInfo, + const JavaVMOption *arOptions, + sal_Int32 nSizeOptions, + JavaVM ** ppVM, + JNIEnv ** ppEnv); + + +/** checks if the installation of the jre still exists. + + This function checks if the JRE described by pInfo still + exists. The check must be very quick because it is called by javaldx + (Linux, Solaris) at start up. + + @param pInfo + [in] the JavaInfo object with information about the JRE. + @param pp_exist + [out] the parameter is set to either sal_True or sal_False. The value is + only valid if the function returns JFW_E_NONE. + + @return + javaPluginError::NONE the function ran successfully.</br> + javaPluginError::Error an error occurred during execution.</br> + javaPluginError::InvalidArg pInfo contains invalid data</br> + */ +javaPluginError jfw_plugin_existJRE(const JavaInfo *pInfo, bool *exist); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/javaenvsetup/javaldx.cxx b/jvmfwk/plugins/sunmajor/javaenvsetup/javaldx.cxx new file mode 100644 index 000000000..9a39f2131 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/javaenvsetup/javaldx.cxx @@ -0,0 +1,160 @@ +/* -*- 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 <memory> + +#include <stdio.h> +#include <string.h> +#include <iostream> +#include <sal/main.h> +#include <sal/types.h> +#include <osl/thread.h> +#include <rtl/ustring.hxx> +#include <rtl/byteseq.hxx> +#include <jvmfwk/framework.hxx> +#include <o3tl/string_view.hxx> + + +static bool hasOption(char const * szOption, int argc, char** argv); +static OString getLD_LIBRARY_PATH(const rtl::ByteSequence & vendorData); +static bool findAndSelect(std::unique_ptr<JavaInfo>*); + +#define HELP_TEXT \ +"\njavaldx is necessary to make Java work on some UNIX platforms." \ +"It prints a string to std out that consists of directories which " \ +"have to be included into the LD_LIBRARY_PATH variable.The setting of " \ +"the variable usually occurs in a shell script that runs javaldx.\n" \ +"The directories are from the chosen java installation. \n" \ +"Options are: \n"\ +"--help or -h\n" + +SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) +{ + try + { + if( hasOption("--help",argc, argv) || hasOption("-h", argc, argv)) + { + fprintf(stdout, HELP_TEXT);// default + return 0; + } + bool bEnabled = false; + javaFrameworkError errcode = jfw_getEnabled( & bEnabled); + if (errcode == JFW_E_NONE && !bEnabled) + { + //Do not do any preparation because that may only slow startup time. + fprintf(stdout, "\n"); + return 0; + } + else if (errcode != JFW_E_NONE && errcode != JFW_E_DIRECT_MODE) + { + fprintf(stderr,"javaldx failed!\n"); + return -1; + } + + std::unique_ptr<JavaInfo> aInfo; + errcode = jfw_getSelectedJRE(&aInfo); + + if (errcode != JFW_E_NONE && errcode != JFW_E_INVALID_SETTINGS) + { + fprintf(stderr,"javaldx failed!\n"); + return -1; + } + + if (!aInfo) + { + if (!findAndSelect(&aInfo)) + return -1; + } + else + { + //check if the JRE was not uninstalled + bool bExist = false; + errcode = jfw_existJRE(aInfo.get(), &bExist); + if (errcode == JFW_E_NONE) + { + if (!bExist && !findAndSelect(&aInfo)) + return -1; + } + else + { + fprintf(stderr, "javaldx: Could not determine if JRE still exist\n"); + return -1; + } + } + + OString sPaths = getLD_LIBRARY_PATH(aInfo->arVendorData); + fprintf(stdout, "%s\n", sPaths.getStr()); + } + catch (const std::exception& e) + { + std::cerr << "javaldx failed! " << e.what() << std::endl; + return -1; + } + + return 0; +} + +OString getLD_LIBRARY_PATH(const rtl::ByteSequence & vendorData) +{ + const sal_Unicode* chars = reinterpret_cast<sal_Unicode const *>(vendorData.getConstArray()); + sal_Int32 len = vendorData.getLength(); + OUString sData(chars, len / 2); + //the runtime lib is on the first line + sal_Int32 index = 0; + std::u16string_view aToken = o3tl::getToken(sData, 1, '\n', index); + + OString paths = + OUStringToOString(aToken, osl_getThreadTextEncoding()); + return paths; +} + +static bool hasOption(char const * szOption, int argc, char** argv) +{ + bool retVal= false; + for(int i= 1; i < argc; i++) + { + if( ! strcmp(argv[i], szOption)) + { + retVal= true; + break; + } + } + return retVal; +} + +static bool findAndSelect(std::unique_ptr<JavaInfo> * ppInfo) +{ + javaFrameworkError errcode = jfw_findAndSelectJRE(ppInfo); + if (errcode == JFW_E_NO_JAVA_FOUND) + { + fprintf(stderr,"javaldx: Could not find a Java Runtime Environment!\n"); + return false; + } + else if (errcode != JFW_E_NONE && errcode != JFW_E_DIRECT_MODE) + { + fprintf(stderr,"javaldx failed!\n"); + return false; + } + return true; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java b/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java new file mode 100644 index 000000000..0d3503af3 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/JREProperties.java @@ -0,0 +1,62 @@ +/* + * 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 . + */ + +import java.util.*; + +/** This class prints out the system properties. + + We cannot print the strings directly because of encoding issues. Since + about 1.3.1 one can start java with the option -Dfile.encoding=UTF-8, but + unfortunately this works only with later update - versions (for example, + 1.3.1_07). Therefore we use this scheme. The property string has this form: + name=value + + Every character is cast to an integer which value is printed, followed by a + space. If all characters of the string are printed, then a new line is printed. +*/ +public class JREProperties +{ + public static void main(String[] args) + { + try + { + Properties p = System.getProperties(); + Enumeration e = p.propertyNames(); + while (e.hasMoreElements()) { + String sProp = (String) e.nextElement(); + String sCompleteProp = sProp + "=" + p.getProperty(sProp); + char[] arChars = new char[sCompleteProp.length()]; + sCompleteProp.getChars(0, sCompleteProp.length(), arChars, 0); + for (int c = 0; c < arChars.length; c++) { + System.out.print(String.valueOf((int) arChars[c])); + System.out.print(" "); + } + System.out.print("\n"); + } + } + catch(Exception e) + { + System.err.println(e); + } + + System.exit(0); + } + + + +} diff --git a/jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h b/jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h new file mode 100644 index 000000000..599c40438 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/diagnostics.h @@ -0,0 +1,35 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_DIAGNOSTICS_H +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_DIAGNOSTICS_H + +#include <sal/config.h> + +#include <sal/log.hxx> + +#define JFW_ENSURE(c, m) SAL_WARN_IF(!(c), "jfw", m) + +#define JFW_TRACE0(m) SAL_INFO("jfw.level1", m) + +#define JFW_TRACE2(m) SAL_INFO("jfw.level2", m) + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx new file mode 100644 index 000000000..528af9498 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.cxx @@ -0,0 +1,115 @@ +/* -*- 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 "otherjre.hxx" + +using ::rtl::Reference; +namespace jfw_plugin +{ + +Reference<VendorBase> OtherInfo::createInstance() +{ + return new OtherInfo; +} + + +char const* const* OtherInfo::getJavaExePaths(int * size) +{ + static char const * ar[] = { +#if defined(_WIN32) + "bin/java.exe", + "jre/bin/java.exe" +#elif defined UNX + "bin/java", + "jre/bin/java" +#endif + }; + *size = SAL_N_ELEMENTS (ar); + return ar; +} + +char const* const* OtherInfo::getRuntimePaths(int * size) +{ + static char const* ar[]= { +#if defined(_WIN32) + "/bin/client/jvm.dll", + "/bin/hotspot/jvm.dll", + "/bin/classic/jvm.dll", + "/bin/jrockit/jvm.dll", + // Needed by IBM Semeru Runtime, which is an OpenJDK (so should actually use SunInfo) with a + // java.vendor of "IBM Corporation", so using OtherInfo here (cf. gVendorMap in + // jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx): + "/bin/server/jvm.dll" +#elif defined UNX +#ifdef MACOSX + "/../../../../../Frameworks/JavaVM.framework/JavaVM" //as of 1.6.0_22 +#else + "/lib/" JFW_PLUGIN_ARCH "/client/libjvm.so", // for Blackdown PPC + "/lib/" JFW_PLUGIN_ARCH "/server/libjvm.so", // for Blackdown AMD64 + "/lib/" JFW_PLUGIN_ARCH "/classic/libjvm.so", // for Blackdown PPC + "/lib/" JFW_PLUGIN_ARCH "/jrockit/libjvm.so", // for Java of BEA Systems + "/bin/classic/libjvm.so", // fallback for older for IBM Java + "/jre/bin/classic/libjvm.so" // fallback for older for IBM Java +#endif +#endif + + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +} + +char const* const* OtherInfo::getLibraryPaths(int* size) +{ + +#if defined(UNX) && !defined(MACOSX) + //mac version does not have a ld library path anymore + static char const * ar[] = { + "/bin", + "/jre/bin", + "/bin/classic", + "/jre/bin/classic", + "/lib/" JFW_PLUGIN_ARCH "/client", + "/lib/" JFW_PLUGIN_ARCH "/server", + "/lib/" JFW_PLUGIN_ARCH "/classic", + "/lib/" JFW_PLUGIN_ARCH "/jrockit", + "/lib/" JFW_PLUGIN_ARCH "/native_threads", + ("/lib/" JFW_PLUGIN_ARCH) + }; + + *size = SAL_N_ELEMENTS(ar); + return ar; +#else + *size = 0; + return nullptr; +#endif +} + +int OtherInfo::compareVersions(const OUString& /*sSecond*/) const +{ + //Need to provide an own algorithm for comparing version. + //Because this function returns always 0, which means the version of + //this JRE and the provided version "sSecond" are equal, one cannot put + //any excludeVersion entries in the javavendors.xml file. + return 0; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx new file mode 100644 index 000000000..f722234ef --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/otherjre.hxx @@ -0,0 +1,43 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_OTHERJRE_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_OTHERJRE_HXX + +#include <vendorbase.hxx> + +namespace jfw_plugin +{ +/* Do not forget to put this class in the vendor map in vendorlist.cxx + */ +class OtherInfo : public VendorBase +{ +public: + static char const* const* getJavaExePaths(int* size); + + static rtl::Reference<VendorBase> createInstance(); + + virtual char const* const* getRuntimePaths(int* size) override; + virtual char const* const* getLibraryPaths(int* size) override; + virtual int compareVersions(const OUString& sSecond) const override; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx new file mode 100644 index 000000000..03c38c1de --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx @@ -0,0 +1,900 @@ +/* -*- 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 . + */ + + +#ifdef _WIN32 +# include <sys/stat.h> +# if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include <windows.h> +#endif + +#ifdef ANDROID +# include <dlfcn.h> +#endif + +#include <string.h> + +#include <cstddef> +#include <cassert> +#include <memory> +#include <string_view> +#include <utility> +#include <vector> + +#include <config_options.h> +#include <osl/diagnose.h> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <osl/module.hxx> +#include <osl/mutex.hxx> +#include <osl/process.h> +#include <osl/thread.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <setjmp.h> +#include <signal.h> + +#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 <rtl/byteseq.hxx> +#include <fwkbase.hxx> +#include <elements.hxx> +#include <vendorbase.hxx> +#include <vendorplugin.hxx> +#include <jvmfwk/framework.hxx> +#include "util.hxx" +#include "sunversion.hxx" +#include "diagnostics.h" + +#if defined MACOSX && defined __x86_64__ +#include "util_cocoa.hxx" +#endif + +#ifdef ANDROID +#include <osl/detail/android-bootstrap.h> +#else +#if !ENABLE_RUNTIME_OPTIMIZATIONS +#define FORCE_INTERPRETED 1 +#elif defined HAVE_VALGRIND_HEADERS +#include <valgrind/valgrind.h> +#define FORCE_INTERPRETED RUNNING_ON_VALGRIND +#else +#define FORCE_INTERPRETED 0 +#endif +#endif + +#if defined LINUX && (defined X86 || defined X86_64) +#include <sys/resource.h> +#endif + +using namespace osl; +using namespace jfw_plugin; + + +namespace { + +#if defined(UNX) && !defined(ANDROID) +OString getPluginJarPath( + std::u16string_view sVendor, + std::u16string_view sLocation, + std::u16string_view sVersion) +{ + OString ret; + OUString sName1("javaplugin.jar"); + OUString sName2("plugin.jar"); + OUString sPath; + if ( sVendor == u"Sun Microsystems Inc." ) + { + SunVersion ver142("1.4.2-ea"); + SunVersion ver150("1.5.0-ea"); + SunVersion ver(sVersion); + OSL_ASSERT(ver142 && ver150 && ver); + + OUString sName; + if (ver < ver142) + { + sName = sName1; + } + else if (ver < ver150) + {//this will cause ea, beta etc. to have plugin.jar in path. + //but this does not harm. 1.5.0-beta < 1.5.0 + sName = sName2; + } + if (!sName.isEmpty()) + { + sName = OUString::Concat(sLocation) + "/lib/" + sName; + OSL_VERIFY( + osl_getSystemPathFromFileURL(sName.pData, & sPath.pData) + == osl_File_E_None); + } + } + else + { + OUString sName(OUString::Concat(sLocation) + "/lib/" + sName1); + OUString sPath1; + OUString sPath2; + if (osl_getSystemPathFromFileURL(sName.pData, & sPath1.pData) + == osl_File_E_None) + { + sName = OUString::Concat(sLocation) + "/lib/" + sName2; + if (osl_getSystemPathFromFileURL(sName.pData, & sPath2.pData) + == osl_File_E_None) + { + sPath = sPath1 + OUStringChar(SAL_PATHSEPARATOR) + sPath2; + } + } + OSL_ASSERT(!sPath.isEmpty()); + } + ret = OUStringToOString(sPath, osl_getThreadTextEncoding()); + + return ret; +} +#endif // UNX + + +std::unique_ptr<JavaInfo> createJavaInfo( + const rtl::Reference<VendorBase> & info) +{ + OUStringBuffer buf(1024); + buf.append(info->getRuntimeLibrary()); + if (!info->getLibraryPath().isEmpty()) + { + buf.append("\n"); + buf.append(info->getLibraryPath()); + buf.append("\n"); + } + OUString sVendorData = buf.makeStringAndClear(); + return std::unique_ptr<JavaInfo>( + new JavaInfo{ + info->getVendor(), info->getHome(), info->getVersion(), + sal_uInt64(info->needsRestart() ? JFW_REQUIRE_NEEDRESTART : 0), + rtl::ByteSequence( + reinterpret_cast<sal_Int8*>(sVendorData.pData->buffer), + sVendorData.getLength() * sizeof(sal_Unicode))}); +} + +OUString getRuntimeLib(const rtl::ByteSequence & data) +{ + const sal_Unicode* chars = reinterpret_cast<sal_Unicode const *>(data.getConstArray()); + sal_Int32 len = data.getLength(); + OUString sData(chars, len / 2); + //the runtime lib is on the first line + sal_Int32 index = 0; + OUString aToken = sData.getToken( 0, '\n', index); + + return aToken; +} + +jmp_buf jmp_jvm_abort; +sig_atomic_t g_bInGetJavaVM = 0; + +extern "C" void JNICALL abort_handler() +{ + // If we are within JNI_CreateJavaVM then we jump back into getJavaVM + if( g_bInGetJavaVM != 0 ) + { + SAL_WARN("jfw", "JavaVM: JNI_CreateJavaVM called os::abort(), caught by abort_handler"); + longjmp( jmp_jvm_abort, 0); + } +} + +typedef jint JNICALL JNI_CreateVM_Type(JavaVM **, JNIEnv **, void *); + +#ifndef ANDROID +int createJvm( + JNI_CreateVM_Type * pCreateJavaVM, JavaVM ** pJavaVM, JNIEnv ** ppEnv, JavaVMInitArgs * vm_args) +{ + /* We set a global flag which is used by the abort handler in order to + determine whether it is should use longjmp to get back into this function. + That is, the abort handler determines if it is on the same stack as this function + and then jumps back into this function. + */ + g_bInGetJavaVM = 1; + jint err; + memset( jmp_jvm_abort, 0, sizeof(jmp_jvm_abort)); + /* If the setjmp return value is not "0" then this point was reached by a longjmp in the + abort_handler, which was called indirectly by JNI_CreateVM. + */ + if( setjmp( jmp_jvm_abort ) == 0) + { + //returns negative number on failure + err= pCreateJavaVM(pJavaVM, ppEnv, vm_args); + g_bInGetJavaVM = 0; + } + else + // set err to a positive number, so as or recognize that an abort (longjmp) + //occurred + err= 1; + return err; +} +#endif + +/** helper function to check Java version requirements + + This function checks if the Java version of the given VendorBase + meets the given Java version requirements. + + @param aVendorInfo + [in] the object to be inspected whether it meets the version requirements + @param sMinVersion + [in] represents the minimum version of a JRE. The string can be empty. + @param sMaxVersion + [in] represents the maximum version of a JRE. The string can be empty. + @param arExcludeList + [in] contains a list of "bad" versions. JREs which have one of these + versions must not be returned by this function. + + @return + javaPluginError::NONE the function ran successfully and the version requirements are met + javaPluginError::FailedVersion at least one of the version requirements (minVersion, + maxVersion, excludeVersions) was violated + javaPluginError::WrongVersionFormat the version strings in + <code>sMinVersion,sMaxVersion,arExcludeList</code> are not recognized as valid + version strings. + + */ +javaPluginError checkJavaVersionRequirements( + rtl::Reference<VendorBase> const & aVendorInfo, + OUString const& sMinVersion, + OUString const& sMaxVersion, + std::vector<OUString> const & arExcludeList) +{ + if (!aVendorInfo->isValidArch()) + { + return javaPluginError::WrongArch; + } + if (!sMinVersion.isEmpty()) + { + try + { + if (aVendorInfo->compareVersions(sMinVersion) < 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The minVersion was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sMinVersion + " for vendor: " + aVendorInfo->getVendor() + + " .Check minimum Version." ); + return javaPluginError::WrongVersionFormat; + } + } + + if (!sMaxVersion.isEmpty()) + { + try + { + if (aVendorInfo->compareVersions(sMaxVersion) > 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The maxVersion was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sMaxVersion + " for vendor: " + aVendorInfo->getVendor() + + " .Check maximum Version." ); + return javaPluginError::WrongVersionFormat; + } + } + + for (auto const & sExVer: arExcludeList) { + try + { + if (aVendorInfo->compareVersions(sExVer) == 0) + return javaPluginError::FailedVersion; + } + catch (MalformedVersionException&) + { + //The excluded version was not recognized as valid for this vendor. + JFW_ENSURE( + false, + "[Java framework]sunjavaplugin does not know version: " + + sExVer + " for vendor: " + aVendorInfo->getVendor() + + " .Check excluded versions." ); + return javaPluginError::WrongVersionFormat; + } + } + + return javaPluginError::NONE; +} + +} + +javaPluginError jfw_plugin_getAllJavaInfos( + bool checkJavaHomeAndPath, + jfw::VendorSettings const & vendorSettings, + std::vector<std::unique_ptr<JavaInfo>>* parJavaInfo, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos) +{ + assert(parJavaInfo); + + //Find all JREs + std::vector<rtl::Reference<VendorBase> > vecInfos = + addAllJREInfos(checkJavaHomeAndPath, infos); + std::vector<rtl::Reference<VendorBase> > vecVerifiedInfos; + + for (auto const& vecInfo : vecInfos) + { + auto const versionInfo = vendorSettings.getVersionInformation(vecInfo->getVendor()); + javaPluginError err = checkJavaVersionRequirements( + vecInfo, versionInfo.sMinVersion, versionInfo.sMaxVersion, versionInfo.vecExcludeVersions); + + if (err == javaPluginError::FailedVersion || err == javaPluginError::WrongArch) + continue; + else if (err == javaPluginError::WrongVersionFormat) + return err; + + vecVerifiedInfos.push_back(vecInfo); + } + //Now vecVerifiedInfos contains all those JREs which meet the version requirements + //Transfer them into the array that is passed out. + parJavaInfo->clear(); + for (auto const& vecVerifiedInfo : vecVerifiedInfos) + { + parJavaInfo->push_back(createJavaInfo(vecVerifiedInfo)); + } + + return javaPluginError::NONE; +} + +javaPluginError jfw_plugin_getJavaInfoByPath( + OUString const& sPath, + jfw::VendorSettings const & vendorSettings, + std::unique_ptr<JavaInfo> * ppInfo) +{ + assert(ppInfo != nullptr); + OSL_ASSERT(!sPath.isEmpty()); + if (sPath.isEmpty()) + return javaPluginError::InvalidArg; + + rtl::Reference<VendorBase> aVendorInfo = getJREInfoByPath(sPath); + if (!aVendorInfo.is()) + return javaPluginError::NoJre; + + //Check if the detected JRE matches the version requirements + javaPluginError errorcode = javaPluginError::NONE; + auto const versionInfo = vendorSettings.getVersionInformation(aVendorInfo->getVendor()); + errorcode = checkJavaVersionRequirements( + aVendorInfo, versionInfo.sMinVersion, versionInfo.sMaxVersion, versionInfo.vecExcludeVersions); + + if (errorcode == javaPluginError::NONE) + *ppInfo = createJavaInfo(aVendorInfo); + + return errorcode; +} + +javaPluginError jfw_plugin_getJavaInfoFromJavaHome( + jfw::VendorSettings const & vendorSettings, + std::unique_ptr<JavaInfo> * ppInfo, + std::vector<rtl::Reference<VendorBase>> & infos) +{ + assert(ppInfo); + + std::vector<rtl::Reference<VendorBase>> infoJavaHome; + addJavaInfoFromJavaHome(infos, infoJavaHome); + + if (infoJavaHome.empty()) + return javaPluginError::NoJre; + assert(infoJavaHome.size() == 1); + + //Check if the detected JRE matches the version requirements + auto const versionInfo = vendorSettings.getVersionInformation(infoJavaHome[0]->getVendor()); + if (checkJavaVersionRequirements( + infoJavaHome[0], + versionInfo.sMinVersion, + versionInfo.sMaxVersion, + versionInfo.vecExcludeVersions) + == javaPluginError::NONE) + { + *ppInfo = createJavaInfo(infoJavaHome[0]); + return javaPluginError::NONE; + } + + return javaPluginError::NoJre; +} + +javaPluginError jfw_plugin_getJavaInfosFromPath( + jfw::VendorSettings const & vendorSettings, + std::vector<std::unique_ptr<JavaInfo>> & javaInfosFromPath, + std::vector<rtl::Reference<jfw_plugin::VendorBase>> & infos) +{ + // find JREs from PATH + std::vector<rtl::Reference<VendorBase>> vecInfosFromPath; + addJavaInfosFromPath(infos, vecInfosFromPath); + + std::vector<std::unique_ptr<JavaInfo>> vecVerifiedInfos; + + // copy infos of JREs that meet version requirements to vecVerifiedInfos + for (auto const& infosFromPath : vecInfosFromPath) + { + auto const versionInfo = vendorSettings.getVersionInformation(infosFromPath->getVendor()); + if (checkJavaVersionRequirements( + infosFromPath, + versionInfo.sMinVersion, + versionInfo.sMaxVersion, + versionInfo.vecExcludeVersions) + == javaPluginError::NONE) + { + vecVerifiedInfos.push_back(createJavaInfo(infosFromPath)); + } + } + + if (vecVerifiedInfos.empty()) + return javaPluginError::NoJre; + + javaInfosFromPath = std::move(vecVerifiedInfos); + + return javaPluginError::NONE; +} + + +#if defined(_WIN32) + +// Load msvcr71.dll using an explicit full path from where it is +// present as bundled with the JRE. In case it is not found where we +// think it should be, do nothing, and just let the implicit loading +// that happens when loading the JVM take care of it. + +static void load_msvcr(std::u16string_view jvm_dll, std::u16string_view msvcr) +{ + // First check if msvcr71.dll is in the same folder as jvm.dll. It + // normally isn't, at least up to 1.6.0_22, but who knows if it + // might be in the future. + std::size_t slash = jvm_dll.rfind('\\'); + + if (slash == std::u16string_view::npos) + { + // Huh, weird path to jvm.dll. Oh well. + SAL_WARN("jfw", "JVM pathname <" << OUString(jvm_dll) << "> w/o backslash"); + return; + } + + if (LoadLibraryW( + o3tl::toW(OUString(OUString::Concat(jvm_dll.substr(0, slash+1)) + msvcr).getStr()))) + return; + + // Then check if msvcr71.dll is in the parent folder of where + // jvm.dll is. That is currently (1.6.0_22) as far as I know the + // normal case. + slash = jvm_dll.rfind('\\', slash); + + if (slash == std::u16string_view::npos) + return; + + (void)LoadLibraryW( + o3tl::toW(OUString(OUString::Concat(jvm_dll.substr(0, slash+1)) + msvcr).getStr())); +} + +// Check if the jvm DLL imports msvcr71.dll, and in that case try +// loading it explicitly. In case something goes wrong, do nothing, +// and just let the implicit loading try to take care of it. +static void do_msvcr_magic(OUString const &jvm_dll) +{ + struct stat st; + + OUString Module; + osl::FileBase::RC nError = osl::FileBase::getSystemPathFromFileURL( + jvm_dll, Module); + + if ( osl::FileBase::E_None != nError ) + { + SAL_WARN( + "jfw", "getSystemPathFromFileURL(" << jvm_dll << "): " << +nError); + return; + } + + FILE *f = _wfopen(o3tl::toW(Module.getStr()), L"rb"); + + if (!f) + { + SAL_WARN("jfw", "_wfopen(" << Module << ") failed"); + return; + } + + if (fstat(fileno(f), &st) == -1) + { + SAL_WARN("jfw", "fstat(" << Module << ") failed"); + fclose(f); + return; + } + + PIMAGE_DOS_HEADER dos_hdr = static_cast<PIMAGE_DOS_HEADER>(malloc(st.st_size)); + assert(dos_hdr); + if (fread(dos_hdr, st.st_size, 1, f) != 1 || + memcmp(dos_hdr, "MZ", 2) != 0 || + dos_hdr->e_lfanew < 0 || + dos_hdr->e_lfanew > static_cast<LONG>(st.st_size - sizeof(IMAGE_NT_HEADERS))) + { + SAL_WARN("jfw", "analyzing <" << Module << "> failed"); + free(dos_hdr); + fclose(f); + return; + } + + fclose(f); + + IMAGE_NT_HEADERS *nt_hdr = reinterpret_cast<IMAGE_NT_HEADERS *>(reinterpret_cast<char *>(dos_hdr) + dos_hdr->e_lfanew); + + DWORD importsVA = nt_hdr->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + // first determine Virtual-to-File-address mapping for the section + // that contains the import directory + IMAGE_SECTION_HEADER *sections = IMAGE_FIRST_SECTION(nt_hdr); + ptrdiff_t VAtoPhys = -1; + for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; ++i) + { + if (sections->VirtualAddress <= importsVA && + importsVA < sections->VirtualAddress + sections->SizeOfRawData) + { + VAtoPhys = static_cast<size_t>(sections->PointerToRawData) - static_cast<size_t>(sections->VirtualAddress); + break; + } + ++sections; + } + if (-1 == VAtoPhys) // not found? + { + SAL_WARN("jfw", "analyzing <" << Module << "> failed"); + free(dos_hdr); + return; + } + IMAGE_IMPORT_DESCRIPTOR *imports = + reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(reinterpret_cast<char *>(dos_hdr) + importsVA + VAtoPhys); + + while (imports <= reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(reinterpret_cast<char *>(dos_hdr) + st.st_size - sizeof (IMAGE_IMPORT_DESCRIPTOR)) && + imports->Name != 0 && + imports->Name + VAtoPhys < static_cast<DWORD>(st.st_size)) + { + static std::u16string_view msvcrts[] = + { + u"msvcr71.dll", + u"msvcr100.dll" + }; + char const* importName = reinterpret_cast<char *>(dos_hdr) + imports->Name + VAtoPhys; + sal_Int32 importNameLen = rtl_str_getLength(importName); + for (size_t i = 0; i < SAL_N_ELEMENTS(msvcrts); ++i) + { + if (0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( + msvcrts[i].data(), msvcrts[i].size(), + importName, importNameLen)) + { + load_msvcr(Module, msvcrts[i]); + free(dos_hdr); + return; + } + } + imports++; + } + + free(dos_hdr); +} + +#endif + +/** starts a Java Virtual Machine. + <p> + The function shall ensure, that the VM does not abort the process + during instantiation. + </p> + */ +javaPluginError jfw_plugin_startJavaVirtualMachine( + const JavaInfo *pInfo, + const JavaVMOption* arOptions, + sal_Int32 cOptions, + JavaVM ** ppVm, + JNIEnv ** ppEnv) +{ + static osl::Mutex aPluginMutex; + assert(pInfo != nullptr); + assert(ppVm != nullptr); + assert(ppEnv != nullptr); + osl::MutexGuard guard(aPluginMutex); + javaPluginError errorcode = javaPluginError::NONE; +#ifdef MACOSX + rtl::Reference<VendorBase> aVendorInfo = getJREInfoByPath( pInfo->sLocation ); + if ( !aVendorInfo.is() || aVendorInfo->compareVersions( pInfo->sVersion ) < 0 ) + return javaPluginError::VmCreationFailed; +#endif + OUString sRuntimeLib = getRuntimeLib(pInfo->arVendorData); +#if defined MACOSX && defined __x86_64__ + if ( !JvmfwkUtil_isLoadableJVM( sRuntimeLib ) ) + return javaPluginError::VmCreationFailed; +#endif + JFW_TRACE2("Using Java runtime library: " << sRuntimeLib); + +#ifndef ANDROID + // On linux we load jvm with RTLD_GLOBAL. This is necessary for debugging, because + // libjdwp.so need a symbol (fork1) from libjvm which it only gets if the jvm is loaded + // with RTLD_GLOBAL. On Solaris libjdwp.so is correctly linked with libjvm.so + osl::Module moduleRt; +#if defined(LINUX) + if (!moduleRt.load(sRuntimeLib, SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_NOW)) +#elif defined MACOSX + // Must be SAL_LOADMODULE_GLOBAL when e.g. specifying a + // -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 option to + // JDK 1.8.0_121 at least, as JNI_CreateJavaVM -> Threads::create_vm -> + // JvmtiExport::post_vm_initialized -> cbEarlyVMInit -> initialize -> + // util_initialize -> sun.misc.VMSupport.getAgentProperties -> + // Java_sun_misc_VMSupport_initAgentProperties -> + // JDK_FindJvmEntry("JVM_INitAgentProperties") -> + // dlsym(RTLD_DEFAULT, "JVM_INitAgentProperties"): + if (!moduleRt.load(sRuntimeLib, SAL_LOADMODULE_GLOBAL)) +#else +#if defined(_WIN32) + do_msvcr_magic(sRuntimeLib); +#endif + if (!moduleRt.load(sRuntimeLib)) +#endif + { + JFW_ENSURE(false, + "[Java framework]sunjavaplugin" SAL_DLLEXTENSION + " could not load Java runtime library: \n" + + sRuntimeLib + "\n"); + JFW_TRACE0("Could not load Java runtime library: " << sRuntimeLib); + return javaPluginError::VmCreationFailed; + } + +#if defined UNX && !defined MACOSX + //Setting the JAVA_HOME is needed for awt + OUString sPathLocation; + osl::FileBase::getSystemPathFromFileURL(pInfo->sLocation, sPathLocation); + osl_setEnvironment(OUString("JAVA_HOME").pData, sPathLocation.pData); +#endif + + OUString sSymbolCreateJava("JNI_CreateJavaVM"); + + JNI_CreateVM_Type * pCreateJavaVM = + reinterpret_cast<JNI_CreateVM_Type *>(moduleRt.getFunctionSymbol(sSymbolCreateJava)); + if (!pCreateJavaVM) + { + OSL_ASSERT(false); + OString sLib = OUStringToOString( + sRuntimeLib, osl_getThreadTextEncoding()); + OString sSymbol = OUStringToOString( + sSymbolCreateJava, osl_getThreadTextEncoding()); + SAL_WARN("jfw", "Java runtime library: " << sLib << " does not export symbol " << sSymbol); + return javaPluginError::VmCreationFailed; + } + moduleRt.release(); + + // Valgrind typically emits many false errors when executing JIT'ed JVM + // code, so force the JVM into interpreted mode: + bool addForceInterpreted = FORCE_INTERPRETED > 0; + + // Some testing with Java 1.4 showed that JavaVMOption.optionString has to + // be encoded with the system encoding (i.e., osl_getThreadTextEncoding): + JavaVMInitArgs vm_args; + + struct Option { + Option(OString const & theOptionString, void * theExtraInfo): + optionString(theOptionString), extraInfo(theExtraInfo) + {} + + OString optionString; + void * extraInfo; + }; + std::vector<Option> options; + + // We set an abort handler which is called when the VM calls _exit during + // JNI_CreateJavaVM. This happens when the LD_LIBRARY_PATH does not contain + // all some directories of the Java installation. This is necessary for + // all versions below 1.5.1 + options.emplace_back("abort", reinterpret_cast<void*>(abort_handler)); + bool hasStackSize = false; +#ifdef UNX + // Until java 1.5 we need to put a plugin.jar or javaplugin.jar (<1.4.2) + // in the class path in order to have applet support: + OString sAddPath = getPluginJarPath(pInfo->sVendor, pInfo->sLocation,pInfo->sVersion); +#endif + for (int i = 0; i < cOptions; i++) + { + OString opt(arOptions[i].optionString); +#ifdef UNX + if (opt.startsWith("-Djava.class.path=")) + { + if (!sAddPath.isEmpty()) + { + opt += OStringChar(SAL_PATHSEPARATOR) + sAddPath; + sAddPath.clear(); + } + } +#endif + if (opt == "-Xint") { + addForceInterpreted = false; + } + if (opt.startsWith("-Xss")) { + hasStackSize = true; + } + options.emplace_back(opt, arOptions[i].extraInfo); + } + if (addForceInterpreted) { + options.emplace_back("-Xint", nullptr); + } + if (!hasStackSize) { +#if defined LINUX && (defined X86 || defined X86_64) + // At least OpenJDK 1.8.0's os::workaround_expand_exec_shield_cs_limit + // (hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp) can mmap an rwx + // page into the area that the main stack can grow down to according to + // "ulimit -s", as os::init_2's (hotspot/src/os/linux/vm/os_linux.cpp) + // call to + // + // Linux::capture_initial_stack(JavaThread::stack_size_at_create()); + // + // caps _initial_thread_stack_size at threadStackSizeInBytes ,i.e., + // -Xss, which appears to default to only 327680, whereas "ulimit -s" + // defaults to 8192 * 1024 at least on Fedora 20; so attempt to pass in + // a useful -Xss argument: + rlimit l; + if (getrlimit(RLIMIT_STACK, &l) == 0) { + if (l.rlim_cur == RLIM_INFINITY) { + SAL_INFO("jfw", "RLIMIT_STACK RLIM_INFINITY -> 8192K"); + l.rlim_cur = 8192 * 1024; + } else if (l.rlim_cur > 512 * 1024 * 1024) { + SAL_INFO( + "jfw", "huge RLIMIT_STACK " << l.rlim_cur << " -> 8192K"); + l.rlim_cur = 8192 * 1024; + } + options.emplace_back("-Xss" + OString::number(l.rlim_cur), nullptr); + } else { + int e = errno; + SAL_WARN("jfw", "getrlimit(RLIMIT_STACK) failed with errno " << e); + } +#endif + } +#ifdef UNX + if (!sAddPath.isEmpty()) { + options.emplace_back("-Djava.class.path=" + sAddPath, nullptr); + } +#endif + + std::unique_ptr<JavaVMOption[]> sarOptions(new JavaVMOption[options.size()]); + for (std::vector<Option>::size_type i = 0; i != options.size(); ++i) { + SAL_INFO( + "jfw", + "VM option \"" << options[i].optionString << "\" " + << options[i].extraInfo); + sarOptions[i].optionString = const_cast<char *>( + options[i].optionString.getStr()); + sarOptions[i].extraInfo = options[i].extraInfo; + } + +#ifdef MACOSX + vm_args.version= JNI_VERSION_1_4; // issue 88987 +#else + vm_args.version= JNI_VERSION_1_2; +#endif + vm_args.options= sarOptions.get(); + vm_args.nOptions= options.size(); //TODO overflow + vm_args.ignoreUnrecognized= JNI_TRUE; + + JavaVM * pJavaVM = nullptr; + jint err = createJvm(pCreateJavaVM, &pJavaVM, ppEnv, &vm_args); + + if(err != 0) + { + if( err < 0) + { + SAL_WARN("jfw", "Can not create Java Virtual Machine, " << err); + errorcode = javaPluginError::VmCreationFailed; + } + else if( err > 0) + { + SAL_WARN("jfw", "Can not create JavaVirtualMachine, abort handler was called"); + errorcode = javaPluginError::VmCreationFailed; + } + } + else + { + *ppVm = pJavaVM; + JFW_TRACE2("JVM created"); + } +#else + (void) arOptions; + (void) cOptions; + (void) ppEnv; + // On Android we always have a Java VM as we only expect this code + // to be run in an Android app anyway. + *ppVm = lo_get_javavm(); +#endif + + return errorcode; +} + +javaPluginError jfw_plugin_existJRE(const JavaInfo *pInfo, bool *exist) +{ + assert(pInfo != nullptr); + assert(exist != nullptr); + + javaPluginError ret = javaPluginError::NONE; + OUString sLocation(pInfo->sLocation); + + if (sLocation.isEmpty()) + return javaPluginError::InvalidArg; + ::osl::DirectoryItem item; + ::osl::File::RC rc_item = ::osl::DirectoryItem::get(sLocation, item); + if (::osl::File::E_None == rc_item) + { + *exist = true; + } + else if (::osl::File::E_NOENT == rc_item) + { + *exist = false; + } + else + { + ret = javaPluginError::Error; + } + //We can have the situation that the JavaVM runtime library is not + //contained within JAVA_HOME. Then the check for JAVA_HOME would return + //true although the runtime library may not be loadable. + //Or the JAVA_HOME directory of a deinstalled JRE left behind. + if (ret == javaPluginError::NONE && *exist) + { + OUString sRuntimeLib = getRuntimeLib(pInfo->arVendorData); + JFW_TRACE2("Checking existence of Java runtime library"); + + ::osl::DirectoryItem itemRt; + ::osl::File::RC rc_itemRt = ::osl::DirectoryItem::get(sRuntimeLib, itemRt); + if (::osl::File::E_None == rc_itemRt) + { + *exist = true; + JFW_TRACE2("Java runtime library exist: " << sRuntimeLib); + + // Check version + rtl::Reference<VendorBase> aVendorInfo = getJREInfoByPath(sLocation); + if (!aVendorInfo.is()) + { + *exist = false; + JFW_TRACE2("JRE or supported vendor not accessible at location: " << sLocation); + } + else if(pInfo->sVersion!=aVendorInfo->getVersion()) + { + *exist = false; + JFW_TRACE2("Mismatch between version number in libreoffice settings and installed JRE: " << pInfo->sVersion <<" != " << aVendorInfo->getVersion()); + } + } + else if (::osl::File::E_NOENT == rc_itemRt) + { + *exist = false; + JFW_TRACE2("Java runtime library does not exist: " << sRuntimeLib); + } + else + { + ret = javaPluginError::Error; + JFW_TRACE2("Error while looking for Java runtime library: " << sRuntimeLib); + } + } + return ret; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx new file mode 100644 index 000000000..a0f8cbee1 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.cxx @@ -0,0 +1,116 @@ +/* -*- 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 "sunjre.hxx" +#include "sunversion.hxx" +#include "diagnostics.h" + +namespace jfw_plugin +{ + +rtl::Reference<VendorBase> SunInfo::createInstance() +{ + return new SunInfo; +} + +char const* const* SunInfo::getJavaExePaths(int * size) +{ + static char const * ar[] = { +#if defined(_WIN32) + "java.exe", + "bin/java.exe", + "jre/bin/java.exe" +#elif defined UNX + "java", + "bin/java", + "jre/bin/java" +#endif + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +} + +char const* const* SunInfo::getRuntimePaths(int * size) +{ + static char const* ar[]= { +#if defined(_WIN32) + "/bin/client/jvm.dll", + "/bin/hotspot/jvm.dll", + "/bin/classic/jvm.dll", + "/bin/jrockit/jvm.dll", + // The 64-bit JRE has the jvm in bin/server + "/bin/server/jvm.dll" +#elif defined MACOSX + // Oracle Java 7, under /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home + "/lib/server/libjvm.dylib", + "/lib/jli/libjli.dylib" +#elif defined UNX + "/lib/" JFW_PLUGIN_ARCH "/client/libjvm.so", + "/lib/" JFW_PLUGIN_ARCH "/server/libjvm.so", + "/lib/" JFW_PLUGIN_ARCH "/classic/libjvm.so", + "/lib/" JFW_PLUGIN_ARCH "/jrockit/libjvm.so", + "/lib/server/libjvm.so" +#endif + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +} + +char const* const* SunInfo::getLibraryPaths(int* size) +{ +#if defined UNX && !defined MACOSX + static char const * ar[] = { + "/lib/" JFW_PLUGIN_ARCH "/client", + "/lib/" JFW_PLUGIN_ARCH "/server", + "/lib/" JFW_PLUGIN_ARCH "/native_threads", + ("/lib/" JFW_PLUGIN_ARCH) + }; + *size = SAL_N_ELEMENTS(ar); + return ar; +#else + *size = 0; + return nullptr; +#endif +} + +int SunInfo::compareVersions(const OUString& sSecond) const +{ + OUString sFirst = getVersion(); + + SunVersion version1(sFirst); + JFW_ENSURE(version1, "[Java framework] sunjavaplugin" SAL_DLLEXTENSION + " does not know the version: " + + sFirst + " as valid for a SUN/Oracle JRE."); + SunVersion version2(sSecond); + if ( ! version2) + throw MalformedVersionException(); + + if (version1 == version2) + return 0; + if (version1 > version2) + return 1; + else + return -1; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx new file mode 100644 index 000000000..773f40080 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjre.hxx @@ -0,0 +1,42 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNJRE_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNJRE_HXX + +#include <vendorbase.hxx> + +namespace jfw_plugin +{ +class SunInfo : public VendorBase +{ +public: + static char const* const* getJavaExePaths(int* size); + + static rtl::Reference<VendorBase> createInstance(); + + virtual char const* const* getRuntimePaths(int* size) override; + virtual char const* const* getLibraryPaths(int* size) override; + + virtual int compareVersions(const OUString& sSecond) const override; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx new file mode 100644 index 000000000..16a1e14f3 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.cxx @@ -0,0 +1,303 @@ +/* -*- 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 "sunversion.hxx" +#include <osl/thread.h> +#include <rtl/character.hxx> +#include <rtl/ustring.hxx> +#include <string.h> +namespace jfw_plugin { //stoc_javadetect + + +SunVersion::SunVersion(std::u16string_view usVer): + m_nUpdateSpecial(0), m_preRelease(Rel_NONE) +{ + OString sVersion= OUStringToOString(usVer, osl_getThreadTextEncoding()); + m_bValid = init(sVersion.getStr()); +} +SunVersion::SunVersion(const char * szVer): + m_nUpdateSpecial(0), m_preRelease(Rel_NONE) +{ + m_bValid = init(szVer); +} + + +/**Format major.minor.maintenance_update + */ +bool SunVersion::init(const char *szVersion) +{ + if (!szVersion || szVersion[0] == '\0') + return false; + + //first get the major,minor,maintenance + const char * pLast = szVersion; + const char * pCur = szVersion; + //pEnd point to the position after the last character + const char * pEnd = szVersion + strlen(szVersion); + // 0 = major, 1 = minor, 2 = maintenance, 3 = update + int nPart = 0; + // position within part beginning with 0 + int nPartPos = 0; + char buf[128]; + + //char must me a number 0 - 999 and no leading + while (true) + { + if (pCur < pEnd && rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) + { + pCur ++; + nPartPos ++; + } + //if correct separator then form integer + else if ( + (nPartPos != 0) // prevents: ".4.1", "..1", part must start with digit + && ( + //separators after maintenance (1.4.1_01, 1.4.1-beta, or 1.4.1) + (pCur == pEnd || *pCur == '_' || *pCur == '-') + || + //separators between major-minor and minor-maintenance (or fourth segment) + (nPart < 3 && *pCur == '.') ) + && ( + //prevent 1.4.0. 1.4.0- + pCur + 1 != pEnd + || rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) ) + { + bool afterMaint = pCur == pEnd || *pCur == '_' || *pCur == '-'; + + int len = pCur - pLast; + if (len >= 127) + return false; + + strncpy(buf, pLast, len); + buf[len] = 0; + pCur ++; + pLast = pCur; + + m_arVersionParts[nPart] = atoi(buf); + + if (afterMaint) + nPart = 2; + nPart ++; + nPartPos = 0; + if (nPart == 3) + break; + + //check next character + if (! ( (pCur < pEnd) + && ( (nPart < 3) + && rtl::isAsciiDigit( + static_cast<unsigned char>(*pCur))))) + return false; + } + else + { + return false; + } + } + if (pCur >= pEnd) + return true; + //We have now 1.4.1. This can be followed by _01 (or a fourth segment .1), -beta, etc. + // _01 (update) According to docu must not be followed by any other + //characters, but on Solaris 9 we have a 1.4.1_01a!! + if (* (pCur - 1) == '_' || *(pCur - 1) == '.') + {// _01, _02 + // update is the last part _01, _01a, part 0 is the digits parts and 1 the trailing alpha + while (true) + { + if (pCur <= pEnd) + { + if ( ! rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) + { + //1.8.0_102-, 1.8.0_01a, + size_t len = pCur - pLast; + if (len > sizeof(buf) - 1) + return false; + //we've got the update: 01, 02 etc + strncpy(buf, pLast, len); + buf[len] = 0; + m_arVersionParts[nPart] = atoi(buf); + if (pCur == pEnd) + { + break; + } + if (*pCur == 'a' && (pCur + 1) == pEnd) + { + //check if it s followed by a simple "a" (not specified) + m_nUpdateSpecial = *pCur; + break; + } + else if (*pCur == '-' && pCur < pEnd) + { + //check 1.5.0_01-ea + PreRelease pr = getPreRelease(++pCur); + if (pr == Rel_NONE) + return false; + //just ignore -ea because its no official release + break; + } + else + { + return false; + } + } + if (pCur < pEnd) + pCur ++; + else + break; + } + } + } + // 1.4.1-ea + else if (*(pCur - 1) == '-') + { + m_preRelease = getPreRelease(pCur); + if (m_preRelease == Rel_NONE) + return false; +#if defined(FREEBSD) + if (m_preRelease == Rel_FreeBSD) + { + pCur++; //eliminate 'p' + if (pCur < pEnd + && rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) + pCur ++; + int len = pCur - pLast -1; //eliminate 'p' + if (len >= 127) + return false; + strncpy(buf, (pLast+1), len); //eliminate 'p' + buf[len] = 0; + m_nUpdateSpecial = atoi(buf)+100; //hack for FBSD #i56953# + return true; + } +#endif + } + else + { + return false; + } + return true; +} + +SunVersion::PreRelease SunVersion::getPreRelease(const char *szRelease) +{ + if (szRelease == nullptr) + return Rel_NONE; + if( ! strcmp(szRelease,"internal")) + return Rel_INTERNAL; + else if( ! strcmp(szRelease,"ea")) + return Rel_EA; + else if( ! strcmp(szRelease,"ea1")) + return Rel_EA1; + else if( ! strcmp(szRelease,"ea2")) + return Rel_EA2; + else if( ! strcmp(szRelease,"ea3")) + return Rel_EA3; + else if ( ! strcmp(szRelease,"beta")) + return Rel_BETA; + else if ( ! strcmp(szRelease,"beta1")) + return Rel_BETA1; + else if ( ! strcmp(szRelease,"beta2")) + return Rel_BETA2; + else if ( ! strcmp(szRelease,"beta3")) + return Rel_BETA3; + else if (! strcmp(szRelease, "rc")) + return Rel_RC; + else if (! strcmp(szRelease, "rc1")) + return Rel_RC1; + else if (! strcmp(szRelease, "rc2")) + return Rel_RC2; + else if (! strcmp(szRelease, "rc3")) + return Rel_RC3; +#if defined (FREEBSD) + else if (! strncmp(szRelease, "p", 1)) + return Rel_FreeBSD; +#endif + else + return Rel_NONE; +} + +/* Examples: + a) 1.0 < 1.1 + b) 1.0 < 1.0.0 + c) 1.0 < 1.0_00 + + returns false if both values are equal +*/ +bool SunVersion::operator > (const SunVersion& ver) const +{ + if( &ver == this) + return false; + + //compare major.minor.maintenance + for( int i= 0; i < 4; i ++) + { + // 1.4 > 1.3 + if(m_arVersionParts[i] > ver.m_arVersionParts[i]) + { + return true; + } + else if (m_arVersionParts[i] < ver.m_arVersionParts[i]) + { + return false; + } + } + //major.minor.maintenance_update are equal. Test for a trailing char + if (m_nUpdateSpecial > ver.m_nUpdateSpecial) + { + return true; + } + + //Until here the versions are equal + //compare pre -release values + if ((m_preRelease == Rel_NONE && ver.m_preRelease == Rel_NONE) + || + (m_preRelease != Rel_NONE && ver.m_preRelease == Rel_NONE)) + return false; + else if (m_preRelease == Rel_NONE && ver.m_preRelease != Rel_NONE) + return true; + else if (m_preRelease > ver.m_preRelease) + return true; + + return false; +} + +bool SunVersion::operator < (const SunVersion& ver) const +{ + return (! operator > (ver)) && (! operator == (ver)); +} + +bool SunVersion::operator == (const SunVersion& ver) const +{ + bool bRet= true; + for(int i= 0; i < 4; i++) + { + if( m_arVersionParts[i] != ver.m_arVersionParts[i]) + { + bRet= false; + break; + } + } + bRet = m_nUpdateSpecial == ver.m_nUpdateSpecial && bRet; + bRet = m_preRelease == ver.m_preRelease && bRet; + return bRet; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx new file mode 100644 index 000000000..58173b74f --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx @@ -0,0 +1,115 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNVERSION_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNVERSION_HXX + +#include <sal/config.h> + +#include <string_view> + +namespace jfw_plugin +{ +/* SunVersion is used to compare java versions based on a string, as taken + from the registry. The strings look like "1.3", "1.3.1", "1.3.1_02" etc. + Versions such as "1.4.1_01a" are allowed although this is not specified. + 1.4.1_01 < 1.4.1_01a < 1.4.1_01b < 1.4.1_02 + Pre - release versions, such as 1.4.1-ea, 1.4.1-beta, 1.4.1-rc are recognized, + but are treated as minor to release versions: + 1.4.0 > 1.4.2-beta + Pre releases relate this way + 1.4.1-ea < 1.4.1-beta < 1.4.1-rc1 + + This class supports also a FreeBSD Java. This is currently necessary because + it also has the vendor string "Sun Microsystems Inc.". + + An object acts as holder for the version string. That string may be present + even if the version could not be parsed. Then the version may not be compatible + to a SUN Java version. + + An invalid object, that is, operator bool returns false, will always be + the lower version in a comparison. If two invalid objects are compared + then they are considered equal. + + To test if the version is ok, that is this object can be compared to others, + use the bool conversion operator. + */ +class SunVersion final +{ + enum PreRelease + { + Rel_NONE, + Rel_INTERNAL, + Rel_EA, + Rel_EA1, + Rel_EA2, + Rel_EA3, + Rel_BETA, + Rel_BETA1, + Rel_BETA2, + Rel_BETA3, + Rel_RC, + Rel_RC1, + Rel_RC2, + Rel_RC3 +#if defined(FREEBSD) + , + Rel_FreeBSD +#endif + }; + + //contains major,minor,micro,update + int m_arVersionParts[4] = {}; + // The update can be followed by a char, e.g. 1.4.1_01a + char m_nUpdateSpecial; + + PreRelease m_preRelease; + +public: + explicit SunVersion(const char* szVer); + explicit SunVersion(std::u16string_view usVer); + + /** + Pre-release versions are taken into account. + 1.5.0-beta > 1.5.0-ea > 1.4.2 + */ + bool operator>(const SunVersion& ver) const; + bool operator<(const SunVersion& ver) const; + bool operator==(const SunVersion& ver) const; + + /** Test if the version is compatible tu SUN's versioning scheme + */ + operator bool() { return m_bValid; } + +private: + bool init(const char* szVer); + + bool m_bValid; + + /* Determines if a string constitutes a pre release. For example, if + "ea" is passed then Rel_EA is returned. If the string is no pre release + then Rel_NONE is returned. + */ + static PreRelease getPreRelease(const char* szRel); +}; +} + +#endif // INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_SUNVERSION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util.cxx b/jvmfwk/plugins/sunmajor/pluginlib/util.cxx new file mode 100644 index 000000000..6eb96d731 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util.cxx @@ -0,0 +1,1281 @@ +/* -*- 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 <config_folders.h> + +#include "util.hxx" + +#include <osl/process.h> +#include <osl/file.hxx> +#include <osl/module.hxx> +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <salhelper/linkhelper.hxx> +#include <salhelper/thread.hxx> +#include <o3tl/string_view.hxx> +#include <memory> +#include <utility> +#include <algorithm> +#include <map> +#include <string_view> + +#if defined(_WIN32) +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#endif +#include <string.h> + +#include "sunjre.hxx" +#include "vendorlist.hxx" +#include "diagnostics.h" +#if defined MACOSX && defined __x86_64__ +#include "util_cocoa.hxx" +#endif + +using namespace osl; + +using ::rtl::Reference; + +#ifdef _WIN32 +#define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment" +#define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit" +#endif + +#if defined( UNX ) && !defined( MACOSX ) +namespace { +char const *g_arJavaNames[] = { + "", + "j2re", + "j2se", + "j2sdk", + "jdk", + "jre", + "java" +}; + +/* These are directory names which could contain multiple java installations. + */ +char const *g_arCollectDirs[] = { + "", +#ifndef JVM_ONE_PATH_CHECK + "j2re/", + "j2se/", + "j2sdk/", + "jdk/", + "jre/", + "java/", +#endif + "jvm/" +}; + +/* These are directories in which a java installation is + looked for. +*/ +char const *g_arSearchPaths[] = { +#ifndef JVM_ONE_PATH_CHECK + "", + "usr/", + "usr/local/", +#ifdef X86_64 + "usr/lib64/", +#endif + "usr/lib/", + "usr/bin/" +#else + JVM_ONE_PATH_CHECK +#endif +}; +} +#endif // UNX && !MACOSX + +namespace jfw_plugin +{ +#if defined(_WIN32) +static bool getSDKInfoFromRegistry(std::vector<OUString> & vecHome); +static bool getJREInfoFromRegistry(std::vector<OUString>& vecJavaHome); +#endif + +static bool decodeOutput(std::string_view s, OUString* out); + + +namespace +{ + +bool addJREInfo( + rtl::Reference<VendorBase> const & info, + std::vector<rtl::Reference<VendorBase>> & infos) +{ + if (std::none_of(infos.begin(), infos.end(), InfoFindSame(info->getHome()))) { + infos.push_back(info); + return true; + } else { + return false; + } +} + +bool getAndAddJREInfoByPath( + const OUString& path, + std::vector<rtl::Reference<VendorBase> > & allInfos, + std::vector<rtl::Reference<VendorBase> > & addedInfos) +{ + rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path); + if (aInfo.is()) { + if (addJREInfo(aInfo, allInfos)) { + addedInfos.push_back(aInfo); + } + return true; + } else { + return false; + } +} + +} + +namespace { + +class FileHandleGuard +{ +public: + explicit FileHandleGuard(oslFileHandle & rHandle): + m_rHandle(rHandle) {} + + inline ~FileHandleGuard(); + + FileHandleGuard(const FileHandleGuard&) = delete; + FileHandleGuard& operator=(const FileHandleGuard&) = delete; + + oslFileHandle & getHandle() { return m_rHandle; } + +private: + oslFileHandle & m_rHandle; +}; + +} + +inline FileHandleGuard::~FileHandleGuard() +{ + if (m_rHandle != nullptr) + { + if (osl_closeFile(m_rHandle) != osl_File_E_None) + { + OSL_FAIL("unexpected situation"); + } + } +} + +namespace { + +class FileHandleReader +{ +public: + enum Result + { + RESULT_OK, + RESULT_EOF, + RESULT_ERROR + }; + + explicit FileHandleReader(oslFileHandle & rHandle): + m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {} + + Result readLine(OString * pLine); + +private: + enum { BUFFER_SIZE = 1024 }; + + char m_aBuffer[BUFFER_SIZE]; + FileHandleGuard m_aGuard; + int m_nSize; + int m_nIndex; + bool m_bLf; +}; + +} + +FileHandleReader::Result +FileHandleReader::readLine(OString * pLine) +{ + OSL_ENSURE(pLine, "specification violation"); + + for (bool bEof = true;; bEof = false) + { + if (m_nIndex == m_nSize) + { + sal_uInt64 nRead = 0; + switch (osl_readFile( + m_aGuard.getHandle(), m_aBuffer, sizeof(m_aBuffer), &nRead)) + { + case osl_File_E_PIPE: //HACK! for windows + nRead = 0; + [[fallthrough]]; + case osl_File_E_None: + if (nRead == 0) + { + m_bLf = false; + return bEof ? RESULT_EOF : RESULT_OK; + } + m_nIndex = 0; + m_nSize = static_cast< int >(nRead); + break; + case osl_File_E_INTR: + continue; + + default: + return RESULT_ERROR; + } + } + + if (m_bLf && m_aBuffer[m_nIndex] == 0x0A) + ++m_nIndex; + m_bLf = false; + + int nStart = m_nIndex; + while (m_nIndex != m_nSize) + switch (m_aBuffer[m_nIndex++]) + { + case 0x0D: + m_bLf = true; + [[fallthrough]]; + case 0x0A: + *pLine += std::string_view(m_aBuffer + nStart, + m_nIndex - 1 - nStart); + //TODO! check for overflow, and not very efficient + return RESULT_OK; + } + + *pLine += std::string_view(m_aBuffer + nStart, m_nIndex - nStart); + //TODO! check for overflow, and not very efficient + } +} + +namespace { + +class AsynchReader: public salhelper::Thread +{ + size_t m_nDataSize; + std::unique_ptr<char[]> m_arData; + + FileHandleGuard m_aGuard; + + virtual ~AsynchReader() override {} + + void execute() override; +public: + + explicit AsynchReader(oslFileHandle & rHandle); + + /** only call this function after this thread has finished. + + That is, call join on this instance and then call getData. + + */ + OString getData(); +}; + +} + +AsynchReader::AsynchReader(oslFileHandle & rHandle): + Thread("jvmfwkAsyncReader"), m_nDataSize(0), + m_aGuard(rHandle) +{ +} + +OString AsynchReader::getData() +{ + return OString(m_arData.get(), m_nDataSize); +} + +void AsynchReader::execute() +{ + const sal_uInt64 BUFFER_SIZE = 4096; + char aBuffer[BUFFER_SIZE]; + while (true) + { + sal_uInt64 nRead; + //the function blocks until something could be read or the pipe closed. + switch (osl_readFile( + m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead)) + { + case osl_File_E_PIPE: //HACK! for windows + nRead = 0; + [[fallthrough]]; + case osl_File_E_None: + break; + default: + return; + } + + if (nRead == 0) + { + break; + } + else if (nRead <= BUFFER_SIZE) + { + //Save the data we have in m_arData into a temporary array + std::unique_ptr<char[]> arTmp( new char[m_nDataSize]); + if (m_nDataSize != 0) { + memcpy(arTmp.get(), m_arData.get(), m_nDataSize); + } + //Enlarge m_arData to hold the newly read data + m_arData.reset(new char[static_cast<size_t>(m_nDataSize + nRead)]); + //Copy back the data that was already in m_arData + memcpy(m_arData.get(), arTmp.get(), m_nDataSize); + //Add the newly read data to m_arData + memcpy(m_arData.get() + m_nDataSize, aBuffer, static_cast<size_t>(nRead)); + m_nDataSize += static_cast<size_t>(nRead); + } + } +} + +bool getJavaProps(const OUString & exePath, +#ifdef JVM_ONE_PATH_CHECK + const OUString & homePath, +#endif + std::vector<std::pair<OUString, OUString> >& props, + bool * bProcessRun) +{ + bool ret = false; + + OSL_ASSERT(!exePath.isEmpty()); + OUString usStartDir; + //We need to set the CLASSPATH in case the office is started from + //a different directory. The JREProperties.class is expected to reside + //next to the plugin, except on macOS where it is in ../Resources/java relative + //to the plugin. + OUString sThisLib; + if (!osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps), + & sThisLib.pData)) + { + return false; + } + sThisLib = getDirFromFile(sThisLib); + OUString sClassPath; + if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData) + != osl_File_E_None) + { + return false; + } + +#ifdef MACOSX +#if defined __x86_64__ + if (!JvmfwkUtil_isLoadableJVM(exePath)) + return false; +#endif + if (sClassPath.endsWith("/")) + sClassPath += "../Resources/java/"; + else + sClassPath += "/../Resources/java"; +#endif + + //prepare the arguments + sal_Int32 const cArgs = 3; + OUString arg1 = "-classpath";// + sClassPath; + OUString arg2 = sClassPath; + OUString arg3("JREProperties"); + rtl_uString *args[cArgs] = {arg1.pData, arg2.pData, arg3.pData}; + + oslProcess javaProcess= nullptr; + oslFileHandle fileOut= nullptr; + oslFileHandle fileErr= nullptr; + + FileHandleReader stdoutReader(fileOut); + rtl::Reference< AsynchReader > stderrReader(new AsynchReader(fileErr)); + + JFW_TRACE2("Executing: " + exePath); + oslProcessError procErr = + osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData, + args, + cArgs, //sal_uInt32 nArguments, + osl_Process_HIDDEN, //oslProcessOption Options, + nullptr, //oslSecurity Security, + usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir, + nullptr, //rtl_uString *strEnvironment[], + 0, // sal_uInt32 nEnvironmentVars, + &javaProcess, //oslProcess *pProcess, + nullptr,//oslFileHandle *pChildInputWrite, + &fileOut,//oslFileHandle *pChildOutputRead, + &fileErr);//oslFileHandle *pChildErrorRead); + + if( procErr != osl_Process_E_None) + { + JFW_TRACE2("Execution failed"); + *bProcessRun = false; + SAL_WARN("jfw", + "osl_executeProcess failed (" << ret << "): \"" << exePath << "\""); + return ret; + } + else + { + JFW_TRACE2("Java executed successfully"); + *bProcessRun = true; + } + + //Start asynchronous reading (different thread) of error stream + stderrReader->launch(); + + //Use this thread to read output stream + FileHandleReader::Result rs = FileHandleReader::RESULT_OK; + JFW_TRACE2("Properties found:"); + while (true) + { + OString aLine; + rs = stdoutReader.readLine( & aLine); + if (rs != FileHandleReader::RESULT_OK) + break; + OUString sLine; + if (!decodeOutput(aLine, &sLine)) + continue; + JFW_TRACE2(" \"" << sLine << "\""); + sLine = sLine.trim(); + if (sLine.isEmpty()) + continue; + //The JREProperties class writes key value pairs, separated by '=' + sal_Int32 index = sLine.indexOf('='); + OSL_ASSERT(index != -1); + OUString sKey = sLine.copy(0, index); + OUString sVal = sLine.copy(index + 1); + +#ifdef JVM_ONE_PATH_CHECK + //replace absolute path by linux distro link + OUString sHomeProperty("java.home"); + if(sHomeProperty.equals(sKey)) + { + sVal = homePath + "/jre"; + } +#endif + + props.emplace_back(sKey, sVal); + } + + if (rs != FileHandleReader::RESULT_ERROR && !props.empty()) + ret = true; + + //process error stream data + stderrReader->join(); + JFW_TRACE2("Java wrote to stderr:\" " + << stderrReader->getData() << " \""); + + TimeValue waitMax= {5 ,0}; + procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax); + OSL_ASSERT(procErr == osl_Process_E_None); + osl_freeProcessHandle(javaProcess); + return ret; +} + +/* converts the properties printed by JREProperties.class into + readable strings. The strings are encoded as integer values separated + by spaces. + */ +bool decodeOutput(std::string_view s, OUString* out) +{ + OSL_ASSERT(out != nullptr); + OUStringBuffer buff(512); + sal_Int32 nIndex = 0; + do + { + std::string_view aToken = o3tl::getToken(s, 0, ' ', nIndex ); + if (!aToken.empty()) + { + for (size_t i = 0; i < aToken.size(); ++i) + { + if (aToken[i] < '0' || aToken[i] > '9') + return false; + } + sal_Unicode value = static_cast<sal_Unicode>(o3tl::toInt32(aToken)); + buff.append(value); + } + } while (nIndex >= 0); + + *out = buff.makeStringAndClear(); + return true; +} + + +#if defined(_WIN32) + +static bool getJavaInfoFromRegistry(const wchar_t* szRegKey, + std::vector<OUString>& vecJavaHome) +{ + HKEY hRoot; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot) + == ERROR_SUCCESS) + { + DWORD dwIndex = 0; + const DWORD BUFFSIZE = 1024; + wchar_t bufVersion[BUFFSIZE]; + FILETIME fileTime; + DWORD nNameLen = sizeof(bufVersion); + + // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment + while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, nullptr, nullptr, nullptr, &fileTime) != ERROR_NO_MORE_ITEMS) + { + HKEY hKey; + // Open a Java Runtime Environment sub key, e.g. "1.4.0" + if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) + { + DWORD dwType; + DWORD dwTmpPathLen= 0; + // Get the path to the JavaHome every JRE entry + // Find out how long the string for JavaHome is and allocate memory to hold the path + if( RegQueryValueExW(hKey, L"JavaHome", nullptr, &dwType, nullptr, &dwTmpPathLen)== ERROR_SUCCESS) + { + unsigned char* szTmpPath= static_cast<unsigned char *>(malloc(dwTmpPathLen+sizeof(sal_Unicode))); + // According to https://msdn.microsoft.com/en-us/ms724911, the application should ensure + // that the string is properly terminated before using it + for (DWORD i = 0; i < sizeof(sal_Unicode); ++i) + szTmpPath[dwTmpPathLen + i] = 0; + // Get the path for the runtime lib + if(RegQueryValueExW(hKey, L"JavaHome", nullptr, &dwType, szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS) + { + // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1 + OUString usHome(reinterpret_cast<sal_Unicode*>(szTmpPath)); + // check if there is already an entry with the same JavaHomeruntime lib + // if so, we use the one with the more accurate version + OUString usHomeUrl; + if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) == + osl_File_E_None) + { + bool bAppend= true; + //iterate over the vector with java home strings + for (auto const& javaHome : vecJavaHome) + { + if(usHomeUrl.equals(javaHome)) + { + bAppend= false; + break; + } + } + // Save the home dir + if(bAppend) + { + vecJavaHome.push_back(usHomeUrl); + } + } + } + free( szTmpPath); + RegCloseKey(hKey); + } + } + dwIndex ++; + nNameLen = BUFFSIZE; + } + RegCloseKey(hRoot); + } + return true; +} + + +bool getSDKInfoFromRegistry(std::vector<OUString> & vecHome) +{ + return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome); +} + +bool getJREInfoFromRegistry(std::vector<OUString>& vecJavaHome) +{ + return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome); +} + +static void addJavaInfoFromWinReg( + std::vector<rtl::Reference<VendorBase> > & allInfos, + std::vector<rtl::Reference<VendorBase> > & addedInfos) +{ + // Get Java s from registry + std::vector<OUString> vecJavaHome; + if(getSDKInfoFromRegistry(vecJavaHome)) + { + // create impl objects + for (auto const& javaHome : vecJavaHome) + { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } + + vecJavaHome.clear(); + if(getJREInfoFromRegistry(vecJavaHome)) + { + for (auto const& javaHome : vecJavaHome) + { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } + + vecJavaHome.clear(); + if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JDK", vecJavaHome)) { + for (auto const & javaHome: vecJavaHome) { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } + + vecJavaHome.clear(); + if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JRE", vecJavaHome)) { + for (auto const & javaHome: vecJavaHome) { + getAndAddJREInfoByPath(javaHome, allInfos, addedInfos); + } + } +} + +#endif // _WIN32 + +void bubbleSortVersion(std::vector<rtl::Reference<VendorBase> >& vec) +{ + if(vec.empty()) + return; + int size= vec.size() - 1; + int cIter= 0; + // sort for version + for(int i= 0; i < size; i++) + { + for(int j= size; j > 0 + cIter; j--) + { + rtl::Reference<VendorBase>& cur= vec.at(j); + rtl::Reference<VendorBase>& next= vec.at(j-1); + + int nCmp = 0; + // comparing invalid SunVersion s is possible, they will be less than a + // valid version + + //check if version of current is recognized, by comparing it with itself + try + { + (void)cur->compareVersions(cur->getVersion()); + } + catch (MalformedVersionException &) + { + nCmp = -1; // current < next + } + //The version of cur is valid, now compare with the second version + if (nCmp == 0) + { + try + { + nCmp = cur->compareVersions(next->getVersion()); + } + catch (MalformedVersionException & ) + { + //The second version is invalid, therefore it regards less. + nCmp = 1; + } + } + if(nCmp == 1) // cur > next + { + std::swap(cur, next); + } + } + ++cIter; + } +} + + +void addJREInfoFromBinPath( + const OUString& path, std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ + // file:///c:/jre/bin + //map: jre/bin/java.exe + + for ( sal_Int32 pos = 0; + gVendorMap[pos].sVendorName != nullptr; ++pos ) + { + std::vector<OUString> vecPaths; + getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc; + + int size = 0; + char const* const* arExePaths = (*pFunc)(&size); + vecPaths = getVectorFromCharArray(arExePaths, size); + + //make sure argument path does not end with '/' + OUString sBinPath = path; + if (path.endsWith("/")) + sBinPath = path.copy(0, path.getLength() - 1); + + for (auto const& looppath : vecPaths) + { + //the map contains e.g. jre/bin/java.exe + //get the directory where the executable is contained + OUString sHome; + sal_Int32 index = looppath.lastIndexOf('/'); + if (index == -1) + { + //map contained only : "java.exe, then the argument + //path is already the home directory + sHome = sBinPath; + } + else + { + // jre/bin/jre -> jre/bin + OUString sMapPath = looppath.copy(0, index); + index = sBinPath.lastIndexOf(sMapPath); + if (index != -1 + && (index + sMapPath.getLength() == sBinPath.getLength()) + && sBinPath[index - 1] == '/') + { + sHome = sBinPath.copy(index - 1); + } + } + if (!sHome.isEmpty() + && getAndAddJREInfoByPath(path, allInfos, addedInfos)) + { + return; + } + } + } +} + +std::vector<Reference<VendorBase> > addAllJREInfos( + bool checkJavaHomeAndPath, + std::vector<rtl::Reference<VendorBase>> & allInfos) +{ + std::vector<Reference<VendorBase> > addedInfos; + +#if defined(_WIN32) + // Get Javas from the registry + addJavaInfoFromWinReg(allInfos, addedInfos); +#endif // _WIN32 + + if (checkJavaHomeAndPath) { + addJavaInfoFromJavaHome(allInfos, addedInfos); + //this function should be called after addJavaInfosDirScan. + //Otherwise in SDKs Java may be started twice + addJavaInfosFromPath(allInfos, addedInfos); + } + +#ifdef UNX + addJavaInfosDirScan(allInfos, addedInfos); +#endif + + bubbleSortVersion(addedInfos); + return addedInfos; +} + + +std::vector<OUString> getVectorFromCharArray(char const * const * ar, int size) +{ + std::vector<OUString> vec; + for( int i = 0; i < size; i++) + { + OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8); + vec.push_back(s); + } + return vec; +} + +/** Checks if the path is a directory. Links are resolved. + In case of an error the returned string has the length 0. + Otherwise the returned string is the "resolved" file URL. + */ +static OUString resolveDirPath(const OUString & path) +{ + OUString ret; + salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type | + osl_FileStatus_Mask_FileURL); + if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None) + { + //check if this is a directory + if (aResolver.m_aStatus.getFileType() == FileStatus::Directory) + { +#ifndef JVM_ONE_PATH_CHECK + ret = aResolver.m_aStatus.getFileURL(); +#else + ret = path; +#endif + } + } + return ret; +} +/** Checks if the path is a file. If it is a link to a file than + it is resolved. + */ +static OUString resolveFilePath(const OUString & path) +{ + OUString ret; + salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type | + osl_FileStatus_Mask_FileURL); + if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None) + { + //check if this is a file + if (aResolver.m_aStatus.getFileType() == FileStatus::Regular) + { +#ifndef JVM_ONE_PATH_CHECK + ret = aResolver.m_aStatus.getFileURL(); +#else + ret = path; +#endif + } + } + return ret; +} + +rtl::Reference<VendorBase> getJREInfoByPath( + const OUString& path) +{ + rtl::Reference<VendorBase> ret; + static std::vector<OUString> vecBadPaths; + + static std::map<OUString, rtl::Reference<VendorBase> > mapJREs; + OUString sFilePath; + std::vector<std::pair<OUString, OUString> > props; + + OUString sResolvedDir = resolveDirPath(path); + // If this path is invalid then there is no chance to find a JRE here + if (sResolvedDir.isEmpty()) + { + return nullptr; + } + + //check if the directory path is good, that is a JRE was already recognized. + //Then we need not detect it again + //For example, a sun JDK contains <jdk>/bin/java and <jdk>/jre/bin/java. + //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java. + //Otherwise we would execute java two times for every JDK found. + auto entry2 = find_if(mapJREs.cbegin(), mapJREs.cend(), + SameOrSubDirJREMap(sResolvedDir)); + if (entry2 != mapJREs.end()) + { + JFW_TRACE2("JRE found again (detected before): " << sResolvedDir); + return entry2->second; + } + + for ( sal_Int32 pos = 0; + gVendorMap[pos].sVendorName != nullptr; ++pos ) + { + std::vector<OUString> vecPaths; + getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc; + + int size = 0; + char const* const* arExePaths = (*pFunc)(&size); + vecPaths = getVectorFromCharArray(arExePaths, size); + + bool bBreak = false; + for (auto const& looppath : vecPaths) + { + //if the path is a link, then resolve it + //check if the executable exists at all + + //path can be only "file:///". Then do not append a '/' + //sizeof counts the terminating 0 + OUString sFullPath; + if (path.getLength() == sizeof("file:///") - 1) + sFullPath = sResolvedDir + looppath; + else + sFullPath = sResolvedDir + "/" + looppath; + + sFilePath = resolveFilePath(sFullPath); + + if (sFilePath.isEmpty()) + { + //The file path (to java exe) is not valid + auto ifull = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFullPath); + if (ifull == vecBadPaths.cend()) + { + vecBadPaths.push_back(sFullPath); + } + continue; + } + + auto ifile = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFilePath); + if (ifile != vecBadPaths.cend()) + { + continue; + } + + auto entry = mapJREs.find(sFilePath); + if (entry != mapJREs.end()) + { + JFW_TRACE2("JRE found again (detected before): " << sFilePath); + + return entry->second; + } + + bool bProcessRun= false; + if (!getJavaProps(sFilePath, +#ifdef JVM_ONE_PATH_CHECK + sResolvedDir, +#endif + props, & bProcessRun)) + { + //The java executable could not be run or the system properties + //could not be retrieved. We can assume that this java is corrupt. + vecBadPaths.push_back(sFilePath); + //If there was a java executable, that could be run but we did not get + //the system properties, then we also assume that the whole Java installation + //does not work. In a jdk there are two executables. One in jdk/bin and the other + //in jdk/jre/bin. We do not search any further, because we assume that if one java + //does not work then the other does not work as well. This saves us to run java + //again which is quite costly. + if (bProcessRun) + { + // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to + //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java + //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was + //invoked to build the path to the executable. It we start the script directly as .java_wrapper + //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which + //is named java, must be used to start the script. + getJavaProps(sFullPath, +#ifdef JVM_ONE_PATH_CHECK + sResolvedDir, +#endif + props, & bProcessRun); + // Either we found a working 1.3.1 + // Or the java is broken. In both cases we stop searching under this "root" directory + bBreak = true; + break; + } + //sFilePath is no working java executable. We continue with another possible + //path. + else + { + continue; + } + } + //sFilePath is a java and we could get the system properties. We proceed with this + //java. + else + { + bBreak = true; + break; + } + } + if (bBreak) + break; + } + + if (props.empty()) + { + return rtl::Reference<VendorBase>(); + } + + //find java.vendor property + OUString sVendorName; + + for (auto const& prop : props) + { + if (prop.first == "java.vendor") + { + sVendorName = prop.second; + break; + } + } + + auto knownVendor = false; + if (!sVendorName.isEmpty()) + { + //find the creator func for the respective vendor name + for ( sal_Int32 c = 0; + gVendorMap[c].sVendorName != nullptr; ++c ) + { + OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName), + RTL_TEXTENCODING_ASCII_US); + if (sNameMap == sVendorName) + { + ret = createInstance(gVendorMap[c].createFunc, props); + knownVendor = true; + break; + } + } + } + // For unknown vendors, try SunInfo as fallback: + if (!knownVendor) + { + ret = createInstance(SunInfo::createInstance, props); + } + if (!ret.is()) + { + vecBadPaths.push_back(sFilePath); + } + else + { + JFW_TRACE2("Found JRE: " << sResolvedDir << " at: " << path); + + mapJREs.emplace(sResolvedDir, ret); + mapJREs.emplace(sFilePath, ret); + } + + return ret; +} + +Reference<VendorBase> createInstance(createInstance_func pFunc, + const std::vector<std::pair<OUString, OUString> >& properties) +{ + + Reference<VendorBase> aBase = (*pFunc)(); + if (aBase.is()) + { + if (!aBase->initialize(properties)) + aBase = nullptr; + } + return aBase; +} + +inline OUString getDirFromFile(std::u16string_view usFilePath) +{ + size_t index = usFilePath.rfind('/'); + return OUString(usFilePath.substr(0, index)); +} + +void addJavaInfosFromPath( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ +#if !defined JVM_ONE_PATH_CHECK +// Get Java from PATH environment variable + char *szPath= getenv("PATH"); + if(!szPath) + return; + + OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding()); + sal_Int32 nIndex = 0; + do + { + OUString usToken( usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex ) ); + OUString usTokenUrl; + if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None) + { + if(!usTokenUrl.isEmpty()) + { + OUString usBin; + if(usTokenUrl == ".") + { + OUString usWorkDirUrl; + if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData)) + usBin= usWorkDirUrl; + } + else if(usTokenUrl == "..") + { + OUString usWorkDir; + if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData)) + usBin= getDirFromFile(usWorkDir); + } + else + { + usBin = usTokenUrl; + } + if(!usBin.isEmpty()) + { + addJREInfoFromBinPath(usBin, allInfos, addedInfos); + } + } + } + } + while ( nIndex >= 0 ); +#endif +} + + +void addJavaInfoFromJavaHome( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ +#if !defined JVM_ONE_PATH_CHECK + // Get Java from JAVA_HOME environment + + // Note that on macOS is it not normal at all to have a JAVA_HOME environment + // variable. We set it in our build environment for build-time programs, though, + // so it is set when running unit tests that involve Java functionality. (Which affects + // at least CppunitTest_dbaccess_dialog_save, too, and not only the JunitTest ones.) + char *szJavaHome= getenv("JAVA_HOME"); + if(szJavaHome) + { + OUString sHome(szJavaHome, strlen(szJavaHome), osl_getThreadTextEncoding()); + OUString sHomeUrl; + if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None) + { + getAndAddJREInfoByPath(sHomeUrl, allInfos, addedInfos); + } + } +#endif +} + +bool makeDriveLetterSame(OUString * fileURL) +{ + bool ret = false; + DirectoryItem item; + if (DirectoryItem::get(*fileURL, item) == File::E_None) + { + FileStatus status(osl_FileStatus_Mask_FileURL); + if (item.getFileStatus(status) == File::E_None) + { + *fileURL = status.getFileURL(); + ret = true; + } + } + return ret; +} + +#ifdef UNX +#ifdef __sun + +void addJavaInfosDirScan( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ + JFW_TRACE2("Checking /usr/jdk/latest"); + getAndAddJREInfoByPath("file:////usr/jdk/latest", allInfos, addedInfos); +} + +#else +void addJavaInfosDirScan( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos) +{ +#ifdef MACOSX + // Ignore all but Oracle's JDK as loading Apple's Java and Oracle's JRE + // will cause macOS's JavaVM framework to display a dialog and invoke + // exit() when loaded via JNI on macOS 10.10 + Directory aDir("file:///Library/Java/JavaVirtualMachines"); + if (aDir.open() == File::E_None) + { + DirectoryItem aItem; + while (aDir.getNextItem(aItem) == File::E_None) + { + FileStatus aStatus(osl_FileStatus_Mask_FileURL); + if (aItem.getFileStatus(aStatus) == File::E_None) + { + OUString aItemURL( aStatus.getFileURL() ); + if (aItemURL.getLength()) + { + aItemURL += "/Contents/Home"; + if (DirectoryItem::get(aItemURL, aItem) == File::E_None) + getAndAddJREInfoByPath(aItemURL, allInfos, addedInfos); + } + } + } + aDir.close(); + } +#else // MACOSX + OUString excMessage = "[Java framework] sunjavaplugin: " + "Error in function addJavaInfosDirScan in util.cxx."; + int cJavaNames= SAL_N_ELEMENTS(g_arJavaNames); + std::unique_ptr<OUString[]> sarJavaNames(new OUString[cJavaNames]); + OUString *arNames = sarJavaNames.get(); + for(int i= 0; i < cJavaNames; i++) + arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]), + RTL_TEXTENCODING_UTF8); + + int cSearchPaths= SAL_N_ELEMENTS(g_arSearchPaths); + std::unique_ptr<OUString[]> sarPathNames(new OUString[cSearchPaths]); + OUString *arPaths = sarPathNames.get(); + for(int c = 0; c < cSearchPaths; c++) + arPaths[c] = OUString(g_arSearchPaths[c], strlen(g_arSearchPaths[c]), + RTL_TEXTENCODING_UTF8); + + int cCollectDirs = SAL_N_ELEMENTS(g_arCollectDirs); + std::unique_ptr<OUString[]> sarCollectDirs(new OUString[cCollectDirs]); + OUString *arCollectDirs = sarCollectDirs.get(); + for(int d = 0; d < cCollectDirs; d++) + arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]), + RTL_TEXTENCODING_UTF8); + + + for( int ii = 0; ii < cSearchPaths; ii ++) + { + OUString usDir1("file:///" + arPaths[ii]); + DirectoryItem item; + if(DirectoryItem::get(usDir1, item) == File::E_None) + { + for(int j= 0; j < cCollectDirs; j++) + { + OUString usDir2(usDir1 + arCollectDirs[j]); + // prevent that we scan the whole /usr, /usr/lib, etc directories + if (!arCollectDirs[j].isEmpty()) + { + //usr/java/xxx + //Examining every subdirectory + Directory aCollectionDir(usDir2); + + Directory::RC openErr = aCollectionDir.open(); + switch (openErr) + { + case File::E_None: + break; + case File::E_NOENT: + case File::E_NOTDIR: + continue; + case File::E_ACCES: + JFW_TRACE2("Could not read directory " << usDir2 << " because of missing access rights"); + continue; + default: + JFW_TRACE2("Could not read directory " << usDir2 << ". Osl file error: " << openErr); + continue; + } + + DirectoryItem curIt; + File::RC errNext = File::E_None; + while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None) + { + FileStatus aStatus(osl_FileStatus_Mask_FileURL); + File::RC errStatus = File::E_None; + if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None) + { + JFW_TRACE2(excMessage + "getFileStatus failed with error " << errStatus); + continue; + } + JFW_TRACE2("Checking if directory: " << aStatus.getFileURL() << " is a Java"); + + getAndAddJREInfoByPath( + aStatus.getFileURL(), allInfos, addedInfos); + } + + JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT, + "[Java framework] sunjavaplugin: " + "Error while iterating over contents of " + + usDir2 + ". Osl file error: " + + OUString::number(openErr)); + } + else + { + //usr/java + //When we look directly into a dir like /usr, /usr/lib, etc. then we only + //look for certain java directories, such as jre, jdk, etc. We do not want + //to examine the whole directory because of performance reasons. + DirectoryItem item2; + if(DirectoryItem::get(usDir2, item2) == File::E_None) + { + for( int k= 0; k < cJavaNames; k++) + { + // /usr/java/j2re1.4.0 + OUString usDir3(usDir2 + arNames[k]); + + DirectoryItem item3; + if(DirectoryItem::get(usDir3, item3) == File::E_None) + { + //remove trailing '/' + sal_Int32 islash = usDir3.lastIndexOf('/'); + if (islash == usDir3.getLength() - 1 + && (islash + > RTL_CONSTASCII_LENGTH("file://"))) + usDir3 = usDir3.copy(0, islash); + getAndAddJREInfoByPath( + usDir3, allInfos, addedInfos); + } + } + } + } + } + } + } +#endif // MACOSX +} +#endif // ifdef __sun +#endif // ifdef UNX +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util.hxx b/jvmfwk/plugins/sunmajor/pluginlib/util.hxx new file mode 100644 index 000000000..9ba005583 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util.hxx @@ -0,0 +1,116 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_HXX + +#include <rtl/ustring.hxx> +#include <vector> +#include <vendorbase.hxx> + +namespace jfw_plugin +{ + +std::vector<OUString> getVectorFromCharArray(char const * const * ar, int size); + +/* The function uses the relative paths, such as "bin/java.exe" and the provided + path to derive the home directory. The home directory is then used as + argument to getJREInfoByPath. For example usBinDir is + file:///c:/j2sdk/jre/bin then file:///c:/j2sdk/jre would be derived. + */ +void addJREInfoFromBinPath( + const OUString& path, + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); +inline OUString getDirFromFile(std::u16string_view usFilePath); +void addJavaInfosFromPath( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); + +/* Returns a VendorBase object if JAVA_HOME environment variable points + to a JRE. + */ +void addJavaInfoFromJavaHome( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); + +void addJavaInfosDirScan( + std::vector<rtl::Reference<VendorBase>> & allInfos, + std::vector<rtl::Reference<VendorBase>> & addedInfos); + +bool makeDriveLetterSame(OUString * fileURL); + + +/* for std::find_if + Used to find a JavaInfo::Impl object in a std::vector<Impl*> which has a member usJavaHome + as the specified string in the constructor. +*/ +struct InfoFindSame +{ + OUString sJava; + explicit InfoFindSame(const OUString& sJavaHome):sJava(sJavaHome){} + + bool operator () (const rtl::Reference<VendorBase> & aVendorInfo) + { + return aVendorInfo->getHome() == sJava; + } +}; + +struct SameOrSubDirJREMap +{ + OUString s1; + explicit SameOrSubDirJREMap(const OUString& s):s1(s){ + } + + bool operator () (const std::pair<const OUString, rtl::Reference<VendorBase> > & s2) + { + if (s1 == s2.first) + return true; + OUString sSub = s2.first + "/"; + if (s1.match(sSub)) + return true; + return false; + } +}; + + +/* Creates a VendorBase object if a JRE could be found at the specified path. + + This depends if there is a JRE at all and if it is from a vendor that + is supported by this plugin. + */ +rtl::Reference<VendorBase> getJREInfoByPath(const OUString& path); + +std::vector<rtl::Reference<VendorBase> > addAllJREInfos( + bool checkJavaHomeAndPath, std::vector<rtl::Reference<VendorBase>> & infos); + +bool getJavaProps( + const OUString & exePath, +#ifdef JVM_ONE_PATH_CHECK + const OUString & homePath, +#endif + std::vector<std::pair<OUString, OUString> >& props, + bool * bProcessRun); + +void bubbleSortVersion(std::vector<rtl::Reference<VendorBase> >& vec); + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx new file mode 100644 index 000000000..ae43cee91 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.hxx @@ -0,0 +1,14 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_COCOA_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_UTIL_COCOA_HXX + +#include <sal/config.h> + +#include <rtl/ustring.hxx> + +bool JvmfwkUtil_isLoadableJVM(OUString const& aURL); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm new file mode 100644 index 000000000..8c745f8d9 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/util_cocoa.mm @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include <rtl/ustring.hxx> + +#include <premac.h> +#import <Foundation/Foundation.h> +#include <postmac.h> + +#import "util_cocoa.hxx" + +using namespace rtl; + +bool JvmfwkUtil_isLoadableJVM( OUString const & aURL ) +{ + bool bRet = false; + + if ( aURL.getLength() ) + { + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + NSString *pString = [NSString stringWithCharacters:reinterpret_cast<unichar const *>(aURL.getStr()) length:aURL.getLength()]; + if ( pString ) + { + // Ignore all but Oracle's JDK as loading Apple's Java and Oracle's + // JRE will cause macOS's JavaVM framework to display a dialog and + // invoke exit() when loaded via JNI on macOS 10.10 + NSURL *pURL = [NSURL URLWithString:pString]; + if ( pURL ) + pURL = [pURL filePathURL]; + if ( pURL ) + pURL = [pURL URLByStandardizingPath]; + if ( pURL ) + pURL = [pURL URLByResolvingSymlinksInPath]; + + while ( pURL ) + { + // Check if this is a valid bundle + NSNumber *pDir = nil; + NSURL *pContentsURL = [pURL URLByAppendingPathComponent:@"Contents"]; + if ( pContentsURL && [pContentsURL getResourceValue:&pDir forKey:NSURLIsDirectoryKey error:nil] && pDir && [pDir boolValue] ) + { + NSBundle *pBundle = [NSBundle bundleWithURL:pURL]; + if ( pBundle ) + { + // Make sure that this bundle's Info.plist has the + // proper JVM keys to supports loading via JNI. If + // this bundle is a valid JVM and these keys + // are missing, loading the JVM will cause macOS's + // JavaVM framework to display a dialog and invoke + // exit() when loaded via JNI on macOS 10.10. + NSDictionary *pInfo = [pBundle infoDictionary]; + if ( pInfo ) + { + NSDictionary *pJavaVM = [pInfo objectForKey:@"JavaVM"]; + if ( pJavaVM && [pJavaVM isKindOfClass:[NSDictionary class]] ) + { + NSArray *pJVMCapabilities = [pJavaVM objectForKey:@"JVMCapabilities"]; + if ( pJVMCapabilities ) + { + if ( [pJVMCapabilities indexOfObjectIdenticalTo:@"JNI"] == NSNotFound ) + { + if ( [pJVMCapabilities isKindOfClass:[NSMutableArray class]] ) + { + [static_cast<NSMutableArray *>(pJVMCapabilities) addObject:@"JNI"]; + bRet = true; + } + else if ( [pJavaVM isKindOfClass:[NSMutableDictionary class]] ) + { + NSMutableArray *pNewJVMCapabilities = [NSMutableArray arrayWithCapacity:[pJVMCapabilities count] + 1]; + if ( pNewJVMCapabilities ) + { + [pNewJVMCapabilities addObject:@"JNI"]; + [static_cast<NSMutableDictionary *>(pJavaVM) setObject:pNewJVMCapabilities forKey:@"JVMCapabilities"]; + bRet = true; + } + } + } + else + { + bRet = true; + } + } + } + } + } + } + + NSURL *pOldURL = pURL; + pURL = [pURL URLByDeletingLastPathComponent]; + if ( pURL ) + { + pURL = [pURL URLByStandardizingPath]; + if ( pURL ) + { + pURL = [pURL URLByResolvingSymlinksInPath]; + if ( pURL && [pURL isEqual:pOldURL] ) + pURL = nil; + } + } + } + } + + [pPool release]; + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx b/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx new file mode 100644 index 000000000..f4cf14a9a --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/vendorbase.cxx @@ -0,0 +1,197 @@ +/* -*- 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 <osl/file.hxx> + +#include <vendorbase.hxx> +#include "util.hxx" + +using namespace osl; + + +namespace jfw_plugin +{ + +MalformedVersionException::~MalformedVersionException() = default; + +VendorBase::VendorBase() +{ +} + +bool VendorBase::initialize(const std::vector<std::pair<OUString, OUString> >& props) +{ + //get java.vendor, java.version, java.home + //from system properties + + bool bVersion = false; + bool bVendor = false; + bool bHome = false; + bool bArch = false; + + for (auto const& prop : props) + { + if(! bVendor && prop.first == "java.vendor") + { + m_sVendor = prop.second; + bVendor = true; + } + else if (!bVersion && prop.first == "java.version") + { + m_sVersion = prop.second; + bVersion = true; + } + else if (!bHome && prop.first == "java.home") + { +#ifndef JVM_ONE_PATH_CHECK + OUString fileURL; + if (osl_getFileURLFromSystemPath(prop.second.pData,& fileURL.pData) == + osl_File_E_None) + { + //make sure that the drive letter have all the same case + //otherwise file:///c:/jre and file:///C:/jre produce two + //different objects!!! + if (makeDriveLetterSame( & fileURL)) + { + m_sHome = fileURL; + bHome = true; + } + } +#else + m_sHome = prop.second; + bHome = true; +#endif + } + else if (!bArch && prop.first == "os.arch") + { + m_sArch = prop.second; + bArch = true; + } + if (bVendor && bVersion && bHome && bArch) { + break; + } + } + if (!bVersion || !bVendor || !bHome || !bArch) + return false; + + // init m_sRuntimeLibrary + OSL_ASSERT(!m_sHome.isEmpty()); + //call virtual function to get the possible paths to the runtime library. + + int size = 0; + char const* const* arRtPaths = getRuntimePaths( & size); + std::vector<OUString> libpaths = getVectorFromCharArray(arRtPaths, size); + + bool bRt = false; + for (auto const& libpath : libpaths) + { + //Construct an absolute path to the possible runtime + OUString usRt= m_sHome + libpath; + DirectoryItem item; + if(DirectoryItem::get(usRt, item) == File::E_None) + { + //found runtime lib + m_sRuntimeLibrary = usRt; + bRt = true; + break; + } + } + if (!bRt) + return false; + + // init m_sLD_LIBRARY_PATH + OSL_ASSERT(!m_sHome.isEmpty()); + size = 0; + char const * const * arLDPaths = getLibraryPaths( & size); + std::vector<OUString> ld_paths = getVectorFromCharArray(arLDPaths, size); + + bool bLdPath = true; + int c = 0; + for (auto const& ld_path : ld_paths) + { + OUString usAbsUrl= m_sHome + ld_path; + // convert to system path + OUString usSysPath; + if(File::getSystemPathFromFileURL(usAbsUrl, usSysPath) == File::E_None) + { + + if(c > 0) + m_sLD_LIBRARY_PATH+= OUStringChar(SAL_PATHSEPARATOR); + m_sLD_LIBRARY_PATH+= usSysPath; + } + else + { + bLdPath = false; + break; + } + ++c; + } + return bLdPath; +} + +const OUString & VendorBase::getVendor() const +{ + return m_sVendor; +} +const OUString & VendorBase::getVersion() const +{ + return m_sVersion; +} + +const OUString & VendorBase::getHome() const +{ + return m_sHome; +} + +const OUString & VendorBase::getLibraryPath() const +{ + return m_sLD_LIBRARY_PATH; +} + +const OUString & VendorBase::getRuntimeLibrary() const +{ + return m_sRuntimeLibrary; +} + +bool VendorBase::isValidArch() const +{ + // Warning: These values come from the "os.arch" property. + // It is not defined what the exact values are. + // Oracle JRE 8 has "x86" and "amd64", the others were found at http://lopica.sourceforge.net/os.html . + // There might still be missing some options; we need to extend the check once we find out. +#if defined _WIN64 || (defined MACOSX && defined __x86_64__) + return m_sArch == "amd64" || m_sArch == "x86_64"; +#elif defined _WIN32 + return m_sArch == "x86" || m_sArch == "i386" || m_sArch == "i686"; +#elif defined MACOSX && defined __aarch64__ + return m_sArch == "aarch64"; +#else + (void)this; + return true; +#endif +} + +bool VendorBase::needsRestart() const +{ + return !getLibraryPath().isEmpty(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx new file mode 100644 index 000000000..96cc05167 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.cxx @@ -0,0 +1,52 @@ +/* -*- 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 "vendorlist.hxx" +#include "sunjre.hxx" +#include "otherjre.hxx" + +namespace jfw_plugin +{ + +/* Note: The vendor strings must be UTF-8. For example, if + the string contains an umlaut then it must be expressed + by "\xXX\xXX" + */ +VendorSupportMapEntry const gVendorMap[] ={ +// For macOS, don't bother with implementations that aren't relevant (or have never existed) +#ifdef MACOSX + VENDOR_MAP_ENTRY<OtherInfo>("Apple Inc."), + VENDOR_MAP_ENTRY<OtherInfo>("Apple Computer, Inc."), +#endif + VENDOR_MAP_ENTRY<SunInfo>("Sun Microsystems Inc."), + VENDOR_MAP_ENTRY<SunInfo>("Oracle Corporation"), + VENDOR_MAP_ENTRY<SunInfo>("Amazon.com Inc."), + VENDOR_MAP_ENTRY<SunInfo>("Azul Systems, Inc."), +#ifndef MACOSX + VENDOR_MAP_ENTRY<OtherInfo>("IBM Corporation"), + VENDOR_MAP_ENTRY<OtherInfo>("Blackdown Java-Linux Team"), + VENDOR_MAP_ENTRY<OtherInfo>("BEA Systems, Inc."), + VENDOR_MAP_ENTRY<OtherInfo>("The FreeBSD Foundation"), +#endif + {nullptr, nullptr, nullptr} }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx new file mode 100644 index 000000000..e10958d24 --- /dev/null +++ b/jvmfwk/plugins/sunmajor/pluginlib/vendorlist.hxx @@ -0,0 +1,49 @@ +/* -*- 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_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_VENDORLIST_HXX +#define INCLUDED_JVMFWK_PLUGINS_SUNMAJOR_PLUGINLIB_VENDORLIST_HXX + +#include <rtl/ref.hxx> + +namespace jfw_plugin { class VendorBase; } + +namespace jfw_plugin +{ + +typedef char const * const * (* getJavaExePaths_func)(int*); +typedef rtl::Reference<VendorBase> (* createInstance_func) (); + +struct VendorSupportMapEntry +{ + char const * sVendorName; + getJavaExePaths_func getJavaFunc; + createInstance_func createFunc; +}; + +extern VendorSupportMapEntry const gVendorMap[]; + +template<typename y> constexpr VendorSupportMapEntry VENDOR_MAP_ENTRY(char const * x) { + return {x, & y::getJavaExePaths, & y::createInstance}; +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/qa/unit/sunversion.cxx b/jvmfwk/qa/unit/sunversion.cxx new file mode 100644 index 000000000..a717dc438 --- /dev/null +++ b/jvmfwk/qa/unit/sunversion.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include <rtl/string.hxx> +#include <sal/types.h> + +#include <jvmfwk/plugins/sunmajor/pluginlib/sunversion.hxx> + +namespace +{ +class TestSunVersion : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestSunVersion); + CPPUNIT_TEST(testSunVersion); + CPPUNIT_TEST_SUITE_END(); + + void testSunVersion(); +}; + +void TestSunVersion::testSunVersion() +{ + static char const * versions[] = {"1.4.0", "1.4.1", "1.0.0", "10.0.0", "10.10.0", + "10.2.2", "10.10.0", "10.10.10", "111.0.999", + "1.4.1_01", "9.90.99_09", "1.4.1_99", + "1.4.1_00a", + "1.4.1-ea", "1.4.1-beta", "1.4.1-rc1", + "1.5.0_01-ea", "1.5.0_01-rc2", + "10.0", "10.10", "1.5.0_010"}; + static char const * badVersions[] = {".4.0", "..1", "", "10.10.0.", "10.10.0-", "10.10.0.", + "10.2-2", "10_10.0", "10..10", "a.0.999", + "1.4b.1_01", "9.90.-99_09", "1.4.1_99-", + "1.4.1_00a2", "1.4.0_z01z", "1.4.1__99A", + "1.4.1-1ea", "1.5.0._01-", "1.5.0_01-eac"}; + static char const * orderedVer[] = { "1.3.1-ea", "1.3.1-beta", "1.3.1-rc1", + "1.3.1", "1.3.1_00a", "1.3.1_01", "1.3.1_01a", + "1.3.2", "1.4.0", "1.5.0_01-ea", "2.0.0"}; + + int num = SAL_N_ELEMENTS (versions); + int numBad = SAL_N_ELEMENTS (badVersions); + int numOrdered = SAL_N_ELEMENTS (orderedVer); + //parsing test (positive) + for (int i = 0; i < num; i++) + { + jfw_plugin::SunVersion ver(versions[i]); + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("SunVersion(\"") + versions[i] + "\") good").getStr(), ver); + } + //Parsing test (negative) + for ( int i = 0; i < numBad; i++) + { + jfw_plugin::SunVersion ver(badVersions[i]); + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("SunVersion(\"") + badVersions[i] + "\") bad").getStr(), !ver); + } + + // Ordering test + int j = 0; + for (int i = 0; i < numOrdered; i ++) + { + jfw_plugin::SunVersion curVer(orderedVer[i]); + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("SunVersion(\"") + orderedVer[i] + "\") good").getStr(), + curVer); + for (j = 0; j < numOrdered; j++) + { + jfw_plugin::SunVersion compVer(orderedVer[j]); + if (i < j) + { + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("\"") + orderedVer[i] + "\" < \"" + orderedVer[j]) + .getStr(), + curVer < compVer); + } + else if ( i == j) + { + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("\"") + orderedVer[i] + "\" == \"" + orderedVer[j]) + .getStr(), + curVer.operator ==(compVer)); + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("not \"") + orderedVer[i] + "\" > \"" + orderedVer[j]) + .getStr(), + ! (curVer > compVer)); + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("not \"") + orderedVer[i] + "\" < \"" + orderedVer[j]) + .getStr(), + ! (curVer < compVer)); + } + else if (i > j) + { + CPPUNIT_ASSERT_MESSAGE( + OString(OString::Concat("\"") + orderedVer[i] + "\" > \"" + orderedVer[j]) + .getStr(), + curVer > compVer); + } + } + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(TestSunVersion); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/jvmfwk/source/elements.cxx b/jvmfwk/source/elements.cxx new file mode 100644 index 000000000..c48f942ef --- /dev/null +++ b/jvmfwk/source/elements.cxx @@ -0,0 +1,971 @@ +/* -*- 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 <elements.hxx> +#include <osl/mutex.hxx> +#include <osl/file.hxx> +#include <fwkutil.hxx> +#include <fwkbase.hxx> +#include "framework.hxx" +#include <libxmlutil.hxx> +#include <algorithm> +#include <libxml/parser.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <optional> +#include <string.h> + +// For backwards compatibility, the nRequirements flag word is +// read/written as potentially signed hexadecimal number (though that has no +// practical relevance given that it has only one flag with value 0x01 +// defined). + +using namespace osl; +namespace jfw +{ + +static OString getElement(OString const & docPath, + xmlChar const * pathExpression) +{ + //Prepare the xml document and context + OSL_ASSERT(!docPath.isEmpty()); + jfw::CXmlDocPtr doc(xmlParseFile(docPath.getStr())); + if (doc == nullptr) + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Error in function getElement (elements.cxx)"); + + jfw::CXPathContextPtr context(xmlXPathNewContext(doc)); + if (xmlXPathRegisterNs(context, reinterpret_cast<xmlChar const *>("jf"), + reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK)) == -1) + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Error in function getElement (elements.cxx)"); + + CXPathObjectPtr pathObj = xmlXPathEvalExpression(pathExpression, context); + OString sValue; + if (xmlXPathNodeSetIsEmpty(pathObj->nodesetval)) + { + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Error in function getElement (elements.cxx)"); + } + sValue = reinterpret_cast<char*>(pathObj->nodesetval->nodeTab[0]->content); + return sValue; +} + +OString getElementUpdated() +{ + return getElement(jfw::getVendorSettingsPath(), + reinterpret_cast<xmlChar const *>("/jf:javaSelection/jf:updated/text()")); +} + +void createSettingsStructure(xmlDoc * document, bool * bNeedsSave) +{ + OString sExcMsg("[Java framework] Error in function createSettingsStructure " + "(elements.cxx)."); + xmlNode * root = xmlDocGetRootElement(document); + if (root == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + bool bFound = false; + xmlNode * cur = root->children; + while (cur != nullptr) + { + if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("enabled")) == 0) + { + bFound = true; + break; + } + cur = cur->next; + } + if (bFound) + { + *bNeedsSave = false; + return; + } + //We will modify this document + *bNeedsSave = true; + // Now we create the child elements ------------------ + //Get xsi:nil namespace + xmlNs* nsXsi = xmlSearchNsByHref( + document, root, reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + + //<enabled xsi:nil="true" + xmlNode * nodeEn = xmlNewTextChild( + root, nullptr, reinterpret_cast<xmlChar const *>("enabled"), reinterpret_cast<xmlChar const *>("")); + if (nodeEn == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlSetNsProp(nodeEn, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true")); + //add a new line + xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(root, nodeCrLf); + + //<userClassPath xsi:nil="true"> + xmlNode * nodeUs = xmlNewTextChild( + root, nullptr, reinterpret_cast<xmlChar const *>("userClassPath"), reinterpret_cast<xmlChar const *>("")); + if (nodeUs == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlSetNsProp(nodeUs, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true")); + //add a new line + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(root, nodeCrLf); + + //<vmParameters xsi:nil="true"> + xmlNode * nodeVm = xmlNewTextChild( + root, nullptr, reinterpret_cast<xmlChar const *>("vmParameters"), reinterpret_cast<xmlChar const *>("")); + if (nodeVm == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlSetNsProp(nodeVm, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true")); + //add a new line + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(root, nodeCrLf); + + //<jreLocations xsi:nil="true"> + xmlNode * nodeJre = xmlNewTextChild( + root, nullptr, reinterpret_cast<xmlChar const *>("jreLocations"), reinterpret_cast<xmlChar const *>("")); + if (nodeJre == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlSetNsProp(nodeJre, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true")); + //add a new line + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(root, nodeCrLf); + + //<javaInfo xsi:nil="true"> + xmlNode * nodeJava = xmlNewTextChild( + root, nullptr, reinterpret_cast<xmlChar const *>("javaInfo"), reinterpret_cast<xmlChar const *>("")); + if (nodeJava == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlSetNsProp(nodeJava, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true")); + //add a new line + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(root, nodeCrLf); +} + +NodeJava::NodeJava(Layer layer): + m_layer(layer) +{ + //This class reads and write to files which should only be done in + //application mode + if (getMode() == JFW_MODE_DIRECT) + throw FrameworkException( + JFW_E_DIRECT_MODE, + "[Java framework] Trying to access settings files in direct mode."); +} + + +void NodeJava::load() +{ + const OString sExcMsg("[Java framework] Error in function NodeJava::load" + "(elements.cxx)."); + if (SHARED == m_layer) + { + //we do not support yet to write into the shared installation + + //check if shared settings exist at all. + OUString sURL(BootParams::getSharedData()); + jfw::FileStatus s = sURL.isEmpty() + ? FILE_DOES_NOT_EXIST : checkFileURL(sURL); + if (s == FILE_INVALID) + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Invalid file for shared Java settings."); + else if (s == FILE_DOES_NOT_EXIST) + //Writing shared data is not supported yet. + return; + } + else if (USER == m_layer) + { + if (!prepareSettingsDocument()) + { + SAL_INFO("jfw.level1", "no path to load user settings document from"); + return; + } + } + else + { + OSL_FAIL("[Java framework] Unknown enum used."); + } + + + //Read the user elements + OString sSettingsPath = getSettingsPath(); + //There must not be a share settings file + CXmlDocPtr docUser(xmlParseFile(sSettingsPath.getStr())); + if (docUser == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + xmlNode * cur = xmlDocGetRootElement(docUser); + if (cur == nullptr || cur->children == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + CXmlCharPtr sNil; + cur = cur->children; + while (cur != nullptr) + { + if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("enabled")) == 0) + { + //only overwrite share settings if xsi:nil="false" + sNil = xmlGetNsProp( + cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + if (sNil == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0) + { + CXmlCharPtr sEnabled( xmlNodeListGetString( + docUser, cur->children, 1)); + if (xmlStrcmp(sEnabled, reinterpret_cast<xmlChar const *>("true")) == 0) + m_enabled = std::optional<sal_Bool>(true); + else if (xmlStrcmp(sEnabled, reinterpret_cast<xmlChar const *>("false")) == 0) + m_enabled = std::optional<sal_Bool>(false); + } + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("userClassPath")) == 0) + { + sNil = xmlGetNsProp( + cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + if (sNil == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0) + { + CXmlCharPtr sUser(xmlNodeListGetString( + docUser, cur->children, 1)); + m_userClassPath = std::optional<OUString>(OUString(sUser)); + } + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("javaInfo")) == 0) + { + sNil = xmlGetNsProp( + cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + if (sNil == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0) + { + if (! m_javaInfo) + m_javaInfo = std::optional<CNodeJavaInfo>(CNodeJavaInfo()); + m_javaInfo->loadFromNode(docUser, cur); + } + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("vmParameters")) == 0) + { + sNil = xmlGetNsProp( + cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + if (sNil == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0) + { + if ( ! m_vmParameters) + m_vmParameters = std::optional<std::vector<OUString> >( + std::vector<OUString> ()); + + xmlNode * pOpt = cur->children; + while (pOpt != nullptr) + { + if (xmlStrcmp(pOpt->name, reinterpret_cast<xmlChar const *>("param")) == 0) + { + CXmlCharPtr sOpt = xmlNodeListGetString( + docUser, pOpt->children, 1); + m_vmParameters->push_back(sOpt); + } + pOpt = pOpt->next; + } + } + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("jreLocations")) == 0) + { + sNil = xmlGetNsProp( + cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + if (sNil == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0) + { + if (! m_JRELocations) + m_JRELocations = std::optional<std::vector<OUString> >( + std::vector<OUString>()); + + xmlNode * pLoc = cur->children; + while (pLoc != nullptr) + { + if (xmlStrcmp(pLoc->name, reinterpret_cast<xmlChar const *>("location")) == 0) + { + CXmlCharPtr sLoc = xmlNodeListGetString( + docUser, pLoc->children, 1); + m_JRELocations->push_back(sLoc); + } + pLoc = pLoc->next; + } + } + } + cur = cur->next; + } +} + +OString NodeJava::getSettingsPath() const +{ + OString ret; + switch (m_layer) + { + case USER: ret = getUserSettingsPath(); break; + case SHARED: ret = getSharedSettingsPath(); break; + default: + OSL_FAIL("[Java framework] NodeJava::getSettingsPath()"); + } + return ret; +} + +OUString NodeJava::getSettingsURL() const +{ + OUString ret; + switch (m_layer) + { + case USER: ret = BootParams::getUserData(); break; + case SHARED: ret = BootParams::getSharedData(); break; + default: + OSL_FAIL("[Java framework] NodeJava::getSettingsURL()"); + } + return ret; +} + +bool NodeJava::prepareSettingsDocument() const +{ + OString sExcMsg( + "[Java framework] Error in function prepareSettingsDocument" + " (elements.cxx)."); + if (!createSettingsDocument()) + { + return false; + } + OString sSettings = getSettingsPath(); + CXmlDocPtr doc(xmlParseFile(sSettings.getStr())); + if (!doc) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + bool bNeedsSave = false; + createSettingsStructure(doc, & bNeedsSave); + if (bNeedsSave) + { + if (xmlSaveFormatFileEnc( + sSettings.getStr(), doc,"UTF-8", 1) == -1) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + } + return true; +} + +void NodeJava::write() const +{ + OString sExcMsg("[Java framework] Error in function NodeJava::writeSettings " + "(elements.cxx)."); + CXmlDocPtr docUser; + CXPathContextPtr contextUser; + CXPathObjectPtr pathObj; + + if (!prepareSettingsDocument()) + { + SAL_INFO("jfw.level1", "no path to write settings document to"); + return; + } + + //Read the user elements + OString sSettingsPath = getSettingsPath(); + docUser = xmlParseFile(sSettingsPath.getStr()); + if (docUser == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + contextUser = xmlXPathNewContext(docUser); + if (xmlXPathRegisterNs(contextUser, reinterpret_cast<xmlChar const *>("jf"), + reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK)) == -1) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + xmlNode * root = xmlDocGetRootElement(docUser); + //Get xsi:nil namespace + xmlNs* nsXsi = xmlSearchNsByHref(docUser, + root, + reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + + //set the <enabled> element + //The element must exist + if (m_enabled) + { + pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:enabled"), + contextUser); + if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval)) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + xmlNode * nodeEnabled = pathObj->nodesetval->nodeTab[0]; + xmlSetNsProp(nodeEnabled, + nsXsi, + reinterpret_cast<xmlChar const *>("nil"), + reinterpret_cast<xmlChar const *>("false")); + + if (m_enabled == std::optional<sal_Bool>(true)) + xmlNodeSetContent(nodeEnabled,reinterpret_cast<xmlChar const *>("true")); + else + xmlNodeSetContent(nodeEnabled,reinterpret_cast<xmlChar const *>("false")); + } + + //set the <userClassPath> element + //The element must exist + if (m_userClassPath) + { + pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:userClassPath"), + contextUser); + if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval)) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + xmlNode * nodeEnabled = pathObj->nodesetval->nodeTab[0]; + xmlSetNsProp(nodeEnabled, nsXsi, reinterpret_cast<xmlChar const *>("nil"),reinterpret_cast<xmlChar const *>("false")); + xmlNodeSetContent(nodeEnabled,static_cast<xmlChar*>(CXmlCharPtr(*m_userClassPath))); + } + + //set <javaInfo> element + if (m_javaInfo) + { + pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:javaInfo"), + contextUser); + if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval)) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + m_javaInfo->writeToNode( + docUser, pathObj->nodesetval->nodeTab[0]); + } + + //set <vmParameters> element + if (m_vmParameters) + { + pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:vmParameters"), + contextUser); + if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval)) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlNode* vmParameters = pathObj->nodesetval->nodeTab[0]; + //set xsi:nil = false; + xmlSetNsProp(vmParameters, nsXsi,reinterpret_cast<xmlChar const *>("nil"), + reinterpret_cast<xmlChar const *>("false")); + + //remove option elements + xmlNode* cur = vmParameters->children; + while (cur != nullptr) + { + xmlNode* lastNode = cur; + cur = cur->next; + xmlUnlinkNode(lastNode); + xmlFreeNode(lastNode); + } + //add a new line after <vmParameters> + if (!m_vmParameters->empty()) + { + xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(vmParameters, nodeCrLf); + } + + for (auto const & vmParameter : *m_vmParameters) + { + xmlNewTextChild(vmParameters, nullptr, reinterpret_cast<xmlChar const *>("param"), + CXmlCharPtr(vmParameter)); + //add a new line + xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(vmParameters, nodeCrLf); + } + } + + //set <jreLocations> element + if (m_JRELocations) + { + pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:jreLocations"), + contextUser); + if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval)) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlNode* jreLocationsNode = pathObj->nodesetval->nodeTab[0]; + //set xsi:nil = false; + xmlSetNsProp(jreLocationsNode, nsXsi,reinterpret_cast<xmlChar const *>("nil"), + reinterpret_cast<xmlChar const *>("false")); + + //remove option elements + xmlNode* cur = jreLocationsNode->children; + while (cur != nullptr) + { + xmlNode* lastNode = cur; + cur = cur->next; + xmlUnlinkNode(lastNode); + xmlFreeNode(lastNode); + } + //add a new line after <vmParameters> + if (!m_JRELocations->empty()) + { + xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(jreLocationsNode, nodeCrLf); + } + + for (auto const & JRELocation : *m_JRELocations) + { + xmlNewTextChild(jreLocationsNode, nullptr, reinterpret_cast<xmlChar const *>("location"), + CXmlCharPtr(JRELocation)); + //add a new line + xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(jreLocationsNode, nodeCrLf); + } + } + + if (xmlSaveFormatFile(sSettingsPath.getStr(), docUser, 1) == -1) + throw FrameworkException(JFW_E_ERROR, sExcMsg); +} + +void NodeJava::setEnabled(bool bEnabled) +{ + m_enabled = std::optional<sal_Bool>(bEnabled); +} + + +void NodeJava::setUserClassPath(const OUString & sClassPath) +{ + m_userClassPath = std::optional<OUString>(sClassPath); +} + +void NodeJava::setJavaInfo(const JavaInfo * pInfo, bool bAutoSelect) +{ + if (!m_javaInfo) + m_javaInfo = std::optional<CNodeJavaInfo>(CNodeJavaInfo()); + m_javaInfo->bAutoSelect = bAutoSelect; + m_javaInfo->bNil = false; + + if (pInfo != nullptr) + { + m_javaInfo->m_bEmptyNode = false; + m_javaInfo->sVendor = pInfo->sVendor; + m_javaInfo->sLocation = pInfo->sLocation; + m_javaInfo->sVersion = pInfo->sVersion; + m_javaInfo->nRequirements = pInfo->nRequirements; + m_javaInfo->arVendorData = pInfo->arVendorData; + } + else + { + m_javaInfo->m_bEmptyNode = true; + m_javaInfo->sVendor.clear(); + m_javaInfo->sLocation.clear(); + m_javaInfo->sVersion.clear(); + m_javaInfo->nRequirements = 0; + m_javaInfo->arVendorData = rtl::ByteSequence(); + } +} + +void NodeJava::setVmParameters(std::vector<OUString> const & arOptions) +{ + m_vmParameters = std::optional<std::vector<OUString> >(arOptions); +} + +void NodeJava::addJRELocation(OUString const & sLocation) +{ + if (!m_JRELocations) + m_JRELocations = std::optional<std::vector<OUString> >( + std::vector<OUString> ()); + //only add the path if not already present + std::vector<OUString>::const_iterator it = + std::find(m_JRELocations->begin(), m_JRELocations->end(), sLocation); + if (it == m_JRELocations->end()) + m_JRELocations->push_back(sLocation); +} + +jfw::FileStatus NodeJava::checkSettingsFileStatus(OUString const & sURL) +{ + jfw::FileStatus ret = FILE_DOES_NOT_EXIST; + + //check the file time + ::osl::DirectoryItem item; + File::RC rc = ::osl::DirectoryItem::get(sURL, item); + if (File::E_None == rc) + { + ::osl::FileStatus stat(osl_FileStatus_Mask_Validate); + File::RC rc_stat = item.getFileStatus(stat); + if (File::E_None == rc_stat) + { + ret = FILE_OK; + } + else if (File::E_NOENT == rc_stat) + { + ret = FILE_DOES_NOT_EXIST; + } + else + { + ret = FILE_INVALID; + } + } + else if(File::E_NOENT == rc) + { + ret = FILE_DOES_NOT_EXIST; + } + else + { + ret = FILE_INVALID; + } + return ret; +} + +bool NodeJava::createSettingsDocument() const +{ + const OUString sURL = getSettingsURL(); + if (sURL.isEmpty()) + { + return false; + } + //make sure there is a user directory + OString sExcMsg("[Java framework] Error in function createSettingsDocument " + "(elements.cxx)."); + // check if javasettings.xml already exist + if (FILE_OK == checkSettingsFileStatus(sURL)) + return true; + + //make sure that the directories are created in case they do not exist + FileBase::RC rcFile = Directory::createPath(getDirFromFile(sURL)); + if (rcFile != FileBase::E_EXIST && rcFile != FileBase::E_None) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + //javasettings.xml does not exist yet + CXmlDocPtr doc(xmlNewDoc(reinterpret_cast<xmlChar const *>("1.0"))); + if (! doc) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + //Create the root element and name spaces + xmlNodePtr root = xmlNewDocNode( + doc, nullptr, reinterpret_cast<xmlChar const *>("java"), reinterpret_cast<xmlChar const *>("\n")); + + if (root == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + if (xmlNewNs(root, reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK),nullptr) == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + if (xmlNewNs(root,reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE),reinterpret_cast<xmlChar const *>("xsi")) == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + xmlDocSetRootElement(doc, root); + + //Create a comment + xmlNodePtr com = xmlNewComment( + reinterpret_cast<xmlChar const *>("This is a generated file. Do not alter this file!")); + if (com == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + if (xmlAddPrevSibling(root, com) == nullptr) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + const OString path = getSettingsPath(); + if (xmlSaveFormatFileEnc(path.getStr(), doc,"UTF-8", 1) == -1) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + return true; +} + + +CNodeJavaInfo::CNodeJavaInfo() : + m_bEmptyNode(false), bNil(true), bAutoSelect(true), + nRequirements(0) +{ +} + +void CNodeJavaInfo::loadFromNode(xmlDoc * pDoc, xmlNode * pJavaInfo) +{ + OString sExcMsg("[Java framework] Error in function NodeJavaInfo::loadFromNode " + "(elements.cxx)."); + + OSL_ASSERT(pJavaInfo && pDoc); + if (pJavaInfo->children == nullptr) + return; + //Get the xsi:nil attribute; + CXmlCharPtr sNil = xmlGetNsProp( + pJavaInfo, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + if ( ! sNil) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("true")) == 0) + bNil = true; + else if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0) + bNil = false; + else + throw FrameworkException(JFW_E_ERROR, sExcMsg); + if (bNil) + return; + + //Get javaInfo@manuallySelected attribute + CXmlCharPtr sAutoSelect = xmlGetProp( + pJavaInfo, reinterpret_cast<xmlChar const *>("autoSelect")); + if ( ! sAutoSelect) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + if (xmlStrcmp(sAutoSelect, reinterpret_cast<xmlChar const *>("true")) == 0) + bAutoSelect = true; + else if (xmlStrcmp(sAutoSelect, reinterpret_cast<xmlChar const *>("false")) == 0) + bAutoSelect = false; + else + throw FrameworkException(JFW_E_ERROR, sExcMsg); + + xmlNode * cur = pJavaInfo->children; + + while (cur != nullptr) + { + if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("vendor")) == 0) + { + CXmlCharPtr xmlVendor = xmlNodeListGetString( + pDoc, cur->children, 1); + if (! xmlVendor) + return; + sVendor = xmlVendor; + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("location")) == 0) + { + CXmlCharPtr xmlLocation = xmlNodeListGetString( + pDoc, cur->children, 1); + sLocation = xmlLocation; + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("version")) == 0) + { + CXmlCharPtr xmlVersion = xmlNodeListGetString( + pDoc, cur->children, 1); + sVersion = xmlVersion; + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("requirements")) == 0) + { + CXmlCharPtr xmlRequire = xmlNodeListGetString( + pDoc, cur->children, 1); + OUString sRequire = xmlRequire; + nRequirements = sRequire.toInt64(16); +#ifdef MACOSX + //javaldx is not used anymore in the mac build. In case the Java + //corresponding to the saved settings does not exist anymore the + //javavm services will look for an existing Java after creation of + //the JVM failed. See stoc/source/javavm/javavm.cxx. Only if + //nRequirements does not have the flag JFW_REQUIRE_NEEDRESTART the + //jvm of the new selected JRE will be started. Old settings (before + //OOo 3.3) still contain the flag which can be safely ignored. + nRequirements &= ~JFW_REQUIRE_NEEDRESTART; +#endif + } + else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("vendorData")) == 0) + { + CXmlCharPtr xmlData = xmlNodeListGetString( + pDoc, cur->children, 1); + xmlChar* _data = static_cast<xmlChar*>(xmlData); + if (_data) + { + rtl::ByteSequence seq(reinterpret_cast<sal_Int8*>(_data), strlen(reinterpret_cast<char*>(_data))); + arVendorData = decodeBase16(seq); + } + } + cur = cur->next; + } + + if (sVendor.isEmpty()) + m_bEmptyNode = true; + //Get the javainfo attributes + CXmlCharPtr sVendorUpdate = xmlGetProp(pJavaInfo, + reinterpret_cast<xmlChar const *>("vendorUpdate")); + if ( ! sVendorUpdate) + throw FrameworkException(JFW_E_ERROR, sExcMsg); + sAttrVendorUpdate = sVendorUpdate; +} + + +void CNodeJavaInfo::writeToNode(xmlDoc* pDoc, + xmlNode* pJavaInfoNode) const + +{ + OSL_ASSERT(pJavaInfoNode && pDoc); + //write the attribute vendorSettings + + //javaInfo@vendorUpdate + //creates the attribute if necessary + OString sUpdated = getElementUpdated(); + + xmlSetProp(pJavaInfoNode, reinterpret_cast<xmlChar const *>("vendorUpdate"), + reinterpret_cast<xmlChar const *>(sUpdated.getStr())); + + //javaInfo@autoSelect + xmlSetProp(pJavaInfoNode, reinterpret_cast<xmlChar const *>("autoSelect"), + reinterpret_cast<xmlChar const *>(bAutoSelect ? "true" : "false")); + + //Set xsi:nil in javaInfo element to false + //the xmlNs pointer must not be destroyed + xmlNs* nsXsi = xmlSearchNsByHref(pDoc, + pJavaInfoNode, + reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE)); + + xmlSetNsProp(pJavaInfoNode, + nsXsi, + reinterpret_cast<xmlChar const *>("nil"), + reinterpret_cast<xmlChar const *>("false")); + + //Delete the children of JavaInfo + xmlNode* cur = pJavaInfoNode->children; + while (cur != nullptr) + { + xmlNode* lastNode = cur; + cur = cur->next; + xmlUnlinkNode(lastNode); + xmlFreeNode(lastNode); + } + + //If the JavaInfo was set with an empty value, + //then we are done. + if (m_bEmptyNode) + return; + + //add a new line after <javaInfo> + xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); + + //Create the vendor element + xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("vendor"), + CXmlCharPtr(sVendor)); + //add a new line for better readability + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); + + //Create the location element + xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("location"), + CXmlCharPtr(sLocation)); + //add a new line for better readability + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); + + //Create the version element + xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("version"), + CXmlCharPtr(sVersion)); + //add a new line for better readability + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); + + //Create the features element, for backwards compatibility (it used to support one flag + // JFW_FEATURE_ACCESSBRIDGE = 0x01, but is ignored and always written as zero now) + xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("features"), + reinterpret_cast<xmlChar const *>("0")); + //add a new line for better readability + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); + + + //Create the requirements element + OUString sRequirements = OUString::number( + nRequirements, 16); + xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("requirements"), + CXmlCharPtr(sRequirements)); + //add a new line for better readability + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); + + + //Create the vendorData element + rtl::ByteSequence data = encodeBase16(arVendorData); + xmlNode* dataNode = xmlNewChild(pJavaInfoNode, nullptr, + reinterpret_cast<xmlChar const *>("vendorData"), + reinterpret_cast<xmlChar const *>("")); + xmlNodeSetContentLen(dataNode, + reinterpret_cast<xmlChar*>(data.getArray()), data.getLength()); + //add a new line for better readability + nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n")); + xmlAddChild(pJavaInfoNode, nodeCrLf); +} + +std::unique_ptr<JavaInfo> CNodeJavaInfo::makeJavaInfo() const +{ + if (bNil || m_bEmptyNode) + return std::unique_ptr<JavaInfo>(); + return std::unique_ptr<JavaInfo>( + new JavaInfo{ + sVendor, sLocation, sVersion, nRequirements, + arVendorData}); +} + + +MergedSettings::MergedSettings(): + m_bEnabled(false) +{ + NodeJava settings(NodeJava::USER); + settings.load(); + NodeJava sharedSettings(NodeJava::SHARED); + sharedSettings.load(); + merge(sharedSettings, settings); +} + +MergedSettings::~MergedSettings() +{ +} + +void MergedSettings::merge(const NodeJava & share, const NodeJava & user) +{ + if (user.getEnabled()) + m_bEnabled = * user.getEnabled(); + else if (share.getEnabled()) + m_bEnabled = * share.getEnabled(); + else + m_bEnabled = true; + + if (user.getUserClassPath()) + m_sClassPath = * user.getUserClassPath(); + else if (share.getUserClassPath()) + m_sClassPath = * share.getUserClassPath(); + + if (user.getJavaInfo()) + m_javaInfo = * user.getJavaInfo(); + else if (share.getJavaInfo()) + m_javaInfo = * share.getJavaInfo(); + + if (user.getVmParameters()) + m_vmParams = * user.getVmParameters(); + else if (share.getVmParameters()) + m_vmParams = * share.getVmParameters(); + + if (user.getJRELocations()) + m_JRELocations = * user.getJRELocations(); + else if (share.getJRELocations()) + m_JRELocations = * share.getJRELocations(); +} + + +::std::vector< OString> MergedSettings::getVmParametersUtf8() const +{ + ::std::vector< OString> ret; + for (auto const & vmParam : m_vmParams) + { + ret.push_back( OUStringToOString(vmParam, RTL_TEXTENCODING_UTF8)); + } + return ret; +} + + +std::unique_ptr<JavaInfo> MergedSettings::createJavaInfo() const +{ + return m_javaInfo.makeJavaInfo(); +} +#ifdef _WIN32 +bool MergedSettings::getJavaInfoAttrAutoSelect() const +{ + return m_javaInfo.bAutoSelect; +} +#endif +void MergedSettings::getVmParametersArray(std::vector<OUString> * parParams) + const +{ + assert(parParams != nullptr); + osl::MutexGuard guard(FwkMutex()); + + *parParams = m_vmParams; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/jvmfwk/source/framework.hxx b/jvmfwk/source/framework.hxx new file mode 100644 index 000000000..39bda148b --- /dev/null +++ b/jvmfwk/source/framework.hxx @@ -0,0 +1,41 @@ +/* -*- 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_JVMFWK_SOURCE_FRAMEWORK_HXX +#define INCLUDED_JVMFWK_SOURCE_FRAMEWORK_HXX + +#include <jvmfwk/framework.hxx> + +namespace jfw +{ + +class FrameworkException : public std::exception +{ +public: + + FrameworkException(javaFrameworkError err, const OString& msg): + errorCode(err), message(msg) + { + } + javaFrameworkError errorCode; + OString message; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/source/fwkbase.cxx b/jvmfwk/source/fwkbase.cxx new file mode 100644 index 000000000..225437011 --- /dev/null +++ b/jvmfwk/source/fwkbase.cxx @@ -0,0 +1,524 @@ +/* -*- 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 <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <libxml/xpathInternals.h> +#include <osl/file.hxx> +#include <osl/thread.hxx> +#include <o3tl/string_view.hxx> +#include "framework.hxx" +#include <fwkutil.hxx> +#include <elements.hxx> +#include <fwkbase.hxx> + +using namespace osl; + + +#define UNO_JAVA_JFW_PARAMETER "UNO_JAVA_JFW_PARAMETER_" +#define UNO_JAVA_JFW_JREHOME "UNO_JAVA_JFW_JREHOME" +#define UNO_JAVA_JFW_ENV_JREHOME "UNO_JAVA_JFW_ENV_JREHOME" +#define UNO_JAVA_JFW_CLASSPATH "UNO_JAVA_JFW_CLASSPATH" +#define UNO_JAVA_JFW_ENV_CLASSPATH "UNO_JAVA_JFW_ENV_CLASSPATH" +#define UNO_JAVA_JFW_CLASSPATH_URLS "UNO_JAVA_JFW_CLASSPATH_URLS" +#define UNO_JAVA_JFW_VENDOR_SETTINGS "UNO_JAVA_JFW_VENDOR_SETTINGS" + +namespace jfw +{ +static bool g_bJavaSet = false; + +namespace { + +#if defined _WIN32 + // The paths are used in libxml. On Windows, it takes UTF-8 paths. +constexpr rtl_TextEncoding PathEncoding() { return RTL_TEXTENCODING_UTF8; } +#else +rtl_TextEncoding PathEncoding() { return osl_getThreadTextEncoding(); } +#endif + +OString getVendorSettingsPath(OUString const & sURL) +{ + if (sURL.isEmpty()) + return OString(); + OUString sSystemPathSettings; + if (osl_getSystemPathFromFileURL(sURL.pData, + & sSystemPathSettings.pData) != osl_File_E_None) + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Error in function getVendorSettingsPath (fwkbase.cxx) "); + OString osSystemPathSettings = OUStringToOString(sSystemPathSettings, PathEncoding()); + return osSystemPathSettings; +} + +OUString getParam(OUString const & name) +{ + OUString retVal; + bool b = Bootstrap()->getFrom(name, retVal); + SAL_INFO( + "jfw", + "Using bootstrap parameter " << name << " = \"" << retVal << "\"" + << (b ? "" : " (undefined)")); + return retVal; +} + +OUString getParamFirstUrl(OUString const & name) +{ + // Some parameters can consist of multiple URLs (separated by space + // characters, although trim() harmlessly also removes other white-space), + // of which only the first is used: + return getParam(name).trim().getToken(0, ' '); +} + +}//blind namespace + + +VendorSettings::VendorSettings() +{ + OUString xmlDocVendorSettingsFileUrl(BootParams::getVendorSettings()); + //Prepare the xml document and context + OString sSettingsPath = getVendorSettingsPath(xmlDocVendorSettingsFileUrl); + if (sSettingsPath.isEmpty()) + { + OString sMsg("[Java framework] A vendor settings file was not specified." + "Check the bootstrap parameter " UNO_JAVA_JFW_VENDOR_SETTINGS "."); + SAL_WARN( "jfw", sMsg ); + throw FrameworkException(JFW_E_CONFIGURATION, sMsg); + } + if (sSettingsPath.isEmpty()) + return; + + m_xmlDocVendorSettings = xmlParseFile(sSettingsPath.getStr()); + if (m_xmlDocVendorSettings == nullptr) + throw FrameworkException( + JFW_E_ERROR, + OString::Concat("[Java framework] Error while parsing file: ") + + sSettingsPath + "."); + + m_xmlPathContextVendorSettings = xmlXPathNewContext(m_xmlDocVendorSettings); + int res = xmlXPathRegisterNs( + m_xmlPathContextVendorSettings, reinterpret_cast<xmlChar const *>("jf"), + reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK)); + if (res == -1) + throw FrameworkException(JFW_E_ERROR, + "[Java framework] Error in constructor VendorSettings::VendorSettings() (fwkbase.cxx)"); +} + +VersionInfo VendorSettings::getVersionInformation(std::u16string_view sVendor) const +{ + OSL_ASSERT(!sVendor.empty()); + OString osVendor = OUStringToOString(sVendor, RTL_TEXTENCODING_UTF8); + CXPathObjectPtr pathObject = xmlXPathEvalExpression( + reinterpret_cast<xmlChar const *>( + OString( + "/jf:javaSelection/jf:vendorInfos/jf:vendor[@name=\"" + osVendor + + "\"]/jf:minVersion").getStr()), + m_xmlPathContextVendorSettings); + if (xmlXPathNodeSetIsEmpty(pathObject->nodesetval)) + { + return { + {}, +#if defined MACOSX && defined __aarch64__ + "17", +#else + "1.8.0", +#endif + ""}; + } + + VersionInfo aVersionInfo; + //Get minVersion + OString sExpression = + "/jf:javaSelection/jf:vendorInfos/jf:vendor[@name=\"" + + osVendor + "\"]/jf:minVersion"; + + CXPathObjectPtr xPathObjectMin = + xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>(sExpression.getStr()), + m_xmlPathContextVendorSettings); + if (xmlXPathNodeSetIsEmpty(xPathObjectMin->nodesetval)) + { + aVersionInfo.sMinVersion.clear(); + } + else + { + CXmlCharPtr sVersion = xmlNodeListGetString( + m_xmlDocVendorSettings, + xPathObjectMin->nodesetval->nodeTab[0]->xmlChildrenNode, 1); + OString osVersion(sVersion); + aVersionInfo.sMinVersion = OStringToOUString( + osVersion, RTL_TEXTENCODING_UTF8); + } + + //Get maxVersion + sExpression = "/jf:javaSelection/jf:vendorInfos/jf:vendor[@name=\"" + + osVendor + "\"]/jf:maxVersion"; + CXPathObjectPtr xPathObjectMax = xmlXPathEvalExpression( + reinterpret_cast<xmlChar const *>(sExpression.getStr()), + m_xmlPathContextVendorSettings); + if (xmlXPathNodeSetIsEmpty(xPathObjectMax->nodesetval)) + { + aVersionInfo.sMaxVersion.clear(); + } + else + { + CXmlCharPtr sVersion = xmlNodeListGetString( + m_xmlDocVendorSettings, + xPathObjectMax->nodesetval->nodeTab[0]->xmlChildrenNode, 1); + OString osVersion(sVersion); + aVersionInfo.sMaxVersion = OStringToOUString( + osVersion, RTL_TEXTENCODING_UTF8); + } + + //Get excludeVersions + sExpression = "/jf:javaSelection/jf:vendorInfos/jf:vendor[@name=\"" + + osVendor + "\"]/jf:excludeVersions/jf:version"; + CXPathObjectPtr xPathObjectVersions = + xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>(sExpression.getStr()), + m_xmlPathContextVendorSettings); + if (!xmlXPathNodeSetIsEmpty(xPathObjectVersions->nodesetval)) + { + xmlNode* cur = xPathObjectVersions->nodesetval->nodeTab[0]; + while (cur != nullptr) + { + if (cur->type == XML_ELEMENT_NODE ) + { + if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("version")) == 0) + { + CXmlCharPtr sVersion = xmlNodeListGetString( + m_xmlDocVendorSettings, cur->xmlChildrenNode, 1); + OString osVersion(sVersion); + OUString usVersion = OStringToOUString( + osVersion, RTL_TEXTENCODING_UTF8); + aVersionInfo.vecExcludeVersions.push_back(usVersion); + } + } + cur = cur->next; + } + } + return aVersionInfo; +} + +::std::vector<OString> BootParams::getVMParameters() +{ + ::std::vector<OString> vecParams; + + for (sal_Int32 i = 1; ; i++) + { + OUString sName = UNO_JAVA_JFW_PARAMETER + OUString::number(i); + OUString sValue; + if (Bootstrap()->getFrom(sName, sValue)) + { + OString sParam = + OUStringToOString(sValue, osl_getThreadTextEncoding()); + vecParams.push_back(sParam); + SAL_INFO( + "jfw.level2", + "Using bootstrap parameter " << sName << " = " << sParam); + } + else + break; + } + return vecParams; +} + +OUString BootParams::getUserData() +{ + return getParamFirstUrl("UNO_JAVA_JFW_USER_DATA"); +} + +OUString BootParams::getSharedData() +{ + return getParamFirstUrl("UNO_JAVA_JFW_SHARED_DATA"); +} + +OString BootParams::getClasspath() +{ + OString sClassPath; + OUString sCP; + if (Bootstrap()->getFrom( UNO_JAVA_JFW_CLASSPATH, sCP )) + { + sClassPath = OUStringToOString(sCP, PathEncoding()); + SAL_INFO( + "jfw.level2", + "Using bootstrap parameter " UNO_JAVA_JFW_CLASSPATH " = " + << sClassPath); + } + + OUString sEnvCP; + if (Bootstrap()->getFrom( UNO_JAVA_JFW_ENV_CLASSPATH, sEnvCP )) + { + char * pCp = getenv("CLASSPATH"); + if (pCp) + { + sClassPath += OStringChar(SAL_PATHSEPARATOR) + pCp; + } + SAL_INFO( + "jfw.level2", + "Using bootstrap parameter " UNO_JAVA_JFW_ENV_CLASSPATH + " and class path is: " << (pCp ? pCp : "")); + } + + return sClassPath; +} + +OUString BootParams::getVendorSettings() +{ + OUString sVendor; + if (Bootstrap()->getFrom(UNO_JAVA_JFW_VENDOR_SETTINGS, sVendor)) + { + //check the value of the bootstrap variable + jfw::FileStatus s = checkFileURL(sVendor); + if (s != FILE_OK) + { + //This bootstrap parameter can contain a relative URL + OUString sAbsoluteUrl; + OUString sBaseDir = getLibraryLocation(); + if (File::getAbsoluteFileURL(sBaseDir, sVendor, sAbsoluteUrl) + != File::E_None) + throw FrameworkException( + JFW_E_CONFIGURATION, + "[Java framework] Invalid value for bootstrap variable: " + UNO_JAVA_JFW_VENDOR_SETTINGS); + sVendor = sAbsoluteUrl; + s = checkFileURL(sVendor); + if (s == jfw::FILE_INVALID || s == jfw::FILE_DOES_NOT_EXIST) + { + throw FrameworkException( + JFW_E_CONFIGURATION, + "[Java framework] Invalid value for bootstrap variable: " + UNO_JAVA_JFW_VENDOR_SETTINGS); + } + } + SAL_INFO( + "jfw.level2", + "Using bootstrap parameter " UNO_JAVA_JFW_VENDOR_SETTINGS " = " + << sVendor); + } + return sVendor; +} + +OUString BootParams::getJREHome() +{ + OUString sJRE; + OUString sEnvJRE; + bool bJRE = Bootstrap()->getFrom(UNO_JAVA_JFW_JREHOME, sJRE); + bool bEnvJRE = Bootstrap()->getFrom(UNO_JAVA_JFW_ENV_JREHOME, sEnvJRE); + + if (bJRE && bEnvJRE) + { + throw FrameworkException( + JFW_E_CONFIGURATION, + "[Java framework] Both bootstrap parameter " + UNO_JAVA_JFW_JREHOME" and " + UNO_JAVA_JFW_ENV_JREHOME" are set. However only one of them can be set." + "Check bootstrap parameters: environment variables, command line " + "arguments, rc/ini files for executable and java framework library."); + } + else if (bEnvJRE) + { + const char * pJRE = getenv("JAVA_HOME"); + if (pJRE == nullptr) + { + throw FrameworkException( + JFW_E_CONFIGURATION, + "[Java framework] Both bootstrap parameter " + UNO_JAVA_JFW_ENV_JREHOME" is set, but the environment variable " + "JAVA_HOME is not set."); + } + OString osJRE(pJRE); + OUString usJRE = OStringToOUString(osJRE, osl_getThreadTextEncoding()); + if (File::getFileURLFromSystemPath(usJRE, sJRE) != File::E_None) + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Error in function BootParams::getJREHome() " + "(fwkbase.cxx)."); + SAL_INFO( + "jfw.level2", + "Using bootstrap parameter " UNO_JAVA_JFW_ENV_JREHOME + " with JAVA_HOME = " << pJRE); + } + else if (getMode() == JFW_MODE_DIRECT && !bJRE) + { + throw FrameworkException( + JFW_E_CONFIGURATION, + "[Java framework] The bootstrap parameter " + UNO_JAVA_JFW_ENV_JREHOME" or " UNO_JAVA_JFW_JREHOME + " must be set in direct mode."); + } + + SAL_INFO_IF( + bJRE, "jfw.level2", + "Using bootstrap parameter " UNO_JAVA_JFW_JREHOME " = " << sJRE); + return sJRE; +} + +OUString BootParams::getClasspathUrls() +{ + OUString sParams; + Bootstrap()->getFrom( UNO_JAVA_JFW_CLASSPATH_URLS, sParams); + SAL_INFO( + "jfw.level2", + "Using bootstrap parameter " UNO_JAVA_JFW_CLASSPATH_URLS " = " + << sParams); + return sParams; +} + +JFW_MODE getMode() +{ + static bool g_bMode = false; + static JFW_MODE g_mode = JFW_MODE_APPLICATION; + + if (!g_bMode) + { + //check if either of the "direct mode" bootstrap variables is set + bool bDirectMode = true; + OUString sValue; + const rtl::Bootstrap * aBoot = Bootstrap(); + if (!aBoot->getFrom(UNO_JAVA_JFW_JREHOME, sValue)) + { + if (!aBoot->getFrom(UNO_JAVA_JFW_ENV_JREHOME, sValue)) + { + if (!aBoot->getFrom(UNO_JAVA_JFW_CLASSPATH, sValue)) + { + if (!aBoot->getFrom(UNO_JAVA_JFW_ENV_CLASSPATH, sValue)) + { + OUString sParams = UNO_JAVA_JFW_PARAMETER + + OUString::number(1); + if (!aBoot->getFrom(sParams, sValue)) + { + bDirectMode = false; + } + } + } + } + } + + if (bDirectMode) + g_mode = JFW_MODE_DIRECT; + else + g_mode = JFW_MODE_APPLICATION; + g_bMode = true; + } + + return g_mode; +} + +OUString getApplicationClassPath() +{ + OSL_ASSERT(getMode() == JFW_MODE_APPLICATION); + OUString sParams = BootParams::getClasspathUrls(); + if (sParams.isEmpty()) + return OUString(); + + OUStringBuffer buf; + sal_Int32 index = 0; + do + { + OUString token( o3tl::trim(o3tl::getToken(sParams, 0, ' ', index )) ); + if (!token.isEmpty()) + { + OUString systemPathElement; + oslFileError rc = osl_getSystemPathFromFileURL( + token.pData, &systemPathElement.pData ); + OSL_ASSERT( rc == osl_File_E_None ); + if (rc == osl_File_E_None && !systemPathElement.isEmpty()) + { + if (buf.getLength() > 0) + buf.append( SAL_PATHSEPARATOR ); + buf.append( systemPathElement ); + } + } + } + while (index >= 0); + return buf.makeStringAndClear(); +} + +OString makeClassPathOption(std::u16string_view sUserClassPath) +{ + //Compose the class path + OString sPaths; + OUStringBuffer sBufCP(4096); + + // append all user selected jars to the class path + if (!sUserClassPath.empty()) + sBufCP.append(sUserClassPath); + + //append all jar libraries and components to the class path + OUString sAppCP = getApplicationClassPath(); + if (!sAppCP.isEmpty()) + { + if (!sUserClassPath.empty()) + { + sBufCP.append(SAL_PATHSEPARATOR); + } + sBufCP.append(sAppCP); + } + + sPaths = OUStringToOString(sBufCP.makeStringAndClear(), PathEncoding()); + if (sPaths.isEmpty()) { + return ""; + } + + OString sOptionClassPath = "-Djava.class.path=" + sPaths; + return sOptionClassPath; +} + +OString getUserSettingsPath() +{ + return getSettingsPath(BootParams::getUserData()); +} + +OString getSharedSettingsPath() +{ + return getSettingsPath(BootParams::getSharedData()); +} + +OString getSettingsPath( const OUString & sURL) +{ + if (sURL.isEmpty()) + return OString(); + OUString sPath; + if (osl_getSystemPathFromFileURL(sURL.pData, + & sPath.pData) != osl_File_E_None) + throw FrameworkException( + JFW_E_ERROR, + "[Java framework] Error in function ::getSettingsPath (fwkbase.cxx)."); + return OUStringToOString(sPath, PathEncoding()); +} + +OString getVendorSettingsPath() +{ + return getVendorSettingsPath(BootParams::getVendorSettings()); +} + +void setJavaSelected() +{ + g_bJavaSet = true; +} + +bool wasJavaSelectedInSameProcess() +{ + //g_setJavaProcId not set means no Java selected + return g_bJavaSet; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/source/fwkutil.cxx b/jvmfwk/source/fwkutil.cxx new file mode 100644 index 000000000..89cb2e541 --- /dev/null +++ b/jvmfwk/source/fwkutil.cxx @@ -0,0 +1,194 @@ +/* -*- 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 . + */ + + +#if defined(_WIN32) +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <algorithm> +#endif + +#include <osl/module.hxx> +#include <rtl/ustring.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> + +#include "framework.hxx" +#include <fwkutil.hxx> +#include <memory> + +using namespace osl; + + +namespace jfw +{ + +/** provides a bootstrap class which already knows the values from the + jvmfkwrc file. +*/ +const rtl::Bootstrap* Bootstrap() +{ + static const rtl::Bootstrap* SINGLETON = []() + { + OUString sIni = getLibraryLocation() + +#ifdef MACOSX + // For some reason the jvmfwk3rc file is traditionally in + // LIBO_URE_ETC_FOLDER + "/../" LIBO_URE_ETC_FOLDER +#endif + SAL_CONFIGFILE("/jvmfwk3"); + ::rtl::Bootstrap * bootstrap = new ::rtl::Bootstrap(sIni); + SAL_INFO("jfw.level2", "Using configuration file " << sIni); + return bootstrap; + }(); + return SINGLETON; +}; + +osl::Mutex& FwkMutex() +{ + static osl::Mutex SINGLETON; + return SINGLETON; +} + + +rtl::ByteSequence encodeBase16(const rtl::ByteSequence& rawData) +{ + static const char EncodingTable[] = + {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + sal_Int32 lenRaw = rawData.getLength(); + std::unique_ptr<char[]> pBuf(new char[lenRaw * 2]); + const sal_Int8* arRaw = rawData.getConstArray(); + + char* pCurBuf = pBuf.get(); + for (int i = 0; i < lenRaw; i++) + { + unsigned char curChar = arRaw[i]; + curChar >>= 4; + + *pCurBuf = EncodingTable[curChar]; + pCurBuf++; + + curChar = arRaw[i]; + curChar &= 0x0F; + + *pCurBuf = EncodingTable[curChar]; + pCurBuf++; + } + + rtl::ByteSequence ret(reinterpret_cast<sal_Int8*>(pBuf.get()), lenRaw * 2); + return ret; +} + +rtl::ByteSequence decodeBase16(const rtl::ByteSequence& data) +{ + static const char decodingTable[] = + {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + sal_Int32 lenData = data.getLength(); + sal_Int32 lenBuf = lenData / 2; //always divisible by two + std::unique_ptr<unsigned char[]> pBuf(new unsigned char[lenBuf]); + const sal_Int8* pData = data.getConstArray(); + for (sal_Int32 i = 0; i < lenBuf; i++) + { + sal_Int8 curChar = *pData++; + //find the index of the first 4bits + // TODO What happens if text is not valid Hex characters? + unsigned char nibble = 0; + for (unsigned char j = 0; j < 16; j++) + { + if (curChar == decodingTable[j]) + { + nibble = j; + break; + } + } + nibble <<= 4; + curChar = *pData++; + //find the index for the next 4bits + for (unsigned char j = 0; j < 16; j++) + { + if (curChar == decodingTable[j]) + { + nibble |= j; + break; + } + } + pBuf[i] = nibble; + } + rtl::ByteSequence ret(reinterpret_cast<sal_Int8*>(pBuf.get()), lenBuf ); + return ret; +} + +OUString getDirFromFile(std::u16string_view usFilePath) +{ + size_t index = usFilePath.rfind('/'); + return OUString(usFilePath.substr(0, index)); +} + +OUString getLibraryLocation() +{ + OUString libraryFileUrl; + + if (!osl::Module::getUrlFromAddress( + reinterpret_cast< oslGenericFunction >(getLibraryLocation), + libraryFileUrl)) + throw FrameworkException(JFW_E_ERROR, + "[Java framework] Error in function getLibraryLocation (fwkutil.cxx)."); + + return getDirFromFile(libraryFileUrl); +} + +jfw::FileStatus checkFileURL(const OUString & sURL) +{ + jfw::FileStatus ret = jfw::FILE_OK; + DirectoryItem item; + File::RC rc_item = DirectoryItem::get(sURL, item); + if (File::E_None == rc_item) + { + osl::FileStatus status(osl_FileStatus_Mask_Validate); + + File::RC rc_stat = item.getFileStatus(status); + if (File::E_None == rc_stat) + { + ret = FILE_OK; + } + else if (File::E_NOENT == rc_stat) + { + ret = FILE_DOES_NOT_EXIST; + } + else + { + ret = FILE_INVALID; + } + } + else if (File::E_NOENT == rc_item) + { + ret = FILE_DOES_NOT_EXIST; + } + else + { + ret = FILE_INVALID; + } + return ret; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/jvmfwk/source/javasettings.xsd b/jvmfwk/source/javasettings.xsd new file mode 100644 index 000000000..e0ccbd47e --- /dev/null +++ b/jvmfwk/source/javasettings.xsd @@ -0,0 +1,70 @@ +<?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 . +--> + +<!-- + Document : javasettings.xsd + Created on : 25. März 2004, 16:16 + Author : jl97489 + Description: + Purpose of XML Schema document follows. +--> + +<schema targetNamespace="http://openoffice.org/2004/java/framework/1.0" + xmlns:jf="http://openoffice.org/2004/java/framework/1.0" + xmlns="http://www.w3.org/2001/XMLSchema" + elementFormDefault="qualified"> + +<element name="java"> + <complexType> + <sequence> + <element name="enabled" nillable="true" default="true" type="boolean"/> + <element name="userClassPath" nillable="true" type="string"/> + <element name="vmParameters" nillable="true" type="jf:vmParametersType"/> + <element name="jreLocations" nillable="true" type="jf:jreLocationsType"/> + <element name="javaInfo" nillable="true" type="jf:javaInfoType"/> + </sequence> + </complexType> + +</element> + +<complexType name="javaInfoType"> + <sequence> + <element name="vendor" type="string"/> + <element name="location" type="string"/> + <element name="version" type="string"/> + <element name="features" default="0" type="unsignedLong"/> + <element name="requirements" default="0" type="unsignedLong"/> + <element name="vendorData" type="base64Binary"/> + </sequence> + <attribute name="vendorUpdate" type="date"/> +</complexType> + +<complexType name="vmParametersType"> + <sequence> + <element name="param" minOccurs="0" maxOccurs="unbounded" type="string"/> + </sequence> +</complexType> + +<complexType name="jreLocationsType"> + <sequence> + <element name="location" minOccurs="0" maxOccurs="unbounded" type="string"/> + </sequence> +</complexType> + +</schema> diff --git a/jvmfwk/source/javasettings_template.xml b/jvmfwk/source/javasettings_template.xml new file mode 100644 index 000000000..c73404c69 --- /dev/null +++ b/jvmfwk/source/javasettings_template.xml @@ -0,0 +1,52 @@ +<?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 . +--> + +<!-- +This file shows what elements the javasettings_platform.xml can contain. +The children of javaInfo are only created when a JRE is selected. The children of +vmParameters are only created when parameters are added and the children of +jreLocations are only created when the paths are added. +See CNodeJava::loadFromSettings and CNodeJava::writeSettings for details. +When extending the javavendors.xml then use the schema to verify it. +--> + +<java xmlns='http://openoffice.org/2004/java/framework/1.0' + xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' + xsi:schemaLocation='http://openoffice.org/2004/java/framework/1.0 file:/D:/cws-jl6/jvmfwk/source/javasettings.xsd'> + <classesDirectory>program/classes</classesDirectory> + <enabled xsi:nil="true"></enabled> + <userClassPath xsi:nil="true"></userClassPath> + <vmParameters xsi:nil="true"/> + <!--param>-Xdebug</param--> + <!--</vmParameters>--> + <jreLocations xsi:nil="true"/> + <!--location></location--> + <!--</jreLocations>--> + <javaInfo xsi:nil="true"/> + <!--javaInfo vendorUpdate="2004-03-27" xsi:nil="false"--> + <!--vendor></vendor> + <location></location> + <version></version> + <features></features> + <requirements></requirements> + <vendorData></vendorData> + </javaInfo--> + </java> + + diff --git a/jvmfwk/source/libxmlutil.cxx b/jvmfwk/source/libxmlutil.cxx new file mode 100644 index 000000000..f83e14143 --- /dev/null +++ b/jvmfwk/source/libxmlutil.cxx @@ -0,0 +1,136 @@ +/* -*- 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 <libxmlutil.hxx> + +namespace jfw +{ + +CXPathObjectPtr::CXPathObjectPtr():_object(nullptr) +{ +} +CXPathObjectPtr::CXPathObjectPtr(xmlXPathObject* pObj):_object(pObj) +{ +} +CXPathObjectPtr::~CXPathObjectPtr() +{ + xmlXPathFreeObject(_object); +} +CXPathObjectPtr & CXPathObjectPtr::operator = (xmlXPathObject* pObj) +{ + if (_object == pObj) + return *this; + + xmlXPathFreeObject(_object); + _object = pObj; + return *this; +} + +CXPathContextPtr::CXPathContextPtr(xmlXPathContextPtr aContext) + : _object(aContext) +{ +} + +CXPathContextPtr::CXPathContextPtr():_object(nullptr) +{ +} + +CXPathContextPtr::~CXPathContextPtr() +{ + xmlXPathFreeContext(_object); +} + +CXPathContextPtr & CXPathContextPtr::operator = (xmlXPathContextPtr pObj) +{ + if (_object == pObj) + return *this; + xmlXPathFreeContext(_object); + _object = pObj; + return *this; +} + + +CXmlDocPtr::CXmlDocPtr(xmlDoc* aDoc) + : _object(aDoc) +{ +} + +CXmlDocPtr::CXmlDocPtr():_object(nullptr) +{ +} + +CXmlDocPtr::~CXmlDocPtr() +{ + xmlFreeDoc(_object); +} +CXmlDocPtr & CXmlDocPtr::operator = (xmlDoc* pObj) +{ + if (_object == pObj) + return *this; + xmlFreeDoc(_object); + _object = pObj; + return *this; +} + + +CXmlCharPtr::CXmlCharPtr(xmlChar * aChar) + : _object(aChar) +{ +} + +CXmlCharPtr::CXmlCharPtr(std::u16string_view s): + _object(nullptr) +{ + OString o = OUStringToOString(s, RTL_TEXTENCODING_UTF8); + _object = xmlCharStrdup(o.getStr()); +} +CXmlCharPtr::CXmlCharPtr():_object(nullptr) +{ +} + +CXmlCharPtr::~CXmlCharPtr() +{ + xmlFree(_object); +} + +CXmlCharPtr & CXmlCharPtr::operator = (xmlChar* pObj) +{ + if (pObj == _object) + return *this; + xmlFree(_object); + _object = pObj; + return *this; +} + + +CXmlCharPtr::operator OUString() +{ + OUString ret; + if (_object != nullptr) + { + OString aOStr(reinterpret_cast<char*>(_object)); + ret = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8); + } + return ret; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |