/* * $Id$ * * Copyright (c) 2000-2007 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ /* * Copyright (c) 2003 by Rodney Kinney. All rights reserved. * Date: May 9, 2003 */ package VASSAL.chat.node; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Properties; import java.util.Set; import java.util.logging.Logger; import VASSAL.chat.HttpRequestWrapper; import VASSAL.tools.PropertiesEncoder; /** * Root node in a hierarchical server. * Represents the server process itself. * Children represent modules. * Children of modules represent rooms. * Children of rooms represent players. */ public class AsynchronousServerNode extends ServerNode { private static Logger logger = Logger.getLogger(AsynchronousServerNode.class.getName()); private StatusReporter statusReporter; private ReportContentsThread contentsReporter; public AsynchronousServerNode(String url) { super(); init(url); } protected void init(String url) { statusReporter = new StatusReporter( url == null ? null : new HttpRequestWrapper(url), this); contentsReporter = new ReportContentsThread(this); } protected synchronized void sendContents(Node node) { contentsReporter.markChanged(node); } public static class ReportContentsThread extends Thread { private AsynchronousServerNode server; private Set<Node> changed; private long lastGlobalUpdate; private static final long GLOBAL_UPDATE_INTERVAL = 1000L * 120L; public ReportContentsThread(AsynchronousServerNode server) { this.server = server; changed = new HashSet<Node>(); start(); } public void run() { while (true) { try { synchronized (this) { wait(); sendContents(); } } catch (InterruptedException e) { } } } private synchronized void sendContents() { server.statusReporter.updateContents(server.getLeafDescendants()); long time = System.currentTimeMillis(); Iterator<Node> modules; if (time - lastGlobalUpdate < GLOBAL_UPDATE_INTERVAL) { modules = Arrays.asList(server.getChildren()).iterator(); lastGlobalUpdate = time; } else { modules = changed.iterator(); } while (modules.hasNext()) { Node module = modules.next(); logger.fine("Sending contents of "+module.getId()); //$NON-NLS-1$ Node[] players = module.getLeafDescendants(); Node[] rooms = module.getChildren(); // Check if any rooms have lost their first player for (int i = 1; i < rooms.length; i++) { Node[] c = rooms[i].getChildren(); if (c.length > 0) { try { final Properties roomProps = new PropertiesEncoder(rooms[i].getInfo()).getProperties(); final String roomOwner = roomProps.getProperty("owner"); final String playerId = new PropertiesEncoder(c[0].getInfo()).getProperties().getProperty("id"); if (roomOwner == null || (! roomOwner.equals(playerId))) { roomProps.setProperty("owner", playerId); rooms[i].setInfo(new PropertiesEncoder(roomProps).toString()); } } catch (IOException e) { // Error encoding/decoding properties. Shouldn't happen. e.printStackTrace(); } } } String listCommand = Protocol.encodeListCommand(players); logger.finer(listCommand); module.send(listCommand); String roomInfo = Protocol.encodeRoomsInfo(rooms); module.send(roomInfo); } changed.clear(); } public synchronized void markChanged(Node module) { logger.fine(module+" has changed"); //$NON-NLS-1$ changed.add(module); notifyAll(); } } }