blob: 40945d0ef6a74322de8408d3c1804a54bf4d20b2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <windows.h>
#include "ignite/common/concurrent.h"
#include "ignite/common/utils.h"
#include "ignite/common/fixed_size_array.h"
#include "ignite/jni/utils.h"
#include "ignite/jni/java.h"
using namespace ignite::common;
using namespace ignite::common::concurrent;
using namespace ignite::jni::java;
namespace ignite
{
namespace jni
{
const char* JAVA_HOME = "JAVA_HOME";
const char* JAVA_DLL1 = "\\jre\\bin\\server\\jvm.dll";
const char* JAVA_DLL2 = "\\bin\\server\\jvm.dll";
const char* IGNITE_HOME = "IGNITE_HOME";
const char* IGNITE_NATIVE_TEST_CLASSPATH = "IGNITE_NATIVE_TEST_CLASSPATH";
/** Excluded modules from test classpath. */
const char* TEST_EXCLUDED_MODULES[] = { "rest-http" };
AttachHelper::~AttachHelper()
{
// No-op.
}
void AttachHelper::OnThreadAttach()
{
// No-op.
}
/**
* Checks if the path looks like binary release home directory.
* Internally checks for presence of core library.
* @return @c true if the path looks like binary release home directory.
*/
bool LooksLikeBinaryReleaseHome(const std::string& path)
{
static const char* PROBE_CORE_LIB = "\\libs\\ignite-core*.jar";
std::string coreLibProbe = path + PROBE_CORE_LIB;
return FileExists(coreLibProbe);
}
/**
* Checks if the path looks like source release home directory.
* Internally checks for presence of core source directory.
* @return @c true if the path looks like binary release home directory.
*/
bool LooksLikeSourceReleaseHome(const std::string& path)
{
static const char* PROBE_CORE_SOURCE = "\\modules\\core\\src\\main\\java\\org\\apache\\ignite";
std::string coreSourcePath = path + PROBE_CORE_SOURCE;
return IsValidDirectory(coreSourcePath);
}
/**
* Helper function for Ignite home resolution.
* Goes upwards in directory hierarchy and checks whether certain
* folders exist in the path.
*
* @param path Path to evaluate.
* @return res Resolved directory. Empty string if not found.
*/
std::string ResolveIgniteHome0(const std::string& path)
{
if (!IsValidDirectory(path))
return std::string();
// Remove trailing slashes, otherwise we will have an infinite loop.
size_t last = path.find_last_not_of("/\\ ");
if (last == std::string::npos)
return std::string();
std::string path0(path, 0, last + 1);
if (LooksLikeBinaryReleaseHome(path0) || LooksLikeSourceReleaseHome(path0))
return path0;
// Evaluate parent directory.
size_t slashPos = path0.find_last_of("/\\");
if (slashPos == std::string::npos)
return std::string();
std::string parent(path0, 0, slashPos);
return ResolveIgniteHome0(parent);
}
/**
* Create classpath picking JARs from the given path.
*
* @path Path.
* @return Classpath;
*/
std::string ClasspathJars(const std::string& path)
{
std::string searchPath = path + "\\*.jar";
WIN32_FIND_DATAA findData;
HANDLE hnd = FindFirstFileA(searchPath.c_str(), &findData);
if (hnd == INVALID_HANDLE_VALUE)
return std::string();
std::string res;
do
{
res.append(path);
res.append("\\");
res.append(findData.cFileName);
res.append(";");
} while (FindNextFileA(hnd, &findData) != 0);
FindClose(hnd);
return res;
}
/**
* Check if path corresponds to excluded module.
*
* @path Path.
* @return True if path should be excluded.
*/
bool IsExcludedModule(const std::string& path) {
std::string lower_path = path;
std::transform(path.begin(), path.end(), lower_path.begin(), ::tolower);
for (size_t i = 0; i < sizeof(TEST_EXCLUDED_MODULES) / sizeof(char*); i++) {
if (lower_path.find(TEST_EXCLUDED_MODULES[i]) != std::string::npos)
return true;
}
return false;
}
/**
* Create classpath picking compiled classes from the given path.
*
* @path Path.
* @return Classpath;
*/
std::string ClasspathExploded(const std::string& path, bool down)
{
std::string res;
if (FileExists(path) && !IsExcludedModule(path))
{
// 1. Append "target\classes".
std::string classesPath = path + "\\target\\classes";
if (FileExists(classesPath)) {
res.append(classesPath);
res.append(";");
}
// 2. Append "target\test-classes"
std::string testClassesPath = path + "\\target\\test-classes";
if (FileExists(testClassesPath)) {
res.append(testClassesPath);
res.append(";");
}
// 3. Append "target\libs"
std::string libsPath = path + "\\target\\libs";
if (FileExists(libsPath)) {
std::string libsCp = ClasspathJars(libsPath);
res.append(libsCp);
}
// 4. Do the same for child if needed.
if (down)
{
std::string searchPath = path + "\\*";
WIN32_FIND_DATAA findData;
HANDLE hnd = FindFirstFileA(searchPath.c_str(), &findData);
if (hnd != INVALID_HANDLE_VALUE)
{
do
{
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::string childPath = findData.cFileName;
if (childPath.compare(".") != 0 &&
childPath.compare("..") != 0)
{
std::string childCp =
ClasspathExploded(path + "\\" + childPath, false);
res.append(childCp);
}
}
} while (FindNextFileA(hnd, &findData) != 0);
FindClose(hnd);
}
}
}
return res;
}
std::string CreateIgniteHomeClasspath(const std::string& home, bool forceTest)
{
std::string res;
// 1. Add exploded test directories.
if (forceTest)
{
std::string examplesPath = home + "\\examples";
std::string examplesCp = ClasspathExploded(examplesPath, true);
res.append(examplesCp);
std::string modulesPath = home + "\\modules";
std::string modulesCp = ClasspathExploded(modulesPath, true);
res.append(modulesCp);
}
// 2. Add regular jars from "libs" folder excluding "optional".
std::string libsPath = home + "\\libs";
if (FileExists(libsPath))
{
res.append(ClasspathJars(libsPath));
// Append inner directories.
std::string libsSearchPath = libsPath + "\\*";
WIN32_FIND_DATAA libsFindData;
HANDLE libsHnd = FindFirstFileA(libsSearchPath.c_str(), &libsFindData);
if (libsHnd != INVALID_HANDLE_VALUE)
{
do
{
if (libsFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::string libsChildPath = libsFindData.cFileName;
if (libsChildPath.compare(".") != 0 &&
libsChildPath.compare("..") != 0 &&
libsChildPath.compare("optional") != 0) {
std::string libsFolder = libsPath + "\\" + libsChildPath;
res.append(ClasspathJars(libsFolder));
}
}
} while (FindNextFileA(libsHnd, &libsFindData) != 0);
FindClose(libsHnd);
}
}
// 3. Return.
return res;
}
std::string FindJvmLibrary(const std::string& path)
{
// If path is provided explicitly, then check only it.
if (!path.empty() && FileExists(path))
return path;
std::string javaEnv = GetEnv(JAVA_HOME);
if (!javaEnv.empty())
{
std::string javaDll = javaEnv + JAVA_DLL1;
if (FileExists(javaDll))
return javaDll;
javaDll = javaEnv + JAVA_DLL2;
if (FileExists(javaDll))
return javaDll;
}
return std::string();
}
bool LoadJvmLibrary(const std::string& path)
{
HMODULE mod = LoadLibraryA(path.c_str());
return mod != NULL;
}
/**
* Create Ignite classpath based on user input and home directory.
*
* @param usrCp User's classpath.
* @param home Ignite home directory.
* @param forceTest Whether test classpath must be used.
* @return Classpath.
*/
std::string CreateIgniteClasspath(const std::string& usrCp, const std::string* home, bool forceTest)
{
// 1. Append user classpath if it exists.
std::string cp;
if (!usrCp.empty())
{
cp.append(usrCp);
if (*cp.rbegin() != ';')
cp.push_back(';');
}
// 2. Append home classpath if home is defined.
if (home)
{
std::string homeCp = CreateIgniteHomeClasspath(*home, forceTest);
cp.append(homeCp);
}
// 3. Return.
return cp;
}
/**
* Adds semicolon at the end of the path if needed.
* @param usrCp Classpath provided by user.
* @return Normalized classpath.
*/
std::string NormalizeClasspath(const std::string& usrCp)
{
if (usrCp.empty() || *usrCp.rbegin() == ';')
return usrCp;
return usrCp + ';';
}
std::string CreateIgniteClasspath(const std::string& usrCp, const std::string& home)
{
// 1. Append user classpath if it exists.
std::string cp = NormalizeClasspath(usrCp);
// 2. Append home classpath
if (!home.empty())
{
std::string env = GetEnv(IGNITE_NATIVE_TEST_CLASSPATH, "false");
bool forceTest = ToLower(env) == "true";
std::string homeCp = CreateIgniteHomeClasspath(home, forceTest);
cp.append(homeCp);
}
// 3. Return.
return cp;
}
std::string ResolveIgniteHome(const std::string& path)
{
// 1. Check passed argument.
if (IsValidDirectory(path))
return path;
// 2. Check environment variable.
std::string home = GetEnv(IGNITE_HOME);
if (IsValidDirectory(home))
return home;
// 3. Check current work dir.
DWORD curDirLen = GetCurrentDirectoryA(0, NULL);
if (!curDirLen)
return std::string();
FixedSizeArray<char> curDir(curDirLen);
curDirLen = GetCurrentDirectoryA(curDir.GetSize(), curDir.GetData());
if (!curDirLen)
return std::string();
std::string curDirStr(curDir.GetData());
return ResolveIgniteHome0(curDirStr);
}
}
}
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
if (!ThreadLocal::OnProcessAttach())
return FALSE;
break;
case DLL_THREAD_DETACH:
ThreadLocal::OnThreadDetach();
JniContext::Detach();
break;
case DLL_PROCESS_DETACH:
ThreadLocal::OnProcessDetach();
break;
default:
break;
}
return TRUE;
}