Author: paperwing
Date: 2012-04-02 16:10:33 -0700 (Mon, 02 Apr 2012)
New Revision: 28717

Modified:
   
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/App.java
   
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/AppManager.java
Log:
Completed AppManager methods for installing and uninstalling simple apps; apps 
are installed to the subdirectory for installed apps and moved to the 
subdirectory for uninstalled apps when uninstalled. App installation now uses a 
URLClassLoader to obtain and load the appropriate classes from the app jar file.

Modified: 
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/App.java
===================================================================
--- 
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/App.java
 2012-04-02 22:45:09 UTC (rev 28716)
+++ 
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/App.java
 2012-04-02 23:10:33 UTC (rev 28717)
@@ -1,6 +1,7 @@
 package org.cytoscape.app.internal.manager;
 
 import java.io.File;
+import java.net.URL;
 
 import org.cytoscape.app.AbstractCyApp;
 
@@ -21,6 +22,31 @@
         */
        private String entryClassName;
        
+       /**
+        * The URL to the jar file containing the app.
+        */
+       private URL jarURL;
+       
+       /**
+        * A reference to the instance of the app's class that extends {@link 
AbstractCyApp}.
+        */
+       private AbstractCyApp appInstance;
+       
+       /**
+        * Whether this App object represents an app that has been checked to 
have valid packaging (such as containing
+        * necessary tags in its manifest file) and contains valid fields, 
making it loadable by the {@link AppManager} service.
+        */
+       private boolean appValidated;
+       
+       private AppStatus status;
+       
+       /**
+        * An enumeration that indicates the status of a given app, such as 
whether it is installed or uninstalled.
+        */
+       public enum AppStatus{
+               INSTALLED
+       }
+       
        public App(String appName, String version, String authors, String 
description, File appFile, String entryClassName) {
                this.appName = appName;
                this.version = version;
@@ -28,6 +54,8 @@
                this.description = description;
                this.appFile = appFile;
                this.entryClassName = entryClassName;
+               
+               this.setAppValidated(false);
        }
        
        public String getAppName() {
@@ -53,4 +81,39 @@
        public String getEntryClassName() {
                return entryClassName;
        }
+       
+       public URL getJarURL() {
+               return jarURL;
+       }
+
+       public AbstractCyApp getAppInstance() {
+               return appInstance;
+       }
+
+       public boolean isAppValidated() {
+               return appValidated;
+       }
+       
+       public AppStatus getStatus() {
+               return status;
+       }
+       
+       public void setAppFile(File appFile) {
+               this.appFile = appFile;
+       }
+       
+       public void setAppInstance(AbstractCyApp appInstance) {
+               this.appInstance = appInstance;
+       }
+
+       public void setAppValidated(boolean appValidated) {
+               this.appValidated = appValidated;
+       }
+
+       public void setStatus(AppStatus status) {
+               this.status = status;
+       }
+
+
+
 }

Modified: 
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/AppManager.java
===================================================================
--- 
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/AppManager.java
  2012-04-02 22:45:09 UTC (rev 28716)
+++ 
csplugins/trunk/toronto/yuedong/app_manager/impl/app-impl/src/main/java/org/cytoscape/app/internal/manager/AppManager.java
  2012-04-02 23:10:33 UTC (rev 28717)
@@ -5,6 +5,7 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
+import java.net.URLClassLoader;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.jar.JarFile;
@@ -13,6 +14,7 @@
 import org.cytoscape.app.AbstractCyApp;
 import org.cytoscape.app.CyAppAdapter;
 import org.cytoscape.app.internal.exception.AppCopyException;
+import org.cytoscape.app.internal.manager.App.AppStatus;
 import org.cytoscape.application.CyApplicationConfiguration;
 
 /**
@@ -20,6 +22,14 @@
  * also provides functionalities for installing and uninstalling apps.
  */
 public class AppManager {
+       /** Installed apps are copied to this subdirectory under the local app 
storage directory. */
+       private static final String INSTALLED_APPS_DIRECTORY_NAME = "Installed";
+       
+       /** Uninstalled apps are copied to this subdirectory under the local 
app storage directory. */
+       private static final String UNINSTALLED_APPS_DIRECTORY_NAME = 
"Uninstalled";
+       
+       /** The name of the key in the app jar's manifest file that indicates 
the fully-qualified name 
+        * of the class to instantiate upon app installation. */
        private static final String APP_CLASS_TAG = "Cytoscape-App";
        
        private Set<App> installedApps;
@@ -46,58 +56,133 @@
        }
        
        /**
-        * Installs an app; first makes a copy of the app file and places it in 
the directory used to hold all installed and uninstalled apps.
-        * Then, the app is created by instancing its {@link AbstractCyApp} 
class that implements.
+        * Attempts to install an app. Makes a copy of the app file and places 
it in the directory 
+        * used to hold all installed and uninstalled apps. Then, the app is 
created by instancing 
+        * its class that extends {@link AbstractCyApp}.
+        * 
+        * Before the app is installed, it is checked if it contains valid 
packaging by its isAppValidated() method.
+        * Apps that have not been validated are ignored.
+        * 
         * @param app The {@link App} object representing and providing 
information about the app to install
         * @throws AppCopyException If there was an IO-related error during the 
copy operation that prevents the app from 
         * being successfully installed.
         */
        public void installApp(App app) throws AppCopyException {
                
-               File appFile = app.getAppFile();
+               // Check if the app has been verified to contain proper 
packaging.
+               if (!app.isAppValidated()) {
+                       
+                       // If the app is not packaged properly or is missing 
fields in its manifest file, do not install the app
+                       // as the install operation will fail.
+                       return;
+               }
                
-               // Copy app to local storage directory using utilities provided 
by the Apache Commons library.
+               // Attempt to copy the app to the directory for installed apps.
                try {
-                       // Overwrites files with the same name.
-                       FileUtils.copyFileToDirectory(appFile, getAppPath());
+                       // Uses Apache Commons library; overwrites files with 
the same name.
+                       FileUtils.copyFileToDirectory(app.getAppFile(), new 
File(getInstalledAppsPath()));
+                       
+                       // Update the app's path
+                       String fileName = app.getAppFile().getName();
+                       app.setAppFile(new File(getInstalledAppsPath() + 
File.separator + fileName));
+                       
                } catch (IOException e) {
                        
                        throw new AppCopyException();
                }
                
                // TODO: Currently uses the CyAppAdapter's loader to load apps' 
classes. Is there reason to use a different one?
-               ClassLoader appClassLoader = 
appAdapter.getClass().getClassLoader();
+               ClassLoader appClassLoader = new URLClassLoader(
+                               new URL[]{app.getJarURL()}, 
appAdapter.getClass().getClassLoader());
                
+               String entryClassName = app.getEntryClassName();
+               
+               // Attempt to load the class
                Class<?> appEntryClass = null;
                try {
-                        appEntryClass = 
appClassLoader.loadClass(app.getEntryClassName());
+                        appEntryClass = 
appClassLoader.loadClass(entryClassName);
                } catch (ClassNotFoundException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
+                       
+                       throw new IllegalStateException("Class " + 
entryClassName + " not found in URL: " + app.getJarURL());
                }
                
+               // Attempt to obtain the constructor
                Constructor<?> constructor = null;
                try {
                        constructor = 
appEntryClass.getConstructor(CyAppAdapter.class);
                } catch (SecurityException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
+                       throw new IllegalStateException("Access to the 
constructor for " + appEntryClass + " denied.");
                } catch (NoSuchMethodException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
+                       throw new IllegalStateException("Unable to find a 
constructor for " + appEntryClass + " that takes a CyAppAdapter as its 
argument.");
                }
                
+               // Attempt to instantiate the app's class that extends 
AbstractCyActivator.
+               Object appInstance = null;
                try {
-                       Object object = constructor.newInstance(appAdapter);
+                       appInstance = constructor.newInstance(appAdapter);
                } catch (IllegalArgumentException e) {
+                       throw new IllegalStateException("Illegal arguments 
passed to the constructor for the app's entry class: " + e.getMessage());
                } catch (InstantiationException e) {
+                       throw new RuntimeException("Error instantiating the 
class " + appEntryClass + ": " + e.getMessage());
                } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
+                       throw new RuntimeException(e);
                }
 
+               // Keep a reference to the newly created instance
+               app.setAppInstance((AbstractCyApp) appInstance);
+               app.setStatus(AppStatus.INSTALLED);
+               
                installedApps.add(app);
        }
        
+       /**
+        * Uninstalls an app. If it was located in the subdirectory containing 
currently installed apps in the
+        * local storage directory, it will be moved to the subdirectory 
containing currently uninstalled apps.
+        * 
+        * The app will only be uninstalled if it is currently installed.
+        * 
+        * @param app The app to be uninstalled.
+        */
+       public void uninstallApp(App app) {
+               // Check if the app is installed before attempting to uninstall.
+               if (app.getStatus() != AppStatus.INSTALLED) {
+                       // If it is not installed, do not attempt to uninstall 
it.
+                       return;
+               }
+               
+               // Check if the app is inside the directory containing 
currently installed apps.
+               // If so, prepare to move it to the uninstalled directory.
+               File appParentDirectory = app.getAppFile().getParentFile();
+               try {
+                       // Obtain the path of the "installed apps" subdirectory.
+                       String uninstalledAppsPath = getUninstalledAppsPath();
+                       
+                       if (appParentDirectory.getCanonicalPath().equals(
+                                       getInstalledAppsPath())) {
+                               
+                               try {
+                                       // Use the Apache commons library to 
move the file, overwriting an existing file if needed.
+                                       
FileUtils.copyFileToDirectory(app.getAppFile(), new File(uninstalledAppsPath));
+                               } catch (IOException e) {
+                                       throw new RuntimeException("Unable to 
copy file: " + e.getMessage());
+                               }
+                               
+                               // Delete the source file after the copy 
operation
+                               String fileName = app.getAppFile().getName();
+                               app.getAppFile().delete();
+                               app.setAppFile(new File(uninstalledAppsPath + 
File.separator + fileName));
+                       }
+               } catch (IOException e) {
+                       throw new RuntimeException("Unable to obtain path: " + 
e.getMessage());
+               }
+       }
+       
+       /**
+        * Return the set of all currently installed apps.
+        * @return
+        */
        public Set<App> getInstalledApps() {
                return installedApps;
        }
@@ -106,8 +191,32 @@
         * Return the path of the directory used to contain all apps.
         * @return The path of the root directory containing all installed and 
uninstalled apps.
         */
-       private File getAppPath() {
+       private File getBaseAppPath() {
                // TODO: At time of writing, CyApplicationConfiguration always 
returns the home directory for directory location.
                return 
applicationConfiguration.getConfigurationDirectoryLocation();
        }
+       
+       /**
+        * Return the canonical path of the subdirectory in the local storage 
directory containing installed apps.
+        * @return The canonical path of the subdirectory in the local storage 
directory containing currently installed apps.
+        */
+       private String getInstalledAppsPath() {
+               try {
+                       return getBaseAppPath().getCanonicalPath() + 
File.separator + INSTALLED_APPS_DIRECTORY_NAME;
+               } catch (IOException e) {
+                       throw new RuntimeException("Unable to obtain canonical 
path for installed apps directory: " + e.getMessage());
+               }
+       }
+       
+       /**
+        * Return the canonical path of the subdirectory in the local storage 
directory containing uninstalled apps.
+        * @return The canonical path of the subdirectory in the local storage 
directory containing uninstalled apps.
+        */
+       private String getUninstalledAppsPath() {
+               try {
+                       return getBaseAppPath().getCanonicalPath() + 
File.separator + UNINSTALLED_APPS_DIRECTORY_NAME;
+               } catch (IOException e) {
+                       throw new RuntimeException("Unable to obtain canonical 
path for uninstalled apps directory: " + e.getMessage());
+               }
+       }
 }

-- 
You received this message because you are subscribed to the Google Groups 
"cytoscape-cvs" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/cytoscape-cvs?hl=en.

Reply via email to