CORBA
6.4 Using Factory Objects
objects? The simple answer is that the server will create a factory object for each factory interface and register that object with the ORB. Clients can then get a reference to the factory object and use the creation method of this object to create CORBA application objects. Assuming that a client has obtained a reference to the AccountFactory object created by the server and that this refer- ence is held in the variable acctFactoryRef , the client could create an Account object with account number 12345 and account name ‘John Andrews’ with the following code:
Account acct = acctFactoryRef.createAccount(
12345,"John Andrews");
For non-persistent objects, methods to destroy CORBA objects should also be defi ned (though we shall not be doing so).
To illustrate the use of factory interfaces and their associated factory objects, the rest of this section will be taken up by a specifi c example.
Example
We shall consider how Java IDL may be used to provide platform-independent access to stock items. Although only one item of stock will be used for illustration purposes, this could easily be extended to as many items of stock as might be required by a real-world application. The same basic steps will be required here as were used in the simple CORBA application of the last section, and the same num- bering will be used here to indicate those steps.
1. Create the IDL fi le.
The fi le will be called StockItem.idl and will hold a module called Sales . This module will contain interfaces called StockItem and StockItemFactory . The former will hold the attributes and operations associated with an individual item of stock. For simplic- ity’s sake, the attributes will be stock code and current level, while the operations will be ones to increase and decrease the stock level of this particular stock item. Since the stock code should never be changed, it will be declared read- only. The StockItemFactory interface will hold method createStockItem , which will be used to create a StockItem object with specifi ed stock code and stock level (as indicated by the parameters of this operation). The contents of StockItem.idl are shown below.
module Sales {
interface StockItem {
readonly attribute string code;
attribute long currentLevel;
long addStock(in long incNumber);
long removeStock(in long decNumber);
};
interface StockItemFactory {
StockItem createStockItem(in string newCode, in long newLevel);
};
};
2. Compile the IDL fi le.
As in the previous example, client and server will be run on the same machine, so the -f fl ag will be followed by all . The command to execute the idlj compiler, then, is:
idlj –fall StockItem.idl
This causes a sub-directory with the same name as the module (i.e., Sales ) to be created, holding the following 12 fi les (six each for the two interfaces):
• StockItem.java
• StockItemHelper.java
• StockItemHolder.java
• StockItemOperations.java
• _StockItemImplBase.java
• _StockItemStub.java
• StockItemFactory.java
• StockItemFactoryHelper.java
• StockItemFactoryHolder.java
• StockItemFactoryOperations.java
• _ StockItemFactoryImplBase.java
• _ StockItemFactoryStub.java
3. Implement the interfaces.
Once again, we shall follow the convention of appending the word ‘servant’ to each of our interface names to form the names of the corresponding implementation classes.
This results in classes StockItemServant and StockItemFactoryServant , which must extend classes _StockItemImplBase and _StockItemFactoryImplBase respectively. The code is shown below. Note that both ‘get’ and ‘set’ methods for attribute currentLevel must be supplied and must have the same name as this attribute, whereas only the ‘get’
method for the read-only attribute code must be supplied.
class StockItemServant extends _StockItemImplBase {
//Declare and initialise instance variables…
private String code = "";
private int currentLevel = 0;
//Constructor…
public StockItemServant(String newCode, int newLevel) {
code = newCode;
currentLevel = newLevel;
}
public int addStock(int incNumber) {
currentLevel += incNumber;
return currentLevel;
}
public int removeStock(int decNumber) {
currentLevel -= decNumber;
return currentLevel;
}
//Must supply following 'get' and 'set' methods…
//Accessor method ('get' method) for stock code…
public String code() {
return code;
}
//Accessor method ('get' method) for stock level…
public int currentLevel() {
return currentLevel;
}
//Mutator method ('set' method) for stock level…
public void currentLevel(int newLevel) {
currentLevel = newLevel;
} }
class StockItemFactoryServant
extends _StockItemFactoryImplBase {
/*
Method to create a StockItemServant object and return a reference to this object (allowing clients to create StockItem objects from the servant)…
*/
public StockItem createStockItem(String newCode, int newLevel) {
return (new StockItemServant(newCode,newLevel));
} }
As in the fi rst example, these classes will be placed inside the same fi le as our server code.
4. Create the server.
Our server program will be called StockItemServer.java and will subsume the ser- vants created in the last step. It will import package Sales and (as in the previous example) the following three standard CORBA packages:
• org.omg.CosNaming ;
• org.omg.CosNaming.NamingContextPackage ;
• org.omg.CORBA .
The same basic sub-steps as were featured in the previous example will again be required, of course. Rather than reiterate these steps formally in the text, they will be indicated by comments within the code. The additional code involves the creation of a StockItemFactoryServant object and the associated registration of this object with the ORB, creation of an associated NameComponent object and so forth. Once again, comments within the code indicate the meaning of individual program lines.
import Sales.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
public class StockItemServer {
public static void main(String[] args) {
try {
//Create and initialise the ORB…
ORB orb = ORB.init(args,null);
//Create a StockItemServant object…
StockItemServant stockServant =
new StockItemServant("S0001", 100);
//Register the object with the ORB…
orb.connect(stockServant);
//Create a StockItemFactoryServant object…
StockItemFactoryServant factoryServant =
new StockItemFactoryServant();
//Register the object with the ORB…
orb.connect(factoryServant);
//Get a reference to the root naming context…
org.omg.CORBA.Object objectRef =
orb.resolve_initial_references("NameService");
//'Narrow' ('downcast') context reference…
NamingContext namingContext =
NamingContextHelper.narrow(objectRef);
//Create a NameComponent object for the // StockItem interface…
NameComponent nameComp =
new NameComponent("Stock", "");
//Specify the path to the interface…
NameComponent[] stockPath = {nameComp};
//Bind the servant to the interface path…
namingContext.rebind(stockPath,stockServant);
//Create a NameComponent object for the // StockFactory interface…
NameComponent factoryNameComp =
new NameComponent("StockFactory", "");
//Specify the path to the interface…
NameComponent[] factoryPath =
{factoryNameComp};
//Bind the servant to the interface path…
namingContext.rebind(
factoryPath,factoryServant);
System.out.print("\nServer running…");
java.lang.Object syncObj =
new java.lang.Object();
synchronized(syncObj) {
syncObj.wait();
} }
catch (Exception ex) {
System.out.println("*** Server error! ***");
ex.printStackTrace();
} } }
class StockItemServant extends _StockItemImplBase {
//Code as shown in step 3 above.
}
class StockItemFactoryServant
extends _StockItemFactoryImplBase {
//Code as shown in step 3 above.
}
5. Compile the server and the idl-generated fi les.
From the directory above directory Sales , execute the following command within a command window:
javac StockItemServer.java Sales\*.java (Correct errors and recompile, as necessary.)
6. Create a client.
Our client program will be called StockItemClient.java and, like the server program, will import package Sales . As with the client program in the previous example, it should also import org.omg.CosNaming and org.omg.CORBA . In addition to the steps executed by the client in the previous example, the steps listed below will be carried out.
• Several method calls will be made on the (pre-existing) StockItem object, rather than just the one made on the Hello object.
• A reference to the StockItemFactory object created and registered by the server will be obtained.
• The above reference will be used to create a new StockItem object by invoking method createStockItem (supplying the arguments required by the constructor).
• Methods of the new StockItem object will be invoked, to demonstrate once again that the object may be treated in just the same way as a local object.
The full code is shown below, with comments indicating the purpose of each operation.
import Sales.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
public class StockItemClient {
public static void main(String[] args) {
try {
//Create and initialise the ORB…
ORB orb = ORB.init(args,null);
//Get a reference to the root naming context…
org.omg.CORBA.Object objectRef =
orb.resolve_initial_references("NameService");
//'Downcast' the context reference…
NamingContext namingContext =
NamingContextHelper.narrow(objectRef);
//Create a NameComponent object for the // StockItem interface…
NameComponent nameComp =
new NameComponent("Stock", "");
//Specify the path to the interface…
NameComponent[] stockPath = {nameComp};
//Get a reference to the interface (reusing //existing reference)…
objectRef = namingContext.resolve(stockPath);
//'Downcast' the reference…
StockItem stockRef1 =
StockItemHelper.narrow(objectRef);
//Now use this reference to call methods of the // StockItem object…
System.out.println("\nStock code: "
+ stockRef1.code());
System.out.println("Current level: "
+ stockRef1.currentLevel());
stockRef1.addStock(58);
System.out.println("\nNew level: "
+ stockRef1.currentLevel());
//Create a NameComponent object for the // StockFactory interface…
NameComponent factoryNameComp =
new NameComponent("StockFactory", "");
//Specify the path to the interface…
NameComponent[] factoryPath =
{factoryNameComp};
//Get a reference to the interface (reusing //existing reference)…
objectRef = namingContext.resolve(factoryPath);
//'Downcast' the reference…
StockItemFactory stockFactoryRef =
StockItemFactoryHelper.narrow(objectRef);
/*
Use factory reference to create a StockItem object on the server and return a reference to this StockItem (using method createStockItem within the StockItemFactory interface)…
*/
StockItem stockRef2 =
stockFactoryRef.createStockItem("S0002",200);
//Now use this reference to call methods of the //new StockItem object…
System.out.println("\nStock code: "
+ stockRef2.code());
System.out.println("Current level: "
+ stockRef2.currentLevel());
}
catch (Exception ex) {
System.out.println("*** Client error! ***");
ex.printStackTrace();
} } }
7. Compile the client.
From the directory above directory Sales , execute the following command:
javac StockItemClient.java
8. Run the application.
As before, this requires three steps…
(i) Start the CORBA naming service (unless it is already running) . Enter the following command:
tnameserv
Output should be as shown in Fig. 6.3 .
N.B. Attempting to start (another instance of) the naming service when it is already running will generate an error message!
(ii) Start the server in a new command window . The command for the current application will be:
java StockItemServer Output should be as shown in Fig. 6.5 .
(iii) Start the client in a third command window . The command for this application will be:
java StockItemClient
The expected output should appear in the client window, as shown in Fig. 6.6 .
Fig. 6.5 Output from the StockItemServer program
Fig. 6.6 Output from the StockItemClient program
The examples in this section and in the previous section have (for conve- nience) made use of localhost to run all the components associated with a CORBA application on the same machine. However, this need not have been the case. The ORB, nameserver and object server program could have been started on one host, whilst the client (or clients) could have been started on a different host (or hosts). Each client would then have needed to use the com- mand line option -ORBInitialHost to specify the host machine for the ORB (and, if appropriate, the command line option -ORBInitialPort to specify the port).