/src/bananaounces/remoting/services/game/GameService.java
Java | 446 lines | 185 code | 63 blank | 198 comment | 27 complexity | 6b8e7f9dc7f18c152795ae68720eca44 MD5 | raw file
- package bananaounces.remoting.services.game;
- import java.io.UnsupportedEncodingException;
- import java.util.HashMap;
- import java.util.List;
- import org.restlet.Application;
- import org.restlet.Context;
- import org.restlet.Request;
- import org.restlet.Response;
- import org.restlet.Restlet;
- import org.restlet.data.ChallengeScheme;
- import org.restlet.routing.Router;
- import org.restlet.security.ChallengeAuthenticator;
- import org.restlet.security.LocalVerifier;
- import bananaounces.model.Game;
- import bananaounces.model.Player;
- import bananaounces.model.Tile;
- import bananaounces.remoting.RemotePlayer;
- import bananaounces.remoting.ServiceBase;
- import com.google.inject.Inject;
- import com.google.inject.Provider;
- /**
- * Service to handle incoming game requests. Stands between clients and
- * BananaouncesGame.
- * <p>
- * GameService is responsible for dispatching calls to the games in play. Rather
- * than calling Game instance methods correctly, resource classes should call
- * the corresponding methods in GameService, which will route the requests to
- * the appropriate game.
- *
- * @author Daniel Rocco
- * @author Justin Chester
- * @author Matthew Farmer
- * @version 0.5 Spring 2010
- */
- public class GameService extends Application {
- /**
- * Map game names to the Bananaounces game object for that game.
- */
- private static HashMap<String, Game> games = new HashMap<String, Game>();
- private final Provider<Game> gameProvider;
- private final Provider<RemotePlayer> playerProvider;
- /**
- * Game service constructor to create a game service on the default service
- * port. The service keeps track of the games currently in play and routes
- * incoming requests to the appropriate resource handlers.
- *
- * @param gameProvider
- * factory that provides instances of the Game class for creating
- * new games
- * @param playerProvider
- * factory that provides instances of the RemotePlayer class for
- * creating new players
- * @throws Exception
- * @see org.restlet.Application constructor
- */
- @Inject
- public GameService(Provider<Game> gameProvider,
- Provider<RemotePlayer> playerProvider) throws Exception {
- super();
- this.gameProvider = gameProvider;
- this.playerProvider = playerProvider;
- }
- /**
- * Map incoming requests to the appropriate resources. Also creates the
- * authenticator for authenticating privileged requests like peel.
- */
- @Override
- public Restlet createInboundRoot() {
- Router router = new Router(getContext());
- router.attach("/games/", GamesResource.class);
- router.attach("/games/{game}/", GamesResource.class);
- router.attach("/games/{game}/players/", PlayersResource.class);
- // the order is important here: Restlet 2.x defaults to "First match"
- // for templates.
- // see:
- // http://www.developer.com/java/web/article.php/10935_3875781_2/Leveraging-the-Restlet-Routing-System-for-Your-Java-Web-Services.htm
- router.attach("/games/{game}/players/{player};{callback}",
- PlayerResource.class);
- router.attach("/games/{game}/players/{player}", PlayerResource.class);
- router.attach("/games/{game}/pile/tile/{tile}", PileResource.class);
- router.attach("/games/{game}/pile/{tile}", PileResource.class);
- router.attach("/games/{game}/pile/", PileResource.class);
- this.authenticator = createAuthenticator();
- authenticator.setNext(router);
- return authenticator;
- }
- //
- // game service interface
- //
- /**
- * Attaches a new game to this service.
- *
- * @param gameName
- * the name of the game
- * @return true if the game was successfully added; false if a game with the
- * given name already exists
- */
- public boolean createGame(String gameName) {
- if (games.containsKey(gameName)) {
- return false;
- }
- games.put(gameName, gameProvider.get());
- return true;
- }
- /**
- * List the games that have been created on this service.
- *
- * @return a list of the game names attached to this service
- */
- public String listGames() {
- return games.keySet().toString();
- }
- /**
- * Test whether a game currently exists.
- *
- * @param gameName
- * the name of the game to check for existence
- * @return true if the game exists, false if it does not or if gameName is
- * null
- */
- public boolean containsGame(String gameName) {
- if (gameName == null) {
- return false;
- }
- return games.containsKey(gameName);
- }
- /**
- * Check to see if the given game has started; that is, has a valid split
- * call been sent to the game.
- *
- * @param gameName
- * the name of the game to check
- * @return true if the game has started, false if it has not or does not
- * exist
- */
- public boolean gameStarted(String gameName) {
- if (!games.containsKey(gameName)) {
- return false;
- }
- return games.get(gameName).gameStarted();
- }
- /**
- * Check to see if a player is registered with a game.
- *
- * @param gameName
- * the name of the game to check
- * @param playerName
- * the name of the player
- * @return true if the player is registered with the game, false otherwise
- */
- public boolean containsPlayer(String gameName, String playerName) {
- if (gameName == null || playerName == null || !containsGame(gameName)) {
- return false;
- }
- return getGame(gameName).hasPlayer(playerName);
- }
- /**
- * Retrieve a player from the specified game.
- *
- * @param gameName
- * the name of the game
- * @param playerName
- * the name of the player, which should uniquely identify them
- * @return the RemotePlayer object representing the specified player, or
- * null if the player does not exist
- */
- public RemotePlayer getPlayer(String gameName, String playerName) {
- return (RemotePlayer) getGame(gameName).getPlayer(playerName);
- }
- /**
- * Register a player with the specified game, creating the game if it does
- * not already exist.
- * <p>
- * Each player needs a unique name identifying them and a web service
- * callback URL that the game can use to send updates to the player.
- *
- * @param gameName
- * the name of the game to join; the corresponding game is
- * created if it does not already exist
- * @param playerName
- * the name of the player joining the game, which should be
- * unique
- * @param callback
- * the player's web service callback URL
- * @return the RemotePlayer object representing the specified player
- * @throws UnsupportedEncodingException
- * if any of the parameters could not be converted to URL-safe
- * utf-8
- */
- public Player registerPlayer(String gameName, String playerName,
- String callback) throws UnsupportedEncodingException {
- if (gameName == null || playerName == null) {
- return null;
- }
- if (!containsGame(gameName)) {
- createGame(gameName);
- }
- RemotePlayer player = playerProvider.get();
- player.setName(playerName);
- player.setGameName(gameName);
- player.setCallbackURL(callback);
- getGame(gameName).registerPlayer(player);
- return player;
- }
- /**
- * Get the status of the given game.
- *
- * @param gameName
- * the name of the game
- * @return the status of the game, or null if the game does not exist
- */
- public String getGameStatus(String gameName) {
- String result = null;
- if (containsGame(gameName)) {
- result = getGame(gameName).toString();
- }
- return result;
- }
- /**
- * Get the number of players registered for the given game.
- *
- * @param gameName
- * the name of the game
- * @return the number of players registered for the game; returns 0 if the
- * game does not exist
- */
- public int getPlayerCount(String gameName) {
- int result = 0;
- if (containsGame(gameName)) {
- result = getGame(gameName).getPlayerCount();
- }
- return result;
- }
- /**
- * Retrieve the list of players for the given game.
- *
- * @param gameName
- * the name of the game
- * @return the list of players currently registered for the game, or null if
- * the game does not exist
- */
- public List<String> getPlayers(String gameName) {
- return containsGame(gameName) ? getGame(gameName).getPlayers() : null;
- }
- /**
- * Call split on the given game, sending players their initial tile list and
- * starting the game.
- * <p>
- * Does nothing if the game does not exist.
- *
- * @param gameName
- * the game to start
- */
- public void split(String gameName) {
- if (containsGame(gameName)) {
- getGame(gameName).split();
- }
- }
- /**
- * Call peel on the given game, sending players their next tile.
- * <p>
- * Does nothing if the game does not exist.
- *
- * @param gameName
- * the game to peel
- */
- public void peel(String gameName) {
- if (containsGame(gameName)) {
- getGame(gameName).peel();
- }
- }
- /**
- * Get the size of the pile for the given game.
- *
- * @param gameName
- * the name of the game
- * @return the size of the pile for the game; returns 0 if the game does not
- * exist
- */
- public int getPileSize(String gameName) {
- int result = 0;
- if (containsGame(gameName)) {
- result = getGame(gameName).getPileSize();
- }
- return result;
- }
- /**
- * Check to see if a dump call is valid for the specified game. A dump is
- * valid if the pile has at least 3 tiles remaining.
- *
- * @param gameName
- * the game to check
- * @return true if a dump is valid, false if not or the game does not exist
- */
- public boolean canDump(String gameName) {
- return containsGame(gameName) ? getGame(gameName).canDump() : false;
- }
- /**
- * Dump a tile; the player exchanges the given tile for 3 replacements.
- * <p>
- * Does nothing if the game does not exist.
- *
- * @param gameName
- * the name of the game
- * @param playerName
- * the player dumping the tile
- * @param tile
- * the tile to return to the pile
- */
- public void dump(String gameName, String playerName, Tile tile) {
- if (containsGame(gameName)) {
- getGame(gameName).dump(playerName, tile);
- }
- }
- //
- // helper methods
- //
- /**
- * Retrieve a given game from this service's list of games.
- *
- * @param gameName
- * the name of the desired game
- * @return the game associated with the given name, or null if no such game
- * exists
- */
- private Game getGame(String gameName) {
- return games.get(gameName);
- }
- //
- // authentication and authorization
- //
- // adapted from
- // http://stackoverflow.com/questions/2217418/fine-grained-authentication-with-restlet
- private ChallengeAuthenticator authenticator;
- private LocalVerifier createVerifier() {
- LocalVerifier verifier = new LocalVerifier() {
- @Override
- public char[] getLocalSecret(String gamePlayerName) {
- if (gamePlayerName == null) {
- return null;
- }
- String[] gamePlayer = gamePlayerName.split("@", 2);
- RemotePlayer player = getPlayer(gamePlayer[1], gamePlayer[0]);
- return (player == null) ? null : player.getCallbackURL()
- .toCharArray();
- }
- };
- return verifier;
- }
- private ChallengeAuthenticator createAuthenticator() {
- Context context = getContext();
- boolean optional = true;
- ChallengeScheme challengeScheme = ChallengeScheme.HTTP_BASIC;
- String realm = "bananaounces";
- LocalVerifier verifier = createVerifier();
- ChallengeAuthenticator auth = new ChallengeAuthenticator(context,
- optional, challengeScheme, realm, verifier) {
- @Override
- protected boolean authenticate(Request request, Response response) {
- if (request.getChallengeResponse() == null) {
- return false;
- } else {
- return super.authenticate(request, response);
- }
- }
- };
- return auth;
- }
- public boolean authenticate(Request request, Response response) {
- if (!request.getClientInfo().isAuthenticated()) {
- authenticator.challenge(response, false);
- return false;
- }
- return true;
- }
- //
- // main
- //
- public static void main(String[] args) throws Exception {
- GameServiceModule.startGameService(new GameServiceModule(),
- ServiceBase.DEFAULT_SERVICE_PORT, "/v1");
- }
- }