Drop some needless mappings to identical strings
[LibreOffice.git] / jvmfwk / plugins / sunmajor / pluginlib / util.cxx
blob21258af12944c6f24f34a9dd82e9242d22a586aa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include "util.hxx"
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>
33 #include <memory>
34 #include <utility>
35 #include <algorithm>
36 #include <map>
37 #include <string_view>
39 #if defined(_WIN32)
40 #if !defined WIN32_LEAN_AND_MEAN
41 # define WIN32_LEAN_AND_MEAN
42 #endif
43 #include <windows.h>
44 #endif
45 #include <string.h>
47 #include "sunjre.hxx"
48 #include "vendorlist.hxx"
49 #include "diagnostics.h"
50 #if defined MACOSX && defined __x86_64__
51 #include "util_cocoa.hxx"
52 #endif
54 using namespace osl;
56 using ::rtl::Reference;
58 #ifdef _WIN32
59 #define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
60 #define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
61 #endif
63 #if defined( UNX ) && !defined( MACOSX )
64 namespace {
65 char const *g_arJavaNames[] = {
66 "",
67 "j2re",
68 "j2se",
69 "j2sdk",
70 "jdk",
71 "jre",
72 "java"
75 /* These are directory names which could contain multiple java installations.
77 char const *g_arCollectDirs[] = {
78 "",
79 #ifndef JVM_ONE_PATH_CHECK
80 "j2re/",
81 "j2se/",
82 "j2sdk/",
83 "jdk/",
84 "jre/",
85 "java/",
86 #endif
87 "jvm/"
90 /* These are directories in which a java installation is
91 looked for.
93 char const *g_arSearchPaths[] = {
94 #ifndef JVM_ONE_PATH_CHECK
95 "",
96 "usr/",
97 "usr/local/",
98 #ifdef X86_64
99 "usr/lib64/",
100 #endif
101 "usr/lib/",
102 "usr/bin/"
103 #else
104 JVM_ONE_PATH_CHECK
105 #endif
108 #endif // UNX && !MACOSX
110 namespace jfw_plugin
112 #if defined(_WIN32)
113 static bool getSDKInfoFromRegistry(std::vector<OUString> & vecHome);
114 static bool getJREInfoFromRegistry(std::vector<OUString>& vecJavaHome);
115 #endif
117 static bool decodeOutput(std::string_view s, OUString* out);
120 namespace
123 bool addJREInfo(
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);
129 return true;
130 } else {
131 return false;
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);
141 if (aInfo.is()) {
142 if (addJREInfo(aInfo, allInfos)) {
143 addedInfos.push_back(aInfo);
145 return true;
146 } else {
147 return false;
153 namespace {
155 class FileHandleGuard
157 public:
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; }
168 private:
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");
185 namespace {
187 class FileHandleReader
189 public:
190 enum Result
192 RESULT_OK,
193 RESULT_EOF,
194 RESULT_ERROR
197 explicit FileHandleReader(oslFileHandle & rHandle):
198 m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {}
200 Result readLine(OString * pLine);
202 private:
203 enum { BUFFER_SIZE = 1024 };
205 char m_aBuffer[BUFFER_SIZE];
206 FileHandleGuard m_aGuard;
207 int m_nSize;
208 int m_nIndex;
209 bool m_bLf;
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
228 nRead = 0;
229 [[fallthrough]];
230 case osl_File_E_None:
231 if (nRead == 0)
233 m_bLf = false;
234 return bEof ? RESULT_EOF : RESULT_OK;
236 m_nIndex = 0;
237 m_nSize = static_cast< int >(nRead);
238 break;
239 case osl_File_E_INTR:
240 continue;
242 default:
243 return RESULT_ERROR;
247 if (m_bLf && m_aBuffer[m_nIndex] == 0x0A)
248 ++m_nIndex;
249 m_bLf = false;
251 int nStart = m_nIndex;
252 while (m_nIndex != m_nSize)
253 switch (m_aBuffer[m_nIndex++])
255 case 0x0D:
256 m_bLf = true;
257 [[fallthrough]];
258 case 0x0A:
259 *pLine += std::string_view(m_aBuffer + nStart,
260 m_nIndex - 1 - nStart);
261 //TODO! check for overflow, and not very efficient
262 return RESULT_OK;
265 *pLine += std::string_view(m_aBuffer + nStart, m_nIndex - nStart);
266 //TODO! check for overflow, and not very efficient
270 namespace {
272 class AsynchReader: public salhelper::Thread
274 size_t m_nDataSize;
275 std::unique_ptr<char[]> m_arData;
277 FileHandleGuard m_aGuard;
279 virtual ~AsynchReader() override {}
281 void execute() override;
282 public:
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.
291 OString getData();
296 AsynchReader::AsynchReader(oslFileHandle & rHandle):
297 Thread("jvmfwkAsyncReader"), m_nDataSize(0),
298 m_aGuard(rHandle)
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];
311 while (true)
313 sal_uInt64 nRead;
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
319 nRead = 0;
320 [[fallthrough]];
321 case osl_File_E_None:
322 break;
323 default:
324 return;
327 if (nRead == 0)
329 break;
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,
352 #endif
353 std::vector<std::pair<OUString, OUString> >& props,
354 bool * bProcessRun)
356 bool ret = false;
358 OSL_ASSERT(!exePath.isEmpty());
359 OUString usStartDir;
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
363 //to the plugin.
364 OUString sThisLib;
365 if (!osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps),
366 & sThisLib.pData))
368 return false;
370 sThisLib = getDirFromFile(sThisLib);
371 OUString sClassPath;
372 if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData)
373 != osl_File_E_None)
375 return false;
378 #ifdef MACOSX
379 #if defined __x86_64__
380 if (!JvmfwkUtil_isLoadableJVM(exePath))
381 return false;
382 #endif
383 if (sClassPath.endsWith("/"))
384 sClassPath += "../Resources/java/";
385 else
386 sClassPath += "/../Resources/java";
387 #endif
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,
406 args,
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;
422 SAL_WARN("jfw",
423 "osl_executeProcess failed (" << ret << "): \"" << exePath << "\"");
424 return ret;
426 else
428 JFW_TRACE2("Java executed successfully");
429 *bProcessRun = true;
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:");
438 while (true)
440 OString aLine;
441 rs = stdoutReader.readLine( & aLine);
442 if (rs != FileHandleReader::RESULT_OK)
443 break;
444 OUString sLine;
445 if (!decodeOutput(aLine, &sLine))
446 continue;
447 JFW_TRACE2(" \"" << sLine << "\"");
448 sLine = sLine.trim();
449 if (sLine.isEmpty())
450 continue;
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";
464 #endif
466 props.emplace_back(sKey, sVal);
469 if (rs != FileHandleReader::RESULT_ERROR && !props.empty())
470 ret = true;
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);
481 return ret;
484 /* converts the properties printed by JREProperties.class into
485 readable strings. The strings are encoded as integer values separated
486 by spaces.
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 );
496 if (!aToken.empty())
498 for (size_t i = 0; i < aToken.size(); ++i)
500 if (aToken[i] < '0' || aToken[i] > '9')
501 return false;
503 sal_Unicode value = static_cast<sal_Unicode>(o3tl::toInt32(aToken));
504 buff.append(value);
506 } while (nIndex >= 0);
508 *out = buff.makeStringAndClear();
509 return true;
513 #if defined(_WIN32)
515 static bool getJavaInfoFromRegistry(const wchar_t* szRegKey,
516 std::vector<OUString>& vecJavaHome)
518 HKEY hRoot;
519 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot)
520 == ERROR_SUCCESS)
522 DWORD dwIndex = 0;
523 const DWORD BUFFSIZE = 1024;
524 wchar_t bufVersion[BUFFSIZE];
525 FILETIME fileTime;
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)
531 HKEY hKey;
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)
535 DWORD dwType;
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
554 OUString usHomeUrl;
555 if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) ==
556 osl_File_E_None)
558 bool bAppend= true;
559 //iterate over the vector with java home strings
560 for (auto const& javaHome : vecJavaHome)
562 if(usHomeUrl.equals(javaHome))
564 bAppend= false;
565 break;
568 // Save the home dir
569 if(bAppend)
571 vecJavaHome.push_back(usHomeUrl);
575 free( szTmpPath);
576 RegCloseKey(hKey);
579 dwIndex ++;
580 nNameLen = BUFFSIZE;
582 RegCloseKey(hRoot);
584 return true;
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);
613 vecJavaHome.clear();
614 if(getJREInfoFromRegistry(vecJavaHome))
616 for (auto const& javaHome : vecJavaHome)
618 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
622 vecJavaHome.clear();
623 if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JDK", vecJavaHome)) {
624 for (auto const & javaHome: vecJavaHome) {
625 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
629 vecJavaHome.clear();
630 if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JRE", vecJavaHome)) {
631 for (auto const & javaHome: vecJavaHome) {
632 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
637 #endif // _WIN32
639 void bubbleSortVersion(std::vector<rtl::Reference<VendorBase> >& vec)
641 if(vec.empty())
642 return;
643 int size= vec.size() - 1;
644 int cIter= 0;
645 // sort for version
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);
653 int nCmp = 0;
654 // comparing invalid SunVersion s is possible, they will be less than a
655 // valid version
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
667 if (nCmp == 0)
671 nCmp = cur->compareVersions(next->getVersion());
673 catch (MalformedVersionException & )
675 //The second version is invalid, therefore it regards less.
676 nCmp = 1;
679 if(nCmp == 1) // cur > next
681 std::swap(cur, next);
684 ++cIter;
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;
702 int size = 0;
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
715 OUString sHome;
716 sal_Int32 index = looppath.lastIndexOf('/');
717 if (index == -1)
719 //map contained only : "java.exe, then the argument
720 //path is already the home directory
721 sHome = sBinPath;
723 else
725 // jre/bin/jre -> jre/bin
726 OUString sMapPath = looppath.copy(0, index);
727 index = sBinPath.lastIndexOf(sMapPath);
728 if (index != -1
729 && (index + sMapPath.getLength() == sBinPath.getLength())
730 && sBinPath[index - 1] == '/')
732 sHome = sBinPath.copy(index - 1);
735 if (!sHome.isEmpty()
736 && getAndAddJREInfoByPath(path, allInfos, addedInfos))
738 return;
744 std::vector<Reference<VendorBase> > addAllJREInfos(
745 bool checkJavaHomeAndPath,
746 std::vector<rtl::Reference<VendorBase>> & allInfos)
748 std::vector<Reference<VendorBase> > addedInfos;
750 #if defined(_WIN32)
751 // Get Javas from the registry
752 addJavaInfoFromWinReg(allInfos, addedInfos);
753 #endif // _WIN32
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);
762 #ifdef UNX
763 addJavaInfosDirScan(allInfos, addedInfos);
764 #endif
766 bubbleSortVersion(addedInfos);
767 return 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);
777 vec.push_back(s);
779 return vec;
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)
788 OUString ret;
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();
798 #else
799 ret = path;
800 #endif
803 return ret;
805 /** Checks if the path is a file. If it is a link to a file than
806 it is resolved.
808 static OUString resolveFilePath(const OUString & path)
810 OUString ret;
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();
820 #else
821 ret = path;
822 #endif
825 return ret;
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;
835 OUString sFilePath;
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())
842 return nullptr;
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;
864 int size = 0;
865 char const* const* arExePaths = (*pFunc)(&size);
866 vecPaths = getVectorFromCharArray(arExePaths, size);
868 bool bBreak = false;
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
876 OUString sFullPath;
877 if (path.getLength() == sizeof("file:///") - 1)
878 sFullPath = sResolvedDir + looppath;
879 else
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);
892 continue;
895 auto ifile = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFilePath);
896 if (ifile != vecBadPaths.cend())
898 continue;
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
912 sResolvedDir,
913 #endif
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.
925 if (bProcessRun)
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
935 sResolvedDir,
936 #endif
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
940 bBreak = true;
941 break;
943 //sFilePath is no working java executable. We continue with another possible
944 //path.
945 else
947 continue;
950 //sFilePath is a java and we could get the system properties. We proceed with this
951 //java.
952 else
954 bBreak = true;
955 break;
958 if (bBreak)
959 break;
962 if (props.empty())
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;
975 break;
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);
991 knownVendor = true;
992 break;
996 // For unknown vendors, try SunInfo as fallback:
997 if (!knownVendor)
999 ret = createInstance(SunInfo::createInstance, props);
1001 if (!ret.is())
1003 vecBadPaths.push_back(sFilePath);
1005 else
1007 JFW_TRACE2("Found JRE: " << sResolvedDir << " at: " << path);
1009 mapJREs.emplace(sResolvedDir, ret);
1010 mapJREs.emplace(sFilePath, ret);
1013 return ret;
1016 Reference<VendorBase> createInstance(createInstance_func pFunc,
1017 const std::vector<std::pair<OUString, OUString> >& properties)
1020 Reference<VendorBase> aBase = (*pFunc)();
1021 if (aBase.is())
1023 if (!aBase->initialize(properties))
1024 aBase = nullptr;
1026 return aBase;
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");
1042 if(!szPath)
1043 return;
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())
1055 OUString usBin;
1056 if(usTokenUrl == ".")
1058 OUString usWorkDirUrl;
1059 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData))
1060 usBin= usWorkDirUrl;
1062 else if(usTokenUrl == "..")
1064 OUString usWorkDir;
1065 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData))
1066 usBin= getDirFromFile(usWorkDir);
1068 else
1070 usBin = usTokenUrl;
1072 if(!usBin.isEmpty())
1074 addJREInfoFromBinPath(usBin, allInfos, addedInfos);
1079 while ( nIndex >= 0 );
1080 #endif
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");
1096 if(szJavaHome)
1098 OUString sHome(szJavaHome, strlen(szJavaHome), osl_getThreadTextEncoding());
1099 OUString sHomeUrl;
1100 if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None)
1102 getAndAddJREInfoByPath(sHomeUrl, allInfos, addedInfos);
1105 #endif
1108 bool makeDriveLetterSame(OUString * fileURL)
1110 bool ret = false;
1111 DirectoryItem item;
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();
1118 ret = true;
1121 return ret;
1124 #ifdef UNX
1125 #ifdef __sun
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);
1135 #else
1136 void addJavaInfosDirScan(
1137 std::vector<rtl::Reference<VendorBase>> & allInfos,
1138 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1140 #ifdef MACOSX
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);
1162 aDir.close();
1164 #else // MACOSX
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]);
1192 DirectoryItem item;
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())
1201 //usr/java/xxx
1202 //Examining every subdirectory
1203 Directory aCollectionDir(usDir2);
1205 Directory::RC openErr = aCollectionDir.open();
1206 switch (openErr)
1208 case File::E_None:
1209 break;
1210 case File::E_NOENT:
1211 case File::E_NOTDIR:
1212 continue;
1213 case File::E_ACCES:
1214 JFW_TRACE2("Could not read directory " << usDir2 << " because of missing access rights");
1215 continue;
1216 default:
1217 JFW_TRACE2("Could not read directory " << usDir2 << ". Osl file error: " << openErr);
1218 continue;
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);
1230 continue;
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));
1244 else
1246 //usr/java
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
1264 && (islash
1265 > RTL_CONSTASCII_LENGTH("file://")))
1266 usDir3 = usDir3.copy(0, islash);
1267 getAndAddJREInfoByPath(
1268 usDir3, allInfos, addedInfos);
1276 #endif // MACOSX
1278 #endif // ifdef __sun
1279 #endif // ifdef UNX
1282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */