1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_folders.h>
24 #include <osl/process.h>
25 #include <osl/file.hxx>
26 #include <osl/module.hxx>
27 #include <osl/diagnose.h>
28 #include <rtl/ustrbuf.hxx>
29 #include <sal/log.hxx>
30 #include <salhelper/linkhelper.hxx>
31 #include <salhelper/thread.hxx>
32 #include <o3tl/string_view.hxx>
37 #include <string_view>
40 #if !defined WIN32_LEAN_AND_MEAN
41 # define WIN32_LEAN_AND_MEAN
48 #include "vendorlist.hxx"
49 #include "diagnostics.h"
50 #if defined MACOSX && defined __x86_64__
51 #include "util_cocoa.hxx"
56 using ::rtl::Reference
;
59 #define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
60 #define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
63 #if defined( UNX ) && !defined( MACOSX )
65 char const *g_arJavaNames
[] = {
75 /* These are directory names which could contain multiple java installations.
77 char const *g_arCollectDirs
[] = {
79 #ifndef JVM_ONE_PATH_CHECK
90 /* These are directories in which a java installation is
93 char const *g_arSearchPaths
[] = {
94 #ifndef JVM_ONE_PATH_CHECK
108 #endif // UNX && !MACOSX
113 static bool getSDKInfoFromRegistry(std::vector
<OUString
> & vecHome
);
114 static bool getJREInfoFromRegistry(std::vector
<OUString
>& vecJavaHome
);
117 static bool decodeOutput(std::string_view s
, OUString
* out
);
124 rtl::Reference
<VendorBase
> const & info
,
125 std::vector
<rtl::Reference
<VendorBase
>> & infos
)
127 if (std::none_of(infos
.begin(), infos
.end(), InfoFindSame(info
->getHome()))) {
128 infos
.push_back(info
);
135 bool getAndAddJREInfoByPath(
136 const OUString
& path
,
137 std::vector
<rtl::Reference
<VendorBase
> > & allInfos
,
138 std::vector
<rtl::Reference
<VendorBase
> > & addedInfos
)
140 rtl::Reference
<VendorBase
> aInfo
= getJREInfoByPath(path
);
142 if (addJREInfo(aInfo
, allInfos
)) {
143 addedInfos
.push_back(aInfo
);
155 class FileHandleGuard
158 explicit FileHandleGuard(oslFileHandle
& rHandle
):
159 m_rHandle(rHandle
) {}
161 inline ~FileHandleGuard();
163 FileHandleGuard(const FileHandleGuard
&) = delete;
164 FileHandleGuard
& operator=(const FileHandleGuard
&) = delete;
166 oslFileHandle
& getHandle() { return m_rHandle
; }
169 oslFileHandle
& m_rHandle
;
174 inline FileHandleGuard::~FileHandleGuard()
176 if (m_rHandle
!= nullptr)
178 if (osl_closeFile(m_rHandle
) != osl_File_E_None
)
180 OSL_FAIL("unexpected situation");
187 class FileHandleReader
197 explicit FileHandleReader(oslFileHandle
& rHandle
):
198 m_aGuard(rHandle
), m_nSize(0), m_nIndex(0), m_bLf(false) {}
200 Result
readLine(OString
* pLine
);
203 enum { BUFFER_SIZE
= 1024 };
205 char m_aBuffer
[BUFFER_SIZE
];
206 FileHandleGuard m_aGuard
;
214 FileHandleReader::Result
215 FileHandleReader::readLine(OString
* pLine
)
217 OSL_ENSURE(pLine
, "specification violation");
219 for (bool bEof
= true;; bEof
= false)
221 if (m_nIndex
== m_nSize
)
223 sal_uInt64 nRead
= 0;
224 switch (osl_readFile(
225 m_aGuard
.getHandle(), m_aBuffer
, sizeof(m_aBuffer
), &nRead
))
227 case osl_File_E_PIPE
: //HACK! for windows
230 case osl_File_E_None
:
234 return bEof
? RESULT_EOF
: RESULT_OK
;
237 m_nSize
= static_cast< int >(nRead
);
239 case osl_File_E_INTR
:
247 if (m_bLf
&& m_aBuffer
[m_nIndex
] == 0x0A)
251 int nStart
= m_nIndex
;
252 while (m_nIndex
!= m_nSize
)
253 switch (m_aBuffer
[m_nIndex
++])
259 *pLine
+= std::string_view(m_aBuffer
+ nStart
,
260 m_nIndex
- 1 - nStart
);
261 //TODO! check for overflow, and not very efficient
265 *pLine
+= std::string_view(m_aBuffer
+ nStart
, m_nIndex
- nStart
);
266 //TODO! check for overflow, and not very efficient
272 class AsynchReader
: public salhelper::Thread
275 std::unique_ptr
<char[]> m_arData
;
277 FileHandleGuard m_aGuard
;
279 virtual ~AsynchReader() override
{}
281 void execute() override
;
284 explicit AsynchReader(oslFileHandle
& rHandle
);
286 /** only call this function after this thread has finished.
288 That is, call join on this instance and then call getData.
296 AsynchReader::AsynchReader(oslFileHandle
& rHandle
):
297 Thread("jvmfwkAsyncReader"), m_nDataSize(0),
302 OString
AsynchReader::getData()
304 return OString(m_arData
.get(), m_nDataSize
);
307 void AsynchReader::execute()
309 const sal_uInt64 BUFFER_SIZE
= 4096;
310 char aBuffer
[BUFFER_SIZE
];
314 //the function blocks until something could be read or the pipe closed.
315 switch (osl_readFile(
316 m_aGuard
.getHandle(), aBuffer
, BUFFER_SIZE
, &nRead
))
318 case osl_File_E_PIPE
: //HACK! for windows
321 case osl_File_E_None
:
331 else if (nRead
<= BUFFER_SIZE
)
333 //Save the data we have in m_arData into a temporary array
334 std::unique_ptr
<char[]> arTmp( new char[m_nDataSize
]);
335 if (m_nDataSize
!= 0) {
336 memcpy(arTmp
.get(), m_arData
.get(), m_nDataSize
);
338 //Enlarge m_arData to hold the newly read data
339 m_arData
.reset(new char[static_cast<size_t>(m_nDataSize
+ nRead
)]);
340 //Copy back the data that was already in m_arData
341 memcpy(m_arData
.get(), arTmp
.get(), m_nDataSize
);
342 //Add the newly read data to m_arData
343 memcpy(m_arData
.get() + m_nDataSize
, aBuffer
, static_cast<size_t>(nRead
));
344 m_nDataSize
+= static_cast<size_t>(nRead
);
349 bool getJavaProps(const OUString
& exePath
,
350 #ifdef JVM_ONE_PATH_CHECK
351 const OUString
& homePath
,
353 std::vector
<std::pair
<OUString
, OUString
> >& props
,
358 OSL_ASSERT(!exePath
.isEmpty());
360 //We need to set the CLASSPATH in case the office is started from
361 //a different directory. The JREProperties.class is expected to reside
362 //next to the plugin, except on macOS where it is in ../Resources/java relative
365 if (!osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps
),
370 sThisLib
= getDirFromFile(sThisLib
);
372 if (osl_getSystemPathFromFileURL(sThisLib
.pData
, & sClassPath
.pData
)
379 #if defined __x86_64__
380 if (!JvmfwkUtil_isLoadableJVM(exePath
))
383 if (sClassPath
.endsWith("/"))
384 sClassPath
+= "../Resources/java/";
386 sClassPath
+= "/../Resources/java";
389 //prepare the arguments
390 sal_Int32
const cArgs
= 3;
391 OUString arg1
= u
"-classpath"_ustr
;// + sClassPath;
392 OUString arg2
= sClassPath
;
393 OUString
arg3(u
"JREProperties"_ustr
);
394 rtl_uString
*args
[cArgs
] = {arg1
.pData
, arg2
.pData
, arg3
.pData
};
396 oslProcess javaProcess
= nullptr;
397 oslFileHandle fileOut
= nullptr;
398 oslFileHandle fileErr
= nullptr;
400 FileHandleReader
stdoutReader(fileOut
);
401 rtl::Reference
< AsynchReader
> stderrReader(new AsynchReader(fileErr
));
403 JFW_TRACE2("Executing: " + exePath
);
404 oslProcessError procErr
=
405 osl_executeProcess_WithRedirectedIO( exePath
.pData
,//usExe.pData,
407 cArgs
, //sal_uInt32 nArguments,
408 osl_Process_HIDDEN
, //oslProcessOption Options,
409 nullptr, //oslSecurity Security,
410 usStartDir
.pData
,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir,
411 nullptr, //rtl_uString *strEnvironment[],
412 0, // sal_uInt32 nEnvironmentVars,
413 &javaProcess
, //oslProcess *pProcess,
414 nullptr,//oslFileHandle *pChildInputWrite,
415 &fileOut
,//oslFileHandle *pChildOutputRead,
416 &fileErr
);//oslFileHandle *pChildErrorRead);
418 if( procErr
!= osl_Process_E_None
)
420 JFW_TRACE2("Execution failed");
421 *bProcessRun
= false;
423 "osl_executeProcess failed (" << ret
<< "): \"" << exePath
<< "\"");
428 JFW_TRACE2("Java executed successfully");
432 //Start asynchronous reading (different thread) of error stream
433 stderrReader
->launch();
435 //Use this thread to read output stream
436 FileHandleReader::Result rs
= FileHandleReader::RESULT_OK
;
437 JFW_TRACE2("Properties found:");
441 rs
= stdoutReader
.readLine( & aLine
);
442 if (rs
!= FileHandleReader::RESULT_OK
)
445 if (!decodeOutput(aLine
, &sLine
))
447 JFW_TRACE2(" \"" << sLine
<< "\"");
448 sLine
= sLine
.trim();
451 //The JREProperties class writes key value pairs, separated by '='
452 sal_Int32 index
= sLine
.indexOf('=');
453 OSL_ASSERT(index
!= -1);
454 OUString sKey
= sLine
.copy(0, index
);
455 OUString sVal
= sLine
.copy(index
+ 1);
457 #ifdef JVM_ONE_PATH_CHECK
458 //replace absolute path by linux distro link
459 OUString
sHomeProperty("java.home");
460 if(sHomeProperty
.equals(sKey
))
462 sVal
= homePath
+ "/jre";
466 props
.emplace_back(sKey
, sVal
);
469 if (rs
!= FileHandleReader::RESULT_ERROR
&& !props
.empty())
472 //process error stream data
473 stderrReader
->join();
474 JFW_TRACE2("Java wrote to stderr:\" "
475 << stderrReader
->getData() << " \"");
477 TimeValue waitMax
= {5 ,0};
478 procErr
= osl_joinProcessWithTimeout(javaProcess
, &waitMax
);
479 OSL_ASSERT(procErr
== osl_Process_E_None
);
480 osl_freeProcessHandle(javaProcess
);
484 /* converts the properties printed by JREProperties.class into
485 readable strings. The strings are encoded as integer values separated
488 bool decodeOutput(std::string_view s
, OUString
* out
)
490 OSL_ASSERT(out
!= nullptr);
491 OUStringBuffer
buff(512);
492 sal_Int32 nIndex
= 0;
495 std::string_view aToken
= o3tl::getToken(s
, 0, ' ', nIndex
);
498 for (size_t i
= 0; i
< aToken
.size(); ++i
)
500 if (aToken
[i
] < '0' || aToken
[i
] > '9')
503 sal_Unicode value
= static_cast<sal_Unicode
>(o3tl::toInt32(aToken
));
506 } while (nIndex
>= 0);
508 *out
= buff
.makeStringAndClear();
515 static bool getJavaInfoFromRegistry(const wchar_t* szRegKey
,
516 std::vector
<OUString
>& vecJavaHome
)
519 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, szRegKey
, 0, KEY_ENUMERATE_SUB_KEYS
, &hRoot
)
523 const DWORD BUFFSIZE
= 1024;
524 wchar_t bufVersion
[BUFFSIZE
];
526 DWORD nNameLen
= sizeof(bufVersion
);
528 // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment
529 while (RegEnumKeyExW(hRoot
, dwIndex
, bufVersion
, &nNameLen
, nullptr, nullptr, nullptr, &fileTime
) != ERROR_NO_MORE_ITEMS
)
532 // Open a Java Runtime Environment sub key, e.g. "1.4.0"
533 if (RegOpenKeyExW(hRoot
, bufVersion
, 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
536 DWORD dwTmpPathLen
= 0;
537 // Get the path to the JavaHome every JRE entry
538 // Find out how long the string for JavaHome is and allocate memory to hold the path
539 if( RegQueryValueExW(hKey
, L
"JavaHome", nullptr, &dwType
, nullptr, &dwTmpPathLen
)== ERROR_SUCCESS
)
541 unsigned char* szTmpPath
= static_cast<unsigned char *>(malloc(dwTmpPathLen
+sizeof(sal_Unicode
)));
542 assert(szTmpPath
&& "Don't handle OOM conditions");
543 // According to https://msdn.microsoft.com/en-us/ms724911, the application should ensure
544 // that the string is properly terminated before using it
545 for (DWORD i
= 0; i
< sizeof(sal_Unicode
); ++i
)
546 szTmpPath
[dwTmpPathLen
+ i
] = 0;
547 // Get the path for the runtime lib
548 if(RegQueryValueExW(hKey
, L
"JavaHome", nullptr, &dwType
, szTmpPath
, &dwTmpPathLen
) == ERROR_SUCCESS
)
550 // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1
551 OUString
usHome(reinterpret_cast<sal_Unicode
*>(szTmpPath
));
552 // check if there is already an entry with the same JavaHomeruntime lib
553 // if so, we use the one with the more accurate version
555 if (osl_getFileURLFromSystemPath(usHome
.pData
, & usHomeUrl
.pData
) ==
559 //iterate over the vector with java home strings
560 for (auto const& javaHome
: vecJavaHome
)
562 if(usHomeUrl
.equals(javaHome
))
571 vecJavaHome
.push_back(usHomeUrl
);
588 bool getSDKInfoFromRegistry(std::vector
<OUString
> & vecHome
)
590 return getJavaInfoFromRegistry(HKEY_SUN_SDK
, vecHome
);
593 bool getJREInfoFromRegistry(std::vector
<OUString
>& vecJavaHome
)
595 return getJavaInfoFromRegistry(HKEY_SUN_JRE
, vecJavaHome
);
598 static void addJavaInfoFromWinReg(
599 std::vector
<rtl::Reference
<VendorBase
> > & allInfos
,
600 std::vector
<rtl::Reference
<VendorBase
> > & addedInfos
)
602 // Get Java s from registry
603 std::vector
<OUString
> vecJavaHome
;
604 if(getSDKInfoFromRegistry(vecJavaHome
))
606 // create impl objects
607 for (auto const& javaHome
: vecJavaHome
)
609 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
614 if(getJREInfoFromRegistry(vecJavaHome
))
616 for (auto const& javaHome
: vecJavaHome
)
618 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
623 if (getJavaInfoFromRegistry(L
"Software\\JavaSoft\\JDK", vecJavaHome
)) {
624 for (auto const & javaHome
: vecJavaHome
) {
625 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
630 if (getJavaInfoFromRegistry(L
"Software\\JavaSoft\\JRE", vecJavaHome
)) {
631 for (auto const & javaHome
: vecJavaHome
) {
632 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
639 void bubbleSortVersion(std::vector
<rtl::Reference
<VendorBase
> >& vec
)
643 int size
= vec
.size() - 1;
646 for(int i
= 0; i
< size
; i
++)
648 for(int j
= size
; j
> 0 + cIter
; j
--)
650 rtl::Reference
<VendorBase
>& cur
= vec
.at(j
);
651 rtl::Reference
<VendorBase
>& next
= vec
.at(j
-1);
654 // comparing invalid SunVersion s is possible, they will be less than a
657 //check if version of current is recognized, by comparing it with itself
660 (void)cur
->compareVersions(cur
->getVersion());
662 catch (MalformedVersionException
&)
664 nCmp
= -1; // current < next
666 //The version of cur is valid, now compare with the second version
671 nCmp
= cur
->compareVersions(next
->getVersion());
673 catch (MalformedVersionException
& )
675 //The second version is invalid, therefore it regards less.
679 if(nCmp
== 1) // cur > next
681 std::swap(cur
, next
);
689 void addJREInfoFromBinPath(
690 const OUString
& path
, std::vector
<rtl::Reference
<VendorBase
>> & allInfos
,
691 std::vector
<rtl::Reference
<VendorBase
>> & addedInfos
)
693 // file:///c:/jre/bin
694 //map: jre/bin/java.exe
696 for ( sal_Int32 pos
= 0;
697 gVendorMap
[pos
].sVendorName
!= nullptr; ++pos
)
699 std::vector
<OUString
> vecPaths
;
700 getJavaExePaths_func pFunc
= gVendorMap
[pos
].getJavaFunc
;
703 char const* const* arExePaths
= (*pFunc
)(&size
);
704 vecPaths
= getVectorFromCharArray(arExePaths
, size
);
706 //make sure argument path does not end with '/'
707 OUString sBinPath
= path
;
708 if (path
.endsWith("/"))
709 sBinPath
= path
.copy(0, path
.getLength() - 1);
711 for (auto const& looppath
: vecPaths
)
713 //the map contains e.g. jre/bin/java.exe
714 //get the directory where the executable is contained
716 sal_Int32 index
= looppath
.lastIndexOf('/');
719 //map contained only : "java.exe, then the argument
720 //path is already the home directory
725 // jre/bin/jre -> jre/bin
726 OUString sMapPath
= looppath
.copy(0, index
);
727 index
= sBinPath
.lastIndexOf(sMapPath
);
729 && (index
+ sMapPath
.getLength() == sBinPath
.getLength())
730 && sBinPath
[index
- 1] == '/')
732 sHome
= sBinPath
.copy(index
- 1);
736 && getAndAddJREInfoByPath(path
, allInfos
, addedInfos
))
744 std::vector
<Reference
<VendorBase
> > addAllJREInfos(
745 bool checkJavaHomeAndPath
,
746 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
)
748 std::vector
<Reference
<VendorBase
> > addedInfos
;
751 // Get Javas from the registry
752 addJavaInfoFromWinReg(allInfos
, addedInfos
);
755 if (checkJavaHomeAndPath
) {
756 addJavaInfoFromJavaHome(allInfos
, addedInfos
);
757 //this function should be called after addJavaInfosDirScan.
758 //Otherwise in SDKs Java may be started twice
759 addJavaInfosFromPath(allInfos
, addedInfos
);
763 addJavaInfosDirScan(allInfos
, addedInfos
);
766 bubbleSortVersion(addedInfos
);
771 std::vector
<OUString
> getVectorFromCharArray(char const * const * ar
, int size
)
773 std::vector
<OUString
> vec
;
774 for( int i
= 0; i
< size
; i
++)
776 OUString
s(ar
[i
], strlen(ar
[i
]), RTL_TEXTENCODING_UTF8
);
782 /** Checks if the path is a directory. Links are resolved.
783 In case of an error the returned string has the length 0.
784 Otherwise the returned string is the "resolved" file URL.
786 static OUString
resolveDirPath(const OUString
& path
)
789 salhelper::LinkResolver
aResolver(osl_FileStatus_Mask_Type
|
790 osl_FileStatus_Mask_FileURL
);
791 if (aResolver
.fetchFileStatus(path
) == osl::FileBase::E_None
)
793 //check if this is a directory
794 if (aResolver
.m_aStatus
.getFileType() == FileStatus::Directory
)
796 #ifndef JVM_ONE_PATH_CHECK
797 ret
= aResolver
.m_aStatus
.getFileURL();
805 /** Checks if the path is a file. If it is a link to a file than
808 static OUString
resolveFilePath(const OUString
& path
)
811 salhelper::LinkResolver
aResolver(osl_FileStatus_Mask_Type
|
812 osl_FileStatus_Mask_FileURL
);
813 if (aResolver
.fetchFileStatus(path
) == osl::FileBase::E_None
)
815 //check if this is a file
816 if (aResolver
.m_aStatus
.getFileType() == FileStatus::Regular
)
818 #ifndef JVM_ONE_PATH_CHECK
819 ret
= aResolver
.m_aStatus
.getFileURL();
828 rtl::Reference
<VendorBase
> getJREInfoByPath(
829 const OUString
& path
)
831 rtl::Reference
<VendorBase
> ret
;
832 static std::vector
<OUString
> vecBadPaths
;
834 static std::map
<OUString
, rtl::Reference
<VendorBase
> > mapJREs
;
836 std::vector
<std::pair
<OUString
, OUString
> > props
;
838 OUString sResolvedDir
= resolveDirPath(path
);
839 // If this path is invalid then there is no chance to find a JRE here
840 if (sResolvedDir
.isEmpty())
845 //check if the directory path is good, that is a JRE was already recognized.
846 //Then we need not detect it again
847 //For example, a sun JDK contains <jdk>/bin/java and <jdk>/jre/bin/java.
848 //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java.
849 //Otherwise we would execute java two times for every JDK found.
850 auto entry2
= find_if(mapJREs
.cbegin(), mapJREs
.cend(),
851 SameOrSubDirJREMap(sResolvedDir
));
852 if (entry2
!= mapJREs
.end())
854 JFW_TRACE2("JRE found again (detected before): " << sResolvedDir
);
855 return entry2
->second
;
858 for ( sal_Int32 pos
= 0;
859 gVendorMap
[pos
].sVendorName
!= nullptr; ++pos
)
861 std::vector
<OUString
> vecPaths
;
862 getJavaExePaths_func pFunc
= gVendorMap
[pos
].getJavaFunc
;
865 char const* const* arExePaths
= (*pFunc
)(&size
);
866 vecPaths
= getVectorFromCharArray(arExePaths
, size
);
869 for (auto const& looppath
: vecPaths
)
871 //if the path is a link, then resolve it
872 //check if the executable exists at all
874 //path can be only "file:///". Then do not append a '/'
875 //sizeof counts the terminating 0
877 if (path
.getLength() == sizeof("file:///") - 1)
878 sFullPath
= sResolvedDir
+ looppath
;
880 sFullPath
= sResolvedDir
+ "/" + looppath
;
882 sFilePath
= resolveFilePath(sFullPath
);
884 if (sFilePath
.isEmpty())
886 //The file path (to java exe) is not valid
887 auto ifull
= find(vecBadPaths
.cbegin(), vecBadPaths
.cend(), sFullPath
);
888 if (ifull
== vecBadPaths
.cend())
890 vecBadPaths
.push_back(sFullPath
);
895 auto ifile
= find(vecBadPaths
.cbegin(), vecBadPaths
.cend(), sFilePath
);
896 if (ifile
!= vecBadPaths
.cend())
901 auto entry
= mapJREs
.find(sFilePath
);
902 if (entry
!= mapJREs
.end())
904 JFW_TRACE2("JRE found again (detected before): " << sFilePath
);
906 return entry
->second
;
909 bool bProcessRun
= false;
910 if (!getJavaProps(sFilePath
,
911 #ifdef JVM_ONE_PATH_CHECK
914 props
, & bProcessRun
))
916 //The java executable could not be run or the system properties
917 //could not be retrieved. We can assume that this java is corrupt.
918 vecBadPaths
.push_back(sFilePath
);
919 //If there was a java executable, that could be run but we did not get
920 //the system properties, then we also assume that the whole Java installation
921 //does not work. In a jdk there are two executables. One in jdk/bin and the other
922 //in jdk/jre/bin. We do not search any further, because we assume that if one java
923 //does not work then the other does not work as well. This saves us to run java
924 //again which is quite costly.
927 // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to
928 //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java
929 //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was
930 //invoked to build the path to the executable. It we start the script directly as .java_wrapper
931 //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which
932 //is named java, must be used to start the script.
933 getJavaProps(sFullPath
,
934 #ifdef JVM_ONE_PATH_CHECK
937 props
, & bProcessRun
);
938 // Either we found a working 1.3.1
939 // Or the java is broken. In both cases we stop searching under this "root" directory
943 //sFilePath is no working java executable. We continue with another possible
950 //sFilePath is a java and we could get the system properties. We proceed with this
964 return rtl::Reference
<VendorBase
>();
967 //find java.vendor property
968 OUString sVendorName
;
970 for (auto const& prop
: props
)
972 if (prop
.first
== "java.vendor")
974 sVendorName
= prop
.second
;
979 auto knownVendor
= false;
980 if (!sVendorName
.isEmpty())
982 //find the creator func for the respective vendor name
983 for ( sal_Int32 c
= 0;
984 gVendorMap
[c
].sVendorName
!= nullptr; ++c
)
986 OUString
sNameMap(gVendorMap
[c
].sVendorName
, strlen(gVendorMap
[c
].sVendorName
),
987 RTL_TEXTENCODING_ASCII_US
);
988 if (sNameMap
== sVendorName
)
990 ret
= createInstance(gVendorMap
[c
].createFunc
, props
);
996 // For unknown vendors, try SunInfo as fallback:
999 ret
= createInstance(SunInfo::createInstance
, props
);
1003 vecBadPaths
.push_back(sFilePath
);
1007 JFW_TRACE2("Found JRE: " << sResolvedDir
<< " at: " << path
);
1009 mapJREs
.emplace(sResolvedDir
, ret
);
1010 mapJREs
.emplace(sFilePath
, ret
);
1016 Reference
<VendorBase
> createInstance(createInstance_func pFunc
,
1017 const std::vector
<std::pair
<OUString
, OUString
> >& properties
)
1020 Reference
<VendorBase
> aBase
= (*pFunc
)();
1023 if (!aBase
->initialize(properties
))
1029 inline OUString
getDirFromFile(std::u16string_view usFilePath
)
1031 size_t index
= usFilePath
.rfind('/');
1032 return OUString(usFilePath
.substr(0, index
));
1035 void addJavaInfosFromPath(
1036 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
,
1037 std::vector
<rtl::Reference
<VendorBase
>> & addedInfos
)
1039 #if !defined JVM_ONE_PATH_CHECK
1040 // Get Java from PATH environment variable
1041 char *szPath
= getenv("PATH");
1045 OUString
usAllPath(szPath
, strlen(szPath
), osl_getThreadTextEncoding());
1046 sal_Int32 nIndex
= 0;
1049 OUString
usToken( usAllPath
.getToken( 0, SAL_PATHSEPARATOR
, nIndex
) );
1050 OUString usTokenUrl
;
1051 if(File::getFileURLFromSystemPath(usToken
, usTokenUrl
) == File::E_None
)
1053 if(!usTokenUrl
.isEmpty())
1056 if(usTokenUrl
== ".")
1058 OUString usWorkDirUrl
;
1059 if(osl_Process_E_None
== osl_getProcessWorkingDir(&usWorkDirUrl
.pData
))
1060 usBin
= usWorkDirUrl
;
1062 else if(usTokenUrl
== "..")
1065 if(osl_Process_E_None
== osl_getProcessWorkingDir(&usWorkDir
.pData
))
1066 usBin
= getDirFromFile(usWorkDir
);
1072 if(!usBin
.isEmpty())
1074 addJREInfoFromBinPath(usBin
, allInfos
, addedInfos
);
1079 while ( nIndex
>= 0 );
1084 void addJavaInfoFromJavaHome(
1085 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
,
1086 std::vector
<rtl::Reference
<VendorBase
>> & addedInfos
)
1088 #if !defined JVM_ONE_PATH_CHECK
1089 // Get Java from JAVA_HOME environment
1091 // Note that on macOS is it not normal at all to have a JAVA_HOME environment
1092 // variable. We set it in our build environment for build-time programs, though,
1093 // so it is set when running unit tests that involve Java functionality. (Which affects
1094 // at least CppunitTest_dbaccess_dialog_save, too, and not only the JunitTest ones.)
1095 char *szJavaHome
= getenv("JAVA_HOME");
1098 OUString
sHome(szJavaHome
, strlen(szJavaHome
), osl_getThreadTextEncoding());
1100 if(File::getFileURLFromSystemPath(sHome
, sHomeUrl
) == File::E_None
)
1102 getAndAddJREInfoByPath(sHomeUrl
, allInfos
, addedInfos
);
1108 bool makeDriveLetterSame(OUString
* fileURL
)
1112 if (DirectoryItem::get(*fileURL
, item
) == File::E_None
)
1114 FileStatus
status(osl_FileStatus_Mask_FileURL
);
1115 if (item
.getFileStatus(status
) == File::E_None
)
1117 *fileURL
= status
.getFileURL();
1127 void addJavaInfosDirScan(
1128 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
,
1129 std::vector
<rtl::Reference
<VendorBase
>> & addedInfos
)
1131 JFW_TRACE2("Checking /usr/jdk/latest");
1132 getAndAddJREInfoByPath("file:////usr/jdk/latest", allInfos
, addedInfos
);
1136 void addJavaInfosDirScan(
1137 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
,
1138 std::vector
<rtl::Reference
<VendorBase
>> & addedInfos
)
1141 // Ignore all but Oracle's JDK as loading Apple's Java and Oracle's JRE
1142 // will cause macOS's JavaVM framework to display a dialog and invoke
1143 // exit() when loaded via JNI on macOS 10.10
1144 Directory
aDir("file:///Library/Java/JavaVirtualMachines");
1145 if (aDir
.open() == File::E_None
)
1147 DirectoryItem aItem
;
1148 while (aDir
.getNextItem(aItem
) == File::E_None
)
1150 FileStatus
aStatus(osl_FileStatus_Mask_FileURL
);
1151 if (aItem
.getFileStatus(aStatus
) == File::E_None
)
1153 OUString
aItemURL( aStatus
.getFileURL() );
1154 if (aItemURL
.getLength())
1156 aItemURL
+= "/Contents/Home";
1157 if (DirectoryItem::get(aItemURL
, aItem
) == File::E_None
)
1158 getAndAddJREInfoByPath(aItemURL
, allInfos
, addedInfos
);
1165 OUString excMessage
= u
"[Java framework] sunjavaplugin: "
1166 "Error in function addJavaInfosDirScan in util.cxx."_ustr
;
1167 int cJavaNames
= SAL_N_ELEMENTS(g_arJavaNames
);
1168 std::unique_ptr
<OUString
[]> sarJavaNames(new OUString
[cJavaNames
]);
1169 OUString
*arNames
= sarJavaNames
.get();
1170 for(int i
= 0; i
< cJavaNames
; i
++)
1171 arNames
[i
] = OUString(g_arJavaNames
[i
], strlen(g_arJavaNames
[i
]),
1172 RTL_TEXTENCODING_UTF8
);
1174 int cSearchPaths
= SAL_N_ELEMENTS(g_arSearchPaths
);
1175 std::unique_ptr
<OUString
[]> sarPathNames(new OUString
[cSearchPaths
]);
1176 OUString
*arPaths
= sarPathNames
.get();
1177 for(int c
= 0; c
< cSearchPaths
; c
++)
1178 arPaths
[c
] = OUString(g_arSearchPaths
[c
], strlen(g_arSearchPaths
[c
]),
1179 RTL_TEXTENCODING_UTF8
);
1181 int cCollectDirs
= SAL_N_ELEMENTS(g_arCollectDirs
);
1182 std::unique_ptr
<OUString
[]> sarCollectDirs(new OUString
[cCollectDirs
]);
1183 OUString
*arCollectDirs
= sarCollectDirs
.get();
1184 for(int d
= 0; d
< cCollectDirs
; d
++)
1185 arCollectDirs
[d
] = OUString(g_arCollectDirs
[d
], strlen(g_arCollectDirs
[d
]),
1186 RTL_TEXTENCODING_UTF8
);
1189 for( int ii
= 0; ii
< cSearchPaths
; ii
++)
1191 OUString
usDir1("file:///" + arPaths
[ii
]);
1193 if(DirectoryItem::get(usDir1
, item
) == File::E_None
)
1195 for(int j
= 0; j
< cCollectDirs
; j
++)
1197 OUString
usDir2(usDir1
+ arCollectDirs
[j
]);
1198 // prevent that we scan the whole /usr, /usr/lib, etc directories
1199 if (!arCollectDirs
[j
].isEmpty())
1202 //Examining every subdirectory
1203 Directory
aCollectionDir(usDir2
);
1205 Directory::RC openErr
= aCollectionDir
.open();
1211 case File::E_NOTDIR
:
1214 JFW_TRACE2("Could not read directory " << usDir2
<< " because of missing access rights");
1217 JFW_TRACE2("Could not read directory " << usDir2
<< ". Osl file error: " << openErr
);
1221 DirectoryItem curIt
;
1222 File::RC errNext
= File::E_None
;
1223 while( (errNext
= aCollectionDir
.getNextItem(curIt
)) == File::E_None
)
1225 FileStatus
aStatus(osl_FileStatus_Mask_FileURL
);
1226 File::RC errStatus
= File::E_None
;
1227 if ((errStatus
= curIt
.getFileStatus(aStatus
)) != File::E_None
)
1229 JFW_TRACE2(excMessage
+ "getFileStatus failed with error " << errStatus
);
1232 JFW_TRACE2("Checking if directory: " << aStatus
.getFileURL() << " is a Java");
1234 getAndAddJREInfoByPath(
1235 aStatus
.getFileURL(), allInfos
, addedInfos
);
1238 JFW_ENSURE(errNext
== File::E_None
|| errNext
== File::E_NOENT
,
1239 "[Java framework] sunjavaplugin: "
1240 "Error while iterating over contents of "
1241 + usDir2
+ ". Osl file error: "
1242 + OUString::number(openErr
));
1247 //When we look directly into a dir like /usr, /usr/lib, etc. then we only
1248 //look for certain java directories, such as jre, jdk, etc. We do not want
1249 //to examine the whole directory because of performance reasons.
1250 DirectoryItem item2
;
1251 if(DirectoryItem::get(usDir2
, item2
) == File::E_None
)
1253 for( int k
= 0; k
< cJavaNames
; k
++)
1255 // /usr/java/j2re1.4.0
1256 OUString
usDir3(usDir2
+ arNames
[k
]);
1258 DirectoryItem item3
;
1259 if(DirectoryItem::get(usDir3
, item3
) == File::E_None
)
1261 //remove trailing '/'
1262 sal_Int32 islash
= usDir3
.lastIndexOf('/');
1263 if (islash
== usDir3
.getLength() - 1
1265 > RTL_CONSTASCII_LENGTH("file://")))
1266 usDir3
= usDir3
.copy(0, islash
);
1267 getAndAddJREInfoByPath(
1268 usDir3
, allInfos
, addedInfos
);
1278 #endif // ifdef __sun
1282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */