Your First Phobos Application
Phobos is a lightweight web application framework that runs on the Java
TM
platform but allows you to develop your entire application using a
scripting language. You can therefore take advantage of the many
benefits offered by scripting languages but still leverage the power
of the Java platform. By being scripting-friendly, Phobos
provides a
programming environment that
fosters rapid development. This short
tutorial uses a simple calculator example to show you how quickly and
easily you can create web applications with Phobos.
Running the
Calculator Application
To run the calculator example, follow these steps:
- Make sure you have the NetBeans 5.5 IDE installed and running on your system.
- Install the Ajax update center and all the Phobos modules in the IDE following all
the instructions on the Phobos download page.
- Create a new project selecting "Scripting / Samples" as the category and
"Calculator" as the project:
Click "Next" and complete the project creation step.
Run the application by right-clicking on the newly created project and selecting the
"Run project" menu item. Your browser should open the front page of the application.
If it doesn't, point it to the following URL:http://localhost:8888/
Enter a number in the Current Total field and another number in
the Second Operand field, select an arithmetic operation, and click Compute
to get the result.
Structure of the Calculator Application
All Phobos applications adhere to a specific structure, which
encourages building web applications according to the
MVC
design
pattern.
Every Phobos
application includes an
application
directory, which in
turn includes at least a
controller, a
script,
and a
view
directory. The following diagram shows the structure of the
calculator example:
calculator
application
controller
calculator.js
script
index.js
view
calculator.ejs
Figure 1: Structure of the
Calculator
Application
As you can see from Figure 1, the application directory contains the
following directories:
- The
controller directory is where you put the
scripting
code that creates a controller object and defines the functions that
are called in response to user actions.
- The
script directory is where you can put any extra
scripting code you use in the application. In the case of the
calculator, the script directory contains a script file
that redirects
requests to particular pages of the application.
- The
view directory is where you put the files that
represent the
different pages of the application.
More complicated applications might need additional directories, such
as a
script directory, in which you can put static files,
such as HTML pages and CSS style sheets. The
Introduction to Phobos document describes the
directory structure in more detail.
As the preceding figure shows, the calculator application contains the
following
files:
calculator.js file, which instantiates a controller
object and
invokes the appropriate methods to perform the arithmetic operations
and redisplay the result.
index.js, which dispatches the first request for the
application to
the controller.
calculator.ejs, which is an embedded JavaScript
file,
an
HTML file that contains JavaScript embedded in it. This file
represents the page of the application.
You can take a look at these files by using the "Files" tab in NetBeans.
The next section describes the role of each of these files during the
processing of a request.
How the Application Works
Before delving into the different pieces of the calculator application,
let's
understand how it works. When the browser makes the first request
for the page, the following happens:
1. The
index.js file redirects the request to the
/calculator/show
URL. The
calculator part of the URL is a
controller, and
show is a function of it. The
show
function is defined in
calculator.js.
2. The Phobos runtime creates the
Calculator controller
and invokes the
show function.
3. The
show function sets the initial values of the
operands, sets the
selected arithmetic operation to
add, and renders the
calculator.ejs
view.
4. The user enters two numbers, selects an operand, and clicks Compute.
5. When the user clicks Compute, the
compute function of
calculator.js
is called by way of an HTTP POST to the
/calculator/compute
URL.
6. The
compute function does the following:
a. Gets the values of the operands and the
selected operation.
b. Performs the appropriate calculation, and
saves the result into session.
c. Redirects to the
show
function so that
calculator.ejs is re-rendered.
The remaining sections describe how to perform the following tasks to
create the calculator application. You can apply these tasks
while developing your own application.
Handling the Initial
Request
When the browser makes its first request for the calculator
application, it passes the URL,
/. This request is
dispatched
to the
index.js
script, located in the
script directory. This
script
redirects the
request to the URL,
/calculator/show by executing the
following
line:
library.httpserver.sendRedirect(library.httpserver.makeURL("/calculator/show"));
Code Example 1: Redirecting
the
Request
The
httpserver library is one of many JavaScript
libraries provided
by
Phobos. This particular library includes JavaScript functions
that perform basic HTTP tasks, such as request dispatching. In
this case,
sendRedirect performs the same task as
the
sendRedirect method of
HttpServletResponse,
namely it sends a temporary redirect
response to the client using the specified redirect location URL.
Creating the Controller
When the request for the URL,
/calculator/show comes in,
the runtime looks for a controller
called
calculator. It does so by executing the
calculator.js
script. This script defines a controller package called
calculator
using the
library.common.define function:
library.common.define(controller, "calculator", function() {
...
}
Code Example 2: Defining the
Controller
Phobos supports separate namespaces for controllers, libraries and
modules. Therefore, when you define a new package you have to say
where you
want to define it. In this case, you want to define the
controller
package.
As shown in the preceding code, the script passes the controller
package, the name of the package (
calculator), and the
function to
invoke to create the calculator controller.
Now that the runtime has located the controller package, the next step
is to instantiate a controller object. To do so, the
runtime looks for a
Calculator property with a function
value defined
inside the package:
this.Calculator = function()
Code Example 3: Instantiating the
Calculator Controller
The first letter of
Calculator is capitalized to indicate
that the
function is supposed to act as a constructor.
Don't get distracted by the use of
this in the actual
code; it's
just an artifact of the way packages are implemented.
At this point, the runtime has a controller instance, so
it's time for it to figure out what method to invoke. To do so, it
looks at the second element in the request URL, which is
show,
and it queries the controller object for a function-valued property of
the same name. Although Javascript doesn't have classes, you should
think of this function as a method.
The
controller/calculator.js code uses the following
pattern to define a method:
this.Calculator = function() {
this.show = function() { ... }
}
Code Example 4: Defining a Method
Alternatively, you can define a method as follows:
this.Calculator = function() {};
this.Calculator.prototype.show = function() { ... };
Code Example 5: Another Way to Define
a Method
It's up to you to choose which style to use.
Setting the
Initial Parameters and Rendering the View
Now that the runtime knows to invoke the
show function,
let's take a
look at what the
show function does. First, the
show
function (or action
method) sets the initial values of the result and of the selected
operation into the HTTP session:
var v = invocation.session.value;
if(v == undefined) {
v = 0;
}
var op = invocation.session.selectedOp;
if(op == undefined) {
op = "add";
}
model = { value: String(v), selectedOp: op };
Code Example 6: Setting Values into
Session
The Phobos framework creates the
invocation.session
object
automatically. As shown in the preceding code, the value of the
result is set to zero because this is the first time the page is being
requested. The operation is set to a default of
add.
Finally, a global variable, called
model, is initialized
with these
values. Later, you'll see how the page accesses these values and
passes them back to the controller.
The last thing the
show function does is it displays the
page, not by
writing HTML directly, but by rendering a separate ''view'',
represented by
calculator.ejs
It does this by using the
render function from the
view
library:
library.view.render("calculator.ejs");
Code Example 7: Rendering the
View
When the preceding code is executed, the
render function
will try to locate the resource called
calculator.ejs
on the path for view resources,
which includes
/application/view. As a result, the
render function resolves to
/application/view/calculator.ejs.
The
ejs extension stands for
embedded Javascript, to signify
that it is an HTML file with Javascript statements and expressions
embedded inside it. When the view is rendered, the embedded code is
evaluated at the appropriate time. The next section describes how
to create the
calculator.ejs
file.
Creating the View
You create a view using an embedded JavaScript file.
Creating an embedded JavaScript
file is done in a similar way to creating a JSP page because you use
the standard HTML form elements to design the interface. The
following
code shows part of the form used to submit the operands and the
selected operation on the calculator page. The table tags have
been removed for better readability.
<form action="/calculator/compute" method="post">
Current Total
<input type="text" size="20" name="value"
value="<%=model.value %>"/>
Second Operand
<input type="text" size="20" name="operand"/>
Operator
<input id="add" type="radio" name="operator" value="add">+</input>
<input id="subtract" type="radio" name="operator" value="subtract">-</input>
<input id="multiply" type="radio" name="operator" value="multiply">*</input>
<input id="divide" type="radio" name="operator" value="divide">/</input>
<input type="submit" value="Compute"/></td></tr>
</form>
Code Example 8: The Form Used to
Submit the User's Input on the Calculator Page
As the preceding code shows, the page has two text input fields. The
Current Total field accepts the first operand and later
displays the result of the operation when the page redisplays. The
Second Operand field accepts the second operand used in the
calculation. The Current Total field gets its value from the
model global variable described in the preceding section by using the
expression
<%=model.value%>.
Following the text field tags, the page includes a set of radio button
tags, which represent the different operations the user can choose to
perform. Finally, the page includes an input tag that represents
a button that causes the form to submit when the button is
clicked.
The
action attribute on the
form tag
specifies the controller function
that must be invoked when the form submits. This is the
calculator/compute
function. The next section describes how the
compute
function works.
But first, let's take a look at the embedded JavaScript in the
page:
<script type="text/javascript">
window.onload = function() {
var selectedOp = "<%=model.selectedOp %>";
document.getElementById(selectedOp).checked = true;
}
</script>
Code Example 9: Embedded JavaScript on the Calculator Page
Because this script is included in the page, it will be executed on the
client. The function in this script gets the value of the the
selectedOp
variable from the model global variable on the server. The
client-side script uses this value to mark the correct radio button as
selected in the HTML page's DOM.
Getting the User's
Parameters and Calculating the Result
As the preceding section explained, the data of the form on the
calculator page is submitted to the URL,
/calculator/compute using the HTTP POST method. The
compute
method in the
Calculator controller performs the
following tasks:
- Extracts the
value, operand, and operator
parameters from the
request.
- Performs the appropriate arithmetic operation based on the value
of the
operator variable.
- Updates the
value and operator values
in the session.
The first thing the
compute method does is it uses the
onMethod
function to restrict itself to only handle POST requests:
library.httpserver.onMethod({
...
any: function() {
library.httpserver.sendNotFound();
}
Code Example 10:
Restricting the onMethod to Handle Only POST Requests
According to the preceding lines of code, if a request is not a POST
request, the
sendNotFound function is invoked. This
function
sends back a 404 error.
The next task of the
onMethod function is to extract the
parameters from the request if the request is a POST:
POST: function() {
var value = Number(request.getParameter("value"));
var operand = Number(request.getParameter("operand"));
var operator = request.getParameter("operator")
...
}
Code Example 11: Extracting
Parameters
From the Request
After the function has retrieved the parameter values, it calculates
the result of the operation and saves it
into the value variable:
value = ({
add: function(x,y) { return x+y; },
subtract: function(x,y) { return x-y; },
multiply: function(x,y) { return x*y; },
divide: function(x,y) { return y == 0 ? 0 : x/y; },
}[operator])(value, operand);
Code Example 12: Calculating the
Result
Note that the preceding code creates object literals. The
rough equivalent in a code running on the Java platform would be
something like the following:
switch(operator) {
add: value = x + y;
...
}
Code Example 13: Equivalent of
Code
Example 13 in Java Code
After updating the
value variable with the result of the
computation,
the function needs to update the value in the session so the page can
access it. The next session describes how to do this.
Redisplaying
the New Values in the Page
After updating the value with the result of the operation, as shown in
the preceding section, the
compute function
updates the
value and
selectedOp variables
in the session:
invocation.session.value = value;
invocation.session.selectedOp = operator;
Code Example 14: Updating Values
in
Session
The final step is to redisplay the the new values in the page.
Recall from
Creating the View
that the calculator page already accesses the
value and
selectedOp
variables using
expressions, as shown here:
...
<script type="text/javascript">
window.onload = function() {
var selectedOp = "<%= model.selectedOp %>";
document.getElementById(selectedOp).checked = true;
}
</script>
...
<td>Current Total</td>
<td><input type="text" size="20" name="value" value="<%= model.value %>"/></td>
...
Code Example 15: Retrieving Values
from Session
So, in order to return the new values to the page, all we need to do is
to re-render the page.
MVC design patterns recommend against returning an HTML page as the
result of a POST request. Rather, the recommendation is to send
back a redirect (code 303) to a URL that will produce the desired page
when invoked with the GET request. With Phobos, you can do this by
using the
sendFound function from the
httpserver
library:
library.httpserver.sendFound("/calculator/show");
Code Example 16: Redirecting
to
the show Function to Re-Render the Page
This function is called
sendFound because FOUND is the
canonical message for code 303. What the method does is it
redirects to the
show function, which re-renders the
view.
Deploying the Application
Once your application is tested and ready for deployment, you can use the
"Export as Web Archive (WAR)" context menu item to create a war file.
The generated war file can be found under the
dist directory of the project.
This war file is fully portable and can be deployed on
GlassFish or any other J2EE application
server or servlet container.
What's Next
Now you understand how to create a simple Phobos application, and you
can apply what you've learned to create your own Phobos
applications. Another advantage of developing with Phobos is
that you are free to change or add on to the application while it
is running. As Phobos matures, you'll be able to do much more
with it, including sophisticated database access and integration with
AJAX technologies, such as
jMaki.
To keep up to date on the
progress of Phobos, visit
phobos.dev.java.net
and join the project
aliases.