Search in sources :

Example 1 with AppParsingException

use of org.cytoscape.app.internal.exception.AppParsingException in project cytoscape-impl by cytoscape.

the class AppManager method setupAlterationMonitor.

private void setupAlterationMonitor() {
    // Set up the FileAlterationMonitor to install/uninstall apps when apps are moved in/out of the
    // installed/uninstalled app directories
    fileAlterationMonitor = new FileAlterationMonitor(2000L);
    File installedAppsPath = new File(getInstalledAppsPath());
    FileAlterationObserver installAlterationObserver = new FileAlterationObserver(installedAppsPath, new AppFileFilter(installedAppsPath), IOCase.SYSTEM);
    final AppManager appManager = this;
    // Listen for events on the "installed apps" folder
    installAlterationObserver.addListener(new FileAlterationListenerAdaptor() {

        @Override
        public void onFileCreate(File file) {
            App parsedApp = null;
            try {
                parsedApp = appParser.parseApp(file);
            } catch (AppParsingException e) {
                userLogger.error("Could not parse app from newly discovered file '" + file.getAbsolutePath() + "' :", e);
                return;
            }
            boolean startApp = parsedApp.isCompatible(version);
            if (!startApp) {
                userLogger.error("Newly discovered app '" + parsedApp.getAppName() + "' is not compatible with the running version of Cytoscape (" + version + ").");
            }
            App registeredApp = null;
            for (App app : apps) {
                if (parsedApp.heuristicEquals(app)) {
                    registeredApp = app;
                    userLogger.warn("Newly discovered app '" + parsedApp.getAppName() + "' in file '" + parsedApp.getAppFile().getAbsolutePath() + "' is equal to an already registered app '" + registeredApp.getAppName() + "' in file '" + (registeredApp.getAppFile() == null ? "N/A" : registeredApp.getAppFile().getAbsolutePath()) + "'");
                    // Delete old file if it was still there
                    File oldFile = registeredApp.getAppFile();
                    if (oldFile != null && oldFile.exists() && !registeredApp.getAppFile().equals(parsedApp.getAppFile())) {
                        userLogger.info("Trying to delete the installed copy of app '" + registeredApp.getAppName() + "' in file '" + registeredApp.getAppFile().getAbsolutePath() + "' because another copy exists.");
                        FileUtils.deleteQuietly(oldFile);
                    }
                    // Update file reference to reflect file having been moved
                    registeredApp.setAppFile(file);
                    registeredApp.setStatus(AppStatus.INACTIVE);
                } else if (parsedApp.isCompatible(version) && parsedApp.getAppName().equals(app.getAppName())) {
                    try {
                        if (!app.isDetached() && app.isCompatible(version)) {
                            if (compareApps(parsedApp, app) > 0) {
                                startApp = false;
                                userLogger.warn("Not starting newly discovered app '" + parsedApp.getAppName() + "' because a newer version is already loaded.");
                            } else {
                                app.unload(AppManager.this);
                                app.setStatus(AppStatus.INACTIVE);
                                userLogger.warn("Unloading app '" + app.getAppName() + "' because the newly discovered app '" + parsedApp.getAppName() + "' is a newer version.");
                            }
                        }
                    } catch (AppUnloadingException e) {
                        // TODO Auto-generated catch block
                        userLogger.warn("Failed to unload app " + app.getAppName(), e);
                    }
                }
            }
            App app = null;
            if (registeredApp == null) {
                app = parsedApp;
                apps.add(app);
            } else {
                app = registeredApp;
            }
            try {
                if (startApp) {
                    app.load(appManager);
                    app.start(appManager);
                    app.setStatus(AppStatus.INSTALLED);
                    userLogger.info("Started newly discovered app '" + app.getAppName() + "'.");
                }
            } catch (AppLoadingException e) {
                app.setStatus(AppStatus.FAILED_TO_LOAD);
                userLogger.error("Failed to load app " + app.getAppName(), e);
            } catch (AppStartupException e) {
                app.setStatus(AppStatus.FAILED_TO_START);
                userLogger.error("Failed to start app " + app.getAppName(), e);
            }
            fireAppsChangedEvent();
        }

        @Override
        public void onFileChange(File file) {
            // Can treat file replacements/changes as old file deleted, new file added
            this.onFileDelete(file);
            this.onFileCreate(file);
            fireAppsChangedEvent();
        }

        @Override
        public void onFileDelete(File file) {
            // System.out.println(file + " on delete");
            DebugHelper.print(this + " installObserverDelete", file.getAbsolutePath() + " deleted.");
            App registeredApp = null;
            for (App app : apps) {
                if (file.equals(app.getAppFile())) {
                    app.setAppFile(null);
                    registeredApp = app;
                    break;
                }
            }
            if (registeredApp == null)
                return;
            try {
                registeredApp.unload(appManager);
                registeredApp.setStatus(AppStatus.FILE_MOVED);
                userLogger.info("Unloaded app '" + registeredApp.getAppName() + "', because its file is no longer available.");
            } catch (AppUnloadingException e) {
                userLogger.warn("Failed to unload app " + registeredApp.getAppName(), e);
            }
            // Do this so that we don't reload an old app when responding to change events
            if (file.exists()) {
                App parsedApp = null;
                try {
                    parsedApp = appParser.parseApp(file);
                } catch (AppParsingException e) {
                    return;
                }
                if (parsedApp.isCompatible(version) && registeredApp.getAppName().equalsIgnoreCase(parsedApp.getAppName()))
                    return;
            }
            App appToStart = null;
            for (App app : apps) {
                if (!app.isDetached() && app.isCompatible(version) && app.getAppName().equalsIgnoreCase(registeredApp.getAppName())) {
                    if (appToStart == null || compareApps(appToStart, app) > 0)
                        appToStart = app;
                }
            }
            if (appToStart != null) {
                try {
                    appToStart.load(appManager);
                    appToStart.start(appManager);
                    appToStart.setStatus(AppStatus.INSTALLED);
                    userLogger.info("Started app " + appToStart.getAppName() + " because a different version has been unloaded.");
                } catch (AppLoadingException e) {
                    appToStart.setStatus(AppStatus.FAILED_TO_LOAD);
                    userLogger.error("Failed to load app " + appToStart.getAppName(), e);
                } catch (AppStartupException e) {
                    appToStart.setStatus(AppStatus.FAILED_TO_START);
                    userLogger.error("Failed to start app " + appToStart.getAppName(), e);
                }
            }
            fireAppsChangedEvent();
        }
    });
    FileAlterationObserver disableAlterationObserver = new FileAlterationObserver(getDisabledAppsPath(), new AppFileFilter(new File(getDisabledAppsPath())), IOCase.SYSTEM);
    // Listen for events on the "disabled apps" folder
    disableAlterationObserver.addListener(new FileAlterationListenerAdaptor() {

        @Override
        public void onFileCreate(File file) {
            App parsedApp = null;
            try {
                parsedApp = appParser.parseApp(file);
            } catch (AppParsingException e) {
                return;
            }
            DebugHelper.print(this + " disableObserver Create", parsedApp.getAppName() + " parsed");
            App registeredApp = null;
            for (App app : apps) {
                if (parsedApp.heuristicEquals(app)) {
                    registeredApp = app;
                    // Delete old file if it was still there
                    // TODO: Possible rename from filename-2 to filename?
                    File oldFile = registeredApp.getAppFile();
                    if (oldFile != null && oldFile.exists() && !registeredApp.getAppFile().equals(parsedApp.getAppFile())) {
                        DebugHelper.print(this + " disableObserverCreate", registeredApp.getAppName() + " moved from " + registeredApp.getAppFile().getAbsolutePath() + " to " + parsedApp.getAppFile().getAbsolutePath() + ". deleting: " + oldFile);
                        FileUtils.deleteQuietly(oldFile);
                    }
                    // Update file reference to reflect file having been moved
                    registeredApp.setAppFile(file);
                }
            }
            App app = null;
            if (registeredApp == null) {
                app = parsedApp;
                apps.add(app);
            } else {
                app = registeredApp;
            }
            app.setStatus(AppStatus.DISABLED);
            fireAppsChangedEvent();
        // System.out.println(file + " on create");
        }

        @Override
        public void onFileChange(File file) {
            // Can treat file replacements/changes as old file deleted, new file added
            this.onFileDelete(file);
            this.onFileCreate(file);
            fireAppsChangedEvent();
        }

        @Override
        public void onFileDelete(File file) {
            // System.out.println(file + " on delete");
            DebugHelper.print(this + " disableObserverDelete", file.getAbsolutePath() + " deleted.");
            for (App app : apps) {
                // System.out.println("checking " + app.getAppFile().getAbsolutePath());
                if (file.equals(app.getAppFile())) {
                    app.setAppFile(null);
                    app.setStatus(AppStatus.FILE_MOVED);
                    break;
                }
            }
            fireAppsChangedEvent();
        }
    });
    FileAlterationObserver uninstallAlterationObserver = new FileAlterationObserver(getUninstalledAppsPath(), new AppFileFilter(new File(getUninstalledAppsPath())), IOCase.SYSTEM);
    // Listen for events on the "uninstalled apps" folder
    uninstallAlterationObserver.addListener(new FileAlterationListenerAdaptor() {

        @Override
        public void onFileCreate(File file) {
            App parsedApp = null;
            try {
                parsedApp = appParser.parseApp(file);
            } catch (AppParsingException e) {
                return;
            }
            DebugHelper.print(this + " uninstallObserverCreate", parsedApp.getAppName() + " parsed");
            App registeredApp = null;
            for (App app : apps) {
                if (parsedApp.heuristicEquals(app)) {
                    registeredApp = app;
                    // Delete old file if it was still there
                    // TODO: Possible rename from filename-2 to filename?
                    File oldFile = registeredApp.getAppFile();
                    if (oldFile != null && oldFile.exists() && !registeredApp.getAppFile().equals(parsedApp.getAppFile())) {
                        DebugHelper.print(this + " uninstallObserverCreate", registeredApp.getAppName() + " moved from " + registeredApp.getAppFile().getAbsolutePath() + " to " + parsedApp.getAppFile().getAbsolutePath() + ". deleting: " + oldFile);
                        FileUtils.deleteQuietly(oldFile);
                    }
                    // Update file reference to reflect file having been moved
                    registeredApp.setAppFile(file);
                }
            }
            App app = null;
            if (registeredApp == null) {
                app = parsedApp;
                apps.add(app);
            } else {
                app = registeredApp;
            }
            app.setStatus(AppStatus.UNINSTALLED);
            fireAppsChangedEvent();
        // System.out.println(file + " on create");
        }

        @Override
        public void onFileChange(File file) {
            // Can treat file replacements/changes as old file deleted, new file added
            this.onFileDelete(file);
            this.onFileCreate(file);
            fireAppsChangedEvent();
        }

        @Override
        public void onFileDelete(File file) {
            // System.out.println(file + " on delete");
            DebugHelper.print(this + " uninstallObserverDelete", file.getAbsolutePath() + " deleted.");
            for (App app : apps) {
                // System.out.println("checking " + app.getAppFile().getAbsolutePath());
                if (file.equals(app.getAppFile())) {
                    app.setAppFile(null);
                    app.setStatus(AppStatus.FILE_MOVED);
                    break;
                }
            }
            fireAppsChangedEvent();
        }
    });
    try {
        installAlterationObserver.initialize();
        fileAlterationMonitor.addObserver(installAlterationObserver);
        disableAlterationObserver.initialize();
        fileAlterationMonitor.addObserver(disableAlterationObserver);
        uninstallAlterationObserver.initialize();
        fileAlterationMonitor.addObserver(uninstallAlterationObserver);
        fileAlterationMonitor.start();
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
Also used : AbstractCyApp(org.cytoscape.app.AbstractCyApp) FileAlterationMonitor(org.apache.commons.io.monitor.FileAlterationMonitor) AppLoadingException(org.cytoscape.app.internal.exception.AppLoadingException) AppParsingException(org.cytoscape.app.internal.exception.AppParsingException) AppUnloadingException(org.cytoscape.app.internal.exception.AppUnloadingException) AppStartupException(org.cytoscape.app.internal.exception.AppStartupException) AppInstallException(org.cytoscape.app.internal.exception.AppInstallException) AppUninstallException(org.cytoscape.app.internal.exception.AppUninstallException) AppLoadingException(org.cytoscape.app.internal.exception.AppLoadingException) IOException(java.io.IOException) AppDisableException(org.cytoscape.app.internal.exception.AppDisableException) AppParsingException(org.cytoscape.app.internal.exception.AppParsingException) FileAlterationObserver(org.apache.commons.io.monitor.FileAlterationObserver) FileAlterationListenerAdaptor(org.apache.commons.io.monitor.FileAlterationListenerAdaptor) AppStartupException(org.cytoscape.app.internal.exception.AppStartupException) AppUnloadingException(org.cytoscape.app.internal.exception.AppUnloadingException) File(java.io.File)

Example 2 with AppParsingException

use of org.cytoscape.app.internal.exception.AppParsingException in project cytoscape-impl by cytoscape.

the class AppParser method parseApp.

/**
 * Attempt to parse a given {@link File} object as an {@link App} object.
 * @param file The file to use for parsing
 * @return An {@link App} object representing the given file if parsing was successful
 * @throws AppParsingException If there was an error during parsing, such as missing data from the manifest file
 */
public App parseApp(File file) throws AppParsingException {
    App parsedApp = new SimpleApp();
    DebugHelper.print("Parsing: " + file.getPath());
    if (!file.exists()) {
        throw new AppParsingException("No file with path: " + file.getAbsolutePath());
    }
    if (!file.isFile()) {
        throw new AppParsingException("The given file, " + file + ", is not a file.");
    }
    // Attempt to parse the file as a jar file
    JarFile jarFile = null;
    try {
        jarFile = new JarFile(file);
    } catch (IOException e) {
        throw new AppParsingException("Error parsing given file as a jar file: " + e.getMessage());
    }
    boolean bundleApp = false;
    boolean xmlParseFailed = false;
    // Treat the jar as an OSGi bundle if OSGi metadata is found
    boolean osgiMetadataFound = false;
    // Check if a manifest that contains OSGi metadata is present
    try {
        Manifest osgiManifest = jarFile.getManifest();
        if (osgiManifest != null) {
            if (osgiManifest.getMainAttributes().getValue("Bundle-SymbolicName") != null) {
                osgiMetadataFound = true;
            }
        }
    } catch (IOException e) {
    }
    if (osgiMetadataFound) {
        bundleApp = true;
        parsedApp = new BundleApp();
    }
    // Attempt to obtain manifest file from jar
    Manifest manifest = null;
    try {
        manifest = jarFile.getManifest();
    } catch (IOException e) {
        throw new AppParsingException("Error obtaining manifest from app jar", e);
    } finally {
        try {
            jarFile.close();
        } catch (IOException e) {
            throw new AppParsingException("Error closing file", e);
        }
    }
    // Make sure the getManifest() call didn't return null
    if (manifest == null) {
        throw new AppParsingException("No manifest was found in the jar file.");
    }
    // Bundle apps are instantiated by OSGi using their activator classes
    String entryClassName = null;
    if (!bundleApp) {
        // Obtain the fully-qualified name of the class to instantiate upon app installation
        entryClassName = manifest.getMainAttributes().getValue(APP_CLASS_TAG);
        if (entryClassName == null || entryClassName.trim().length() == 0) {
            throw new AppParsingException("Jar is missing value for entry " + APP_CLASS_TAG + " in its manifest file.");
        }
    } else {
        // Obtain the fully-qualified name of the class to instantiate upon app installation
        entryClassName = manifest.getMainAttributes().getValue("Bundle-Activator");
        if (entryClassName == null || entryClassName.trim().length() == 0) {
            throw new AppParsingException("Jar is missing value for entry Bundle-Activator");
        }
    }
    // Obtain the human-readable name of the app
    String readableName = null;
    if (!bundleApp) {
        readableName = manifest.getMainAttributes().getValue(APP_READABLE_NAME_TAG);
        if (readableName == null || readableName.trim().length() == 0) {
            throw new AppParsingException("Jar is missing value for entry " + APP_READABLE_NAME_TAG + " in its manifest file.");
        }
    } else {
        readableName = manifest.getMainAttributes().getValue("Bundle-Name");
        if (readableName == null || readableName.trim().length() == 0) {
            readableName = manifest.getMainAttributes().getValue("Bundle-SymbolicName");
        }
        if (readableName == null || readableName.trim().length() == 0) {
            throw new AppParsingException("Bundle jar manifest had no entry for Bundle-Name, and no entry for Bundle-SymbolicName");
        }
    }
    // Obtain the version of the app, in major.minor.patch[-tag] format, ie. 3.0.0-SNAPSHOT or 1.2.3
    String appVersion = null;
    if (!bundleApp) {
        appVersion = manifest.getMainAttributes().getValue(APP_VERSION_TAG);
        if (appVersion == null || appVersion.trim().length() == 0) {
            throw new AppParsingException("Jar is missing value for entry " + APP_VERSION_TAG + " in its manifiest file.");
        } else if (!APP_VERSION_TAG_REGEX.matcher(appVersion).matches()) {
            throw new AppParsingException("The app version specified in its manifest file under the key " + APP_VERSION_TAG + " was found to not match the format major.minor[.patch][-tag], eg. 2.1, 2.1-test, 3.0.0 or 3.0.0-SNAPSHOT");
        }
    } else {
        appVersion = manifest.getMainAttributes().getValue("Bundle-Version");
        if (appVersion == null || appVersion.trim().length() == 0) {
            // For now, while it hasn't been decided, accept values for Cytoscape-App-Version if Bundle-Version is not found
            appVersion = manifest.getMainAttributes().getValue(APP_VERSION_TAG);
            if (appVersion == null || appVersion.trim().length() == 0) {
                throw new AppParsingException("Bundle jar manifest has no entry for Bundle-Version");
            }
        }
    }
    String compatibleVersions = null;
    if (bundleApp) {
        compatibleVersions = manifest.getMainAttributes().getValue("Import-Package");
        if (compatibleVersions == null || compatibleVersions.trim().length() == 0) {
            throw new AppParsingException("Jar is missing value for entry Import-Package in its manifest file.");
        }
    } else {
        compatibleVersions = manifest.getMainAttributes().getValue(APP_COMPATIBLE_TAG);
        if (compatibleVersions == null || compatibleVersions.trim().length() == 0) {
            // For now, accept the deprecated field Cytoscape-App-Works-With if the official field was not found
            compatibleVersions = manifest.getMainAttributes().getValue("Cytoscape-App-Works-With");
        }
        if (compatibleVersions == null || compatibleVersions.trim().length() == 0) {
            throw new AppParsingException("Jar is missing value for entry " + APP_COMPATIBLE_TAG + " in its manifest file.");
        } else if (!compatibleVersions.matches(APP_COMPATIBLE_TAG_REGEX)) {
            throw new AppParsingException("The known compatible versions of Cytoscape specified in the manifest under the" + " key " + APP_COMPATIBLE_TAG + " does not match the form of a comma-delimited list of versions of the form" + " major[.minor] (eg. 1 or 1.0) with variable whitespace around versions");
        }
    }
    List<App.Dependency> deps = null;
    final String depsStr = manifest.getMainAttributes().getValue("Cytoscape-App-Dependencies");
    if (depsStr != null && depsStr.trim().length() != 0) {
        deps = new ArrayList<App.Dependency>();
        final String[] depsPieces = splitSmart(',', depsStr);
        for (final String depPiece : depsPieces) {
            final String[] depPieces = splitSmart(';', depPiece);
            if (depPieces.length < 2)
                throw new AppParsingException("Each dependency must have a name and version");
            deps.add(new App.Dependency(depPieces[0], depPieces[1]));
        }
    }
    String fileHash;
    try {
        fileHash = getChecksum(file);
        parsedApp.setSha512Checksum(fileHash);
    } catch (ChecksumException e) {
        parsedApp.setSha512Checksum(null);
    }
    parsedApp.setAppFile(file);
    parsedApp.setAppName(readableName);
    parsedApp.setEntryClassName(entryClassName);
    parsedApp.setVersion(appVersion);
    parsedApp.setCompatibleVersions(compatibleVersions);
    parsedApp.setAppValidated(true);
    parsedApp.setDependencies(deps);
    return parsedApp;
}
Also used : IOException(java.io.IOException) JarFile(java.util.jar.JarFile) Manifest(java.util.jar.Manifest) AppParsingException(org.cytoscape.app.internal.exception.AppParsingException)

Example 3 with AppParsingException

use of org.cytoscape.app.internal.exception.AppParsingException in project cytoscape-impl by cytoscape.

the class AppManager method obtainAppsFromDirectory.

/**
 * Obtain a set of {@link App} objects through attempting to parse files found in the first level of the given directory.
 * @param directory The directory used to parse {@link App} objects
 * @return A set of all {@link App} objects that were successfully parsed from files in the given directory
 */
private Set<App> obtainAppsFromDirectory(File directory, boolean isBundled) {
    // Obtain all files in the given directory with supported extensions, perform a non-recursive search
    Collection<File> files = FileUtils.listFiles(directory, APP_EXTENSIONS, false);
    Set<App> parsedApps = new HashSet<App>();
    App app;
    for (File file : files) {
        app = null;
        try {
            app = appParser.parseApp(file);
            app.setBundledApp(isBundled);
        } catch (AppParsingException e) {
            app = null;
        } finally {
            if (app != null) {
                parsedApps.add(app);
                DebugHelper.print("App parsed: " + app);
            }
        }
    }
    return parsedApps;
}
Also used : AbstractCyApp(org.cytoscape.app.AbstractCyApp) File(java.io.File) HashSet(java.util.HashSet) AppParsingException(org.cytoscape.app.internal.exception.AppParsingException)

Aggregations

AppParsingException (org.cytoscape.app.internal.exception.AppParsingException)3 File (java.io.File)2 IOException (java.io.IOException)2 AbstractCyApp (org.cytoscape.app.AbstractCyApp)2 HashSet (java.util.HashSet)1 JarFile (java.util.jar.JarFile)1 Manifest (java.util.jar.Manifest)1 FileAlterationListenerAdaptor (org.apache.commons.io.monitor.FileAlterationListenerAdaptor)1 FileAlterationMonitor (org.apache.commons.io.monitor.FileAlterationMonitor)1 FileAlterationObserver (org.apache.commons.io.monitor.FileAlterationObserver)1 AppDisableException (org.cytoscape.app.internal.exception.AppDisableException)1 AppInstallException (org.cytoscape.app.internal.exception.AppInstallException)1 AppLoadingException (org.cytoscape.app.internal.exception.AppLoadingException)1 AppStartupException (org.cytoscape.app.internal.exception.AppStartupException)1 AppUninstallException (org.cytoscape.app.internal.exception.AppUninstallException)1 AppUnloadingException (org.cytoscape.app.internal.exception.AppUnloadingException)1