You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by mj...@apache.org on 2017/10/06 16:51:21 UTC

[04/17] incubator-guacamole-client git commit: GUACAMOLE-364: add extension module support for event listeners

GUACAMOLE-364: add extension module support for event listeners


Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/109d57ec
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/109d57ec
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/109d57ec

Branch: refs/heads/master
Commit: 109d57ecb372c3fc8114792086f339e9269eaf98
Parents: dca7862
Author: Carl Harris <ce...@vt.edu>
Authored: Wed Aug 16 06:55:28 2017 -0400
Committer: Carl Harris <ce...@vt.edu>
Committed: Wed Aug 16 06:55:28 2017 -0400

----------------------------------------------------------------------
 .../apache/guacamole/extension/Extension.java   | 95 ++++++++++++++++++++
 .../guacamole/extension/ExtensionManifest.java  | 31 +++++++
 .../guacamole/extension/ExtensionModule.java    | 53 +++++++++++
 3 files changed, 179 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/109d57ec/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java
index 3183fa2..ac58676 100644
--- a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java
@@ -35,6 +35,8 @@ import java.util.Map;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
+
+import org.apache.guacamole.net.event.listener.Listener;
 import org.codehaus.jackson.JsonParseException;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.apache.guacamole.GuacamoleException;
@@ -110,6 +112,11 @@ public class Extension {
     private final Collection<Class<AuthenticationProvider>> authenticationProviderClasses;
 
     /**
+     * The collection of all Listener classes defined within the extension.
+     */
+    private final Collection<Class<Listener>> listenerClasses;
+
+    /**
      * The resource for the small favicon for the extension. If provided, this
      * will replace the default Guacamole icon.
      */
@@ -266,6 +273,80 @@ public class Extension {
     }
 
     /**
+     * Retrieve the Listener subclass having the given name. If
+     * the class having the given name does not exist or isn't actually a
+     * subclass of Listener, an exception will be thrown.
+     *
+     * @param name
+     *     The name of the Listener class to retrieve.
+     *
+     * @return
+     *     The subclass of Listener having the given name.
+     *
+     * @throws GuacamoleException
+     *     If no such class exists, or if the class with the given name is not
+     *     a subclass of Listener.
+     */
+    @SuppressWarnings("unchecked") // We check this ourselves with isAssignableFrom()
+    private Class<Listener> getListenerClass(String name)
+            throws GuacamoleException {
+
+        try {
+
+            // Get listener class
+            Class<?> listenerClass = classLoader.loadClass(name);
+
+            // Verify the located class is actually a subclass of Listener
+            if (!Listener.class.isAssignableFrom(listenerClass))
+                throw new GuacamoleServerException("Listeners MUST implement a Listener subclass.");
+
+            // Return located class
+            return (Class<Listener>) listenerClass;
+
+        }
+        catch (ClassNotFoundException e) {
+            throw new GuacamoleException("Listener class not found.", e);
+        }
+        catch (LinkageError e) {
+            throw new GuacamoleException("Listener class cannot be loaded (wrong version of API?).", e);
+        }
+
+    }
+
+    /**
+     * Returns a new collection of all Listener subclasses having the given names.
+     * If any class does not exist or isn't actually subclass of Listener, an
+     * exception will be thrown, an no further Listener classes will be loaded.
+     *
+     * @param names
+     *     The names of the AuthenticationProvider classes to retrieve.
+     *
+     * @return
+     *     A new collection of all AuthenticationProvider subclasses having the
+     *     given names.
+     *
+     * @throws GuacamoleException
+     *     If any given class does not exist, or if any given class is not a
+     *     subclass of AuthenticationProvider.
+     */
+    private Collection<Class<Listener>> getListenerClasses(Collection<String> names)
+            throws GuacamoleException {
+
+        // If no classnames are provided, just return an empty list
+        if (names == null)
+            return Collections.<Class<Listener>>emptyList();
+
+        // Define all auth provider classes
+        Collection<Class<Listener>> classes = new ArrayList<Class<Listener>>(names.size());
+        for (String name : names)
+            classes.add(getListenerClass(name));
+
+        // Callers should not rely on modifying the result
+        return Collections.unmodifiableCollection(classes);
+    }
+
+
+    /**
      * Loads the given file as an extension, which must be a .jar containing
      * a guac-manifest.json file describing its contents.
      *
@@ -363,6 +444,9 @@ public class Extension {
         // Define authentication providers
         authenticationProviderClasses = getAuthenticationProviderClasses(manifest.getAuthProviders());
 
+        // Define listeners
+        listenerClasses = getListenerClasses(manifest.getListeners());
+
         // Get small icon resource if provided
         if (manifest.getSmallIcon() != null)
             smallIcon = new ClassPathResource(classLoader, "image/png", manifest.getSmallIcon());
@@ -489,6 +573,17 @@ public class Extension {
     }
 
     /**
+     * Returns all declared listener classes associated wit this extension. Listeners are
+     * declared within the extension manifest.
+     *
+     * @return
+     *     All declared listener classes with this extension.
+     */
+    public Collection<Class<Listener>> getListenerClasses() {
+        return listenerClasses;
+    }
+
+    /**
      * Returns the resource for the small favicon for the extension. If
      * provided, this will replace the default Guacamole icon.
      * 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/109d57ec/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java
index 9b9bd9b..1636b03 100644
--- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java
@@ -88,6 +88,11 @@ public class ExtensionManifest {
     private Collection<String> authProviders;
 
     /**
+     * The names of all listener classes within this extension, if any.
+     */
+    private Collection<String> listeners;
+
+    /**
      * The path to the small favicon. If provided, this will replace the default
      * Guacamole icon.
      */
@@ -356,6 +361,32 @@ public class ExtensionManifest {
     }
 
     /**
+     * Returns the classnames of all listener classes within the extension.
+     * These classnames are defined within the manifest by the "listeners"
+     * property as an array of strings, where each string is a listener
+     * class name.
+     *
+     * @return
+     *      a collection of classnames for all listeners within the extension
+     */
+    public Collection<String> getListeners() {
+        return listeners;
+    }
+
+    /**
+     * Sets the classnames of all listener classes within the extension.
+     * These classnames are defined within the manifest by the "listeners"
+     * property as an array of strings, where each string is a listener
+     * class name.
+     *
+     * @param listeners
+     *      a collection of classnames for all listeners within the extension
+     */
+    public void setListeners(Collection<String> listeners) {
+        this.listeners = listeners;
+    }
+
+    /**
      * Returns the path to the small favicon, relative to the root of the
      * extension.
      *

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/109d57ec/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
index 792066c..1e1a854 100644
--- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
@@ -34,6 +34,7 @@ import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleServerException;
 import org.apache.guacamole.environment.Environment;
 import org.apache.guacamole.net.auth.AuthenticationProvider;
+import org.apache.guacamole.net.event.listener.Listener;
 import org.apache.guacamole.resource.Resource;
 import org.apache.guacamole.resource.ResourceServlet;
 import org.apache.guacamole.resource.SequenceResource;
@@ -92,6 +93,12 @@ public class ExtensionModule extends ServletModule {
             new ArrayList<AuthenticationProvider>();
 
     /**
+     * All currently-bound authentication providers, if any.
+     */
+    private final List<ListenerProvider> boundListenerProviders =
+            new ArrayList<ListenerProvider>();
+
+    /**
      * Service for adding and retrieving language resources.
      */
     private final LanguageResourceService languageResourceService;
@@ -188,6 +195,49 @@ public class ExtensionModule extends ServletModule {
     }
 
     /**
+     * Binds the given Listener class such that any service
+     * requiring access to the Listener can obtain it via
+     * injection, along with any other bound Listener.
+     *
+     * @param listenerClass
+     *     The Listener class to bind.
+     */
+    private void bindListenerProvider(Class<? extends Listener> listenerClass) {
+
+        // Bind listener
+        logger.debug("[{}] Binding Listener \"{}\".",
+                boundListenerProviders.size(), listenerClass.getName());
+        boundListenerProviders.add(new ListenerFacade(listenerClass));
+    }
+
+    /**
+     * Binds each of the the given Listener classes such that any
+     * service requiring access to the Listener can obtain it via
+     * injection.
+     *
+     * @param listeners
+     *     The Listener classes to bind.
+     */
+    private void bindListenerProviders(Collection<Class<Listener>> listeners) {
+
+        // Bind each listener within extension
+        for (Class<Listener> listener : listeners)
+            bindListenerProvider(listener);
+    }
+
+    /**
+     * Returns a list of all currently-bound ListenerProvider instances.
+     *
+     * @return
+     *     A List of all currently-bound ListenerProvider instances. The List is
+     *     not modifiable.
+     */
+    @Provides
+    public List<ListenerProvider> getListenerProviders() {
+        return Collections.unmodifiableList(boundListenerProviders);
+    }
+
+    /**
      * Serves each of the given resources as a language resource. Language
      * resources are served from within the "/translations" directory as JSON
      * files, where the name of each JSON file is the language key.
@@ -327,6 +377,9 @@ public class ExtensionModule extends ServletModule {
                 // Attempt to load all authentication providers
                 bindAuthenticationProviders(extension.getAuthenticationProviderClasses());
 
+                // Attempt to load all listeners
+                bindListenerProviders(extension.getListenerClasses());
+
                 // Add any translation resources
                 serveLanguageResources(extension.getTranslationResources());