You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2016/03/29 06:20:22 UTC
[13/51] [abbrv] incubator-guacamole-client git commit: GUACAMOLE-1:
Remove useless .net.basic subpackage, now that everything is being renamed.
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/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
new file mode 100644
index 0000000..3e2a8ae
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionManifest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * 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.
+ */
+
+package org.apache.guacamole.extension;
+
+import java.util.Collection;
+import java.util.Map;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * Java representation of the JSON manifest contained within every Guacamole
+ * extension, identifying an extension and describing its contents.
+ *
+ * @author Michael Jumper
+ */
+public class ExtensionManifest {
+
+ /**
+ * The version of Guacamole for which this extension was built.
+ * Compatibility rules built into the web application will guard against
+ * incompatible extensions being loaded.
+ */
+ private String guacamoleVersion;
+
+ /**
+ * The name of the extension associated with this manifest. The extension
+ * name is human-readable, and used for display purposes only.
+ */
+ private String name;
+
+ /**
+ * The namespace of the extension associated with this manifest. The
+ * extension namespace is required for internal use, and is used wherever
+ * extension-specific files or resources need to be isolated from those of
+ * other extensions.
+ */
+ private String namespace;
+
+ /**
+ * The paths of all JavaScript resources within the .jar of the extension
+ * associated with this manifest.
+ */
+ private Collection<String> javaScriptPaths;
+
+ /**
+ * The paths of all CSS resources within the .jar of the extension
+ * associated with this manifest.
+ */
+ private Collection<String> cssPaths;
+
+ /**
+ * The paths of all HTML patch resources within the .jar of the extension
+ * associated with this manifest.
+ */
+ private Collection<String> htmlPaths;
+
+ /**
+ * The paths of all translation JSON files within this extension, if any.
+ */
+ private Collection<String> translationPaths;
+
+ /**
+ * The mimetypes of all resources within this extension which are not
+ * already declared as JavaScript, CSS, or translation resources, if any.
+ * The key of each entry is the resource path, while the value is the
+ * corresponding mimetype.
+ */
+ private Map<String, String> resourceTypes;
+
+ /**
+ * The names of all authentication provider classes within this extension,
+ * if any.
+ */
+ private Collection<String> authProviders;
+
+ /**
+ * The path to the small favicon. If provided, this will replace the default
+ * Guacamole icon.
+ */
+ private String smallIcon;
+
+ /**
+ * The path to the large favicon. If provided, this will replace the default
+ * Guacamole icon.
+ */
+ private String largeIcon;
+
+ /**
+ * Returns the version of the Guacamole web application for which the
+ * extension was built, such as "0.9.7".
+ *
+ * @return
+ * The version of the Guacamole web application for which the extension
+ * was built.
+ */
+ public String getGuacamoleVersion() {
+ return guacamoleVersion;
+ }
+
+ /**
+ * Sets the version of the Guacamole web application for which the
+ * extension was built, such as "0.9.7".
+ *
+ * @param guacamoleVersion
+ * The version of the Guacamole web application for which the extension
+ * was built.
+ */
+ public void setGuacamoleVersion(String guacamoleVersion) {
+ this.guacamoleVersion = guacamoleVersion;
+ }
+
+ /**
+ * Returns the name of the extension associated with this manifest. The
+ * name is human-readable, for display purposes only, and is defined within
+ * the manifest by the "name" property.
+ *
+ * @return
+ * The name of the extension associated with this manifest.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of the extension associated with this manifest. The name
+ * is human-readable, for display purposes only, and is defined within the
+ * manifest by the "name" property.
+ *
+ * @param name
+ * The name of the extension associated with this manifest.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the namespace of the extension associated with this manifest.
+ * The namespace is required for internal use, and is used wherever
+ * extension-specific files or resources need to be isolated from those of
+ * other extensions. It is defined within the manifest by the "namespace"
+ * property.
+ *
+ * @return
+ * The namespace of the extension associated with this manifest.
+ */
+ public String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Sets the namespace of the extension associated with this manifest. The
+ * namespace is required for internal use, and is used wherever extension-
+ * specific files or resources need to be isolated from those of other
+ * extensions. It is defined within the manifest by the "namespace"
+ * property.
+ *
+ * @param namespace
+ * The namespace of the extension associated with this manifest.
+ */
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ /**
+ * Returns the paths to all JavaScript resources within the extension.
+ * These paths are defined within the manifest by the "js" property as an
+ * array of strings, where each string is a path relative to the root of
+ * the extension .jar.
+ *
+ * @return
+ * A collection of paths to all JavaScript resources within the
+ * extension.
+ */
+ @JsonProperty("js")
+ public Collection<String> getJavaScriptPaths() {
+ return javaScriptPaths;
+ }
+
+ /**
+ * Sets the paths to all JavaScript resources within the extension. These
+ * paths are defined within the manifest by the "js" property as an array
+ * of strings, where each string is a path relative to the root of the
+ * extension .jar.
+ *
+ * @param javaScriptPaths
+ * A collection of paths to all JavaScript resources within the
+ * extension.
+ */
+ @JsonProperty("js")
+ public void setJavaScriptPaths(Collection<String> javaScriptPaths) {
+ this.javaScriptPaths = javaScriptPaths;
+ }
+
+ /**
+ * Returns the paths to all CSS resources within the extension. These paths
+ * are defined within the manifest by the "js" property as an array of
+ * strings, where each string is a path relative to the root of the
+ * extension .jar.
+ *
+ * @return
+ * A collection of paths to all CSS resources within the extension.
+ */
+ @JsonProperty("css")
+ public Collection<String> getCSSPaths() {
+ return cssPaths;
+ }
+
+ /**
+ * Sets the paths to all CSS resources within the extension. These paths
+ * are defined within the manifest by the "js" property as an array of
+ * strings, where each string is a path relative to the root of the
+ * extension .jar.
+ *
+ * @param cssPaths
+ * A collection of paths to all CSS resources within the extension.
+ */
+ @JsonProperty("css")
+ public void setCSSPaths(Collection<String> cssPaths) {
+ this.cssPaths = cssPaths;
+ }
+
+ /**
+ * Returns the paths to all HTML patch resources within the extension. These
+ * paths are defined within the manifest by the "html" property as an array
+ * of strings, where each string is a path relative to the root of the
+ * extension .jar.
+ *
+ * @return
+ * A collection of paths to all HTML patch resources within the
+ * extension.
+ */
+ @JsonProperty("html")
+ public Collection<String> getHTMLPaths() {
+ return htmlPaths;
+ }
+
+ /**
+ * Sets the paths to all HTML patch resources within the extension. These
+ * paths are defined within the manifest by the "html" property as an array
+ * of strings, where each string is a path relative to the root of the
+ * extension .jar.
+ *
+ * @param htmlPatchPaths
+ * A collection of paths to all HTML patch resources within the
+ * extension.
+ */
+ @JsonProperty("html")
+ public void setHTMLPaths(Collection<String> htmlPatchPaths) {
+ this.htmlPaths = htmlPatchPaths;
+ }
+
+ /**
+ * Returns the paths to all translation resources within the extension.
+ * These paths are defined within the manifest by the "translations"
+ * property as an array of strings, where each string is a path relative to
+ * the root of the extension .jar.
+ *
+ * @return
+ * A collection of paths to all translation resources within the
+ * extension.
+ */
+ @JsonProperty("translations")
+ public Collection<String> getTranslationPaths() {
+ return translationPaths;
+ }
+
+ /**
+ * Sets the paths to all translation resources within the extension. These
+ * paths are defined within the manifest by the "translations" property as
+ * an array of strings, where each string is a path relative to the root of
+ * the extension .jar.
+ *
+ * @param translationPaths
+ * A collection of paths to all translation resources within the
+ * extension.
+ */
+ @JsonProperty("translations")
+ public void setTranslationPaths(Collection<String> translationPaths) {
+ this.translationPaths = translationPaths;
+ }
+
+ /**
+ * Returns a map of all resources to their corresponding mimetypes, for all
+ * resources not already declared as JavaScript, CSS, or translation
+ * resources. These paths and corresponding types are defined within the
+ * manifest by the "resources" property as an object, where each property
+ * name is a path relative to the root of the extension .jar, and each
+ * value is a mimetype.
+ *
+ * @return
+ * A map of all resources within the extension to their corresponding
+ * mimetypes.
+ */
+ @JsonProperty("resources")
+ public Map<String, String> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ /**
+ * Sets the map of all resources to their corresponding mimetypes, for all
+ * resources not already declared as JavaScript, CSS, or translation
+ * resources. These paths and corresponding types are defined within the
+ * manifest by the "resources" property as an object, where each property
+ * name is a path relative to the root of the extension .jar, and each
+ * value is a mimetype.
+ *
+ * @param resourceTypes
+ * A map of all resources within the extension to their corresponding
+ * mimetypes.
+ */
+ @JsonProperty("resources")
+ public void setResourceTypes(Map<String, String> resourceTypes) {
+ this.resourceTypes = resourceTypes;
+ }
+
+ /**
+ * Returns the classnames of all authentication provider classes within the
+ * extension. These classnames are defined within the manifest by the
+ * "authProviders" property as an array of strings, where each string is an
+ * authentication provider classname.
+ *
+ * @return
+ * A collection of classnames of all authentication providers within
+ * the extension.
+ */
+ public Collection<String> getAuthProviders() {
+ return authProviders;
+ }
+
+ /**
+ * Sets the classnames of all authentication provider classes within the
+ * extension. These classnames are defined within the manifest by the
+ * "authProviders" property as an array of strings, where each string is an
+ * authentication provider classname.
+ *
+ * @param authProviders
+ * A collection of classnames of all authentication providers within
+ * the extension.
+ */
+ public void setAuthProviders(Collection<String> authProviders) {
+ this.authProviders = authProviders;
+ }
+
+ /**
+ * Returns the path to the small favicon, relative to the root of the
+ * extension.
+ *
+ * @return
+ * The path to the small favicon.
+ */
+ public String getSmallIcon() {
+ return smallIcon;
+ }
+
+ /**
+ * Sets the path to the small favicon. This will replace the default
+ * Guacamole icon.
+ *
+ * @param smallIcon
+ * The path to the small favicon.
+ */
+ public void setSmallIcon(String smallIcon) {
+ this.smallIcon = smallIcon;
+ }
+
+ /**
+ * Returns the path to the large favicon, relative to the root of the
+ * extension.
+ *
+ * @return
+ * The path to the large favicon.
+ */
+ public String getLargeIcon() {
+ return largeIcon;
+ }
+
+ /**
+ * Sets the path to the large favicon. This will replace the default
+ * Guacamole icon.
+ *
+ * @param largeIcon
+ * The path to the large favicon.
+ */
+ public void setLargeIcon(String largeIcon) {
+ this.largeIcon = largeIcon;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/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
new file mode 100644
index 0000000..cb3c0ef
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * 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.
+ */
+
+package org.apache.guacamole.extension;
+
+import com.google.inject.Provides;
+import com.google.inject.servlet.ServletModule;
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.guacamole.auth.basic.BasicFileAuthenticationProvider;
+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.properties.BasicGuacamoleProperties;
+import org.apache.guacamole.resource.Resource;
+import org.apache.guacamole.resource.ResourceServlet;
+import org.apache.guacamole.resource.SequenceResource;
+import org.apache.guacamole.resource.WebApplicationResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Guice Module which loads all extensions within the
+ * GUACAMOLE_HOME/extensions directory, if any.
+ *
+ * @author Michael Jumper
+ */
+public class ExtensionModule extends ServletModule {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(ExtensionModule.class);
+
+ /**
+ * The version strings of all Guacamole versions whose extensions are
+ * compatible with this release.
+ */
+ private static final List<String> ALLOWED_GUACAMOLE_VERSIONS =
+ Collections.unmodifiableList(Arrays.asList(
+ "*",
+ "0.9.9"
+ ));
+
+ /**
+ * The name of the directory within GUACAMOLE_HOME containing any .jars
+ * which should be included in the classpath of all extensions.
+ */
+ private static final String LIB_DIRECTORY = "lib";
+
+ /**
+ * The name of the directory within GUACAMOLE_HOME containing all
+ * extensions.
+ */
+ private static final String EXTENSIONS_DIRECTORY = "extensions";
+
+ /**
+ * The string that the filenames of all extensions must end with to be
+ * recognized as extensions.
+ */
+ private static final String EXTENSION_SUFFIX = ".jar";
+
+ /**
+ * The Guacamole server environment.
+ */
+ private final Environment environment;
+
+ /**
+ * All currently-bound authentication providers, if any.
+ */
+ private final List<AuthenticationProvider> boundAuthenticationProviders =
+ new ArrayList<AuthenticationProvider>();
+
+ /**
+ * Service for adding and retrieving language resources.
+ */
+ private final LanguageResourceService languageResourceService;
+
+ /**
+ * Service for adding and retrieving HTML patch resources.
+ */
+ private final PatchResourceService patchResourceService;
+
+ /**
+ * Returns the classloader that should be used as the parent classloader
+ * for all extensions. If the GUACAMOLE_HOME/lib directory exists, this
+ * will be a classloader that loads classes from within the .jar files in
+ * that directory. Lacking the GUACAMOLE_HOME/lib directory, this will
+ * simply be the classloader associated with the ExtensionModule class.
+ *
+ * @return
+ * The classloader that should be used as the parent classloader for
+ * all extensions.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the classloader.
+ */
+ private ClassLoader getParentClassLoader() throws GuacamoleException {
+
+ // Retrieve lib directory
+ File libDir = new File(environment.getGuacamoleHome(), LIB_DIRECTORY);
+
+ // If lib directory does not exist, use default class loader
+ if (!libDir.isDirectory())
+ return ExtensionModule.class.getClassLoader();
+
+ // Return classloader which loads classes from all .jars within the lib directory
+ return DirectoryClassLoader.getInstance(libDir);
+
+ }
+
+ /**
+ * Creates a module which loads all extensions within the
+ * GUACAMOLE_HOME/extensions directory.
+ *
+ * @param environment
+ * The environment to use when configuring authentication.
+ */
+ public ExtensionModule(Environment environment) {
+ this.environment = environment;
+ this.languageResourceService = new LanguageResourceService(environment);
+ this.patchResourceService = new PatchResourceService();
+ }
+
+ /**
+ * Reads the value of the now-deprecated "auth-provider" property from
+ * guacamole.properties, returning the corresponding AuthenticationProvider
+ * class. If no authentication provider could be read, or the property is
+ * not present, null is returned.
+ *
+ * As this property is deprecated, this function will also log warning
+ * messages if the property is actually specified.
+ *
+ * @return
+ * The value of the deprecated "auth-provider" property, or null if the
+ * property is not present.
+ */
+ @SuppressWarnings("deprecation") // We must continue to use this property until it is truly no longer supported
+ private Class<AuthenticationProvider> getAuthProviderProperty() {
+
+ // Get and bind auth provider instance, if defined via property
+ try {
+
+ // Use "auth-provider" property if present, but warn about deprecation
+ Class<AuthenticationProvider> authenticationProvider = environment.getProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
+ if (authenticationProvider != null)
+ logger.warn("The \"auth-provider\" and \"lib-directory\" properties are now deprecated. Please use the \"extensions\" and \"lib\" directories within GUACAMOLE_HOME instead.");
+
+ return authenticationProvider;
+
+ }
+ catch (GuacamoleException e) {
+ logger.warn("Value of deprecated \"auth-provider\" property within guacamole.properties is not valid: {}", e.getMessage());
+ logger.debug("Error reading authentication provider from guacamole.properties.", e);
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Binds the given AuthenticationProvider class such that any service
+ * requiring access to the AuthenticationProvider can obtain it via
+ * injection, along with any other bound AuthenticationProviders.
+ *
+ * @param authenticationProvider
+ * The AuthenticationProvider class to bind.
+ */
+ private void bindAuthenticationProvider(Class<? extends AuthenticationProvider> authenticationProvider) {
+
+ // Bind authentication provider
+ logger.debug("[{}] Binding AuthenticationProvider \"{}\".",
+ boundAuthenticationProviders.size(), authenticationProvider.getName());
+ boundAuthenticationProviders.add(new AuthenticationProviderFacade(authenticationProvider));
+
+ }
+
+ /**
+ * Binds each of the the given AuthenticationProvider classes such that any
+ * service requiring access to the AuthenticationProvider can obtain it via
+ * injection.
+ *
+ * @param authProviders
+ * The AuthenticationProvider classes to bind.
+ */
+ private void bindAuthenticationProviders(Collection<Class<AuthenticationProvider>> authProviders) {
+
+ // Bind each authentication provider within extension
+ for (Class<AuthenticationProvider> authenticationProvider : authProviders)
+ bindAuthenticationProvider(authenticationProvider);
+
+ }
+
+ /**
+ * Returns a list of all currently-bound AuthenticationProvider instances.
+ *
+ * @return
+ * A List of all currently-bound AuthenticationProvider. The List is
+ * not modifiable.
+ */
+ @Provides
+ public List<AuthenticationProvider> getAuthenticationProviders() {
+ return Collections.unmodifiableList(boundAuthenticationProviders);
+ }
+
+ /**
+ * 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.
+ *
+ * @param resources
+ * A map of all language resources to serve, where the key of each
+ * entry in the language key from which the name of the JSON file will
+ * be derived.
+ */
+ private void serveLanguageResources(Map<String, Resource> resources) {
+
+ // Add all resources to language resource service
+ for (Map.Entry<String, Resource> translationResource : resources.entrySet()) {
+
+ // Get path and resource from path/resource pair
+ String path = translationResource.getKey();
+ Resource resource = translationResource.getValue();
+
+ // Derive key from path
+ String languageKey = languageResourceService.getLanguageKey(path);
+ if (languageKey == null) {
+ logger.warn("Invalid language file name: \"{}\"", path);
+ continue;
+ }
+
+ // Add language resource
+ languageResourceService.addLanguageResource(languageKey, resource);
+
+ }
+
+ }
+
+ /**
+ * Serves each of the given resources under the given prefix. The path of
+ * each resource relative to the prefix is the key of its entry within the
+ * map.
+ *
+ * @param prefix
+ * The prefix under which each resource should be served.
+ *
+ * @param resources
+ * A map of all resources to serve, where the key of each entry in the
+ * map is the desired path of that resource relative to the prefix.
+ */
+ private void serveStaticResources(String prefix, Map<String, Resource> resources) {
+
+ // Add all resources under given prefix
+ for (Map.Entry<String, Resource> staticResource : resources.entrySet()) {
+
+ // Get path and resource from path/resource pair
+ String path = staticResource.getKey();
+ Resource resource = staticResource.getValue();
+
+ // Serve within namespace-derived path
+ serve(prefix + path).with(new ResourceServlet(resource));
+
+ }
+
+ }
+
+ /**
+ * Returns whether the given version of Guacamole is compatible with this
+ * version of Guacamole as far as extensions are concerned.
+ *
+ * @param guacamoleVersion
+ * The version of Guacamole the extension was built for.
+ *
+ * @return
+ * true if the given version of Guacamole is compatible with this
+ * version of Guacamole, false otherwise.
+ */
+ private boolean isCompatible(String guacamoleVersion) {
+ return ALLOWED_GUACAMOLE_VERSIONS.contains(guacamoleVersion);
+ }
+
+ /**
+ * Loads all extensions within the GUACAMOLE_HOME/extensions directory, if
+ * any, adding their static resource to the given resoure collections.
+ *
+ * @param javaScriptResources
+ * A modifiable collection of static JavaScript resources which may
+ * receive new JavaScript resources from extensions.
+ *
+ * @param cssResources
+ * A modifiable collection of static CSS resources which may receive
+ * new CSS resources from extensions.
+ */
+ private void loadExtensions(Collection<Resource> javaScriptResources,
+ Collection<Resource> cssResources) {
+
+ // Retrieve and validate extensions directory
+ File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY);
+ if (!extensionsDir.isDirectory())
+ return;
+
+ // Retrieve list of all extension files within extensions directory
+ File[] extensionFiles = extensionsDir.listFiles(new FileFilter() {
+
+ @Override
+ public boolean accept(File file) {
+ return file.isFile() && file.getName().endsWith(EXTENSION_SUFFIX);
+ }
+
+ });
+
+ // Verify contents are accessible
+ if (extensionFiles == null) {
+ logger.warn("Although GUACAMOLE_HOME/" + EXTENSIONS_DIRECTORY + " exists, its contents cannot be read.");
+ return;
+ }
+
+ // Sort files lexicographically
+ Arrays.sort(extensionFiles);
+
+ // Load each extension within the extension directory
+ for (File extensionFile : extensionFiles) {
+
+ logger.debug("Loading extension: \"{}\"", extensionFile.getName());
+
+ try {
+
+ // Load extension from file
+ Extension extension = new Extension(getParentClassLoader(), extensionFile);
+
+ // Validate Guacamole version of extension
+ if (!isCompatible(extension.getGuacamoleVersion())) {
+ logger.debug("Declared Guacamole version \"{}\" of extension \"{}\" is not compatible with this version of Guacamole.",
+ extension.getGuacamoleVersion(), extensionFile.getName());
+ throw new GuacamoleServerException("Extension \"" + extension.getName() + "\" is not "
+ + "compatible with this version of Guacamole.");
+ }
+
+ // Add any JavaScript / CSS resources
+ javaScriptResources.addAll(extension.getJavaScriptResources().values());
+ cssResources.addAll(extension.getCSSResources().values());
+
+ // Attempt to load all authentication providers
+ bindAuthenticationProviders(extension.getAuthenticationProviderClasses());
+
+ // Add any translation resources
+ serveLanguageResources(extension.getTranslationResources());
+
+ // Add all HTML patch resources
+ patchResourceService.addPatchResources(extension.getHTMLResources().values());
+
+ // Add all static resources under namespace-derived prefix
+ String staticResourcePrefix = "/app/ext/" + extension.getNamespace() + "/";
+ serveStaticResources(staticResourcePrefix, extension.getStaticResources());
+
+ // Serve up the small favicon if provided
+ if(extension.getSmallIcon() != null)
+ serve("/images/logo-64.png").with(new ResourceServlet(extension.getSmallIcon()));
+
+ // Serve up the large favicon if provided
+ if(extension.getLargeIcon()!= null)
+ serve("/images/logo-144.png").with(new ResourceServlet(extension.getLargeIcon()));
+
+ // Log successful loading of extension by name
+ logger.info("Extension \"{}\" loaded.", extension.getName());
+
+ }
+ catch (GuacamoleException e) {
+ logger.error("Extension \"{}\" could not be loaded: {}", extensionFile.getName(), e.getMessage());
+ logger.debug("Unable to load extension.", e);
+ }
+
+ }
+
+ }
+
+ @Override
+ protected void configureServlets() {
+
+ // Bind resource services
+ bind(LanguageResourceService.class).toInstance(languageResourceService);
+ bind(PatchResourceService.class).toInstance(patchResourceService);
+
+ // Load initial language resources from servlet context
+ languageResourceService.addLanguageResources(getServletContext());
+
+ // Load authentication provider from guacamole.properties for sake of backwards compatibility
+ Class<AuthenticationProvider> authProviderProperty = getAuthProviderProperty();
+ if (authProviderProperty != null)
+ bindAuthenticationProvider(authProviderProperty);
+
+ // Init JavaScript resources with base guacamole.min.js
+ Collection<Resource> javaScriptResources = new ArrayList<Resource>();
+ javaScriptResources.add(new WebApplicationResource(getServletContext(), "/guacamole.min.js"));
+
+ // Init CSS resources with base guacamole.min.css
+ Collection<Resource> cssResources = new ArrayList<Resource>();
+ cssResources.add(new WebApplicationResource(getServletContext(), "/guacamole.min.css"));
+
+ // Load all extensions
+ loadExtensions(javaScriptResources, cssResources);
+
+ // Always bind basic auth last
+ bindAuthenticationProvider(BasicFileAuthenticationProvider.class);
+
+ // Dynamically generate app.js and app.css from extensions
+ serve("/app.js").with(new ResourceServlet(new SequenceResource(javaScriptResources)));
+ serve("/app.css").with(new ResourceServlet(new SequenceResource(cssResources)));
+
+ // Dynamically serve all language resources
+ for (Map.Entry<String, Resource> entry : languageResourceService.getLanguageResources().entrySet()) {
+
+ // Get language key/resource pair
+ String languageKey = entry.getKey();
+ Resource resource = entry.getValue();
+
+ // Serve resource within /translations
+ serve("/translations/" + languageKey + ".json").with(new ResourceServlet(resource));
+
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/extension/LanguageResourceService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/LanguageResourceService.java b/guacamole/src/main/java/org/apache/guacamole/extension/LanguageResourceService.java
new file mode 100644
index 0000000..c043887
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/LanguageResourceService.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * 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.
+ */
+
+package org.apache.guacamole.extension;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.servlet.ServletContext;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.node.JsonNodeFactory;
+import org.codehaus.jackson.node.ObjectNode;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.properties.BasicGuacamoleProperties;
+import org.apache.guacamole.resource.ByteArrayResource;
+import org.apache.guacamole.resource.Resource;
+import org.apache.guacamole.resource.WebApplicationResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Service which provides access to all built-in languages as resources, and
+ * allows other resources to be added or overlaid against existing resources.
+ *
+ * @author Michael Jumper
+ */
+public class LanguageResourceService {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(LanguageResourceService.class);
+
+ /**
+ * The path to the translation folder within the webapp.
+ */
+ private static final String TRANSLATION_PATH = "/translations";
+
+ /**
+ * The JSON property for the human readable display name.
+ */
+ private static final String LANGUAGE_DISPLAY_NAME_KEY = "NAME";
+
+ /**
+ * The Jackson parser for parsing the language JSON files.
+ */
+ private static final ObjectMapper mapper = new ObjectMapper();
+
+ /**
+ * The regular expression to use for parsing the language key from the
+ * filename.
+ */
+ private static final Pattern LANGUAGE_KEY_PATTERN = Pattern.compile(".*/([a-z]+(_[A-Z]+)?)\\.json");
+
+ /**
+ * The set of all language keys which are explicitly listed as allowed
+ * within guacamole.properties, or null if all defined languages should be
+ * allowed.
+ */
+ private final Set<String> allowedLanguages;
+
+ /**
+ * Map of all language resources by language key. Language keys are
+ * language and country code pairs, separated by an underscore, like
+ * "en_US". The country code and underscore SHOULD be omitted in the case
+ * that only one dialect of that language is defined, or in the case of the
+ * most universal or well-supported of all supported dialects of that
+ * language.
+ */
+ private final Map<String, Resource> resources = new HashMap<String, Resource>();
+
+ /**
+ * Creates a new service for tracking and parsing available translations
+ * which reads its configuration from the given environment.
+ *
+ * @param environment
+ * The environment from which the configuration properties of this
+ * service should be read.
+ */
+ public LanguageResourceService(Environment environment) {
+
+ Set<String> parsedAllowedLanguages;
+
+ // Parse list of available languages from properties
+ try {
+ parsedAllowedLanguages = environment.getProperty(BasicGuacamoleProperties.ALLOWED_LANGUAGES);
+ logger.debug("Available languages will be restricted to: {}", parsedAllowedLanguages);
+ }
+
+ // Warn of failure to parse
+ catch (GuacamoleException e) {
+ parsedAllowedLanguages = null;
+ logger.error("Unable to parse list of allowed languages: {}", e.getMessage());
+ logger.debug("Error parsing list of allowed languages.", e);
+ }
+
+ this.allowedLanguages = parsedAllowedLanguages;
+
+ }
+
+ /**
+ * Derives a language key from the filename within the given path, if
+ * possible. If the filename is not a valid language key, null is returned.
+ *
+ * @param path
+ * The path containing the filename to derive the language key from.
+ *
+ * @return
+ * The derived language key, or null if the filename is not a valid
+ * language key.
+ */
+ public String getLanguageKey(String path) {
+
+ // Parse language key from filename
+ Matcher languageKeyMatcher = LANGUAGE_KEY_PATTERN.matcher(path);
+ if (!languageKeyMatcher.matches())
+ return null;
+
+ // Return parsed key
+ return languageKeyMatcher.group(1);
+
+ }
+
+ /**
+ * Merges the given JSON objects. Any leaf node in overlay will overwrite
+ * the corresponding path in original.
+ *
+ * @param original
+ * The original JSON object to which changes should be applied.
+ *
+ * @param overlay
+ * The JSON object containing changes that should be applied.
+ *
+ * @return
+ * The newly constructed JSON object that is the result of merging
+ * original and overlay.
+ */
+ private JsonNode mergeTranslations(JsonNode original, JsonNode overlay) {
+
+ // If we are at a leaf node, the result of merging is simply the overlay
+ if (!overlay.isObject() || original == null)
+ return overlay;
+
+ // Create mutable copy of original
+ ObjectNode newNode = JsonNodeFactory.instance.objectNode();
+ Iterator<String> fieldNames = original.getFieldNames();
+ while (fieldNames.hasNext()) {
+ String fieldName = fieldNames.next();
+ newNode.put(fieldName, original.get(fieldName));
+ }
+
+ // Merge each field
+ fieldNames = overlay.getFieldNames();
+ while (fieldNames.hasNext()) {
+ String fieldName = fieldNames.next();
+ newNode.put(fieldName, mergeTranslations(original.get(fieldName), overlay.get(fieldName)));
+ }
+
+ return newNode;
+
+ }
+
+ /**
+ * Parses the given language resource, returning the resulting JsonNode.
+ * If the resource cannot be read because it does not exist, null is
+ * returned.
+ *
+ * @param resource
+ * The language resource to parse. Language resources must have the
+ * mimetype "application/json".
+ *
+ * @return
+ * A JsonNode representing the root of the parsed JSON tree, or null if
+ * the given resource does not exist.
+ *
+ * @throws IOException
+ * If an error occurs while parsing the resource as JSON.
+ */
+ private JsonNode parseLanguageResource(Resource resource) throws IOException {
+
+ // Get resource stream
+ InputStream stream = resource.asStream();
+ if (stream == null)
+ return null;
+
+ // Parse JSON tree
+ try {
+ JsonNode tree = mapper.readTree(stream);
+ return tree;
+ }
+
+ // Ensure stream is always closed
+ finally {
+ stream.close();
+ }
+
+ }
+
+ /**
+ * Returns whether a language having the given key should be allowed to be
+ * loaded. If language availability restrictions are imposed through
+ * guacamole.properties, this may return false in some cases. By default,
+ * this function will always return true. Note that just because a language
+ * key is allowed to be loaded does not imply that the language key is
+ * valid.
+ *
+ * @param languageKey
+ * The language key of the language to test.
+ *
+ * @return
+ * true if the given language key should be allowed to be loaded, false
+ * otherwise.
+ */
+ private boolean isLanguageAllowed(String languageKey) {
+
+ // If no list is provided, all languages are implicitly available
+ if (allowedLanguages == null)
+ return true;
+
+ return allowedLanguages.contains(languageKey);
+
+ }
+
+ /**
+ * Adds or overlays the given language resource, which need not exist in
+ * the ServletContext. If a language resource is already defined for the
+ * given language key, the strings from the given resource will be overlaid
+ * on top of the existing strings, augmenting or overriding the available
+ * strings for that language.
+ *
+ * @param key
+ * The language key of the resource being added. Language keys are
+ * pairs consisting of a language code followed by an underscore and
+ * country code, such as "en_US".
+ *
+ * @param resource
+ * The language resource to add. This resource must have the mimetype
+ * "application/json".
+ */
+ public void addLanguageResource(String key, Resource resource) {
+
+ // Skip loading of language if not allowed
+ if (!isLanguageAllowed(key)) {
+ logger.debug("OMITTING language: \"{}\"", key);
+ return;
+ }
+
+ // Merge language resources if already defined
+ Resource existing = resources.get(key);
+ if (existing != null) {
+
+ try {
+
+ // Read the original language resource
+ JsonNode existingTree = parseLanguageResource(existing);
+ if (existingTree == null) {
+ logger.warn("Base language resource \"{}\" does not exist.", key);
+ return;
+ }
+
+ // Read new language resource
+ JsonNode resourceTree = parseLanguageResource(resource);
+ if (resourceTree == null) {
+ logger.warn("Overlay language resource \"{}\" does not exist.", key);
+ return;
+ }
+
+ // Merge the language resources
+ JsonNode mergedTree = mergeTranslations(existingTree, resourceTree);
+ resources.put(key, new ByteArrayResource("application/json", mapper.writeValueAsBytes(mergedTree)));
+
+ logger.debug("Merged strings with existing language: \"{}\"", key);
+
+ }
+ catch (IOException e) {
+ logger.error("Unable to merge language resource \"{}\": {}", key, e.getMessage());
+ logger.debug("Error merging language resource.", e);
+ }
+
+ }
+
+ // Otherwise, add new language resource
+ else {
+ resources.put(key, resource);
+ logger.debug("Added language: \"{}\"", key);
+ }
+
+ }
+
+ /**
+ * Adds or overlays all languages defined within the /translations
+ * directory of the given ServletContext. If no such language files exist,
+ * nothing is done. If a language is already defined, the strings from the
+ * will be overlaid on top of the existing strings, augmenting or
+ * overriding the available strings for that language. The language key
+ * for each language file is derived from the filename.
+ *
+ * @param context
+ * The ServletContext from which language files should be loaded.
+ */
+ public void addLanguageResources(ServletContext context) {
+
+ // Get the paths of all the translation files
+ Set<?> resourcePaths = context.getResourcePaths(TRANSLATION_PATH);
+
+ // If no translation files found, nothing to add
+ if (resourcePaths == null)
+ return;
+
+ // Iterate through all the found language files and add them to the map
+ for (Object resourcePathObject : resourcePaths) {
+
+ // Each resource path is guaranteed to be a string
+ String resourcePath = (String) resourcePathObject;
+
+ // Parse language key from path
+ String languageKey = getLanguageKey(resourcePath);
+ if (languageKey == null) {
+ logger.warn("Invalid language file name: \"{}\"", resourcePath);
+ continue;
+ }
+
+ // Add/overlay new resource
+ addLanguageResource(
+ languageKey,
+ new WebApplicationResource(context, "application/json", resourcePath)
+ );
+
+ }
+
+ }
+
+ /**
+ * Returns a set of all unique language keys currently associated with
+ * language resources stored in this service. The returned set cannot be
+ * modified.
+ *
+ * @return
+ * A set of all unique language keys currently associated with this
+ * service.
+ */
+ public Set<String> getLanguageKeys() {
+ return Collections.unmodifiableSet(resources.keySet());
+ }
+
+ /**
+ * Returns a map of all languages currently associated with this service,
+ * where the key of each map entry is the language key. The returned map
+ * cannot be modified.
+ *
+ * @return
+ * A map of all languages currently associated with this service.
+ */
+ public Map<String, Resource> getLanguageResources() {
+ return Collections.unmodifiableMap(resources);
+ }
+
+ /**
+ * Returns a mapping of all language keys to their corresponding human-
+ * readable language names. If an error occurs while parsing a language
+ * resource, its key/name pair will simply be omitted. The returned map
+ * cannot be modified.
+ *
+ * @return
+ * A map of all language keys and their corresponding human-readable
+ * names.
+ */
+ public Map<String, String> getLanguageNames() {
+
+ Map<String, String> languageNames = new HashMap<String, String>();
+
+ // For each language key/resource pair
+ for (Map.Entry<String, Resource> entry : resources.entrySet()) {
+
+ // Get language key and resource
+ String languageKey = entry.getKey();
+ Resource resource = entry.getValue();
+
+ // Get stream for resource
+ InputStream resourceStream = resource.asStream();
+ if (resourceStream == null) {
+ logger.warn("Expected language resource does not exist: \"{}\".", languageKey);
+ continue;
+ }
+
+ // Get name node of language
+ try {
+ JsonNode tree = mapper.readTree(resourceStream);
+ JsonNode nameNode = tree.get(LANGUAGE_DISPLAY_NAME_KEY);
+
+ // Attempt to read language name from node
+ String languageName;
+ if (nameNode == null || (languageName = nameNode.getTextValue()) == null) {
+ logger.warn("Root-level \"" + LANGUAGE_DISPLAY_NAME_KEY + "\" string missing or invalid in language \"{}\"", languageKey);
+ languageName = languageKey;
+ }
+
+ // Add language key/name pair to map
+ languageNames.put(languageKey, languageName);
+
+ }
+
+ // Continue with next language if unable to read
+ catch (IOException e) {
+ logger.warn("Unable to read language resource \"{}\".", languageKey);
+ logger.debug("Error reading language resource.", e);
+ }
+
+ }
+
+ return Collections.unmodifiableMap(languageNames);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/extension/PatchResourceService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/PatchResourceService.java b/guacamole/src/main/java/org/apache/guacamole/extension/PatchResourceService.java
new file mode 100644
index 0000000..e0c1c0b
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/PatchResourceService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 Glyptodon LLC
+ *
+ * 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.
+ */
+
+package org.apache.guacamole.extension;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.apache.guacamole.resource.Resource;
+
+/**
+ * Service which provides access to all HTML patches as resources, and allows
+ * other patch resources to be added.
+ *
+ * @author Michael Jumper
+ */
+public class PatchResourceService {
+
+ /**
+ * A list of all HTML patch resources currently defined, in the order they
+ * should be applied.
+ */
+ private final List<Resource> resources = new ArrayList<Resource>();
+
+ /**
+ * Adds the given HTML patch resource such that it will apply to the
+ * Guacamole UI. The patch will be applied by the JavaScript side of the
+ * web application in the order that addPatchResource() is invoked.
+ *
+ * @param resource
+ * The HTML patch resource to add. This resource must have the mimetype
+ * "text/html".
+ */
+ public void addPatchResource(Resource resource) {
+ resources.add(resource);
+ }
+
+ /**
+ * Adds the given HTML patch resources such that they will apply to the
+ * Guacamole UI. The patches will be applied by the JavaScript side of the
+ * web application in the order provided.
+ *
+ * @param resources
+ * The HTML patch resources to add. Each resource must have the
+ * mimetype "text/html".
+ */
+ public void addPatchResources(Collection<Resource> resources) {
+ for (Resource resource : resources)
+ addPatchResource(resource);
+ }
+
+ /**
+ * Returns a list of all HTML patches currently associated with this
+ * service, in the order they should be applied. The returned list cannot
+ * be modified.
+ *
+ * @return
+ * A list of all HTML patches currently associated with this service.
+ */
+ public List<Resource> getPatchResources() {
+ return Collections.unmodifiableList(resources);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/extension/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/package-info.java b/guacamole/src/main/java/org/apache/guacamole/extension/package-info.java
new file mode 100644
index 0000000..02f4afc
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * 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.
+ */
+
+/**
+ * Classes which represent and facilitate the loading of extensions to the
+ * Guacamole web application.
+ */
+package org.apache.guacamole.extension;
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/log/LogModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/log/LogModule.java b/guacamole/src/main/java/org/apache/guacamole/log/LogModule.java
new file mode 100644
index 0000000..67e93c4
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/log/LogModule.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Glyptodon LLC
+ *
+ * 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.
+ */
+
+package org.apache.guacamole.log;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.util.StatusPrinter;
+import com.google.inject.AbstractModule;
+import java.io.File;
+import org.apache.guacamole.environment.Environment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Initializes the logging subsystem.
+ *
+ * @author Michael Jumper
+ */
+public class LogModule extends AbstractModule {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(LogModule.class);
+
+ /**
+ * The Guacamole server environment.
+ */
+ private final Environment environment;
+
+ /**
+ * Creates a new LogModule which uses the given environment to determine
+ * the logging configuration.
+ *
+ * @param environment
+ * The environment to use when configuring logging.
+ */
+ public LogModule(Environment environment) {
+ this.environment = environment;
+ }
+
+ @Override
+ protected void configure() {
+
+ // Only load logback configuration if GUACAMOLE_HOME exists
+ File guacamoleHome = environment.getGuacamoleHome();
+ if (!guacamoleHome.isDirectory())
+ return;
+
+ // Check for custom logback.xml
+ File logbackConfiguration = new File(guacamoleHome, "logback.xml");
+ if (!logbackConfiguration.exists())
+ return;
+
+ logger.info("Loading logback configuration from \"{}\".", logbackConfiguration);
+
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ context.reset();
+
+ try {
+
+ // Initialize logback
+ JoranConfigurator configurator = new JoranConfigurator();
+ configurator.setContext(context);
+ configurator.doConfigure(logbackConfiguration);
+
+ // Dump any errors that occur during logback init
+ StatusPrinter.printInCaseOfErrorsOrWarnings(context);
+
+ }
+ catch (JoranException e) {
+ logger.error("Initialization of logback failed: {}", e.getMessage());
+ logger.debug("Unable to load logback configuration..", e);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicFileAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicFileAuthenticationProvider.java b/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicFileAuthenticationProvider.java
deleted file mode 100644
index 7fe6bbd..0000000
--- a/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicFileAuthenticationProvider.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * 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.
- */
-
-package org.apache.guacamole.net.basic;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.environment.Environment;
-import org.apache.guacamole.environment.LocalEnvironment;
-import org.apache.guacamole.net.auth.Credentials;
-import org.apache.guacamole.net.auth.simple.SimpleAuthenticationProvider;
-import org.apache.guacamole.net.basic.auth.Authorization;
-import org.apache.guacamole.net.basic.auth.UserMapping;
-import org.apache.guacamole.xml.DocumentHandler;
-import org.apache.guacamole.net.basic.xml.usermapping.UserMappingTagHandler;
-import org.apache.guacamole.properties.FileGuacamoleProperty;
-import org.apache.guacamole.protocol.GuacamoleConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-/**
- * Authenticates users against a static list of username/password pairs.
- * Each username/password may be associated with multiple configurations.
- * This list is stored in an XML file which is reread if modified.
- *
- * @author Michael Jumper, Michal Kotas
- */
-public class BasicFileAuthenticationProvider extends SimpleAuthenticationProvider {
-
- /**
- * Logger for this class.
- */
- private final Logger logger = LoggerFactory.getLogger(BasicFileAuthenticationProvider.class);
-
- /**
- * The time the user mapping file was last modified. If the file has never
- * been read, and thus no modification time exists, this will be
- * Long.MIN_VALUE.
- */
- private long lastModified = Long.MIN_VALUE;
-
- /**
- * The parsed UserMapping read when the user mapping file was last parsed.
- */
- private UserMapping cachedUserMapping;
-
- /**
- * Guacamole server environment.
- */
- private final Environment environment;
-
- /**
- * The XML file to read the user mapping from.
- */
- public static final FileGuacamoleProperty BASIC_USER_MAPPING = new FileGuacamoleProperty() {
-
- @Override
- public String getName() { return "basic-user-mapping"; }
-
- };
-
- /**
- * The default filename to use for the user mapping, if not defined within
- * guacamole.properties.
- */
- public static final String DEFAULT_USER_MAPPING = "user-mapping.xml";
-
- /**
- * Creates a new BasicFileAuthenticationProvider that authenticates users
- * against simple, monolithic XML file.
- *
- * @throws GuacamoleException
- * If a required property is missing, or an error occurs while parsing
- * a property.
- */
- public BasicFileAuthenticationProvider() throws GuacamoleException {
- environment = new LocalEnvironment();
- }
-
- @Override
- public String getIdentifier() {
- return "default";
- }
-
- /**
- * Returns a UserMapping containing all authorization data given within
- * the XML file specified by the "basic-user-mapping" property in
- * guacamole.properties. If the XML file has been modified or has not yet
- * been read, this function may reread the file.
- *
- * @return
- * A UserMapping containing all authorization data within the user
- * mapping XML file, or null if the file cannot be found/parsed.
- */
- private UserMapping getUserMapping() {
-
- // Get user mapping file, defaulting to GUACAMOLE_HOME/user-mapping.xml
- File userMappingFile;
- try {
- userMappingFile = environment.getProperty(BASIC_USER_MAPPING);
- if (userMappingFile == null)
- userMappingFile = new File(environment.getGuacamoleHome(), DEFAULT_USER_MAPPING);
- }
-
- // Abort if property cannot be parsed
- catch (GuacamoleException e) {
- logger.warn("Unable to read user mapping filename from properties: {}", e.getMessage());
- logger.debug("Error parsing user mapping property.", e);
- return null;
- }
-
- // Abort if user mapping does not exist
- if (!userMappingFile.exists()) {
- logger.debug("User mapping file \"{}\" does not exist and will not be read.", userMappingFile);
- return null;
- }
-
- // Refresh user mapping if file has changed
- if (lastModified < userMappingFile.lastModified()) {
-
- logger.debug("Reading user mapping file: \"{}\"", userMappingFile);
-
- // Parse document
- try {
-
- // Get handler for root element
- UserMappingTagHandler userMappingHandler =
- new UserMappingTagHandler();
-
- // Set up document handler
- DocumentHandler contentHandler = new DocumentHandler(
- "user-mapping", userMappingHandler);
-
- // Set up XML parser
- XMLReader parser = XMLReaderFactory.createXMLReader();
- parser.setContentHandler(contentHandler);
-
- // Read and parse file
- InputStream input = new BufferedInputStream(new FileInputStream(userMappingFile));
- parser.parse(new InputSource(input));
- input.close();
-
- // Store mod time and user mapping
- lastModified = userMappingFile.lastModified();
- cachedUserMapping = userMappingHandler.asUserMapping();
-
- }
-
- // If the file is unreadable, return no mapping
- catch (IOException e) {
- logger.warn("Unable to read user mapping file \"{}\": {}", userMappingFile, e.getMessage());
- logger.debug("Error reading user mapping file.", e);
- return null;
- }
-
- // If the file cannot be parsed, return no mapping
- catch (SAXException e) {
- logger.warn("User mapping file \"{}\" is not valid: {}", userMappingFile, e.getMessage());
- logger.debug("Error parsing user mapping file.", e);
- return null;
- }
-
- }
-
- // Return (possibly cached) user mapping
- return cachedUserMapping;
-
- }
-
- @Override
- public Map<String, GuacamoleConfiguration>
- getAuthorizedConfigurations(Credentials credentials)
- throws GuacamoleException {
-
- // Abort authorization if no user mapping exists
- UserMapping userMapping = getUserMapping();
- if (userMapping == null)
- return null;
-
- // Validate and return info for given user and pass
- Authorization auth = userMapping.getAuthorization(credentials.getUsername());
- if (auth != null && auth.validate(credentials.getUsername(), credentials.getPassword()))
- return auth.getConfigurations();
-
- // Unauthorized
- return null;
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicGuacamoleTunnelServlet.java b/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
deleted file mode 100644
index d2bff1a..0000000
--- a/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * 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.
- */
-
-package org.apache.guacamole.net.basic;
-
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.net.GuacamoleTunnel;
-import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Connects users to a tunnel associated with the authorized connection
- * having the given ID.
- *
- * @author Michael Jumper
- */
-@Singleton
-public class BasicGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet {
-
- /**
- * Service for handling tunnel requests.
- */
- @Inject
- private TunnelRequestService tunnelRequestService;
-
- /**
- * Logger for this class.
- */
- private static final Logger logger = LoggerFactory.getLogger(BasicGuacamoleTunnelServlet.class);
-
- @Override
- protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
-
- // Attempt to create HTTP tunnel
- GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new HTTPTunnelRequest(request));
-
- // If successful, warn of lack of WebSocket
- logger.info("Using HTTP tunnel (not WebSocket). Performance may be sub-optimal.");
-
- return tunnel;
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicServletContextListener.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicServletContextListener.java b/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicServletContextListener.java
deleted file mode 100644
index 3bf6b9e..0000000
--- a/guacamole/src/main/java/org/apache/guacamole/net/basic/BasicServletContextListener.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * 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.
- */
-
-package org.apache.guacamole.net.basic;
-
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Stage;
-import com.google.inject.servlet.GuiceServletContextListener;
-import javax.servlet.ServletContextEvent;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.environment.Environment;
-import org.apache.guacamole.environment.LocalEnvironment;
-import org.apache.guacamole.net.basic.extension.ExtensionModule;
-import org.apache.guacamole.net.basic.log.LogModule;
-import org.apache.guacamole.net.basic.rest.RESTServiceModule;
-import org.apache.guacamole.net.basic.rest.auth.BasicTokenSessionMap;
-import org.apache.guacamole.net.basic.rest.auth.TokenSessionMap;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A ServletContextListener to listen for initialization of the servlet context
- * in order to set up dependency injection.
- *
- * @author James Muehlner
- */
-public class BasicServletContextListener extends GuiceServletContextListener {
-
- /**
- * Logger for this class.
- */
- private final Logger logger = LoggerFactory.getLogger(BasicServletContextListener.class);
-
- /**
- * The Guacamole server environment.
- */
- private Environment environment;
-
- /**
- * Singleton instance of a TokenSessionMap.
- */
- private TokenSessionMap sessionMap;
-
- @Override
- public void contextInitialized(ServletContextEvent servletContextEvent) {
-
- try {
- environment = new LocalEnvironment();
- sessionMap = new BasicTokenSessionMap(environment);
- }
- catch (GuacamoleException e) {
- logger.error("Unable to read guacamole.properties: {}", e.getMessage());
- logger.debug("Error reading guacamole.properties.", e);
- throw new RuntimeException(e);
- }
-
- super.contextInitialized(servletContextEvent);
-
- }
-
- @Override
- protected Injector getInjector() {
- return Guice.createInjector(Stage.PRODUCTION,
- new EnvironmentModule(environment),
- new LogModule(environment),
- new ExtensionModule(environment),
- new RESTServiceModule(sessionMap),
- new TunnelModule()
- );
- }
-
- @Override
- public void contextDestroyed(ServletContextEvent servletContextEvent) {
-
- super.contextDestroyed(servletContextEvent);
-
- // Shutdown TokenSessionMap
- if (sessionMap != null)
- sessionMap.shutdown();
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/net/basic/ClipboardState.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/net/basic/ClipboardState.java b/guacamole/src/main/java/org/apache/guacamole/net/basic/ClipboardState.java
deleted file mode 100644
index dae5d74..0000000
--- a/guacamole/src/main/java/org/apache/guacamole/net/basic/ClipboardState.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2014 Glyptodon LLC.
- *
- * 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.
- */
-
-package org.apache.guacamole.net.basic;
-
-/**
- * Provides central storage for a cross-connection clipboard state. This
- * clipboard state is shared only for a single HTTP session. Multiple HTTP
- * sessions will all have their own state.
- *
- * @author Michael Jumper
- */
-public class ClipboardState {
-
- /**
- * The maximum number of bytes to track.
- */
- private static final int MAXIMUM_LENGTH = 262144;
-
- /**
- * The mimetype of the current contents.
- */
- private String mimetype = "text/plain";
-
- /**
- * The mimetype of the pending contents.
- */
- private String pending_mimetype = "text/plain";
-
- /**
- * The current contents.
- */
- private byte[] contents = new byte[0];
-
- /**
- * The pending clipboard contents.
- */
- private final byte[] pending = new byte[MAXIMUM_LENGTH];
-
- /**
- * The length of the pending data, in bytes.
- */
- private int pending_length = 0;
-
- /**
- * The timestamp of the last contents update.
- */
- private long last_update = 0;
-
- /**
- * Returns the current clipboard contents.
- * @return The current clipboard contents
- */
- public synchronized byte[] getContents() {
- return contents;
- }
-
- /**
- * Returns the mimetype of the current clipboard contents.
- * @return The mimetype of the current clipboard contents.
- */
- public synchronized String getMimetype() {
- return mimetype;
- }
-
- /**
- * Begins a new update of the clipboard contents. The actual contents will
- * not be saved until commit() is called.
- *
- * @param mimetype The mimetype of the contents being added.
- */
- public synchronized void begin(String mimetype) {
- pending_length = 0;
- this.pending_mimetype = mimetype;
- }
-
- /**
- * Appends the given data to the clipboard contents.
- *
- * @param data The raw data to append.
- */
- public synchronized void append(byte[] data) {
-
- // Calculate size of copy
- int length = data.length;
- int remaining = pending.length - pending_length;
- if (remaining < length)
- length = remaining;
-
- // Append data
- System.arraycopy(data, 0, pending, pending_length, length);
- pending_length += length;
-
- }
-
- /**
- * Commits the pending contents to the clipboard, notifying any threads
- * waiting for clipboard updates.
- */
- public synchronized void commit() {
-
- // Commit contents
- mimetype = pending_mimetype;
- contents = new byte[pending_length];
- System.arraycopy(pending, 0, contents, 0, pending_length);
-
- // Notify of update
- last_update = System.currentTimeMillis();
- this.notifyAll();
-
- }
-
- /**
- * Wait up to the given timeout for new clipboard data.
- *
- * @param timeout The amount of time to wait, in milliseconds.
- * @return true if the contents were updated within the timeframe given,
- * false otherwise.
- */
- public synchronized boolean waitForContents(int timeout) {
-
- // Wait for new contents if it's been a while
- if (System.currentTimeMillis() - last_update > timeout) {
- try {
- this.wait(timeout);
- return true;
- }
- catch (InterruptedException e) { /* ignore */ }
- }
-
- return false;
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/net/basic/EnvironmentModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/net/basic/EnvironmentModule.java b/guacamole/src/main/java/org/apache/guacamole/net/basic/EnvironmentModule.java
deleted file mode 100644
index 76559d1..0000000
--- a/guacamole/src/main/java/org/apache/guacamole/net/basic/EnvironmentModule.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 Glyptodon LLC
- *
- * 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.
- */
-
-package org.apache.guacamole.net.basic;
-
-import com.google.inject.AbstractModule;
-import org.apache.guacamole.environment.Environment;
-
-/**
- * Guice module which binds the base Guacamole server environment.
- *
- * @author Michael Jumper
- */
-public class EnvironmentModule extends AbstractModule {
-
- /**
- * The Guacamole server environment.
- */
- private final Environment environment;
-
- /**
- * Creates a new EnvironmentModule which will bind the given environment
- * for future injection.
- *
- * @param environment
- * The environment to bind.
- */
- public EnvironmentModule(Environment environment) {
- this.environment = environment;
- }
-
- @Override
- protected void configure() {
-
- // Bind environment
- bind(Environment.class).toInstance(environment);
-
- }
-
-}
-