/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.broad.igv.util;
import org.apache.log4j.Logger;
import org.broad.igv.DirectoryManager;
import org.broad.igv.ui.util.MessageUtils;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author jrobinso
*/
public class RuntimeUtils {
private static Logger log = Logger.getLogger(RuntimeUtils.class);
public static long getAvailableMemory() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long allocatedMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
return freeMemory + (maxMemory - allocatedMemory);
}
public static double getAvailableMemoryFraction() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long allocatedMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
return (double) ((freeMemory + (maxMemory - allocatedMemory))) / maxMemory;
}
/**
* Start an external process with the provided message.
* Also starts a separate thread to read back error stream
* <p/>
* See {@link Runtime#exec(String, String[], java.io.File)} for explanation of arguments
*
* @return
*/
public static Process startExternalProcess(String[] msg, String[] envp, File dir) throws IOException {
Process pr = Runtime.getRuntime().exec(msg, envp, dir);
startErrorReadingThread(pr);
return pr;
}
private static Process startErrorReadingThread(Process pr) {
final BufferedReader err = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
//Supposed to read error stream on separate thread to prevent blocking
Thread runnable = new Thread() {
private boolean messageDisplayed = false;
@Override
public void run() {
String line;
try {
while ((line = err.readLine()) != null) {
log.error(line);
if (!messageDisplayed && line.toLowerCase().contains("error")) {
MessageUtils.showMessage(line + "<br>See igv.log for more details");
messageDisplayed = true;
}
}
err.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
};
runnable.start();
return pr;
}
/**
* @param cmd
* @param envp
* @param dir
* @return
* @throws java.io.IOException
* @deprecated Use {@link #executeShellCommand(String[], String[], java.io.File)}
*/
@Deprecated
public static String executeShellCommand(String cmd, String[] envp, File dir) throws IOException {
return executeShellCommand(new String[]{cmd}, envp, dir);
}
public static String executeShellCommand(String cmd[], String[] envp, File dir) throws IOException {
return executeShellCommand(cmd, envp, dir, true);
}
public static String executeShellCommand(String cmd[], String[] envp, File dir, boolean waitFor) throws IOException {
Process pr = startExternalProcess(cmd, envp, dir);
if(waitFor){
try {
pr.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
InputStream inputStream = null;
String line = "";
try {
inputStream = pr.getInputStream();
BufferedReader buf = new BufferedReader(new InputStreamReader(inputStream));
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
while ((line = buf.readLine()) != null) {
pw.println(line);
}
pw.close();
return writer.toString();
} finally {
if (inputStream != null) {
inputStream.close();
}
OutputStream os = pr.getOutputStream();
if(os != null){
os.close();
}
}
}
/**
* Converts input files to file URLs
* @param files
* @return
*/
private static URL[] filesToURLs(File[] files) {
URL[] urls = new URL[files.length];
for (int pp = 0; pp < files.length; pp++) {
try {
urls[pp] = new URL("file://" + files[0].getAbsolutePath());
} catch (MalformedURLException e) {
log.error(e);
}
}
return urls;
}
/**
* Returns an array of builtin library URL locations, as well as those passed in with {@code libURLs}
* @param libURLs
* @return
*/
private static URL[] addBuiltinURLs(URL[] libURLs) {
File[] builtin_paths = getPluginJars();
URL[] builtin_urls = filesToURLs(builtin_paths);
List<URL> outURLs = new ArrayList<URL>(Arrays.asList(libURLs != null ? libURLs : new URL[0]));
outURLs.addAll(Arrays.asList(builtin_urls));
return outURLs.toArray(new URL[outURLs.size()]);
}
private static URL[] getClassURLs(URL[] libURLs){
return addBuiltinURLs(libURLs);
}
/**
* Create {@link java.lang.Class} object with the desired name,
* looking in {@code libURLs} as well as built-in locations
* @param className
* @param libURLs
* @return
* @throws IllegalAccessException
* @throws InstantiationException
* @throws ClassNotFoundException
*/
public static Class loadClassForName(String className, URL[] libURLs) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
Class clazz = null;
//Easy way
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
//Try with custom loader below
}
if (clazz != null) return clazz;
//If not found, check other locations
URL[] allURLs = getClassURLs(libURLs);
ClassLoader loader = URLClassLoader.newInstance(
allURLs, ClassLoader.getSystemClassLoader()
);
clazz = loader.loadClass(className);
return clazz;
}
/**
* Create an instance of the specified class. Must have no-arg constructor
* @param className
* @param libURLs
* @return
* @throws IllegalAccessException
* @throws InstantiationException
* @throws ClassNotFoundException
*/
public static Object loadInstanceForName(String className, URL[] libURLs) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
Class clazz = loadClassForName(className, libURLs);
Constructor constructor = clazz.getConstructor();
return constructor.newInstance();
}
/**
* Return an array of jar files in the plugin directory.
* Never returns null, only empty array
* @return
*/
public static File[] getPluginJars(){
File builtinDir = new File(DirectoryManager.getIgvDirectory(), "plugins/");
File[] pluginJars = builtinDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".jar");
}
});
if(pluginJars == null){
pluginJars = new File[0];
}
return pluginJars;
}
/**
* Add jars in plugin directory to system classloader
* This may not work on all systems, for security reasons
*/
public static void loadPluginJars(){
File[] pluginJars = getPluginJars();
if(pluginJars != null){
for(File jar: pluginJars){
loadLibrary(jar);
}
}
}
//Not sure about the security implications of this method
private static boolean loadLibrary(java.io.File jar){
try {
//We are using reflection here to circumvent encapsulation; addURL is not public
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
//Skip if already loaded
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return false;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod(
"addURL",
new Class[]{java.net.URL.class}
);
method.setAccessible(true);
method.invoke(loader, new Object[]{url});
return true;
} catch (Exception e){
log.warn("Error loading jar: " + e.getMessage());
return false;
}
}
}