diff --git a/build.mk b/build.mk index 50402f0a3093c9a9e3360bae5df1e4f10a12e073..ab1e07db7c467944efabb80de5d4941ff9605d93 100644 --- a/build.mk +++ b/build.mk @@ -591,6 +591,7 @@ drivers += drivers/ide.o drivers += drivers/scsi-common.o drivers += drivers/vmw-pvscsi.o drivers += java/jvm_balloon.o +drivers += java/java_api.o objects = bootfs.o objects += arch/x64/dump.o diff --git a/java/java.cc b/java/java.cc index 77a859e7cd339e2c464a2e91880ec67995b30e83..3a8d52322893ac9b3a1f7e83e18ceb09ff336e39 100644 --- a/java/java.cc +++ b/java/java.cc @@ -11,6 +11,7 @@ #include <osv/debug.hh> #include "jvm_balloon.hh" #include <osv/mempool.hh> +#include "java_api.hh" // java.so is similar to the standard "java" command line launcher in Linux. // @@ -137,6 +138,7 @@ int main(int argc, char **argv) return 1; } + java_api::set(jvm); auto mainclass = env->FindClass(RUNJAVA); if (!mainclass) { if (env->ExceptionOccurred()) { diff --git a/java/java_api.cc b/java/java_api.cc new file mode 100644 index 0000000000000000000000000000000000000000..d269d1585c28a0e97d93c752c1b5e73bf6957fdf --- /dev/null +++ b/java/java_api.cc @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "java_api.hh" +#include <jni.h> + +using namespace std; +java_api* java_api::_instance = nullptr; +std::mutex java_api::lock; + +/** + * jvm_getter is a helper class to handle the JNI interaction. + * It's constructor and destructor handle the attach and detach thread + * and it holds helper method to handle JNI parsing. + */ +class jvm_getter { +public: + /** + * The constructor attach to the current thread. + * @param _jvm a pointer to the jvm + */ + jvm_getter(JavaVM& _jvm) + : env(nullptr), jvm(_jvm) + { + if (jvm.AttachCurrentThread((void **) &env, nullptr) != 0) { + throw jvm_error_exception("Fail attaching to jvm thread"); + } + } + + /** + * The destructor detach from the current thread + */ + ~jvm_getter() + { + jvm.DetachCurrentThread(); + } + + /** + * A helper method that check if an exception occur + * if so, it throw a C++ exception + */ + void check_exception() + { + jthrowable exc = env->ExceptionOccurred(); + if (exc) { + env->ExceptionDescribe(); + jclass exClass = env->GetObjectClass(exc); + jmethodID mid = env->GetMethodID(exClass, "toString", + "()Ljava/lang/String;"); + + env->ExceptionClear(); + jstring err_msg = (jstring) env->CallObjectMethod(exc, mid); + throw jvm_error_exception(to_str(err_msg)); + } + } + /** + * A helper method that return a java class + * @param name the name of the class + * @return the java class + */ + jclass get_class(const string& name) + { + auto cls = env->FindClass(name.c_str()); + check_exception(); + if (cls == nullptr) { + throw jvm_error_exception("Fail getting class " + name); + } + return cls; + } + + /** + * get the JavaInfo class + * @return the JavaInfo + */ + jclass get_java_info() + { + return get_class("io/osv/JavaInfo"); + } + + /** + * A helper method to get a string field from an array of java objects + * @param arr an array of objects + * @param pos the position in the array + * @param name the name of the field. + * @return a string with the value + */ + string get_str_arr(jobjectArray arr, int pos, const string& name) + { + jobject obj = env->GetObjectArrayElement(arr, pos); + jstring str = (jstring) env->GetObjectField(obj, + get_field_id(obj, name, "Ljava/lang/String;")); + check_exception(); + return to_str(str); + } + + /** + * A helper method to get a long field from an array of java objects + * @param arr an array of objects + * @param pos the position in the array + * @param name the name of the field. + * @return the field value + */ + long get_long_arr(jobjectArray arr, int pos, const string& name) + { + jobject obj = env->GetObjectArrayElement(arr, pos); + jlong val = env->GetLongField(obj, get_field_id(obj, name, "J")); + check_exception(); + return (long) val; + } + + /** + * A helper method to get java field id + * @param obj the java object + * @param field the field name + * @param type the field type + * @return the field id + */ + jfieldID get_field_id(jobject obj, const string& field, const string& type) + { + jclass cls = env->GetObjectClass(obj); + return env->GetFieldID(cls, field.c_str(), type.c_str()); + } + + /** + * Create a C++ string from java string + * @param str java string + * @return a C++ string with the java string + */ + string to_str(jstring str) + { + const char* tmp = env->GetStringUTFChars(str, NULL); + string res = tmp; + env->ReleaseStringUTFChars(str, tmp); + return res; + } + + JNIEnv* env; + JavaVM& jvm; +}; + +bool java_api::is_valid() +{ + lock.lock(); + bool valid = instance().jvm != nullptr; + lock.unlock(); + return valid; +} + +void java_api::set(JavaVM_* jvm) +{ + lock.lock(); + instance().jvm = jvm; + lock.unlock(); +} + +java_api& java_api::instance() +{ + lock.lock(); + if (_instance == nullptr) { + _instance = new java_api(); + } + lock.unlock(); + return *_instance; +} + +std::string java_api::get_mbean_info(const std::string& jmx_path) +{ + jvm_getter info(get_jvm()); + + auto java_info = info.get_java_info(); + + jmethodID get_mbean = info.env->GetStaticMethodID(java_info, + "getMbean", + "(Ljava/lang/String;)Ljava/lang/String;"); + + auto path = info.env->NewStringUTF(jmx_path.c_str()); + jstring str = (jstring) info.env->CallStaticObjectMethod( + java_info, + get_mbean, path); + info.check_exception(); + return (str == nullptr) ? "" : info.to_str(str); +} + +std::vector<std::string> java_api::get_all_mbean() +{ + jvm_getter info(get_jvm()); + + auto java_info = info.get_java_info(); + + jmethodID get_mbean = info.env->GetStaticMethodID(java_info, + "getAllMbean", + "()[Ljava/lang/String;"); + + jobjectArray stringArray = (jobjectArray) info.env->CallStaticObjectMethod( + java_info, + get_mbean); + info.check_exception(); + vector<string> res; + for (int i = 0; i < info.env->GetArrayLength(stringArray); i++) { + res.push_back( + info.to_str( + (jstring) info.env->GetObjectArrayElement(stringArray, + i))); + } + return res; +} + +std::vector<gc_info> java_api::get_all_gc() +{ + jvm_getter info(get_jvm()); + + auto java_info = info.get_java_info(); + jmethodID get_mbean = info.env->GetStaticMethodID(java_info, + "getAllGC", + "()[Lio/osv/GCInfo;"); + jobjectArray objArray = (jobjectArray) info.env->CallStaticObjectMethod( + java_info, + get_mbean); + info.check_exception(); + std::vector<gc_info> res; + for (int i = 0; i < info.env->GetArrayLength(objArray); i++) { + gc_info gc; + gc.name = info.get_str_arr(objArray, i, "name"); + gc.count = info.get_long_arr(objArray, i, "count"); + gc.time = info.get_long_arr(objArray, i, "time"); + res.push_back(gc); + } + return res; +} + +std::string +java_api::get_system_property(const std::string& property) +{ + jvm_getter info(get_jvm()); + + auto java_info = info.get_java_info(); + + jmethodID get_mbean = info.env->GetStaticMethodID(java_info, + "getProperty", + "(Ljava/lang/String;)Ljava/lang/String;"); + + auto str = info.env->NewStringUTF(property.c_str()); + jstring res = (jstring) info.env->CallStaticObjectMethod(java_info, + get_mbean, str); + info.check_exception(); + return info.to_str(res); +} + +void java_api::set_mbean_info(const std::string& jmx_path, + const std::string& attribute, const std::string& value) +{ + jvm_getter info(get_jvm()); + + auto java_info = info.get_java_info(); + + jmethodID set_mbean = info.env->GetStaticMethodID(java_info, + "setMbean", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + auto str = info.env->NewStringUTF(jmx_path.c_str()); + auto attr = info.env->NewStringUTF(attribute.c_str()); + auto val = info.env->NewStringUTF(value.c_str()); + info.env->CallStaticVoidMethod(java_info, + set_mbean, str, attr, val); + info.check_exception(); +} + +void java_api::call_gc() +{ + jvm_getter info(get_jvm()); + auto system = info.get_class("java/lang/System"); + jmethodID gc = info.env->GetStaticMethodID(system, + "gc", + "()V"); + info.env->CallStaticVoidMethod(system, gc); + info.check_exception(); +} diff --git a/java/java_api.hh b/java/java_api.hh new file mode 100644 index 0000000000000000000000000000000000000000..91f1abeb1ab70a001fb15060cff92dfd55bdc4e2 --- /dev/null +++ b/java/java_api.hh @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef JAVA_API_HH_ +#define JAVA_API_HH_ + +#include <string> +#include <vector> +#include <mutex> + +struct JavaVM_; +class _jarray; +class jvm_error_exception : public std::exception +{ +public: + jvm_error_exception(const std::string& msg) + : _msg(msg) + { + } + virtual const char* what() const throw () + { + return _msg.c_str(); + } + +private: + std::string _msg; + +}; + +struct gc_info { + long count; + long time; + std::string name; + std::vector<std::string> pools; +}; + +/** + * This is an entry point for the JVM that the API can use + */ +class java_api { +public: + java_api() + : jvm(nullptr) + { + } + + /** + * get an mbean information + * @param jmx_path an mbean name + * @return a string representation of the mbean informtion + */ + std::string get_mbean_info(const std::string& jmx_path); + + /** + * Set an attribute in an mbean + * @param jmx_path an mbean name + * @param attribute the attribute to change + * @param value the new value + */ + void set_mbean_info(const std::string& jmx_path, + const std::string& attribute, const std::string& value); + + /** + * Get a list of all the available mbean names + * @return a vector of all the mbean names + */ + std::vector<std::string> get_all_mbean(); + + /** + * Get a system property + * @param property a property name + * @return the system property value + */ + std::string get_system_property(const std::string& property); + + /** + * Get all the garbage collection information. + * @return a vector with all the garbage collection information + */ + std::vector<gc_info> get_all_gc(); + + /** + * Check if the jvm was set. + * @return true if it was set or false otherwise + */ + bool is_valid(); + + /** + * Explicitly call the garbage collection + */ + void call_gc(); + + /** + * Set a jvm. + * This method is called when a jvm goes up. + * It calls the instance method internally + * @param jvm the jvm to set + */ + static void set(JavaVM_* jvm); + + /** + * get an instance + * @return a java_api instance + */ + static java_api& instance(); + +private: + /** + * this is a safe method to get the jvm. + * it make sure that the jvm is valid before returning it and + * throw an exception if not. + * @return the jvm. + */ + JavaVM_& get_jvm() + { + if (!is_valid()) { + throw jvm_error_exception("JVM not provided"); + } + return *jvm; + } + + static std::mutex lock; + static java_api* _instance; + JavaVM_* jvm; +}; + +#endif /* JAVA_API_HH_ */ diff --git a/java/runjava/src/main/java/io/osv/GCInfo.java b/java/runjava/src/main/java/io/osv/GCInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..e3ca99e68008897224a8d8b4169caa363e4d7ee3 --- /dev/null +++ b/java/runjava/src/main/java/io/osv/GCInfo.java @@ -0,0 +1,20 @@ +package io.osv; +/* + * Copyright (C) 2013-2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + + +/** + * + * A helper class that holds Garbage collection information + * + */ +public class GCInfo { + long count; + long time; + String name; + String[] pools; +} diff --git a/java/runjava/src/main/java/io/osv/JavaInfo.java b/java/runjava/src/main/java/io/osv/JavaInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..ec50266e876ccadd02569e3a4d7b03c35515fe3b --- /dev/null +++ b/java/runjava/src/main/java/io/osv/JavaInfo.java @@ -0,0 +1,174 @@ +package io.osv; + +import io.osv.util.json.ArrayBuilder; +import io.osv.util.json.JsonGenerator; +import java.lang.management.GarbageCollectorMXBean; +import java.util.List; +import java.util.Set; +import javax.management.Attribute; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.ReflectionException; + +/* + * Copyright (C) 2013-2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +public class JavaInfo { + /** + * Get an Mbean description and data + * + * @param mbeanName + * the MBean name + * @return a string in a JSON format representative of the MBean + * @throws MalformedObjectNameException + * @throws ReflectionException + * @throws InstanceNotFoundException + * @throws IntrospectionException + * @throws MBeanException + * @throws AttributeNotFoundException + */ + public static String getMbean(String mbeanName) + throws MalformedObjectNameException, IntrospectionException, + InstanceNotFoundException, ReflectionException, + AttributeNotFoundException, MBeanException { + javax.management.MBeanServer mbeanServer = java.lang.management.ManagementFactory + .getPlatformMBeanServer(); + ObjectName objName = new ObjectName(mbeanName); + MBeanInfo res = mbeanServer.getMBeanInfo(objName); + + MBeanAttributeInfo[] att = res.getAttributes(); + ArrayBuilder sb = new ArrayBuilder(); + for (int i = 0; i < att.length; i++) { + try { + sb.append(JsonGenerator.attrToString(att[i], objName, + mbeanServer)); + } catch (javax.management.RuntimeMBeanException e) { + // some of the attribute can throw UnsupportedOperationException + // even if + // they have read permission + } + } + return sb.toString(); + } + + /** + * Set an MBean attribute to a new value + * + * @param mbeanName + * the MBean name + * @param attribute + * the attribute name, the attribute use an xpath syntax to + * define the full path into the attribute + * @param value + * the value to set + * @throws MalformedObjectNameException + * @throws ReflectionException + * @throws MBeanException + * @throws InstanceNotFoundException + * @throws AttributeNotFoundException + * @throws InvalidAttributeValueException + */ + public static void setMbean(String mbeanName, String attribute, String value) + throws MalformedObjectNameException, AttributeNotFoundException, + InstanceNotFoundException, MBeanException, ReflectionException, + InvalidAttributeValueException { + javax.management.MBeanServer mbeanServer = java.lang.management.ManagementFactory + .getPlatformMBeanServer(); + ObjectName objName = new ObjectName(mbeanName); + Object attr = mbeanServer.getAttribute(objName, attribute); + Object valueObject = getUpdatedAttribute(attr, value); + if (valueObject != null) { + mbeanServer.setAttribute(objName, new Attribute(attribute, + valueObject)); + } + } + + /** + * A helper method to get an updated attribute from an existing one. It uses + * the original attribute determine the type the value should be mapped to. + * + * @param attr + * the original attribute + * @param value + * the new value + * @return the new attribute + */ + private static Object getUpdatedAttribute(Object attr, String value) { + if (attr instanceof Long) { + return Long.parseLong(value); + } + if (attr instanceof String) { + return value; + } + if (attr instanceof Boolean) { + return Boolean.valueOf(value); + } + if (attr instanceof Integer) { + return Integer.parseInt(value); + } + return null; + } + + /** + * Get a list of all available MBean names. + * + * @return an array of string with all the mbeanServer names + */ + public static String[] getAllMbean() { + javax.management.MBeanServer mbeanServer = java.lang.management.ManagementFactory + .getPlatformMBeanServer(); + Set<ObjectName> instances = mbeanServer.queryNames(null, null); + String[] res = new String[instances.size()]; + int i = 0; + for (ObjectName obj : instances) { + res[i++] = obj.getCanonicalName(); + } + + return res; + } + + /** + * Get garbage collector information + * + * @return an array of GCInfo object + */ + public static GCInfo[] getAllGC() { + List<GarbageCollectorMXBean> gcCollection = java.lang.management.ManagementFactory + .getGarbageCollectorMXBeans(); + GCInfo[] res = new GCInfo[gcCollection.size()]; + int i = 0; + for (GarbageCollectorMXBean gc : gcCollection) { + GCInfo info = new GCInfo(); + info.count = gc.getCollectionCount(); + info.name = gc.getName(); + info.time = gc.getCollectionTime(); + info.pools = gc.getMemoryPoolNames(); + res[i++] = info; + + } + return res; + } + + /** + * Get a system property + * + * @param str + * a system property name + * @return the system property value + */ + public static String getProperty(String str) { + return System.getProperty(str); + } + +} diff --git a/java/runjava/src/main/java/io/osv/util/json/ArrayBuilder.java b/java/runjava/src/main/java/io/osv/util/json/ArrayBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..6df05900f600cf528c701ce4b0c25371bbbc9470 --- /dev/null +++ b/java/runjava/src/main/java/io/osv/util/json/ArrayBuilder.java @@ -0,0 +1,65 @@ +package io.osv.util.json; + +/* + * Copyright (C) 2013-2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +/** + * A helper class to create array representative and handle the comma before + * values + * + */ +public class ArrayBuilder { + private boolean first = true; + private StringBuilder sb = new StringBuilder(); + private String close = "]"; + + /** + * The default constructor is used when the surrounding chars are square + * braces + */ + public ArrayBuilder() { + sb.append("["); + } + + /** + * A constructor that set an open and close characters + * + * @param open + * the open characters + * @param close + * the close characters + */ + public ArrayBuilder(String open, String close) { + this.close = close; + sb.append(open); + } + + /** + * Append a value + * + * @param val + * the value to add + */ + public StringBuilder append(String val) { + if (first) { + first = false; + } else { + sb.append(", "); + } + return sb.append(val); + } + + /** + * Close the array and get a string out of it. + * + * @return a string of the array + */ + public String toString() { + sb.append(close); + return sb.toString(); + } +} diff --git a/java/runjava/src/main/java/io/osv/util/json/JsonGenerator.java b/java/runjava/src/main/java/io/osv/util/json/JsonGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..08426a16d182f12345dd0c996658917c68b9d26a --- /dev/null +++ b/java/runjava/src/main/java/io/osv/util/json/JsonGenerator.java @@ -0,0 +1,101 @@ +package io.osv.util.json; + +/* + * Copyright (C) 2013-2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.openmbean.CompositeData; + +/** + * This is a helper class to map types to json format. + * + */ +public class JsonGenerator { + /** + * Recursively map an attribute value to string + * + * @param value + * an attribute value + * @return a string representative of the value + */ + public static String attrValueToString(Object value) { + if (value instanceof CompositeData[]) { + CompositeData data[] = (CompositeData[]) value; + ArrayBuilder sb = new ArrayBuilder(); + for (int i = 0; i < data.length; i++) { + sb.append(compositeToString(data[i])); + } + return sb.toString(); + } + if (value instanceof CompositeData) { + return compositeToString((CompositeData) value); + } + if (value instanceof String[]) { + String vals[] = (String[]) value; + ArrayBuilder sb = new ArrayBuilder(); + for (int i = 0; i < vals.length; i++) { + sb.append("\"" + vals[i] + "\""); + } + return sb.toString(); + } + if (value instanceof Long || value instanceof Integer) { + return value.toString(); + } + return (value == null) ? "\"\"" : "\"" + value.toString() + "\""; + } + + /** + * Recursively map a composite value to a string + * + * @param data + * Composite Data value + * @return a string representation of the data + */ + public static String compositeToString(CompositeData data) { + ArrayBuilder sb = new ArrayBuilder("{", "}"); + for (String key : data.getCompositeType().keySet()) { + sb.append("\"").append(key).append("\": ") + .append(attrValueToString(data.get(key))); + } + return sb.toString(); + } + + /** + * Map an attribute to a string and add its value if present + * + * @param att + * the mbeanServer attribute + * @param objName + * the mbeanServer object name + * @param mbeanServer + * an mbeanServer server + * @return a string representative of the attribute in a JSON format + * @throws ReflectionException + * @throws MBeanException + * @throws InstanceNotFoundException + * @throws AttributeNotFoundException + */ + public static String attrToString(MBeanAttributeInfo att, + ObjectName objName, MBeanServer mbeanServer) + throws AttributeNotFoundException, InstanceNotFoundException, + MBeanException, ReflectionException { + String value = ""; + value = (att.isReadable()) ? attrValueToString(mbeanServer + .getAttribute(objName, att.getName())) : "\"\""; + + return "{\"name\": \"" + att.getName() + "\", \"type\": \"" + + att.getType() + "\", \"value\": " + value + + ", \"writable\": " + Boolean.toString(att.isWritable()) + + ", \"description\": \"" + att.getDescription() + "\"}"; + } +}