Summary


NetRatServer Implementation — Final Iteration

The NetRatServer application has some cool functionality. You can connect to it and control robot rats with two types of client applications. However, the persistence of the robot rat images on the floor after clients disconnect is a minor irritant, and the presence of magic values in the server code is problematic. This iteration implements a solution to both of these issues. Table 20-8 presents the design considerations and decision for this final NetRatServer implementation iteration.

Table 20-8: Final Iteration Design Considerations and Decisions
Open table as spreadsheet

Check-Off

Design Consideration

Design Decision

 

Objectives for final iteration server application development activities.

The objectives for this iteration are three fold: First, implement a mechanism that will remove the rat images from the floor when clients disconnect from the server application. Second, eliminate embedded magic values from the server application code to improve maintainability and make it easy to make server configuration changes. The RobotRat and NetRatServer source file must be examined to identify candidate magic values. Third, test code can be removed from the NetRatServer class.

 

NetRatServerProperties class

The NetRatServerProperties class will extend the java.util.Properties class. Server configuration values will be stored here and retrieved by server application components as required. Consolidating server configuration values in one location will significantly improve maintainability and make it easier to effect configuration changes. The NetRatServerProperties class will also implement the Singleton pattern. The Singleton pattern is used when you want only one object of a particular class type to exist. Any component within the NetRatServer application can gain access to the single instance of the NetRatServerProperties object to store and retrieve properties as required.

 

RobotRat class

The RobotRat class must be modified to utilize the NetRatServerProperties class.

 

NetRatServer class

The NetRatServer class must be modified to utilize the NetRatServerProperties class. The code that creates the two instances of RobotRat for testing purposes can be removed. However, when thiscode is removed the floor will not be drawn on the screen until thefirst client connects to the server. (This is due to the fact that the RobotRat class is not loaded into the Java Virtual Machine until it isneeded by the server application. This may or may not be the desired server behavior but, for this example, the NetRatServer will explicitly load the class on application startup using the Class.forname() method.

 

rmic tool

The RobotRat_stub.class file will need to be generated with the rmic tool and deployed to the server side and client side.

The final NetRatServer application design is shown in figure 20-21.

image from book
Figure 20-21: Final NetRatServer Application Design Class Diagram

Referring to figure 20-21 — the new class, NetRatServerProperties, has been added to the mix. The RobotRat and NetRatServer classes now have a dependency on the NetRatServerProperties class. The NetRatServerProperties class stores its properties in a properties file.

Although not shown in the diagram, the NetRatServerProperties object implements the Singleton pattern. The singleton pattern is employed when you want only one instance of a particular class type to exist for use within an application.

Another change to the design that is evident in the diagram was made to the NetRatServer class. It no longer extends JFrame or implements ActionListener. The test code previously used to create two instances of RobotRat and move them around the floor with the single move button was removed.

Final Iteration Code

The following code is developed as a result of this iteration.

Referring to example 20.18 through 20.20 —The NetRatServerProperties class extends the java.util.Properties class. Lines 21 through 33 declare a set of class-wide String constants that represent the keys through which property values will be accessed. The actual default property values are set in the body of the constructor.

Example 20.18: NetRatServerProperties.java

image from book
 1     /************************************************************************************************ 2     *   FileName: NetRatServerProperties.java 3     *   @author   Rick Miller 4     * 5     *   Description: Implements the singleton pattern. A NetRatServerProperties object implements 6     *   the persistence of application information required by the NetRatServer application. 7     **************************************************************************************************/ 8     import java.util.*; 9     import java.io.*; 10 11    /************************************************************************************************** 12    * NetRatServerProperties class Description: Implements the singleton pattern. A NetRatServerProperties 13    * object implements the persistence of application information required by the NetRatServer 14    * application. 15    * @see Properties 16    ****************************************************************************************************/ 17 18    public class NetRatServerProperties extends Properties { 19 20       // class constants 21       public static final String DEFAULT_RMI_PORT = "DEFAULT_RMI_PORT"; 22       public static final String DEFAULT_SOCKET_PORT = "DEFAULT_SOCKET_PORT"; 23       public static final String DEFAULT_SERVER_IP = "DEFAULT_SERVER_IP"; 24       public static final String DEFAULT_PROPERTIES_FILE = "DEFAULT_PROPERTIES_FILE"; 25       public static final String DEFAULT_PROPERTIES_FILENAME = "netratserver.properties"; 26       public static final String DEFAULT_FRAME_HEIGHT = "DEFAULT_FRAME_HEIGHT"; 27       public static final String DEFAULT_FRAME_WIDTH = "DEFAULT_FRAME_WIDTH"; 28       public static final String DEFAULT_FRAME_X_POSITION = "DEFAULT_FRAME_X_POSITION"; 29       public static final String DEFAULT_FRAME_Y_POSITION = "DEFAULT_FRAME_Y_POSITION"; 30       public static final String DEFAULT_STEP_SIZE = "DEFAULT_STEP_SIZE"; 31       public static final String DEFAULT_ROBOTRAT_CLASS_NAME = "DEFAULT_ROBOTRAT_CLASS_NAME"; 32       public static final String DEFAULT_ROBOTRAT_FACTORY_SERVICE_NAME = 33                                                       "DEFAULT_ROBOTRAT_RMI_SERVICE_NAME"; 34 35       // class variables 36       private static NetRatServerProperties _properties_object = null; 37 38 39    /************************************************************************************************** 40    *  Private constructor 41    *  Design Decisions: 42    *      -- Attempts to open properties file and load persistent properties. If that fails, it will 43    *         try to create the properties file, set itself up with default property values, and attempt 44    *         to store the property values in the file. If that fails...check your hard drive! 45    ****************************************************************************************************/ 46 47       private NetRatServerProperties( String properties_file ){ 48         try{ 49            FileInputStream fis = new FileInputStream(properties_file); 50            load(fis); 51           }catch(Exception e) { 52             System.out.println("Problem opening properties file!"); 53             System.out.println("Bootstrapping properties..."); 54             try{ 55               FileOutputStream fos = new 56                             FileOutputStream(NetRatServerProperties.DEFAULT_PROPERTIES_FILENAME); 57               setProperty(NetRatServerProperties.DEFAULT_RMI_PORT, "1099"); 58               setProperty(NetRatServerProperties.DEFAULT_SOCKET_PORT, "5001"); 59               setProperty(NetRatServerProperties.DEFAULT_SERVER_IP, "127.0.0.1"); 60               setProperty(NetRatServerProperties.DEFAULT_PROPERTIES_FILE, "netratserver.properties"); 61               setProperty(NetRatServerProperties.DEFAULT_FRAME_HEIGHT, "300"); 62               setProperty(NetRatServerProperties.DEFAULT_FRAME_WIDTH, "300"); 63               setProperty(NetRatServerProperties.DEFAULT_FRAME_X_POSITION, "200"); 64               setProperty(NetRatServerProperties.DEFAULT_FRAME_Y_POSITION, "200"); 65               setProperty(NetRatServerProperties.DEFAULT_STEP_SIZE, "3"); 66               setProperty(NetRatServerProperties.DEFAULT_ROBOTRAT_CLASS_NAME, "RobotRat"); 67               setProperty(NetRatServerProperties.DEFAULT_ROBOTRAT_FACTORY_SERVICE_NAME, 68                             "Robot_Rat_Factory"); 69 70               super.store(fos, "NetRatServerProperties File - Edit Carefully"); 71               fos.close(); 72              }catch(Exception e2){ System.out.println("Uh ohh...Bigger problems exist!"); } 73           } 74       } 75 76 77    /************************************************************************************************** 78    *  Private default constructor. Applications will get an instance via the getInstance() method. 79    *  @see getInstance() 80    ***************************************************************************************************/ 81       private NetRatServerProperties(){ 82           this(DEFAULT_PROPERTIES_FILENAME); 83       } 84 85    /************************************************************************************************** 86    *  The store() method attempts to persist its properties collection. 87    ***************************************************************************************************/ 88       public void store(){ 89         try{ 90           FileOutputStream fos = new 91                         FileOutputStream(getProperty(NetRatServerProperties.DEFAULT_PROPERTIES_FILE)); 92           super.store(fos, "NetRatServerProperties File"); 93           fos.close(); 94         }catch(Exception e){ System.out.println("Trouble storing properties!"); } 95       } 96 97    /************************************************************************************************** 98    *  getInstance() returns a singleton instance if the NetRatServerProperties object. 99    ***************************************************************************************************/ 100 101      public static NetRatServerProperties getInstance(){ 102        if(_properties_object == null){ 103           _properties_object = new NetRatServerProperties(); 104         } 105        return _properties_object; 106      } 107   } // end NetRatServerProperties class definition
image from book

Example 20.19: RobotRat.java

image from book
 1     import javax.swing.*; 2     import java.util.*; 3     import java.rmi.*; 4     import java.rmi.server.*; 5     import java.awt.*; 6 7     public class RobotRat extends UnicastRemoteObject implements RobotRatInterface { 8 9       /* *********************************** 10          static variables 11      **************************************/ 12      private static JFrame _frame = null; 13      private static Floor _floor = null; 14      private static Vector _rats = null; 15      private static int _frame_height; 16      private static int _frame_width; 17      private static int _frame_x_position; 18      private static int _frame_y_position; 19      private static NetRatServerProperties _properties = null; 20 21      /* *********************************** 22          instance variables 23      **************************************/ 24      private Rat _its_rat = null; 25      private int _step_size; 26 27 28      static { 29        _properties = NetRatServerProperties.getInstance(); 30        _rats  = new Vector(); 31        _floor = new Floor(_rats); 32        _frame = new JFrame("Rat Movement Floor"); 33        _frame.getContentPane().add(_floor); 34        _frame_height = 35                 Integer.parseInt(_properties.getProperty(NetRatServerProperties.DEFAULT_FRAME_HEIGHT)); 36        _frame_width                                                                                        = 37                 Integer.parseInt(_properties.getProperty(NetRatServerProperties.DEFAULT_FRAME_WIDTH)); 38        _frame.setSize(_frame_height, _frame_width); 39        _frame_x_position = 40            Integer.parseInt(_properties.getProperty(NetRatServerProperties.DEFAULT_FRAME_X_POSITION)); 41        _frame_y_position = 42            Integer.parseInt(_properties.getProperty(NetRatServerProperties.DEFAULT_FRAME_Y_POSITION)); 43        _frame.setLocation(_frame_x_position, _frame_y_position); 44        _frame.show(); 45      } 46 47 48      public RobotRat() throws RemoteException { 49        _its_rat = new Rat(); 50        _rats.addElement(_its_rat); 51        _step_size = Integer.parseInt(_properties.getProperty(NetRatServerProperties.DEFAULT_STEP_SIZE)); 52        _floor.repaint(); 53      } 54 55      protected void finalize() throws Throwable { 56        System.out.println("RobotRat finalize() called!"); 57        if(_its_rat != null){ 58           _rats.removeElement(_its_rat); 59           _its_rat = null; 60        } 61      } 62 63 64      public void moveEast(){ 65        _its_rat.setX(_its_rat.getX() + _step_size); 66        _floor.repaint(); 67      } 68 69      public void moveSouth(){ 70        _its_rat.setY(_its_rat.getY() + _step_size); 71        _floor.repaint(); 72      } 73 74      public void moveWest(){ 75        _its_rat.setX(_its_rat.getX() - _step_size); 76        _floor.repaint(); 77      } 78 79      public void moveNorth(){ 80        _its_rat.setY(_its_rat.getY() - _step_size); 81        _floor.repaint(); 82      } 83 84      public void moveNorthWest(){ 85        _its_rat.setY(_its_rat.getY() - _step_size); 86        _its_rat.setX(_its_rat.getX() - _step_size); 87        _floor.repaint(); 88      } 89 90      public void moveSouthWest(){ 91        _its_rat.setY(_its_rat.getY() + _step_size); 92        _its_rat.setX(_its_rat.getX() - _step_size); 93        _floor.repaint(); 94      } 95 96      public void moveNorthEast(){ 97        _its_rat.setY(_its_rat.getY() - _step_size); 98        _its_rat.setX(_its_rat.getX() + _step_size); 99        _floor.repaint(); 100     } 101 102     public void moveSouthEast(){ 103       _its_rat.setY(_its_rat.getY() + _step_size); 104       _its_rat.setX(_its_rat.getX() + _step_size); 105       _floor.repaint(); 106     } 107   } // end RobotRat class definition
image from book

Example 20.20: NetRatServer.java

image from book
 1     import java.rmi.*; 2     import java.rmi.registry.*; 3     import java.net.*; 4 5     public class NetRatServer { 6       public static void main(String[] args){ 7         NetRatServerProperties properties = NetRatServerProperties.getInstance(); 8         System.out.println("NetRatServer Lives!!"); 9         try{ 10        Class.forName(properties.getProperty(NetRatServerProperties.DEFAULT_ROBOTRAT_CLASS_NAME)); 11        System.out.println("Starting registry..."); 12        LocateRegistry.createRegistry(Integer.parseInt( 13                             properties.getProperty(NetRatServerProperties.DEFAULT_RMI_PORT))); 14        System.out.println("Registry started on port " + 15                               properties.getProperty(NetRatServerProperties.DEFAULT_RMI_PORT) + "."); 16        System.out.println("Binding service name to remote object..."); 17        Naming.bind(properties.getProperty( 18                 NetRatServerProperties.DEFAULT_ROBOTRAT_FACTORY_SERVICE_NAME), new RobotRatFactory()); 19        System.out.println("Bind successful."); 20        System.out.println("Ready for remote method invocation."); 21 22        System.out.println("Creating ServerSocket Object..."); 23        ServerSocket server_socket = new ServerSocket(Integer.parseInt(properties.getProperty( 24                             NetRatServerProperties.DEFAULT_SOCKET_PORT))); 25        System.out.println("ServerSocket object created successfully!"); 26        while(true){ 27          System.out.println("Listening for incoming client connections..."); 28          Socket socket = server_socket.accept(); 29          System.out.println("Incoming client connection detected."); 30          System.out.println("Creating ThreadedClientProcessor object..."); 31          ThreadedClientProcessor client_processor = new ThreadedClientProcessor(socket); 32          System.out.println("ThreadedClientProcessor object created."); 33          System.out.println("Calling start() method..."); 34          client_processor.start(); 35         } 36        }catch(Exception e){ 37          e.printStackTrace(); 38        } 39      } 40    }// end NetRatServer class
image from book

When an instance of NetRatServerProperties is created it will attempt to load the image from book netratserver.properties file. If the properties file cannot be located a new copy will be created which contains the required default values. Once the image from book netratserver.properties file exists, changes to the server application configuration can be made to this file where appropriate before the server application is started. In this manner application behavior can be changed without recompiling the source code.

The RobotRat and NetRatServer classes have been modified to utilize the NetRatServerProperties class. In each case the Singleton instance of NetRatServerProperties is retrieved via the static getInstance() method. (See line 29 of example 20-19 or line 7 of example 20.20.) Property values are retrieved from the properties object as required via the getProperty() method. Note that all property values are stored in the properties object as Strings. In cases where an integer value is expected in the code, say to set the size of the JFrame in the RobotRat class, the String value must be converted to an integer value via the Integer.parseInt() method.

Notice the use of the Class.forName() method on line 10 of example 20.20. The RobotRat class is explicitly loaded so that the floor displays when the server starts up. The name of the RobotRat class is also retrieved from the properties object.

The RobotRat class has also been modified to include a finalize() method starting on line 55 of example 20.19. The finalize method is called when the Java virtual machine garbage collector collects an unreferenced RobotRat object. This will result in the _its_rat reference being set to null which will effectively remove the rat image from the floor. However, when exactly this will occur is purely at the discretion of the virtual machine garbage collector. A RobotRat object created for a socket-based client will be collected sooner when the client disconnects than will a RobotRat object created for an RMI-based client. This is due to the Java RMI runtime holding a reference to the RobotRat object for quite some time after the RMI-based client disconnects. Testing on my computer resulted in the RMI runtime holding on to RobotRat objects for as long as ten minutes. In any case, you cannot guarantee when, exactly, the finalize() method will be called, and you cannot call it explicitly in any case. Rat images will, for now, have to linger upon the floor until the garbage collector removes their corresponding RobotRat objects from memory.

Testing The Final Iteration Code

When the NetRatServer application is run for the first time after making the required modifications the image from book netratserver.properties file will not exist. Figure 20-22 shows the console output when the NetRatServer application is started and the image from book netratserver.properties file cannot be located.

image from book
Figure 20-22: Console Output on NetRatServer Application Startup

Figure 20-23 shows the empty floor waiting for the arrival of the first robot rat image. The floor is displayed as a result of explicitly loading the RobotRat class using the Class.forName() method. Start up a few socket-based and RMI-based clients and move the rats around the floor. Shutdown the clients and note how long it takes for the JVM on your computer to collect the RobotRat objects.

image from book
Figure 20-23: Empty Floor Displayed as a Result of Explicitly Loading the RobotRat Class

Parting Comments

The robot rat client-server application presented in this chapter has some interesting functionality and demonstrates many important concepts regarding network-based client-server application design. Space limitations of this book force me to bring the discussion of this project to a close. However, thorough study of the techniques and code presented in this chapter combined with a little imagination will enable you to expand the scope of this project and make it a really neat application while having fun at the same time. Suggestions for improvements to the robot rat client-server application, as well as ideas for other client-server projects, are offered in the Skill-Building and Suggested Projects sections.

Quick Review

Hard-coded magic values render it difficult to change an application’s configuration settings. The Properties class can be used to store and retrieve application property values.

There is no guarantee when the JVM garbage collector will run and remove unreferenced objects from memory. Objects referenced by the RMI runtime will generally be held longer that objects referenced by independent threads.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net