Java tutorial
/* * Copyright 2012-2013 Arie Benichou * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package blockplus.transport; import java.util.Map; import java.util.concurrent.ConcurrentLinkedDeque; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocketClient; import org.eclipse.jetty.websocket.WebSocketClientFactory; import org.eclipse.jetty.websocket.WebSocketServlet; import blockplus.model.Context; import blockplus.transport.events.interfaces.IClient; import blockplus.transport.events.interfaces.IDisconnect; import blockplus.transport.events.interfaces.IEvent; import blockplus.transport.events.interfaces.IInPatio; import blockplus.transport.messages.Messages; import blockplus.transport.protocol.IMessage; import blockplus.transport.protocol.IMessageHandler; import blockplus.transport.protocol.MessageDecoder; import blockplus.transport.protocol.MessageHandler; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.DeadEvent; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.gson.JsonObject; @SuppressWarnings("serial") public class BlockplusServer extends WebSocketServlet { private final static Messages MESSAGES = new Messages(); public final static int GAMES = 50; //public final EventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(4)); public final EventBus eventBus = new EventBus(); private final IMessageHandler messageHandler = new MessageHandler(); // TODO injecter private final MessageDecoder messageDecoder = new MessageDecoder(); // TODO injecter private final Map<IEndPoint, IClient> clientByEndpoint = Maps.newConcurrentMap(); private final ConcurrentLinkedDeque<IEndPoint> endpointsInPatio = new ConcurrentLinkedDeque<IEndPoint>(); private final Map<Integer, IGame<Context>> gameByOrdinal = Maps.newConcurrentMap(); // TODO utiliser un Futur @Override public void init() throws ServletException { super.init(); this.eventBus.register(this); final BlockplusServerEvents blockplusServerEvents = new BlockplusServerEvents(this); // TODO injecter this.eventBus.register(blockplusServerEvents); final ImmutableList<IClient> empty = ImmutableList.of(); for (int i = 1; i <= GAMES; ++i) { this.updateGame(i, new BlockplusGame(i, empty, null, false)); } } public void updateClients(final IEndPoint endPoint, final IClient user) { this.clientByEndpoint.put(endPoint, user); } public IGame<Context> getGame(final Integer ordinal) { return this.gameByOrdinal.get(ordinal); } public void updateGame(final Integer ordinal, final IGame<Context> newGame) { this.gameByOrdinal.put(ordinal, newGame); } public void removeFromPatio(final IEndPoint endpoint) { this.endpointsInPatio.remove(endpoint); } public ConcurrentLinkedDeque<IEndPoint> getEndpointsInPatio() { return this.endpointsInPatio; } @Override public WebSocket doWebSocketConnect(final HttpServletRequest request, final String protocol) { return new EndPoint(this); } public IMessage decode(final String data) { return this.messageDecoder.decode(data); } public Object handle(final IEndPoint endPoint, final IMessage message) { return this.messageHandler.handle(endPoint, message); } public IClient getClientByEndpoint(final IEndPoint endPoint) { return this.clientByEndpoint.get(endPoint); } private void connect(final IEndPoint endPoint, final IClient client) { this.clientByEndpoint.put(endPoint, client); } public void disconnect(final IEndPoint endPoint) { this.clientByEndpoint.remove(endPoint); this.endpointsInPatio.remove(endPoint); } public void onMessage(final IEndPoint endPoint, final String data) { IMessage message = null; try { message = this.decode(data); // System.out.println(message); } catch (final Exception e) { // TODO MessageConstructionException endPoint.say("Message could not be created from " + data + " : " + Throwables.getRootCause(e)); } if (message != null) { Object object = null; try { object = this.handle(endPoint, message); } catch (final Exception e) { // TODO EventConstructionException endPoint.say("Event could not be created from " + message + " : " + Throwables.getRootCause(e)); } if (object != null) { try { this.eventBus.post(object); } catch (final Exception e) { // TODO EventDispatchingException endPoint.say( "Event could not be dispatched from " + object + " : " + Throwables.getRootCause(e)); } } } } @Subscribe @AllowConcurrentEvents public void onConnection(final IClient newClient) { this.connect(newClient.getEndpoint(), newClient); } @Subscribe @AllowConcurrentEvents public void onDisconnection(final IDisconnect client) { this.disconnect(client.getEndpoint()); } @Subscribe @AllowConcurrentEvents public void onInPatio(final IInPatio inPatio) { final IEndPoint endPoint = inPatio.getEndpoint(); final JsonObject tables = this.games(); endPoint.emit("tables", tables.toString()); for (final IEndPoint other : this.endpointsInPatio) { other.emit("tables", tables.toString()); } this.endpointsInPatio.add(endPoint); } private BlockplusGame reset(final IGame<Context> game) { final ImmutableList<IClient> empty = ImmutableList.of(); final BlockplusGame newGame = new BlockplusGame(game.getOrdinal(), empty, null, false); this.updateGame(game.getOrdinal(), newGame); return newGame; } public JsonObject games() { final JsonObject tables = new JsonObject(); for (final IGame<Context> game : this.gameByOrdinal.values()) { if (game.isFull()) { boolean isAlive = false; for (final IClient client : game.getClients()) { final boolean isOpen = client.getEndpoint().isOpen(); if (!isOpen) this.disconnect(client.getEndpoint()); isAlive = isAlive || isOpen; } if (!isAlive) { tables.addProperty("" + this.reset(game).getOrdinal(), this.reset(game).getClients().size()); } } else { boolean isFullyAlive = true; boolean isFullyDead = true; for (final IClient client : game.getClients()) { final boolean isOpen = client.getEndpoint().isOpen(); isFullyAlive = isFullyAlive && isOpen; isFullyDead = isFullyDead && !isOpen; } if (isFullyAlive) { tables.addProperty("" + game.getOrdinal(), game.getClients().size()); } else if (isFullyDead) { tables.addProperty("" + this.reset(game).getOrdinal(), this.reset(game).getClients().size()); } } } return tables; } @Subscribe @AllowConcurrentEvents public void onDeadEvent(final DeadEvent deadEvent) { final Object event = deadEvent.getEvent(); if (event instanceof IEvent) { final IEvent eventInterface = (IEvent) event; eventInterface.getEndpoint().emit("dead event", "\"" + eventInterface + "\""); } else System.out.println(deadEvent.getEvent()); } // TODO use other virtual clients for testing public static VirtualClient runVC(final String[] args) throws Exception { final int game = args.length > 0 ? Integer.parseInt(args[0]) : 1; final String host = "localhost"; //final String host = "192.168.0.1"; final int port = 8282; final WebSocketClientFactory factory = new WebSocketClientFactory(); factory.setBufferSize(4096); factory.start(); final WebSocketClient client = factory.newWebSocketClient(); client.setMaxIdleTime(60 * 1000 * 5); client.setMaxTextMessageSize(1024 * 64); final VirtualClient virtualClient = new VirtualClient("virtual-client", client, host, port, "io"); virtualClient.start(); final IMessage message1 = MESSAGES.newClient(virtualClient.getName()); virtualClient.send(message1); final IMessage message2 = MESSAGES.newGameConnection(game); virtualClient.send(message2); //virtualClient.stop(); //factory.stop(); return virtualClient; } public static void main(final String[] args) throws Exception { for (int n = 0; n < 4; ++n) { runVC(args); Thread.sleep(1000); } } }