Print 

ProJAX is an implementation of AJAX designed to get Progress developers, especially those working in legacy environments, up and running with a minimum of muss or fuss. ProJAX makes it simple to leverage your existing Progress 4GL programming skills to deliver rich and responsive web applications without annoying delays and timeouts for page refreshes.

Traditional web applications send whole pages to users with every request:

  • User changes some data on a FORM.
  • User clicks SUBMIT and the browser sends the FORM.
  • User is waiting for a response.
  • Server processes the data, builds a new page and sends the new page back to the user.
  • Browser clears the old page and renders the new page -- much of which probably looks like the old page. Various elements often shift around slightly in an annoying manner.
  • User can now continue working.

AJAX applications do not redraw the whole page with each transaction. Instead, they send a small request, receive a result, and update only the affected parts of the page:

  • User changes some data on a FORM.
  • Changes are transmitted via XMLHttpRequest. The user continues to work -- the request is not blocking.
  • Server processes the data and returns a response.
  • Browser receives response from server and updates affected elements of the page.

The AJAX style of application interaction brings the responsiveness and usability of a traditional desktop application to web applications. ProJAX brings those advantages to the Progress development community.

The problem we solve with Java most frequently is this: Put a web based interface onto a typical production database. If your development team controls the database schema, then Ruby on Rails [an AJAX framework] solves that problem much more quickly than Java does, and with better support for some features like AJAX, which provides more powerful Web-based user interfaces.

From the January 1 issue of "Software Development Times" . The opening question of an interview with Bruce Tate (page 16 or http://www.sdtimes.com/article/story-20060101-10.html ) -- renowned author of numerous Java books and articles. Tate's comments apply equally well to .NET.

ProJAX Advantages

Zero Install

With ProJAX here is nothing to install on the client desktop. All you need is a web browser. There is nothing to download. Nobody has to visit every desktop. No changes need to be made to your locked down system image. You already have everything you need to run ProJAX.

No ActiveX, No Flash

No ActiveX, no Flash, no worries about ActiveX exploits. No worries about ActiveX and Flash version compatibility. ProJAX frees you from having to manage and support the complexities of ActiveX and Flash versions on each user desktop. With ProJAX everything a user needs is always available.

Rich, browser-based user interface

Rich Internet Applications, Web 2.0. Leverage the power of millions of web developers and the mainstream development world by using ProJAX to bridge between environments!

No page refreshes

No page refresh delays to annoy your users -- server interactions happen in real time just as they do with a traditional desktop application. Finally you can get the repsoniveness of a terminal with the attractiveness of a web browser!

Control library

ProJAX includes built-in controls to handle Tabbed User Interfaces, Sortable Tables, Input Editing and many other common tasks. And because ProJAX is built on AJAX technology you can easily extend the client by adding your favorite web components!

Cross-browser support

Any DOM compliant browser will work with ProJAX! Even Internet Explorer. AJAX technology works with IE, FireFox, Safari, Camino, Opera and all modern browsers. AJAX will not work with NetScape 4 or IE 4 and earlier but the number of users with those browsers is vanishingly small.

Operating system independent

Since any DOM compliant browser will work with ProJAX (even Internet Explorer) ProJAX applications can be run by clients on any Operating System that supports a modern, standards compliant web browser.

Business logic that you already understand

Leverage your investment and expertise in Progress business logic! ProJAX provides a gateway to your existing applications that enables you to take advantage of the business logic that you already have and the skills that your staff has developed without extensive retraining or massive application retooling.

Works with Progress version 9 or higher

ProJAX will work with any version of Progress that supports XML. That is to say version 9 and above. And don't forget that a v9 client can connect to a v8 database! For the truly adventureous you could even back-port ProJAX to v6 without too much trouble!

Full source code

All of the source code for ProJAX is included! You're in complete control of your destiny.



The high-level ProJAX plumbing diagram is easy to understand. On the client a small JavaScript procedure (projax.js) encapsulates the routine that handles XMLHttpRequest. XMLHttpRequest is the core of any AJAX technology -- by using this call we can make requests of the server and receive responses without waiting for page refreshes.

Client side HTML makes requests of the JavaScript proJAX() function (contained in the js/projax.js library) which properly packages the request, dispatches it to the server and sets up a response handler.

On the server side we use a simple CGI script to pass the request to a Progress program (pjxsrvMT.p) which is listening for requests. This avoids the (very significant) overhead of starting and stopping _progres executables for each request.

The pjxsrvMT.p listener examines the request and determines what business handler (or "agent") it needs to be dispatched to. The business service handler is thus invoked, runs the relevant business process, wraps the data in XML and returns the response.

The result is then passed back to the browser where the response handler (that proJAX() previously established) will extract and act on the data!"



ProJAX Client Programming

A basic ProJAX client program requires a few simple CSS and JavaScript references in the header. These encapsulate the complexity of the XMLHttpRequest infrastructure freeing programmers to focus on providing business value to end users:

  <html>
  <head>

    <title>Simple ProJAX Test Page</title>

    <link rel="stylesheet" type="text/css" href="./css/pjx.css"></link>

    <script type="text/javascript" src="./js/projax.js"></script>

    <script type="text/javascript">
      pjxService = "simple";
    </script>

  </head>

Proper styling of many screen elements depends on pjx.css. Several alternative style-sheets are provided in the css directory if you'd like to try some different looks.

The JavaScript variable pjxService must be set to the name of the service being accessed. (This is so that the onload handler knows which service to login to.)

The projax.js JavaScript library provides the core XMLHttpRequest infrastructure as well as a number of useful utility functions. Highlights include:

proJAX() Make an XMLHttpRequest of a Server
pjxArg() Turn a named element into an argument string to be passed to the server
pjxDisplay() Refresh the screen with new data (from the server)
getXML() Get the first instance of a specific tag from an XML document
setFocus() Set the focus on a specific HTML element
addEvent() Add an event handler to a document element
removeEvent() Remove an event handler
pjx.debugMsg() Add a debugging message to the debug window
editInteger() Ensure that only valid digits are typed into an integer field
editDecimal() Ensure that only valid digits and "." are typed into a decimal field
editDate() Ensure that only valid digits and "/" are typed into a date field
initEdits() Automatically applies data type edits based on the CSS "class" of the input field
pjxLogin() Login to a service
pjxLogout() Logout from a service
pjxDrag.dragStart() Start dragging a draggable element
pjxDrag.dragCancel() Stop dragging a draggable element

pjxLogin() is used here in the BODY tag as a convenience. The login policy associated with a service determines if a valid login is required for a transaction. If the policy is set (on the server) to require a login then the server will reject transactions from sessions that have not logged in and return a "login required" message. The ProJAX library will at that point run pjxLogin() automatically. So even if a user creates their own modified page that lacks the onload handler they are not able to thwart the login process. On the other hand prompting for the login upfront avoids having the user's initial transaction rejected for lack of credentials and is, therefore, a helpful thing to do.

    <body onload="pjxLogin()">

This very simple application consists of a button, a counter and a "fortune cookie". You can be as fancy or as plain as you wish designing the layout -- it's just plain old HTML. Hooks to the application are coded as event handlers such as the onclick handler seen below.

    <div style="float:left;display:block;width:200;">
      <b><input type="button" id="simpleButton" value="Push Me!" onclick="updCounter()"></b>
      <br/>
      <div id="Counter" style="padding:20px;"></div>
    </div>

    <div style="float:left;display:block;width:200;">
      <p>
        This simple demo displays a counter that increments with every button press.
        When the button is pressed a call is made to the server and the current
        server-side counter value is returned.  As a bonus a "fortune cookie" is
        returned in the user message area.
      </p>
      <p>
        This demo is especially good for quickly testing the round-trip "plumbing"
        of the ProJAX infrastructure.
      </p>
    </div>

If DIV tags with ids of "pjxUserMessage" or "pjxPerfMessage" are present they will have a style applied to them as described in css/pjx.css. In this case space is set aside across the bottom of the window for a message to the user and in the lower right hand corner for performance metrics. When a server response is received the ProJAX library looks for a user message and, if found, places it into the message area. The library also extracts the server side performance data, calculates the client metrics and populates those. If the DIVs are not present the messages are skipped.

    <div id="pjxUserMessage"></div>
    <div id="pjxPerfMessage"></div>

  </body>
  </html>

Application specific JavaScript is included at the bottom of the .html file. You need to define event handlers for actions requested above. One particularly useful style of creating these is to use the same function for both requests and replies -- when called without an argument it makes a request. When the reply is returned an argument is passed (the "xmlDoc") and the contents of this argument are used to update the screen.

  <script type="text/javascript">

  function updCounter( xmlDoc ) {

    if ( xmlDoc == null ) {
      debugMsg( 1, "Push!" );
      proJAX( "simple", "pjxMethod=updCounter" );
    } else {
      pjxDisplay( "Counter", getXML( xmlDoc, "Counter" ));
    }

  }

  </script>

In this example when the button is pushed it's onclick handler fires and the updCounter() function is called. Since no argument was passed xmlDoc is null and the first branch of the if is taken.

debugMsg() is a ProJAX utility function that logs a message to the debug window. The debug window can be toggled on/off by using the F12 key. The first argument is the "level" of the message with 0 being reserved for hard errors and 9 being minutiae. The logging level can be modified interactively on the debug window screen or the default can be changed in the header by setting pjxDebugLevel to a value between 1 and 9.

After writing a debug message the proJAX() function is called with two arguments -- the service name and the arguments to that service. The argument list is the usual HTTP convention of name=value separated by &. proJAX() is the core ProJAX function. This function takes a request, formats it for the server, manages the XMLHttpRequest process and handles the dispatching of the response. In this case we're only asking that the server run a parameterless method.

When a response is received the updCounter() function will be called (see the Server Code page for details) and the XML response that was provided by the server will be passed as the argument. This will result in the second branch of the if statement executing.

pjxDisplay() is a ProJAX utility function that extracts the specified element from the returned xmlDoc and then displays it in the HTML document. The first argument is the "id" of an HTML element to update with the extracted value. The second argument is the value itself. In many cases the id and the xmlDoc tag name will be the same but they are not required to be.



ProJAX Server Programming

ProJAX server programming is straight-forward Progress 4GL. Each "application" or "service" that you create needs to have a "handler" or "dispatch" procedure that interprets ProJAX requests, arranges for the proper 4GL code to be run with whatever arguments are needed and which then packages the reply and returns it.

It's much easier than it sounds ;-)

  /* simple.p
   *
   */

  {projax/pjxlib.i}

  define input parameter pjx_method as character no-undo.
  define input parameter pjx_args   as character no-undo.
  define input parameter pjx_sid    as integer   no-undo.
  define input parameter response   as handle    no-undo.
  define input parameter rootnode   as handle    no-undo.

  pjx_Log( 4, "Handler", "Method: " + pjx_method + " Arguments: " + pjx_args ).

Every handler has the same prologue. The pjxlib.i include file declares function prototypes for standard support routines:

XMLCreate() Simplified XML document creation
XMLAddElement() Simplified XML element creation
pjx_DateTime() DateTime isn't available until OE10 so we roll our own to support v9
pjx_Log() General logging function
pjx_Error() Build an error message
pjx_GetParam() Find a named parameter and extract it's argument
pjx_PutParam() Find a named parameter and update or insert it
pjx_GetState() Get a "state" variable associated with this session
pjx_PutState() Save a "state" variable for this session
pjx_Fortune() Get a "fortune cookie"

Handlers are always called with 5 arguments:

pjx_method This is the method which the client is asking the server to execute.
pjx_args Any arguments which the client provided.
pjx_sid The session id assigned by the ProJAX infrastructure. This might be useful for persisting data between calls.
response A handle to the XML response document.
rootnode A handle to the root node of the XML response document.

The log message is not mandatory. But it goes a long ways towards tracing problems if errors occur. I strongly suggest that you follow the convention of including the log message.

The heart of the dispatch routine is some method of deciding what request is being made and then running an appropriate service routine. In this case we have a very simple service with just one method:

  case ( pjx_method ):
    when "updCounter" then run updCounter( pjx_args, response, rootnode ).
  end.

One simple way to dispatch requests is via a CASE statement. You can do this in any way that you like but CASE statements are simple, efficient and easy to debug. On the other hand a large enough service might benefit from a more data driven approach to avoid making coding changes to the handler. It's up to you. This is just a simple example.

After the dispatch logic executes the procedure finishes up and returns. It is the responibility of the called service routine to execute the desired method and populate the response XML with a reply (if there is one).

  pjx_Log( 5, "Handler returning", "" ).

  return.

Again, the log message is not mandatory. It does, however, go a long ways towards tracing problems if errors occur. With both the entry and exit log messages in place you will be able to confirm that the dispatcher ran without error. I strongly suggest that you follow the convention of including the log message.

The actual business logic can be anywhere. It could be an external procedure, a persistent procedure, a super procedure an app server call, a Sonic queue or topic or some other arrangement. It is only necessary that the disptach code above be able to somehow call it -- via RUN, a user defined FUNCTION call, direct inline code, PUBLISH and SUBSCRIBE or whatever.

In this case it is coded as an internal procedure of the dispatcher:

  procedure updCounter:

    define input parameter arglist  as character no-undo.
    define input parameter response as handle    no-undo.
    define input parameter rootnode as handle    no-undo.

    XMLAddElement( response, rootnode, "pjxMethod", "updCounter" ).
    XMLAddElement( response, rootnode, "Counter", string( next-value( pjx_counter ))).
    XMLAddElement( response, rootnode, "pjxUserMsgTxt", pjx_fortune()).

    return.

  end.

Remember -- this is just a silly demo! In this demo we tell the client to use its "updCounter" method to process the response. The response data itself is simply the next value of a counter and a "fortune cookie".



Why should I use ProJAX instead of XYZ?
Where can I find examples of AJAX?
What skills do I need to program the web pages?
Do my users need to enable JavaScript?
Do I have to use XML?
Is this "asynchronous" stuff important?
What about performance?
What platforms is ProJAX tested on?
What Progress components do I need to have?
     Where is the IDE?
Is your server overloaded?
Where did you get those icons?
ProJAX sounds a lot like Prozac -- why is that?

Why should I use ProJAX instead of XYZ?

Unlike most other AJAX tools ProJAX is specifically designed to work with Progress. Other AJAX toolsets want you to be running J2EE, .NET or PHP back ends. ProJAX doesn't. If you want to run plain old Progress rather than jump through hoops of complexity then ProJAX is the solution for you.

Where can I find examples of AJAX?

Google Maps, Google Suggest, Gmail... Or just Google for "AJAX". AJAX is fast becoming the preferred way to quickly develop and deploy applications that were previously only available through desktops.

What skills do I need to program the web pages?

You need to have a foundation of basic HTML and some familiarity with CSS. You will also be writing some light JavaScript. Take a look ("view source") at the demos for some insight into the client side programming -- it really is easy!

Do my users need to enable JavaScript?

Yes they do. AJAX (and thus ProJAX) need JavaScript in order to function. Some AJAX implementations attempt to "gracefully degrade" if JavaScript is unavailable but ProJAX makes no bones about it. ProJAX is primarily targetted at internal use corporate applications where you have a degree of control over such settings. You need to have JavaScript running or ProJAX will not work.

Do I have to use XML?

Yes and no. Only responses are XML. Requests are normal encoded URL's sent to the web server as either GET or POST submissions. As it stands the server side code returns XML with every request and the client expects that. But it isn't really a requirement (in spite of the name of "XMLHttpRequest") and I'm considering making it an option.

Is this "asynchronous" stuff important?

Yes! But it is not mandatory. XMLHttpRequest can function in either synchronous or asynchronous modes. ProJAX treats all requests as asynchronous by default but an optional 3rd argument to the proJAX() function permits you to override that behavior if you prefer.

What about performance?

Performance is excellent. But don't take my word for it. Enable the debug logging window (press F12) and take a look at the response times of the demos. Both client and server response time data is provided.

If F12 is to much data just look in the lower right hand corner of the demo windows. You'll see 4 numbers like 22:1:156:8m. These are the server execution time, the data transfer time and the total round trip time in milliseconds followed by the data transfer rate in bits per second (k = kilobits, m = megabits, g = gigabits).

What platforms is ProJAX tested on?

Development work is done using FireFox 2 on MS-Windows and Progress version 10.1B on SUSE Linux. Additional testing is periodically performed against Internet Explorer 6 & 7, Safari and Opera.

What Progress components do I need to have?

ProJAX does not require any special Progress infrastructure. The minimum technical requirement is that you have a version 9 or better Progress self-service client.

You do need to have appropriate licensing for your user count from Progress. Using an alternative deployment technology such as ProJAX does not relieve you of your licensing responsibility. The ProJAX Watchdog conveniently keeps track of concurrent users which might be a useful tool for convincing certain parties that you have fewer than 6 billion users.

Where is the IDE?

We don't need no steenkin' IDE... and neither do you! I use (micro)Emacs. WordPad isn't half bad either.

Is your server overloaded?

Thanks for asking! Probably not. The server is nothing special -- it's a 2x2Ghz Hyperthreaded Xeon with 512MB of RAM but it hardly breaks a sweat running this stuff. It's more likely that my ADSL connection is overloaded. I've only got 128k upstream (from me to the internet) and 768k downstream so it's possible that you're bottlenecked there. You might occasionally see bursts over 128k in the performance metrics due to small transfers and rounding but if you're consistently seeing less than 128k it probably means that the site is pretty busy.

Where did you get those icons?

I got them here. They're a bit small on some screens but the price was hard to argue with!

ProJAX sounds a lot like Prozac -- why is that?

They're both effective anti-depressants. ProJAX will help you stay ahead of the eight-ball (or get out from behind it) by enabling you to deliver high quality, responsive and attractive business applications to your users.


Feedback 

I need your help to improve this toolkit - please provide feedback!


Available Demos

Displays a counter that increments with every button press. Good for exercising the plumbing.

A loan calculator in a tabbed interface.

Customer lookup, inquiry and maintenance. Includes an asynchronous check for company name uniqueness during lookup or insert operations.

ProJAX Server Monitor. Demonstrates asynchronous request/response behavior.

 
Demo Notes
  • Demo programs run as "chromeless" sub-windows. (The windows have no browser buttons or address bar -- they are very much like a desktop application.)
  • Demos currently run on my development server:
    • The server is nothing special -- 2x2Ghz Xeon, 512MB RAM, Suse9 Linux
    • You're coming in on a 128kb DSL -- so don't expect lightning fast response times.
    • I develop with FireFox first and test IE later -- so if IE isn't working try FF before complaining.
    • Opera mostly works but there are some rendering "issues" here and there.
  • F12 will toggle open/closed a debugging log window:
    • The window is draggable.
    • You can set the logging level -- 1 is just critical errors, 9 is everything. 5 shows XMLHttpRequest traffic.

 

Terms and Conditions of Use

All materials on this site are copyrighted by Tom Bascom, all rights reserved. No content or programming code may be sold, published or distributed without express permission from the author except as described below.

Use of Source Code

All programming code on the site may be used, redistributed and/or modified under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License or (at your option) any later version.

No support, guarantee or warranty is offered or implied. By using any code found on this site you assume full risk and responsibility for that use.

Use of Other Materials

Articles or other informational material found on this site may not be posted or published elsewhere, in whole or in part, without express permission. You may, however, make copies for your own personal reference.

Alternative licensing is available.

OEMs, ISVs, VARs and others who wish to distribute ProJAX with their commercial products, and who are not licensing and distributing their source code under the GPL, or who do not wish to be bound by the GPL need to purchase a commercial license of ProJAX.

Licensing Terms can be tailored to meet the unique requirements of OEMs, ISVs, VARs and other parties interested in embedding, bundling, reselling or otherwise using ProJAX outside of the GPL.


Download

The tarball can be downloaded from the Greenfield Technologies download section. (This arrangement saves bandwidth on the demo server.)

Installation

0) You have presumably already read and agreed to the terms and conditions of use, downloaded the tarball and extracted it somewhere. These instructions assume that you have extracted ProJAX to /home/pjxdev.

1) Find a home for the cgi scripts. You may need to mess with Apache to get this going. I use SUSE 9 and Apache2. The config files for this setup are in /etc/apache2. The default server is default-server.conf and the virtual hosts are in vhosts.d/vhost.conf. If you're going to support multiple environments you'll probably be using vhosts. In either event you will want to specify a "ScriptAlias" to point to the cgi directory like so:

	ScriptAlias /cgi-bin/ "/home/pjxdev/www/cgi-bin/"

along with a "Directory" entry similar to this:

	<Directory "/home/pjxdev/www/cgi-bin">
	  AllowOverride None
	  Options +ExecCGI -Includes
	</Directory>

2) Similarly make a home for the HTML, JavaScript and CSS. In this case you may only need a "Directory" entry for the document root:

	<Directory "/home/pjxdev/www">
	  Options +Includes
	  AddType text/javascript .js
	  AddOutputFilter INCLUDES .js
	  XBitHack on
	</Directory>

or you might elect to put the pjx directory inside an existing document root. In which case you should ensure that server side includes are turned on:

	<Directory "/srv/www/htdocs">
	  Options +Includes
	  AddType text/javascript .js
	  AddOutputFilter INCLUDES .js
	  XBitHack on
	</Directory>

If you use an existing document root you can turn on symbolic links to point to the pjx directory by using "Options FollowSymLinks" or you can copy the whole tree with a command like this:

	cd /home/pjxdev/www
	tar cf - pjx | ( cd /srv/www/htdocs ; tar xvf - )

3) Restart Apache:

	su
	/etc/rc.d/apache2 reload

Verify that your web pages are properly setup and functioning by navigating the static pages. Don't run the demos yet though ;-)

4) Modify bin/pjxenv to reflect your desired environments. Two environments are pre-configured -- /home/pjxdev and /home/pjxdemo:

	cd /home/pjxdev/bin
	vi pjxenv
	pjxenv dev

If you have installed ProJAX in the default location and put the CGI scripts in the suggested directory then the only change you would need to make is to specify the location of a sports2000 database.

You might also find it useful to copy the scripts to a generally available location. I like to keep them in /usr/local/bin:

	su
	cp * /usr/local/bin

5) Build the projax database:

a) Set your working directory to the db directory.

	cd /home/pjxdev/db

b) Run the build script

	./build-db

c) Check for errors

	cat projax.err

6) Start the database:

	pjxdb start

7) Start your sports2000 database if you'd like to run the "customer" demo.

8) Start the ProJAX services:

	pjxwdog start
	pjxsvc start simple
	pjxsvc start loancalc
	pjxsvc start customer
	pjxsvc start pjxmon 3

9) Make sure that the services are running properly:

	pjxwdog status
	pjxsvc status simple
	...

	pjxwdog log
	pjxsvc log simple
	...

Use control-c (or whatever you have INTERRUPT set to) to break out of the log display.

10) Verify that /home/pjxdev/tmp is world read/write

	chmod 777 /home/pjxdev/tmp

If it is not then the cgi scripts will be unable to create the response FIFOs and applications will hang waiting for responses that can not be received.

11) To setup environments other than "dev" you will need to run the pjxcgi routine for each service. This will tailor the cgi scripts to their environment:

	. pjxenv demo
	pjxcgi simple
	pjxcgi loancalc
	pjxcgi customer
	pjxcgi pjxmon

12) You might also want to edit www/pjx/app/pjxmon.html and modify the header to reflect the environment that it is running in.

13) The demos should all run now.