All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.liv.pgb.analytica.lib.wrappedr.RRuntime Maven / Gradle / Ivy

Go to download

Library allowing generation of plots and data from proteomics and metabolomics mass spectrometry data through the use of R and java methods.

The newest version!
package uk.ac.liv.pgb.analytica.lib.wrappedr;

import java.io.File;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
import org.rosuda.JRI.RMainLoopCallbacks;
import org.rosuda.JRI.Rengine;
import uk.ac.liv.pgb.analytica.lib.wrappedr.FileSystem.SystemType;

/**
 * Manages the interaction with the R runtime itself.
 *
 * @author sperkins
 */
public final class RRuntime implements RMainLoopCallbacks {

    /**
     * The singleton instance of this class.
     */
    private static final RRuntime INSTANCE = new RRuntime();

    /**
     * Gets the singleton instance of this class. This method is synchronized as
     * it may involve creation of a static object. Synchronization may be
     * overkill here.
     *
     * @return Singleton instance of RRuntime.
     */
    public static synchronized RRuntime getInstance() {
        return INSTANCE;
    }

    /**
     * The folder containing the R application files.
     */
    private File rApplicationFolder;

    /**
     * The R engine object.
     */
    private Rengine localEngine;

    /**
     * The current state of this runtime.
     */
    private RRuntimeState state;

    /**
     * Private constructor to prevent external instantiation.
     */
    private RRuntime() {
        // First need to check if R is installed.
        findRHomeFolder();
        if (rApplicationFolder == null) {
            state = RRuntimeState.R_APPLICATION_FOLDER_NOT_FOUND;
            return;
        }

        // Load the JRI (rJava) and hence its dependencies.
        loadJri();

    }

    /**
     * Load the JRI linked library.
     *
     * @return True if the library is loaded successfully.
     */
    private boolean loadJri() {
        boolean jriDllFound = false;
        boolean dependenciesFixed = false;
        while (true) {
            try {
                System.loadLibrary("jri");
            } catch (UnsatisfiedLinkError linkError) {
                if (dependencyFailure(linkError)) {
                    if (dependenciesFixed) {
                        // Oh dear. We think we have fixed the dependencies, but the loading still failed.
                        // Either we didn't find the correct dll, or there was an error we haven't anticipated.
                        state = RRuntimeState.JRI_DEPENDENCY_FIX_FAIL;
                        return false;
                    }

                    dependenciesFixed = fixDependentLibraries();
                    if (!dependenciesFixed) {
                        state = RRuntimeState.JRI_DEPENDENCY_FIX_FAIL;
                        return false;
                    }

                    continue;
                } else {
                    if (jriDllFound) {
                        // Oh dear. We think we have found the correct jriDll, but the loading failed.
                        // Either we didn't find the correct Dll, or there was an error we haven't anticipated.
                        state = RRuntimeState.JRI_WRONG_FOUND_OR_OTHER;
                        return false;
                    }

                    File jriDll = null;

                    jriDll = findJriDll();

                    if (jriDll == null) {
                        // We still didn't find the Jri Dll.
                        // This not recoverable.
                        state = RRuntimeState.JRI_NOT_FOUND;
                        return false;
                    }

                    // Let's add the Jri Dll's folder to the path for this java session.
                    FileSystem.addFolderToPathForJavaSession(jriDll.getParentFile());

                    // Let's add the jri Dll's folder to the path. For future sessions.
                    FileSystem.addFolderToPathPermanent(jriDll.getParentFile());
                    jriDllFound = true;
                    continue;
                }
            }

            state = RRuntimeState.READY;

            break;
        }

        return true;
    }

    /**
     * Fix the depending libraries error reported when attempting to load the
     * JRI library.
     *
     * @return True if the dependent library issues 'appear' to be fixed.
     */
    private boolean fixDependentLibraries() {
        // So we have dependent libraries to load.
        // Documentation says that this error is solvable by setting the 'R_HOME' environmental variable.
        // We can certainly do that, but that may not take until the next restart of the application.
        // Not for now: We could load every Dll in the R application folder.
        SystemType system = FileSystem.getSystemType();

        File rBinFolder = new File(rApplicationFolder.getAbsolutePath() + File.separator + "bin");
        if (!rBinFolder.exists()) {
            // Oh dear, this is bad, but should never happen.
            return false;
        }

        if (system == SystemType.WIN64) {
            File rBinFolder64 = new File(rBinFolder.getAbsolutePath() + File.separator + "x64");
            if (rBinFolder64.exists()) {
                
                if (FileSystem.addFolderToPathPermanent(rBinFolder64)) {
                    return true;
                }
            }
        }

        if (system == SystemType.WIN32 || system == SystemType.WIN64) {
            File rBinFolder32 = new File(rBinFolder.getAbsolutePath() + File.separator + "i386");
            if (rBinFolder32.exists()) {
                if (FileSystem.addFolderToPathPermanent(rBinFolder32)) {
                    return true;
                }
            }
        }

        return FileSystem.addFolderToPathPermanent(rBinFolder);
    }

    /**
     * Checks whether the UnsatisfiedLinkError is due to dependencies.
     *
     * @param linkError The link exception.
     * @return True if the error is due to dependencies.
     */
    private boolean dependencyFailure(final UnsatisfiedLinkError linkError) {
        return linkError.getLocalizedMessage().toUpperCase().contains("DEPENDENT");
    }

    /**
     * Checks whether the R engine is available for use.
     *
     * @return True if available for use.
     */
    public boolean isAvailable() {
        return this.localEngine != null;
    }

    /**
     * Gets the current state of this runtime.
     *
     * @return The current runtime state.
     */
    public RRuntimeState getState() {
        return this.state;
    }

    /**
     * Install and load a package.
     *
     * @param dependency The name of the package to be installed/loaded.
     */
    public synchronized void loadPackageDependency(final String dependency) {
        // First install the package - this will have no effect if the package is already installed.
        this.localEngine.eval("install.package(" + dependency + ")");

        // Now load the package.
        this.localEngine.eval("library(" + dependency + ")");
    }

    /**
     * Loads the file graphics device, as a PNG in the specified file.
     *
     * @param outputFile The file to write the PNG data to.
     */
    public synchronized void loadFileGraphicsDevice(final File outputFile) {
        this.localEngine.eval("png(filename=\"" + outputFile.getAbsolutePath() + "\")");
    }

    /**
     * Loads the java graphics device.
     */
    public synchronized void loadJavaGraphicsDevice() {

    }

    /**
     * Closes the graphics device, whatever it was.
     */
    public synchronized void closeGraphicsDevice() {
        this.localEngine.eval("dev.off()");
    }

    /**
     * Executes the given function by name, with the specified arguments.
     *
     * @param function The name of the function to be executed.
     * @param arguments The arguments to the function to be executed.
     */
    public synchronized void executeFunction(final String function, final String... arguments) {
        StringBuilder execBuilder = new StringBuilder();
        execBuilder.append(function).append("(");
        execBuilder.append(String.join(", ", Arrays.stream(arguments).filter(p -> p != null).collect(Collectors.toList())));
        execBuilder.append(")");

        this.localEngine.eval(execBuilder.toString());
    }

    /**
     * Reads and evaluates an R source file.
     *
     * @param sourceFile The R source file.
     */
    public synchronized void source(final File sourceFile) {
        this.localEngine.eval("source(" + sourceFile.getAbsolutePath() + ")");
    }

    /**
     * Take action when an R source file cannot be 'sourced'.
     */
    private void doSourcingFailAction() {
    }

    /**
     * Take action when a dependency cannot be loaded.
     */
    private void doDependencyLoadFailAction() {
    }

    /**
     * Execute naked R code.
     *
     * @param command The R code to be executed.
     */
    public synchronized void execute(final String command) {
        this.localEngine.eval(command);

    }

    /**
     * Find the R home folder and set it as an environment variable.
     */
    private void findRHomeFolder() {
        SystemType system = FileSystem.getSystemType();
        if (system == SystemType.WIN32) {
            rApplicationFolder = FileSystem.findFile("R", new File("C:\\Program Files"), true, true);
        } else if (system == SystemType.WIN64) {
            rApplicationFolder = FileSystem.findFile("R", new File("C:\\Program Files"), true, true);
            if (rApplicationFolder == null) {
                rApplicationFolder = FileSystem.findFile("R", new File("C:\\Program Files (x86)"), true, true);
            }
        } else {
            throw new RuntimeException("Only Windows OS are currently supported.");
        }

        if (rApplicationFolder != null) {
            // Scan it's children to make sure we have the correct R home.
            File[] children = rApplicationFolder.listFiles();
            if (children != null) {
                if (Arrays.stream(children).anyMatch(p -> p.getName().equalsIgnoreCase("etc")) && Arrays.stream(children).anyMatch(p -> p.getName().equalsIgnoreCase("bin"))) {
                    return;
                } else if (children.length == 1) {
                    rApplicationFolder = children[0];
                } else {
                    rApplicationFolder = null;
                }
            }

            if (rApplicationFolder != null) {
                FileSystem.setEnvironmentVariablePermanent("R_HOME", rApplicationFolder.getAbsolutePath());
            }
        }
    }

    /**
     * Finds the JRI linked library file.
     *
     * @return The JRI linked library file.
     */
    private File findJriDll() {
        SystemType system = FileSystem.getSystemType();

        if (rApplicationFolder == null) {
            return null;
        }

        File rJavaFolder = new File(rApplicationFolder.getAbsolutePath() + File.separator + "library" + File.separator + "rJava");
        if (rJavaFolder.exists()) {
            File jriFolder = FileSystem.findFile("jri", rJavaFolder, true, true);
            if (jriFolder == null) {
                return null;
            }

            if (system == SystemType.WIN32) {
                Optional i386 = Arrays.stream(jriFolder.listFiles()).filter(p -> p.getName().equalsIgnoreCase("i386")).findAny();
                if (i386.isPresent()) {
                    File jriDll = FileSystem.findFile("jri.dll", i386.get(), true, false);
                    if (jriDll != null) {
                        return jriDll;
                    }
                }

                File jriDll = FileSystem.findFile("jri.dll", jriFolder, true, false);
                return jriDll;
            } else if (system == SystemType.WIN64) {
                Optional x64 = Arrays.stream(jriFolder.listFiles()).filter(p -> p.getName().equalsIgnoreCase("x64")).findAny();
                if (x64.isPresent()) {
                    File jriDll = FileSystem.findFile("jri.dll", x64.get(), true, false);
                    if (jriDll != null) {
                        return jriDll;
                    }
                }

                File jriDll = FileSystem.findFile("jri.dll", jriFolder, true, false);
                return jriDll;
            }
        }

        return null;
    }

    /**
     * Write text to the console.
     * @param engine The R engine.
     * @param outText The text to write out.
     * @param outputType Type of output. 0=Regular, 1=Error/Warning.
     */
    @Override
    public void rWriteConsole(final Rengine engine, final String outText, final int outputType) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Indicate that the engine has changed its busy state.
     * @param engine The R engine.
     * @param stateEvent The type of event being indicated. 1=Busy, 0=NotBusy
     */
    @Override
    public void rBusy(final Rengine engine, final int stateEvent) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Read text into the console.
     * @param engine The R engine.
     * @param consolePrompt The console prompt text.
     * @param addToHistory Whether to add the returned output to the history. 0=No, !0=Yes
     * @return Text to be read in to the console.
     */
    @Override
    public String rReadConsole(final Rengine engine, final String consolePrompt, final int addToHistory) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Show warning/error text.
     * @param engine The R engine.
     * @param errorWarning The error/warning text.
     */
    @Override
    public void rShowMessage(final Rengine engine, final String errorWarning) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Choose file for use in console.
     * @param engine The R engine.
     * @param newFile Whether a new or existing file is needed.
     * @return The path of the chosen file.
     */
    @Override
    public String rChooseFile(final Rengine engine, final int newFile) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Flush any buffered output.
     * @param engine The R engine.
     */
    @Override
    public void rFlushConsole(final Rengine engine) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Save the history to file.
     * @param engine The R engine.
     * @param historyFile The path of the history file to save.
     */
    @Override
    public void rSaveHistory(final Rengine engine, final String historyFile) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * Load the history from file.
     * @param engine The R engine.
     * @param historyFile The path of the history file to load.
     */
    @Override
    public void rLoadHistory(final Rengine engine, final String historyFile) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    /**
     * The possible states of the runtime.
     */
    public enum RRuntimeState {
        /**
         * The JRI was successfully found but its location is not in the path.
         */
        JRI_FOUND_NOT_IN_PATH,

        /**
         * There was an error while finding the JRI.
         */
        JRI_ERROR_FINDING,

        /**
         * The JRI could not be found.
         */
        JRI_NOT_FOUND,

        /**
         * The JRI was found in the path and there are no errors.
         */
        READY,

        /**
         * The R application folder could not be found.
         */
        R_APPLICATION_FOLDER_NOT_FOUND,

        /**
         * The JRI was found but loading failed due to a non-dependency error issue.
         */
        JRI_WRONG_FOUND_OR_OTHER,

        /**
         * The dependency fixing did not resolve the issue with the dependencies.
         */
        JRI_DEPENDENCY_FIX_FAIL
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy