Ticket #362: Got JSON mapping working.

This commit is contained in:
James Muehlner
2013-09-16 21:17:21 -07:00
parent 064802a9fb
commit b15777a70f
9 changed files with 316 additions and 46 deletions

View File

@@ -129,14 +129,14 @@
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.7</version>
<version>1.17.1</version>
</dependency>
<!-- Jersey - Guice extension -->
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-guice</artifactId>
<version>1.7</version>
<version>1.17.1</version>
</dependency>
<!-- JSR-250 annotations -->
@@ -153,6 +153,13 @@
<version>1.4</version>
</dependency>
<!-- Jackson for JSON support -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.17.1</version>
</dependency>
<!-- Guacamole Java API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>

View File

@@ -0,0 +1,47 @@
package org.glyptodon.guacamole.net.basic.rest;
/*
* Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* A simple object to represent an error to be sent from the REST API.
* @author James Muehlner
*/
public class APIError {
/**
* The error message.
*/
private String message;
/**
* Get the error message.
* @return The error message.
*/
public String getMessage() {
return message;
}
/**
* Create a new APIError with the specified error message.
* @param message The error message.
*/
public APIError(String message) {
this.message = message;
}
}

View File

@@ -26,6 +26,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.AuthTokenGenerator;
import org.glyptodon.guacamole.net.basic.rest.auth.BasicTokenUserContextMap;
import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator;
import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap;
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService;
import org.glyptodon.guacamole.properties.GuacamoleProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,6 +63,7 @@ public class RESTModule extends AbstractModule {
bind(AuthenticationProvider.class).toInstance(authProvider);
bind(TokenUserContextMap.class).toInstance(new BasicTokenUserContextMap());
bind(ConnectionService.class);
bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class);
}

View File

@@ -18,10 +18,12 @@ package org.glyptodon.guacamole.net.basic.rest;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.google.inject.Scopes;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import org.glyptodon.guacamole.net.basic.rest.auth.LoginService;
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
import org.glyptodon.guacamole.net.basic.rest.auth.LoginRESTService;
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService;
/**
* A Guice Module to set up the servlet mappings for the Guacamole REST API.
@@ -33,10 +35,14 @@ public class RESTServletModule extends ServletModule {
@Override
protected void configureServlets() {
bind(ConnectionService.class);
bind(LoginService.class);
// Set up the API endpoints
bind(ConnectionRESTService.class);
bind(LoginRESTService.class);
serve("*").with(GuiceContainer.class);
// Set up the servlet and JSON mappings
bind(GuiceContainer.class);
bind(JacksonJsonProvider.class).in(Scopes.SINGLETON);
serve("/*").with(GuiceContainer.class);
}
}

View File

@@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
@Path("/api/login")
public class LoginService {
public class LoginRESTService {
/**
* The authentication provider used to authenticate this user.
@@ -65,7 +65,7 @@ public class LoginService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(LoginService.class);
private static final Logger logger = LoggerFactory.getLogger(LoginRESTService.class);
/**
* Authenticates a user, generates an auth token, associates that auth token
@@ -76,7 +76,6 @@ public class LoginService {
* @return The auth token for the newly logged-in user.
*/
@POST
@Path("/")
public String login(@QueryParam("username") String username,
@QueryParam("password") String password) {

View File

@@ -0,0 +1,104 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
/*
* Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.ConnectionRecord;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
/**
* A simple connection to expose through the REST endpoints.
*
* @author James Muehlner
*/
public class Connection {
/**
* The name of this connection.
*/
private String name;
/**
* The identifier of this connection.
*/
private String identifier;
/**
* The configuration associated with this connection.
*/
private GuacamoleConfiguration configuration;
/**
* The history records associated with this connection.
*/
private List<? extends ConnectionRecord> history;
/**
* Create an empty Connection.
*/
public Connection() {}
/**
* Create a Connection from a org.glyptodon.guacamole.net.auth.Connection.
* @param connection The connection to create this Connection from.
* @throws GuacamoleException If a problem is encountered while
* instantiating this new Connection.
*/
public Connection(org.glyptodon.guacamole.net.auth.Connection connection)
throws GuacamoleException {
this.name = connection.getName();
this.identifier = connection.getIdentifier();
this.configuration = connection.getConfiguration();
this.history = connection.getHistory();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public GuacamoleConfiguration getConfiguration() {
return configuration;
}
public void setConfiguration(GuacamoleConfiguration configuration) {
this.configuration = configuration;
}
public List<? extends ConnectionRecord> getHistory() {
return history;
}
public void setHistory(List<? extends ConnectionRecord> history) {
this.history = history;
}
}

View File

@@ -0,0 +1,108 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
/*
* Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
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.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
import org.glyptodon.guacamole.net.auth.Directory;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.net.basic.rest.APIError;
import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap;
/**
* A REST Service for handling connection CRUD operations.
*
* @author James Muehlner
*/
@Path("/api/connection")
@RequestScoped
public class ConnectionRESTService {
/**
* The map of auth tokens to users for the REST endpoints.
*/
@Inject
private TokenUserContextMap tokenUserMap;
/**
* A service for managing the REST endpoint Connection objects.
*/
@Inject
private ConnectionService connectionService;
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Connection> getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) {
UserContext userContext = tokenUserMap.get(authToken);
// authentication failed.
if(userContext == null)
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
try {
// If the parent connection group is passed in, try to find it.
ConnectionGroup parentConnectionGroup;
if(parentID == null)
parentConnectionGroup = userContext.getRootConnectionGroup();
else {
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
parentConnectionGroup = connectionGroupDirectory.get(parentID);
}
if(parentConnectionGroup == null)
throw new WebApplicationException(
Response.status(Response.Status.BAD_REQUEST)
.entity(new APIError("No ConnectionGroup found with the provided parentID."))
.build());
Directory<String, org.glyptodon.guacamole.net.auth.Connection> connectionDirectory =
parentConnectionGroup.getConnectionDirectory();
// Get the list of connection names
List<org.glyptodon.guacamole.net.auth.Connection> connections
= new ArrayList<org.glyptodon.guacamole.net.auth.Connection>();
Iterable<String> identifiers = connectionDirectory.getIdentifiers();
// Get the connection for each name
for(String identifier : identifiers)
connections.add(connectionDirectory.get(identifier));
return connectionService.convertConnectionList(connections);
} catch(GuacamoleSecurityException e) {
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
} catch(GuacamoleException e) {
throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
}
}
}

View File

@@ -1,5 +1,9 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
import java.util.ArrayList;
import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
/*
* Guacamole - Clientless Remote Desktop
* Copyright (C) 2010 Michael Jumper
@@ -18,48 +22,31 @@ package org.glyptodon.guacamole.net.basic.rest.connection;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.google.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap;
/**
* A REST Service for handling connection CRUD operations.
* A service for performing useful manipulations on REST Connections.
*
* @author James Muehlner
*/
@Path("/api/connection")
public class ConnectionService {
/**
* The map of auth tokens to users for the REST endpoints.
* Converts a list of org.glyptodon.guacamole.net.auth.Connection to
* Connection objects for exposure with the REST endpoints.
*
* @param connections The org.glyptodon.guacamole.net.auth.Connection to
* convert for REST endpoint use.
* @return A List of Connection objects for use with the REST endpoint.
* @throws GuacamoleException If an error occurs while converting the
* connections.
*/
@Inject
private TokenUserContextMap tokenUserMap;
public List<Connection> convertConnectionList(List<? extends org.glyptodon.guacamole.net.auth.Connection> connections)
throws GuacamoleException {
List<Connection> restConnections = new ArrayList<Connection>();
@Path("/")
@GET
public String getConnections(@QueryParam("token") String authToken) {
UserContext userContext = tokenUserMap.get(authToken);
// authentication failed.
if(userContext == null)
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
try {
//TODO: Make this work for realzies
return userContext.getRootConnectionGroup().getConnectionDirectory().getIdentifiers().toString();
} catch(GuacamoleSecurityException e) {
throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);
} catch(GuacamoleException e) {
throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR);
for(org.glyptodon.guacamole.net.auth.Connection connection : connections) {
restConnections.add(new Connection(connection));
}
}
return restConnections;
}
}

View File

@@ -256,6 +256,16 @@
<listener-class>org.glyptodon.guacamole.net.basic.rest.RESTServletContextListener</listener-class>
</listener>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.codehaus.jackson.jaxrs</param-value>
</init-param>
<mime-mapping>
<extension>mp3</extension>
<mime-type>audio/mpeg</mime-type>