View Javadoc

1   // START LICENSE
2   // JSet - a Java JSet card board game implementation
3   // Copyright (C) 2004 Jerome Lacoste
4   //
5   // This program is free software; you can redistribute it and/or modify
6   // it under the terms of the GNU General Public License as published by
7   // the Free Software Foundation; either version 2 of the License, or (at
8   // your option) any later version.
9   //
10  // This program is distributed in the hope that it will be useful, but
11  // WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // General Public License for more details.
14  //
15  // You should have received a copy of the GNU General Public License
16  // along with this program; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  // END LICENSE
19  
20  package org.cb.jset.client;
21  
22  import org.cb.jset.CardSet;
23  import org.cb.jset.MatchingException;
24  import org.cb.jset.CardProperties;
25  import org.cb.jset.server.RemoteSetGame;
26  import org.cb.jset.server.RemoteSetGamePlayer;
27  import org.cb.jset.server.RemoteSetGameConnection;
28  
29  import org.apache.log4j.Logger;
30  
31  import java.rmi.RemoteException;
32  import java.rmi.NoSuchObjectException;
33  import java.rmi.server.UnicastRemoteObject;
34  import java.io.Serializable;
35  
36  /***
37   * FIXME document
38   *
39   * @author jerome@coffeebreaks.org - last modified by $LastModifiedBy$
40   * @version $Id: NetworkGame.java 129 2004-04-15 05:00:43Z jerome $
41   */
42  public class NetworkGame extends AbstractGame
43  {
44    private Logger _logger = Logger.getLogger(NetworkGame.class);
45  
46    private RemoteSetGame _remoteGame;
47    private PlayerRemoteGame _playerRemoteGame;
48    private RemoteSetGameConnection _gameConnection;
49    private final String _clientName;
50  
51    private boolean _wasDisconnected;
52  
53    public NetworkGame(final String clientName)
54    {
55      _clientName = clientName;
56    }
57  
58    static class PlayerRemoteGame implements RemoteSetGamePlayer, Serializable
59    {
60      private final transient Logger _logger = Logger.getLogger(PlayerRemoteGame.class);
61      private boolean _isBlocked;
62      private final String _clientName;
63      private transient final NetworkGame _game;
64  
65      public PlayerRemoteGame(final NetworkGame game, final String clientName)
66      {
67        _game = game;
68        _clientName = clientName;
69      }
70  
71      public String getName() throws RemoteException
72      {
73        return _clientName;
74      }
75  
76      public void block() throws RemoteException
77      {
78        _isBlocked = true;
79        // FIXME forward events?
80      }
81  
82      public void unblock() throws RemoteException
83      {
84        _isBlocked = false;
85        // FIXME forward events?
86      }
87  
88      public boolean isBlocked() throws RemoteException
89      {
90        return _isBlocked;  //To change body of implemented methods use File | Settings | File Templates.
91      }
92  
93      public void disconnect() throws RemoteException
94      {
95        _game.setWasDisconnected(true);
96      }
97  
98      public void cardsAdded(final CardProperties[] cards) throws RemoteException
99      {
100       _logger.debug("Cards Added " + CardProperties.toString(cards));
101       _game.fireCardsAdded(cards);
102     }
103 
104     public void setRemoved(final CardSet set) throws RemoteException
105     {
106       _logger.debug("Set removed " + set);
107       _game.fireSetRemoved(set);
108     }
109 
110     void registerClient()
111         throws RemoteException
112     {
113       UnicastRemoteObject.exportObject(this);
114     }
115 
116     private void unregisterClient()
117         throws NoSuchObjectException
118     {
119       final boolean lUnexported = UnicastRemoteObject.unexportObject(this, false);
120       if (!lUnexported)
121       {
122         _logger.warn("Failure to unexport. Calls pending!! trying to force");
123         final boolean lUnexportedSecondTry = UnicastRemoteObject.unexportObject(this, true);
124         _logger.info("Managed to disconnect? " + lUnexportedSecondTry);
125       }
126     }
127 
128   }
129 
130   public void setRemoteGame(final RemoteSetGame remoteGame)
131   {
132     _remoteGame = remoteGame;
133   }
134 
135   public void start()
136   {
137     _playerRemoteGame = new PlayerRemoteGame(this, _clientName);
138     final GameThread thread = new GameThread();
139     // FIXME do we want this?
140     // thread.setDaemon(true);
141     thread.start();
142   }
143 
144 
145   private void connect() throws RemoteException
146   {
147     _gameConnection = _remoteGame.connect(_playerRemoteGame);
148   }
149 
150 
151   public synchronized void setWasDisconnected(final boolean wasDisconnected)
152   {
153     _wasDisconnected = wasDisconnected;
154     notifyAll();
155   }
156 
157   public synchronized boolean wasDisconnected()
158   {
159     return _wasDisconnected;
160   }
161 
162   public void stop()
163   {
164     try
165     {
166       _gameConnection.close();
167     }
168     catch (RemoteException e)
169     {
170       _logger.debug("Failure to stop game " + e.getMessage(), e);
171       setWasDisconnected(true);
172     }
173   }
174 
175   public void endTurn()
176   {
177     // FIXME unused
178   }
179 
180   public void removeSet(final CardSet set) throws MatchingException
181   {
182     try
183     {
184       _gameConnection.matchSet(set);
185     }
186     catch (RemoteException e)
187     {
188       _logger.debug("Failure to remove set " + e.getMessage(), e);
189       setWasDisconnected(true);
190     }
191   }
192 
193 
194   private void forceCloseConnection()
195   {
196     try
197     {
198       //	    if (! _connection.isClosed()) {
199       if (_gameConnection != null && !this.wasDisconnected())
200       {
201         if (_logger.isInfoEnabled())
202         {
203           _logger.info("May have caught a CTRL-C");
204         }
205         _gameConnection.close();
206       }
207     }
208     catch (RemoteException e)
209     {
210       _logger.error("Communication problem while closing.", e);
211     }
212   }
213 
214   void play() throws RemoteException
215   {
216 //        int oldCounter = _counter;
217     if (_logger.isInfoEnabled())
218     {
219       _logger.info("-- starting to play --");
220     }
221     while (! (_gameConnection.isClosed() && this.wasDisconnected()))
222     {
223       if (_logger.isInfoEnabled())
224       {
225         _logger.info("-- wait for cards to be added --");
226       }
227       synchronized (this)
228       {
229         try
230         {
231           this.wait();
232         }
233         catch (InterruptedException e)
234         {
235         }
236       }
237     }
238     if (_logger.isInfoEnabled())
239     {
240       _logger.info("-- player dying --");
241     }
242   }
243 
244   void addShutdownHook()
245   {
246     Runtime.getRuntime().addShutdownHook(new Thread()
247     {
248       public void run()
249       {
250         forceCloseConnection();
251       }
252     });
253   }
254 
255   private class GameThread extends Thread
256   {
257     public void run()
258     {
259       addShutdownHook();
260       try
261       {
262         registerClient();
263         try
264         {
265           connect();
266           play();
267         }
268         catch (RemoteException e)
269         {
270           _logger.error("Communication error in run().", e);
271         }
272         finally
273         {
274           unregisterClient();
275         }
276       }
277       catch (RemoteException e)
278       {
279         _logger.error("Couldn't export Client object properly.", e);
280       }
281       finally
282       {
283         // FIXME reestablish
284 //          _eventHandler.fireGameEvent(new GameEvent(GameEvent.END));
285         _gameConnection = null;
286       }
287       if (_logger.isInfoEnabled())
288       {
289         _logger.info("player ending...");
290       }
291     }
292 
293     private void registerClient()
294         throws RemoteException
295     {
296       UnicastRemoteObject.exportObject(_playerRemoteGame);
297     }
298 
299     private void unregisterClient()
300         throws NoSuchObjectException
301     {
302       final boolean lUnexported = UnicastRemoteObject.unexportObject(_playerRemoteGame, false);
303       if (!lUnexported)
304       {
305         _logger.warn("Failure to unexport. Calls pending!! trying to force");
306         final boolean lUnexportedSecondTry = UnicastRemoteObject.unexportObject(_playerRemoteGame, true);
307         _logger.info("Managed to disconnect? " + lUnexportedSecondTry);
308       }
309     }
310   }
311 }