View Javadoc

1   /*
2    * Copyright 2004 (C) Applied Software Engineering--TU Muenchen
3    *                    http://wwwbruegge.in.tum.de
4    *
5    * This file is part of ARENA.
6    *
7    * ARENA is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * ARENA is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with ARENA; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */
21  package org.globalse.arena.server;
22  
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileNotFoundException;
26  import java.io.IOException;
27  import java.lang.reflect.InvocationTargetException;
28  import java.net.MalformedURLException;
29  import java.rmi.Naming;
30  import java.rmi.RMISecurityManager;
31  import java.rmi.RemoteException;
32  import java.rmi.registry.LocateRegistry;
33  import java.rmi.server.RMIClassLoader;
34  import java.util.logging.LogManager;
35  import java.util.logging.Logger;
36  
37  import org.globalse.arena.remote.LeagueInfo;
38  import org.globalse.arena.remote.MatchPanelFactory;
39  import org.globalse.arena.remote.RemoteLeague;
40  import org.globalse.arena.remote.RemoteTournament;
41  import org.globalse.arena.remote.TournamentInfo;
42  import org.globalse.arena.remote.exceptions.ArenaException;
43  import org.globalse.arena.remote.exceptions.GameAlreadyExistsException;
44  import org.globalse.arena.remote.exceptions.GameNotFoundException;
45  import org.globalse.arena.user.DefaultAccessPolicy;
46  import org.globalse.arena.user.User;
47  import org.globalse.arena.util.PropertyLoader;
48  
49  /***
50   * This class provides the main method for starting an arena server. It takes an
51   * optional argument on the command line which specified a filename or a URL for
52   * a properties file. The properties understood by StartArena include:
53   * <UL>
54   *   <LI><code>ArenaPort</code> (default 1099)      The TCP/IP port on which the arena server should accept RMI connections</LI>
55   *   <LI><code>CodeBase</code>  (no default)        A list of space-separated URLs specifying where to load game-specified and tournament style specific classes.</LI>
56   *   <LI><code>TournamentStyles</code> (no default) A space-separated list of fully qualified class names of tournament styles to be loaded into this arena.</LI>
57   *   <LI><code>Games</code> (no default)            A space-separated list of fully qualified class names of games to be loaded into this arena.</LI>
58   *   <LI><code>SetupDemo</code> (default false)     A flag specifying whether test users, leagues, tournaments, and matches should be created.</LI>
59   * </UL>
60   * <P>In addition, this class will use the properties file to initialize the loggers. See the
61   * documentation on java.util.logging for information about logging properties.</P>
62   *
63   * <P>Once it creates and intializes the arena, this class dynamically loads the specified tournament styles
64   * and games and registers them. If at any point the initialization or loading fails, the main method
65   * exists with status 1.</P>
66   *
67   * @author Allen Dutoit
68   */
69  public class StartArena {
70  	
71  	private static Logger logger = Logger.getLogger("org.globalse.arena.server");
72  	
73  	private static Arena arena = Arena.getInstance();
74  	private static String codeBase = null;
75  	
76  	// Constants used by the setupDemo method for loading the tic tac toe example
77  	private static final String TICTACTOE = "TicTacToe";
78  	private static final String TICTACTOE_CLASSNAME = "org.globalse.arena.ttt.TicTacToe";
79  	private static final String TICTACTOEFACTORY_CLASSNAME = TICTACTOE_CLASSNAME + "MatchPanelFactory";
80  	private static final String KNOCK_OUT = "KnockOutStyle";
81  	private static final String KNOCKOUT_CLASSNAME = "org.globalse.arena.styles.KnockOutStyle";
82  	
83  	private static void tellUser(String message) {
84  		System.out.println(message);
85  	}
86  	
87  	private static PropertyLoader loadProperties(String fileName) {
88  		PropertyLoader propertyLoader = null;
89  		try {
90  			propertyLoader = new PropertyLoader(fileName);
91  		} catch (FileNotFoundException e) {
92  			tellUser("Properties file \"" + fileName + "\" not found.");
93  			System.exit(1);
94  		} catch (IOException e) {
95  			tellUser("An exception occured while reading from properties file \"" + fileName + "\": " + e.getMessage());
96  			e.printStackTrace();
97  			System.exit(1);
98  		}
99  		return propertyLoader;
100 	}
101 	
102 	private static void initLogger(String fileName) {
103 		if (fileName != null) {
104 			LogManager logManager = LogManager.getLogManager();
105 			try {
106 				logManager.readConfiguration(new FileInputStream(new File(fileName)));
107 			} catch (Exception e) {
108 				tellUser("An exception occured while configuring the logger from properties file \"" + fileName + "\": " + e.getMessage());
109 			}
110 		}
111 	}
112 	
113 	private static void initArena(PropertyLoader propertyLoader) {
114 		try {
115 			Arena.init();
116 			arena = Arena.getInstance();
117 			// TODO: Use properties to determine the class name of the access policy.
118 			arena.setAccessPolicy(new DefaultAccessPolicy());
119 			// set the admnistrator
120 			String operatorName = propertyLoader.getStringProperty("Operator", "admin");
121 			String operatorPassword = propertyLoader.getStringProperty("OperatorPassword", "adminpass");
122 			User operator = arena.createUser(operatorName, operatorPassword);
123 			arena.setOperator(operator);
124 			int serverPort = propertyLoader.getIntProperty("ArenaPort", 1099);
125 			LocateRegistry.createRegistry(serverPort);
126 			tellUser("Registering arena on port " + serverPort + " ...");
127 			Naming.rebind("//localhost:" + serverPort + "/ArenaServer", arena);
128 			tellUser("... arena successfully registered as a remote object.");
129 		} catch (Exception e) {
130 			tellUser("Failed to initialize arena: " + e.getMessage());
131 			e.printStackTrace();
132 		}
133 	}
134 	
135 	private static Object getInstanceOfClass(String className) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, SecurityException, IllegalAccessException, IllegalArgumentException, MalformedURLException {
136 		Class result = null;
137 		try {
138 			tellUser("Loading class " + className + " from classpath.");
139 			result = Class.forName(className);
140 		} catch (ClassNotFoundException e) {
141 			tellUser(className + " not found in classpath, trying remote loading from codebase (" + codeBase + ").");
142 			result = RMIClassLoader.loadClass(codeBase, className);
143 		}
144 		return result.getMethod("getInstance", null).invoke(null, null);
145 	}
146 	
147 	private static void registerTournamentStyles(PropertyLoader propertyLoader) {
148 		String [] styleClassNames = propertyLoader.getStringArrayProperty("TournamentStyles");
149 		String styleClassName = null;
150 		boolean loadingSucceeded = false;
151 		try {
152 			for (int i = 0; i < styleClassNames.length; i++) {
153 				styleClassName = styleClassNames[i];
154 				String defaultStyleName = styleClassName.substring(styleClassName.lastIndexOf(".")+1);
155 				String styleName = propertyLoader.getStringProperty(styleClassName + ".name", defaultStyleName);
156 				TournamentStyle style = (TournamentStyle)getInstanceOfClass(styleClassName);
157 				arena.registerTournamentStyle(styleName, style);
158 			}
159 			loadingSucceeded = true;
160 		} catch (ClassNotFoundException e) {
161 			tellUser("Tournament style class \"" + styleClassName + "\" not found (neither in class path nor in codebase).");
162 		} catch (MalformedURLException e) {
163 			tellUser("Failed to remotely load tournament style class \"" + styleClassName +
164 						 "\" because codebase URL is not well-formed (\"" + codeBase + "\").");
165 		} catch (NoSuchMethodException e) {
166 			tellUser("Tournament style class \"" + styleClassName +
167 						 "\" does not define a public static method getInstance().");
168 		} catch (Exception e) {
169 			tellUser("Tournament style class \"" + styleClassName +
170 						 "\" static method getInstance() threw exception: " + e.getMessage());
171 			e.printStackTrace();
172 		}
173 		if (!loadingSucceeded) {
174 			System.exit(1);
175 		}
176 	}
177 	
178 	private static String registerOneGame(PropertyLoader propertyLoader, String gameClassName) throws MalformedURLException, SecurityException, IllegalArgumentException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, GameAlreadyExistsException {
179 		String defaultStyleName = gameClassName.substring(gameClassName.lastIndexOf(".")+1);
180 		String gameName = propertyLoader.getStringProperty(gameClassName + ".name", defaultStyleName);
181 		String gameDescription = propertyLoader.getStringProperty(gameClassName + ".description", "");
182 		Game game = (Game)getInstanceOfClass(gameClassName);
183 		String panelFactoryClassName = propertyLoader.getStringProperty(gameClassName + ".factory", null);
184 		if (panelFactoryClassName == null) {
185 			panelFactoryClassName = gameClassName + "MatchPanelFactory";
186 		}
187 		MatchPanelFactory matchPanelFactory = (MatchPanelFactory)getInstanceOfClass(panelFactoryClassName);
188 		arena.registerGame(game, gameName, gameDescription, matchPanelFactory);
189 		return gameName;
190 	}
191 	
192 	private static void registerGames(PropertyLoader propertyLoader) {
193 		String [] gameClassNames = propertyLoader.getStringArrayProperty("Games");
194 		String gameClassName = null;
195 		boolean loadingSucceeded = false;
196 		try {
197 			for (int i = 0; i < gameClassNames.length; i++) {
198 				gameClassName = gameClassNames[i];
199 				registerOneGame(propertyLoader, gameClassName);
200 			}
201 			loadingSucceeded = true;
202 		} catch (ClassNotFoundException e) {
203 			tellUser("Failed to load game class or associated match panel factory \"" + gameClassName + "\":" + e.getMessage());
204 		} catch (NoSuchMethodException e) {
205 			tellUser("Game class \"" + gameClassName + "\" does not define a static method getInstance().\n" + e.getMessage());
206 		} catch (Exception e) {
207 			tellUser("Game class \"" + gameClassName + "\" static method getInstance() threw exception: " + e.getMessage());
208 		}
209 		if (!loadingSucceeded) {
210 			System.exit(1);
211 		}
212 	}
213 	
214 	
215 	private static void setupDemo(PropertyLoader propertyLoader) {
216 		if (!propertyLoader.getBooleanProperty("SetupDemo", false)) {
217 			return;
218 		}
219 		String gameName = propertyLoader.getStringProperty("DemoGameName", "TicTacToe");
220 		tellUser("Setting up demo for game " + gameName);
221 		
222 		try {
223 			arena.getGameByName(gameName);
224 		} catch (GameNotFoundException e) {
225 			tellUser("Demo game \"" + gameName + "\" is not registered with arena.");
226 			tellUser("The Games and <game class>.name properties should be set for each game. For example:");
227 			tellUser("Games=org.globalse.arena.ttt.TicTacToe");
228 			tellUser("org.globalse.arena.ttt.TicTacToe.name=TicTacToe");
229 			System.exit(1);
230 		}
231 		
232 		User alice = null, joe = null, mike = null, mark = null, mary = null, bob = null;
233 		try {
234 			// Create demo users
235 			alice = arena.createUser("alice", "alicepass");
236 			joe = arena.createUser("joe", "joepass");
237 			mike = arena.createUser("mike", "mikepass");
238 			mark = arena.createUser("mark", "markpass");
239 			mary = arena.createUser("mary", "marypass");
240 			bob = arena.createUser("bob", "bobpass");
241 			
242 		} catch (Exception e) {
243 			tellUser("Failed to create demo users.");
244 			e.printStackTrace();
245 			System.exit(1);
246 		}
247 		
248 		// register the KO tournament style if needed
249 		try {
250 			if (arena.getTournamentStyleByName(KNOCK_OUT) == null) {
251 				TournamentStyle ko = (TournamentStyle)getInstanceOfClass(KNOCKOUT_CLASSNAME);
252 				arena.registerTournamentStyle(KNOCK_OUT, ko);
253 			}
254 		} catch (Exception e) {
255 			tellUser("Failed to load knock out tournament style.");
256 			e.printStackTrace();
257 			System.exit(1);
258 		}
259 		
260 		try {
261 			String operatorName = propertyLoader.getStringProperty("Operator", "admin");
262 			String operatorPassword = propertyLoader.getStringProperty("OperatorPassword", "adminpass");
263 			String operatorTicket = arena.login(operatorName, operatorPassword);
264 			tellUser("Operator logged in with ticket: " + operatorTicket);
265 
266 			String bobTicket = arena.login("bob", "bobpass");
267 			tellUser("Bob logged in with ticket: " + bobTicket);
268 			LeagueInfo linfo;
269 			RemoteLeague l;
270 			TournamentInfo tinfo;
271 			RemoteTournament t;
272 			
273 			linfo = arena.createLeague(operatorTicket, bob, "Expert " + gameName + " League", "A restricted league for insiders.", gameName, KNOCK_OUT);
274 			l = linfo.getLeague();
275 			l.addPlayer(bobTicket, alice);
276 			l.addPlayer(bobTicket, joe);
277 			tinfo = linfo.getLeague().createTournament(bobTicket, "2003 Championship", "");
278 			t = tinfo.getTournament();
279 			t.openRegistration(bobTicket);
280 			t.acceptPlayer(bobTicket, alice);
281 			t.acceptPlayer(bobTicket, joe);
282 			t.closeRegistration(bobTicket);
283 			t.launch(bobTicket);
284 			tinfo = linfo.getLeague().createTournament(bobTicket, "2004 Championship", "");
285 			t = tinfo.getTournament();
286 			t.openRegistration(bobTicket);
287 			
288 			linfo = arena.createLeague(operatorTicket, bob, "Novice " + gameName + " League", "A simple, unrestricted league for beginners.", gameName, KNOCK_OUT);
289 			linfo.getLeague().unrestrict(bobTicket);
290 			tinfo = linfo.getLeague().createTournament(bobTicket, "Paper Cup", "An adhoc knockout tournament.");
291 			t = tinfo.getTournament();
292 			t.openRegistration(bobTicket);
293 			t.acceptPlayer(bobTicket, joe);
294 			t.acceptPlayer(bobTicket, mike);
295 			t.acceptPlayer(bobTicket, alice);
296 			t.acceptPlayer(bobTicket, mark);
297 			t.acceptPlayer(bobTicket, mary);
298 			t.closeRegistration(bobTicket);
299 			t.launch(bobTicket);
300 		} catch (RemoteException e) {
301 			e.printStackTrace();
302 		} catch (ArenaException e) {
303 			e.printStackTrace();
304 		}
305 		tellUser("Demo setup finished.");
306 	}
307 	
308 	public static void main(String[] args) {
309 		
310 		// Load properties from the arena specified file or URL
311 		String propertiesFileName = null;
312 		if (args.length > 0) {
313 			propertiesFileName = args[0];
314 		}
315 		PropertyLoader propertyLoader = loadProperties(propertiesFileName);
316 		
317 		// The code base property is used for loading games and tournament styles
318 		// If the CodeBase property is specified, initialize the corresponding
319 		// RMI code base property so that RMI clients can also dynamically load
320 		// the same objects.
321 		codeBase = propertyLoader.getStringProperty("CodeBase", null);
322 		if (codeBase != null) {
323 			System.setProperty("java.rmi.server.codebase", codeBase);
324 		}
325 		
326 		// Initialize the logger using the properties file.
327 		initLogger(propertiesFileName);
328 		
329 		// The RMI secutiry manager prevents dynamically loaded classes to access
330 		// local resources (e.g., the file system). Allow the creation of sockets
331 		// so that match front ends and game peers can still connect to this server,
332 		// overriding the site policy.
333 		System.setSecurityManager(new RMISecurityManager() {
334 					// This enables the arena server to accept RMI calls from match front ends
335 					// and game peers, regardless of the current policy
336 					public void checkAccept(String host, int port) {}
337 					// This enables the arena server to make connections to match front ends
338 					// to send notification events over the listener interfaces
339 					public void checkConnect (String host, int port) {}
340 					public void checkConnect (String host, int port, Object context) {}
341 				});
342 		
343 		// Create an arena and register it as a remote object
344 		initArena(propertyLoader);
345 		
346 		// Register tournament styles
347 		registerTournamentStyles(propertyLoader);
348 		
349 		// Register games
350 		registerGames(propertyLoader);
351 		
352 		// Create test objects if the SetupDemo property is set to true.
353 		setupDemo(propertyLoader);
354 		
355 		// Wait for RMI connections. Connections will be handeled in separate
356 		// threads created by RMI.
357 		while (true) {
358 			try { Thread.sleep(5000); } catch (InterruptedException e) {}
359 		}
360 	}
361 }
362