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:14 UTC
[05/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/properties/AuthenticationProviderProperty.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/properties/AuthenticationProviderProperty.java b/guacamole/src/main/java/org/apache/guacamole/properties/AuthenticationProviderProperty.java
new file mode 100644
index 0000000..09f3576
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/properties/AuthenticationProviderProperty.java
@@ -0,0 +1,68 @@
+/*
+ * 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.properties;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.AuthenticationProvider;
+import org.apache.guacamole.properties.GuacamoleProperty;
+
+/**
+ * A GuacamoleProperty whose value is the name of a class to use to
+ * authenticate users. This class must implement AuthenticationProvider. Use
+ * of this property type is deprecated in favor of the
+ * GUACAMOLE_HOME/extensions directory.
+ *
+ * @author Michael Jumper
+ */
+@Deprecated
+public abstract class AuthenticationProviderProperty implements GuacamoleProperty<Class<AuthenticationProvider>> {
+
+ @Override
+ @SuppressWarnings("unchecked") // Explicitly checked within by isAssignableFrom()
+ public Class<AuthenticationProvider> parseValue(String authProviderClassName) throws GuacamoleException {
+
+ // If no property provided, return null.
+ if (authProviderClassName == null)
+ return null;
+
+ // Get auth provider instance
+ try {
+
+ // Get authentication provider class
+ Class<?> authProviderClass = org.apache.guacamole.GuacamoleClassLoader.getInstance().loadClass(authProviderClassName);
+
+ // Verify the located class is actually a subclass of AuthenticationProvider
+ if (!AuthenticationProvider.class.isAssignableFrom(authProviderClass))
+ throw new GuacamoleException("Specified authentication provider class is not a AuthenticationProvider.");
+
+ // Return located class
+ return (Class<AuthenticationProvider>) authProviderClass;
+
+ }
+ catch (ClassNotFoundException e) {
+ throw new GuacamoleException("Authentication provider class not found", e);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/properties/BasicGuacamoleProperties.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/properties/BasicGuacamoleProperties.java b/guacamole/src/main/java/org/apache/guacamole/properties/BasicGuacamoleProperties.java
new file mode 100644
index 0000000..9d8d99f
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/properties/BasicGuacamoleProperties.java
@@ -0,0 +1,90 @@
+/*
+ * 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.properties;
+
+import org.apache.guacamole.properties.FileGuacamoleProperty;
+import org.apache.guacamole.properties.IntegerGuacamoleProperty;
+import org.apache.guacamole.properties.StringGuacamoleProperty;
+
+/**
+ * Properties used by the default Guacamole web application.
+ *
+ * @author Michael Jumper
+ */
+public class BasicGuacamoleProperties {
+
+ /**
+ * This class should not be instantiated.
+ */
+ private BasicGuacamoleProperties() {}
+
+ /**
+ * The authentication provider to user when retrieving the authorized
+ * configurations of a user. This property is currently supported, but
+ * deprecated in favor of the GUACAMOLE_HOME/extensions directory.
+ */
+ @Deprecated
+ public static final AuthenticationProviderProperty AUTH_PROVIDER = new AuthenticationProviderProperty() {
+
+ @Override
+ public String getName() { return "auth-provider"; }
+
+ };
+
+ /**
+ * The directory to search for authentication provider classes. This
+ * property is currently supported, but deprecated in favor of the
+ * GUACAMOLE_HOME/lib directory.
+ */
+ @Deprecated
+ public static final FileGuacamoleProperty LIB_DIRECTORY = new FileGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "lib-directory"; }
+
+ };
+
+ /**
+ * The session timeout for the API, in minutes.
+ */
+ public static final IntegerGuacamoleProperty API_SESSION_TIMEOUT = new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "api-session-timeout"; }
+
+ };
+
+ /**
+ * Comma-separated list of all allowed languages, where each language is
+ * represented by a language key, such as "en" or "en_US". If specified,
+ * only languages within this list will be listed as available by the REST
+ * service.
+ */
+ public static final StringSetProperty ALLOWED_LANGUAGES = new StringSetProperty() {
+
+ @Override
+ public String getName() { return "allowed-languages"; }
+
+ };
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/properties/StringSetProperty.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/properties/StringSetProperty.java b/guacamole/src/main/java/org/apache/guacamole/properties/StringSetProperty.java
new file mode 100644
index 0000000..5bcd13c
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/properties/StringSetProperty.java
@@ -0,0 +1,66 @@
+/*
+ * 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.properties;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.properties.GuacamoleProperty;
+
+/**
+ * A GuacamoleProperty whose value is a Set of unique Strings. The string value
+ * parsed to produce this set is a comma-delimited list. Duplicate values are
+ * ignored, as is any whitespace following delimiters. To maintain
+ * compatibility with the behavior of Java properties in general, only
+ * whitespace at the beginning of each value is ignored; trailing whitespace
+ * becomes part of the value.
+ *
+ * @author Michael Jumper
+ */
+public abstract class StringSetProperty implements GuacamoleProperty<Set<String>> {
+
+ /**
+ * 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*");
+
+ @Override
+ public Set<String> parseValue(String values) throws GuacamoleException {
+
+ // If no property provided, return null.
+ if (values == null)
+ return null;
+
+ // Split string into a set of individual values
+ List<String> valueList = Arrays.asList(DELIMITER_PATTERN.split(values));
+ return new HashSet<String>(valueList);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/properties/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/properties/package-info.java b/guacamole/src/main/java/org/apache/guacamole/properties/package-info.java
new file mode 100644
index 0000000..0640815
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/properties/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to the properties which the Guacamole web application
+ * (and stock parts of it) read from guacamole.properties.
+ */
+package org.apache.guacamole.properties;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/AbstractResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/AbstractResource.java b/guacamole/src/main/java/org/apache/guacamole/resource/AbstractResource.java
new file mode 100644
index 0000000..f985361
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/AbstractResource.java
@@ -0,0 +1,82 @@
+/*
+ * 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.resource;
+
+/**
+ * Base abstract resource implementation which provides an associated mimetype,
+ * and modification time. Classes which extend AbstractResource must provide
+ * their own InputStream, however.
+ *
+ * @author Michael Jumper
+ */
+public abstract class AbstractResource implements Resource {
+
+ /**
+ * The mimetype of this resource.
+ */
+ private final String mimetype;
+
+ /**
+ * The time this resource was last modified, in milliseconds since midnight
+ * of January 1, 1970 UTC.
+ */
+ private final long lastModified;
+
+ /**
+ * Initializes this AbstractResource with the given mimetype and
+ * modification time.
+ *
+ * @param mimetype
+ * The mimetype of this resource.
+ *
+ * @param lastModified
+ * The time this resource was last modified, in milliseconds since
+ * midnight of January 1, 1970 UTC.
+ */
+ public AbstractResource(String mimetype, long lastModified) {
+ this.mimetype = mimetype;
+ this.lastModified = lastModified;
+ }
+
+ /**
+ * Initializes this AbstractResource with the given mimetype. The
+ * modification time of the resource is set to the current system time.
+ *
+ * @param mimetype
+ * The mimetype of this resource.
+ */
+ public AbstractResource(String mimetype) {
+ this(mimetype, System.currentTimeMillis());
+ }
+
+ @Override
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ @Override
+ public String getMimeType() {
+ return mimetype;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/ByteArrayResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/ByteArrayResource.java b/guacamole/src/main/java/org/apache/guacamole/resource/ByteArrayResource.java
new file mode 100644
index 0000000..2fa8199
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/ByteArrayResource.java
@@ -0,0 +1,62 @@
+/*
+ * 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.resource;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * A resource which contains a defined byte array.
+ *
+ * @author Michael Jumper
+ */
+public class ByteArrayResource extends AbstractResource {
+
+ /**
+ * The bytes contained by this resource.
+ */
+ private final byte[] bytes;
+
+ /**
+ * Creates a new ByteArrayResource which provides access to the given byte
+ * array. Changes to the given byte array will affect this resource even
+ * after the resource is created. Changing the byte array while an input
+ * stream from this resource is in use has undefined behavior.
+ *
+ * @param mimetype
+ * The mimetype of the resource.
+ *
+ * @param bytes
+ * The bytes that this resource should contain.
+ */
+ public ByteArrayResource(String mimetype, byte[] bytes) {
+ super(mimetype);
+ this.bytes = bytes;
+ }
+
+ @Override
+ public InputStream asStream() {
+ return new ByteArrayInputStream(bytes);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/ClassPathResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/ClassPathResource.java b/guacamole/src/main/java/org/apache/guacamole/resource/ClassPathResource.java
new file mode 100644
index 0000000..fb81791
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/ClassPathResource.java
@@ -0,0 +1,85 @@
+/*
+ * 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.resource;
+
+import java.io.InputStream;
+
+/**
+ * A resource which is located within the classpath of an arbitrary
+ * ClassLoader.
+ *
+ * @author Michael Jumper
+ */
+public class ClassPathResource extends AbstractResource {
+
+ /**
+ * The classloader to use when reading this resource.
+ */
+ private final ClassLoader classLoader;
+
+ /**
+ * The path of this resource relative to the classloader.
+ */
+ private final String path;
+
+ /**
+ * Creates a new ClassPathResource which uses the given ClassLoader to
+ * read the resource having the given path.
+ *
+ * @param classLoader
+ * The ClassLoader to use when reading the resource.
+ *
+ * @param mimetype
+ * The mimetype of the resource.
+ *
+ * @param path
+ * The path of the resource relative to the given ClassLoader.
+ */
+ public ClassPathResource(ClassLoader classLoader, String mimetype, String path) {
+ super(mimetype);
+ this.classLoader = classLoader;
+ this.path = path;
+ }
+
+ /**
+ * Creates a new ClassPathResource which uses the ClassLoader associated
+ * with the ClassPathResource class to read the resource having the given
+ * path.
+ *
+ * @param mimetype
+ * The mimetype of the resource.
+ *
+ * @param path
+ * The path of the resource relative to the ClassLoader associated
+ * with the ClassPathResource class.
+ */
+ public ClassPathResource(String mimetype, String path) {
+ this(ClassPathResource.class.getClassLoader(), mimetype, path);
+ }
+
+ @Override
+ public InputStream asStream() {
+ return classLoader.getResourceAsStream(path);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/Resource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/Resource.java b/guacamole/src/main/java/org/apache/guacamole/resource/Resource.java
new file mode 100644
index 0000000..845bd72
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/Resource.java
@@ -0,0 +1,67 @@
+/*
+ * 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.resource;
+
+import java.io.InputStream;
+
+/**
+ * An arbitrary resource that can be served to a user via HTTP. Resources are
+ * anonymous but have a defined mimetype and corresponding input stream.
+ *
+ * @author Michael Jumper
+ */
+public interface Resource {
+
+ /**
+ * Returns the mimetype of this resource. This function MUST always return
+ * a value. If the type is unknown, return "application/octet-stream".
+ *
+ * @return
+ * The mimetype of this resource.
+ */
+ String getMimeType();
+
+ /**
+ * Returns the time the resource was last modified in milliseconds since
+ * midnight of January 1, 1970 UTC.
+ *
+ * @return
+ * The time the resource was last modified, in milliseconds.
+ */
+ long getLastModified();
+
+ /**
+ * Returns an InputStream which reads the contents of this resource,
+ * starting with the first byte. Reading from the returned InputStream will
+ * not affect reads from other InputStreams returned by other calls to
+ * asStream(). The returned InputStream must be manually closed when no
+ * longer needed. If the resource is unexpectedly unavailable, this will
+ * return null.
+ *
+ * @return
+ * An InputStream which reads the contents of this resource, starting
+ * with the first byte, or null if the resource is unavailable.
+ */
+ InputStream asStream();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/ResourceServlet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/ResourceServlet.java b/guacamole/src/main/java/org/apache/guacamole/resource/ResourceServlet.java
new file mode 100644
index 0000000..f1af7c6
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/ResourceServlet.java
@@ -0,0 +1,126 @@
+/*
+ * 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.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Servlet which serves a given resource for all HTTP GET requests. The HEAD
+ * method is correctly supported, and HTTP 304 ("Not Modified") responses will
+ * be properly returned for GET requests depending on the last time the
+ * resource was modified.
+ *
+ * @author Michael Jumper
+ */
+public class ResourceServlet extends HttpServlet {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(ResourceServlet.class);
+
+ /**
+ * The size of the buffer to use when transferring data from the input
+ * stream of a resource to the output stream of a request.
+ */
+ private static final int BUFFER_SIZE = 10240;
+
+ /**
+ * The resource to serve for every GET request.
+ */
+ private final Resource resource;
+
+ /**
+ * Creates a new ResourceServlet which serves the given Resource for all
+ * HTTP GET requests.
+ *
+ * @param resource
+ * The Resource to serve.
+ */
+ public ResourceServlet(Resource resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ protected void doHead(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ // Set last modified and content type headers
+ response.addDateHeader("Last-Modified", resource.getLastModified());
+ response.setContentType(resource.getMimeType());
+
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ // Get input stream from resource
+ InputStream input = resource.asStream();
+
+ // If resource does not exist, return not found
+ if (input == null) {
+ logger.debug("Resource does not exist: \"{}\"", request.getServletPath());
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ try {
+
+ // Write headers
+ doHead(request, response);
+
+ // If not modified since "If-Modified-Since" header, return not modified
+ long ifModifiedSince = request.getDateHeader("If-Modified-Since");
+ if (resource.getLastModified() - ifModifiedSince < 1000) {
+ logger.debug("Resource not modified: \"{}\"", request.getServletPath());
+ response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+ return;
+ }
+
+ int length;
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ // Write resource to response body
+ OutputStream output = response.getOutputStream();
+ while ((length = input.read(buffer)) != -1)
+ output.write(buffer, 0, length);
+
+ }
+
+ // Ensure input stream is always closed
+ finally {
+ input.close();
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/SequenceResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/SequenceResource.java b/guacamole/src/main/java/org/apache/guacamole/resource/SequenceResource.java
new file mode 100644
index 0000000..fe407b5
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/SequenceResource.java
@@ -0,0 +1,153 @@
+/*
+ * 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.resource;
+
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * A resource which is the logical concatenation of other resources.
+ *
+ * @author Michael Jumper
+ */
+public class SequenceResource extends AbstractResource {
+
+ /**
+ * The resources to be concatenated.
+ */
+ private final Iterable<Resource> resources;
+
+ /**
+ * Returns the mimetype of the first resource in the given Iterable, or
+ * "application/octet-stream" if no resources are provided.
+ *
+ * @param resources
+ * The resources from which the mimetype should be retrieved.
+ *
+ * @return
+ * The mimetype of the first resource, or "application/octet-stream"
+ * if no resources were provided.
+ */
+ private static String getMimeType(Iterable<Resource> resources) {
+
+ // If no resources, just assume application/octet-stream
+ Iterator<Resource> resourceIterator = resources.iterator();
+ if (!resourceIterator.hasNext())
+ return "application/octet-stream";
+
+ // Return mimetype of first resource
+ return resourceIterator.next().getMimeType();
+
+ }
+
+ /**
+ * Creates a new SequenceResource as the logical concatenation of the
+ * given resources. Each resource is concatenated in iteration order as
+ * needed when reading from the input stream of the SequenceResource.
+ *
+ * @param mimetype
+ * The mimetype of the resource.
+ *
+ * @param resources
+ * The resources to concatenate within the InputStream of this
+ * SequenceResource.
+ */
+ public SequenceResource(String mimetype, Iterable<Resource> resources) {
+ super(mimetype);
+ this.resources = resources;
+ }
+
+ /**
+ * Creates a new SequenceResource as the logical concatenation of the
+ * given resources. Each resource is concatenated in iteration order as
+ * needed when reading from the input stream of the SequenceResource. The
+ * mimetype of the resulting concatenation is derived from the first
+ * resource.
+ *
+ * @param resources
+ * The resources to concatenate within the InputStream of this
+ * SequenceResource.
+ */
+ public SequenceResource(Iterable<Resource> resources) {
+ super(getMimeType(resources));
+ this.resources = resources;
+ }
+
+ /**
+ * Creates a new SequenceResource as the logical concatenation of the
+ * given resources. Each resource is concatenated in iteration order as
+ * needed when reading from the input stream of the SequenceResource.
+ *
+ * @param mimetype
+ * The mimetype of the resource.
+ *
+ * @param resources
+ * The resources to concatenate within the InputStream of this
+ * SequenceResource.
+ */
+ public SequenceResource(String mimetype, Resource... resources) {
+ this(mimetype, Arrays.asList(resources));
+ }
+
+ /**
+ * Creates a new SequenceResource as the logical concatenation of the
+ * given resources. Each resource is concatenated in iteration order as
+ * needed when reading from the input stream of the SequenceResource. The
+ * mimetype of the resulting concatenation is derived from the first
+ * resource.
+ *
+ * @param resources
+ * The resources to concatenate within the InputStream of this
+ * SequenceResource.
+ */
+ public SequenceResource(Resource... resources) {
+ this(Arrays.asList(resources));
+ }
+
+ @Override
+ public InputStream asStream() {
+ return new SequenceInputStream(new Enumeration<InputStream>() {
+
+ /**
+ * Iterator over all resources associated with this
+ * SequenceResource.
+ */
+ private final Iterator<Resource> resourceIterator = resources.iterator();
+
+ @Override
+ public boolean hasMoreElements() {
+ return resourceIterator.hasNext();
+ }
+
+ @Override
+ public InputStream nextElement() {
+ return resourceIterator.next().asStream();
+ }
+
+ });
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/WebApplicationResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/WebApplicationResource.java b/guacamole/src/main/java/org/apache/guacamole/resource/WebApplicationResource.java
new file mode 100644
index 0000000..b8aae14
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/WebApplicationResource.java
@@ -0,0 +1,116 @@
+/*
+ * 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.resource;
+
+import java.io.InputStream;
+import javax.servlet.ServletContext;
+
+/**
+ * A resource which is located within the classpath associated with another
+ * class.
+ *
+ * @author Michael Jumper
+ */
+public class WebApplicationResource extends AbstractResource {
+
+ /**
+ * The servlet context to use when reading the resource and, if necessary,
+ * when determining the mimetype of the resource.
+ */
+ private final ServletContext context;
+
+ /**
+ * The path of this resource relative to the ServletContext.
+ */
+ private final String path;
+
+ /**
+ * Derives a mimetype from the filename within the given path using the
+ * given ServletContext, if possible.
+ *
+ * @param context
+ * The ServletContext to use to derive the mimetype.
+ *
+ * @param path
+ * The path to derive the mimetype from.
+ *
+ * @return
+ * An appropriate mimetype based on the name of the file in the path,
+ * or "application/octet-stream" if no mimetype could be determined.
+ */
+ private static String getMimeType(ServletContext context, String path) {
+
+ // If mimetype is known, use defined mimetype
+ String mimetype = context.getMimeType(path);
+ if (mimetype != null)
+ return mimetype;
+
+ // Otherwise, default to application/octet-stream
+ return "application/octet-stream";
+
+ }
+
+ /**
+ * Creates a new WebApplicationResource which serves the resource at the
+ * given path relative to the given ServletContext. Rather than deriving
+ * the mimetype of the resource from the filename within the path, the
+ * mimetype given is used.
+ *
+ * @param context
+ * The ServletContext to use when reading the resource.
+ *
+ * @param mimetype
+ * The mimetype of the resource.
+ *
+ * @param path
+ * The path of the resource relative to the given ServletContext.
+ */
+ public WebApplicationResource(ServletContext context, String mimetype, String path) {
+ super(mimetype);
+ this.context = context;
+ this.path = path;
+ }
+
+ /**
+ * Creates a new WebApplicationResource which serves the resource at the
+ * given path relative to the given ServletContext. The mimetype of the
+ * resource is automatically determined based on the filename within the
+ * path.
+ *
+ * @param context
+ * The ServletContext to use when reading the resource and deriving the
+ * mimetype.
+ *
+ * @param path
+ * The path of the resource relative to the given ServletContext.
+ */
+ public WebApplicationResource(ServletContext context, String path) {
+ this(context, getMimeType(context, path), path);
+ }
+
+ @Override
+ public InputStream asStream() {
+ return context.getResourceAsStream(path);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/resource/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/resource/package-info.java b/guacamole/src/main/java/org/apache/guacamole/resource/package-info.java
new file mode 100644
index 0000000..edbe504
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/resource/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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 describe and provide access to arbitrary resources, such as
+ * the contents of the classpath of a classloader, or files within the web
+ * application itself.
+ */
+package org.apache.guacamole.resource;
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/APIError.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIError.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIError.java
new file mode 100644
index 0000000..74fa7d2
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIError.java
@@ -0,0 +1,183 @@
+/*
+ * 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.rest;
+
+import java.util.Collection;
+import javax.ws.rs.core.Response;
+import org.apache.guacamole.form.Field;
+
+/**
+ * Describes an error that occurred within a REST endpoint.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+public class APIError {
+
+ /**
+ * The error message.
+ */
+ private final String message;
+
+ /**
+ * All expected request parameters, if any, as a collection of fields.
+ */
+ private final Collection<Field> expected;
+
+ /**
+ * The type of error that occurred.
+ */
+ private final Type type;
+
+ /**
+ * All possible types of REST API errors.
+ */
+ public enum Type {
+
+ /**
+ * The requested operation could not be performed because the request
+ * itself was malformed.
+ */
+ BAD_REQUEST(Response.Status.BAD_REQUEST),
+
+ /**
+ * The credentials provided were invalid.
+ */
+ INVALID_CREDENTIALS(Response.Status.FORBIDDEN),
+
+ /**
+ * The credentials provided were not necessarily invalid, but were not
+ * sufficient to determine validity.
+ */
+ INSUFFICIENT_CREDENTIALS(Response.Status.FORBIDDEN),
+
+ /**
+ * An internal server error has occurred.
+ */
+ INTERNAL_ERROR(Response.Status.INTERNAL_SERVER_ERROR),
+
+ /**
+ * An object related to the request does not exist.
+ */
+ NOT_FOUND(Response.Status.NOT_FOUND),
+
+ /**
+ * Permission was denied to perform the requested operation.
+ */
+ PERMISSION_DENIED(Response.Status.FORBIDDEN);
+
+ /**
+ * The HTTP status associated with this error type.
+ */
+ private final Response.Status status;
+
+ /**
+ * Defines a new error type associated with the given HTTP status.
+ *
+ * @param status
+ * The HTTP status to associate with the error type.
+ */
+ Type(Response.Status status) {
+ this.status = status;
+ }
+
+ /**
+ * Returns the HTTP status associated with this error type.
+ *
+ * @return
+ * The HTTP status associated with this error type.
+ */
+ public Response.Status getStatus() {
+ return status;
+ }
+
+ }
+
+ /**
+ * Create a new APIError with the specified error message.
+ *
+ * @param type
+ * The type of error that occurred.
+ *
+ * @param message
+ * The error message.
+ */
+ public APIError(Type type, String message) {
+ this.type = type;
+ this.message = message;
+ this.expected = null;
+ }
+
+ /**
+ * Create a new APIError with the specified error message and parameter
+ * information.
+ *
+ * @param type
+ * The type of error that occurred.
+ *
+ * @param message
+ * The error message.
+ *
+ * @param expected
+ * All parameters expected in the original request, or now required as
+ * a result of the original request, as a collection of fields.
+ */
+ public APIError(Type type, String message, Collection<Field> expected) {
+ this.type = type;
+ this.message = message;
+ this.expected = expected;
+ }
+
+ /**
+ * Returns the type of error that occurred.
+ *
+ * @return
+ * The type of error that occurred.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Returns a collection of all required parameters, where each parameter is
+ * represented by a field.
+ *
+ * @return
+ * A collection of all required parameters.
+ */
+ public Collection<Field> getExpected() {
+ return expected;
+ }
+
+ /**
+ * Returns a human-readable error message describing the error that
+ * occurred.
+ *
+ * @return
+ * A human-readable error message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java
new file mode 100644
index 0000000..7a70fb8
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java
@@ -0,0 +1,85 @@
+/*
+ * 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.rest;
+
+import java.util.Collection;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import org.apache.guacamole.form.Field;
+
+/**
+ * An exception that will result in the given error error information being
+ * returned from the API layer. All error messages have the same format which
+ * is defined by APIError.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+public class APIException extends WebApplicationException {
+
+ /**
+ * Construct a new APIException with the given error. All information
+ * associated with this new exception will be extracted from the given
+ * APIError.
+ *
+ * @param error
+ * The error that occurred.
+ */
+ public APIException(APIError error) {
+ super(Response.status(error.getType().getStatus()).entity(error).build());
+ }
+
+ /**
+ * Creates a new APIException with the given type and message. The
+ * corresponding APIError will be created from the provided information.
+ *
+ * @param type
+ * The type of error that occurred.
+ *
+ * @param message
+ * A human-readable message describing the error.
+ */
+ public APIException(APIError.Type type, String message) {
+ this(new APIError(type, message));
+ }
+
+ /**
+ * Creates a new APIException with the given type, message, and parameter
+ * information. The corresponding APIError will be created from the
+ * provided information.
+ *
+ * @param type
+ * The type of error that occurred.
+ *
+ * @param message
+ * A human-readable message describing the error.
+ *
+ * @param expected
+ * All parameters expected in the original request, or now required as
+ * a result of the original request, as a collection of fields.
+ */
+ public APIException(APIError.Type type, String message, Collection<Field> expected) {
+ this(new APIError(type, message, expected));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/APIPatch.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIPatch.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIPatch.java
new file mode 100644
index 0000000..08ee062
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIPatch.java
@@ -0,0 +1,104 @@
+/*
+ * 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.rest;
+
+/**
+ * An object for representing the body of a HTTP PATCH method.
+ * See https://tools.ietf.org/html/rfc6902
+ *
+ * @author James Muehlner
+ * @param <T> The type of object being patched.
+ */
+public class APIPatch<T> {
+
+ /**
+ * The possible operations for a PATCH request.
+ */
+ public enum Operation {
+ add, remove, test, copy, replace, move
+ }
+
+ /**
+ * The operation to perform for this patch.
+ */
+ private Operation op;
+
+ /**
+ * The value for this patch.
+ */
+ private T value;
+
+ /**
+ * The path for this patch.
+ */
+ private String path;
+
+ /**
+ * Returns the operation for this patch.
+ * @return the operation for this patch.
+ */
+ public Operation getOp() {
+ return op;
+ }
+
+ /**
+ * Set the operation for this patch.
+ * @param op The operation for this patch.
+ */
+ public void setOp(Operation op) {
+ this.op = op;
+ }
+
+ /**
+ * Returns the value of this patch.
+ * @return The value of this patch.
+ */
+ public T getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of this patch.
+ * @param value The value of this patch.
+ */
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the path for this patch.
+ * @return The path for this patch.
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Set the path for this patch.
+ * @param path The path for this patch.
+ */
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/APIRequest.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIRequest.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIRequest.java
new file mode 100644
index 0000000..cd4fcd5
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIRequest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.rest;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * Wrapper for HttpServletRequest which uses a given MultivaluedMap to provide
+ * the values of all request parameters.
+ *
+ * @author Michael Jumper
+ */
+public class APIRequest extends HttpServletRequestWrapper {
+
+ /**
+ * Map of all request parameter names to their corresponding values.
+ */
+ private final Map<String, String[]> parameters;
+
+ /**
+ * Wraps the given HttpServletRequest, using the given MultivaluedMap to
+ * provide all request parameters. All HttpServletRequest functions which
+ * do not deal with parameter names and values are delegated to the wrapped
+ * request.
+ *
+ * @param request
+ * The HttpServletRequest to wrap.
+ *
+ * @param parameters
+ * All request parameters.
+ */
+ public APIRequest(HttpServletRequest request,
+ MultivaluedMap<String, String> parameters) {
+
+ super(request);
+
+ // Copy parameters from given MultivaluedMap
+ this.parameters = new HashMap<String, String[]>(parameters.size());
+ for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
+
+ // Get parameter name and all corresponding values
+ String name = entry.getKey();
+ List<String> values = entry.getValue();
+
+ // Add parameters to map
+ this.parameters.put(name, values.toArray(new String[values.size()]));
+
+ }
+
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ return parameters.get(name);
+ }
+
+ @Override
+ public Enumeration<String> getParameterNames() {
+ return Collections.enumeration(parameters.keySet());
+ }
+
+ @Override
+ public Map<String, String[]> getParameterMap() {
+ return Collections.unmodifiableMap(parameters);
+ }
+
+ @Override
+ public String getParameter(String name) {
+
+ // If no such parameter exists, just return null
+ String[] values = getParameterValues(name);
+ if (values == null)
+ return null;
+
+ // Otherwise, return first value
+ return values[0];
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java b/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java
new file mode 100644
index 0000000..1cf8919
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java
@@ -0,0 +1,280 @@
+/*
+ * 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.rest;
+
+import java.util.List;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleResourceNotFoundException;
+import org.apache.guacamole.net.auth.AuthenticationProvider;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.User;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.GuacamoleSession;
+import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup;
+
+/**
+ * Provides easy access and automatic error handling for retrieval of objects,
+ * such as users, connections, or connection groups. REST API semantics, such
+ * as the special root connection group identifier, are also handled
+ * automatically.
+ */
+public class ObjectRetrievalService {
+
+ /**
+ * Retrieves a single UserContext from the given GuacamoleSession, which
+ * may contain multiple UserContexts.
+ *
+ * @param session
+ * The GuacamoleSession to retrieve the UserContext from.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider that created the
+ * UserContext being retrieved. Only one UserContext per User per
+ * AuthenticationProvider can exist.
+ *
+ * @return
+ * The UserContext that was created by the AuthenticationProvider
+ * having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the UserContext, or if the
+ * UserContext does not exist.
+ */
+ public UserContext retrieveUserContext(GuacamoleSession session,
+ String authProviderIdentifier) throws GuacamoleException {
+
+ // Get list of UserContexts
+ List<UserContext> userContexts = session.getUserContexts();
+
+ // Locate and return the UserContext associated with the
+ // AuthenticationProvider having the given identifier, if any
+ for (UserContext userContext : userContexts) {
+
+ // Get AuthenticationProvider associated with current UserContext
+ AuthenticationProvider authProvider = userContext.getAuthenticationProvider();
+
+ // If AuthenticationProvider identifier matches, done
+ if (authProvider.getIdentifier().equals(authProviderIdentifier))
+ return userContext;
+
+ }
+
+ throw new GuacamoleResourceNotFoundException("Session not associated with authentication provider \"" + authProviderIdentifier + "\".");
+
+ }
+
+ /**
+ * Retrieves a single user from the given user context.
+ *
+ * @param userContext
+ * The user context to retrieve the user from.
+ *
+ * @param identifier
+ * The identifier of the user to retrieve.
+ *
+ * @return
+ * The user having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the user, or if the
+ * user does not exist.
+ */
+ public User retrieveUser(UserContext userContext,
+ String identifier) throws GuacamoleException {
+
+ // Get user directory
+ Directory<User> directory = userContext.getUserDirectory();
+
+ // Pull specified user
+ User user = directory.get(identifier);
+ if (user == null)
+ throw new GuacamoleResourceNotFoundException("No such user: \"" + identifier + "\"");
+
+ return user;
+
+ }
+
+ /**
+ * Retrieves a single user from the given GuacamoleSession.
+ *
+ * @param session
+ * The GuacamoleSession to retrieve the user from.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider that created the
+ * UserContext from which the user should be retrieved. Only one
+ * UserContext per User per AuthenticationProvider can exist.
+ *
+ * @param identifier
+ * The identifier of the user to retrieve.
+ *
+ * @return
+ * The user having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the user, or if the
+ * user does not exist.
+ */
+ public User retrieveUser(GuacamoleSession session, String authProviderIdentifier,
+ String identifier) throws GuacamoleException {
+
+ UserContext userContext = retrieveUserContext(session, authProviderIdentifier);
+ return retrieveUser(userContext, identifier);
+
+ }
+
+ /**
+ * Retrieves a single connection from the given user context.
+ *
+ * @param userContext
+ * The user context to retrieve the connection from.
+ *
+ * @param identifier
+ * The identifier of the connection to retrieve.
+ *
+ * @return
+ * The connection having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection, or if the
+ * connection does not exist.
+ */
+ public Connection retrieveConnection(UserContext userContext,
+ String identifier) throws GuacamoleException {
+
+ // Get connection directory
+ Directory<Connection> directory = userContext.getConnectionDirectory();
+
+ // Pull specified connection
+ Connection connection = directory.get(identifier);
+ if (connection == null)
+ throw new GuacamoleResourceNotFoundException("No such connection: \"" + identifier + "\"");
+
+ return connection;
+
+ }
+
+ /**
+ * Retrieves a single connection from the given GuacamoleSession.
+ *
+ * @param session
+ * The GuacamoleSession to retrieve the connection from.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider that created the
+ * UserContext from which the connection should be retrieved. Only one
+ * UserContext per User per AuthenticationProvider can exist.
+ *
+ * @param identifier
+ * The identifier of the connection to retrieve.
+ *
+ * @return
+ * The connection having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection, or if the
+ * connection does not exist.
+ */
+ public Connection retrieveConnection(GuacamoleSession session,
+ String authProviderIdentifier, String identifier)
+ throws GuacamoleException {
+
+ UserContext userContext = retrieveUserContext(session, authProviderIdentifier);
+ return retrieveConnection(userContext, identifier);
+
+ }
+
+ /**
+ * Retrieves a single connection group from the given user context. If
+ * the given identifier the REST API root identifier, the root connection
+ * group will be returned. The underlying authentication provider may
+ * additionally use a different identifier for root.
+ *
+ * @param userContext
+ * The user context to retrieve the connection group from.
+ *
+ * @param identifier
+ * The identifier of the connection group to retrieve.
+ *
+ * @return
+ * The connection group having the given identifier, or the root
+ * connection group if the identifier the root identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection group, or if the
+ * connection group does not exist.
+ */
+ public ConnectionGroup retrieveConnectionGroup(UserContext userContext,
+ String identifier) throws GuacamoleException {
+
+ // Use root group if identifier is the standard root identifier
+ if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER))
+ return userContext.getRootConnectionGroup();
+
+ // Pull specified connection group otherwise
+ Directory<ConnectionGroup> directory = userContext.getConnectionGroupDirectory();
+ ConnectionGroup connectionGroup = directory.get(identifier);
+
+ if (connectionGroup == null)
+ throw new GuacamoleResourceNotFoundException("No such connection group: \"" + identifier + "\"");
+
+ return connectionGroup;
+
+ }
+
+ /**
+ * Retrieves a single connection group from the given GuacamoleSession. If
+ * the given identifier is the REST API root identifier, the root
+ * connection group will be returned. The underlying authentication
+ * provider may additionally use a different identifier for root.
+ *
+ * @param session
+ * The GuacamoleSession to retrieve the connection group from.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider that created the
+ * UserContext from which the connection group should be retrieved.
+ * Only one UserContext per User per AuthenticationProvider can exist.
+ *
+ * @param identifier
+ * The identifier of the connection group to retrieve.
+ *
+ * @return
+ * The connection group having the given identifier, or the root
+ * connection group if the identifier is the root identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection group, or if the
+ * connection group does not exist.
+ */
+ public ConnectionGroup retrieveConnectionGroup(GuacamoleSession session,
+ String authProviderIdentifier, String identifier) throws GuacamoleException {
+
+ UserContext userContext = retrieveUserContext(session, authProviderIdentifier);
+ return retrieveConnectionGroup(userContext, identifier);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java b/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java
new file mode 100644
index 0000000..f87ca98
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/PATCH.java
@@ -0,0 +1,40 @@
+/*
+ * 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.rest;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+/**
+ * An annotation for using the HTTP PATCH method in the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("PATCH")
+public @interface PATCH {}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java
new file mode 100644
index 0000000..0193e22
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTExceptionWrapper.java
@@ -0,0 +1,274 @@
+/*
+ * 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.rest;
+
+import com.google.inject.Inject;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.QueryParam;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleResourceNotFoundException;
+import org.apache.guacamole.GuacamoleSecurityException;
+import org.apache.guacamole.GuacamoleUnauthorizedException;
+import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
+import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
+import org.apache.guacamole.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A method interceptor which wraps custom exception handling around methods
+ * which can throw GuacamoleExceptions and which are exposed through the REST
+ * interface. The various types of GuacamoleExceptions are automatically
+ * translated into appropriate HTTP responses, including JSON describing the
+ * error that occurred.
+ *
+ * @author James Muehlner
+ * @author Michael Jumper
+ */
+public class RESTExceptionWrapper implements MethodInterceptor {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger = LoggerFactory.getLogger(RESTExceptionWrapper.class);
+
+ /**
+ * Service for authenticating users and managing their Guacamole sessions.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * Determines whether the given set of annotations describes an HTTP
+ * request parameter of the given name. For a parameter to be associated
+ * with an HTTP request parameter, it must be annotated with either the
+ * <code>@QueryParam</code> or <code>@FormParam</code> annotations.
+ *
+ * @param annotations
+ * The annotations associated with the Java parameter being checked.
+ *
+ * @param name
+ * The name of the HTTP request parameter.
+ *
+ * @return
+ * true if the given set of annotations describes an HTTP request
+ * parameter having the given name, false otherwise.
+ */
+ private boolean isRequestParameter(Annotation[] annotations, String name) {
+
+ // Search annotations for associated HTTP parameters
+ for (Annotation annotation : annotations) {
+
+ // Check if parameter is associated with the HTTP query string
+ if (annotation instanceof QueryParam && name.equals(((QueryParam) annotation).value()))
+ return true;
+
+ // Failing that, check whether the parameter is associated with the
+ // HTTP request body
+ if (annotation instanceof FormParam && name.equals(((FormParam) annotation).value()))
+ return true;
+
+ }
+
+ // No parameter annotations are present
+ return false;
+
+ }
+
+ /**
+ * Returns the authentication token that was passed in the given method
+ * invocation. If the given method invocation is not associated with an
+ * HTTP request (it lacks the appropriate JAX-RS annotations) or there is
+ * no authentication token, null is returned.
+ *
+ * @param invocation
+ * The method invocation whose corresponding authentication token
+ * should be determined.
+ *
+ * @return
+ * The authentication token passed in the given method invocation, or
+ * null if there is no such token.
+ */
+ private String getAuthenticationToken(MethodInvocation invocation) {
+
+ Method method = invocation.getMethod();
+
+ // Get the types and annotations associated with each parameter
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+ Class<?>[] parameterTypes = method.getParameterTypes();
+
+ // The Java standards require these to be parallel arrays
+ assert(parameterAnnotations.length == parameterTypes.length);
+
+ // Iterate through all parameters, looking for the authentication token
+ for (int i = 0; i < parameterTypes.length; i++) {
+
+ // Only inspect String parameters
+ Class<?> parameterType = parameterTypes[i];
+ if (parameterType != String.class)
+ continue;
+
+ // Parameter must be declared as a REST service parameter
+ Annotation[] annotations = parameterAnnotations[i];
+ if (!isRequestParameter(annotations, "token"))
+ continue;
+
+ // The token parameter has been found - return its value
+ Object[] args = invocation.getArguments();
+ return (String) args[i];
+
+ }
+
+ // No token parameter is defined
+ return null;
+
+ }
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+
+ try {
+
+ // Invoke wrapped method
+ try {
+ return invocation.proceed();
+ }
+
+ // Ensure any associated session is invalidated if unauthorized
+ catch (GuacamoleUnauthorizedException e) {
+
+ // Pull authentication token from request
+ String token = getAuthenticationToken(invocation);
+
+ // If there is an associated auth token, invalidate it
+ if (authenticationService.destroyGuacamoleSession(token))
+ logger.debug("Implicitly invalidated session for token \"{}\".", token);
+
+ // Continue with exception processing
+ throw e;
+
+ }
+
+ }
+
+ // Additional credentials are needed
+ catch (GuacamoleInsufficientCredentialsException e) {
+
+ // Generate default message
+ String message = e.getMessage();
+ if (message == null)
+ message = "Permission denied.";
+
+ throw new APIException(
+ APIError.Type.INSUFFICIENT_CREDENTIALS,
+ message,
+ e.getCredentialsInfo().getFields()
+ );
+ }
+
+ // The provided credentials are wrong
+ catch (GuacamoleInvalidCredentialsException e) {
+
+ // Generate default message
+ String message = e.getMessage();
+ if (message == null)
+ message = "Permission denied.";
+
+ throw new APIException(
+ APIError.Type.INVALID_CREDENTIALS,
+ message,
+ e.getCredentialsInfo().getFields()
+ );
+ }
+
+ // Generic permission denied
+ catch (GuacamoleSecurityException e) {
+
+ // Generate default message
+ String message = e.getMessage();
+ if (message == null)
+ message = "Permission denied.";
+
+ throw new APIException(
+ APIError.Type.PERMISSION_DENIED,
+ message
+ );
+
+ }
+
+ // Arbitrary resource not found
+ catch (GuacamoleResourceNotFoundException e) {
+
+ // Generate default message
+ String message = e.getMessage();
+ if (message == null)
+ message = "Not found.";
+
+ throw new APIException(
+ APIError.Type.NOT_FOUND,
+ message
+ );
+
+ }
+
+ // Arbitrary bad requests
+ catch (GuacamoleClientException e) {
+
+ // Generate default message
+ String message = e.getMessage();
+ if (message == null)
+ message = "Invalid request.";
+
+ throw new APIException(
+ APIError.Type.BAD_REQUEST,
+ message
+ );
+
+ }
+
+ // All other errors
+ catch (GuacamoleException e) {
+
+ // Generate default message
+ String message = e.getMessage();
+ if (message == null)
+ message = "Unexpected server error.";
+
+ // Ensure internal errors are logged at the debug level
+ logger.debug("Unexpected exception in REST endpoint.", e);
+
+ throw new APIException(
+ APIError.Type.INTERNAL_ERROR,
+ message
+ );
+
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java
new file mode 100644
index 0000000..0283e63
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java
@@ -0,0 +1,109 @@
+/*
+ * 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.rest;
+
+import com.google.inject.matcher.AbstractMatcher;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import javax.ws.rs.HttpMethod;
+import org.apache.guacamole.GuacamoleException;
+
+/**
+ * A Guice Matcher which matches only methods which throw GuacamoleException
+ * (or a subclass thereof) and are explicitly annotated as with an HTTP method
+ * annotation like <code>@GET</code> or <code>@POST</code>. Any method which
+ * throws GuacamoleException and is annotated with an annotation that is
+ * annotated with <code>@HttpMethod</code> will match.
+ *
+ * @author Michael Jumper
+ */
+public class RESTMethodMatcher extends AbstractMatcher<Method> {
+
+ /**
+ * Returns whether the given method throws the specified exception type,
+ * including any subclasses of that type.
+ *
+ * @param method
+ * The method to test.
+ *
+ * @param exceptionType
+ * The exception type to test for.
+ *
+ * @return
+ * true if the given method throws an exception of the specified type,
+ * false otherwise.
+ */
+ private boolean methodThrowsException(Method method,
+ Class<? extends Exception> exceptionType) {
+
+ // Check whether the method throws an exception of the specified type
+ for (Class<?> thrownType : method.getExceptionTypes()) {
+ if (exceptionType.isAssignableFrom(thrownType))
+ return true;
+ }
+
+ // No such exception is declared to be thrown
+ return false;
+
+ }
+
+ /**
+ * Returns whether the given method is annotated as a REST method. A REST
+ * method is annotated with an annotation which is annotated with
+ * <code>@HttpMethod</code>.
+ *
+ * @param method
+ * The method to test.
+ *
+ * @return
+ * true if the given method is annotated as a REST method, false
+ * otherwise.
+ */
+ private boolean isRESTMethod(Method method) {
+
+ // Check whether the required REST annotations are present
+ for (Annotation annotation : method.getAnnotations()) {
+
+ // A method is a REST method if it is annotated with @HttpMethod
+ Class<? extends Annotation> annotationType = annotation.annotationType();
+ if (annotationType.isAnnotationPresent(HttpMethod.class))
+ return true;
+
+ }
+
+ // The method is not an HTTP method
+ return false;
+
+ }
+
+ @Override
+ public boolean matches(Method method) {
+
+ // Guacamole REST methods are REST methods which throw
+ // GuacamoleExceptions
+ return isRESTMethod(method)
+ && methodThrowsException(method, GuacamoleException.class);
+
+ }
+
+}