| By P.G. Sarang, Mohan Rajagopalan | Article Rating: |
|
| June 1, 1999 12:00 AM EDT | Reads: |
13,177 |
What Is Dynamic Invocation?
CORBA allows you to discover an object's properties dynamically and use its services by invoking the public methods on the object at runtime. This is known as Dynamic Invocation Interface (DII). It's essential for a CORBA object that allows the client to discover its properties at runtime to publish its interface definition language (IDL) for the client to look up. CORBA architecture provides for an Interface Repository (IR) in which the publisher of an object publishes its IDL. Once the client obtains a reference to an object, it can retrieve the IDL from this IR. The IDL helps the client understand the public attributes and methods of the discovered object. The client then invokes the methods on the object using the DII mechanism.
The use of IR and DII not only benefits the client in discovering and using the object, but also helps its publisher add new features to the object anytime after its deployment. The client can then dynamically discover these new features and use them. The new interface definition is published in the IR by the developer. The client now has access to all the latest features provided by the application using the DII mechanism. DII is the "dream come true" for developers since it allows them to modify previously published CORBA objects without the need to recompile and redistribute client-side stubs.
A practical example: you're developing a CORBA application for setting up an online store where customers visit, make some purchases and pay for purchases with a check "snail-mailed" to your account. You'll supply a client-side application that interfaces with your server application. The client application contains the compiled stubs required to use your server application. At a later time you may wish to provide the facility of payment by credit card. Adding a new purchase method in the IDL definition does this job. However, you'll need to supply the new stubs to all your clients. If you'd used the DII mechanism in the client application, the client would be able to discover the newly added method without the necessity of obtaining the new stub.
Object Browser
To emphasize the practical usability of the DII, we've developed a CORBA object browser using Java and JFC classes. The browser requests that the user enter a reference to an existing CORBA object. Usually a CORBA object registers itself with the CORBA Naming Service using a unique name. We pass this name to the object browser, which looks up the Naming Service to resolve the reference to the object. Once the browser obtains a reference, it displays the IDL to the user. The user can now select an operation from the displayed list and invoke it. (A method is called an operation in CORBA terminology and we use the terms interchangeably in this article.) A method invocation may require one or more parameters and may return a result. The browser determines the types for the required parameters, constructs the parameters of appropriate data types, obtains values for each parameter from the user and, on successful completion of the method, returns the appropriate values to the user. In CORBA each parameter may be of IN, OUT or INOUT type. Since Java supports only pass by value, it supports only IN type parameters, implying that the parameters to a Java method are read-only. In CORBA the method may return the value through OUT or INOUT types of parameters. Our browser takes care of all three types of parameters.
To help understand the complex DII interface of CORBA, we'll discuss the design of the CORBA object browser. But first we'll touch on some aspects of locating the object on the Web using the Naming and URL Naming Services of Visigenic, the IOR (Interoperable Object Reference) and Interface Repository.
Locating Object: CORBA Naming Service and Web Naming Service
Our object browser needs to locate the object before trying to use its services. A CORBA object publishes a reference to itself by registering its name with the Naming Service. This is done with the help of the bind or rebind method, in which the object reference is tied to its symbolic name. Before you use this method you must start the Naming Service. On Visigenic ORB (we use this for our development) you start the Naming Service using the following command line, with the root name specified as ABCOM:
vbj -DORBservices=CosNaming -DSVCnameroot=ABCOM DJDKrenameBug
com.visigenic.vbroker.services.CosNaming.
Ext Factory ABCOM namingLog
If you use the Java 2 platform, you start the Naming Service using the following command line:
tnameserv [-ORBInitialPort
The principal task of the Naming Service is to keep track of the namespace, which is the collection of object names bound to a Naming Service. The namespace may contain a hierarchy of bindings extending over several domains just like a file system on your disk. If the namespace extends over several domains, it's called the Federated NameSpace. The binding is the logical association of the object reference to its symbolic name.
A client obtains a reference to the Naming Service by calling the resolve_initial_references() method of the ORB. Once the initial context of the Naming Service is resolved, it calls the resolve method on the NamingContext to obtain a reference to the desired object. The whole task of name resolution is transparent to the client.
Another way to publish your object is to convert the object reference to a string and store it in a file using ORB's object_to_string method. The method returns a stringified version of the IOR, which contains information such as IIOP version, Host, Port, etc., which helps to uniquely identify the object on the Web. The client reads the IOR from the specified file (usually with the extension .ior) at the given URL, and obtains a reference to the object using the information in the IOR. Visigenic requires that the extension given to the IOR file must be .ior if you wish to use Visigenic GateKeeper.
Visigenic provides the URL Naming Service to locate an object using a URL rather than its generic name. This provides for interoperability among objects bound to ORBs on different machines. The following code segment shows you how to use Visigenic's URL Naming Service to locate an object running on a different ORB:
// create the resolver object
// locate object using resolver
com.visigenic.vbroker.URLNaming.Resolver
URLresolver =
com.visigenic.vbroker.URLNaming.ResolverHelper.narrow
(orb.resolve_initial_references
("URLNamingResolver"));
obj = URLresolver.locate(http_location);
The URL Naming Service uses a Resolver Object to locate the .ior file and resolve the reference to the remote object. In the above code snippet we create a Resolver Object using the ResolverHelper provided with the URL Naming Service. The Resolver uses the locate method to search the specified IOR at a given URL. If the IOR file is found, it's read and the reference to the object running on the remote server is returned to the caller.
The publisher must publish the IOR file on a Web server so the client can locate it using the URL Naming Service. If you're using Microsoft's IIS (Internet Information Server), you'll put this file in the InetPub/www.root folder or the folder defined for public access.
Interface Repository
The Interface Repository is used for storing IDL definitions for various CORBA objects. The IDL definition of a CORBA object forms its skeleton, and the object implementation is developed from this definition. The Interface Repository is the online database that maintains a record of interfaces of all registered objects. On a Visigenic ORB you start the Interface Repository using the following command line:
Prompt> irep IRname fileName The IRname is the name assigned to the Interface Repository, and fileName is the physical file used for storage. Once the IR is created, you can add your object definitions using the idl2ir utility:
Prompt> idl2ir file.idl The IDL definition contained in file.idl is now added to the IR. Each entry in the IR contains a header describing the file name, time of creation, user name and location of the file on the machine. The IR stores information in simple text format. You can use a standard text editor to view this.
A typical IR entry is shown below.
/*
File : abcom.ir
Date : Sat Jan 30 06:58:40 GMT+00:00 1999
User : Administrator
Dir : C:\OBJECTBROWSER
*/
module bank {
interface TermDeposit;
interface TermDeposit {
attribute float Interest;
float Compute(
in float Principal,
in short Period
);
};
};
A client obtains the IDL definition from this IR by using the _get_interface() method of the CORBA Object class. This returns the InterfaceDef object. The full description of the interface definition can now be obtained by using the describe_interface() method on this InterfaceDef object.
The following code snippet illustrates this process:
org.omg.CORBA.InterfaceDef objIntfce = obj._get_interface();
org.omg.CORBA.FullInterfaceDescription
fullObjectInterface =
objIntfce.describe_interface();
You may now use the attributes member of the FullInterfaceDescription class to obtain information on the various attributes published by the object. Similarly, you'll use the operations member of the FullInterfaceDescription class to obtain information on the various operations permitted on the object.
To recapitulate, so far you've learned how to:
We've also seen how a client program retrieves this Interface Definition from the IR and discovers all the attributes and operations published by the object. Most important, you've discovered the object and would now like to use its services by invoking one or more of its published methods.
Dynamic Invocation Interface
To invoke a method, you'll first need to construct a list of arguments (parameters) required by the method. CORBA provides an NVList object for constructing such a list. You use the create_list() method of ORB to construct it.
NVList parameterList = orb.create_list(0); The NVList (Named Value List) contains the names and values for the various parameters required by the method. Initially the list is created with zero elements. You then add elements by using its add_value() method:
parameterList.add_value(name, currentParameter, mode); where name is the string representing the name of the parameter being added, currentParameter is the object that represents the parameter and mode indicates whether the parameter is of IN, OUT or INOUT type.
You'll need to add the required number of elements to the NVList depending on the number of parameters used by the desired method. Once an NVList is constructed, you create a request object and pass the parameters to it using the constructed NVList object. To create a request object, you use the _create_request() method of the CORBA Object class.
org.omg.CORBA.Object obj = new org.omg.CORBA.Object();
org.omg.CORBA.Request request =
obj._create_request(
null,
nameMethod,
parameterList,
resultField);
Once the request object is constructed, you can invoke the method on the server using the invoke() method:
request.invoke(); This invokes the method on the server object and returns the result, if any, in the resultField NamedValue object. If the method uses OUT and INOUT types of parameters, you may examine their contents for the return values.
User Interface
Having seen the various requirements for DII, we'll now discuss the user interface of our object browser, which is used for dynamically invoking the methods on CORBA servers for which the stubs aren't available at runtime. The user interface of the browser is shown in Figure 1.
The user enters the name of the server object in the input panel of the browser and clicks on the introspect button. The name specified must be the name the object is registered under with the Naming Service. Alternatively, the user can specify the name of the IOR file along with the URL at which the file is located. A typical URL string may be specified as follows:
http://www.abcom.com/abcom.ior Once an object is located, the browser displays its IDL in the tabbed pane as seen in Figure 1. You can now click on the "Operation and Attribute Listing" tab to see the various operations and attributes published by the server. If you want to examine an attribute, click on the desired one and its various properties will be displayed in a pop-up window. If you decide to invoke a method, click on the method name, which opens a window showing the various parameters required by the method (see Figure 1). If the method doesn't require parameters, it's directly executed. If it does, the type and mode are displayed for each one. Next to each parameter an edit box is provided in which the user can enter the desired parameter value. The browser, however, doesn't provide any validation on the parameter values. Once all the required parameter values are entered, the user clicks on the invoke button to invoke the server method. The program now builds the request object and invokes the method on the server. If the method completes successfully, the results will be displayed in a pop-up window (see Figure 1). The interface is fairly easy to use. We'll now look into the design of the browser.
Browser Design
We used JFC for developing the user interface of the browser. The browser consists of seven public classes:
The ObjectBrowser class (see Listing 1) is the main class of the application and is derived from the JFrame class. It creates instances of InputPanel, OutputPanel, StatusBar and BackEnd classes. The main method creates an instance of ObjectBrowser class and displays the frame to the user. The init() method of the class adds the three user-interface elements InputPanel, OutputPanel and StatusBar to the main display window. The InputPanel is used for accepting the object reference from the user. The OutputPanel shows the IDL listing and the operations/attribute names to the user. The StatusBar provides a status display to the user. The ObjectBrowser class provides a utility method called resolveAndIntrospect(), which is called by the InputPanel class. The method receives the object reference that's to be resolved. The method calls the resolve() method of BackEnd class, and if the object is successfully resolved it calls the introspect() method of BackEnd class to introspect the object and to display the IDL to the user on the OutputPanel.
The InputPanel class (see Listing 2) is derived from the JPanel and provides the user interface for accepting the object name. The user interface consists of an edit box and two buttons Introspect and Exit. The event handler for the Introspect button calls the resolveAndIntrospect() method on the ObjectBrowser class by passing the received object reference. As mentioned earlier, the object reference is either the object's registered name with the Naming Service or the complete URL containing the location of the IOR file.
The OutputPanel class (see Listing 3), derived from JTabbedPane, contains two tab panes: one for displaying the IDL to the user, the other for displaying the lists of operations and attributes. The init() method sets up the two panes. The Interface Definition pane uses a TextArea control to display the IDL to the user. The Operation and Attribute pane creates two Box objects and adds JList control to each for displaying the operations and attributes.
Whenever the user changes the selection in the attributes list control, the showAttribute-Description() method is called. This in turn creates an AttributeDescription object and displays the information about the selected attribute to the user. Similarly, whenever the user changes the selection in the operations list control, the showMethodDescription() method is called. This creates the OperationDescriptionTable object and displays it to the user. The updateList() method updates the contents of the list control by first clearing it and then filling it with the new data.
The AttributeDescription class (see Listing 4) is derived from JFrame and displays the information on the selected attribute to the user. The class constructor receives the name of the attribute as a parameter and a reference to our ObjectBrowser class. The init() method calls its own getDescription() method to get the information on the desired attribute and then displays the information to the user by calling its displayFrame() method. The getDescription() method iterates through all the attributes defined in the fullObjectInterface variable and retrieves the mode and type information for the desired attribute. The displayFrame() method then displays the attribute name, mode and type to the user.
The OperationDescriptionTable class (see Listing 5) derived from JFrame displays in tabular format the names of parameters required by the desired operation. The class also accepts the values for each IN and INOUT type of parameter from the user. In button event handler, if the user has clicked the invoke button, we copy the input parameters from the TextField objects in the table to a String array, which is then sent to the BackEnd object by calling its setParameters() method. The BackEnd will use these parameters while invoking the server method. Once the parameters are set, the program calls the local performDii() method, which passes the request to the processRequest() method of the BackEnd class. The BackEnd processes the request and displays the results.
The StatusBar class (see Listing 6) is derived from the JLabel and is simply a utility class for displaying status messages in the browser window.
Now we come to the most important class, BackEnd, which is responsible for all CORBA-related back-end processing (see Listing 7). The class constructor receives a reference to our ObjectBrowser and copies it into a local variable. The init() method first initializes the ORB by calling its init() method:
orb = org.omg.CORBA.ORB.init (param, null); If the ORB is successfully initialized, we call resolve_initial_references() method on the ORB to resolve a reference to the Naming Service. We then narrow (type cast) the returned object to the NamingContext object type. The initialization of the BackEnd object is complete and the object now waits for its other methods (resolve, introspect, processRequest) to be invoked. The resolve() method receives the object as a string and tries to locate the object on either the current ORB or the ORB running at the specified URL:
if (!(resolved = resolveUsingName(object)))
resolved = resolveUsingURL(object);
The resolveUsingName() method constructs the NameComponent array and tries to resolve the object reference by calling the resolve method of the Naming Service:
NameComponent[] name = {new NameComponent(object, "")};
obj = nameService.resolve (name);
If this resolution fails, we try to resolve using the URL Naming Service. The method resolveUsingURL() obtains a reference to the URLNamingResolver and narrows it down to the proper data type as follows:
org.omg.CORBA.Object resolverObj =
orb.resolve_initial_references("URLNaming
Resolver");
com.visigenic.vbroker.URLNaming.Resolver
URLresolver =
com.visigenic.vbroker.URLNaming.
ResolverHelper.narrow(resolverObj);
The StatusBar class (see Listing 6) is derived from the JLabel and is simply a utility class for displaying status messages in the browser window.
Now we come to the most important class, BackEnd, which is responsible for all CORBA-related back-end processing (see Listing 7). The class constructor receives a reference to our ObjectBrowser and copies it into a local variable. The init() method first initializes the ORB by calling its init() method:
orb = org.omg.CORBA.ORB.init (param, null); If the ORB is successfully initialized, we call resolve_initial_references() method on the ORB to resolve a reference to the Naming Service. We then narrow (type cast) the returned object to the NamingContext object type. The initialization of the BackEnd object is complete and the object now waits for its other methods (resolve, introspect, processRequest) to be invoked. The resolve() method receives the object as a string and tries to locate the object on either the current ORB or the ORB running at the specified URL:
if (!(resolved = resolveUsingName(object)))
resolved = resolveUsingURL(object);
The resolveUsingName() method constructs the NameComponent array and tries to resolve the object reference by calling the resolve method of the Naming Service:
NameComponent[] name = {new NameComponent(object, "")};
obj = nameService.resolve (name);
Next, we use the locate method to resolve the object reference:
obj = URLresolver.locate(object);
InterfaceDef objIntfce =
obj._get_interface();
fullObjectInterface =
objIntfce.describe_interface();
final int noAttributes =
fullObjectInterface.attributes.length;
final int noOperations =
fullObjectInterface.operations.length;
for (int i = 0; i
mode = fullObjectInterface.attributes[i].mode.value
());
type = fullObjectInterface.attributes[i].type;
name =
fullObjectInterface.attributes[i].name;
}
Finally, we look at the most important method, processRequest(). This method obtains the name of the operation to be invoked as the parameter and invokes the method on the server object. First we obtain the index of the desired method from the list of operations described in the fullObjectInterface object:
for ( ; i
if (operationName.equals
(fullObjectInterface.operations[i].name))
break;
}
int noParameters =
fullObjectInterface.operations[i].
parameters.length;
NVList parameterList = orb.create_list(0);
Any currentParameter = orb.create_any(); The special type "Any" defined in CORBA is used to represent the element with any data type. We initialize this object with the proper data type by using the "type" member of parameters[i] variable. The program then examines the mode for the parameter, which can be IN, OUT or INOUT type. The method receives an input value through this parameter and returns the result to the caller through the same parameter.
Depending on the value of the mode, we set our mode variable to the proper CORBA data type. For the IN type of parameter the mode variable is set to org.omg.CORBA.ARG_IN.value; for the OUT type it's set to org.omg.CORBA.ARG_OUT value; and for the INOUT type it's set to org.omg.CORBA.ARG_INOUT value. We also set an "accept" flag for each parameter. If the accept flag is set, it indicates we'll be assigning a value obtained from the user to this object (applicable to the IN and INOUT types of parameters).
The program then retrieves the value entered by the user for the current parameter by using parametersValue array. Note that all parameter values are stored as String data type. We now set up a switch statement to convert this parameter value to the proper data type and assign it to our parameter object in an NVList. We check the parameter data type by using kind() method on the current parameter:
switch
( currentParameter.type().kind().value())
The CORBA "Any" class provides several insert_xxx types of methods to assign the objects of various data types to the "Any" object. For example, insert_short() method assigns an object of type "short," insert_long() assigns an object of type "long," etc. Thus, in the switch statement, we check for the kind of parameter required and use the corresponding insert_xxx method to assign the appropriate object type to our parameter object. We've programmed most of the cases in the switch statement. Some of the complicated cases are left out due to the complexity involved. For example, if you want to insert a structure data type, you'll need a Java class encapsulating the structure definition. You can then insert a Java object of this class for the desired parameter value.
Once the parameter is initialized with the proper data type and its value, we add the parameter to an NVList using its add_value() method:
parameterList.add_value(
parameters[j].name,
fullObjectInterface.operations[i].
currentParameter, mode);
Once the NVList for operation parameters is constructed, we need to create a Named Value object for the return value of the method. We construct and initialize this NamedValue object using the following lines of code:
Any resultParameter = orb.create_any();
org.omg.CORBA.NamedValue resultField =
resultParameter.type
(fullObjectInterface.operations[i].result);
orb.create_named_value("resultField",
resultParameter,
ParameterMode._PARAM_OUT);
Request request =
obj._create_request(null,
fullObjectInterface.operations[i].name,
parameterList, resultField);
The _create_request() method requires four parameters. The first specifies the CORBA context and is set to null in this case; the second specifies the operation name; the third specifies the NVList containing the list of parameters required by the operation; and the fourth is another NVList in which the return value, if any, is returned.
You're now ready to invoke the method on the server, a simple process. You use invoke() method on the request object to invoke the method on the remote server:
request.invoke();
Having seen the design of the object browser, we now examine how to compile and run the entire application.
Running the Browser
We used JDK 1.1.5 and Swing classes for the development of the object browser. We've created several batch files for compiling and running the code. The swingcompile.bat file (see Listing 8) compiles the entire source. This file contains the following statement:
javac -classpath %SWINGPATH% %1
where SWINGPATH is the system variable defined on our system that sets up the class path for Java, Visigenic CORBA and Swing classes.
The parameter to the batch file is our main Java class ObjectBrowser.java. If your classpath is set properly, running swingcompile batch file should compile all the relevant files.
Before you run the browser, you need to do certain startup operations, such as starting OSAgent, starting the Naming Service, etc., with the help of startup.bat file (see Listing 9). This batch file starts the OSAgent using the following command line:
start OSAgent c
Next, it starts the interface repository using the following command line:
start irep InterfaceRepository ABCOM.ir
The command irep starts the repository service. The name of our repository is InterfaceRepository, and it's stored in the file called ABCOM.ir in the current folder. Next, the CORBA Naming Service is started using the following command line:
start vbj -DORBservices=CosNaming
-DSVCnameroot=ABCOM DJDKrenameBug
com.visigenic.vbroker.services.CosNaming.
ExtFactory ABCOM namingLog
This completes the operation of our startup batch file. When you run this batch file, you'll notice that three windows pop up on your terminal; one is used by OSAgent, the second by Interface Repository and the third by the Naming Service.
Now we're ready to run our object browser, which is started using show.bat file (see Listing 10):
vbj -DORBservices=CosNaming -DSVCnameroot=ABCOM -VBJclasspath
"%SWINGPATH%" ObjectBrowser
Running this batch file starts our object browser. You're now ready to discover any object either in the current ORB or the ORB running at some known URL.
We've provided a test server application for your convenience. It's called SampleServer (see IDL Listing in Sample.idl file) (Listing 11). The server provides three sample methods:
The implementation files, along with the compiled code, can be downloaded from the JDJ Web site. You'll need to run idl2ir to load the IDL into your IR before running the SampleServer application. The application registers itself with the Naming Service using the name "SampleObject". Type this name in the browser input panel and click on introspect to display the IDL. You can then select any of the three listed methods, invoke them and test the result.
Conclusion
The DII is probably the most complex yet most versatile interface provided in CORBA. It allows you to discover and use a CORBA object at runtime without the need for compiled stubs. We've described the entire DII mechanism and the design of a CORBA object browser that's based on the discussed techniques.
Acknowledgments
We'd like to thank Kamal Shah and Bhakti Mehta for their valuable help in the development and testing of the CORBA object browser.Dr. Poornachandra G. Sarang is president and CEO of ABCOM Information Systems Pvt. Ltd., a consulting firm specializing in Internet, Java, CORBA, Visual C++ and VB programming and training.sarang@abcom.comMohan Rajagopalan is currently studying for a degree in computers, and plans to pursue a graduate degree specializing in distributed systems and heterogeneous networks. The CORBA object browser was developed as part of his practical training at ABCOM
If this resolution fails, we try to resolve using the URL Naming Service. The method resolveUsingURL() obtains a reference to the URLNamingResolver and narrows it down to the proper data type as follows:
org.omg.CORBA.Object resolverObj =
orb.resolve_initial_references("URLNaming
Resolver");
com.visigenic.vbroker.URLNaming.Resolver
URLresolver =
com.visigenic.vbroker.URLNaming.
ResolverHelper.narrow(resolverObj);
Published June 1, 1999 Reads 13,177
Copyright © 1999 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By P.G. Sarang
Dr. Sarang in his long tenure of 20+ years has worked in various capacities in the IT industry. Dr. Sarang currently holds the position of Director (Architecture) with Kynetia, Spain and has been a Consultant to Sun Microsystems for last several years. He has previously worked as a Visiting Professor of Computer Engineering at University of Notre Dame, USA and is currently an adjunct faculty in the Univ. Dept. of Computer Science at University of Mumbai. Dr. Sarang has spoken in number of prestigious international conferences on Java/CORBA/XML/.NET and has authored several articles, research papers, courseware and books.
More Stories By Mohan Rajagopalan
Mohan Rajagopalan is currently studying for a degree in computers, and plans to pursue a graduate degree specializing in distributed systems and heterogeneous networks. The CORBA object browser was developed as part of his practical training at ABCOM
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Journal Opens "Readers' Choice Awards" Nominations
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Industry Experts Discuss the State of Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- It's the Java vs. C++ Shootout Revisited!
- The End of IT 1.0 As We Know It Has Begun
- An Introduction to Abbot
- Java Kicks Ruby on Rails in the Butt
- Interviewing Java Developers With Tears in My Eyes
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- 1st Annual Government IT Expo: Call for Papers Deadline July 15
- How to Diagnose Java Resource Starvation
- REA Is Where RIA Becomes the Norm
- Kindle 2 vs Nook
- Anatomy of a Java Finalizer
- Why IBM’s Server Chief Got Busted
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?



































