Java tutorial
/* * Copyright (c) 2013 Christoph Brill * * 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 de.egore911.opengate.services; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import com.google.appengine.labs.repackaged.org.json.JSONArray; import com.google.appengine.labs.repackaged.org.json.JSONException; import com.google.appengine.labs.repackaged.org.json.JSONObject; import de.egore911.opengate.EntityManagerFilter; import de.egore911.opengate.annotation.Documentation; import de.egore911.opengate.model.Gameserver; @Path("/gameserver") @Documentation("Provides methods for the game server instances to register and deregister themselves.") public class GameserverService { private static final Logger LOG = Logger.getLogger(GameserverService.class.getName()); @GET @Path("/add") @Produces("application/json") @Documentation("Add or update a gameserver to the list of available game servers. " + "The remote IP will be obtained from the packet sent to the server so server " + "behind a NAT will not work. The method will answer using a JSON object like " + "{status: 'ok', detail: '...'}.") public Response addGameserver(@Context HttpServletRequest req) { String remoteAddr = req.getRemoteAddr(); EntityManager em = EntityManagerFilter.getEntityManager(); Gameserver gameserver; try { JSONObject retval = new JSONObject(); StatusHelper.ok(retval); try { gameserver = (Gameserver) em .createQuery("select gameserver from Gameserver gameserver where address = :address") .setParameter("address", remoteAddr).getSingleResult(); retval.put("detail", "Gameserver updated"); } catch (NoResultException e) { gameserver = new Gameserver(); gameserver.setAddress(remoteAddr); retval.put("detail", "Gameserver added"); } gameserver.setLastSeen(new Date()); em.persist(gameserver); return Response.ok(retval.toString()).build(); } catch (JSONException e) { LOG.log(Level.SEVERE, e.getMessage(), e); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @GET @Path("/remove") @Produces("application/json") @Documentation("This method removes a gameserver from the list. It is recommend to call this " + "method on shutdown of the gameserver. The remote IP will be obtained from the packet " + "sent to the server so server behind a NAT will not work. The method will answer using " + "a JSON object like {status: 'ok', detail: '...'}.") public Response removeGameserver(@Context HttpServletRequest req) { String remoteAddr = req.getRemoteAddr(); EntityManager em = EntityManagerFilter.getEntityManager(); Gameserver gameserver; try { JSONObject retval = new JSONObject(); StatusHelper.ok(retval); try { gameserver = (Gameserver) em .createQuery("select gameserver from Gameserver gameserver where address = :address") .setParameter("address", remoteAddr).getSingleResult(); em.remove(gameserver); retval.put("detail", "Successfully removed"); } catch (NoResultException e) { // Nothing to do retval.put("detail", "Already removed"); } return Response.ok(retval.toString()).build(); } catch (JSONException e) { LOG.log(Level.SEVERE, e.getMessage(), e); return Response.status(Status.INTERNAL_SERVER_ERROR).build(); } } @GET @Path("/list") @Produces("application/json") @Documentation("Returns a JSON array of available gameserver addresses (e.g. ['1.2.3.4', '4.5.6.7']). Only servers that called the 'add'-method within the last 15 minutes will be returned.") public Response listGameservers() { EntityManager em = EntityManagerFilter.getEntityManager(); JSONArray retval = new JSONArray(); Calendar lastFifteenMinutes = Calendar.getInstance(); lastFifteenMinutes.add(Calendar.MINUTE, -15); List<Gameserver> gameservers = em .createQuery("select gameserver from Gameserver gameserver where lastSeen > :lastSeen") .setParameter("lastSeen", lastFifteenMinutes.getTime()).getResultList(); for (Gameserver gameserver : gameservers) { retval.put(gameserver.getAddress()); } return Response.ok(retval.toString()).build(); } @GET @Path("/purge") @Produces("text/plain") @Documentation("Internal method to remove the gameservers that haven't been seen for more than one hour.") public Response purgeGameservers() { EntityManager em = EntityManagerFilter.getEntityManager(); Calendar oneHourAgo = Calendar.getInstance(); oneHourAgo.add(Calendar.MINUTE, -15); int deleted = em.createQuery("delete from Gameserver gameserver where lastSeen > :lastSeen") .setParameter("lastSeen", oneHourAgo.getTime()).executeUpdate(); return Response.ok(Integer.toString(deleted)).build(); } }