How does it work
The way every web applicaion works is the same. There is a client
making a request to the server and the server will send a response.
Java supports this mechanism with servlets. Right from the beginning
there were attempts to ease the development with servlets. There was
also a pattern or better to say architecture introduced called MVC.
This model-view-controller architecture decouples the part of the
application into different pieces. There is more than enough written
about MVC and we will skip this part here.
The first attempts were to map actions to requests. One servlet will
act as controller (or call it dispatcher) that will find the corresponding
action and execute it. Usually the HttpServletRequest and response are
wrapped in an object which also supports more informations. Actually
this is nothing new and not really different than the "pure" servlet
approach. It just different names and different objects. There is no
real advantage.
How does jZonic handle this?
This framework takes the same approach first of all. Every reuest
is mapped to a so called "page". The page contains a set of process
items that define the workflow for this request. Every page can contain
a set of process items and of course items for conditional processing.
How is a request handled
Every request is handled by the ControllerServlet first. This servlet
prepares the WebContext which wraps the HttpServletRequest and response
togther with more attributes. Then the servlet hands over the WebContext
and incoming uri to the dispatcher. The dispatcher will try to find the
corresponding page in the PageRepository. If the page is not found it will
throw an exception and the servlet will call the ErrorHandler. When the
page is found the dispatcher will call the PageProcessor to procss the
workflow inside the page. The PageProcessor will process every item
in the same order as they are defined. These items can define and use
variables to communicate with each other. The variables are stored in
the WebContext which is send to every item when it is executed.
How are the pages defined
All pages and their workflow are defined insidde an XML file called
"pages.xml". First we look at the skeleton of such a page.xml file:
<?xml version="1.0"?>
<application>
<page name="index">
{process items will go here}
</page>
</application>
We have defined one page called "index". In order to call this page you have to put a ".jz" at the end in the request. You can change this ending inside the web.xml. This is explained later.
You can add as many pages to the pages.xml as you need.
The process itemsInside every page block you can define a set of process items. These items are processed in the same order. There are also items to handle conditions. Here is the list of items that jZonic supports:
- view: this is a class that will generate the view and returning a string with the content
- command executes a command
- include for including static content like text files
- jsp for processing JSP pages
- redirect redirects the server to another page
- forward forwards to another page
- if for conditional statements
- else same as above
- transform transform an XML file with an xsl stylesheet
- sendfile sending binary content to the client
Command itemThe command item is used to execute commands. This can be any java class that implements the org.jzonic.core.Command interface. You can define a set of commands and they will be processed in the same order.
Syntax:
<command>{full qualified class name}</command>
One short example:
import org.jzonic.core.*;
import org.jzonic.jwiki.repository.*;
public class SaveWikiPage implements Command {
public SaveWikiPage() {
}
public String execute(WebContext webContext) throws CommandException {
String pageName = webContext.getRequestParameter("page");
String content = webContext.getRequestParameter("content");
WikiFileRepository wfr = new WikiFileRepository();
wfr.addVersion(pageName,content);
return null;
}
}
View itemA view items is used to generate the content of a page or only a part of it. This can be any java class that implements the org.jzonic.core.View interface. The class must return a string with the content which will be put in the internal buffer. You can define a set of views in order to aggregate the content. There is no limitation how you will generate the content. It can be pure HTML, velocity, freemarker or whatever you desire as long as you return the content as string.
Syntax:
<view>{full qualified class name}</view>
One very simple example:
<view>org.foo.app.MyView</view>
This is the java source code:
package org.foo.app;
import org.jzonic.core.*;
public class MyView implements View {
public MyView() {
}
public String generate(WebContext webContext) throws ViewException {
return "Hello world";
}
}
Include itemYou can use this item to include static content from a file. The content is cached but reloaded when the file has changed. The base directory is set to that main directory of your web application.
Syntax:
<include>{the file name}</include>
One simple example:
<page name="welcome">
<inlcude>welcome.html</inlcude>
</page>
Using a static file as header and one as footer and using a view in the middle:
<page name="welcome">
<include>header.html</include>
<view>org.foo.app.MyView</view>
<include>footer.html</include>
</page>
In this case first of all the static content of the file header.html will be put into the internal buffer. Then the view will be processed and the resulting string is added to the buffer. Now the content of the footer.html file will be added and the entire content of the internal buffer is send back to the client.
JSP itemThe jsp command will forward to the desired jsp page.
Syntax:
<jsp>main_page.jsp</jsp>
You can for example combine to the command and jsp item. Use the command to prepare some data that can be used by the JSP. This is called the ServiceToWorker pattern (actually there are more names for this).
<page name="listitems">
<command>org.foo.app.GetAllItems</command>
<jsp>items.jsp</jsp>
</page>
TransformUsing xml and xsl to run a website can ease the maintenance of the documentation a lot. Rather then writing tons of HTML it is much simpler working on simple xml files. The base directory is the same as the webapps directory of your application
Syntax:
<transform xml="{xml file name} xsl="{xsl file name}"/>
One example combining inclues and a transform item
<page name="catalog">
<include>header.html</header>
<command>org.foo.app.GenerateCatalogXML</command>
<transform xml="catalog.xml" xsl="catalog.xsl"/>
<include>footer.html</include>
</page>
The resulting HTML is cached. When the next request comes in then the transform item will check if the files have changed and then reload them. In order to speed up the transformation the xsl is also cached. So if only the xml has changed then the transformation will use the precompiled xsl.
If and else itemSince all items inside one page definition are processed in the given order it can be necessary to use conditions to fork the workflow. The if expression is based on the evaluation of a variable that can be set by any of the above items.
Syntax:
<if expression="${varname}='value'">....</if>
One example:
<if expression="${usrtype}='admin'">
<view>com.foo.app.ShowAdminPage</view>
</if>
Everything in the if block is executed when the variable matches the desired value. These if block can be nested. Since there is an if item there has to be an else item as well. Syntax:
<else>...</else>
Putting it together:
<if expression="${login}='true'">
<view>com.foo.app.ShowWelcomePage</view>
</if>
<else>
<view>com.foo.app.ShowErrorPage</view>
</else>
Variables and how items can communicateIn order to communicate jZonic uses variables. These variables can be set by views or commands and can be used by all items. Setting a variable can be done like this:
import org.jzonic.core.AbstractCommand;
import org.jzonic.core.CommandException;
import org.jzonic.core.WebContext;
public class ExampleCommand extends AbstractCommand {
public ExampleCommand() {
}
public String execute(WebContext context) throws CommandException {
String val = context.getRequestParameter("value");
context.setVariable("varname", val);
return null;
}
}
This will add the variable called "varname" with the value from the request parameter "value".
Using this variable inside an if/else block is here:
<page name="redirect">
<command>ExampleCommand</command>
<if expression="${varname}='hello'">
<view>WelcomeView</view>
</if>
<else>
<redirect>index.jz</redirect>
</else>
</page>
You can also use this to "skin" your applications by using a different jsp depending on such a variable:
import org.jzonic.core.AbstractCommand;
import org.jzonic.core.CommandException;
import org.jzonic.core.WebContext;
public class SkinExampleCommand extends AbstractCommand {
public SkinExampleCommand() {
}
public String execute(WebContext context) throws CommandException {
// just get the info from an imaginary configuration
String val = config.getProperty("skin");
context.setVariable("theme", val);
return null;
}
}
and now put it in a page:
<page name="main">
<command>SkinExampleCommand</command>
<jsp>${theme}/hello.jsp</jsp>
</page>
When you use a variable inside a process item you have to put it in ${varname}. This example will take the JSP from the corresponding subdirectory of your webapps directory.
Using a template for a pageEven when you combine several static files and some dynamic content in one page definition it can be hard to get a nice template. Such a template is the static content around dynamic content. jZonic supports templates.
Let us start with a simple template layout (we call it base.templ):
My web site
<table border="0" width="100%">
$CONTENT(inner_part)
This is plain HTML togther with one placeholder. These placeholders are defined with $CONTENT({name of the reference}). You can put as many as you need inside such a template.
In order to use the template you have to define the page like this:
<page name="welcome" layout="org.jzonic.core.layout.TemplateLayout">
<parameter name="file" value="base.templ">
<inner_part>
<view>org.foo.app.ContentView</view>
</inner_part>
</page>
When the page is processed the placeholder will be replaced with the content of the blocked that is defined with the same name inside the page definition. Such a block can contain a complete workflow as described before.
Here is an example with two placeholders:
My web site
<table border="0" width="100%">
<td width="25%">
$CONTENT(MENU)
$CONTENT(inner_part)
The page definition is here (with a bit more conditional checkings to make a bit more interesting):
<page name="welcome" layout="org.jzonic.core.layout.TemplateLayout">
<parameter name="file" value="base.templ">
<command>org.foo.CheckUser</command>
<if expression="${role}='admin'">
<view>org.foo.AdminMenu</view>
</if>
<view>org.foo.Menu</view>
<inner_part>
<view>org.foo.app.ContentView</view>
</inner_part>
</page>
|