Multitier Applications: Remote Method Invocation and JDBC

I discuss different approaches to software partitioning in Chapter 1, which discusses N-tiered approaches, RMI allows Java objects to be distributed and shared across computers and networks. This example uses RMI between the client and the middleware and JDBC between the middleware and the database server.

RMI combined with JDBC for database access is a simple but efficient enabler for software partitioning. In this example, the client performs presentation (GUI) tasks only, the middleware stores the application’s logic, and the database provides a persistent and coherent storage for the data.

This example deals with cars. In this example, all the cars are built in hypothetical car factories, and there is one factory for each different car brand. Clients can invoke various methods on the cars and factories, although these objects aren’t local client objects to them; they are remote objects. This means that all methods called on these objects are executed where these objects reside — that is, on the RMI server. If parameters must be passed to such methods, they are serialized by RMI and passed to the server object that will deserialize them.

The example uses these classes:

  • Car.java is an interface for objects that represent cars. It extends java.rmi.remote.

  • CarImpl.java implements Car.

  • CarFactory.java is an interface that extends java.rmi.remote. It is used for objects that are factories of car objects. Factory is used in the OO sense here, not the industrial sense.

  • CarFactoryImpl.java implements CarFactory.

  • CarSupplierServer.java is a server. It brings car factories to life and makes them reachable through the RMI mechanism.

  • CarSales.java is a client that deals with objects such as Car and CarFactories.

The client has access only to the Car and CarFactory interfaces. All methods invoked on these objects are actually implemented within the RMI server, which, in turn, performs the calls to the database through JDBC. Figure 12-13 illustrates the whole architecture for this example.

click to expand
Figure 12-13: The three-tiered architecture of the car factory example.

Instead of providing object persistency at the Car level, a simpler option was chosen: The CarFactory level provides persistent storage. Practically, this means that information about the car factories is saved in a database table and loaded by the CarFactoryImpl constructor. The database immediately reflects all updates of the car factories. Another option is to save the different instances of CarFactory in their finalize() methods whenever the RMI server program terminates.

The database side

The data structure is quite simple. The following is the SQL to create the table used for this example. The primary key is composed of two fields: serialno and brand.

CREATE TABLE cars (serialno INT NULL, brand VARCHAR(12) NULL, model VARCHAR(12) NULL, color VARCHAR(12) NULL, price INT NULL, owner VARCHAR(12) NULL) GO INSERT INTO cars VALUES (1000000, ‘Volkswagen’, ‘Golf GL’, ‘black’, NULL, ‘’) GO

The following SQL statement queries the content of the table after a few executions of the RMI client and RMI server:

1> SELECT * FROM cars 2> ORDER BY brand, serialno 3> GO serialno brand model color price owner ------ ------ ------ ------ ------ ------ 1000001 Audi A6 black NULL 1000002 Audi A8 yellow NULL 1000003 Audi A6 black NULL 1000004 Audi A8 yellow NULL 1000005 Audi A6 black NULL 1000006 Audi A8 yellow NULL 1000007 Audi A6 black NULL 1000008 Audi A8 yellow NULL 1000001 BMW 528i fjordgreen NULL 1000002 BMW 750Li arcticsilver NULL 1000003 BMW 528i fjordgreen NULL 1000004 BMW 750Li arcticsilver NULL 1000005 BMW 528i fjordgreen NULL 1000006 BMW 750Li arcticsilver NULL 1000007 BMW 528i fjordgreen NULL 1000008 BMW 750Li arcticsilver NULL 1000000 Volkswagen Golf GL black NULL 1000001 Volkswagen Golf CL darkred NULL 1000002 Volkswagen Golf CL darkred NULL 1000003 Volkswagen Golf CL darkred NULL 1000004 Volkswagen Golf CL darkred NULL (21 rows affected)

The RMI server side

A script file starts the server and an RMI service called the RMI Registry. As soon as the RMI server launches, the CarSupplierServer object re-creates car factories and cars from the data in the database. CarFactory objects are thus created and bound to a name. Client lookups use this name to obtain references to these remote objects. Here is the output of the CarSupplierServer when launched on UNIX (Windows users: the RMI registry command must be executed in a separate DOS shell):

csh: start starting registry [1] 7852 starting CarSupplierServer press CTRL-C to interrupt server type: java CarSales to start client Creating Car Factories CarFactory: Loaded new Car:Car: model=Audi model=A6 color=black serial=1 owner= CarFactory: Loaded new Car:Car: model=Audi model=A8 color=yellow serial=2 owner= CarFactory: Loaded new Car:Car: model=Audi model=A8 color=yellow serial=3 owner= CarFactory: Loaded new Car:Car: model=Audi model=A8 color=pink serial=4 owner= CarFactory: Loaded new Car:Car: model=BMW model=528i color=fjordgreen  serial=1 owner= CarFactory: Loaded new Car:Car: model=BMW model=750Li color=arcticsilver  serial=2 owner= CarFactory: Loaded new Car:Car: model=BMW model=528i color=applegreen  serial=3 owner= CarFactory: Loaded new Car:Car: model=BMW model=528i color=sunblue  serial=5 owner= CarFactory: Loaded new Car:Car: model=Volkswagen model=Golf TDI  color=black serial=1000000 owner= CarFactory: Loaded new Car:Car: model=Volkswagen model=Golf CL  color=darkred serial=1000001 owner= CarFactory: Loaded new Car:Car: model=Volkswagen model=Golf TDI  color=deep space b serial=1000002 owner= Registering Car Factories

The client side

The client programs are started by simply running CarSales as shown in this command line:

% java CarSales

CarSales calls a lookup method to obtain references to remote objects. Once obtained, these references are used to invoke various methods defined in CarFactory and implemented in CarFactoryImpl.

The window shown in Figure 12-14 appears on-screen, displaying a trace of the different actions the client performs.

click to expand
Figure 12-14: The RMI client window.

As soon as the client launches, the RMI server logs all the actions that it performs on behalf of the client, such as the following:

CarFactory Audi: request for all cars. 4 returned. CarFactory BMW: request for all cars. 4 returned. CarFactory Volkswagen: request for all cars. 3 returned. CarFactory Audi: request for all cars. 4 returned. CarFactory: Created new Car:Car: model=Audi model=A8 color=orange serial=5 owner=None CarFactory Audi: request for all cars. 5 returned. CarFactory: Deleted Car:CarImpl_Stub[RemoteStub [ref:  [endpoint:[guadalajara:37065](remote),objID:[14]]]] CarFactory Audi: request for all cars. 4 returned.

Car.java

Car.java is the interface for cars. It extends java.rmi.Remote and is implemented by the CarImpl class. All methods invoked on Car instances trigger respective methods implemented by CarImpl instances. Listing 12-11 shows the source code for Car.java.

Listing 12-11: Car.java

start example
// // Car.java // // (C) 1996 Wim De Munck mailto: wimdm@dm-mediaware.be // // Interface describing stub Car functionality // the actual implementation will be the responsibility of // CarImpl; Car’s will be created by a remote factory object: // CarFactory and CarFactoryImplementation. // import java.rmi.Remote; import java.rmi.RemoteException; public interface Car extends Remote {   public int getPrice () throws RemoteException;   public int getTopSpeed() throws RemoteException;   public long getSerialNr() throws RemoteException;   public void sellTo ( String owner ) throws RemoteException;   public String getOwner ( ) throws RemoteException;   public String getColor ( ) throws RemoteException;   public String getModel ( ) throws RemoteException;   public String getObjectString ( ) throws RemoteException; }
end example

CarImpl.java

The CarImpl.java class implements Car. It contains various methods to perform miscellaneous actions on Cars. Listing 12-12 is the source code for CarImpl.java.

Listing 12-12: CarImpl.java

start example
// // CarImpl.java // // (C) 1996 Wim De Munck mailto: wimdm@dm-mediaware.be // import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class CarImpl extends UnicastRemoteObject implements Car {   private int price;   private long serialNr;   private String brand;   private String model;   private String color;   private String owner = null;   public CarImpl ( String brand, String model, String color, long serialNr )     throws RemoteException {     this.brand = brand;     this.model = model;     this.color = color;     this.serialNr = serialNr;   }   public CarImpl ( String brand, String model, String color, long serialNr,                     String owner ) throws RemoteException {     this.brand = brand;     this.model = model;     this.color = color;     this.serialNr = serialNr;     this.owner = owner;   }   public int getPrice () throws RemoteException {     return 0; // Catalog.getPrice( brand, model );   }   public int getTopSpeed() throws RemoteException {     return 0; // Catalog.getTopSpeed( brand, model );   }   public long getSerialNr() throws RemoteException {     return serialNr;   }   public void sellTo ( String owner ) throws RemoteException {     this.owner = owner;   }   public String getOwner ( ) throws RemoteException {     return owner;   }   public String getColor ( ) throws RemoteException {     return color;   }   public String getBrand ( ) throws RemoteException {     return brand;   }   public String getModel ( ) throws RemoteException {     return model;   }   public String toString ( ) {     return "Car: model=" + brand + " model=" + model + " color=" + color +     " serial=" + serialNr + " owner=" + ((owner == null)? "None" : owner);   }   public String getObjectString ( ) throws RemoteException {     return this.toString();   } }
end example

CarFactory.java

The CarFactory class is an interface implemented by CarFactoryImpl. Like the Car interface, CarFactory inherits from java.rmi.Remote. Listing 12-13 is the source code for CarFactory.java.

Listing 12-13: CarFactory.java

start example
// // CarFactory.java // // (C) 1996 Wim De Munck mailto: wimdm@dm-mediaware.be // // interface describing stub CarFactory functionality // the actual implementation will be the responsibility of // CarFactoryImpl. // import java.rmi.Remote; import java.rmi.RemoteException; import java.sql.*; public interface CarFactory extends Remote {   public Car createCar ( String model, String color ) throws RemoteException,                                                               SQLException;   public Car getCar ( long serialNr ) throws RemoteException;   public Car[] getAll () throws RemoteException;   public boolean deleteCar ( Car car ) throws RemoteException, SQLException;   public String getObjectString () throws RemoteException; }
end example

CarFactoryImpl.java

CarFactoryImpl implements CarFactory and provides connectivity to the database through JDBC. It manipulates objects whose lifetime is longer than the application’s lifetime. These objects are persisted, stored in a database table, and loaded by the CarFactoryImpl constructor upon initialization.

The methods that perform updates of data call JDBC and execute SQL statements to synchronize the data in the database.

Listing 12-14 shows the source code for CarFactoryImpl.java.

Listing 12-14: CarFactoryImpl.java

start example
// // CarFactoryImpl.java // // (C) 1996 Wim De Munck mailto: wimdm@dm-mediaware.be // // interface describing stub CarFactory functionality // the actual implementation will be the responsibility of // CarFactoryImpl. // import java.util.Hashtable; import java.util.Enumeration; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.sql.*; public class CarFactoryImpl extends UnicastRemoteObject implements CarFactory {   private String brand;   private long lastSerialNr = 1000000;   private Hashtable cars = new Hashtable();   // local variables for connection state   private Connection conn;   private String uid = "guest";   private String pwd = "sybase";   private String table = "cars";   // url for sybase’s driver   private String url = "jdbc:sybase:Tds:guadalajara:8192";   private String driver = "com.sybase.jdbc.SybDriver";   public CarFactoryImpl ( String brand ) throws RemoteException,                                                  SQLException, Exception {     Class.forName(driver);     conn = DriverManager.getConnection(url, uid, pwd);     Statement stmt = conn.createStatement();     ResultSet rs = stmt.executeQuery("SELECT * FROM " + table     + " WHERE brand = ‘" + brand + "‘");     while (rs.next()) {       wakeupCar(rs.getInt("serialno"),       rs.getString("brand"),       rs.getString("model"),       rs.getString("color"),       rs.getString("owner"));     }     rs = stmt.executeQuery("SELECT MAX(serialno) FROM " + table     + " WHERE brand = ‘" + brand + "‘");     rs.next();     lastSerialNr = rs.getLong(1);     rs.close();     stmt.close();     this.brand = brand;   }   protected void finalize() throws SQLException {     conn.close();   }   public void wakeupCar ( int sn, String brand, String model, String color,                            String owner ) throws RemoteException {     CarImpl car = new CarImpl ( brand, model, color, sn, owner );     cars.put("SN"+car.getSerialNr(), car);     System.out.println("CarFactory: Loaded new Car:" + car);   }   public Car createCar ( String model, String color ) throws RemoteException,                                                               SQLException {     CarImpl car = new CarImpl ( brand, model, color, ++lastSerialNr );     Statement stmt = conn.createStatement();     int res = stmt.executeUpdate("INSERT INTO " + table       + "(serialno, brand, model, color, owner) VALUES ("       + car.getSerialNr() + ", ‘" + brand + "‘, ‘" + model       + "‘, ‘" + color + "‘, ‘’)");     stmt.close();     cars.put ( "SN" + car.getSerialNr(), car );     System.out.println("CarFactory: Created new Car:" + car);     return car;   }   public Car getCar ( long serialNr ) throws RemoteException {     return (Car)cars.get("SN"+serialNr);   }   /**   * get all cars without worrying about synchronization   */   public Car[] getAll () throws RemoteException {     Car [] allcars = new Car[cars.size()];     int i = 0;     Enumeration e = cars.elements();     while (e.hasMoreElements()) {       allcars[i++] = (Car)e.nextElement();     }     System.out.println("CarFactory " + brand + ": request for all cars. "                         + i + " returned.");     return allcars;   }   public boolean deleteCar ( Car car ) throws RemoteException, SQLException {     long sn = car.getSerialNr();     if ( cars.remove("SN"+ sn) != null ) {       Statement stmt = conn.createStatement();       int res = stmt.executeUpdate("DELETE " + table         + " WHERE brand = ‘" + brand + "‘ AND serialno = " + sn + "");       stmt.close();       System.out.println("CarFactory: Deleted Car:" + car);       return true;     }     else {       return false;     }   }   public String toString () {     return "CarFactory: " + brand + " lastNr=" + lastSerialNr;   }   public String getObjectString () throws RemoteException {     return this.toString();   } }
end example

CarSales.java

CarSales.java is the client class. CarSales is a standalone application that looks up CarFactories in the RMI server to get references to these remote CarFactories instances.

The application tries to obtain references to three remote CarFactories. After it obtains the references, actions such as selecting, ordering, selling, and destroying cars can be performed. This RMI client refers only to the CarFactory and Car interfaces, not their actual implementation, which is the main objective of this exercise. The implementation class instances run elsewhere on the network and are seamlessly accessed through RMI.

Listing 12-15 shows the source code for CarSales.java.

Listing 12-15: CarSales.java

start example
// // CarSales.java // // (C) 1996 Wim De Munck mailto: wimdm@dm-mediaware.be // // This code is not to be distributed without // explicit confirmation by the author. // import java.awt.*; import java.rmi.Naming; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.net.UnknownHostException; /** * CarSales-class connecting to a Remote Object * registry. * The Carsales instance allows simple order and selling * of cars. */ public class CarSales extends Frame {   TextArea ta = new TextArea("Application started");   Button quitB = new Button ( "Quit" ), orderB = new Button ( "Order Car" );   Button sellB = new Button ( "Sell" );   Button wreckB = new Button ( "Wreck" );   Choice brandCH = new Choice (), allCarsCH = new Choice();   TextField modelTF = new TextField(10), colorTF = new TextField(10);   TextField buyerTF = new TextField(10);   CarFactory carFactory[];   Car cars[] = null;   /**   * Construct the CarSales GUI   */   public CarSales ( ) {     super ("BEST CARS IN THE WORLD");     // Setup the User Interface     Panel northPan = new Panel ( );     northPan.setLayout ( new BorderLayout () );     northPan.add ( "North",     new Label ("Car Ordering: Select brand and model", Label.LEFT) );     northPan.setLayout ( new BorderLayout() );     Panel orderPanel = new Panel ();     //orderPanel.setLayout ( new FlowLayout ( FlowLayout.LEFT ) );     orderPanel.setLayout ( new GridLayout ( 1,7) );     orderPanel.add ( new Label ("Brand:", Label.RIGHT ) );     orderPanel.add ( brandCH );     orderPanel.add ( new Label ("Model:", Label.RIGHT ) );     orderPanel.add ( modelTF );     orderPanel.add ( new Label ("Color:", Label.RIGHT ) );     orderPanel.add ( colorTF );     orderPanel.add ( orderB );     Panel sellPanel = new Panel ();     //sellPanel.setLayout ( new FlowLayout ( FlowLayout.LEFT ) );     sellPanel.setLayout ( new GridLayout ( 1,7) );     sellPanel.add ( new Label ("All Cars:", Label.RIGHT ) );     sellPanel.add ( allCarsCH );     sellPanel.add ( new Label ("Buyer:", Label.RIGHT ) );     sellPanel.add ( buyerTF );     sellPanel.add ( new Label ( "" ) );     sellPanel.add ( new Label( "Actions:", Label.RIGHT ) );     Panel buttonPan = new Panel ( );     buttonPan.setLayout ( new GridLayout ( 1, 2) );     buttonPan.add ( sellB );     buttonPan.add ( wreckB );     sellPanel.add ( buttonPan );     Panel northCenterPanel = new Panel ();     northCenterPanel.setLayout ( new GridLayout ( 2, 1 ) );     northCenterPanel.add ( orderPanel );     northCenterPanel.add ( sellPanel );     northPan.add ( "Center", northCenterPanel );     northPan.add ( "South", new Label ( "RMI/SQL log:", Label.CENTER ) );     add("North", northPan );     add("Center", ta);     add("South", quitB );     // In a real application there would be a remote object     // giving us an array or enumeration with all brands.     // In this example we do it hard coded.     String carBrands[] = { "audi", "bmw", "vw" };     carFactory = new CarFactory [ carBrands.length ];     for (int ndx=0; ndx< carBrands.length; ndx ++ ) {       try {         brandCH.addItem ( carBrands[ndx] );         carFactory[ndx] = (CarFactory) (java.rmi.Naming.lookup (                             "rmi://serverhost/CarFactory." + carBrands[ndx] ) );         appendTA( carBrands[ndx] + ": " + carFactory[ndx]);       }       catch ( java.rmi.RemoteException ex) {         appendTA( "Constructor() RemoteException: " + ex.getMessage() );       }       catch ( java.rmi.NotBoundException ex) {         appendTA( "Constructor() NotBoundException: " + ex.getMessage() );}       catch ( java.net.MalformedURLException ex) {         appendTA ( "Constructor() MalformedURLException: "+ ex.getMessage() );       }     } // end for carBrands[ndx]     try {       appendTA("");       appendTA("Creating Cars:");       updateAllCarsChoice ( ) ;       //Car nr1 = audiFact.createCar("A6", "black");     }     catch ( Exception ex) {       appendTA( "Constructor() Exception: " + ex.getMessage() );       ex.printStackTrace();     }     pack();     setVisible ( true );    // JDK1.1 replaced deprecated show()   }   private void updateAllCarsChoice ( ) {     int ndx = brandCH.getSelectedIndex();     int count = 0;     allCarsCH.removeAll();     try {       cars = carFactory[ndx].getAll();       for ( int i=0 ; i < cars.length; i++ ) {         allCarsCH.addItem ( "" + cars[i].getSerialNr() + " " +           cars[i].getModel() + "," + cars[i].getColor() );         count++;       }     }     catch ( Exception ex ) {       appendTA ( "updateAllCarChoice() Exception: " + ex.getMessage() );     }     if ( count > 0 ) {       allCarsCH.setEnabled ( true );       allCarsCH.select( 0 );       try {         buyerTF.setText ( cars[0].getOwner() );         colorTF.setText ( cars[0].getColor() );         modelTF.setText ( cars[0].getModel() );       }       catch ( Exception ex ) {         appendTA ( "updateAllCarChoice() Exception: " + ex.getMessage() );       }     }     else {       allCarsCH.setEnabled ( false );       allCarsCH.addItem ( "No Cars Available" );     }     appendTA ( "Added " + count + " cars to allCarsCH for " +                 brandCH.getSelectedItem() );   }   private void appendTA ( String text ) {     ta.setText( ta.getText() + "\n" + text.toString() );   }   private void appendTA ( Car car ) {     try {       ta.setText( ta.getText() + "\n" + car.getObjectString() );     }     catch ( java.rmi.RemoteException ex ) {       ta.setText ( "RemoteException: " + ex.getMessage() );       ex.printStackTrace();     }   }   // JDK1.02 event-model   public boolean action ( Event evt, Object arg ) {     try {       if ( evt.target == quitB ) {         setVisible ( false ); // JDK1.1’s setVisible() replaced deprecated hide()         dispose();         System.exit(0);         return true;       }       else if ( evt.target == orderB ) {         int factIndex = brandCH.getSelectedIndex();         carFactory[factIndex].createCar ( modelTF.getText(), colorTF.getText() );         updateAllCarsChoice ( ) ;       }       else if ( evt.target == sellB ) {         int carIndex = allCarsCH.getSelectedIndex();         cars[carIndex].sellTo ( buyerTF.getText() );         appendTA ( "sold car: " + cars[carIndex].getSerialNr() + " to "                     + buyerTF.getText() );       }       else if ( evt.target == wreckB ) {         int carIndex = allCarsCH.getSelectedIndex();         int factIndex = brandCH.getSelectedIndex();         appendTA ( "wrecked car: " + cars[carIndex].getSerialNr() );         carFactory[factIndex].deleteCar ( cars[carIndex] );         updateAllCarsChoice ( ) ;       }       else if ( evt.target == brandCH ) {         colorTF.setText ( "" );         modelTF.setText ( "" );         updateAllCarsChoice ( ) ;       }       else if ( evt.target == allCarsCH ) {         int carIndex = allCarsCH.getSelectedIndex();         colorTF.setText ( cars[carIndex].getColor() );         modelTF.setText ( cars[carIndex].getModel() );         buyerTF.setText ( cars[carIndex].getOwner() );       }     }      catch ( java.rmi.RemoteException ex) {       appendTA ( "action() RemoteException: " + ex.getMessage() );     }     catch ( java.sql.SQLException ex) {       appendTA ( "action() SQLException: " + ex.getMessage() );     }     return true;   }   public boolean handleEvent ( Event evt ) { // deprecated by processEvent( )     if ( evt.id == Event.WINDOW_DESTROY ) {       setVisible ( false); // JDK1.1’s setVisible() replaced deprecated hide( )       dispose();       System.exit(0);       return true;     }     else {       return super.handleEvent ( evt );     }   }   public static void main ( String args[] ) {     // Create GUI-object     CarSales cs = new CarSales();   } }
end example

CarSupplierServer.java

The CarSupplierServer class is the RMI server class. It creates three instances of CarFactory and binds them to the RMI Registry. A simple string names the bindings to enable clients to perform lookups in the Registry. Listing 12-16 shows the source code for CarSupplierServer.java.

Listing 12-16: CarSupplierServer.java

start example
// // CarSupplierServer.java // // (C) 1996 Wim De Munck mailto: wimdm@dm-mediaware.be // import java.rmi.Naming; class CarSupplierServer {   public static void main ( String args[] ) {     // Install a SecurityManager for this server     System.setSecurityManager( new java.rmi.RMISecurityManager() );     // create and register all CarFactories     System.out.println ( "Creating Car Factories" );     CarFactoryImpl audi, bmw, vw ;     try {       audi = new CarFactoryImpl("Audi");       bmw = new CarFactoryImpl("BMW");       vw = new CarFactoryImpl("Volkswagen");     } catch ( Exception ex ) {       ex.printStackTrace();       return;     }     System.out.println ( "Registering Car Factories" );     try {       Naming.bind("CarFactory.audi", audi);       Naming.bind("CarFactory.bmw", bmw);       Naming.bind("CarFactory.vw", vw);     } catch ( java.net.MalformedURLException ex ) {       ex.printStackTrace();     } catch ( java.rmi.AlreadyBoundException ex ) {       ex.printStackTrace();     } catch ( java.rmi.RemoteException ex) {       ex.printStackTrace();     }   } }
end example

RMI, as defined and implemented by JavaSoft, Inc., is HTTP-proxy aware. The networking layer of RMI provides server- and client-side transparent support for HTTP tunneling that allows applets to communicate with remote methods and remote objects through WWW proxy servers.



JDBC 3. 0. JAVA Database Connectivity
JDBC 3: Java Database Connectivity
ISBN: 0764548751
EAN: 2147483647
Year: 2002
Pages: 148

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