blob: 44cb52c3eaf25a696754a681d2560fdce93ad7eb [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 <stdarg.h>
#include <glog/logging.h>
#include <algorithm>
#include <map>
#include <memory>
#include <sstream>
#include <vector>
#include <stout/dynamiclibrary.hpp>
#include <stout/exit.hpp>
#include <stout/foreach.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include "common/build.hpp"
#include "jvm/jvm.hpp"
#include "jvm/java/lang.hpp"
// Static storage and initialization.
Jvm* Jvm::instance = nullptr;
Try<Jvm*> Jvm::create(
const std::vector<std::string>& _options,
JNI::Version version,
bool exceptions)
{
// TODO(benh): Make this thread-safe.
if (instance != nullptr) {
return Error("Java Virtual Machine already created");
}
JavaVMInitArgs vmArgs;
vmArgs.version = version;
vmArgs.ignoreUnrecognized = false;
std::vector<std::string> options = _options;
#ifdef __APPLE__
// The Apple JNI implementation requires the AWT thread to not
// be the main application thread. Enabling headless mode
// circumvents the issue. Further details:
// https://issues.apache.org/jira/browse/MESOS-524
// http://www.oracle.com/technetwork/articles/javase/headless-136834.html
if (std::find(options.begin(), options.end(), "-Djava.awt.headless=true")
== options.end()) {
options.push_back("-Djava.awt.headless=true");
}
#endif
JavaVM* jvm = nullptr;
JNIEnv* env = nullptr;
Option<std::string> libJvmPath = os::getenv("JAVA_JVM_LIBRARY");
if (libJvmPath.isNone()) {
libJvmPath = mesos::internal::build::JAVA_JVM_LIBRARY;
}
static DynamicLibrary* libJvm = new DynamicLibrary();
Try<Nothing> openResult = libJvm->open(libJvmPath.get());
if (openResult.isError()) {
return Error(openResult.error());
}
Try<void*> symbol = libJvm->loadSymbol("JNI_CreateJavaVM");
if (symbol.isError()) {
libJvm->close();
return Error(symbol.error());
}
std::vector<JavaVMOption> opts(options.size());
for (size_t i = 0; i < options.size(); i++) {
opts[i].optionString = const_cast<char*>(options[i].c_str());
}
vmArgs.nOptions = opts.size();
if (!opts.empty()) {
vmArgs.options = &opts[0];
}
// typedef function pointer to JNI.
typedef jint (*fnptr_JNI_CreateJavaVM)(JavaVM**, void**, void*);
fnptr_JNI_CreateJavaVM fn_JNI_CreateJavaVM =
(fnptr_JNI_CreateJavaVM)symbol.get();
int createResult = fn_JNI_CreateJavaVM(&jvm, JNIENV_CAST(&env), &vmArgs);
if (createResult == JNI_ERR) {
libJvm->close();
return Error("Failed to create JVM!");
}
return instance = new Jvm(jvm, version, exceptions);
}
bool Jvm::created()
{
return instance != nullptr;
}
Jvm* Jvm::get()
{
if (instance == nullptr) {
create();
}
return CHECK_NOTNULL(instance);
}
Jvm::ConstructorFinder::ConstructorFinder(const Jvm::Class& _clazz)
: clazz(_clazz), parameters() {}
Jvm::ConstructorFinder& Jvm::ConstructorFinder::parameter(
const Jvm::Class& clazz)
{
parameters.push_back(clazz);
return *this;
}
Jvm::Constructor::Constructor(const Constructor& that)
: clazz(that.clazz), id(that.id) {}
Jvm::Constructor::Constructor(const Class& _clazz, const jmethodID _id)
: clazz(_clazz), id(_id) {}
Jvm::MethodFinder::MethodFinder(
const Jvm::Class& _clazz,
const std::string& _name)
: clazz(_clazz),
name(_name),
parameters() {}
Jvm::MethodFinder& Jvm::MethodFinder::parameter(const Class& type)
{
parameters.push_back(type);
return *this;
}
Jvm::MethodSignature Jvm::MethodFinder::returns(const Class& returnType) const
{
return Jvm::MethodSignature(clazz, name, returnType, parameters);
}
Jvm::MethodSignature::MethodSignature(const MethodSignature& that)
: clazz(that.clazz),
name(that.name),
returnType(that.returnType),
parameters(that.parameters) {}
Jvm::MethodSignature::MethodSignature(
const Class& _clazz,
const std::string& _name,
const Class& _returnType,
const std::vector<Class>& _parameters)
: clazz(_clazz),
name(_name),
returnType(_returnType),
parameters(_parameters) {}
Jvm::Method::Method(const Method& that)
: clazz(that.clazz), id(that.id) {}
Jvm::Method::Method(const Class& _clazz, const jmethodID _id)
: clazz(_clazz), id(_id) {}
const Jvm::Class Jvm::Class::named(const std::string& name)
{
return Jvm::Class(name, false /* NOT a native type. */);
}
Jvm::Class::Class(const Class& that)
: name(that.name), native(that.native) {}
Jvm::Class::Class(const std::string& _name, bool _native)
: name(_name), native(_native) {}
const Jvm::Class Jvm::Class::arrayOf() const
{
return Jvm::Class("[" + name, native);
}
Jvm::ConstructorFinder Jvm::Class::constructor() const
{
return Jvm::ConstructorFinder(*this);
}
Jvm::MethodFinder Jvm::Class::method(const std::string& name) const
{
return Jvm::MethodFinder(*this, name);
}
std::string Jvm::Class::signature() const
{
return native ? name : "L" + name + ";";
}
Jvm::Field::Field(const Field& that)
: clazz(that.clazz), id(that.id) {}
Jvm::Field::Field(const Class& _clazz, const jfieldID _id)
: clazz(_clazz), id(_id) {}
Jvm::Env::Env(bool daemon)
: env(nullptr), detach(false)
{
JavaVM* jvm = Jvm::get()->jvm;
// First check if we are already attached.
int result = jvm->GetEnv(JNIENV_CAST(&env), Jvm::get()->version);
// If we're not attached, attach now.
if (result == JNI_EDETACHED) {
if (daemon) {
jvm->AttachCurrentThreadAsDaemon(JNIENV_CAST(&env), nullptr);
} else {
jvm->AttachCurrentThread(JNIENV_CAST(&env), nullptr);
}
detach = true;
}
}
Jvm::Env::~Env()
{
if (detach) {
Jvm::get()->jvm->DetachCurrentThread();
}
}
jstring Jvm::string(const std::string& s)
{
Env env;
return env->NewStringUTF(s.c_str());
}
Jvm::Constructor Jvm::findConstructor(const ConstructorFinder& finder)
{
jmethodID id = findMethod(
finder.clazz,
"<init>",
voidClass,
finder.parameters,
false);
return Jvm::Constructor(finder.clazz, id);
}
Jvm::Method Jvm::findMethod(const MethodSignature& signature)
{
jmethodID id = findMethod(
signature.clazz,
signature.name,
signature.returnType,
signature.parameters,
false);
return Jvm::Method(signature.clazz, id);
}
Jvm::Method Jvm::findStaticMethod(const MethodSignature& signature)
{
jmethodID id = findMethod(
signature.clazz,
signature.name,
signature.returnType,
signature.parameters,
true);
return Jvm::Method(signature.clazz, id);
}
Jvm::Field Jvm::findField(
const Class& clazz,
const std::string& name,
const std::string& signature)
{
Env env;
jfieldID id = env->GetFieldID(
findClass(clazz),
name.c_str(),
signature.c_str());
check(env);
return Jvm::Field(clazz, id);
}
Jvm::Field Jvm::findStaticField(
const Class& clazz,
const std::string& name,
const std::string& signature)
{
Env env;
jfieldID id = env->GetStaticFieldID(
findClass(clazz),
name.c_str(),
signature.c_str());
check(env);
return Jvm::Field(clazz, id);
}
jobject Jvm::invoke(const Constructor ctor, ...)
{
Env env;
va_list args;
va_start(args, ctor);
jobject o = env->NewObjectV(findClass(ctor.clazz), ctor.id, args);
va_end(args);
check(env);
return o;
}
template <>
void Jvm::setField<jobject>(jobject receiver, const Field& field, jobject o)
{
Env env;
env->SetObjectField(receiver, field.id, o);
check(env);
}
template <>
void Jvm::setField<bool>(jobject receiver, const Field& field, bool b)
{
Env env;
env->SetBooleanField(receiver, field.id, b);
check(env);
}
template <>
void Jvm::setField<char>(jobject receiver, const Field& field, char c)
{
Env env;
env->SetCharField(receiver, field.id, c);
check(env);
}
template <>
void Jvm::setField<short>(jobject receiver, const Field& field, short s)
{
Env env;
env->SetShortField(receiver, field.id, s);
check(env);
}
template <>
void Jvm::setField<int>(jobject receiver, const Field& field, int i)
{
Env env;
env->SetIntField(receiver, field.id, i);
check(env);
}
template <>
void Jvm::setField<long>(jobject receiver, const Field& field, long l)
{
Env env;
env->SetLongField(receiver, field.id, l);
check(env);
}
template <>
void Jvm::setField<float>(jobject receiver, const Field& field, float f)
{
Env env;
env->SetFloatField(receiver, field.id, f);
check(env);
}
template <>
void Jvm::setField<double>(jobject receiver, const Field& field, double d)
{
Env env;
env->SetDoubleField(receiver, field.id, d);
check(env);
}
template <>
jobject Jvm::getStaticField<jobject>(const Field& field)
{
Env env;
jobject o = env->GetStaticObjectField(findClass(field.clazz), field.id);
check(env);
return o;
}
template <>
bool Jvm::getStaticField<bool>(const Field& field)
{
Env env;
bool b = env->GetStaticBooleanField(findClass(field.clazz), field.id);
check(env);
return b;
}
template <>
char Jvm::getStaticField<char>(const Field& field)
{
Env env;
char c = env->GetStaticCharField(findClass(field.clazz), field.id);
check(env);
return c;
}
template <>
short Jvm::getStaticField<short>(const Field& field)
{
Env env;
short s = env->GetStaticShortField(findClass(field.clazz), field.id);
check(env);
return s;
}
template <>
int Jvm::getStaticField<int>(const Field& field)
{
Env env;
int i = env->GetStaticIntField(findClass(field.clazz), field.id);
check(env);
return i;
}
template <>
long Jvm::getStaticField<long>(const Field& field)
{
Env env;
long l = env->GetStaticLongField(findClass(field.clazz), field.id);
check(env);
return l;
}
template <>
float Jvm::getStaticField<float>(const Field& field)
{
Env env;
float f = env->GetStaticFloatField(findClass(field.clazz), field.id);
check(env);
return f;
}
template <>
double Jvm::getStaticField<double>(const Field& field)
{
Env env;
double d = env->GetStaticDoubleField(findClass(field.clazz), field.id);
check(env);
return d;
}
Jvm::Jvm(JavaVM* _jvm, JNI::Version _version, bool _exceptions)
: voidClass("V"),
booleanClass("Z"),
byteClass("B"),
charClass("C"),
shortClass("S"),
intClass("I"),
longClass("J"),
floatClass("F"),
doubleClass("D"),
stringClass(Class::named("java/lang/String")),
jvm(_jvm),
version(_version),
exceptions(_exceptions) {}
Jvm::~Jvm()
{
LOG(FATAL) << "Destroying the JVM is not supported";
}
jobject Jvm::newGlobalRef(const jobject object)
{
Env env;
return env->NewGlobalRef(object);
}
void Jvm::deleteGlobalRef(const jobject object)
{
Env env;
if (object != nullptr) {
env->DeleteGlobalRef(object);
}
}
jclass Jvm::findClass(const Class& clazz)
{
Env env;
jclass jclazz = env->FindClass(clazz.name.c_str());
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
LOG(FATAL) << "Failed to find class " << clazz.name;
}
// TODO(John Sirois): Consider CHECK_NOTNULL -> return Option if
// re-purposing this code outside of tests.
return CHECK_NOTNULL(jclazz);
}
jmethodID Jvm::findMethod(
const Jvm::Class& clazz,
const std::string& name,
const Jvm::Class& returnType,
const std::vector<Jvm::Class>& argTypes,
bool isStatic)
{
Env env;
std::ostringstream signature;
signature << "(";
foreach (const Jvm::Class& type, argTypes) {
signature << type.signature();
}
signature << ")" << returnType.signature();
LOG(INFO) << "Looking up" << (isStatic ? " static " : " ")
<< "method " << name << signature.str();
jmethodID id = isStatic
? env->GetStaticMethodID(
findClass(clazz),
name.c_str(),
signature.str().c_str())
: env->GetMethodID(
findClass(clazz),
name.c_str(),
signature.str().c_str());
// TODO(John Sirois): Consider CHECK_NOTNULL -> return Option if
// re-purposing this code outside of tests.
return CHECK_NOTNULL(id);
}
template <>
void Jvm::invokeV<void>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
env->CallVoidMethodV(receiver, id, args);
check(env);
}
template <>
jobject Jvm::invokeV<jobject>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
jobject o = env->CallObjectMethodV(receiver, id, args);
check(env);
return o;
}
template <>
bool Jvm::invokeV<bool>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
bool b = env->CallBooleanMethodV(receiver, id, args);
check(env);
return b;
}
template <>
char Jvm::invokeV<char>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
char c = env->CallCharMethodV(receiver, id, args);
check(env);
return c;
}
template <>
short Jvm::invokeV<short>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
short s = env->CallShortMethodV(receiver, id, args);
check(env);
return s;
}
template <>
int Jvm::invokeV<int>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
int i = env->CallIntMethodV(receiver, id, args);
check(env);
return i;
}
template <>
long Jvm::invokeV<long>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
long l = env->CallLongMethodV(receiver, id, args);
check(env);
return l;
}
template <>
float Jvm::invokeV<float>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
float f = env->CallFloatMethodV(receiver, id, args);
check(env);
return f;
}
template <>
double Jvm::invokeV<double>(
const jobject receiver,
const jmethodID id,
va_list args)
{
Env env;
double d = env->CallDoubleMethodV(receiver, id, args);
check(env);
return d;
}
template <>
void Jvm::invokeStaticV<void>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
env->CallStaticVoidMethodV(findClass(receiver), id, args);
check(env);
}
template <>
jobject Jvm::invokeStaticV<jobject>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
jobject o = env->CallStaticObjectMethodV(findClass(receiver), id, args);
check(env);
return o;
}
template <>
bool Jvm::invokeStaticV<bool>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
bool b = env->CallStaticBooleanMethodV(findClass(receiver), id, args);
check(env);
return b;
}
template <>
char Jvm::invokeStaticV<char>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
char c = env->CallStaticCharMethodV(findClass(receiver), id, args);
check(env);
return c;
}
template <>
short Jvm::invokeStaticV<short>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
short s = env->CallStaticShortMethodV(findClass(receiver), id, args);
check(env);
return s;
}
template <>
int Jvm::invokeStaticV<int>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
int i = env->CallStaticIntMethodV(findClass(receiver), id, args);
check(env);
return i;
}
template <>
long Jvm::invokeStaticV<long>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
long l = env->CallStaticLongMethodV(findClass(receiver), id, args);
check(env);
return l;
}
template <>
float Jvm::invokeStaticV<float>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
float f = env->CallStaticFloatMethodV(findClass(receiver), id, args);
check(env);
return f;
}
template <>
double Jvm::invokeStaticV<double>(
const Class& receiver,
const jmethodID id,
va_list args)
{
Env env;
double d = env->CallStaticDoubleMethodV(findClass(receiver), id, args);
check(env);
return d;
}
void Jvm::check(JNIEnv* env)
{
if (env->ExceptionCheck() == JNI_TRUE) {
if (!exceptions) {
env->ExceptionDescribe();
EXIT(EXIT_FAILURE) << "Caught a JVM exception, not propagating";
} else {
java::lang::Throwable throwable;
Object* object = &throwable;
object->object = env->ExceptionOccurred();
env->ExceptionClear();
throw throwable;
}
}
}
// N.B. Both Jvm::invoke<void> and Jvm::invokeStatic<void> template
// instantiations need to be defined AFTER template instantions that
// they use (i.e., Jvm::invokeV<void>, Jvm::invokeStaticV<void>).
template <>
void Jvm::invoke<void>(const jobject receiver, const Method method, ...)
{
va_list args;
va_start(args, method);
invokeV<void>(receiver, method.id, args);
va_end(args);
}
template <>
void Jvm::invokeStatic<void>(const Method method, ...)
{
va_list args;
va_start(args, method);
invokeStaticV<void>(method.clazz, method.id, args);
va_end(args);
}