lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / jvmfwk / plugins / sunmajor / pluginlib / util.cxx
blob4a634b329f2ca12183a89ab7b6af4d27f4e340d4
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_features.h>
21 #include <config_folders.h>
23 #include "util.hxx"
25 #include <osl/process.h>
26 #include <osl/security.hxx>
27 #include <osl/file.hxx>
28 #include <osl/module.hxx>
29 #include <osl/diagnose.h>
30 #include <osl/getglobalmutex.hxx>
31 #include <rtl/byteseq.hxx>
32 #include <rtl/ustrbuf.hxx>
33 #include <rtl/instance.hxx>
34 #include <sal/log.hxx>
35 #include <salhelper/linkhelper.hxx>
36 #include <salhelper/thread.hxx>
37 #include <memory>
38 #include <utility>
39 #include <algorithm>
40 #include <map>
42 #if defined(_WIN32)
43 #if !defined WIN32_LEAN_AND_MEAN
44 # define WIN32_LEAN_AND_MEAN
45 #endif
46 #include <windows.h>
47 #endif
48 #include <string.h>
50 #include "sunjre.hxx"
51 #include "vendorlist.hxx"
52 #include "diagnostics.h"
53 #ifdef MACOSX
54 #include "util_cocoa.hxx"
55 #endif
57 using namespace osl;
58 using namespace std;
60 using ::rtl::Reference;
62 #ifdef _WIN32
63 #define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
64 #define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
65 #endif
67 #if defined( UNX ) && !defined( MACOSX )
68 namespace {
69 char const *g_arJavaNames[] = {
70 "",
71 "j2re",
72 "j2se",
73 "j2sdk",
74 "jdk",
75 "jre",
76 "java"
79 /* These are directory names which could contain multiple java installations.
81 char const *g_arCollectDirs[] = {
82 "",
83 #ifndef JVM_ONE_PATH_CHECK
84 "j2re/",
85 "j2se/",
86 "j2sdk/",
87 "jdk/",
88 "jre/",
89 "java/",
90 #endif
91 "jvm/"
94 /* These are directories in which a java installation is
95 looked for.
97 char const *g_arSearchPaths[] = {
98 #ifndef JVM_ONE_PATH_CHECK
99 "",
100 "usr/",
101 "usr/local/",
102 #ifdef X86_64
103 "usr/lib64/",
104 #endif
105 "usr/lib/",
106 "usr/bin/"
107 #else
108 JVM_ONE_PATH_CHECK
109 #endif
112 #endif // UNX && !MACOSX
114 namespace jfw_plugin
116 #if defined(_WIN32)
117 static bool getSDKInfoFromRegistry(vector<OUString> & vecHome);
118 static bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome);
119 #endif
121 static bool decodeOutput(const OString& s, OUString* out);
124 namespace
127 bool addJREInfo(
128 rtl::Reference<VendorBase> const & info,
129 std::vector<rtl::Reference<VendorBase>> & infos)
131 if (std::none_of(infos.begin(), infos.end(), InfoFindSame(info->getHome()))) {
132 infos.push_back(info);
133 return true;
134 } else {
135 return false;
139 bool getAndAddJREInfoByPath(
140 const OUString& path,
141 std::vector<rtl::Reference<VendorBase> > & allInfos,
142 std::vector<rtl::Reference<VendorBase> > & addedInfos)
144 rtl::Reference<VendorBase> aInfo = getJREInfoByPath(path);
145 if (aInfo.is()) {
146 if (addJREInfo(aInfo, allInfos)) {
147 addedInfos.push_back(aInfo);
149 return true;
150 } else {
151 return false;
155 OUString getLibraryLocation()
157 OUString libraryFileUrl;
158 OSL_VERIFY(osl::Module::getUrlFromAddress(reinterpret_cast<void *>(getLibraryLocation), libraryFileUrl));
159 return getDirFromFile(libraryFileUrl);
162 struct InitBootstrap
164 rtl::Bootstrap * operator()(const OUString& sIni)
166 static rtl::Bootstrap aInstance(sIni);
167 return & aInstance;
172 struct InitBootstrapData
174 OUString const & operator()()
176 static OUString sIni;
177 OUStringBuffer buf( 255);
178 buf.append( getLibraryLocation());
179 #ifdef MACOSX
180 buf.append( "/../" LIBO_ETC_FOLDER );
181 #endif
182 buf.append( SAL_CONFIGFILE("/sunjavaplugin") );
183 sIni = buf.makeStringAndClear();
184 JFW_TRACE2("Using configuration file " << sIni);
185 return sIni;
190 rtl::Bootstrap * getBootstrap()
192 return rtl_Instance< rtl::Bootstrap, InitBootstrap,
193 ::osl::MutexGuard, ::osl::GetGlobalMutex,
194 OUString, InitBootstrapData >::create(
195 InitBootstrap(), ::osl::GetGlobalMutex(), InitBootstrapData());
199 class FileHandleGuard
201 public:
202 explicit FileHandleGuard(oslFileHandle & rHandle):
203 m_rHandle(rHandle) {}
205 inline ~FileHandleGuard();
207 FileHandleGuard(const FileHandleGuard&) = delete;
208 FileHandleGuard& operator=(const FileHandleGuard&) = delete;
210 oslFileHandle & getHandle() { return m_rHandle; }
212 private:
213 oslFileHandle & m_rHandle;
216 inline FileHandleGuard::~FileHandleGuard()
218 if (m_rHandle != nullptr)
220 if (osl_closeFile(m_rHandle) != osl_File_E_None)
222 OSL_FAIL("unexpected situation");
228 class FileHandleReader
230 public:
231 enum Result
233 RESULT_OK,
234 RESULT_EOF,
235 RESULT_ERROR
238 explicit FileHandleReader(oslFileHandle & rHandle):
239 m_aGuard(rHandle), m_nSize(0), m_nIndex(0), m_bLf(false) {}
241 Result readLine(OString * pLine);
243 private:
244 enum { BUFFER_SIZE = 1024 };
246 sal_Char m_aBuffer[BUFFER_SIZE];
247 FileHandleGuard m_aGuard;
248 int m_nSize;
249 int m_nIndex;
250 bool m_bLf;
253 FileHandleReader::Result
254 FileHandleReader::readLine(OString * pLine)
256 OSL_ENSURE(pLine, "specification violation");
258 for (bool bEof = true;; bEof = false)
260 if (m_nIndex == m_nSize)
262 sal_uInt64 nRead = 0;
263 switch (osl_readFile(
264 m_aGuard.getHandle(), m_aBuffer, sizeof(m_aBuffer), &nRead))
266 case osl_File_E_PIPE: //HACK! for windows
267 nRead = 0;
268 SAL_FALLTHROUGH;
269 case osl_File_E_None:
270 if (nRead == 0)
272 m_bLf = false;
273 return bEof ? RESULT_EOF : RESULT_OK;
275 m_nIndex = 0;
276 m_nSize = static_cast< int >(nRead);
277 break;
278 case osl_File_E_INTR:
279 continue;
281 default:
282 return RESULT_ERROR;
286 if (m_bLf && m_aBuffer[m_nIndex] == 0x0A)
287 ++m_nIndex;
288 m_bLf = false;
290 int nStart = m_nIndex;
291 while (m_nIndex != m_nSize)
292 switch (m_aBuffer[m_nIndex++])
294 case 0x0D:
295 m_bLf = true;
296 SAL_FALLTHROUGH;
297 case 0x0A:
298 *pLine += OString(m_aBuffer + nStart,
299 m_nIndex - 1 - nStart);
300 //TODO! check for overflow, and not very efficient
301 return RESULT_OK;
304 *pLine += OString(m_aBuffer + nStart, m_nIndex - nStart);
305 //TODO! check for overflow, and not very efficient
309 class AsynchReader: public salhelper::Thread
311 size_t m_nDataSize;
312 std::unique_ptr<sal_Char[]> m_arData;
314 FileHandleGuard m_aGuard;
316 virtual ~AsynchReader() override {}
318 void execute() override;
319 public:
321 explicit AsynchReader(oslFileHandle & rHandle);
323 /** only call this function after this thread has finished.
325 That is, call join on this instance and then call getData.
328 OString getData();
331 AsynchReader::AsynchReader(oslFileHandle & rHandle):
332 Thread("jvmfwkAsyncReader"), m_nDataSize(0),
333 m_aGuard(rHandle)
337 OString AsynchReader::getData()
339 return OString(m_arData.get(), m_nDataSize);
342 void AsynchReader::execute()
344 const sal_uInt64 BUFFER_SIZE = 4096;
345 sal_Char aBuffer[BUFFER_SIZE];
346 while (true)
348 sal_uInt64 nRead;
349 //the function blocks until something could be read or the pipe closed.
350 switch (osl_readFile(
351 m_aGuard.getHandle(), aBuffer, BUFFER_SIZE, &nRead))
353 case osl_File_E_PIPE: //HACK! for windows
354 nRead = 0;
355 SAL_FALLTHROUGH;
356 case osl_File_E_None:
357 break;
358 default:
359 return;
362 if (nRead == 0)
364 break;
366 else if (nRead <= BUFFER_SIZE)
368 //Save the data we have in m_arData into a temporary array
369 std::unique_ptr<sal_Char[]> arTmp( new sal_Char[m_nDataSize]);
370 memcpy(arTmp.get(), m_arData.get(), m_nDataSize);
371 //Enlarge m_arData to hold the newly read data
372 m_arData.reset(new sal_Char[static_cast<size_t>(m_nDataSize + nRead)]);
373 //Copy back the data that was already in m_arData
374 memcpy(m_arData.get(), arTmp.get(), m_nDataSize);
375 //Add the newly read data to m_arData
376 memcpy(m_arData.get() + m_nDataSize, aBuffer, static_cast<size_t>(nRead));
377 m_nDataSize += static_cast<size_t>(nRead);
382 static bool isEnvVarSetToOne(const OUString &aVar)
384 OUString aValue;
385 getBootstrap()->getFrom(aVar, aValue);
386 return aValue == "1";
389 bool getJavaProps(const OUString & exePath,
390 #ifdef JVM_ONE_PATH_CHECK
391 const OUString & homePath,
392 #endif
393 std::vector<std::pair<OUString, OUString> >& props,
394 bool * bProcessRun)
396 bool ret = false;
398 OSL_ASSERT(!exePath.isEmpty());
399 OUString usStartDir;
400 //We need to set the CLASSPATH in case the office is started from
401 //a different directory. The JREProperties.class is expected to reside
402 //next to the plugin, except on OS X where it is in ../Resources/java relative
403 //to the plugin.
404 OUString sThisLib;
405 if (!osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps),
406 & sThisLib.pData))
408 return false;
410 sThisLib = getDirFromFile(sThisLib);
411 OUString sClassPath;
412 if (osl_getSystemPathFromFileURL(sThisLib.pData, & sClassPath.pData)
413 != osl_File_E_None)
415 return false;
418 #ifdef MACOSX
419 if (!JvmfwkUtil_isLoadableJVM(exePath))
420 return false;
421 if (sClassPath.endsWith("/"))
422 sClassPath += "../Resources/java/";
423 else
424 sClassPath += "/../Resources/java";
425 #endif
427 #ifdef UNX
428 // Java is no longer required for a11y - we use atk directly.
429 bool bNoAccessibility = !isEnvVarSetToOne("JFW_PLUGIN_FORCE_ACCESSIBILITY");
430 #else
431 //check if we shall examine a Java for accessibility support
432 //If the bootstrap variable is "1" then we pass the argument
433 //"noaccessibility" to JREProperties.class. This will prevent
434 //that it calls java.awt.Toolkit.getDefaultToolkit();
435 bool bNoAccessibility = isEnvVarSetToOne("JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY");
436 #endif
438 //prepare the arguments
439 sal_Int32 cArgs = 3;
440 OUString arg1 = "-classpath";// + sClassPath;
441 OUString arg2 = sClassPath;
442 OUString arg3("JREProperties");
443 OUString arg4 = "noaccessibility";
444 rtl_uString *args[4] = {arg1.pData, arg2.pData, arg3.pData};
445 // Only add the fourth param if the bootstrap parameter is set.
446 if (bNoAccessibility)
448 args[3] = arg4.pData;
449 cArgs = 4;
452 oslProcess javaProcess= nullptr;
453 oslFileHandle fileOut= nullptr;
454 oslFileHandle fileErr= nullptr;
456 FileHandleReader stdoutReader(fileOut);
457 rtl::Reference< AsynchReader > stderrReader(new AsynchReader(fileErr));
459 JFW_TRACE2("Executing: " + exePath);
460 oslProcessError procErr =
461 osl_executeProcess_WithRedirectedIO( exePath.pData,//usExe.pData,
462 args,
463 cArgs, //sal_uInt32 nArguments,
464 osl_Process_HIDDEN, //oslProcessOption Options,
465 nullptr, //oslSecurity Security,
466 usStartDir.pData,//usStartDir.pData,//usWorkDir.pData, //rtl_uString *strWorkDir,
467 nullptr, //rtl_uString *strEnvironment[],
468 0, // sal_uInt32 nEnvironmentVars,
469 &javaProcess, //oslProcess *pProcess,
470 nullptr,//oslFileHandle *pChildInputWrite,
471 &fileOut,//oslFileHandle *pChildOutputRead,
472 &fileErr);//oslFileHandle *pChildErrorRead);
474 if( procErr != osl_Process_E_None)
476 JFW_TRACE2("Execution failed");
477 *bProcessRun = false;
478 SAL_WARN("jfw",
479 "osl_executeProcess failed (" << ret << "): \"" << exePath << "\"");
480 return ret;
482 else
484 JFW_TRACE2("Java executed successfully");
485 *bProcessRun = true;
488 //Start asynchronous reading (different thread) of error stream
489 stderrReader->launch();
491 //Use this thread to read output stream
492 FileHandleReader::Result rs = FileHandleReader::RESULT_OK;
493 JFW_TRACE2("Properties found:");
494 while (true)
496 OString aLine;
497 rs = stdoutReader.readLine( & aLine);
498 if (rs != FileHandleReader::RESULT_OK)
499 break;
500 OUString sLine;
501 if (!decodeOutput(aLine, &sLine))
502 continue;
503 JFW_TRACE2(" \"" << sLine << "\"");
504 sLine = sLine.trim();
505 if (sLine.isEmpty())
506 continue;
507 //The JREProperties class writes key value pairs, separated by '='
508 sal_Int32 index = sLine.indexOf('=');
509 OSL_ASSERT(index != -1);
510 OUString sKey = sLine.copy(0, index);
511 OUString sVal = sLine.copy(index + 1);
513 #ifdef JVM_ONE_PATH_CHECK
514 //replace absolute path by linux distro link
515 OUString sHomeProperty("java.home");
516 if(sHomeProperty.equals(sKey))
518 sVal = homePath + "/jre";
520 #endif
522 props.emplace_back(sKey, sVal);
525 if (rs != FileHandleReader::RESULT_ERROR && !props.empty())
526 ret = true;
528 //process error stream data
529 stderrReader->join();
530 JFW_TRACE2("Java wrote to stderr:\" "
531 << stderrReader->getData() << " \"");
533 TimeValue waitMax= {5 ,0};
534 procErr = osl_joinProcessWithTimeout(javaProcess, &waitMax);
535 OSL_ASSERT(procErr == osl_Process_E_None);
536 osl_freeProcessHandle(javaProcess);
537 return ret;
540 /* converts the properties printed by JREProperties.class into
541 readable strings. The strings are encoded as integer values separated
542 by spaces.
544 bool decodeOutput(const OString& s, OUString* out)
546 OSL_ASSERT(out != nullptr);
547 OUStringBuffer buff(512);
548 sal_Int32 nIndex = 0;
551 OString aToken = s.getToken( 0, ' ', nIndex );
552 if (!aToken.isEmpty())
554 for (sal_Int32 i = 0; i < aToken.getLength(); ++i)
556 if (aToken[i] < '0' || aToken[i] > '9')
557 return false;
559 sal_Unicode value = static_cast<sal_Unicode>(aToken.toInt32());
560 buff.append(value);
562 } while (nIndex >= 0);
564 *out = buff.makeStringAndClear();
565 return true;
569 #if defined(_WIN32)
571 static bool getJavaInfoFromRegistry(const wchar_t* szRegKey,
572 vector<OUString>& vecJavaHome)
574 HKEY hRoot;
575 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ENUMERATE_SUB_KEYS, &hRoot)
576 == ERROR_SUCCESS)
578 DWORD dwIndex = 0;
579 const DWORD BUFFSIZE = 1024;
580 wchar_t bufVersion[BUFFSIZE];
581 FILETIME fileTime;
582 DWORD nNameLen = sizeof(bufVersion);
584 // Iterate over all subkeys of HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment
585 while (RegEnumKeyExW(hRoot, dwIndex, bufVersion, &nNameLen, nullptr, nullptr, nullptr, &fileTime) != ERROR_NO_MORE_ITEMS)
587 HKEY hKey;
588 // Open a Java Runtime Environment sub key, e.g. "1.4.0"
589 if (RegOpenKeyExW(hRoot, bufVersion, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
591 DWORD dwType;
592 DWORD dwTmpPathLen= 0;
593 // Get the path to the JavaHome every JRE entry
594 // Find out how long the string for JavaHome is and allocate memory to hold the path
595 if( RegQueryValueExW(hKey, L"JavaHome", nullptr, &dwType, nullptr, &dwTmpPathLen)== ERROR_SUCCESS)
597 unsigned char* szTmpPath= static_cast<unsigned char *>(malloc(dwTmpPathLen+sizeof(sal_Unicode)));
598 // According to https://msdn.microsoft.com/en-us/ms724911, the application should ensure
599 // that the string is properly terminated before using it
600 for (DWORD i = 0; i < sizeof(sal_Unicode); ++i)
601 szTmpPath[dwTmpPathLen + i] = 0;
602 // Get the path for the runtime lib
603 if(RegQueryValueExW(hKey, L"JavaHome", nullptr, &dwType, szTmpPath, &dwTmpPathLen) == ERROR_SUCCESS)
605 // There can be several version entries referring with the same JavaHome,e.g 1.4 and 1.4.1
606 OUString usHome(reinterpret_cast<sal_Unicode*>(szTmpPath));
607 // check if there is already an entry with the same JavaHomeruntime lib
608 // if so, we use the one with the more accurate version
609 OUString usHomeUrl;
610 if (osl_getFileURLFromSystemPath(usHome.pData, & usHomeUrl.pData) ==
611 osl_File_E_None)
613 bool bAppend= true;
614 //iterate over the vector with java home strings
615 for (auto const& javaHome : vecJavaHome)
617 if(usHomeUrl.equals(javaHome))
619 bAppend= false;
620 break;
623 // Save the home dir
624 if(bAppend)
626 vecJavaHome.push_back(usHomeUrl);
630 free( szTmpPath);
631 RegCloseKey(hKey);
634 dwIndex ++;
635 nNameLen = BUFFSIZE;
637 RegCloseKey(hRoot);
639 return true;
643 bool getSDKInfoFromRegistry(vector<OUString> & vecHome)
645 return getJavaInfoFromRegistry(HKEY_SUN_SDK, vecHome);
648 bool getJREInfoFromRegistry(vector<OUString>& vecJavaHome)
650 return getJavaInfoFromRegistry(HKEY_SUN_JRE, vecJavaHome);
653 static void addJavaInfoFromWinReg(
654 std::vector<rtl::Reference<VendorBase> > & allInfos,
655 std::vector<rtl::Reference<VendorBase> > & addedInfos)
657 // Get Java s from registry
658 std::vector<OUString> vecJavaHome;
659 if(getSDKInfoFromRegistry(vecJavaHome))
661 // create impl objects
662 for (auto const& javaHome : vecJavaHome)
664 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
668 vecJavaHome.clear();
669 if(getJREInfoFromRegistry(vecJavaHome))
671 for (auto const& javaHome : vecJavaHome)
673 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
677 vecJavaHome.clear();
678 if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JDK", vecJavaHome)) {
679 for (auto const & javaHome: vecJavaHome) {
680 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
684 vecJavaHome.clear();
685 if (getJavaInfoFromRegistry(L"Software\\JavaSoft\\JRE", vecJavaHome)) {
686 for (auto const & javaHome: vecJavaHome) {
687 getAndAddJREInfoByPath(javaHome, allInfos, addedInfos);
692 #endif // WNT
694 void bubbleSortVersion(vector<rtl::Reference<VendorBase> >& vec)
696 if(vec.empty())
697 return;
698 int size= vec.size() - 1;
699 int cIter= 0;
700 // sort for version
701 for(int i= 0; i < size; i++)
703 for(int j= size; j > 0 + cIter; j--)
705 rtl::Reference<VendorBase>& cur= vec.at(j);
706 rtl::Reference<VendorBase>& next= vec.at(j-1);
708 int nCmp = 0;
709 // comparing invalid SunVersion s is possible, they will be less than a
710 // valid version
712 //check if version of current is recognized, by comparing it with itself
715 (void)cur->compareVersions(cur->getVersion());
717 catch (MalformedVersionException &)
719 nCmp = -1; // current < next
721 //The version of cur is valid, now compare with the second version
722 if (nCmp == 0)
726 nCmp = cur->compareVersions(next->getVersion());
728 catch (MalformedVersionException & )
730 //The second version is invalid, therefore it regards less.
731 nCmp = 1;
734 if(nCmp == 1) // cur > next
736 std::swap(vec.at(j-1), vec.at(j));
739 ++cIter;
744 void addJREInfoFromBinPath(
745 const OUString& path, vector<rtl::Reference<VendorBase>> & allInfos,
746 std::vector<rtl::Reference<VendorBase>> & addedInfos)
748 // file:///c:/jre/bin
749 //map: jre/bin/java.exe
751 for ( sal_Int32 pos = 0;
752 gVendorMap[pos].sVendorName != nullptr; ++pos )
754 vector<OUString> vecPaths;
755 getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
757 int size = 0;
758 char const* const* arExePaths = (*pFunc)(&size);
759 vecPaths = getVectorFromCharArray(arExePaths, size);
761 //make sure argument path does not end with '/'
762 OUString sBinPath = path;
763 if (path.endsWith("/"))
764 sBinPath = path.copy(0, path.getLength() - 1);
766 for (auto const& looppath : vecPaths)
768 //the map contains e.g. jre/bin/java.exe
769 //get the directory where the executable is contained
770 OUString sHome;
771 sal_Int32 index = looppath.lastIndexOf('/');
772 if (index == -1)
774 //map contained only : "java.exe, then the argument
775 //path is already the home directory
776 sHome = sBinPath;
778 else
780 // jre/bin/jre -> jre/bin
781 OUString sMapPath = looppath.copy(0, index);
782 index = sBinPath.lastIndexOf(sMapPath);
783 if (index != -1
784 && (index + sMapPath.getLength() == sBinPath.getLength())
785 && sBinPath[index - 1] == '/')
787 sHome = sBinPath.copy(index - 1);
790 if (!sHome.isEmpty()
791 && getAndAddJREInfoByPath(path, allInfos, addedInfos))
793 return;
799 vector<Reference<VendorBase> > addAllJREInfos(
800 bool checkJavaHomeAndPath,
801 std::vector<rtl::Reference<VendorBase>> & allInfos)
803 vector<Reference<VendorBase> > addedInfos;
805 #if defined(_WIN32)
806 // Get Javas from the registry
807 addJavaInfoFromWinReg(allInfos, addedInfos);
808 #endif // WNT
810 if (checkJavaHomeAndPath) {
811 addJavaInfoFromJavaHome(allInfos, addedInfos);
812 //this function should be called after addJavaInfosDirScan.
813 //Otherwise in SDKs Java may be started twice
814 addJavaInfosFromPath(allInfos, addedInfos);
817 #ifdef UNX
818 addJavaInfosDirScan(allInfos, addedInfos);
819 #endif
821 bubbleSortVersion(addedInfos);
822 return addedInfos;
826 vector<OUString> getVectorFromCharArray(char const * const * ar, int size)
828 vector<OUString> vec;
829 for( int i = 0; i < size; i++)
831 OUString s(ar[i], strlen(ar[i]), RTL_TEXTENCODING_UTF8);
832 vec.push_back(s);
834 return vec;
837 /** Checks if the path is a directory. Links are resolved.
838 In case of an error the returned string has the length 0.
839 Otherwise the returned string is the "resolved" file URL.
841 static OUString resolveDirPath(const OUString & path)
843 OUString ret;
844 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type |
845 osl_FileStatus_Mask_FileURL);
846 if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None)
848 //check if this is a directory
849 if (aResolver.m_aStatus.getFileType() == FileStatus::Directory)
851 #ifndef JVM_ONE_PATH_CHECK
852 ret = aResolver.m_aStatus.getFileURL();
853 #else
854 ret = path;
855 #endif
858 return ret;
860 /** Checks if the path is a file. If it is a link to a file than
861 it is resolved.
863 static OUString resolveFilePath(const OUString & path)
865 OUString ret;
866 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_Type |
867 osl_FileStatus_Mask_FileURL);
868 if (aResolver.fetchFileStatus(path) == osl::FileBase::E_None)
870 //check if this is a file
871 if (aResolver.m_aStatus.getFileType() == FileStatus::Regular)
873 #ifndef JVM_ONE_PATH_CHECK
874 ret = aResolver.m_aStatus.getFileURL();
875 #else
876 ret = path;
877 #endif
880 return ret;
883 rtl::Reference<VendorBase> getJREInfoByPath(
884 const OUString& path)
886 rtl::Reference<VendorBase> ret;
887 static vector<OUString> vecBadPaths;
889 static map<OUString, rtl::Reference<VendorBase> > mapJREs;
890 OUString sFilePath;
891 vector<pair<OUString, OUString> > props;
893 OUString sResolvedDir = resolveDirPath(path);
894 // If this path is invalid then there is no chance to find a JRE here
895 if (sResolvedDir.isEmpty())
897 return nullptr;
900 //check if the directory path is good, that is a JRE was already recognized.
901 //Then we need not detect it again
902 //For example, a sun JDK contains <jdk>/bin/java and <jdk>/jre/bin/java.
903 //When <jdk>/bin/java has been found then we need not find <jdk>/jre/bin/java.
904 //Otherwise we would execute java two times for every JDK found.
905 auto entry2 = find_if(mapJREs.cbegin(), mapJREs.cend(),
906 SameOrSubDirJREMap(sResolvedDir));
907 if (entry2 != mapJREs.end())
909 JFW_TRACE2("JRE found again (detected before): " << sResolvedDir);
910 return entry2->second;
913 for ( sal_Int32 pos = 0;
914 gVendorMap[pos].sVendorName != nullptr; ++pos )
916 vector<OUString> vecPaths;
917 getJavaExePaths_func pFunc = gVendorMap[pos].getJavaFunc;
919 int size = 0;
920 char const* const* arExePaths = (*pFunc)(&size);
921 vecPaths = getVectorFromCharArray(arExePaths, size);
923 bool bBreak = false;
924 for (auto const& looppath : vecPaths)
926 //if the path is a link, then resolve it
927 //check if the executable exists at all
929 //path can be only "file:///". Then do not append a '/'
930 //sizeof counts the terminating 0
931 OUString sFullPath;
932 if (path.getLength() == sizeof("file:///") - 1)
933 sFullPath = sResolvedDir + looppath;
934 else
935 sFullPath = sResolvedDir + "/" + looppath;
937 sFilePath = resolveFilePath(sFullPath);
939 if (sFilePath.isEmpty())
941 //The file path (to java exe) is not valid
942 auto ifull = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFullPath);
943 if (ifull == vecBadPaths.cend())
945 vecBadPaths.push_back(sFullPath);
947 continue;
950 auto ifile = find(vecBadPaths.cbegin(), vecBadPaths.cend(), sFilePath);
951 if (ifile != vecBadPaths.cend())
953 continue;
956 auto entry = mapJREs.find(sFilePath);
957 if (entry != mapJREs.end())
959 JFW_TRACE2("JRE found again (detected before): " << sFilePath);
961 return entry->second;
964 bool bProcessRun= false;
965 if (!getJavaProps(sFilePath,
966 #ifdef JVM_ONE_PATH_CHECK
967 sResolvedDir,
968 #endif
969 props, & bProcessRun))
971 //The java executable could not be run or the system properties
972 //could not be retrieved. We can assume that this java is corrupt.
973 vecBadPaths.push_back(sFilePath);
974 //If there was a java executable, that could be run but we did not get
975 //the system properties, then we also assume that the whole Java installation
976 //does not work. In a jdk there are two executables. One in jdk/bin and the other
977 //in jdk/jre/bin. We do not search any further, because we assume that if one java
978 //does not work then the other does not work as well. This saves us to run java
979 //again which is quite costly.
980 if (bProcessRun)
982 // 1.3.1 special treatment: jdk/bin/java and /jdk/jre/bin/java are links to
983 //a script, named .java_wrapper. The script starts jdk/bin/sparc/native_threads/java
984 //or jdk/jre/bin/sparc/native_threads/java. The script uses the name with which it was
985 //invoked to build the path to the executable. It we start the script directly as .java_wrapper
986 //then it tries to start a jdk/.../native_threads/.java_wrapper. Therefore the link, which
987 //is named java, must be used to start the script.
988 getJavaProps(sFullPath,
989 #ifdef JVM_ONE_PATH_CHECK
990 sResolvedDir,
991 #endif
992 props, & bProcessRun);
993 // Either we found a working 1.3.1
994 // Or the java is broken. In both cases we stop searching under this "root" directory
995 bBreak = true;
996 break;
998 //sFilePath is no working java executable. We continue with another possible
999 //path.
1000 else
1002 continue;
1005 //sFilePath is a java and we could get the system properties. We proceed with this
1006 //java.
1007 else
1009 bBreak = true;
1010 break;
1013 if (bBreak)
1014 break;
1017 if (props.empty())
1019 return rtl::Reference<VendorBase>();
1022 //find java.vendor property
1023 OUString sVendorName;
1025 for (auto const& prop : props)
1027 if (prop.first == "java.vendor")
1029 sVendorName = prop.second;
1030 break;
1034 auto knownVendor = false;
1035 if (!sVendorName.isEmpty())
1037 //find the creator func for the respective vendor name
1038 for ( sal_Int32 c = 0;
1039 gVendorMap[c].sVendorName != nullptr; ++c )
1041 OUString sNameMap(gVendorMap[c].sVendorName, strlen(gVendorMap[c].sVendorName),
1042 RTL_TEXTENCODING_ASCII_US);
1043 if (sNameMap == sVendorName)
1045 ret = createInstance(gVendorMap[c].createFunc, props);
1046 knownVendor = true;
1047 break;
1051 // For unknown vendors, try SunInfo as fallback:
1052 if (!knownVendor)
1054 ret = createInstance(SunInfo::createInstance, props);
1056 if (!ret.is())
1058 vecBadPaths.push_back(sFilePath);
1060 else
1062 JFW_TRACE2("Found JRE: " << sResolvedDir << " at: " << path);
1064 mapJREs.emplace(sResolvedDir, ret);
1065 mapJREs.emplace(sFilePath, ret);
1068 return ret;
1071 Reference<VendorBase> createInstance(createInstance_func pFunc,
1072 const vector<pair<OUString, OUString> >& properties)
1075 Reference<VendorBase> aBase = (*pFunc)();
1076 if (aBase.is())
1078 if (!aBase->initialize(properties))
1079 aBase = nullptr;
1081 return aBase;
1084 inline OUString getDirFromFile(const OUString& usFilePath)
1086 sal_Int32 index = usFilePath.lastIndexOf('/');
1087 return usFilePath.copy(0, index);
1090 void addJavaInfosFromPath(
1091 std::vector<rtl::Reference<VendorBase>> & allInfos,
1092 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1094 #if !defined JVM_ONE_PATH_CHECK
1095 // Get Java from PATH environment variable
1096 char *szPath= getenv("PATH");
1097 if(szPath)
1099 OUString usAllPath(szPath, strlen(szPath), osl_getThreadTextEncoding());
1100 sal_Int32 nIndex = 0;
1103 OUString usToken = usAllPath.getToken( 0, SAL_PATHSEPARATOR, nIndex );
1104 OUString usTokenUrl;
1105 if(File::getFileURLFromSystemPath(usToken, usTokenUrl) == File::E_None)
1107 if(!usTokenUrl.isEmpty())
1109 OUString usBin;
1110 if(usTokenUrl == ".")
1112 OUString usWorkDirUrl;
1113 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDirUrl.pData))
1114 usBin= usWorkDirUrl;
1116 else if(usTokenUrl == "..")
1118 OUString usWorkDir;
1119 if(osl_Process_E_None == osl_getProcessWorkingDir(&usWorkDir.pData))
1120 usBin= getDirFromFile(usWorkDir);
1122 else
1124 usBin = usTokenUrl;
1126 if(!usBin.isEmpty())
1128 addJREInfoFromBinPath(usBin, allInfos, addedInfos);
1133 while ( nIndex >= 0 );
1135 #endif
1139 void addJavaInfoFromJavaHome(
1140 std::vector<rtl::Reference<VendorBase>> & allInfos,
1141 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1143 #if !defined JVM_ONE_PATH_CHECK
1144 // Get Java from JAVA_HOME environment
1146 // Note that on OS X is it not normal at all to have a JAVA_HOME environment
1147 // variable. We set it in our build environment for build-time programs, though,
1148 // so it is set when running unit tests that involve Java functionality. (Which affects
1149 // at least CppunitTest_dbaccess_dialog_save, too, and not only the JunitTest ones.)
1150 char *szJavaHome= getenv("JAVA_HOME");
1151 if(szJavaHome)
1153 OUString sHome(szJavaHome, strlen(szJavaHome), osl_getThreadTextEncoding());
1154 OUString sHomeUrl;
1155 if(File::getFileURLFromSystemPath(sHome, sHomeUrl) == File::E_None)
1157 getAndAddJREInfoByPath(sHomeUrl, allInfos, addedInfos);
1160 #endif
1163 bool makeDriveLetterSame(OUString * fileURL)
1165 bool ret = false;
1166 DirectoryItem item;
1167 if (DirectoryItem::get(*fileURL, item) == File::E_None)
1169 FileStatus status(osl_FileStatus_Mask_FileURL);
1170 if (item.getFileStatus(status) == File::E_None)
1172 *fileURL = status.getFileURL();
1173 ret = true;
1176 return ret;
1179 #ifdef UNX
1180 #ifdef __sun
1182 void addJavaInfosDirScan(
1183 std::vector<rtl::Reference<VendorBase>> & allInfos,
1184 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1186 JFW_TRACE2("Checking /usr/jdk/latest");
1187 getAndAddJREInfoByPath("file:////usr/jdk/latest", allInfos, addedInfos);
1190 #else
1191 void addJavaInfosDirScan(
1192 std::vector<rtl::Reference<VendorBase>> & allInfos,
1193 std::vector<rtl::Reference<VendorBase>> & addedInfos)
1195 #ifdef MACOSX
1196 // Ignore all but Oracle's JDK as loading Apple's Java and Oracle's JRE
1197 // will cause OS X's JavaVM framework to display a dialog and invoke
1198 // exit() when loaded via JNI on OS X 10.10
1199 Directory aDir("file:///Library/Java/JavaVirtualMachines");
1200 if (aDir.open() == File::E_None)
1202 DirectoryItem aItem;
1203 while (aDir.getNextItem(aItem) == File::E_None)
1205 FileStatus aStatus(osl_FileStatus_Mask_FileURL);
1206 if (aItem.getFileStatus(aStatus) == File::E_None)
1208 OUString aItemURL( aStatus.getFileURL() );
1209 if (aItemURL.getLength())
1211 aItemURL += "/Contents/Home";
1212 if (DirectoryItem::get(aItemURL, aItem) == File::E_None)
1213 getAndAddJREInfoByPath(aItemURL, allInfos, addedInfos);
1217 aDir.close();
1219 #else // MACOSX
1220 OUString excMessage = "[Java framework] sunjavaplugin: "
1221 "Error in function addJavaInfosDirScan in util.cxx.";
1222 int cJavaNames= SAL_N_ELEMENTS(g_arJavaNames);
1223 std::unique_ptr<OUString[]> sarJavaNames(new OUString[cJavaNames]);
1224 OUString *arNames = sarJavaNames.get();
1225 for(int i= 0; i < cJavaNames; i++)
1226 arNames[i] = OUString(g_arJavaNames[i], strlen(g_arJavaNames[i]),
1227 RTL_TEXTENCODING_UTF8);
1229 int cSearchPaths= SAL_N_ELEMENTS(g_arSearchPaths);
1230 std::unique_ptr<OUString[]> sarPathNames(new OUString[cSearchPaths]);
1231 OUString *arPaths = sarPathNames.get();
1232 for(int c = 0; c < cSearchPaths; c++)
1233 arPaths[c] = OUString(g_arSearchPaths[c], strlen(g_arSearchPaths[c]),
1234 RTL_TEXTENCODING_UTF8);
1236 int cCollectDirs = SAL_N_ELEMENTS(g_arCollectDirs);
1237 std::unique_ptr<OUString[]> sarCollectDirs(new OUString[cCollectDirs]);
1238 OUString *arCollectDirs = sarCollectDirs.get();
1239 for(int d = 0; d < cCollectDirs; d++)
1240 arCollectDirs[d] = OUString(g_arCollectDirs[d], strlen(g_arCollectDirs[d]),
1241 RTL_TEXTENCODING_UTF8);
1244 for( int ii = 0; ii < cSearchPaths; ii ++)
1246 OUString usDir1("file:///" + arPaths[ii]);
1247 DirectoryItem item;
1248 if(DirectoryItem::get(usDir1, item) == File::E_None)
1250 for(int j= 0; j < cCollectDirs; j++)
1252 OUString usDir2(usDir1 + arCollectDirs[j]);
1253 // prevent that we scan the whole /usr, /usr/lib, etc directories
1254 if (!arCollectDirs[j].isEmpty())
1256 //usr/java/xxx
1257 //Examining every subdirectory
1258 Directory aCollectionDir(usDir2);
1260 Directory::RC openErr = aCollectionDir.open();
1261 switch (openErr)
1263 case File::E_None:
1264 break;
1265 case File::E_NOENT:
1266 case File::E_NOTDIR:
1267 continue;
1268 case File::E_ACCES:
1269 JFW_TRACE2("Could not read directory " << usDir2 << " because of missing access rights");
1270 continue;
1271 default:
1272 JFW_TRACE2("Could not read directory " << usDir2 << ". Osl file error: " << openErr);
1273 continue;
1276 DirectoryItem curIt;
1277 File::RC errNext = File::E_None;
1278 while( (errNext = aCollectionDir.getNextItem(curIt)) == File::E_None)
1280 FileStatus aStatus(osl_FileStatus_Mask_FileURL);
1281 File::RC errStatus = File::E_None;
1282 if ((errStatus = curIt.getFileStatus(aStatus)) != File::E_None)
1284 JFW_TRACE2(excMessage + "getFileStatus failed with error " << errStatus);
1285 continue;
1287 JFW_TRACE2("Checking if directory: " << aStatus.getFileURL() << " is a Java");
1289 getAndAddJREInfoByPath(
1290 aStatus.getFileURL(), allInfos, addedInfos);
1293 JFW_ENSURE(errNext == File::E_None || errNext == File::E_NOENT,
1294 "[Java framework] sunjavaplugin: "
1295 "Error while iterating over contents of "
1296 + usDir2 + ". Osl file error: "
1297 + OUString::number(openErr));
1299 else
1301 //usr/java
1302 //When we look directly into a dir like /usr, /usr/lib, etc. then we only
1303 //look for certain java directories, such as jre, jdk, etc. We do not want
1304 //to examine the whole directory because of performance reasons.
1305 DirectoryItem item2;
1306 if(DirectoryItem::get(usDir2, item2) == File::E_None)
1308 for( int k= 0; k < cJavaNames; k++)
1310 // /usr/java/j2re1.4.0
1311 OUString usDir3(usDir2 + arNames[k]);
1313 DirectoryItem item3;
1314 if(DirectoryItem::get(usDir3, item) == File::E_None)
1316 //remove trailing '/'
1317 sal_Int32 islash = usDir3.lastIndexOf('/');
1318 if (islash == usDir3.getLength() - 1
1319 && (islash
1320 > RTL_CONSTASCII_LENGTH("file://")))
1321 usDir3 = usDir3.copy(0, islash);
1322 getAndAddJREInfoByPath(
1323 usDir3, allInfos, addedInfos);
1331 #endif // MACOSX
1333 #endif // ifdef __sun
1334 #endif // ifdef UNX
1337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */