Skip to content

PC2v9 Module Startup

John Clevenger edited this page Apr 28, 2020 · 1 revision

Initial Startup

PC² modules are started by executing a script located in the bin folder inside the PC2v9 Eclipse project (just below the root of the Eclipse project, at the same level as the src folder containing the PC2v9 Java classes). For example, starting a server is done by invoking the pc2server script; starting a Judge is done by invoking the pc2judge script, etc.[1] Although the scripts vary slightly in some details, fundamentally they all do the same thing: invoke the java command to start a Java Virtual Machine (JVM) running a class named edu.csus.ecs.pc2.Starter, passing to Starter any command line arguments specified by the user.

When the Starter class begins running, it constructs a new InternalContest (model), constructs a new InternalController (controller), attaches the model to the controller so that the controller knows what model (contest) it is manipulating, and calls method start() in the controller passing any command line arguments to the start() method.

The controller’s start() method first parses the command line arguments using an instance of class core.ParseArguments, which stores all arguments in a hashtable. start() then passes the parsed arguments to the contest (model).

Next, the start() method checks the parsed arguments to determine whether the module being started is a PC2 Server or a Client module. If the command line contained a “--server” argument then the module is a Server; in this case start() initializes the server module’s “Profile” if necessary (that is, in the case that this server is not going to be obtaining a profile from another remote server). In either case (server or client module), start() also then initializes the module’s logging mechanism.

start() then checks for and handles any special command line arguments such as “--help” (which displays a “usage” message then exits); “-F”, which instructs the module to read additional options from a file; “--nogui”, which instructs the module to run without displaying a Graphical User Interface [2] ; and “--debug”, which causes the module to output verbose debugging/tracing information to the console.

Next, start() initializes the module’s transport layer by creating a new instance of class core.transport.connection.ConnectionManager (however, the transport layer is not actually started yet; see below).

start() then checks the command line arguments. Unless the arguments include the option “--skipini”, it checks to see if the arguments include “--ini”; if so, it uses an instance of the class core.Ini to load the .ini file whose URL is specified by the argument; if no “--ini” is present, it uses an instance of the class core.IniFile to load the .ini file whose name is the default, pc2v9.ini. [3]

After reading the .ini file, the start() method starts the ConnectionManager’s transport layer running. For server modules, this causes the transport to start listening for packets from other modules (clients as well as remote servers). For client modules, start() also invokes a method connectToMyServer() which establishes a new core.transport.ConnectionHandler for managing connections from the client to its server.

Once the above initialization has completed, the start() method checks whether login/password credential information was specified in the command line arguments and whether the module is to be run in “GUI mode” or not. For the case of GUI mode, start() constructs an instance of class ui.LoginFrame, installs the contest (model) and controller into the frame, and displays the login frame, which prompts the user for an account and password.

Once the user has provided credentials (either having provided them on the command line or through the GUI), the controller’s login() method is invoked (either directly by the start() method or indirectly by the action handler attached to the GUI “Login” button). This sends a LOGIN_REQUEST packet containing the credentials. For a client module, this packet is sent to the server whose IP address is specified in the client’s .ini file; for a server, this packet goes directly back to the packet handler in the same server module. In either case, the server examines the credentials and returns either a LOGIN_SUCCESS or LOGIN_FAILED packet, which the controller uses to determine the next action to be taken (see the section on Login and UI Frames, below).

Login and UI Frames

When a controller’s start() method sends a LOGIN_REQUEST packet to a server, it gets back either a LOGIN_SUCCESS or a LOGIN_FAILED packet. Both types of packets are initially handled in the module’s PacketHandler instance.

A LOGIN_FAILED packet is handled by invoking method InternalContest.loginDenied() in the contest model; this method fires an event of type LOGIN_DENIED which is forwarded to a listener registered at startup with the server by the original requester.

A LOGIN_SUCCESS packet is processed in the controller’s PacketHandler, by invoking method loginSuccess(). This method first checks to see if this is the very first login. If so, it checks whether the login came from a Client or from another server (in the case of a multi-site contest). In the case of a login from another server, the method fetches the “contest master password” (defined by the user the first time a server is started) and verifies that it matches the contest master password provided by the packet sender (the remote server). In either case (client or server) the method then initializes the model with the data from the packet.

Once the module (client or server) has been initialized, the loginSuccess() method invokes method otherLoginActivities(), which in turn calls the controller’s startMainUI() method passing to it the “client ID” for the module which logged in. startMainUI() first checks if the logged in module is a server, and if so it tells the Transport layer to start listening for connections on the port indicated by the remote server.

startMainUI() then checks whether the module is supposed to be using a GUI. If so, and if no GUI has previously been specified (which is the default case), startMainUI() calls static method LoadUIClass.getUIClassName() to find the name of the GUI class which should be displayed for the specified client ID and then calls static method LoadUIClass.loadUIClass() to load the specified UI class.

startMainUI() then calls setContestAndController() in the specified UI class, inserting the contest model and the controller into the GUI class (so that the GUI class, which implements the client view, will have access to the corresponding MVC model and controller). Finally, if the (no-longer-needed) login frame still exists, startMainUI() disposes it.

GUI class names are defined by LoadUIClass.getUIClassName() in one of two ways. When called, this method first checks for the existence of a “properties file” whose name is given in the System Properties table under the key “uiname”. If the file is found, or if there is a folder of the corresponding name and that folder contains a file named “uiname.properties”, the file is loaded into a Properties object which is then checked for a GUI property name corresponding to the client. If found, that name is returned as the UI class name for the client.

If no UI properties file is found, or if the file does not contain a key matching the client’s name, then getUIClassName() returns a default GUI class name for the specified client. The list of default class names is defined in method getDefaultUIProperties(); each type of client has a default UI whose class name is specified by that method.


[1] There are two similarly-named versions of each script, one containing Windows (DOS) commands and another containing Unix (Linux/MaxOSX) commands; typing the name of a script at a command prompt in a given OS will automatically invoke the corresponding OS script version.

[2] While most modules run using a GUI, there are some modules (such as the Server) which support a “--nogui” option to allow them to run “headless”. See the PC2v9 Contest Administrator’s Guide for further details.

[3] Other than the fact that class core.Ini supports reading from an arbitrary URL while class IniFile always uses a local file, there is no good reason for having two different classes for loading .ini files; this is an historical anomaly that should be cleaned up by refactoring.

PC2 Logo

Clone this wiki locally