Repository-Based J2EE Development

Dr. Paul Dorsey, Dulcian, Inc. & Yalim Gerger, Gerger Consulting

 

I. The Problem

The current web applications development product environment is clearly not ideal. It has many different components and is difficult to navigate. The resulting systems are also less robust than the client-server applications that were being built several years ago. The J2EE environment is evolving and changing so quickly that it is very difficult to keep up. Each year, the current thinking about web architecture evolves considerably. Two years ago, JavaServer Pages (JSPs) were the solution of choice, currently JSP/Struts are being used and next year, JavaServer Faces (JSFs) are likely to be the chosen alternative.

 

The industry standard, Enterprise JavaBeans (EJB), for middle tier development has been completely revamped with EJB3. Web Services and BPEL must now be taken into consideration along with a host of other new technologies. There is no agreed-upon industry standard for the right way to build J2EE-compliant web-based systems for no other reason than that these standards take months, if not years, to evolve and there continue to be major technology revisions every few months.

 

II. The Solution

One solution to this dilemma is to articulate the system in a technology-independent fashion. Every enterprise-level system includes thousands, if not tens of thousands of business rules. However, these rules fall into a small number of categories. It is possible to create a code generator to generate the appropriate code from these rule categories. The generator itself only requires a few thousand lines of code. The advantage of building systems using this approach is that when the technology changes, you only need to refactor the generator code to accommodate the new architecture.

 

This is not a new idea. CASE tools from many years ago were based on this same vision. So, why isn’t everyone using this approach? The answer is that it is very difficult to design the repository so that it is technology-independent, user-friendly, usable by normal developers, and understandable to users. It must also be robust enough to handle a reasonable number of applications and complex rule structures.

 

In practice, once the repository has been designed and built, writing the generator code is a surprisingly simple exercise. It may require weeks or even months to determine the user interface standards, appropriate technological implementation (e.g. the appropriate Struts flow diagram to generate). However, once these decisions have been made, building the code generator requires much less effort than initially creating, let alone maintaining, traditional applications. The reason for this is that the ratio of generator code to generated code may easily be 100:1 or more. As an example, in one project that the author worked on, the system generator required approximately 1000 lines of code. This generator was used to generate two versions of the system in both PL/SQL and Java, each of which contained 300,000 lines of code. In this one instance, it was possible to achieve a 600:1 payback on generator/generated code.

 

A. The Taxonomy

The taxonomy for describing the repository-based solution is as follows:

 

I. Logical Specification of a System

            A. Object Rules

·         Structural

·         Process

·         Data Validation

            B. User Interface (UI) Rules

                        1. Model

a. Structure

                                    b. Binding to objects

                        2. View

                                    a. Structure

                                    b. Binding to model

                                    c. Logic

                                    d. Presentation

3. Controller

 

II. Physical Specification of a System

            A. Persistence Layer

                        1. Objects

2. Code

            B. Model

                        1. Objects

                        2. Code

            C. View

                        1. Objects

                        2. Code

            D. Controller

                        1. Objects

                        2. Code

 

B. Logical vs. Physical Specifications

Making the distinction between the logical and physical portions of the solution should raise few objections, but it is surprising to note how few product architectures (even ones with an expressed “business-rules based” philosophy) embrace this dichotomy. 

 

The idea is the system can be specified in an implementation-independent way. Deciding what to do with this specification is then an independent step.  This approach can be summed up in the motto: “The articulation of the rules is independent of the implementation of the rules.” 

 

Most products ignore this idea and straddle the fence.  As an example, the Oracle Application Development Framework (ADF) Business Components (BC) architecture in the JDeveloper product includes some user-friendly screens that allow you to articulate quite complex business rules.  These rules are then represented either as XML files that are directly referenced by Java classes at runtime or are themselves code generators.  This architecture makes it very difficult for someone building in ADF BC to change architectures.  Even moving from version to version of the JDeveloper product has usually required a complete rewrite of the business components layer. ILOG’s JRules and FairIsaac’s Blaze Advisor tools suffer from similar problems.

 

In the case of JDeveloper, ignoring the distinction between the logical and physical aspects of the system being built is appropriate.  JDeveloper is an implementation tool.  Its goal is to provide developers with quick and efficient ways to implement an architectural vision. With business rules-based products such as those mentioned above, this direct-to-code approach is inappropriate.  One of the greatest advantages of a rules approach is that you are describing the system from the user’s perspective. It ought to then be possible to implement that description in a variety of different ways.  As the technology evolves, it ought to be very easy to refactor the rules to support new technologies. 

 

In the tools explicitly catering to the system architect, this separation of logical and physical is implemented quite cleanly.  Both Oracle Designer and IBM’s Rational Rose allow users to specify and then generate large portions of the system.

 

This division between the logical system specification and its implementation is not without problems.  Having separate logical and physical layers requires that they be kept in synch.  In systems that require maintaining two (or more) models, it is nearly impossible to keep them synchronized. This problem should be familiar to most database designers.  Anyone using a tool that generates a data structure (such as Oracle Designer) is aware of the practical impossibility of keeping the model and database in synch.  With Designer, the situation was more difficult since the tool supported both a logical ERD as well as a loosely coupled physical data model which was used to generate the actual physical database.  Therefore with Designer, there are three representations of the database.  When changes are required or errors discovered, they potentially must be made in all three places.

 

Using a waterfall methodology where the design progresses in an orderly fashion from logical development to physical implementation, this type of tool worked very well.  However, once the system was in place, it was a very difficult architecture to maintain over time.

 

The problem arises from using the logical model to generate the implementation model and then allowing independent manipulation of the implementation model. There is nothing wrong with the idea of having both a logical and physical model, but trying to maintain multiple models is usually impractical.

 

The solution to the dilemma is to tightly couple the logical and implementation models.  Instead of having independent logical and physical models, specify what is “logical” (implementation-independent) in the logical model and only specify “additional” (implementation-dependent) things in the implementation model.  Only allow limited overriding of the algorithm used to generate the physical model and keep track of what was overridden so that element changes in the logical model can be updated in the physical model. This tight coupling of the system layers is an important concept to remember and enforce.

 

III. Logical Specification of a System

When logically specifying a system, the idea is to partition rules into those about objects that will be true for any representation of those objects and rules that are UI-dependent.  Few existing products divide the rules in this way.

 

A. Object Rules

Object-level rules are where most of the design of the system takes place. This idea should be the core of our thinking about application development. Traditionally business rules about the objects are not even captured coherently let alone coded so they are clearly attached to the objects.

 

Logically, object rules are not a difficult concept to grasp.  For example, if lastName_tx is a required attribute of the Employee class, then any user interface that supports inserting or editing that object should properly enforce that rule.  Therefore, it should not be incumbent upon the system designer to specify that the attribute is required at the UI level.  All that should be necessary is an indication that this text box is an editable representation of the attribute so that the object level rules (length, required, textual validations) apply. 

 

The newer and more complex aspect to the concept of object rules is that their inheritance should be automatic.  In addition, whenever possible rules should be specified at the object level and be inherited at the UI level.

 

Rules associated with objects usually comprise a large portion of the total system rules. Many people think of object rules as only contained in the data model. While it is true that object rules will define the data structure of the database, these rules can specify much more than what is contained in a traditional ERD. For example, the logical process flow for handling an object such a purchase order can be defined at the object level. The process should also be independent of any particular representation in the user interface. Data validation rules such as “any contract actions should take place between the start date and end date of the associated contract” should be articulated with the object so that any representation would conform to the rule.

 

B. User Interface (UI) Rules

If rules are placed at the object level, why are additional rules needed at the user interface level? It is possible to generate an entire default user interface based solely upon knowledge of the associated objects. However, what is generated will not be particularly user-friendly. There are additional rules that are concerned more with the needs of the user that are not properly associated with system objects at all. For example, determining which attributes will be displayed in what positions on the screen, what areas of the screen are automatically populated when the screen opens, whether or not rules are validated when tabbing out of a field, are rules that are not dependent upon system objects, but instead are UI design decisions.

 

C. Where should the rules be defined?

This division between object rules and UI rules is not always clear cut. Occasionally, there are rules encountered that could be placed in either category. For example, in an address object with COUNTRY and POSTAL CODE attributes, at the object level, the COUNTRY and POSTAL CODE should be consistent. However, in practice, changing the COUNTRY from USA to Canada when there used to be a US zip code in the postal code field should blank out the zip code (set its value to Null). Typing in a US or Canadian postal code in the field should change the country field to the appropriate country. Should the rule controlling the nullification of these fields be associated with the address object itself or should it be defined in each representation of the address object? It is possible to argue the answer either way. The factors driving these decisions are the complexity and usability of the underlying repository.

 

If UI rules are attached to the objects themselves and overridden in the representation of the object, full object-oriented (OO) thinking must be used in designing the repository. This renders the repository more difficult to use, especially for non-IT professionals. If a rule structure is too complex for users to understand, there is little reason to include it in the repository.

 

It is still a good idea to define rules that are universal across all representations of an object at the object level. Only additional UI-specific rules should be defined at the UI level. Most of the rules required to define an application occur at the object level. In practice, the division is approximately 80% object rules and 20% UI rules.

 

D. Object Rule Types

Object Rules can be subdivided into Structural, Process and Data Validation rules.  It is important to remember that these rules by their nature do not divide into these three classifications.  These are three different and arbitrary ways to describe object rules. For any given rule, it is usually most convenient to articulate it using one particular mechanism, but most rules can be articulated using more than one mechanism.

 

1. Structural Rules

Structural rules describe the static nature of the objects in the system.  What are the objects of interest? What are the attributes associated with them? This is the kind of information that is traditionally captured in ERDs although in recent years, UML class diagrams have become more widely used for diagramming systems.

 

UML Class diagrams (with some extensions) are somewhat better suited for data modeling than ERDs.  ERD proponents argue that UML is an implementation tool and that cannot be used for logical modeling. UML proponents argue that ERDs are too limited and do not support inheritance well enough. This debate is beyond the scope of this paper but the choice of diagramming syntax is of far less importance than deciding what is to be captured. 

 

Data modeling has traditionally only been concerned with capturing the information required to generate the tables, columns and constraints for the DBMS.  Structural rules include a much broader range of information than that limited scope. Structural rules should include the following areas not traditionally included in data modeling:

·         Derived attributes

·         Objects defined using other objects (for example, to generate database views)

·         Audit, security, and history requirements

·         Default object display

·         Triggering events based on Insert, Update, and Deletion of objects or attributes

 

The idea is to partition all of the rules required to specify a system, and not simply identify the rules necessary to generate the database. Focusing on database generation rather than on the structure of the objects is a primary reason for the limitation of CASE tools such as Oracle Designer.  Rather than thinking about capturing rules of a specific type (i.e. structural), CASE tools focused on the purpose (i.e. generating a database), hence the tools never evolved to full system specification tools.

 

For the structural rules area of the repository, imagine a standard UML class diagram meta-model as shown in Figure 1. Of course the real model would be more complex, but for the purposes of this paper, showing the key classes is sufficient.

 

Figure 1: UML Class Diagram Meta-Model

 

NOTE: The only part of the model that may seem odd is having “association end” as its own class. This allows generalizations to have one head end class and multiple end classes.

 

2. Process Rules

Process rules define the process flow of an object from one state to another.  A particular object may have states associated with different aspects of the object.  For example, a person may simultaneously have a physical health as well as an economic health.  One could define independent process flows for each aspect.

 

Process rules should be described using some type of flowchart or process flow rather than a declarative mechanism.  There are simply too many rules associated with an object to be able to manage without dividing the object flow into states.  If process rules are specified using a declarative mechanism, you end up having thousands (or even tens of thousands) of rules.  These rules interact, may cancel each other out, and are impossible to manage. 

 

Traditional State Transition Engine (STE) diagrams or flowcharts are similarly hard to manage.  You might remember university classes in programming where you were forced to create flowcharts to document your programming assignments.  You always wrote the code first and the flowchart afterward. The flowchart for a simple 200-line program had 60-80 little boxes on it and the flowchart was harder to read than the program code itself.

 

The solution to making an STE work is to embed some logic in the state itself.  This allows the number of states to remain manageable. Perhaps the best way to do this is to view the state from the users’ perspective.  Think of a state as being a desk in an office.  It has an inbox and an outbox. When the object arrives at the inbox state, various actions can be taken. If the object sits in the state for too long, it can automatically be routed to another state.  Having the ability to create a state that is a much richer object drastically reduces the required number of states used to describe a process.  Rather than requiring hundreds of states to describe a process, even a very complex process can be described in a few dozen states.

 

The formal structure used is to add the idea of an “event” on a state (similar to the idea of a database trigger on a table).  When the event is triggered, actions can be executed.  Rules can be attached to an event to prevent it from occurring (for example security to prevent an object from being opened). 

 

States are associated with a class of object.  Process flows are not independent things.  The process flow defines the allowable states for a particular class of objects.  

 

The high-level process flow repository is shown in Figure 2.

 

 

 

 

Figure 2: Process Flow Repository

 

3. Data Validation Rules

Data validation is a complex topic in its own right.  A system can easily have hundreds of data validation rules. These rules may always need to be enforced or only contingently enforced based upon some condition or the state of the object. The rules may only require looking at the object being validated or accessing objects in other classes. Rule failure may only trigger a user warning or may prevent data modification entirely.  

 

The difficulty is in coming up with a grammar to help specify the rules.  Natural language is not precise enough and code is too hard to read.  The solution is to place the rules at the object level but support an Object Constraint Language (OCL)-like syntax that allows you to validate across classes.  For example, the rule to say that a department must have at least one employee (in the standard emp/dept 1-many model) would be written as:

 

:_child.emp.count >= 1

 

This grammar can be easily extended to support 99% of all rules encountered.

 

Validation rules are often only contingently required.  Therefore, these rules can be invoked at the object state level and may be contingently executed based upon some condition. The high level model to support the data validation repository is shown in Figure 3.

 

Figure 3: Data Validation Repository Model

 

E. User Interface (UI) Rule Types

Once the object rules are collected, there are some additional rules required to specify the UI.  For the purposes of this discussion, the UI rules will be kept to a minimum so that rules described at the object level will not be repeated at the UI level.

 

The model-view-controller (MVC) pattern architecture itself will not be discussed here nor will the decision to use it be defended.  However, the author’s perspective on MVC may be slightly different than the industry standard in that it is viewed as a logical design pattern, and not simply a way to write code. The goal here is to define the application independent of any technology or implementation considerations. 

 

1. Model Layer

The model portion of the logical UI rules is not difficult to specify.  There is already so much information at the object level that little extra information is required at the UI level.  Classes, attributes and associations have already been defined at the object level.  As a result, the only requirement at the UI level is to select a subset of objects (classes, attributes, associations) from the object level for use in the UI specification. This approach runs counter to the way in which most systems are built.  Most tools specializing in model development support very sophisticated object specification in the model portion of the UI.  This approach does not preclude a “thick” UI model level for implementation; it merely implies that the structure of the UI model should properly be defined at the object-level. 

 

Using this approach means that the structural rules at the object level will be quite sophisticated, requiring not only standard views, but also views that are dynamically altered or generated based on the values of some passed parameters. All that remains for the UI model specification is to point to existing structural object specifications.  The model to support this is shown in Figure 4.

 

 

Figure 4: UI Model Specification

 

2. View Layer

The rules in the view layer of the logical UI are themselves divided into structural (what are the elements and how are they grouped), logical (what happens when a screen opens, or a button is pressed), and presentation (how and where the elements are displayed).  

 

The view layer structural rules are very simple.  They define the UI elements (fields, buttons, etc.) and how they are grouped and bound to the UI model. 

 

On the other hand, the view layer logical rules are quite complex.  A full Event-Condition-Action (ECA) architecture is needed to define what happens when events (button press, open an application, etc.) occur. Conditions, actions, and events are defined as reusable objects.  The model to support this ECA structure is shown in Figure 5.

 

Figure 5: ECA Architecture model

 

Presentation rules are simply attributes of UI Element Group and UI Element.  The tricky part here is that different products use very different methods to describe layout presentation. The Oracle Developer tool used X and Y position, whereas HTML uses tags that automatically position themselves on the screen. Trying to come up with a technology-independent way to describe layout is not a problem that is currently well under control.

 

3. Controller Layer

The real strength of the MVC design pattern is the Controller layer.  This layer can be partitioned into rules controlling screen navigation and what happens during these screen transitions. The Struts controller should not be used to logically define rules.  This is too restrictive an implementation and would force a redesign if/when JavaServer Faces become the standard. The Controller layer is a natural place for using the same type of STE design employed for process flows at the object level.  However, rather than the states representing object states, they will represent UI element groups. 

 

By using a technology-independent way of describing page flow, you can generate the system to Struts, JSFs, or any other platform.

 

4. UI Shortcuts

There are standard UI structures that you should not have to build over and over again. You can define system elements such as browse screens that only require a few elements to be specified. All of the logical specification will then be generated automatically. 

 

For example, in the browse screen, the only elements that must be specified are:

·         Fields that you want to query by (and how they appear)

·         Fields in the display block (and how they will appear)

 

The rest is automatic. This approach allows you to build the user interface very quickly. You simply specify the browse screen for a particular class, point and click the desired query by attributes and display and out pops the application.

 

The model for specifying a browse screen is shown in Figure 6.

 

 

 

Figure 6: Browse screen model

 

As shown in Figure 6, the repository is very simple.  Since you know the object class on which to base the browse screen, all that is left is to specify a few attributes.

 

IV. Physical Specification of a System

Once a system has been logically specified, you need to specify the physical structure of the generated code. This can be done using a standard meta-model that conforms to the way in which the system will be built.  The appropriate model will vary depending upon the technology.  (That discussion is beyond the scope of this paper.)

 

Generated elements will be linked to their generating counterparts.  It is good design practice to limit how much overriding of generated elements will be allowed.  Allowing developers to override table and column names so that they are different from class and attribute names would make the object structural class diagram useless and should be avoided.

 

Most of the work of generating the final system which involves designing the repository and writing the generators is not difficult. The hard part is deciding on the appropriate UI standards and the best way to build the generated system.  There are many decisions to make. The following sections describe some of the most important ones to consider.

 

A. Use a Thick Database Approach

The author prefers to keep the applications as “thin” as possible and generate as much as possible in the database. This means generating separate views for each screen and placing code in the database whenever possible.    

 

No object rules will ever be implemented outside of the database. Structural rules generate to tables, views (perhaps with INSTEAD OF triggers) and packages.  Even “getters” and “setters” that OO developers might want to exploit are generated as packages.  Process logic is generated as PL/SQL packages and dynamic views.

 

All UI logic is controlled within the database. The application will notify the database that an event has occurred (e.g. a button is pressed) and the database will return a list of actions for the application to execute.

 

Even browse screens will have query criteria based on an INSTEAD OF trigger view.  Updating the view will automatically rebuild an object collection (using the PL/SQL in the INSTEAD OF trigger) that will be returned to the application containing what is to be displayed.

 

Using a thick database/thin application approach means that moving to a different UI architecture (e.g. JSP/Struts to JavaServer Faces) will not require rewriting a large portion of the generators.

 

B. Use ADF BC

Usage of Oracle’s Application Development Framework - Business Components (ADF BC) is a very important decision.  Generating to such a powerful and capable environment can greatly decrease the cost of development. Oddly enough, a related important decision was to not use most of the features built into this environment.  ADF BC includes many seductive and easy-to-use features that, if used inappropriately, can prevent the system from ever working. Because a thick database approach was used, the model layer in this case ended up not being very thick.

 

C. Limit the UI Design Options

Set your UI standard and stick to it.  Do not allow developers more than a small handful of options when specifying the UI.

 

D. Use JSP/Struts

For most applications, client/server architecture is no longer necessary.  JavaServer Faces are not ready for prime time.  Using the industry standard JSP/Struts provides the robustness and capabilities necessary to build a production system.

 

E. Avoid Post-Generation Modification

Make your architecture rich enough so that you do not need to modify the generated applications. 

 

V. Overview of the Generated Application

The generated application consists of the following parts:

·         one user login page

·         one main menu page

·         a browse page for each class in the application

·         an edit page for each class in the application

·         a State Transition Engine (STE) page for each class that has an associated process flow

 

On the Login page, the user name and passwords are defined in a separate table. The generated applications verify the login information using these definitions.

 

The main menu page contains links to the browse page of each class involved in the application.

 

An example of a browse page is shown in Figure 7.

 

 

Figure 7: Sample Browse Page

 

The Browse page consists of four sections:

 

1.       Search Criteria: This section includes search fields that are generated according to the domain of the column with which they are associated. For example, Value Lists are generated as combo boxes.

2.       Results: This section displays the query results. It also includes a navigation bar to quickly locate the desired rows.

3.       Associations: This section shows links to the master and detail classes. It is generated using the associations specified in the UML Data Model.  For example, in a standard Department/Employees application, there would be an Emp link in the Dept browse screen. Clicking this link brings you to the Emp browse screen and only shows the employees associated with the active Department in the Dept browse screen. 

4.       Menu: This section includes links to all classes in the application.

 

A sample Edit screen is shown in Figure 8.

 

 

Figure 8: Sample Edit Screen

Fields on the Edit screen appear or disappear depending upon the security settings of the user who is logged in. The fields can be editable, display-only or not show at all. The size of the fields is based on the domain settings of the associated column in the object model.

 

A. The Generator

Once the logical application is specified, the user calls a database procedure that starts the application generation process.

The generator is written in PL/SQL. It consists of about 18,000 lines of code. Its output is a JDeveloper workspace folder in the operating system. Once the generation is complete, the generated workspace is zipped and the user downloads the zip file onto his/her local machine. At this point the workspace is ready to use.

 

The generated workspace uses a small custom tag library (paging functionality used in the browse page) and a code library (for security).

 

B. The Generated Application

The generated application is a standard JSP/Struts application based on ADF BC.  A sample Struts Page Flow Diagram is shown in Figure 9.

 

 

Figure 9: Sample Struts Page Flow Diagram

 

The generated classes depend upon the function required.  As an example, a generated search class as used in the Browse page is described here.

 

Search Functionality

This is integrated with the Search section in the Browse screen. The search section consists of fields (Search Elements) that are used to enter search criteria, a radio button for search method (Case sensitive, Exact match, Default) and a Search button. When the button is pressed, the SearchDA data action executes. The SearchDA is coupled with the corresponding doSearch method in the application module using the “published function” method. This coupling is done in the Struts-config.xml file.

 

The SearchDA passes the values (Search Elements) entered in the Search screen to the application module. The application module calls the corresponding search method in the generated Java Class. The search method processes the search elements and builds a “Search Criteria” object.  A Search Criteria object consists of search elements which are used to build the WHERE clause and a View Object instance to apply the WHERE clause. The search Criteria object is then processed. During this processing, the WHERE clause string is constructed. Each Search Element is analyzed and, depending on the type of the column represented, the appropriate formatting is applied to the WHERE clause.

 

The WHERE clause is applied to the View Object and the view object query is executed returning the resulting rows of the search. At this point, the SearchDA data action completes and the Browse screen displays the results.

 

The following is an example of the generated code for the class called SrSurvey and the Struts Config. Xml tags:

 

    <action path="/searchDA" className="oracle.adf.controller.struts.actions.DataActionMapping" type="view.SrSurveySearchDAAction" name="DataForm">

      <set-property property="modelReference" value="srsurvey_browseDataUIModel"/>

      <set-property property="methodName" value="srsurvey_browseDataUIModel.searchSrSurvey"/>

      <set-property property="resultLocation" value="${requestScope.methodResult}"/>

      <set-property property="paramNames[0]" value="${param.fSrSurvey_SrSurveyOid}"/>

      <set-property property="paramNames[1]" value="${param.fSrSurvey_NameTx}"/>

      <set-property property="paramNames[2]" value="${param.fSrSurvey_Case}"/>

      <set-property property="numParams" value="3"/>

      <forward name="success" path="/browseData.do"/>

    </action>

 

Binding in the UI Model:

 

    <DCControl

      id="searchSrSurvey"

      SubType="DCMethodAction"

      Action="999"

      RequiresUpdateModel="false"

      DataControl="AppModuleDataControl"

      InstanceName="AppModuleDataControl.dataProvider"

      MethodName="searchSrSurvey"

      ReturnName="AppModuleDataControl.methodResults.AppModuleDataControl_dataProvider_searchSrSurvey_result" >

      <Contents>

        <NamedData

          NDName="Arg0"

          NDType="java.lang.String"

          NDOption="2"

          NDValue="%null%" >

        </NamedData>

        <NamedData

          NDName="Arg1"

          NDType="java.lang.String"

          NDOption="2"

          NDValue="%null%" >

        </NamedData>

        <NamedData

          NDName="Arg2"

          NDType="java.lang.String"

          NDOption="2"

          NDValue="%null%" >

        </NamedData>

      </Contents>

    </DCControl>

 

Exposed method in the application module:

 

  public void orderSrSurvey(String column, String type) {

    Object _ret = this.riInvokeExportedMethod(this, "orderSrSurvey", new String[] {"java.lang.String", "java.lang.String"}, new Object[] {column, type});

    return;

  }

 

Method in the application module:

 

  SrSurveyBrim SrSurveyb = new SrSurveyBrim();

  public void searchSrSurvey( String SrSurveyOid, String NameTx, String searchCase) {

    SrSurveyb.doSearch(getSrSurveyView1(), SrSurveyOid, NameTx, searchCase);

  }

 

The Generated Java Class for Class SrSurvey

package com.dulcian.brim.bag;

 

import oracle.jbo.ViewObject;

 

public class SrSurveyBrim extends ClassBrim  {

 

  public SrSurveyBrim() {

  }

 

  SearchCriteria sc = new SearchCriteria();

 

  public void doSearch(ViewObject vo,  String SrSurveyOid, String NameTx, String searchCase) {

    sc.setCase(searchCase);

    sc.setVO(vo);

    sc.addElement("SR_SURVEY_OID", SrSurveyOid, null, OIDCOLUMN, NUMBER, null);

    sc.addElement("NAME_TX", NameTx, null, REGULARATTRIBUTE, VARCHAR2, SIMPLE);

    super.doSearch(sc);

  }

}

 

Java Class ClassBrim.java. This class is the super class of all generated classes. It contains the search code that is shared by all of them.

 

package com.dulcian.brim.bag;

 

import java.util.Iterator;

import oracle.jbo.ViewObject;

 

public class ClassBrim {

 

  public ClassBrim() {

  }

 

  public final String NUMBER = "NUMBER";

  public final String DATE = "DATE";

  public final String VARCHAR2 = "VARCHAR2";

  public final String OIDCOLUMN = "OIDColumn";

  public final String MASTERDETAILRELATIONSHIP = "MasterDetailRelationship";

  public final String ASSOCIATIONCLASSEND = "AssociationClassEnd";

  public final String ASSOCIATIONCLASSHEAD = "AssociationClassHead";

  public final String STATE = "State";

  public final String BESTATUS = "BEStatus";

  public final String REGULARATTRIBUTE = "RegularAttribute";

  public final String DERIVEDATTRIBUTE = "DerivedAttribute";

  public final String STARTDATE = "StartDate";

  public final String ENDDATE = "EndDate";

  public final String EXPIRATIONDATE = "ExpirationDate";

  public final String CLASSINDICATOR = "ClassIndicator";

  public final String ACTIVEINDICATOR = "ActiveIndicator";

  public final String VALUELIST = "Value List";

  public final String SIMPLE = "Simple";

 

  public void doSearch(SearchCriteria searchCriteria) {

 

    String dateFormat = "MM/DD/YYYY";

    String searchCase = searchCriteria.getCase();

    ViewObject vo = searchCriteria.getVO();

    String query = "";

 

    Iterator iter = searchCriteria.getSearchCriteriaElements();

    for(; iter.hasNext();) {

      SearchCriteriaElement sce = (SearchCriteriaElement) iter.next();

      if(sce.columnType != null && (sce.columnType.equals(OIDCOLUMN) || sce.columnType.

            equals(MASTERDETAILRELATIONSHIP) || sce.columnType.equals(ASSOCIATIONCLASSEND)

            || sce.columnType.equals(ASSOCIATIONCLASSHEAD) || sce.columnType.equals(STATE)

            || sce.columnType.equals(CLASSINDICATOR))) {

        if (sce.value1 != null && sce.value1.equals("NO_VALUE"))

          query += sce.columnName + " IS NULL AND ";

        else if(sce.value1 != null && sce.value1.length() > 0)

          query += sce.columnName + " = " + sce.value1 + " AND ";

 

      } else if(sce.domainType != null && sce.domainType.equals(VALUELIST)) {

        if(sce.value1 != null && sce.value1.length() > 0 && !sce.value1.equals("-1"))

          query += sce.columnName + " = '" + sce.value1 + "' AND ";

 

      } else if(sce.dataType != null && sce.dataType.equals(VARCHAR2)) {

        if(sce.value1 != null && sce.value1.length() > 0) {

          if(searchCase.equals("0"))

            query += "UPPER(" + sce.columnName + ") LIKE UPPER('" + sce.value1 + "%') AND ";

          else if(searchCase.equals("1"))

            query += sce.columnName + " LIKE '" + sce.value1 + "%' AND ";

          else if(searchCase.equals("2"))

            query += sce.columnName + " = '" + sce.value1 + "' AND ";

        }

 

      } else if(sce.dataType != null && sce.dataType.equals(NUMBER)) {

        if(sce.value1 != null && sce.value1.length() > 0) {

          if(sce.value2 != null && sce.value2.length() > 0)

            query += sce.columnName + " BETWEEN " + sce.value1 + " AND " + sce.value2 + " AND ";

          else

            query += sce.columnName + " = " + sce.value1 + " AND ";

        } else {

          if(sce.value2 != null && sce.value2.length() > 0)

            query += sce.columnName + " = " + sce.value2 + " AND ";

        }

 

      } else if(sce.dataType != null && sce.dataType.equals(DATE)) {

        if(sce.value1 != null && sce.value1.length() > 0) {

          if(sce.value2 != null && sce.value2.length() > 0)

            query += sce.columnName + " BETWEEN to_date('" + sce.value1 + "', '" + dateFormat

                     + "') AND to_date('" + sce.value2 + "', '" + dateFormat + "') AND ";

          else

            query += sce.columnName + " = to_date('" + sce.value1 + "', '" + dateFormat + "') AND ";

        } else {

          if(sce.value2 != null && sce.value2.length() > 0)

            query += sce.columnName + " = to_date('" + sce.value2 + "', '" + dateFormat + "') AND ";

        }

      }

    }

 

System.out.println(query);

 

    if(!query.equals(""))

      vo.setWhereClause(query.substring(0, query.length()-5));

    else

      vo.setWhereClause(null);

    vo.setOrderByClause(null);

    vo.executeQuery();

    searchCriteria.getSearchCriteriaHashtable().clear();

  }

 

}

 

Conclusions

It is possible to create a complete architecture to describe and generate a full J2EE application.  The challenges are coming up with a repository/grammar to describe the system and deciding on the UI and architecture that you want to generate. Writing the repository managers and generators is a relatively simple task. The big surprise is really how well it all works.  Using this approach, applications are quickly specified, effortlessly generated, and easily maintained.  It can be a long road to get it all working, but there is a great pay-off at the end of the process.

 

About the Authors

Dr. Paul Dorsey is the founder and president of Dulcian, Inc. an Oracle consulting firm specializing in business rules and web based application development. He is the chief architect of Dulcian's Business Rules Information Manager (BRIM®) tool. Paul is the co-author of seven Oracle Press books on Designer, Database Design, Developer, and JDeveloper, which have been translated into nine languages and the Wiley Press PL/SQL for Dummies. Paul is an Oracle Fusion Regional Director. He is the President of the New York Oracle Users’ Group and a Contributing Editor of the International Oracle User Group’s SELECT Journal.  In 2003, Dr. Dorsey was honored by ODTUG as volunteer of the year, in 2001 by IOUG as volunteer of the year and by Oracle as one of the six initial honorary Oracle 9i Certified Masters.  Paul is also the founder and Chairperson of the ODTUG Business Rules Symposium, (now called Best Practices Symposium), currently in its sixth year, and the J2EE SIG.

 

Yalim Gerger is the founder and president of Gerger Consulting, representing Dulcian, Inc. in Turkey. He is the lead technical architect of BRIMÒ, Dulcian’s business rules product.  Yalim has seven years of experience in various IT areas including database modeling, server side development, Java development and code generation. He has presented at the Virginia Oracle User Group (VOUG) and co-authored an article entitled “Coding on the Fly: Using the Execute Immediate Procedure” that appeared in the April 2000 issue of the SELECT Journal (the official magazine of the IOUG).