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:12 UTC
[03/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/rest/connection/ConnectionRESTService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java
new file mode 100644
index 0000000..b301082
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java
@@ -0,0 +1,349 @@
+/*
+ * 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.connection;
+
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleSecurityException;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.net.auth.ConnectionRecord;
+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.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
+import org.apache.guacamole.net.auth.permission.SystemPermission;
+import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
+import org.apache.guacamole.GuacamoleSession;
+import org.apache.guacamole.rest.ObjectRetrievalService;
+import org.apache.guacamole.rest.auth.AuthenticationService;
+import org.apache.guacamole.rest.history.APIConnectionRecord;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling connection CRUD operations.
+ *
+ * @author James Muehlner
+ */
+@Path("/data/{dataSource}/connections")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ConnectionRESTService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(ConnectionRESTService.class);
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * Service for convenient retrieval of objects.
+ */
+ @Inject
+ private ObjectRetrievalService retrievalService;
+
+ /**
+ * Retrieves an individual connection.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection to be retrieved.
+ *
+ * @param connectionID
+ * The identifier of the connection to retrieve.
+ *
+ * @return
+ * The connection having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection.
+ */
+ @GET
+ @Path("/{connectionID}")
+ public APIConnection getConnection(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionID") String connectionID)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+
+ // Retrieve the requested connection
+ return new APIConnection(retrievalService.retrieveConnection(session, authProviderIdentifier, connectionID));
+
+ }
+
+ /**
+ * Retrieves the parameters associated with a single connection.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection whose parameters are to be
+ * retrieved.
+ *
+ * @param connectionID
+ * The identifier of the connection.
+ *
+ * @return
+ * A map of parameter name/value pairs.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection parameters.
+ */
+ @GET
+ @Path("/{connectionID}/parameters")
+ public Map<String, String> getConnectionParameters(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionID") String connectionID)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+ User self = userContext.self();
+
+ // Retrieve permission sets
+ SystemPermissionSet systemPermissions = self.getSystemPermissions();
+ ObjectPermissionSet connectionPermissions = self.getConnectionPermissions();
+
+ // Deny access if adminstrative or update permission is missing
+ if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER)
+ && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, connectionID))
+ throw new GuacamoleSecurityException("Permission to read connection parameters denied.");
+
+ // Retrieve the requested connection
+ Connection connection = retrievalService.retrieveConnection(userContext, connectionID);
+
+ // Retrieve connection configuration
+ GuacamoleConfiguration config = connection.getConfiguration();
+
+ // Return parameter map
+ return config.getParameters();
+
+ }
+
+ /**
+ * Retrieves the usage history of a single connection.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection whose history is to be
+ * retrieved.
+ *
+ * @param connectionID
+ * The identifier of the connection.
+ *
+ * @return
+ * A list of connection records, describing the start and end times of
+ * various usages of this connection.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection history.
+ */
+ @GET
+ @Path("/{connectionID}/history")
+ public List<APIConnectionRecord> getConnectionHistory(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionID") String connectionID)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+
+ // Retrieve the requested connection
+ Connection connection = retrievalService.retrieveConnection(session, authProviderIdentifier, connectionID);
+
+ // Retrieve the requested connection's history
+ List<APIConnectionRecord> apiRecords = new ArrayList<APIConnectionRecord>();
+ for (ConnectionRecord record : connection.getHistory())
+ apiRecords.add(new APIConnectionRecord(record));
+
+ // Return the converted history
+ return apiRecords;
+
+ }
+
+ /**
+ * Deletes an individual connection.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection to be deleted.
+ *
+ * @param connectionID
+ * The identifier of the connection to delete.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while deleting the connection.
+ */
+ @DELETE
+ @Path("/{connectionID}")
+ public void deleteConnection(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionID") String connectionID)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Get the connection directory
+ Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
+
+ // Delete the specified connection
+ connectionDirectory.remove(connectionID);
+
+ }
+
+ /**
+ * Creates a new connection and returns the new connection, with identifier
+ * field populated.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext in which the connection is to be created.
+ *
+ * @param connection
+ * The connection to create.
+ *
+ * @return
+ * The new connection.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while creating the connection.
+ */
+ @POST
+ public APIConnection createConnection(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ APIConnection connection) throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Validate that connection data was provided
+ if (connection == null)
+ throw new GuacamoleClientException("Connection JSON must be submitted when creating connections.");
+
+ // Add the new connection
+ Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
+ connectionDirectory.add(new APIConnectionWrapper(connection));
+
+ // Return the new connection
+ return connection;
+
+ }
+
+ /**
+ * Updates an existing connection. If the parent identifier of the
+ * connection is changed, the connection will also be moved to the new
+ * parent group.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection to be updated.
+ *
+ * @param connectionID
+ * The identifier of the connection to update.
+ *
+ * @param connection
+ * The connection data to update the specified connection with.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while updating the connection.
+ */
+ @PUT
+ @Path("/{connectionID}")
+ public void updateConnection(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionID") String connectionID,
+ APIConnection connection) throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Validate that connection data was provided
+ if (connection == null)
+ throw new GuacamoleClientException("Connection JSON must be submitted when updating connections.");
+
+ // Get the connection directory
+ Directory<Connection> connectionDirectory = userContext.getConnectionDirectory();
+
+ // Retrieve connection to update
+ Connection existingConnection = retrievalService.retrieveConnection(userContext, connectionID);
+
+ // Build updated configuration
+ GuacamoleConfiguration config = new GuacamoleConfiguration();
+ config.setProtocol(connection.getProtocol());
+ config.setParameters(connection.getParameters());
+
+ // Update the connection
+ existingConnection.setConfiguration(config);
+ existingConnection.setParentIdentifier(connection.getParentIdentifier());
+ existingConnection.setName(connection.getName());
+ existingConnection.setAttributes(connection.getAttributes());
+ connectionDirectory.update(existingConnection);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/connection/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/package-info.java
new file mode 100644
index 0000000..d80155f
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to the connection manipulation aspect of the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.connection;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroup.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroup.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroup.java
new file mode 100644
index 0000000..67dea8e
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroup.java
@@ -0,0 +1,272 @@
+/*
+ * 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.connectiongroup;
+
+import java.util.Collection;
+import java.util.Map;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.ConnectionGroup.Type;
+import org.apache.guacamole.rest.connection.APIConnection;
+
+/**
+ * A simple connection group to expose through the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+public class APIConnectionGroup {
+
+ /**
+ * The identifier of the root connection group.
+ */
+ public static final String ROOT_IDENTIFIER = "ROOT";
+
+ /**
+ * The name of this connection group.
+ */
+ private String name;
+
+ /**
+ * The identifier of this connection group.
+ */
+ private String identifier;
+
+ /**
+ * The identifier of the parent connection group for this connection group.
+ */
+ private String parentIdentifier;
+
+ /**
+ * The type of this connection group.
+ */
+ private Type type;
+
+ /**
+ * The count of currently active connections using this connection group.
+ */
+ private int activeConnections;
+
+ /**
+ * All child connection groups. If children are not being queried, this may
+ * be omitted.
+ */
+ private Collection<APIConnectionGroup> childConnectionGroups;
+
+ /**
+ * All child connections. If children are not being queried, this may be
+ * omitted.
+ */
+ private Collection<APIConnection> childConnections;
+
+ /**
+ * Map of all associated attributes by attribute identifier.
+ */
+ private Map<String, String> attributes;
+
+ /**
+ * Create an empty APIConnectionGroup.
+ */
+ public APIConnectionGroup() {}
+
+ /**
+ * Create a new APIConnectionGroup from the given ConnectionGroup record.
+ *
+ * @param connectionGroup The ConnectionGroup record to initialize this
+ * APIConnectionGroup from.
+ */
+ public APIConnectionGroup(ConnectionGroup connectionGroup) {
+
+ // Set connection group information
+ this.identifier = connectionGroup.getIdentifier();
+ this.parentIdentifier = connectionGroup.getParentIdentifier();
+ this.name = connectionGroup.getName();
+ this.type = connectionGroup.getType();
+ this.activeConnections = connectionGroup.getActiveConnections();
+
+ // Associate any attributes
+ this.attributes = connectionGroup.getAttributes();
+
+ }
+
+ /**
+ * Returns the name of this connection group.
+ * @return The name of this connection group.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of this connection group.
+ * @param name The name of this connection group.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the identifier of this connection group.
+ * @return The identifier of this connection group.
+ */
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Set the identifier of this connection group.
+ * @param identifier The identifier of this connection group.
+ */
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Returns the unique identifier for this connection group.
+ * @return The unique identifier for this connection group.
+ */
+ public String getParentIdentifier() {
+ return parentIdentifier;
+ }
+ /**
+ * Sets the parent connection group identifier for this connection group.
+ * @param parentIdentifier The parent connection group identifier
+ * for this connection group.
+ */
+ public void setParentIdentifier(String parentIdentifier) {
+ this.parentIdentifier = parentIdentifier;
+ }
+
+ /**
+ * Returns the type of this connection group.
+ * @return The type of this connection group.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Set the type of this connection group.
+ * @param type The Type of this connection group.
+ */
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns a collection of all child connection groups, or null if children
+ * have not been queried.
+ *
+ * @return
+ * A collection of all child connection groups, or null if children
+ * have not been queried.
+ */
+ public Collection<APIConnectionGroup> getChildConnectionGroups() {
+ return childConnectionGroups;
+ }
+
+ /**
+ * Sets the collection of all child connection groups to the given
+ * collection, which may be null if children have not been queried.
+ *
+ * @param childConnectionGroups
+ * The collection containing all child connection groups of this
+ * connection group, or null if children have not been queried.
+ */
+ public void setChildConnectionGroups(Collection<APIConnectionGroup> childConnectionGroups) {
+ this.childConnectionGroups = childConnectionGroups;
+ }
+
+ /**
+ * Returns a collection of all child connections, or null if children have
+ * not been queried.
+ *
+ * @return
+ * A collection of all child connections, or null if children have not
+ * been queried.
+ */
+ public Collection<APIConnection> getChildConnections() {
+ return childConnections;
+ }
+
+ /**
+ * Sets the collection of all child connections to the given collection,
+ * which may be null if children have not been queried.
+ *
+ * @param childConnections
+ * The collection containing all child connections of this connection
+ * group, or null if children have not been queried.
+ */
+ public void setChildConnections(Collection<APIConnection> childConnections) {
+ this.childConnections = childConnections;
+ }
+
+ /**
+ * Returns the number of currently active connections using this
+ * connection group.
+ *
+ * @return
+ * The number of currently active usages of this connection group.
+ */
+ public int getActiveConnections() {
+ return activeConnections;
+ }
+
+ /**
+ * Set the number of currently active connections using this connection
+ * group.
+ *
+ * @param activeConnections
+ * The number of currently active usages of this connection group.
+ */
+ public void setActiveUsers(int activeConnections) {
+ this.activeConnections = activeConnections;
+ }
+
+ /**
+ * Returns a map of all attributes associated with this connection group.
+ * Each entry key is the attribute identifier, while each value is the
+ * attribute value itself.
+ *
+ * @return
+ * The attribute map for this connection group.
+ */
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Sets the map of all attributes associated with this connection group.
+ * Each entry key is the attribute identifier, while each value is the
+ * attribute value itself.
+ *
+ * @param attributes
+ * The attribute map for this connection group.
+ */
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroupWrapper.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroupWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroupWrapper.java
new file mode 100644
index 0000000..0c8198a
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/APIConnectionGroupWrapper.java
@@ -0,0 +1,124 @@
+/*
+ * 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.connectiongroup;
+
+import java.util.Map;
+import java.util.Set;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.GuacamoleTunnel;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.protocol.GuacamoleClientInformation;
+
+/**
+ * A wrapper to make an APIConnection look like a ConnectionGroup.
+ * Useful where a org.apache.guacamole.net.auth.ConnectionGroup is required.
+ *
+ * @author James Muehlner
+ */
+public class APIConnectionGroupWrapper implements ConnectionGroup {
+
+ /**
+ * The wrapped APIConnectionGroup.
+ */
+ private final APIConnectionGroup apiConnectionGroup;
+
+ /**
+ * Create a new APIConnectionGroupWrapper to wrap the given
+ * APIConnectionGroup as a ConnectionGroup.
+ * @param apiConnectionGroup the APIConnectionGroup to wrap.
+ */
+ public APIConnectionGroupWrapper(APIConnectionGroup apiConnectionGroup) {
+ this.apiConnectionGroup = apiConnectionGroup;
+ }
+
+ @Override
+ public String getName() {
+ return apiConnectionGroup.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ apiConnectionGroup.setName(name);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return apiConnectionGroup.getIdentifier();
+ }
+
+ @Override
+ public void setIdentifier(String identifier) {
+ apiConnectionGroup.setIdentifier(identifier);
+ }
+
+ @Override
+ public String getParentIdentifier() {
+ return apiConnectionGroup.getParentIdentifier();
+ }
+
+ @Override
+ public void setParentIdentifier(String parentIdentifier) {
+ apiConnectionGroup.setParentIdentifier(parentIdentifier);
+ }
+
+ @Override
+ public void setType(Type type) {
+ apiConnectionGroup.setType(type);
+ }
+
+ @Override
+ public Type getType() {
+ return apiConnectionGroup.getType();
+ }
+
+ @Override
+ public int getActiveConnections() {
+ return apiConnectionGroup.getActiveConnections();
+ }
+
+ @Override
+ public Set<String> getConnectionIdentifiers() {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+ @Override
+ public Set<String> getConnectionGroupIdentifiers() {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+ @Override
+ public Map<String, String> getAttributes() {
+ return apiConnectionGroup.getAttributes();
+ }
+
+ @Override
+ public void setAttributes(Map<String, String> attributes) {
+ apiConnectionGroup.setAttributes(attributes);
+ }
+
+ @Override
+ public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java
new file mode 100644
index 0000000..84e5283
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java
@@ -0,0 +1,287 @@
+/*
+ * 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.connectiongroup;
+
+import com.google.inject.Inject;
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.GuacamoleSession;
+import org.apache.guacamole.rest.ObjectRetrievalService;
+import org.apache.guacamole.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling connection group CRUD operations.
+ *
+ * @author James Muehlner
+ */
+@Path("/data/{dataSource}/connectionGroups")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ConnectionGroupRESTService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(ConnectionGroupRESTService.class);
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * Service for convenient retrieval of objects.
+ */
+ @Inject
+ private ObjectRetrievalService retrievalService;
+
+ /**
+ * Gets an individual connection group.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection group to be retrieved.
+ *
+ * @param connectionGroupID
+ * The ID of the connection group to retrieve.
+ *
+ * @return
+ * The connection group, without any descendants.
+ *
+ * @throws GuacamoleException
+ * If a problem is encountered while retrieving the connection group.
+ */
+ @GET
+ @Path("/{connectionGroupID}")
+ public APIConnectionGroup getConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionGroupID") String connectionGroupID)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+
+ // Retrieve the requested connection group
+ return new APIConnectionGroup(retrievalService.retrieveConnectionGroup(session, authProviderIdentifier, connectionGroupID));
+
+ }
+
+ /**
+ * Gets an individual connection group and all children.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection group to be retrieved.
+ *
+ * @param connectionGroupID
+ * The ID of the connection group to retrieve.
+ *
+ * @param permissions
+ * If specified and non-empty, limit the returned list to only those
+ * connections for which the current user has any of the given
+ * permissions. Otherwise, all visible connections are returned.
+ * Connection groups are unaffected by this parameter.
+ *
+ * @return
+ * The requested connection group, including all descendants.
+ *
+ * @throws GuacamoleException
+ * If a problem is encountered while retrieving the connection group or
+ * its descendants.
+ */
+ @GET
+ @Path("/{connectionGroupID}/tree")
+ public APIConnectionGroup getConnectionGroupTree(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionGroupID") String connectionGroupID,
+ @QueryParam("permission") List<ObjectPermission.Type> permissions)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Retrieve the requested tree, filtering by the given permissions
+ ConnectionGroup treeRoot = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID);
+ ConnectionGroupTree tree = new ConnectionGroupTree(userContext, treeRoot, permissions);
+
+ // Return tree as a connection group
+ return tree.getRootAPIConnectionGroup();
+
+ }
+
+ /**
+ * Deletes an individual connection group.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection group to be deleted.
+ *
+ * @param connectionGroupID
+ * The identifier of the connection group to delete.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while deleting the connection group.
+ */
+ @DELETE
+ @Path("/{connectionGroupID}")
+ public void deleteConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionGroupID") String connectionGroupID)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Get the connection group directory
+ Directory<ConnectionGroup> connectionGroupDirectory = userContext.getConnectionGroupDirectory();
+
+ // Delete the connection group
+ connectionGroupDirectory.remove(connectionGroupID);
+
+ }
+
+ /**
+ * Creates a new connection group and returns the new connection group,
+ * with identifier field populated.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext in which the connection group is to be created.
+ *
+ * @param connectionGroup
+ * The connection group to create.
+ *
+ * @return
+ * The new connection group.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while creating the connection group.
+ */
+ @POST
+ public APIConnectionGroup createConnectionGroup(
+ @QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ APIConnectionGroup connectionGroup) throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Validate that connection group data was provided
+ if (connectionGroup == null)
+ throw new GuacamoleClientException("Connection group JSON must be submitted when creating connections groups.");
+
+ // Add the new connection group
+ Directory<ConnectionGroup> connectionGroupDirectory = userContext.getConnectionGroupDirectory();
+ connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup));
+
+ // Return the new connection group
+ return connectionGroup;
+
+ }
+
+ /**
+ * Updates a connection group. If the parent identifier of the
+ * connection group is changed, the connection group will also be moved to
+ * the new parent group.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection group to be updated.
+ *
+ * @param connectionGroupID
+ * The identifier of the existing connection group to update.
+ *
+ * @param connectionGroup
+ * The data to update the existing connection group with.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while updating the connection group.
+ */
+ @PUT
+ @Path("/{connectionGroupID}")
+ public void updateConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @PathParam("connectionGroupID") String connectionGroupID,
+ APIConnectionGroup connectionGroup)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Validate that connection group data was provided
+ if (connectionGroup == null)
+ throw new GuacamoleClientException("Connection group JSON must be submitted when updating connection groups.");
+
+ // Get the connection group directory
+ Directory<ConnectionGroup> connectionGroupDirectory = userContext.getConnectionGroupDirectory();
+
+ // Retrieve connection group to update
+ ConnectionGroup existingConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID);
+
+ // Update the connection group
+ existingConnectionGroup.setName(connectionGroup.getName());
+ existingConnectionGroup.setParentIdentifier(connectionGroup.getParentIdentifier());
+ existingConnectionGroup.setType(connectionGroup.getType());
+ existingConnectionGroup.setAttributes(connectionGroup.getAttributes());
+ connectionGroupDirectory.update(existingConnectionGroup);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
new file mode 100644
index 0000000..75e46ae
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
@@ -0,0 +1,259 @@
+/*
+ * 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.connectiongroup;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
+import org.apache.guacamole.rest.connection.APIConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides access to the entire tree of connection groups and their
+ * connections.
+ *
+ * @author Michael Jumper
+ */
+public class ConnectionGroupTree {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(ConnectionGroupTree.class);
+
+ /**
+ * The context of the user obtaining this tree.
+ */
+ private final UserContext userContext;
+
+ /**
+ * The root connection group as an APIConnectionGroup.
+ */
+ private final APIConnectionGroup rootAPIGroup;
+
+ /**
+ * All connection groups that have been retrieved, stored by their
+ * identifiers.
+ */
+ private final Map<String, APIConnectionGroup> retrievedGroups =
+ new HashMap<String, APIConnectionGroup>();
+
+ /**
+ * Adds each of the provided connections to the current tree as children
+ * of their respective parents. The parent connection groups must already
+ * be added.
+ *
+ * @param connections
+ * The connections to add to the tree.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while adding the connection to the tree.
+ */
+ private void addConnections(Collection<Connection> connections)
+ throws GuacamoleException {
+
+ // Add each connection to the tree
+ for (Connection connection : connections) {
+
+ // Retrieve the connection's parent group
+ APIConnectionGroup parent = retrievedGroups.get(connection.getParentIdentifier());
+ if (parent != null) {
+
+ Collection<APIConnection> children = parent.getChildConnections();
+
+ // Create child collection if it does not yet exist
+ if (children == null) {
+ children = new ArrayList<APIConnection>();
+ parent.setChildConnections(children);
+ }
+
+ // Add child
+ children.add(new APIConnection(connection));
+
+ }
+
+ // Warn of internal consistency issues
+ else
+ logger.debug("Connection \"{}\" cannot be added to the tree: parent \"{}\" does not actually exist.",
+ connection.getIdentifier(),
+ connection.getParentIdentifier());
+
+ } // end for each connection
+
+ }
+
+ /**
+ * Adds each of the provided connection groups to the current tree as
+ * children of their respective parents. The parent connection groups must
+ * already be added.
+ *
+ * @param connectionGroups
+ * The connection groups to add to the tree.
+ */
+ private void addConnectionGroups(Collection<ConnectionGroup> connectionGroups) {
+
+ // Add each connection group to the tree
+ for (ConnectionGroup connectionGroup : connectionGroups) {
+
+ // Retrieve the connection group's parent group
+ APIConnectionGroup parent = retrievedGroups.get(connectionGroup.getParentIdentifier());
+ if (parent != null) {
+
+ Collection<APIConnectionGroup> children = parent.getChildConnectionGroups();
+
+ // Create child collection if it does not yet exist
+ if (children == null) {
+ children = new ArrayList<APIConnectionGroup>();
+ parent.setChildConnectionGroups(children);
+ }
+
+ // Add child
+ APIConnectionGroup apiConnectionGroup = new APIConnectionGroup(connectionGroup);
+ retrievedGroups.put(connectionGroup.getIdentifier(), apiConnectionGroup);
+ children.add(apiConnectionGroup);
+
+ }
+
+ // Warn of internal consistency issues
+ else
+ logger.debug("Connection group \"{}\" cannot be added to the tree: parent \"{}\" does not actually exist.",
+ connectionGroup.getIdentifier(),
+ connectionGroup.getParentIdentifier());
+
+ } // end for each connection group
+
+ }
+
+ /**
+ * Adds all descendants of the given parent groups to their corresponding
+ * parents already stored under root.
+ *
+ * @param parents
+ * The parents whose descendants should be added to the tree.
+ *
+ * @param permissions
+ * If specified and non-empty, limit added connections to only
+ * connections for which the current user has any of the given
+ * permissions. Otherwise, all visible connections are added.
+ * Connection groups are unaffected by this parameter.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the descendants.
+ */
+ private void addDescendants(Collection<ConnectionGroup> parents,
+ List<ObjectPermission.Type> permissions)
+ throws GuacamoleException {
+
+ // If no parents, nothing to do
+ if (parents.isEmpty())
+ return;
+
+ Collection<String> childConnectionIdentifiers = new ArrayList<String>();
+ Collection<String> childConnectionGroupIdentifiers = new ArrayList<String>();
+
+ // Build lists of identifiers for retrieval
+ for (ConnectionGroup parent : parents) {
+ childConnectionIdentifiers.addAll(parent.getConnectionIdentifiers());
+ childConnectionGroupIdentifiers.addAll(parent.getConnectionGroupIdentifiers());
+ }
+
+ // Filter identifiers based on permissions, if requested
+ if (permissions != null && !permissions.isEmpty()) {
+ ObjectPermissionSet permissionSet = userContext.self().getConnectionPermissions();
+ childConnectionIdentifiers = permissionSet.getAccessibleObjects(permissions, childConnectionIdentifiers);
+ }
+
+ // Retrieve child connections
+ if (!childConnectionIdentifiers.isEmpty()) {
+ Collection<Connection> childConnections = userContext.getConnectionDirectory().getAll(childConnectionIdentifiers);
+ addConnections(childConnections);
+ }
+
+ // Retrieve child connection groups
+ if (!childConnectionGroupIdentifiers.isEmpty()) {
+ Collection<ConnectionGroup> childConnectionGroups = userContext.getConnectionGroupDirectory().getAll(childConnectionGroupIdentifiers);
+ addConnectionGroups(childConnectionGroups);
+ addDescendants(childConnectionGroups, permissions);
+ }
+
+ }
+
+ /**
+ * Creates a new connection group tree using the given connection group as
+ * the tree root.
+ *
+ * @param userContext
+ * The context of the user obtaining the connection group tree.
+ *
+ * @param root
+ * The connection group to use as the root of this connection group
+ * tree.
+ *
+ * @param permissions
+ * If specified and non-empty, limit the contents of the tree to only
+ * those connections for which the current user has any of the given
+ * permissions. Otherwise, all visible connections are returned.
+ * Connection groups are unaffected by this parameter.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the tree of connection groups
+ * and their descendants.
+ */
+ public ConnectionGroupTree(UserContext userContext, ConnectionGroup root,
+ List<ObjectPermission.Type> permissions) throws GuacamoleException {
+
+ this.userContext = userContext;
+
+ // Store root of tree
+ this.rootAPIGroup = new APIConnectionGroup(root);
+ retrievedGroups.put(root.getIdentifier(), this.rootAPIGroup);
+
+ // Add all descendants
+ addDescendants(Collections.singleton(root), permissions);
+
+ }
+
+ /**
+ * Returns the entire connection group tree as an APIConnectionGroup. The
+ * returned APIConnectionGroup is the root group and will contain all
+ * descendant connection groups and connections, arranged hierarchically.
+ *
+ * @return
+ * The root connection group, containing the entire connection group
+ * tree and all connections.
+ */
+ public APIConnectionGroup getRootAPIConnectionGroup() {
+ return rootAPIGroup;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/package-info.java
new file mode 100644
index 0000000..9152989
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to the connection group manipulation aspect
+ * of the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.connectiongroup;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java
new file mode 100644
index 0000000..e5d92a0
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecord.java
@@ -0,0 +1,163 @@
+/*
+ * 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.history;
+
+import java.util.Date;
+import org.apache.guacamole.net.auth.ConnectionRecord;
+
+/**
+ * A connection record which may be exposed through the REST endpoints.
+ *
+ * @author Michael Jumper
+ */
+public class APIConnectionRecord {
+
+ /**
+ * The identifier of the connection associated with this record.
+ */
+ private final String connectionIdentifier;
+
+ /**
+ * The identifier of the connection associated with this record.
+ */
+ private final String connectionName;
+
+ /**
+ * The date and time the connection began.
+ */
+ private final Date startDate;
+
+ /**
+ * The date and time the connection ended, or null if the connection is
+ * still running or if the end time is unknown.
+ */
+ private final Date endDate;
+
+ /**
+ * The host from which the connection originated, if known.
+ */
+ private final String remoteHost;
+
+ /**
+ * The name of the user who used or is using the connection.
+ */
+ private final String username;
+
+ /**
+ * Whether the connection is currently active.
+ */
+ private final boolean active;
+
+ /**
+ * Creates a new APIConnectionRecord, copying the data from the given
+ * record.
+ *
+ * @param record
+ * The record to copy data from.
+ */
+ public APIConnectionRecord(ConnectionRecord record) {
+ this.connectionIdentifier = record.getConnectionIdentifier();
+ this.connectionName = record.getConnectionName();
+ this.startDate = record.getStartDate();
+ this.endDate = record.getEndDate();
+ this.remoteHost = record.getRemoteHost();
+ this.username = record.getUsername();
+ this.active = record.isActive();
+ }
+
+ /**
+ * Returns the identifier of the connection associated with this
+ * record.
+ *
+ * @return
+ * The identifier of the connection associated with this record.
+ */
+ public String getConnectionIdentifier() {
+ return connectionIdentifier;
+ }
+
+ /**
+ * Returns the name of the connection associated with this record.
+ *
+ * @return
+ * The name of the connection associated with this record.
+ */
+ public String getConnectionName() {
+ return connectionName;
+ }
+
+ /**
+ * Returns the date and time the connection began.
+ *
+ * @return
+ * The date and time the connection began.
+ */
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ /**
+ * Returns the date and time the connection ended, if applicable.
+ *
+ * @return
+ * The date and time the connection ended, or null if the connection is
+ * still running or if the end time is unknown.
+ */
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ /**
+ * Returns the remote host from which this connection originated.
+ *
+ * @return
+ * The remote host from which this connection originated.
+ */
+ public String getRemoteHost() {
+ return remoteHost;
+ }
+
+ /**
+ * Returns the name of the user who used or is using the connection at the
+ * times given by this connection record.
+ *
+ * @return
+ * The name of the user who used or is using the associated connection.
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Returns whether the connection associated with this record is still
+ * active.
+ *
+ * @return
+ * true if the connection associated with this record is still active,
+ * false otherwise.
+ */
+ public boolean isActive() {
+ return active;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecordSortPredicate.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecordSortPredicate.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecordSortPredicate.java
new file mode 100644
index 0000000..7006f58
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIConnectionRecordSortPredicate.java
@@ -0,0 +1,148 @@
+/*
+ * 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.history;
+
+import org.apache.guacamole.net.auth.ConnectionRecordSet;
+import org.apache.guacamole.rest.APIError;
+import org.apache.guacamole.rest.APIException;
+
+/**
+ * A sort predicate which species the property to use when sorting connection
+ * records, along with the sort order.
+ *
+ * @author Michael Jumper
+ */
+public class APIConnectionRecordSortPredicate {
+
+ /**
+ * The prefix which will be included before the name of a sortable property
+ * to indicate that the sort order is descending, not ascending.
+ */
+ public static final String DESCENDING_PREFIX = "-";
+
+ /**
+ * All possible property name strings and their corresponding
+ * ConnectionRecordSet.SortableProperty values.
+ */
+ public enum SortableProperty {
+
+ /**
+ * The date that the connection associated with the connection record
+ * began (connected).
+ */
+ startDate(ConnectionRecordSet.SortableProperty.START_DATE);
+
+ /**
+ * The ConnectionRecordSet.SortableProperty that this property name
+ * string represents.
+ */
+ public final ConnectionRecordSet.SortableProperty recordProperty;
+
+ /**
+ * Creates a new SortableProperty which associates the property name
+ * string (identical to its own name) with the given
+ * ConnectionRecordSet.SortableProperty value.
+ *
+ * @param recordProperty
+ * The ConnectionRecordSet.SortableProperty value to associate with
+ * the new SortableProperty.
+ */
+ SortableProperty(ConnectionRecordSet.SortableProperty recordProperty) {
+ this.recordProperty = recordProperty;
+ }
+
+ }
+
+ /**
+ * The property to use when sorting ConnectionRecords.
+ */
+ private ConnectionRecordSet.SortableProperty property;
+
+ /**
+ * Whether the requested sort order is descending (true) or ascending
+ * (false).
+ */
+ private boolean descending;
+
+ /**
+ * Parses the given string value, determining the requested sort property
+ * and ordering. Possible values consist of any valid property name, and
+ * may include an optional prefix to denote descending sort order. Each
+ * possible property name is enumerated by the SortableValue enum.
+ *
+ * @param value
+ * The sort predicate string to parse, which must consist ONLY of a
+ * valid property name, possibly preceded by the DESCENDING_PREFIX.
+ *
+ * @throws APIException
+ * If the provided sort predicate string is invalid.
+ */
+ public APIConnectionRecordSortPredicate(String value)
+ throws APIException {
+
+ // Parse whether sort order is descending
+ if (value.startsWith(DESCENDING_PREFIX)) {
+ descending = true;
+ value = value.substring(DESCENDING_PREFIX.length());
+ }
+
+ // Parse sorting property into ConnectionRecordSet.SortableProperty
+ try {
+ this.property = SortableProperty.valueOf(value).recordProperty;
+ }
+
+ // Bail out if sort property is not valid
+ catch (IllegalArgumentException e) {
+ throw new APIException(
+ APIError.Type.BAD_REQUEST,
+ String.format("Invalid sort property: \"%s\"", value)
+ );
+ }
+
+ }
+
+ /**
+ * Returns the SortableProperty defined by ConnectionRecordSet which
+ * represents the property requested.
+ *
+ * @return
+ * The ConnectionRecordSet.SortableProperty which refers to the same
+ * property as the string originally provided when this
+ * APIConnectionRecordSortPredicate was created.
+ */
+ public ConnectionRecordSet.SortableProperty getProperty() {
+ return property;
+ }
+
+ /**
+ * Returns whether the requested sort order is descending.
+ *
+ * @return
+ * true if the sort order is descending, false if the sort order is
+ * ascending.
+ */
+ public boolean isDescending() {
+ return descending;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java
new file mode 100644
index 0000000..f5d4651
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java
@@ -0,0 +1,147 @@
+/*
+ * 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.history;
+
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.ConnectionRecord;
+import org.apache.guacamole.net.auth.ConnectionRecordSet;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.GuacamoleSession;
+import org.apache.guacamole.rest.ObjectRetrievalService;
+import org.apache.guacamole.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for retrieving and managing the history records of Guacamole
+ * objects.
+ *
+ * @author Michael Jumper
+ */
+@Path("/data/{dataSource}/history")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class HistoryRESTService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(HistoryRESTService.class);
+
+ /**
+ * The maximum number of history records to return in any one response.
+ */
+ private static final int MAXIMUM_HISTORY_SIZE = 1000;
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * Service for convenient retrieval of objects.
+ */
+ @Inject
+ private ObjectRetrievalService retrievalService;
+
+ /**
+ * Retrieves the usage history for all connections, restricted by optional
+ * filter parameters.
+ *
+ * @param authToken
+ * The authentication token that is used to authenticate the user
+ * performing the operation.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider associated with
+ * the UserContext containing the connection whose history is to be
+ * retrieved.
+ *
+ * @param requiredContents
+ * The set of strings that each must occur somewhere within the
+ * returned connection records, whether within the associated username,
+ * the name of the associated connection, or any associated date. If
+ * non-empty, any connection record not matching each of the strings
+ * within the collection will be excluded from the results.
+ *
+ * @param sortPredicates
+ * A list of predicates to apply while sorting the resulting connection
+ * records, describing the properties involved and the sort order for
+ * those properties.
+ *
+ * @return
+ * A list of connection records, describing the start and end times of
+ * various usages of this connection.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection history.
+ */
+ @GET
+ @Path("/connections")
+ public List<APIConnectionRecord> getConnectionHistory(@QueryParam("token") String authToken,
+ @PathParam("dataSource") String authProviderIdentifier,
+ @QueryParam("contains") List<String> requiredContents,
+ @QueryParam("order") List<APIConnectionRecordSortPredicate> sortPredicates)
+ throws GuacamoleException {
+
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
+
+ // Retrieve overall connection history
+ ConnectionRecordSet history = userContext.getConnectionHistory();
+
+ // Restrict to records which contain the specified strings
+ for (String required : requiredContents) {
+ if (!required.isEmpty())
+ history = history.contains(required);
+ }
+
+ // Sort according to specified ordering
+ for (APIConnectionRecordSortPredicate predicate : sortPredicates)
+ history = history.sort(predicate.getProperty(), predicate.isDescending());
+
+ // Limit to maximum result size
+ history = history.limit(MAXIMUM_HISTORY_SIZE);
+
+ // Convert record set to collection of API connection records
+ List<APIConnectionRecord> apiRecords = new ArrayList<APIConnectionRecord>();
+ for (ConnectionRecord record : history.asCollection())
+ apiRecords.add(new APIConnectionRecord(record));
+
+ // Return the converted history
+ return apiRecords;
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/history/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/package-info.java
new file mode 100644
index 0000000..ed2d0fc
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/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 related to retrieval or maintenance of history records using the
+ * Guacamole REST API.
+ */
+package org.apache.guacamole.rest.history;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java
new file mode 100644
index 0000000..2595ae3
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/language/LanguageRESTService.java
@@ -0,0 +1,63 @@
+/*
+ * 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.language;
+
+import com.google.inject.Inject;
+import java.util.Map;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.extension.LanguageResourceService;
+
+
+/**
+ * A REST Service for handling the listing of languages.
+ *
+ * @author James Muehlner
+ */
+@Path("/languages")
+@Produces(MediaType.APPLICATION_JSON)
+public class LanguageRESTService {
+
+ /**
+ * Service for retrieving information regarding available language
+ * resources.
+ */
+ @Inject
+ private LanguageResourceService languageResourceService;
+
+ /**
+ * Returns a map of all available language keys to their corresponding
+ * human-readable names.
+ *
+ * @return
+ * A map of languages defined in the system, of language key to
+ * display name.
+ */
+ @GET
+ public Map<String, String> getLanguages() {
+ return languageResourceService.getLanguageNames();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/language/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/language/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/language/package-info.java
new file mode 100644
index 0000000..cb0a190
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/language/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to the language retrieval aspect of the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.language;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/package-info.java
new file mode 100644
index 0000000..541ff76
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to the basic Guacamole REST API.
+ */
+package org.apache.guacamole.rest;
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java
new file mode 100644
index 0000000..1d823b1
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/patch/PatchRESTService.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.apache.guacamole.rest.patch;
+
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.extension.PatchResourceService;
+import org.apache.guacamole.resource.Resource;
+
+/**
+ * A REST Service for handling the listing of HTML patches.
+ *
+ * @author Michael Jumper
+ */
+@Path("/patches")
+@Produces(MediaType.APPLICATION_JSON)
+public class PatchRESTService {
+
+ /**
+ * Service for retrieving information regarding available HTML patch
+ * resources.
+ */
+ @Inject
+ private PatchResourceService patchResourceService;
+
+ /**
+ * Reads the entire contents of the given resource as a String. The
+ * resource is assumed to be encoded in UTF-8.
+ *
+ * @param resource
+ * The resource to read as a new String.
+ *
+ * @return
+ * A new String containing the contents of the given resource.
+ *
+ * @throws IOException
+ * If an I/O error prevents reading the resource.
+ */
+ private String readResourceAsString(Resource resource) throws IOException {
+
+ StringBuilder contents = new StringBuilder();
+
+ // Read entire resource into StringBuilder one chunk at a time
+ Reader reader = new InputStreamReader(resource.asStream(), "UTF-8");
+ try {
+
+ char buffer[] = new char[8192];
+ int length;
+
+ while ((length = reader.read(buffer)) != -1) {
+ contents.append(buffer, 0, length);
+ }
+
+ }
+
+ // Ensure resource is always closed
+ finally {
+ reader.close();
+ }
+
+ return contents.toString();
+
+ }
+
+ /**
+ * Returns a list of all available HTML patches, in the order they should
+ * be applied. Each patch is raw HTML containing additional meta tags
+ * describing how and where the patch should be applied.
+ *
+ * @return
+ * A list of all HTML patches defined in the system, in the order they
+ * should be applied.
+ *
+ * @throws GuacamoleException
+ * If an error occurs preventing any HTML patch from being read.
+ */
+ @GET
+ public List<String> getPatches() throws GuacamoleException {
+
+ try {
+
+ // Allocate a list of equal size to the total number of patches
+ List<Resource> resources = patchResourceService.getPatchResources();
+ List<String> patches = new ArrayList<String>(resources.size());
+
+ // Convert each patch resource to a string
+ for (Resource resource : resources) {
+ patches.add(readResourceAsString(resource));
+ }
+
+ // Return all patches in string form
+ return patches;
+
+ }
+
+ // Bail out entirely on error
+ catch (IOException e) {
+ throw new GuacamoleServerException("Unable to read HTML patches.", e);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/648a6c96/guacamole/src/main/java/org/apache/guacamole/rest/patch/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/patch/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/patch/package-info.java
new file mode 100644
index 0000000..399c329
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/patch/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Classes related to the HTML patch retrieval aspect of the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.patch;
+