Developing Applications With Complex Graphics in JDeveloper 9i
Background
JDeveloper 9i is a sophisticated development tool. For building client/server applications, it is slightly less efficient and more difficult to build using JDeveloper than in a standard 4GL such as Developer. It also takes significantly longer although JDeveloper is improving with every version and the Oracle team is working to decrease the lag between the products.
There are things that JDeveloper can do that Developer and other 4GLs cannot. In particular, JDeveloper is very well suited for creating highly complex, graphics-intensive applications. These types of applications could never be built in Developer regardless of the time and skill of the resources devoted to the project.
At Dulcian, we used JDeveloper 3.2 to build an application using sophisticated graphics over the last 18-months. This was a long and difficult process, requiring two complete rewrites of the application before adequate functionality and performance were achieved. In the end, a number of creative workarounds were also required to reach the desired result.
This paper
· Describe the application we built
· Outline the difficulties encountered
· Provide advice for building similar applications
· Show some of the basic ideas and code examples associated with building a JDeveloper graphics application
Description of the Application
Dulcian has evolved our own business-rule based systems development methodology over the last few years. All system requirements are placed into a unified repository from which the system is generated. This grand vision requires a single application to maintain a complex repository containing different kinds of business rules:
· Structural business rules are entered using UML class diagrams
· Process-related business rules are entered using modified UML activity diagrams
Using this approach, in addition to supporting the traditional complex application requirements, it was also necessary to include the ability to draw diagrams on the screen similar to the functionality of Oracle Designer’s ER Diagrammer or JDeveloper’s Class Modeler. We were ultimately trying to create a single, integrated application to maintain the entire rules repository.
To achieve this goal, we had to overcome the “piecemeal” feel of products such as Designer where the Repository Object Navigator (RON) is separate from the ER Diagrammer. Changes made in one part of the tool are not immediately reflected in the other. After many months of work, we were able to create a unified environment where we can design, generate, test and maintain configuration management of a system. Figure 1 shows the resulting integrated Repository Manager Process Flow tool.
Figure 1: Repository Manager Process Flow Tool
It is beyond the scope of this paper to fully discuss the
architecture of the business rule engine. Contact the author for further
information.
Difficulties Encountered in Creating a Graphics Application in JDeveloper
In order to build applications using the business-rule based
methodology described above, you must recognized that the coding required goes
far beyond what the JDeveloper 9i wizards can support. A lot of hand coding was
necessary in order to create these types of applications. If you have not had a
great deal of experience with Java, you
by David M. Geary (Prentice Hall , ISBN: 0130796662 1998 ) were very
useful.
One of the toughest challenges faced in working with the JDeveloper 3.2 release was in getting the graphical objects to interact meaningfully with the Oracle database. For example, in a UML class diagram, if each row in the database table CLASS represents a class and a diagram showing classes, it is necessary to represent the classes from the table on the screen as well as being able to add classes on the screen and have them inserted into the database.
In traditional applications development, the user interface (UI) components are intelligently linked to the database. This is a more complex with graphics objects since they must be created on the fly. Immediate access was used in our system to extract the information from the JDeveloper Business Components for Java (BC4J). This used the old ROWSET/Infobus architecture, which produced terrible performance. It took 1/100th of a second to perform immediate access retrieval from a BC4J cached object. This may not sound like a long time, but when displaying a diagram, there may be several thousand information elements to consider. Every object has at least four: height, width, x and y positions). Using this interval rate (10 seconds/1000 elements), some diagrams took up to 30 seconds to load.
The next section
Building a Graphics Application
This section
The second section
NOTE: This is not a novice step-by-step tutorial. It is assumed that you already know hot to create workspaces, projects and simple applications in JDeveloper.
Application to Draw a Box and Line (no database access)
The following steps can be used to create a simple drawing
application:
1. In a new workspace and project, create a JFrame object. Inside the frame, create a JDesktopPane and JInternalFrame. These are required to support MDI graphics development. An excerpt of the code used is shown here (Snippet 1)
public class FrameBc4jPackageModule extends JFrame
{
private JUApplication app
= null;
private GridLayout
gridlayout = new GridLayout();
JDesktopPane myDesktop =
new JDesktopPane();
BorderLayout
borderLayout1 = new BorderLayout();
JInternalFrame
class1Frame = new JInternalFrame();//Must use JInternalFrame for MDI
…
Snippet 1: Code to support MDI graphics development
Class1Frame is the name of the object to be displayed and sized on the screen. Note that the frame is set up using GridLayout as the Layout property. This can be done either by using the JDeveloper wizards to generate the frame or hand coded.
2. In the Jbinit method for the frame, add myDesktop (JDesktopPane) to the overall application frame’s content using the code shown here (Snippet 2):
this.getContentPane().add(myDesktop, BorderLayout.CENTER);
Snippet 2: Code to add a pane to the desktop
3. Define the location and size of the window by invoking the appropriate set methods using the following code (Snippet 3):
class1Frame.setLocation(50,50);
class1Frame.setSize(100,50);
Snippet 3: Code to define location and size of window
4. Add and display the frame using the code shown here (Snippet 4):
insert 4
myDesktop.add(class1Frame);
class1Frame.setVisible(true);
Snippet 4: Code to display the frame
The four steps above are all that is necessary to handle frames.
You can take advantage of the native MDI environment to support the drawing of
frames. For most drawing objects, you
5. To draw lines or other objects on the screen, it is necessary to make standard graphics calls as shown in the code below (Snippet 5):
public void myDraw(Graphics g)
{
//This paints lines
g.setColor(Color.black);
g.drawLine(0,0,200,200);
}
Snippet 5: Code for graphics call
This code creates a graphics object and draws a black line. MyDraw is invoked in the library paint( ) method as shown in the following code (Snippet 6):
public void paint(Graphics g)
{
myDraw(g);
}
Snippet 6: Code to invoke MyDraw
If you run the routine at this point, the line
public void paint(Graphics g){
myDraw(g);
class1Frame.repaint(); //This paints Frames over lines
}
Snippet 7: Code to repaint the frame
Moving graphics on the screen
You may want to click and drag frames and not allow the line to
disappear. Using only the steps above, at this point, the frame
There are significant resources required in moving frames around on a screen that may not be justified. Some programs use degraded graphics where the user sees an outline of the object being moved. The object is then repainted after movement is complete. This is a simple issue for frames. You simply need to set the myDesktop JDesktop DRAG mode in the Jbinit method as shown here (Snippet 8).
myDesktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
Snippet 8: Code to move frame on screen
The line must now be repainted after the frame is moved. This is accomplished by adding a mouseReleased event to the frame to re-invoke myDraw as shown in the following code (Snippet 9):
void class1Frame_mouseReleased(MouseEvent e)
{
myDraw(this.getGraphics());
}
Snippet 9: Code to repaint line after moving frame
Create a Simple Repository Manager (database access)
The application works by loading the classes into BC4J, looping through the BC4J view and displaying the classes on the screen. The application also includes a button with the functionality of loading and displaying the classes.
1. Create a table called “CLASS” and populate it with data using the code shown below (Snippet 10):
CREATE TABLE class
(
classid NUMBER(10) NOT NULL,
name VARCHAR2(200),
locx NUMBER(10),
locy NUMBER(10),
width NUMBER(10),
height NUMBER(10),
color VARCHAR2(20)
)
Snippet 10: Code to create CLASS table
2. In a new workspace, create a standard BC4J project using the wizards to generate an entity and view for the CLASS table.
3. Again using the JDeveloper wizards, create a frame and panel for the BC4J object.
4. Create a default application to support the CLASS table.
At this point, there are two different ways to interact with a BC4J view:
· Directly by manipulating the view
· Indirectly by bolting a BC4J view to a UI construct and interacting with the UI object for retrieval and updating of data
These two approaches result in virtually identical performance statistics. Each has its own pros and cons. Using UI components, you also have the advantage of reusing these components. For this application, you can have the graphical classes appearing on the screen and the UI components appearing in a separate window such as a property sheet.
Using the UI components approach takes advantage of the power of the JDeveloper 9i wizards by decreasing the amount of manual coding required.
Not using the UI components is a conceptually cleaner approach. For some, it may be more technically satisfying to interact directly with the BC4J components rather than placing them in an intermediate area.
In JDeveloper v. 3.2, it was necessary to use UI components because directly accessing BC4J rowsets was significantly slower. In the 9i release, this performance difference does not exist.
5. In a BC4J Panel, using a BorderLayout, place a button on the screen. Label the Button “Load.”
6. Create an InternalFrame and JDesktopPane and add the internal frame to the desktop pane as in the previous example.
7. As you spin through the BC4J objects, they must be loaded into a vector. To declare the vector, use the following code: (Snippet 11):
Vector classVector = new Vector(); //Holds pointers to Frames representing classes
Snippet 11: Code to declare vector
8. Clear the vector using the code shown here (Snippet 12):
classVector.removeAllElements();
Snippet 12: Code to clear vector
9. Generate a pointer to the BC4J class view by creating the appropriate objects in the declaration section of the class as shown here (Snippet 13):
ViewObject vo; //access to BC4J 'Class' view
int voRows; //used to store the number of rows returned in the View
Snippet 13: Code to generate pointer
10. Instantiate the pointer using the following code (Snippet 14):
vo =
app.getApplicationModule().findViewObject("Class1View");
voRows = vo.getRowCount();
vo.reset();//resets the iterator to 'before' the first row
Snippet 14: Code to instantiate pointer
11. Create
an action event on the Load button either suing the wizards or by adding the
following method to the class (Snippet 15):
void loadjButton1_actionPerformed(ActionEvent e) {}
Snippet 15: Code to enable Load button
12. Spin through the BC4J rowset using the code below (Snippet 16) to retrieve the information for the frames and add it to the vector:
for(int r=0; r<voRows; r++){
if(vo.hasNext()){
Row row =
vo.next();//returns first row, then next row, etc...
//Creats a new
JInternalFrame for each row in the ClassView
JInternalFrame
currentFrame = new JInternalFrame("",true,false,true,false);
myDesktop.add(currentFrame);
currentFrame.setTitle(new
String(row.getAttribute("Name").toString()));
currentFrame.setLocation(
Integer.parseInt(row.getAttribute("Locx").toString()),
Integer.parseInt(row.getAttribute("Locy").toString()));
currentFrame.setSize(
Integer.parseInt(row.getAttribute("Width").toString()),
Integer.parseInt(row.getAttribute("Height").toString()));
currentFrame.setVisible(true);
classVector.addElement(currentFrame);//add instance of
JInternalFrame to the vector
}
Snippet 16: Code to retrieve frame information
Since SET commands take integers, the only way to get information from the BC4J layer is by using the toString( ) method. You must then cast the value back to be an integer using the parseInt( ) method.
Because the frames are stored in a vector, they can be re-displayed very quickly with a simple myClassDraw method as shown here (Snippet 17):
public void myClassDraw(Graphics g)
{
g.setColor(Color.black);
if (!classVector.isEmpty())
{
for(int i=0;
i<classVector.size();i++)
{
((JInternalFrame)classVector.elementAt(i)).repaint();
}//End
}//End If
}
Snippet 17: myClassDraw method
The only difference between working directly with BC4J and using the UI components is that another command must be used to retrieve the information to set the title and location as shown here (Snippet 18):
currentFrame.setTitle(new String(name_jTextField1.getText()));
currentFrame.setLocation(
Integer.parseInt(locXJtextField.getText()),
Integer.parseInt(locYjTextField.getText()));
Snippet 18: Code to set title and location
Note that the getText( ) method must be explicitly cast to an “int” for the setLocation method.
Conclusions
Working with graphics in Java is neither simple nor intuitive. As is usually the case, there are easier and harder ways of accomplishing the same tasks. After 18 months of experience with this new tool, we found that Murphy’s Law seemed to be in full force. The first three ways we attempted for many tasks seemed to be the least effective.
We also gained a new appreciation for why some capability was never built into other products by their development teams. Even functionality that may appear quite simple, is in reality, quite complex. For example, detecting whether or not a user has clicked near a line appears to be a simple task. This task can be approached in two ways. The hard way is to globally detect a mouse click event, determine the cursor location and mathematically calculate the distance to each line on the screen to see if it is close. The easy way is to place an invisible long, thin box around each line with a listener to detect a mouse click event. Unfortunately, we tried the harder way first.
In another case, to draw boxes on the screen , you can either draw rectangles and code intelligent behavior on them or use native frames to get all of the drag and drop, resizing and degraded graphics functionality for free. (Guess which approach we tried first?)
The point is that graphics programming in Java, and particularly
when connecting to an Oracle database is a unique, specialized area of
knowledge. Before embarking on such a “trek,” you would be wise to find a “code
Sherpa” to help you avoid the myriad crevasses where you can get stuck.
About the Author
Dr.