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_features.h>
21 #include <config_folders.h>
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>
43 #if !defined WIN32_LEAN_AND_MEAN
44 # define WIN32_LEAN_AND_MEAN
51 #include "vendorlist.hxx"
52 #include "diagnostics.h"
54 #include "util_cocoa.hxx"
60 using ::rtl::Reference
;
63 #define HKEY_SUN_JRE L"Software\\JavaSoft\\Java Runtime Environment"
64 #define HKEY_SUN_SDK L"Software\\JavaSoft\\Java Development Kit"
67 #if defined( UNX ) && !defined( MACOSX )
69 char const *g_arJavaNames
[] = {
79 /* These are directory names which could contain multiple java installations.
81 char const *g_arCollectDirs
[] = {
83 #ifndef JVM_ONE_PATH_CHECK
94 /* These are directories in which a java installation is
97 char const *g_arSearchPaths
[] = {
98 #ifndef JVM_ONE_PATH_CHECK
112 #endif // UNX && !MACOSX
117 static bool getSDKInfoFromRegistry(vector
<OUString
> & vecHome
);
118 static bool getJREInfoFromRegistry(vector
<OUString
>& vecJavaHome
);
121 static bool decodeOutput(const OString
& s
, OUString
* out
);
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
);
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
);
146 if (addJREInfo(aInfo
, allInfos
)) {
147 addedInfos
.push_back(aInfo
);
155 OUString
getLibraryLocation()
157 OUString libraryFileUrl
;
158 OSL_VERIFY(osl::Module::getUrlFromAddress(reinterpret_cast<void *>(getLibraryLocation
), libraryFileUrl
));
159 return getDirFromFile(libraryFileUrl
);
164 rtl::Bootstrap
* operator()(const OUString
& sIni
)
166 static rtl::Bootstrap
aInstance(sIni
);
172 struct InitBootstrapData
174 OUString
const & operator()()
176 static OUString sIni
;
177 OUStringBuffer
buf( 255);
178 buf
.append( getLibraryLocation());
180 buf
.append( "/../" LIBO_ETC_FOLDER
);
182 buf
.append( SAL_CONFIGFILE("/sunjavaplugin") );
183 sIni
= buf
.makeStringAndClear();
184 JFW_TRACE2("Using configuration file " << 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
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
; }
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
238 explicit FileHandleReader(oslFileHandle
& rHandle
):
239 m_aGuard(rHandle
), m_nSize(0), m_nIndex(0), m_bLf(false) {}
241 Result
readLine(OString
* pLine
);
244 enum { BUFFER_SIZE
= 1024 };
246 sal_Char m_aBuffer
[BUFFER_SIZE
];
247 FileHandleGuard m_aGuard
;
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
269 case osl_File_E_None
:
273 return bEof
? RESULT_EOF
: RESULT_OK
;
276 m_nSize
= static_cast< int >(nRead
);
278 case osl_File_E_INTR
:
286 if (m_bLf
&& m_aBuffer
[m_nIndex
] == 0x0A)
290 int nStart
= m_nIndex
;
291 while (m_nIndex
!= m_nSize
)
292 switch (m_aBuffer
[m_nIndex
++])
298 *pLine
+= OString(m_aBuffer
+ nStart
,
299 m_nIndex
- 1 - nStart
);
300 //TODO! check for overflow, and not very efficient
304 *pLine
+= OString(m_aBuffer
+ nStart
, m_nIndex
- nStart
);
305 //TODO! check for overflow, and not very efficient
309 class AsynchReader
: public salhelper::Thread
312 std::unique_ptr
<sal_Char
[]> m_arData
;
314 FileHandleGuard m_aGuard
;
316 virtual ~AsynchReader() override
{}
318 void execute() override
;
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.
331 AsynchReader::AsynchReader(oslFileHandle
& rHandle
):
332 Thread("jvmfwkAsyncReader"), m_nDataSize(0),
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
];
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
356 case osl_File_E_None
:
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
)
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
,
393 std::vector
<std::pair
<OUString
, OUString
> >& props
,
398 OSL_ASSERT(!exePath
.isEmpty());
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
405 if (!osl_getModuleURLFromAddress(reinterpret_cast<void *>(&getJavaProps
),
410 sThisLib
= getDirFromFile(sThisLib
);
412 if (osl_getSystemPathFromFileURL(sThisLib
.pData
, & sClassPath
.pData
)
419 if (!JvmfwkUtil_isLoadableJVM(exePath
))
421 if (sClassPath
.endsWith("/"))
422 sClassPath
+= "../Resources/java/";
424 sClassPath
+= "/../Resources/java";
428 // Java is no longer required for a11y - we use atk directly.
429 bool bNoAccessibility
= !isEnvVarSetToOne("JFW_PLUGIN_FORCE_ACCESSIBILITY");
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");
438 //prepare the arguments
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
;
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,
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;
479 "osl_executeProcess failed (" << ret
<< "): \"" << exePath
<< "\"");
484 JFW_TRACE2("Java executed successfully");
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:");
497 rs
= stdoutReader
.readLine( & aLine
);
498 if (rs
!= FileHandleReader::RESULT_OK
)
501 if (!decodeOutput(aLine
, &sLine
))
503 JFW_TRACE2(" \"" << sLine
<< "\"");
504 sLine
= sLine
.trim();
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";
522 props
.emplace_back(sKey
, sVal
);
525 if (rs
!= FileHandleReader::RESULT_ERROR
&& !props
.empty())
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
);
540 /* converts the properties printed by JREProperties.class into
541 readable strings. The strings are encoded as integer values separated
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')
559 sal_Unicode value
= static_cast<sal_Unicode
>(aToken
.toInt32());
562 } while (nIndex
>= 0);
564 *out
= buff
.makeStringAndClear();
571 static bool getJavaInfoFromRegistry(const wchar_t* szRegKey
,
572 vector
<OUString
>& vecJavaHome
)
575 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
, szRegKey
, 0, KEY_ENUMERATE_SUB_KEYS
, &hRoot
)
579 const DWORD BUFFSIZE
= 1024;
580 wchar_t bufVersion
[BUFFSIZE
];
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
)
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
)
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
610 if (osl_getFileURLFromSystemPath(usHome
.pData
, & usHomeUrl
.pData
) ==
614 //iterate over the vector with java home strings
615 for (auto const& javaHome
: vecJavaHome
)
617 if(usHomeUrl
.equals(javaHome
))
626 vecJavaHome
.push_back(usHomeUrl
);
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
);
669 if(getJREInfoFromRegistry(vecJavaHome
))
671 for (auto const& javaHome
: vecJavaHome
)
673 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
678 if (getJavaInfoFromRegistry(L
"Software\\JavaSoft\\JDK", vecJavaHome
)) {
679 for (auto const & javaHome
: vecJavaHome
) {
680 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
685 if (getJavaInfoFromRegistry(L
"Software\\JavaSoft\\JRE", vecJavaHome
)) {
686 for (auto const & javaHome
: vecJavaHome
) {
687 getAndAddJREInfoByPath(javaHome
, allInfos
, addedInfos
);
694 void bubbleSortVersion(vector
<rtl::Reference
<VendorBase
> >& vec
)
698 int size
= vec
.size() - 1;
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);
709 // comparing invalid SunVersion s is possible, they will be less than a
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
726 nCmp
= cur
->compareVersions(next
->getVersion());
728 catch (MalformedVersionException
& )
730 //The second version is invalid, therefore it regards less.
734 if(nCmp
== 1) // cur > next
736 std::swap(vec
.at(j
-1), vec
.at(j
));
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
;
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
771 sal_Int32 index
= looppath
.lastIndexOf('/');
774 //map contained only : "java.exe, then the argument
775 //path is already the home directory
780 // jre/bin/jre -> jre/bin
781 OUString sMapPath
= looppath
.copy(0, index
);
782 index
= sBinPath
.lastIndexOf(sMapPath
);
784 && (index
+ sMapPath
.getLength() == sBinPath
.getLength())
785 && sBinPath
[index
- 1] == '/')
787 sHome
= sBinPath
.copy(index
- 1);
791 && getAndAddJREInfoByPath(path
, allInfos
, addedInfos
))
799 vector
<Reference
<VendorBase
> > addAllJREInfos(
800 bool checkJavaHomeAndPath
,
801 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
)
803 vector
<Reference
<VendorBase
> > addedInfos
;
806 // Get Javas from the registry
807 addJavaInfoFromWinReg(allInfos
, addedInfos
);
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
);
818 addJavaInfosDirScan(allInfos
, addedInfos
);
821 bubbleSortVersion(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
);
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
)
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();
860 /** Checks if the path is a file. If it is a link to a file than
863 static OUString
resolveFilePath(const OUString
& path
)
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();
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
;
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())
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
;
920 char const* const* arExePaths
= (*pFunc
)(&size
);
921 vecPaths
= getVectorFromCharArray(arExePaths
, size
);
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
932 if (path
.getLength() == sizeof("file:///") - 1)
933 sFullPath
= sResolvedDir
+ looppath
;
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
);
950 auto ifile
= find(vecBadPaths
.cbegin(), vecBadPaths
.cend(), sFilePath
);
951 if (ifile
!= vecBadPaths
.cend())
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
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.
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
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
998 //sFilePath is no working java executable. We continue with another possible
1005 //sFilePath is a java and we could get the system properties. We proceed with this
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
;
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
);
1051 // For unknown vendors, try SunInfo as fallback:
1054 ret
= createInstance(SunInfo::createInstance
, props
);
1058 vecBadPaths
.push_back(sFilePath
);
1062 JFW_TRACE2("Found JRE: " << sResolvedDir
<< " at: " << path
);
1064 mapJREs
.emplace(sResolvedDir
, ret
);
1065 mapJREs
.emplace(sFilePath
, ret
);
1071 Reference
<VendorBase
> createInstance(createInstance_func pFunc
,
1072 const vector
<pair
<OUString
, OUString
> >& properties
)
1075 Reference
<VendorBase
> aBase
= (*pFunc
)();
1078 if (!aBase
->initialize(properties
))
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");
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())
1110 if(usTokenUrl
== ".")
1112 OUString usWorkDirUrl
;
1113 if(osl_Process_E_None
== osl_getProcessWorkingDir(&usWorkDirUrl
.pData
))
1114 usBin
= usWorkDirUrl
;
1116 else if(usTokenUrl
== "..")
1119 if(osl_Process_E_None
== osl_getProcessWorkingDir(&usWorkDir
.pData
))
1120 usBin
= getDirFromFile(usWorkDir
);
1126 if(!usBin
.isEmpty())
1128 addJREInfoFromBinPath(usBin
, allInfos
, addedInfos
);
1133 while ( nIndex
>= 0 );
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");
1153 OUString
sHome(szJavaHome
, strlen(szJavaHome
), osl_getThreadTextEncoding());
1155 if(File::getFileURLFromSystemPath(sHome
, sHomeUrl
) == File::E_None
)
1157 getAndAddJREInfoByPath(sHomeUrl
, allInfos
, addedInfos
);
1163 bool makeDriveLetterSame(OUString
* fileURL
)
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();
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
);
1191 void addJavaInfosDirScan(
1192 std::vector
<rtl::Reference
<VendorBase
>> & allInfos
,
1193 std::vector
<rtl::Reference
<VendorBase
>> & addedInfos
)
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
);
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
]);
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())
1257 //Examining every subdirectory
1258 Directory
aCollectionDir(usDir2
);
1260 Directory::RC openErr
= aCollectionDir
.open();
1266 case File::E_NOTDIR
:
1269 JFW_TRACE2("Could not read directory " << usDir2
<< " because of missing access rights");
1272 JFW_TRACE2("Could not read directory " << usDir2
<< ". Osl file error: " << openErr
);
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
);
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
));
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
1320 > RTL_CONSTASCII_LENGTH("file://")))
1321 usDir3
= usDir3
.copy(0, islash
);
1322 getAndAddJREInfoByPath(
1323 usDir3
, allInfos
, addedInfos
);
1333 #endif // ifdef __sun
1337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */