[vassalengine-svn] SF.net SVN: vassalengine:[5527] VASSAL-src/branches/swampwallaby-3.1
Brought to you by:
rodneykinney,
uckelman
From: <swa...@us...> - 2009-04-21 11:22:16
|
Revision: 5527 http://vassalengine.svn.sourceforge.net/vassalengine/?rev=5527&view=rev Author: swampwallaby Date: 2009-04-21 11:10:06 +0000 (Tue, 21 Apr 2009) Log Message: ----------- Merged up to 3.1@5525 Modified Paths: -------------- VASSAL-src/branches/swampwallaby-3.1/CHANGES.txt VASSAL-src/branches/swampwallaby-3.1/Makefile VASSAL-src/branches/swampwallaby-3.1/NOTES VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/Info.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/GameModule.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/ImageSaver.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/boardPicker/Board.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/widget/PanelWidget.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/AbstractLaunchAction.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/CommandServer.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Editor.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Launcher.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManager.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManagerWindow.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Player.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/ErrorDialog.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/MemoryUtils.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/image/GeneralFilter.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/image/ImageUtils.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/image/memmap/MappedImageUtils.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/CommandClientAdapter.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/LogEntry.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/LogListener.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/LogOutputStreamAdapter.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/LogPane.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/LoggedOutputStream.java VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/Logger.java Added Paths: ----------- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Command.java Removed Paths: ------------- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/logging/LogManager.java Modified: VASSAL-src/branches/swampwallaby-3.1/CHANGES.txt =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/CHANGES.txt 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/CHANGES.txt 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,3 +1,23 @@ +3.1.5 - ?? + +3.1.4 - 15 April 2009 + +* Bug 2731738: Memory test fails on Windows 98, Windows ME +* Bug 2721306: IllegalArgumentException in PanelWidget.getLayout() +* Bug 2718948: v3.0+ DoesNotStack trait bug + - Replace Crtl-Shift-Click Selection filter with Alt-Click selection filter + - Allow Alt-lasso to select Alt-click selection filter units + - Allow Ctrl-lasso to deselect Does Not Stack units. + - Double-click on Does Not Stack units causes selection status to toggle +* Bug 2696618: ImageSaver fails when map has nonpositive dimensions +* Bug: Bind to loopback ports only to avoid collisions and firewall problems +* Bug: Module Manager fails to start if port in prefs is already in use +* Bug: Race condition when setting Module Manager port and key +* Bug: GUI calls by Module Manager server executed off the EDT +* Bug: Memory test inaccurate on Linux +* Bug: Deadlock when Module Manager is closed before children +* Bug: Java 1.5 ImageIO fails to load PNGs with iTXt chunks + 3.1.3 - 1 April 2009 * Bug 2721276: Illegal Heap sizes crashing startup Modified: VASSAL-src/branches/swampwallaby-3.1/Makefile =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/Makefile 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/Makefile 2009-04-21 11:10:06 UTC (rev 5527) @@ -46,7 +46,7 @@ DOCDIR:=doc DISTDIR:=dist -VNUM:=3.1.4 +VNUM:=3.1.5 SVNVERSION:=$(shell svnversion | perl -pe 's/(\d+:)?(\d+[MS]?)/$$2/; s/(\d+)M/$$1+1/e') VERSION:=$(VNUM)-svn$(SVNVERSION) #VERSION:=$(VNUM) Modified: VASSAL-src/branches/swampwallaby-3.1/NOTES =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/NOTES 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/NOTES 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,4 +1,4 @@ -The VASSAL Team is happy to announce the release of VASSAL 3.1.4, which +The VASSAL Team is happy to announce the release of VASSAL 3.1.5, which fixes several problems found in earlier versions. ********************* @@ -7,32 +7,51 @@ For Linux: - http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.4-linux.tar.bz2 + http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.5-linux.tar.bz2 For Mac OS X: - http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.4-macosx.dmg + http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.5-macosx.dmg For Windows: - http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.4-windows.exe + http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.5-windows.exe For other operating systems: - http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.4-other.zip + http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.5-other.zip Source code: - http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.4-src.zip + http://downloads.sourceforge.net/vassalengine/VASSAL-3.1.5-src.zip ********************* -Changes since 3.1.0: +Changes since 3.1.0 ********************* +The following bugs are fixed in 3.1.5: + +* Bug 2659577: ImageSaver paints Boards with no background black +* Image scaling is now 35% faster. + The following bugs are fixed in 3.1.4: +* Bug 2731738: Memory test fails on Windows 98, Windows ME +* Bug 2721306: IllegalArgumentException in PanelWidget.getLayout() +* Bug 2718948: v3.0+ DoesNotStack trait bug + - Replace Crtl-Shift-Click Selection filter with Alt-Click selection filter + - Allow Alt-lasso to select Alt-click selection filter units + - Allow Ctrl-lasso to deselect Does Not Stack units. + - Double-click on Does Not Stack units causes selection status to toggle +* Bug 2696618: ImageSaver fails when map has nonpositive dimensions * Bug: Bind to loopback ports only to avoid collisions and firewall problems +* Bug: Module Manager fails to start if port in prefs is already in use +* Bug: Race condition when setting Module Manager port and key +* Bug: GUI calls by Module Manager server executed off the EDT +* Bug: Memory test inaccurate on Linux +* Bug: Deadlock when Module Manager is closed before children +* Bug: Java 1.5 ImageIO fails to load PNGs with iTXt chunks The following bugs are fixed in 3.1.3: @@ -77,7 +96,7 @@ of Java older than 1.5.0_08 upgrade to *at least* 1.5.0_08. For Linux and Mac users, this should require no action on your part. Windows -users installing VASSAL 3.1.4 with the Windows installer will be prompted to +users installing VASSAL 3.1.5 with the Windows installer will be prompted to update Java if what they have is older than 1.5.0_08. For feature changes from 3.0 to 3.1, see the release notes for 3.1.0. Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/Info.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/Info.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/Info.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -33,7 +33,7 @@ * Class for storing release-related information */ public final class Info { - private static final String VERSION = "3.1.3"; //$NON-NLS-1$ + private static final String VERSION = "3.1.4"; //$NON-NLS-1$ // Do not allow editing of modules with this revision or later private static final String EDITOR_EXPIRY_VERSION = "3.2"; //$NON-NLS-1$ @@ -95,13 +95,31 @@ } } - // Strictly speaking, this doesn't guarantee uniqueness, but the - // probability that any reasonable number or simultaneously-running - // instances of the VASSAL ModuleManager, Player or Editor will get - // the same ID is vanishingly small. - private static final int instanceID = - (int) (Math.random() * Integer.MAX_VALUE); + private static final int instanceID; + // Set the instance id from the system properties. + static { + final String idstr = System.getProperty("VASSAL.id"); + if (idstr == null) { + instanceID = 0; + } + else { + int id; + try { + id = Integer.parseInt(idstr); + } + catch (NumberFormatException e) { + id = -1; + } + + instanceID = id; + } + } + + /** + * Returns the instance id for this process. The instance id will be + * be unique across the Module Manager and its children. + */ public static int getInstanceID() { return instanceID; } Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/GameModule.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/GameModule.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/GameModule.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -87,6 +87,7 @@ import VASSAL.tools.ArchiveWriter; import VASSAL.tools.CRCUtils; import VASSAL.tools.DataArchive; +import VASSAL.tools.FutureUtils; import VASSAL.tools.KeyStrokeListener; import VASSAL.tools.KeyStrokeSource; import VASSAL.tools.MTRandom; @@ -724,7 +725,7 @@ ReadErrorDialog.error(e, archive.getName()); } - VASSAL.tools.logging.Logger.logAndWait("-- Exiting"); + FutureUtils.wait(VASSAL.tools.logging.Logger.logAndWait("-- Exiting")); } return !cancelled; Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/ImageSaver.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/ImageSaver.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/ImageSaver.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -18,6 +18,7 @@ */ package VASSAL.build.module.map; +import java.awt.Color; import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics2D; @@ -53,6 +54,7 @@ import VASSAL.build.GameModule; import VASSAL.build.module.Map; import VASSAL.build.module.documentation.HelpFile; +import VASSAL.configure.ColorConfigurer; import VASSAL.configure.Configurer; import VASSAL.configure.ConfigurerFactory; import VASSAL.configure.IconConfigurer; @@ -192,11 +194,29 @@ dialog.setIndeterminate(true); dialog.setLocationRelativeTo(frame); + // get the dimensions of the image to write final Dimension s = map.mapSize(); - s.width *= map.getZoom(); - s.height *= map.getZoom(); - writeMapRectAsImage(file, 0, 0, s.width, s.height); + + if (s.width == 0) s.width = 1; + if (s.height == 0) s.height = 1; + int w = (int) Math.round(s.width * map.getZoom()); + int h = (int) Math.round(s.height * map.getZoom()); + + // ensure that the resulting image is at least 1x1 + if (w < 1 || h < 1) { + if (s.width < s.height) { + w = 1; + h = s.height/s.width; + } + else { + h = 1; + w = s.width/s.height; + } + } + + writeMapRectAsImage(file, 0, 0, w, h); + dialog.setVisible(true); } @@ -249,6 +269,9 @@ private final int w; private final int h; + private final Color bg = ColorConfigurer.stringToColor( + map.getAttributeValueString(Map.BACKGROUND_COLOR)); + private final List<File> files = new ArrayList<File>(); // FIXME: SnapshotTask ignores x,y! @@ -274,8 +297,14 @@ // FIXME: do something to estimate how long painting will take final Graphics2D g = img.createGraphics(); + + final Color oc = g.getColor(); + g.setColor(bg); + g.fillRect(0, 0, img.getWidth(), img.getHeight()); + g.setColor(oc); + g.translate(-r.x, -r.y); - map.paintRegion(g, r, null); + map.paintRegion(g, r); g.dispose(); // update the dialog on the EDT Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/boardPicker/Board.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/boardPicker/Board.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/module/map/boardPicker/Board.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -250,7 +250,7 @@ } public Class<?>[] getAllowableConfigureComponents() { - return new Class[] { + return new Class<?>[] { HexGrid.class, SquareGrid.class, RegionGrid.class, @@ -431,16 +431,10 @@ } */ } - else { - if (color != null) { - g.setColor(color); - g.fillRect(visibleRect.x, visibleRect.y, - visibleRect.width, visibleRect.height); - } - else { - g.clearRect(visibleRect.x, visibleRect.y, - visibleRect.width, visibleRect.height); - } + else if (color != null) { + g.setColor(color); + g.fillRect(visibleRect.x, visibleRect.y, + visibleRect.width, visibleRect.height); } if (grid != null) { Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/widget/PanelWidget.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/widget/PanelWidget.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/build/widget/PanelWidget.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -28,9 +28,11 @@ import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JPanel; +import VASSAL.build.BadDataReport; import VASSAL.build.Buildable; import VASSAL.build.Widget; import VASSAL.configure.VisibilityCondition; +import VASSAL.tools.ErrorDialog; /** * A Widget that corresponds to a JPanel with a @@ -188,7 +190,16 @@ if (value instanceof String) { value = new Integer((String) value); } + nColumns = ((Integer) value).intValue(); + + if (nColumns < 1) { + // FIXME: also dialog should not permit values < 1 to be entered + ErrorDialog.dataError( + new BadDataReport("Panel has < 1 column:", getConfigureName())); + + nColumns = 1; + } } else if (VERTICAL.equals(name)) { if (value instanceof String) { Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/AbstractLaunchAction.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/AbstractLaunchAction.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/AbstractLaunchAction.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (c) 2008 by Joel Uckelman + * Copyright (c) 2008-2009 by Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -42,7 +42,11 @@ import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; + import javax.swing.AbstractAction; +import javax.swing.SwingUtilities; // FIXME: switch back to javax.swing.SwingWorker on move to Java 1.6 //import javax.swing.SwingWorker; @@ -68,7 +72,6 @@ import VASSAL.tools.io.IOUtils; import VASSAL.tools.logging.Logger; import VASSAL.tools.logging.LogEntry; -import VASSAL.tools.logging.LogManager; /** * @@ -108,10 +111,12 @@ protected static final List<CommandClient> children = Collections.synchronizedList(new ArrayList<CommandClient>()); + protected static final AtomicInteger nextId = new AtomicInteger(1); public AbstractLaunchAction(String name, Window window, String entryPoint, LaunchRequest lr) { super(name); + this.window = window; this.entryPoint = entryPoint; this.lr = lr; @@ -145,7 +150,9 @@ synchronized (children) { for (CommandClient child : children) { try { - if ("NOK".equals(child.request("REQUEST_CLOSE"))) return false; + if ("NOK".equals(child.request(new Launcher.CloseRequest()))) { + return false; + } } catch (EOFException ignore) { // Normal. Child closed. @@ -219,6 +226,8 @@ } protected class LaunchTask extends SwingWorker<Void,Void> { + protected final int id = nextId.getAndIncrement(); + // lr might be modified before the task is over, keep a local copy protected final LaunchRequest lr = new LaunchRequest(AbstractLaunchAction.this.lr); @@ -344,16 +353,16 @@ } // create a socket for communicating which the child process - serverSocket = - new ServerSocket(0, 50, InetAddress.getByName("localhost")); + serverSocket = new ServerSocket(0, 0, InetAddress.getByName(null)); cmdS = new LaunchCommandServer(serverSocket); - new Thread(cmdS).start(); + new Thread(cmdS, "command server " + id).start(); // build the argument list final ArrayList<String> al = new ArrayList<String>(); al.add(Info.javaBinPath); al.add(""); // reserved for initial heap - al.add(""); // reserved for maximum heap + al.add(""); // reserved for maximum heap + al.add("-DVASSAL.id=" + id); // instance id al.add("-cp"); al.add(System.getProperty("java.class.path")); @@ -412,10 +421,16 @@ final int childPort = din.readInt(); // pump child's stderr to our own stderr - new Thread(new StreamPump(p.getErrorStream(), System.err)).start(); + new Thread( + new StreamPump(p.getErrorStream(), System.err), + "err pump " + id + ).start(); // pump child's stdout to our own stdout - new Thread(new StreamPump(p.getInputStream(), System.out)).start(); + new Thread( + new StreamPump(p.getInputStream(), System.out), + "out pump " + id + ).start(); // Check that the child's port is sane. Reading stdout from a // failed launch tends to give impossible port numbers. @@ -535,55 +550,160 @@ } } - protected class LaunchCommandServer extends CommandServer { + // + // Commands + // + + /** + * The abstract base class of all commands which require access to + * the {@link AbstractLaunchAction} receiving the command. + */ + private static abstract class LaunchCommand implements Command { + protected AbstractLaunchAction theLaunchAction; + + public void init(AbstractLaunchAction la) { + theLaunchAction = la; + } + + public abstract Object execute(); + + }; + + /** + * Notifies the Module Manager that a module has been opened. + */ + public static class NotifyOpenModuleOk extends LaunchCommand { + private static final long serialVersionUID = 1L; + + public Object execute() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ModuleManagerWindow.getInstance() + .addModule(theLaunchAction.lr.module); + theLaunchAction.setWaitCursor(false); + } + }); + + return "OK"; + } + } + + /** + * Notifies the Module Manager that a new module has been created. + */ + public static class NotifyNewModuleOk extends LaunchCommand { + private static final long serialVersionUID = 1L; + + public Object execute() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + theLaunchAction.setWaitCursor(false); + } + }); + + return "OK"; + } + } + + /** + * Notifies the Module Manager that a new module has been imported. + */ + public static class NotifyImportModuleOk extends LaunchCommand { + private static final long serialVersionUID = 1L; + + public Object execute() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + theLaunchAction.setWaitCursor(false); + } + }); + + return "OK"; + } + } + + /** + * Notifies the Module Manager that a module failed to open. + */ + public static class NotifyOpenModuleFailed extends LaunchCommand { + private static final long serialVersionUID = 1L; + + private final Throwable thrown; + + public NotifyOpenModuleFailed(Throwable thrown) { + this.thrown = thrown; + } + + public Object execute() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + theLaunchAction.setWaitCursor(false); + } + }); + + ErrorDialog.showDetails( + thrown, + ThrowableUtils.getStackTrace(thrown), + "Error.module_load_failed", + thrown.getMessage() + ); + + return "OK"; + } + } + + /** + * Notifies the Module Manager that a file was saved successfully. + */ + public static class NotifySaveFileOk implements Command { + private static final long serialVersionUID = 1L; + + private final File file; + + public NotifySaveFileOk(File file) { + this.file = file; + } + + public Object execute() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ModuleManagerWindow.getInstance().update(file); + } + }); + + return "OK"; + } + } + + /** + * Enqueues a {@link LogEntry} from a child process. + */ + public static class EnqueueLogEntry implements Command { + private static final long serialVersionUID = 1L; + + private final LogEntry entry; + + public EnqueueLogEntry(LogEntry entry) { + this.entry = entry; + } + + public Object execute() { + final Future<?> future = Logger.enqueue(entry); + if (entry.wait) FutureUtils.wait(future); + return "OK"; + } + } + + private class LaunchCommandServer extends CommandServer { public LaunchCommandServer(ServerSocket serverSocket) { super(serverSocket); } @Override - protected Object reply(Object cmd) { - if (cmd instanceof LogEntry) { - LogManager.enqueue((LogEntry) cmd); - return "OK"; + public void init(Command command) { + if (command instanceof LaunchCommand) { + ((LaunchCommand) command).init(AbstractLaunchAction.this); } - else if ("LOG_FLUSH".equals(cmd)) { - LogManager.flush(); - return "OK"; - } - else if (cmd instanceof Launcher.SaveFileCmd) { - return ModuleManagerWindow.getInstance().update( - ((Launcher.SaveFileCmd) cmd).getFile()); - } - else if ("NOTIFY_OPEN_OK".equals(cmd)) { - ModuleManagerWindow.getInstance().addModule(lr.module); - setWaitCursor(false); - return "OK"; - } - else if ("NOTIFY_NEW_OK".equals(cmd)) { - setWaitCursor(false); - return "OK"; - } - else if ("NOTIFY_IMPORT_OK".equals(cmd)) { - setWaitCursor(false); - return "OK"; - } - else if (cmd instanceof Launcher.LoadFailedCmd) { - setWaitCursor(false); - - final Throwable thrown = ((Launcher.LoadFailedCmd) cmd).getThrowable(); - - ErrorDialog.showDetails( - thrown, - ThrowableUtils.getStackTrace(thrown), - "Error.module_load_failed", - thrown.getMessage() - ); - - return "OK"; - } - else { - return "UNRECOGNIZED_COMMAND"; - } } } Copied: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Command.java (from rev 5525, VASSAL-src/branches/3.1/src/VASSAL/launch/Command.java) =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Command.java (rev 0) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Command.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -0,0 +1,35 @@ +/* + * $Id$ + * + * Copyright (c) 2009 by Joel Uckelman + * + * 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. + */ + +package VASSAL.launch; + +import java.io.Serializable; + +/** + * The interface for objects passed by {@link CommandClient} to + * {@link CommandServer}. + */ +public interface Command extends Serializable { + /** + * Execute the command. + * + * @return the result + */ + Object execute(); +} Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/CommandServer.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/CommandServer.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/CommandServer.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (c) 2008 by Joel Uckelman + * Copyright (c) 2008-2009 by Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,13 +23,10 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.lang.reflect.InvocationTargetException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; -import javax.swing.SwingUtilities; - import VASSAL.tools.ErrorDialog; import VASSAL.tools.ThrowableUtils; import VASSAL.tools.io.IOUtils; @@ -37,14 +34,14 @@ /** * The base class for socket servers for communication between the * {@link ModuleManager} and its children {@link Player} and {@link Editor} - * processes. Concrete extensions will implement {@link #reply(Object)} to + * processes. Concrete extensions will implement {@link #execute(Object)} to * process incoming requests. * * @see CommandClient * @author Joel Uckelman * @since 3.1.0 */ -public abstract class CommandServer implements Runnable { +public class CommandServer implements Runnable { private final ServerSocket serverSocket; public CommandServer(ServerSocket serverSocket) { @@ -52,25 +49,11 @@ } /** - * Executes a command and returns a reply. - * - * Unlike the rest of <code>CommandServer</code>, this method will be run - * on the EDT, so it is safe to call Swing methods from here. - * - * @param cmd a command object - * @return the reply + * Initialize the {@link Command}. Subclasses handling commands + * which need access to some local state will override this method. */ - protected abstract Object reply(Object cmd); + public void init(Command command) {} - private class Query implements Runnable { - public Object cmd; - public Object result; - - public void run() { - result = reply(cmd); - } - } - public void run() { Socket clientSocket = null; ObjectOutputStream out = null; @@ -80,22 +63,22 @@ out = new ObjectOutputStream(clientSocket.getOutputStream()); in = new ObjectInputStream(clientSocket.getInputStream()); - - final Query q = new Query(); + + Object obj; + Object result; try { - while ((q.cmd = in.readObject()) != null) { - // Execute commands on the EDT - try { - SwingUtilities.invokeAndWait(q); + while ((obj = in.readObject()) != null) { + // Execute commands as they come and send back the reply + if (obj instanceof Command) { + final Command command = (Command) obj; + init(command); + result = command.execute(); } - catch (InterruptedException e) { - ErrorDialog.bug(e); + else { + result = "UNRECOGNIZED_COMMAND"; } - catch (InvocationTargetException e) { - ErrorDialog.bug(e); - } - out.writeObject(q.result); + out.writeObject(result); } } catch (EOFException e) { Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Editor.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Editor.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Editor.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (c) 2000-2008 by Rodney Kinney, Joel Uckelman + * Copyright (c) 2000-2009 by Rodney Kinney, Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -58,69 +58,34 @@ return Info.isMacOSX() ? new MacOSXMenuManager() : new EditorMenuManager(); } - protected CommandServer createCommandServer(ServerSocket serverSocket) { - return new EditorCommandServer(serverSocket); - } - - protected static class EditorCommandServer extends CommandServer { - public EditorCommandServer(ServerSocket serverSocket) { - super(serverSocket); - } - - @Override - protected Object reply(Object cmd) { - if ("REQUEST_CLOSE".equals(cmd)) { - final GameModule module = GameModule.getGameModule(); - final boolean shutDown; - if (module != null) { - module.getFrame().toFront(); - shutDown = module.shutDown(); - } - else { - shutDown = true; - } - - try { - return shutDown ? "OK" : "NOK"; - } - finally { - if (shutDown) System.exit(0); - } - } - else { - return "UNRECOGNIZED_COMMAND"; - } - } - } - protected void launch() throws IOException { Object req = null; switch (lr.mode) { case EDIT: new EditModuleAction(lr.module).loadModule(lr.module); - req = "NOTIFY_OPEN_OK"; + req = new AbstractLaunchAction.NotifyOpenModuleOk(); break; case IMPORT: new ImportAction(null).loadModule(lr.importFile); - req = "NOTIFY_IMPORT_OK"; + req = new AbstractLaunchAction.NotifyImportModuleOk(); break; case NEW: new CreateModuleAction(null).performAction(null); - req = "NOTIFY_NEW_OK"; + req = new AbstractLaunchAction.NotifyNewModuleOk(); break; case EDIT_EXT: GameModule.init(new BasicModule(new DataArchive(lr.module.getPath()))); GameModule.getGameModule().getFrame().setVisible(true); new EditExtensionAction(lr.extension).performAction(null); - req = "NOTIFY_OPEN_OK"; + req = new AbstractLaunchAction.NotifyOpenModuleOk(); break; case NEW_EXT: GameModule.init(new BasicModule(new DataArchive(lr.module.getPath()))); final JFrame f = GameModule.getGameModule().getFrame(); f.setVisible(true); new NewExtensionAction(f).performAction(null); - req = "NOTIFY_OPEN_OK"; + req = new AbstractLaunchAction.NotifyOpenModuleOk(); } if (cmdC != null) { Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Launcher.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Launcher.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Launcher.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (c) 2000-2008 by Rodney Kinney, Joel Uckelman + * Copyright (c) 2000-2009 by Rodney Kinney, Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,6 +26,7 @@ import java.io.ObjectInputStream; import java.io.PrintStream; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; @@ -33,6 +34,7 @@ import javax.swing.SwingUtilities; import VASSAL.Info; +import VASSAL.build.GameModule; import VASSAL.build.module.ExtensionsLoader; import VASSAL.i18n.Resources; import VASSAL.tools.ErrorDialog; @@ -41,7 +43,6 @@ import VASSAL.tools.logging.CommandClientAdapter; import VASSAL.tools.logging.LoggedOutputStream; import VASSAL.tools.logging.Logger; -import VASSAL.tools.logging.LogManager; import VASSAL.tools.logging.LogOutputStreamAdapter; import VASSAL.tools.menu.MenuManager; @@ -91,15 +92,13 @@ // start logging to the errorLog final File errorLog = new File(Info.getHomeDir(), "errorLog"); try { - LogManager.addLogListener( + Logger.addLogListener( new LogOutputStreamAdapter( new FileOutputStream(errorLog))); } catch (IOException e) { WriteErrorDialog.error(e, errorLog); } - - LogManager.start(); } start.startErrorLog(); @@ -117,9 +116,9 @@ try { // set up our command listener final ServerSocket serverSocket = - new ServerSocket(0, 50, InetAddress.getByName("localhost")); - cmdS = createCommandServer(serverSocket); - new Thread(cmdS).start(); + new ServerSocket(0, 0, InetAddress.getByName(null)); + cmdS = new CommandServer(serverSocket); + new Thread(cmdS, "command server").start(); // write our socket port out to the module manager final DataOutputStream out = new DataOutputStream(System.out); @@ -135,8 +134,7 @@ // set up our command client cmdC = new CommandClient(new Socket((String) null, port)); - LogManager.addLogListener(new CommandClientAdapter(cmdC)); - LogManager.start(); + Logger.addLogListener(new CommandClientAdapter(cmdC)); } catch (ClassNotFoundException e) { ErrorDialog.bug(e); @@ -183,7 +181,7 @@ else { // we have a manager, so pass the load failure back to it try { - cmdC.request(new LoadFailedCmd(e1)); + cmdC.request(new AbstractLaunchAction.NotifyOpenModuleFailed(e1)); } catch (IOException e2) { // warn the user directly as a last resort @@ -211,22 +209,44 @@ protected abstract MenuManager createMenuManager(); - protected abstract CommandServer createCommandServer(ServerSocket s); - - public static class LoadFailedCmd implements Serializable { + /** + * A request from the Module Manager to close. + */ + public static class CloseRequest implements Command { private static final long serialVersionUID = 1L; - protected final Throwable t; + private boolean shutdown = true; - public LoadFailedCmd(Throwable throwable) { - t = throwable; - } + public Object execute() { + final GameModule module = GameModule.getGameModule(); + if (module != null) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + module.getFrame().toFront(); + shutdown = module.shutDown(); + } + }); + } + catch (InterruptedException e) { + Logger.log(e); + shutdown = false; + } + catch (InvocationTargetException e) { + ErrorDialog.bug(e); + shutdown = false; + } + } - public Throwable getThrowable() { - return t; + try { + return shutdown ? "OK" : "NOK"; + } + finally { + if (shutdown) System.exit(0); + } } } - + /** * Send a message to the ModuleManager that a file has been saved by the * Editor or the Player @@ -236,25 +256,11 @@ public void sendSaveCmd(File f) { if (cmdC != null) { try { - cmdC.request(new SaveFileCmd(f)); + cmdC.request(new AbstractLaunchAction.NotifySaveFileOk(f)); } // FIXME: review error message catch (IOException e) { } } } - - public static class SaveFileCmd implements Serializable { - private static final long serialVersionUID = 1L; - - protected final File file; - - public SaveFileCmd(File f) { - file = f; - } - - public File getFile() { - return file; - } - } } Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManager.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManager.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManager.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -27,11 +27,15 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.lang.reflect.InvocationTargetException; import java.net.BindException; import java.net.ConnectException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; import javax.swing.JFrame; import javax.swing.JMenuBar; @@ -50,7 +54,6 @@ import VASSAL.tools.ThrowableUtils; import VASSAL.tools.WriteErrorDialog; import VASSAL.tools.io.IOUtils; -import VASSAL.tools.logging.LogManager; import VASSAL.tools.logging.LogOutputStreamAdapter; import VASSAL.tools.logging.LoggedOutputStream; import VASSAL.tools.logging.Logger; @@ -67,17 +70,11 @@ */ public class ModuleManager { - private static final String MODULE_MANAGER_PORT = "moduleManagerPort"; - private static final String MODULE_MANAGER_KEY = "moduleManagerKey"; - private static final String NEXT_VERSION_CHECK = "nextVersionCheck"; public static final String MAXIMUM_HEAP = "maximumHeap"; //$NON-NLS-1$ public static final String INITIAL_HEAP = "initialHeap"; //$NON-NLS-1$ - private static int port; - private static long key; - public static void main(String[] args) { // parse command-line arguments LaunchRequest lr = null; @@ -85,6 +82,7 @@ lr = LaunchRequest.parseArgs(args); } catch (LaunchRequestException e) { +// FIXME: should be a dialog... System.err.println("VASSAL: " + e.getMessage()); System.exit(1); } @@ -103,102 +101,122 @@ return; } - // set up security key so other users can't talk with our socket - final LongConfigurer keyConfig = - new LongConfigurer(MODULE_MANAGER_KEY, null, -1L); - Prefs.getGlobalPrefs().addOption(null, keyConfig); - - key = keyConfig.getLongValue(-1L); - if (key == -1) { - key = (long) (Math.random() * Long.MAX_VALUE); - keyConfig.setValue(key); + // + // How we start exactly one request server: + // + // To ensure that exactly one process acts as the request server, we + // acquire a lock on the ~/VASSAL/key file, and then attempt to acquire + // a lock on the ~/VASSAL/lock file. If we cannot lock ~/VASSAL/lock, + // then there is already a server running; in that case, we read the + // port number and security key from ~/VASSAL/key. If we can lock + // ~/VASSAL/lock, then we start the server, write the port number and + // key to ~/VASSAL/key, and continue to hold the lock on ~/VASSAL/lock. + // Finally, we unlock ~/VASSAL/key and proceed to act as a client, + // sending requests over localhost:port using the security key. + // + // The advantages of this method are: + // + // (1) No race conditions between processes started at the same time. + // (2) No port collisions, because we don't use a predetermined port. + // + + final File keyfile = new File(Info.getConfDir(), "key"); + final File lockfile = new File(Info.getConfDir(), "lock"); + + int port = 0; + long key = 0; + + RandomAccessFile kraf = null; + FileLock klock = null; + try { + // acquire an exclusive lock on the key file + kraf = new RandomAccessFile(keyfile, "rw"); + try { - Prefs.getGlobalPrefs().write(); + klock = kraf.getChannel().lock(); } - catch (IOException e) { -// FIXME: this could clobber the errorLog of an existing MM - WriteErrorDialog.error(e, Prefs.getGlobalPrefs().getFile().getPath()); + catch (OverlappingFileLockException e) { + throw (IOException) new IOException().initCause(e); } - } + + // determine whether we are the server or a client - lr.key = key; + // Note: We purposely keep lout open in the case where we are the + // server, because closing lout will release the lock. + FileLock lock = null; + final FileOutputStream lout = new FileOutputStream(lockfile); + try { + lock = lout.getChannel().tryLock(); + } + catch (OverlappingFileLockException e) { + throw (IOException) new IOException().initCause(e); + } - // set up prefs for port to listen on - final IntConfigurer portConfig = - new IntConfigurer(MODULE_MANAGER_PORT, null, -1); - Prefs.getGlobalPrefs().addOption(null, portConfig); - - // set port from command-line if specified; else try the prefs - if (lr.port >= 0) { - port = lr.port; + if (lock != null) { + // we have the lock, so we will be the request server - // we have a port, write it to the prefs - portConfig.setValue(port); - try { - Prefs.getGlobalPrefs().write(); + // bind to an available port on the loopback device + final ServerSocket serverSocket = + new ServerSocket(0, 0, InetAddress.getByName(null)); + + // write the port number where we listen to the key file + port = serverSocket.getLocalPort(); + kraf.writeInt(port); + + // create new security key and write it to the key file + key = (long) (Math.random() * Long.MAX_VALUE); + kraf.writeLong(key); + + // create a new Module Manager + new ModuleManager(serverSocket, key, lout, lock); } - catch (IOException e) { -// FIXME: this could clobber the errorLog of an existing MM - WriteErrorDialog.error(e, Prefs.getGlobalPrefs().getFile().getPath()); + else { + // we do not have the lock, so we will be a request client + lout.close(); + + // read the port number we will connect to from the key file + port = kraf.readInt(); + + // read the security key from the key file + key = kraf.readLong(); } - } - else { - port = portConfig.getIntValue(-1); - } - // try to create ModuleManager - try { - new ModuleManager(); + kraf.close(); } - catch (BindException e) { - // if this fails, a ModuleManager is (probably) already listening -// FIXME: we should set a flag noting that this failed, so we can throw -// up a warning later if we can't connect to the socket - } catch (IOException e) { - // should not happen -// FIXME: this could clobber the errorLog of an existing MM - ErrorDialog.bug(e); +// FIXME: should be a dialog... + System.err.println("VASSAL: IO error"); + e.printStackTrace(); System.exit(1); - } + } + finally { + // this will also release the lock on the key file + IOUtils.closeQuietly(kraf); + } + + lr.key = key; // pass launch parameters on to the ModuleManager via the socket Socket clientSocket = null; ObjectOutputStream out = null; InputStream in = null; - try { - try { - clientSocket = new Socket((String) null, port); - } - catch (IOException e) { - System.err.println("VASSAL: Couldn't open socket on port " + port); - e.printStackTrace(); - System.exit(1); - } + clientSocket = new Socket((String) null, port); - try { - out = new ObjectOutputStream( - new BufferedOutputStream(clientSocket.getOutputStream())); - out.writeObject(lr); - out.flush(); - } - catch (IOException e) { - System.err.println("VASSAL: Couldn't write to socket on port " + port); - e.printStackTrace(); - System.exit(1); - } + out = new ObjectOutputStream( + new BufferedOutputStream(clientSocket.getOutputStream())); + out.writeObject(lr); + out.flush(); - try { - in = clientSocket.getInputStream(); - IOUtils.copy(in, System.err); - } - catch (IOException e) { - System.err.println("VASSAL: Couldn't read from socket on port " + port); - e.printStackTrace(); - System.exit(1); - } + in = clientSocket.getInputStream(); + IOUtils.copy(in, System.err); } + catch (IOException e) { +// FIXME: should be a dialog... + System.err.println("VASSAL: Problem with socket on port " + port); + e.printStackTrace(); + System.exit(1); + } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); @@ -206,61 +224,40 @@ } } - private final ServerSocket serverSocket; - private static ModuleManager instance = null; public static ModuleManager getInstance() { return instance; } - public ModuleManager() throws IOException { + private final long key; + + private FileOutputStream lout; + private FileLock lock; + + private final ServerSocket serverSocket; + + public ModuleManager(ServerSocket serverSocket, long key, + FileOutputStream lout, FileLock lock) + throws IOException { + if (instance != null) throw new IllegalStateException(); instance = this; - // if the port is bad, try a random port - if (port < 0 || port > 65535) { - ServerSocket socket = null; + this.serverSocket = serverSocket; + this.key = key; - while (socket == null) { - // check a random port in the range [49152,65535] - port = (int)(Math.random() * 16384) + 49152; - try { - socket = - new ServerSocket(port, 50, InetAddress.getByName("localhost")); + // we hang on to these to prevent the lock from being lost + this.lout = lout; + this.lock = lock; - // we have a port, write it to the prefs - final IntConfigurer portConfig = (IntConfigurer) - Prefs.getGlobalPrefs().getOption(MODULE_MANAGER_PORT); - portConfig.setValue(port); - try { - Prefs.getGlobalPrefs().write(); - } - catch (IOException e) { - WriteErrorDialog.error(e, - Prefs.getGlobalPrefs().getFile().getPath()); - } - } - catch (ConnectException e) { - // we can't connect, try another port - IOUtils.closeQuietly(socket); - } - } - - serverSocket = socket; - } - else { - serverSocket = - new ServerSocket(port, 50, InetAddress.getByName("localhost")); - } - final StartUp start = Info.isMacOSX() ? new ModuleManagerMacOSXStartUp() : new StartUp(); // start logging to the errorLog final File errorLog = new File(Info.getHomeDir(), "errorLog"); try { - LogManager.addLogListener( + Logger.addLogListener( new LogOutputStreamAdapter( new FileOutputStream(errorLog))); } @@ -268,8 +265,6 @@ WriteErrorDialog.error(e, errorLog); } - LogManager.start(); - start.startErrorLog(); // log everything which comes across our stderr @@ -290,7 +285,7 @@ }); // ModuleManagerWindow.getInstance() != null now, so listen on the socket - new Thread(new SocketListener(serverSocket)).start(); + new Thread(new SocketListener(serverSocket), "socket listener").start(); final Prefs globalPrefs = Prefs.getGlobalPrefs(); @@ -408,8 +403,42 @@ return "incorrect key"; } -// FIXME: is this being run on the EDT? + final LaunchRequestHandler handler = new LaunchRequestHandler(lr); + try { + SwingUtilities.invokeAndWait(handler); + } + catch (InterruptedException e) { + return "interrupted"; // FIXME + } + catch (InvocationTargetException e) { + ErrorDialog.bug(e); + return null; + } + + return handler.getResult(); + } + else { + return "unrecognized command"; // FIXME + } + } + private static class LaunchRequestHandler implements Runnable { + private final LaunchRequest lr; + private String result; + + public LaunchRequestHandler(LaunchRequest lr) { + this.lr = lr; + } + + public void run() { + result = handle(); + } + + public String getResult() { + return result; + } + + private String handle() { final ModuleManagerWindow window = ModuleManagerWindow.getInstance(); switch (lr.mode) { @@ -459,7 +488,8 @@ new Editor.LaunchAction(window, lr.module).actionPerformed(null); break; case IMPORT: - new Editor.ImportLaunchAction(window, lr.importFile).actionPerformed(null); + new Editor.ImportLaunchAction( + window, lr.importFile).actionPerformed(null); break; case NEW: new Editor.NewModuleLaunchAction(window).actionPerformed(null); @@ -468,13 +498,12 @@ return "not yet implemented"; // FIXME case NEW_EXT: return "not yet implemented"; // FIXME + default: + return "unrecognized mode"; // FIXME } + + return null; } - else { - return "unrecognized command"; // FIXME - } - - return null; } private static class ModuleManagerMenuManager extends MenuManager { Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManagerWindow.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManagerWindow.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/ModuleManagerWindow.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -106,13 +106,13 @@ import VASSAL.tools.BrowserSupport; import VASSAL.tools.ComponentSplitter; import VASSAL.tools.ErrorDialog; +import VASSAL.tools.FutureUtils; import VASSAL.tools.SequenceEncoder; import VASSAL.tools.WriteErrorDialog; import VASSAL.tools.filechooser.FileChooser; import VASSAL.tools.filechooser.ModuleExtensionFileFilter; import VASSAL.tools.io.IOUtils; import VASSAL.tools.logging.Logger; -import VASSAL.tools.logging.LogManager; import VASSAL.tools.logging.LogPane; import VASSAL.tools.menu.CheckBoxMenuItemProxy; import VASSAL.tools.menu.MenuBarProxy; @@ -192,7 +192,7 @@ IOUtils.closeQuietly(gl); } - Logger.logAndWait("-- Exiting"); + FutureUtils.wait(Logger.logAndWait("-- Exiting")); System.exit(0); } }; @@ -596,9 +596,8 @@ * A File has been saved or created by the Player or the Editor. Update * the display as necessary. * @param f The file - * @return */ - public String update(File f) { + public void update(File f) { final AbstractMetaData data = MetaDataFactory.buildMetaData(f); // Module. @@ -623,7 +622,7 @@ for (ExtensionInfo ext : moduleInfo.getExtensions()) { if (ext.getFile().equals(f)) { moduleNode.refresh(); - return "OK"; + return; } } } @@ -636,14 +635,15 @@ for (int i = 0; i < rootNode.getChildCount(); i++) { final MyTreeNode moduleNode = rootNode.getChild(i); final MyTreeNode folderNode = moduleNode.findNode(f.getParentFile()); - if (folderNode != null && folderNode.getNodeInfo() instanceof GameFolderInfo) { - ((GameFolderInfo) folderNode.getNodeInfo()).update(f); - return "OK"; + if (folderNode != null && + folderNode.getNodeInfo() instanceof GameFolderInfo) { + ((GameFolderInfo) folderNode.getNodeInfo()).update(f); + return; } } } + tree.repaint(); - return "OK"; } /** @@ -1778,7 +1778,7 @@ // FIXME: What to do here???? } - LogManager.addLogListener(lp); + Logger.addLogListener(lp); // FIXME: this should have its own key. Probably keys should be renamed // to reflect what they are labeling, e.g., Help.show_error_log_menu_item, @@ -1793,7 +1793,7 @@ d.addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent e) { // unregister the LogPane when this dialog is closed - LogManager.removeLogListener(lp); + Logger.removeLogListener(lp); } }); Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Player.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Player.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/launch/Player.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -1,7 +1,7 @@ /* * $Id$ * - * Copyright (c) 2000-2008 by Rodney Kinney, Joel Uckelman + * Copyright (c) 2000-2009 by Rodney Kinney, Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,6 +27,7 @@ import javax.swing.Action; import javax.swing.JFrame; import javax.swing.JMenuBar; +import javax.swing.SwingUtilities; import VASSAL.Info; import VASSAL.build.GameModule; @@ -60,41 +61,6 @@ return Info.isMacOSX() ? new MacOSXMenuManager() : new PlayerMenuManager(); } - protected CommandServer createCommandServer(ServerSocket serverSocket) { - return new PlayerCommandServer(serverSocket); - } - - protected static class PlayerCommandServer extends CommandServer { - public PlayerCommandServer(ServerSocket serverSocket) { - super(serverSocket); - } - - @Override - protected Object reply(Object cmd) { - if ("REQUEST_CLOSE".equals(cmd)) { - final GameModule module = GameModule.getGameModule(); - final boolean shutDown; - if (module != null) { - module.getFrame().toFront(); - shutDown = module.shutDown(); - } - else { - shutDown = true; - } - - try { - return shutDown ? "OK" : "NOK"; - } - finally { - if (shutDown) System.exit(0); - } - } - else { - return "UNRECOGNIZED_COMMAND"; - } - } - } - protected void launch() throws IOException { if (lr.builtInModule) { GameModule.init(createModule(createDataArchive())); @@ -125,7 +91,7 @@ if (cmdC != null) { try { - cmdC.request("NOTIFY_OPEN_OK"); + cmdC.request(new AbstractLaunchAction.NotifyOpenModuleOk()); } catch (IOException e) { // This is not fatal, since we've successfully opened the module, Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/ErrorDialog.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/ErrorDialog.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/ErrorDialog.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -53,9 +53,7 @@ } // show a bug report dialog if one has not been shown before else if (!DialogUtils.setDisabled(BugDialog.class, true)) { -// FIXME: logAndWait() probably doesn't work and creates a race condition. -// The logging system needs to be rethought... - Logger.logAndWait(thrown, Logger.BUG); + FutureUtils.wait(Logger.logAndWait(thrown, Logger.BUG)); final Frame frame = GameModule.getGameModule() == null ? null : GameModule.getGameModule().getFrame(); Modified: VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/MemoryUtils.java =================================================================== --- VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/MemoryUtils.java 2009-04-20 22:04:35 UTC (rev 5526) +++ VASSAL-src/branches/swampwallaby-3.1/src/VASSAL/tools/MemoryUtils.java 2009-04-21 11:10:06 UTC (rev 5527) @@ -18,16 +18,24 @@ */ package VASSAL.tools; -import java.io.File; +import java.util.HashMap; +import java.util.Map; import com.sun.jna.Library; import com.sun.jna.Native; +import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.LongByReference; +import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.StdCallLibrary; +import com.sun.jna.win32.W32APIFunctionMapper; +import com.sun.jna.win32.W32APITypeMapper; +/** + * A utility class for getting information about system memory. + */ public class MemoryUtils { private MemoryUtils() {} @@ -56,7 +64,14 @@ IMPL = new MacOSXMemoryUtilsImpl(); } else if (os.startsWith("windows")) { - IMPL = new WindowsMemoryUtilsImpl(); + // kernel32 didn't exist until Windows 2000, so use the dummy + // implementation for Windows 98 and Windows ME. + if (os.equals("windows 98") || os.equals("windows me")) { + IMPL = new DummyMemoryUtilsImpl(); + } + else { + IMPL = new WindowsMemoryUtilsImpl(); + } } else { IMPL = new DummyMemoryUtilsImpl(); @@ -68,10 +83,60 @@ } private static class LinuxMemoryUtilsImpl implements MemoryUtilsImpl{ + public static interface Libc extends Library { + + /** + * The Java peer of the sysinfo struct. + * + * See the man page for <code>sysinfo(2)</code> for details. + */ + public static final class sysinfo extends Structure { + public NativeLong uptime; + public NativeLong[] loads = new NativeLong[3]; + public NativeLong totalram; + public NativeLong freeram; + public NativeLong sharedram; + public NativeLong bufferram; + public NativeLong totalswap; + public NativeLong freeswap; + public short procs; + public NativeLong totalhigh; + public NativeLong freehigh; + public int mem_unit; + + // Note: On 64-bit systems, _f would point to the end of the struct. + // JNA complains if we have a zero-length byte array, so we make + // sure that the padding is always at least one byte long. + public byte[] _f = new byte[ + Math.max(20-2*NativeLong.SIZE-Native.getNativeSize(int.class), 1)]; + } + + /** + * Wrapper for sysinfo(2). + */ + int sysinfo(sysinfo info); + + /** + * Wrapper for strerror(3). + */ + String strerror(int errno); + + public static final Libc INSTANCE = + (Libc) Native.loadLibrary("c", Libc.class); + } + public long getPhysicalMemory() { - // The file /proc/kcore corresponds to the physical RAM on Linux. - // We use File.length() to stat it, giving us the RAM in bytes. - return new File("/proc/kcore").length(); + final Libc.sysinfo info = new Libc.sysinfo(); + if (Libc.INSTANCE.sysinfo(info) == 0) { + return info.totalram.longValue() * info.mem_unit; + } + else { + final int errno = Native.getLastError(); + System.err.p... [truncated message content] |