mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUACAMOLE-1364: Merge add support for overriding extension priority without renaming files.
This commit is contained in:
		| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "CAS Authentication Extension", |     "name"      : "CAS Authentication Extension", | ||||||
|     "namespace" : "guac-cas", |     "namespace" : "cas", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.cas.CASAuthenticationProvider" |         "org.apache.guacamole.auth.cas.CASAuthenticationProvider" | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.2.0", |     "guacamoleVersion" : "1.2.0", | ||||||
|  |  | ||||||
|     "name"      : "HTTP Header Authentication Extension", |     "name"      : "HTTP Header Authentication Extension", | ||||||
|     "namespace" : "guac-header", |     "namespace" : "header", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.header.HTTPHeaderAuthenticationProvider" |         "org.apache.guacamole.auth.header.HTTPHeaderAuthenticationProvider" | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "MySQL Authentication", |     "name"      : "MySQL Authentication", | ||||||
|     "namespace" : "guac-mysql", |     "namespace" : "mysql", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.mysql.MySQLAuthenticationProvider", |         "org.apache.guacamole.auth.mysql.MySQLAuthenticationProvider", | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "PostgreSQL Authentication", |     "name"      : "PostgreSQL Authentication", | ||||||
|     "namespace" : "guac-postgresql", |     "namespace" : "postgresql", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.postgresql.PostgreSQLAuthenticationProvider", |         "org.apache.guacamole.auth.postgresql.PostgreSQLAuthenticationProvider", | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "SQLServer Authentication", |     "name"      : "SQLServer Authentication", | ||||||
|     "namespace" : "guac-sqlserver", |     "namespace" : "sqlserver", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.sqlserver.SQLServerAuthenticationProvider", |         "org.apache.guacamole.auth.sqlserver.SQLServerAuthenticationProvider", | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "Encrypted JSON Authentication", |     "name"      : "Encrypted JSON Authentication", | ||||||
|     "namespace" : "guac-json", |     "namespace" : "json", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.json.JSONAuthenticationProvider" |         "org.apache.guacamole.auth.json.JSONAuthenticationProvider" | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "LDAP Authentication", |     "name"      : "LDAP Authentication", | ||||||
|     "namespace" : "guac-ldap", |     "namespace" : "ldap", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider" |         "org.apache.guacamole.auth.ldap.LDAPAuthenticationProvider" | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
|     "guacamoleVersion" : "1.3.0", |     "guacamoleVersion" : "1.3.0", | ||||||
|  |  | ||||||
|     "name"      : "OpenID Authentication Extension", |     "name"      : "OpenID Authentication Extension", | ||||||
|     "namespace" : "guac-openid", |     "namespace" : "openid", | ||||||
|  |  | ||||||
|     "authProviders" : [ |     "authProviders" : [ | ||||||
|         "org.apache.guacamole.auth.openid.OpenIDAuthenticationProvider" |         "org.apache.guacamole.auth.openid.OpenIDAuthenticationProvider" | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ import java.io.IOException; | |||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashMap; | import java.util.LinkedHashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.zip.ZipEntry; | import java.util.zip.ZipEntry; | ||||||
| import java.util.zip.ZipException; | import java.util.zip.ZipException; | ||||||
| @@ -55,6 +55,11 @@ public class Extension { | |||||||
|      */ |      */ | ||||||
|     private static final String MANIFEST_NAME = "guac-manifest.json"; |     private static final String MANIFEST_NAME = "guac-manifest.json"; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The extension .jar file. | ||||||
|  |      */ | ||||||
|  |     private final File file; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * The parsed manifest file of this extension, describing the location of |      * The parsed manifest file of this extension, describing the location of | ||||||
|      * resources within the extension. |      * resources within the extension. | ||||||
| @@ -144,7 +149,7 @@ public class Extension { | |||||||
|             return Collections.<String, Resource>emptyMap(); |             return Collections.<String, Resource>emptyMap(); | ||||||
|  |  | ||||||
|         // Add classpath resource for each path provided |         // Add classpath resource for each path provided | ||||||
|         Map<String, Resource> resources = new HashMap<String, Resource>(paths.size()); |         Map<String, Resource> resources = new LinkedHashMap<>(paths.size()); | ||||||
|         for (String path : paths) |         for (String path : paths) | ||||||
|             resources.put(path, new ClassPathResource(classLoader, mimetype, path)); |             resources.put(path, new ClassPathResource(classLoader, mimetype, path)); | ||||||
|  |  | ||||||
| @@ -173,7 +178,7 @@ public class Extension { | |||||||
|             return Collections.<String, Resource>emptyMap(); |             return Collections.<String, Resource>emptyMap(); | ||||||
|  |  | ||||||
|         // Add classpath resource for each path/mimetype pair provided |         // Add classpath resource for each path/mimetype pair provided | ||||||
|         Map<String, Resource> resources = new HashMap<String, Resource>(resourceTypes.size()); |         Map<String, Resource> resources = new LinkedHashMap<>(resourceTypes.size()); | ||||||
|         for (Map.Entry<String, String> resource : resourceTypes.entrySet()) { |         for (Map.Entry<String, String> resource : resourceTypes.entrySet()) { | ||||||
|  |  | ||||||
|             // Get path and mimetype from entry |             // Get path and mimetype from entry | ||||||
| @@ -357,6 +362,9 @@ public class Extension { | |||||||
|      */ |      */ | ||||||
|     public Extension(final ClassLoader parent, final File file) throws GuacamoleException { |     public Extension(final ClassLoader parent, final File file) throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         // Associate extension abstraction with original file | ||||||
|  |         this.file = file; | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|  |  | ||||||
|             // Open extension |             // Open extension | ||||||
| @@ -427,6 +435,16 @@ public class Extension { | |||||||
|             largeIcon = null; |             largeIcon = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the .jar file containing this Guacamole extension. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     The extension .jar file. | ||||||
|  |      */ | ||||||
|  |     public File getFile() { | ||||||
|  |         return file; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the version of the Guacamole web application for which this |      * Returns the version of the Guacamole web application for which this | ||||||
|      * extension was built. |      * extension was built. | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ import java.util.ArrayList; | |||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
|  | import java.util.Comparator; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| @@ -40,7 +41,6 @@ import org.apache.guacamole.properties.StringSetProperty; | |||||||
| import org.apache.guacamole.resource.Resource; | import org.apache.guacamole.resource.Resource; | ||||||
| import org.apache.guacamole.resource.ResourceServlet; | import org.apache.guacamole.resource.ResourceServlet; | ||||||
| import org.apache.guacamole.resource.SequenceResource; | import org.apache.guacamole.resource.SequenceResource; | ||||||
| import org.apache.guacamole.resource.WebApplicationResource; |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| @@ -105,6 +105,22 @@ public class ExtensionModule extends ServletModule { | |||||||
|  |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * A comma-separated list of the namespaces of all extensions that should | ||||||
|  |      * be loaded in a specific order. The special value "*" can be used in | ||||||
|  |      * lieu of a namespace to represent all extensions that are not listed. All | ||||||
|  |      * extensions explicitly listed will be sorted in the order given, while | ||||||
|  |      * all extensions not explicitly listed will be sorted by their filenames. | ||||||
|  |      */ | ||||||
|  |     public static final ExtensionOrderProperty EXTENSION_PRIORITY = new ExtensionOrderProperty() { | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public String getName() { | ||||||
|  |             return "extension-priority"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * The Guacamole server environment. |      * The Guacamole server environment. | ||||||
|      */ |      */ | ||||||
| @@ -394,9 +410,101 @@ public class ExtensionModule extends ServletModule { | |||||||
|  |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a comparator that sorts extensions by their desired load order, | ||||||
|  |      * as dictated by the "extension-priority" property and their filenames. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     A comparator that sorts extensions by their desired load order. | ||||||
|  |      */ | ||||||
|  |     private Comparator<Extension> getExtensionLoadOrder() { | ||||||
|  |  | ||||||
|  |         // Parse desired sort order of extensions | ||||||
|  |         try { | ||||||
|  |             return environment.getProperty(EXTENSION_PRIORITY, ExtensionOrderProperty.DEFAULT_COMPARATOR); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Sort by filename if the desired order cannot be read | ||||||
|  |         catch (GuacamoleException e) { | ||||||
|  |             logger.warn("The list of extensions specified via the \"{}\" property could not be parsed: {}", EXTENSION_PRIORITY.getName(), e.getMessage()); | ||||||
|  |             logger.debug("Unable to parse \"{}\" property.", EXTENSION_PRIORITY.getName(), e); | ||||||
|  |             return ExtensionOrderProperty.DEFAULT_COMPARATOR; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a list of all installed extensions in the order they should be | ||||||
|  |      * loaded. Extension load order is dictated by the "extension-priority" | ||||||
|  |      * property and by extension filename. Each extension within | ||||||
|  |      * GUACAMOLE_HOME/extensions is read and validated, but not fully loaded. | ||||||
|  |      * It is the responsibility of the caller to continue the load process with | ||||||
|  |      * the extensions in the returned list. | ||||||
|  |      * | ||||||
|  |      * @return | ||||||
|  |      *     A list of all installed extensions, ordered by load priority. | ||||||
|  |      */ | ||||||
|  |     private List<Extension> getExtensions() { | ||||||
|  |  | ||||||
|  |         // Retrieve and validate extensions directory | ||||||
|  |         File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY); | ||||||
|  |         if (!extensionsDir.isDirectory()) | ||||||
|  |             return Collections.emptyList(); | ||||||
|  |  | ||||||
|  |         // 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 Collections.emptyList(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Read (but do not fully load) each extension within the extension | ||||||
|  |         // directory | ||||||
|  |         List<Extension> extensions = new ArrayList<>(extensionFiles.length); | ||||||
|  |         for (File extensionFile : extensionFiles) { | ||||||
|  |  | ||||||
|  |             logger.debug("Reading 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."); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 extensions.add(extension); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             catch (GuacamoleException e) { | ||||||
|  |                 logger.error("Extension \"{}\" could not be loaded: {}", extensionFile.getName(), e.getMessage()); | ||||||
|  |                 logger.debug("Unable to load extension.", e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         extensions.sort(getExtensionLoadOrder()); | ||||||
|  |         return extensions; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Loads all extensions within the GUACAMOLE_HOME/extensions directory, if |      * Loads all extensions within the GUACAMOLE_HOME/extensions directory, if | ||||||
|      * any, adding their static resource to the given resoure collections. |      * any, adding their static resource to the given resource collections. | ||||||
|      * |      * | ||||||
|      * @param javaScriptResources |      * @param javaScriptResources | ||||||
|      *     A modifiable collection of static JavaScript resources which may |      *     A modifiable collection of static JavaScript resources which may | ||||||
| @@ -420,47 +528,26 @@ public class ExtensionModule extends ServletModule { | |||||||
|             Collection<Resource> cssResources, |             Collection<Resource> cssResources, | ||||||
|             Set<String> toleratedAuthProviders) { |             Set<String> toleratedAuthProviders) { | ||||||
|  |  | ||||||
|         // Retrieve and validate extensions directory |         // Advise of current extension load order and how the order may be | ||||||
|         File extensionsDir = new File(environment.getGuacamoleHome(), EXTENSIONS_DIRECTORY); |         // changed | ||||||
|         if (!extensionsDir.isDirectory()) |         List<Extension> extensions = getExtensions(); | ||||||
|             return; |         if (extensions.size() > 1) { | ||||||
|  |             logger.info("Multiple extensions are installed and will be " | ||||||
|  |                     + "loaded in order of decreasing priority:"); | ||||||
|  |  | ||||||
|         // Retrieve list of all extension files within extensions directory |             for (Extension extension : extensions) { | ||||||
|         File[] extensionFiles = extensionsDir.listFiles(new FileFilter() { |                 logger.info(" - [{}] \"{}\" ({})", extension.getNamespace(), | ||||||
|  |                         extension.getName(), extension.getFile()); | ||||||
|             @Override |  | ||||||
|             public boolean accept(File file) { |  | ||||||
|                 return file.isFile() && file.getName().endsWith(EXTENSION_SUFFIX); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         }); |             logger.info("To change this order, set the \"{}\" property or " | ||||||
|  |                     + "rename the extension files. The default priority of " | ||||||
|         // Verify contents are accessible |                     + "extensions is dictated by the sort order of their " | ||||||
|         if (extensionFiles == null) { |                     + "filenames.", EXTENSION_PRIORITY.getName()); | ||||||
|             logger.warn("Although GUACAMOLE_HOME/" + EXTENSIONS_DIRECTORY + " exists, its contents cannot be read."); |  | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Sort files lexicographically |         // Load all extensions | ||||||
|         Arrays.sort(extensionFiles); |         for (Extension extension : extensions) { | ||||||
|  |  | ||||||
|         // 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 |             // Add any JavaScript / CSS resources | ||||||
|             javaScriptResources.addAll(extension.getJavaScriptResources().values()); |             javaScriptResources.addAll(extension.getJavaScriptResources().values()); | ||||||
| @@ -491,13 +578,7 @@ public class ExtensionModule extends ServletModule { | |||||||
|                 serve("/images/logo-144.png").with(new ResourceServlet(extension.getLargeIcon())); |                 serve("/images/logo-144.png").with(new ResourceServlet(extension.getLargeIcon())); | ||||||
|  |  | ||||||
|             // Log successful loading of extension by name |             // Log successful loading of extension by name | ||||||
|                 logger.info("Extension \"{}\" loaded.", extension.getName()); |             logger.info("Extension \"{}\" ({}) loaded.", extension.getName(), extension.getNamespace()); | ||||||
|  |  | ||||||
|             } |  | ||||||
|             catch (GuacamoleException e) { |  | ||||||
|                 logger.error("Extension \"{}\" could not be loaded: {}", extensionFile.getName(), e.getMessage()); |  | ||||||
|                 logger.debug("Unable to load extension.", e); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,163 @@ | |||||||
|  | /* | ||||||
|  |  * Licensed to the Apache Software Foundation (ASF) under one | ||||||
|  |  * or more contributor license agreements.  See the NOTICE file | ||||||
|  |  * distributed with this work for additional information | ||||||
|  |  * regarding copyright ownership.  The ASF licenses this file | ||||||
|  |  * to you under the Apache License, Version 2.0 (the | ||||||
|  |  * "License"); you may not use this file except in compliance | ||||||
|  |  * with the License.  You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *   http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, | ||||||
|  |  * software distributed under the License is distributed on an | ||||||
|  |  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||||
|  |  * KIND, either express or implied.  See the License for the | ||||||
|  |  * specific language governing permissions and limitations | ||||||
|  |  * under the License. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package org.apache.guacamole.extension; | ||||||
|  |  | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.Comparator; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.regex.Pattern; | ||||||
|  | import org.apache.guacamole.GuacamoleException; | ||||||
|  | import org.apache.guacamole.properties.GuacamoleProperty; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * A GuacamoleProperty that defines the order of Guacamole extensions. The | ||||||
|  |  * property value is a comma-separated list of extension namespaces, with "*" | ||||||
|  |  * used to represent all extensions that aren't listed. For example, a value | ||||||
|  |  * like "saml, *, ldap" would order SAML support first and LDAP support last, | ||||||
|  |  * with all other extensions loaded between the two in filename order. For | ||||||
|  |  * values without "*", all other extensions are implicitly after all extensions | ||||||
|  |  * that are explicitly listed. | ||||||
|  |  */ | ||||||
|  | public abstract class ExtensionOrderProperty implements GuacamoleProperty<Comparator<Extension>> { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Logger for this class. | ||||||
|  |      */ | ||||||
|  |     private static final Logger logger = LoggerFactory.getLogger(ExtensionOrderProperty.class); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * A pattern which matches against the delimiters between values. This is | ||||||
|  |      * currently simply a comma and any following whitespace. Parts of the | ||||||
|  |      * input string which match this pattern will not be included in the parsed | ||||||
|  |      * result. | ||||||
|  |      */ | ||||||
|  |     private static final Pattern DELIMITER_PATTERN = Pattern.compile(",\\s*"); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Static comparator instance that sorts extensions by their filenames | ||||||
|  |      * alone. | ||||||
|  |      */ | ||||||
|  |     public static final Comparator<Extension> DEFAULT_COMPARATOR = new ExtensionComparator(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Comparator that sorts extensions in order of priority, as dictated by a | ||||||
|  |      * list of the extensions that should be ordered first or last. All | ||||||
|  |      * extensions not explicitly listed will instead be sorted by filename. | ||||||
|  |      */ | ||||||
|  |     private static class ExtensionComparator implements Comparator<Extension> { | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The string value representing the set of all extensions not | ||||||
|  |          * explicitly listed. | ||||||
|  |          */ | ||||||
|  |         private final String OTHER_EXTENSIONS = "*"; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The relative priorities of all extensions. Any extension not listed | ||||||
|  |          * within this map should be sorted with the priority value stored in | ||||||
|  |          * {@link #defaultPriority}. | ||||||
|  |          */ | ||||||
|  |         private final Map<String, Integer> extensionPriority; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * The relative priority that should be used for all extensions not | ||||||
|  |          * explicitly listed within {@link #extensionPriority}. | ||||||
|  |          */ | ||||||
|  |         private final int defaultPriority; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Creates a new ExtensionComparator that sorts all extensions by their | ||||||
|  |          * filenames only. | ||||||
|  |          */ | ||||||
|  |         public ExtensionComparator() { | ||||||
|  |             defaultPriority = 0; | ||||||
|  |             extensionPriority = Collections.emptyMap(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Creates a new ExtensionComparator that ensures each of the given | ||||||
|  |          * extensions are sorted in the relative order listed, with any | ||||||
|  |          * extensions not explicitly listed sorted by filename. | ||||||
|  |          * | ||||||
|  |          * @param name | ||||||
|  |          *     The name of the property defining the provided list of | ||||||
|  |          *     extensions. | ||||||
|  |          * | ||||||
|  |          * @param extensions | ||||||
|  |          *     The namespaces of the extensions in the order they should be | ||||||
|  |          *     sorted, with the special value "*" functioning as a | ||||||
|  |          *     placeholder for all extensions that are not explicitly listed. | ||||||
|  |          */ | ||||||
|  |         public ExtensionComparator(String name, String... extensions) { | ||||||
|  |  | ||||||
|  |             extensionPriority = new HashMap<>(extensions.length); | ||||||
|  |  | ||||||
|  |             for (int priority = 0; priority < extensions.length; priority++) { | ||||||
|  |                 String extension = extensions[priority]; | ||||||
|  |                 if (extensionPriority.putIfAbsent(extension, priority) != null) | ||||||
|  |                     logger.warn("The value \"{}\" was specified multiple " | ||||||
|  |                             + "times for property \"{}\". Only the first " | ||||||
|  |                             + "occurrence of this value will have any effect.", | ||||||
|  |                             extension, name); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Integer otherExtensionPriority = extensionPriority.remove(OTHER_EXTENSIONS); | ||||||
|  |             if (otherExtensionPriority != null) | ||||||
|  |                 defaultPriority = otherExtensionPriority; | ||||||
|  |             else | ||||||
|  |                 defaultPriority = extensions.length; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public int compare(Extension extA, Extension extB) { | ||||||
|  |  | ||||||
|  |             int priorityA = extensionPriority.getOrDefault(extA.getNamespace(), defaultPriority); | ||||||
|  |             int priorityB = extensionPriority.getOrDefault(extB.getNamespace(), defaultPriority); | ||||||
|  |  | ||||||
|  |             // Sort by explicit priority first | ||||||
|  |             if (priorityA != priorityB) | ||||||
|  |                 return priorityA - priorityB; | ||||||
|  |  | ||||||
|  |             // Sort all extensions without explicit priorities by their | ||||||
|  |             // filenames (no extensions will have the same priority except | ||||||
|  |             // those that aren't explicitly listed) | ||||||
|  |             return extA.getFile().compareTo(extB.getFile()); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Comparator<Extension> parseValue(String value) throws GuacamoleException { | ||||||
|  |  | ||||||
|  |         // If no property provided, return null. | ||||||
|  |         if (value == null) | ||||||
|  |             return null; | ||||||
|  |  | ||||||
|  |         // Split string into a set of individual values | ||||||
|  |         return new ExtensionComparator(getName(), DELIMITER_PATTERN.split(value)); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user