Reference
This reference contains detailed information about the Orkwan framework and should be sufficient for most web application development. It does not however cover all aspects of Orkwan nor does it explain all details of the API:s. To get more in depth understanding of how Orkwan works the reader is referred to the javadocs (which can be generated using the build script in the complete distribution of Orkwan) and to the source code itself (which is also included in the complete distribution).
- Request Processing
- MyServlet (inherits from OrkwanServlet)
- MyController (inherits from OrkwanController)
- orkwan.xml
- Controller invocation by URL parameter (?action=helloworld)
- Controller invocation by URI (/demo/helloworld)
- Controller invocation by file name (helloworld.html)
- Controller properties
- Controller output (view)
- Accessing the controller in a JSP
- Controller data
- Output encoding
- Controller lookup
- Execution and flow control
- Inheritance hierarchy
Request Processing
The following figure describes the http request processing in a web application based on the Orkwan framework. The required application components, here named MyServlet, MyController, and the orkwan configuration file orkwan.xml, are explained in the following chapters.
- An incoming http request is dispatched to the class MyServlet. The class MyServlet inherits from the abstract base class OrkwanServlet in the Orkwan framework. MyServlet is defined in the standard java web.xml file as the receiver of the incoming http request.
- Based on the URL of the http request MyServlet will create a new instance of a controller and execute the controller. The mapping between the URL and the controller class is defined in the orkwan configuration file orkwan.xml. The MyController class inherits from the abstract base class OrkwanController.
- The controller is responsible for executing the request. It validates the request, operates on the business model, and creates a view.
- After execution of the controller the framework calls a view target, either a JSP (Java Server Page) or a servlet, to create a view. In both cases a relative URL to the web application root is specified to identify the view target. If the url matches the servlet mapping för MyServlet a cascaded execution of controllers will occur. The definition of the view target for a controller is made in the file orkwan.xml.
- The controller can also directly generate the view output as html source or an xml document. The controller can even write directly on the fly to the http response if the view is defined as custom in orkwan.xml.
- At the end of the request processing a http response is sent back to the user.
MyServlet (inherits from OrkwanServlet)
MyServlet is the main control servlet in the web application. It inherits from the abstract base class OrkwanServlet in the Orkwan framework. MyServlet must implement all methods which are declared abstract in the base class OrkwanServlet. These methods are designed to be key placeholders which allow a web application to customize the default behavior in the Orkwan framework. The required methods in MyServlet are:
- handleError(): this method is invoked by the framework when an error (throwable) occurs during processing a request. The error (throwable) as well as the executing controller where the error occured is available for handling. A typical implementation of this method will call the log function in the application and redirect the user to an error page.
- handleNotFound(): this method is invoked by the framework when it can not find a controller matching the URL of the request (equivalent to a 404 response code in a web server). A typical implementation of this method will redirect the user to a page explaining that the resource can not be found.
- init(): this method is invoked by the framework to allow for different kinds of initialization when a new request is to be processed.
- validate(): this method is invoked by the framework to validate a new http request. Typical validation may involve verifying the http session, cookies, and other general request data.
MyController (inherits from OrkwanController)
MyController inherits from the abstract base class OrkwanController in the Orkwan framework. MyController must implement all methods which are declared abstract in the base class OrkwanController. These methods are designed to be key placeholders for defining the logic of a general web function. The required methods in MyController are:
- init(): this method is invoked by the framework to initialize the controller.
- validateInput(): this method is invoked by the framework to validate the request. A typical implementation may get and validate the input parameters of the request and save them as internal variables for later processing of the business model.
- updateModel(): this method is invoked by the framework to operate on the business model of the application. This method is where the business logic of the controller should be placed.
orkwan.xml
orkwan.xml is the root configuration file for the Orkwan framework. It defines the mapping between a request URL, the controller to execute, and the output view to create. It can also define a number of other properties for the orkwan framework which are described in more detail throughout this reference. An example of an orkwan.xml file is:
[orkwan.xml] <orkwan> <include file="/demo/search/search.orkwan.xml"/> <controllers> <controller> <class>demo.MyController</class> <url-parameter name="action" value="helloworld"/> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
The configuration above means that the controller demo.MyController will be executed for an input url containing the url parameter action=helloworld (e.g. http://www.orkwan.com/demo?action=helloworld ), and the output view is /helloworld.jsp.
The <include> tag specifies the location of another orkwan configuration file. The location is specified relative to the web application root. The content of the file is also included in the configuration of the Orkwan framework.
To completely remove all execution of Orkwan controllers in the application, define the following configuration in orkwan.xml:
<orkwan> <controllers> </controllers> </orkwan>
Simply removing the orkwan.xml file or deleting all content from the file will not work since the Orkwan framework will fail to initalize.
Controller invocation by URL parameter (?action=helloworld)
One way of invoking a controller is to invoke it by URL parameter. To invoke a controller by url parameter the tag <url-parameter> must be included in the controller definition as shown below:
[orkwan.xml] <orkwan> <controllers> <controller> <class>demo.MyController</class> <url-parameter name="action" value="helloworld"/> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
The configuration above means that the controller demo.MyController will be executed for an input url containing the url parameter action=helloworld (e.g. http://www.orkwan.com/demo?action=helloworld ), and the output view is /helloworld.jsp. The value of the view tag is a relative url to the web application root, hence the root slash, "/", must be included in the path to the JSP file.
Controller invocation by URI (/demo/helloworld)
A controller can be invoked by URI. URI is an abbreviation for Uniform Resource Identifier and is defined as "the part of the url from the protocol name up to the query string". An example of the relationsship between URI and URL is given below.
URL = http://www.orkwan.com/demo/helloworld?name=jamesbond URI = /demo/helloworld
To invoke a controller by URI the <uri> tag must be specified in the configuration of the controller as shown below:
[orkwan.xml] <orkwan> <controllers> <controller> <class>demo.MyController</class> <uri>/demo/helloworld</uri> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
The configuration above means that the controller demo.MyController will be executed for an input url containing the uri /demo/helloworld (e.g. http://www.orkwan.com/demo/helloworld ), and the output view is /helloworld.jsp.
Regular expressions
The URI can also be specified using a regular expression. In some web applications the first part of the URI serves as a country or market identifier, and the second part specifies the actual web funtion, like in the following example:
URI 1 = /se/helloworld URI 2 = /uk/helloworld URI 3 = /de/helloworld URI 4 = /fr/helloworld ...
In this case the controller is matched using the second part of the uri, not taking the first part containing the country identifier into account. To do this matching a regular expression must be used in orkwan.xml.
[orkwan.xml] <orkwan> <controllers> <controller> <class>demo.MyController</class> <uri>/[\w_]+/helloworld</uri> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
An in depth tutorial on how to use regular expressions in Java is found here.
Attribute uri-root
If several controllers are matching the same basic URI pattern they can be grouped together in a controllers tag and the attribute uri-root can be specified. When a uri-root is specified the uri-root is prepended the uri in the controller tag (if a uri is defined in the controller).
[orkwan.xml] <orkwan> <controllers uri-root="/[\w_]+/"> <controller> <class>demo.MyController</class> <uri>helloworld</uri> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
Cascaded controllers and relative addressing
Sometimes it is of interest to execute one controller after the other. In case the uri contains a country identifier as described above it may be necessary to specify the cascaded view (controller) using a relative url. For example, if MyController is executed, the LoginController must also be executed as shown below:
URL 1: http://www.orkwan.com/se/helloworld => execute MyController then continue to execute LoginController: URL 2: http://www.orkwan.com/se/login => execute LoginController
In this case an uri matching /se/helloworld will lead to a cascaded execution of /se/login. The problem is that the value of the country identifier in the uri, /se, is not known since MyController was matched using a regular expression. The solution is to use a relative addressing in the output view of MyController:
[orkwan.xml] <orkwan> <controllers uri-root="/[\w_]+/"> <controller> <class>demo.MyController</class> <uri>helloworld</uri> <view>./login</view> </controller> <controller> <class>demo.LoginController</class> <uri>login</uri> <view>/login.jsp</view> </controller> </controllers> </orkwan>
The ./ value tells the servlet container that the address is relative and that addressing shall begin at the current directory which is /se (the /helloworld at the end of the path is perceived by a servlet container as the name of a file, not a directory).
Controller invocation by file name (helloworld.html)
A controller can be invoked by file name, e.g. helloworld.html. To invoke a controller by file name use the URI invocation method. For example, to invoke the controller for the url http://www.orkwan.com/demo/helloworld.html use the following URI matching:
[orkwan.xml] <orkwan> <controllers> <controller> <class>demo.MyController</class> <uri>/demo/helloworld.html</uri> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
Controller properties
A controller is configured by a set or properties. A property can be set by either an attribute or a tag in the orkwan.xml configuration file. The core properties which are reserved for use by the Orkwan framework are:
Property (tag) | Required | Description |
---|---|---|
class | yes | Specifies which java controller class to invoke |
url-parameter | *yes | Specifies which url parameter to match controller to |
uri | *yes | Specifies which uri to match controller to |
view | yes | Specifies view target |
(* either url-parameter or uri must be specified).
In addition to the generic Orkwan properties described above user defined properties can be added to the xml configuration of a controller and retrieved during runtime using the method getControllerProperty(String property). There are two ways of adding a property to a controller:
- XML attribute in the controller tag, or
- XML sub tag to the controller tag
In the first example it is shown how to add property, recaptcha, with value demo, as an XML attribute to a controller.
[orkwan.xml] <orkwan> <controllers> <controller recaptcha="demo" > <class>demo.MyController</class> <uri>/demo/helloworld</uri> <view>/helloworld.jsp</view> </controller> </controllers> </orkwan>
The next example shows how to add a property, recaptcha, with value demo, as an XML sub tag to a controller.
[orkwan.xml] <orkwan> <controllers> <controller> <class>demo.MyController</class> <uri>/demo/helloworld</uri> <view>/helloworld.jsp</view> <recaptcha after="20210327:13.00.00">demo</recaptcha> </controller> </controllers> </orkwan>
The xml sub tag method enables the use of grouping properties by declaring XML attributes on the sub tag. An attribute, after, has been added to the recaptcha tag. The property recaptcha, and the attribute after are available as controller properties in the MyController class. To get the values of the properties the following code can be used in MyController:
String g = getControllerProperty("recaptcha"); String a = getControllerPropertyAttribute("recaptcha", "after");
Controller output (view)
The output of a controller is defined by the content of the <view> tag. A controller can generate different kinds of output as described in the table below.
Output type | <view> tag | Description |
---|---|---|
PAGE | <view>/index.jsp</view> | Path to JSP page or servlet from web application root (/). |
DOCUMENT | <view type="xml"/> | XML document output. To create the XML document override method createDocument() in the controller, or use setDocument() in e.g. updateModel(). |
SOURCE | <view type="html"/> | HTML source output. To create the html source override method createSource in your controller, or use setSource() in e.g. updateModel(). |
CUSTOM | <view type="custom"/> | No output is generated by the framework. The output must be created by the controller itself (by accessing the Writer in the response). |
If several controllers define a view which is located in a specific directory of the web application a view root may be defined in the controllers tag. For example, if all jsp:s are located directly under the web application root the root slash may be defined as view-root. In this case, the individual view tags do not have to be prepended with the root slash.
[orkwan.xml] <orkwan> <controllers view-root="/"> <controller> <class>demo.MyController</class> <uri>/demo/helloworld.html</uri> <view>helloworld.jsp</view> </controller> </controllers> </orkwan>
Changing the view
There are several ways to change the configured view or output type during execution of a controller:
- To change the view use method setView(String view)
- To change the view and immediately display the view (abort execution in controller) use method
createView(String view) - To change the output type (from e.g. a view target to an xml output) use method
setOutput(ControllerOutput output)
Accessing the controller in a JSP
The controller carries data from the domain model to the creation of the view in the JSP. The controller responsible for executing a request is saved as an attribute in the request under the key "controller". To get the controller in a JSP call: pageContext.getAttribute("controller", PageContext.REQUEST_SCOPE)
The following code shows how to get the controller in a JSP:
[helloworld.jsp] <%@ page import="demo.MyController" %> <% MyController controller = (MyController) pageContext.getAttribute("controller", PageContext.REQUEST_SCOPE); %> <html> <head> <title>Hello World</title> </head> <body> <p>Hello World from <%=controller.getModelData("name")%>!</p> </body> </html>
Controller data
To carry data from the request to the business model to the view the controller contains an internal HashMap. In the hashmap any type of data can be stored. The data is stored using a key. The standard getters and setters are:
type | set method | get method |
---|---|---|
String | setModelData(String key, String value) | getModelData(String key) |
Integer | setModelInt(String key, int value) | getModelInt(String key) |
Boolean | setModelBoolean(String key, boolean value) | getModelBoolean(String key) |
Object | setModelObject(String key, Object value) | getModelObject(String key) |
In addition to the generic hashmap common in all controllers it is of course possible to define new member variables of any type to carry data from model to view. It is also possible to define "wrapper" getters/setters for custom objects which internally operate on the internal hashmap but externally look like getters/setters for custom member variables.
Note also that data stored in the generic hashmap is automatically copied to the next controller in case of cascaded controller execution.
Output encoding
The default character encoding in Orkwan is UTF-8. To set a different character encoding in the output response use the method setEncoding(String encoding). For example, to create a response using the character set
setEncoding("iso-8859-1");
The encoding can be set any time during the controller life cycle. After the controller has finished executing the framework will use the encoding value to set the content type on the response and to create an output stream using the specifed character set. The servlet specification guarantees that as long as the content type is set before the output is written the response will be encoded using the content type.
The encoding will apply to all kinds of output types and views. However, in the case of a JSP the following should be noted:
- text written directly in the JSP should be of the same encoding as the encoding specified in Orkwan. Otherwise the output result may be inconsistent.
- a page content type directive in the JSP page will override any encoding set in a controller
Controller lookup
Setting controller lookup properties may significantly improve controller lookup performance especially for large web applications. Controller lookup properties can only be set in the Orkwan root file configuration file orkwan.xml. The lookup element consists of three properties:
- <primary> The primary lookup. It can either be by uri or by url-parameter
- <secondary> The secondary lookup. The secondary lookup is run if the primary lookup did not result in a match (a controller could not be matched). It can either be by uri or by url-parameter
- <parameter> The first url parameter to check for if url-parameter lookup is used. If there is no match using this parameter, all other parameters in the http request will subsequently be checked.
The following table shows all possible combinations of the primary and secondary lookup properties and how they control the lookup of controllers in Orkwan:
lookup primary | lookup secondary | Description |
---|---|---|
uri | - | Only the uri part of the http request will be matched to controllers |
uri | The uri of the request will first be matched to controllers. If there is no match the url parameters in the request will be matched. | |
- | Only the url parameters of the http request will be matched to controllers | |
uri | The url parameters of the request will first be matched to controllers. If there is no match the uri in the request will be matched. |
An example of a lookup tag element is given below. The values shown are the default lookup assignments in Orkwan.
[orkwan.xml] <orkwan> <lookup> <primary>url-parameter</primary> <secondary>uri</secondary> <parameter>action</parameter> </lookup> </orkwan>
Execution and flow control
The execute() method in class OrkwanController lies at the heart of the Orkwan framework. This is the only method that is called by the framework after the controller has been created and it encapsulates the entire work flow in Orkwan. The content of the method is shown below:
[OrkwanController.java] /** * Execute controller */ public void execute() throws Exception { // boot controller boot(); // init controller init(); // validate input validateInput(); // update model updateModel(); // create output createOutput(); // exit handling exit(); // successful execution setSuccessful(); }
The chain of methods in the execute() method makes up the execution flow in a controller. Some of these methods must be implemented when creating a controller, others can be overridden to customize the execution flow in a controller.
Method | Abstract | Description |
---|---|---|
boot | no | The boot() method is implemented in OrkwanController and the framework implementation takes care of various things such as verifying the request, copying model data from previous controllers in cascaded execution and setting http response headers |
init | yes | init() must be implemented in a subclass to OrkwanController. Use the method to initialize a controller. |
validateInput | yes | validateInput() must be implemented in a subclass to OrkwanController. Use this method to validate input parameters and data in the http request. Do not put validation of database data in the model here. |
updateModel | yes | updateModel() must be implemented in a subclass to OrkwanController. Use this method to operate on and update the business model. This method usually contains the actual work done by the controller. |
createOutput | no | The createOutput() method creates the output based on the configuration of the controller. Note that this method does not actually write any data to the http response, it only creates and sets the output data |
exit | no | This method is a hook to allow for exit handling in a controller. The framework implementation is empty. |
setSuccessful | no | If the controller executes without an exception the setSuccessful method will set the successful execution flag of the controller to true. |
Methods declared as Abstract must be implemented when creating a controller. If they are not abstract and already implemented in the base class OrkwanController they can still be overridden to customize the execution flow in a controller. Note however that overriding a method defined in OrkwanController is potentially dangerous and any override should begin with invoking the super.method() implementation to preserve the integrity of the Orkwan framework.
Controlling the flow of execution
Sometimes it is of interest to change the flow of execution as defined in the execute() method. For example, if validation fails in the validateInput() method it may not of interest to continue the normal execution but instead to abort the execution and show the user an error page. For this purpose there are methods that change the flow of control available. They all throw different types of ControlFlowException to change the execution flow.
- redirect(String url): interupts the execution and redirects the user to any URL on the internet. The method throws a RedirectException to change control flow.
- createView(String view): interupts the execution and calls the view target (path from web application root to a JSP/servlet/resource). The method throws an IncludeException to change control flow.
- forwardView(String view): interupts the execution and calls the view target (path from web application root to a JSP/servlet/resource). It also changes the request url to that of the forward view target. The method throws an ForwardException to change control flow.
To change the control flow it is also possible to throw a ControlFlowException directly. There are three different kinds of control flow exceptions available: RedirectException, IncludeException, and ForwardException.
Inheritance hierarchy
A good way of leveraging the Orkwan framework is to use inheritance as a mechanism for grouping common behavior in an application. By defining a local base class controller and inheriting from this controller rather than directly from OrkwanController, a natural placeholder for common functionality is obtained in the application. If the application contains several distinctive areas of functionality, e.g. a public web site, and an internal administrative console, a local base class controller should be defined for each specific area of functionality. The following figure depicts an application inheritance hierarchy for a web site with a public interface (base class MyController) and an admin console (base class MyAdminController).
The local base classes, MyController and MyAdminController, are both declared abstract and only function as placeholders for common validation, business logic, logging etc. They are not real classes which can be used for executing a web function. The real web functions are performed by subclasses to the local abstract base controllers. By not implementing the framework methods init(), validateInput(), updateModel(), in the local abstract base class controllers the subclasses are still forced to implement these methods and thus uphold consistency with the main execution flow in the Orkwan framework. In addition, new abstract methods may be defined to further impose a uniform implementation in the application controllers.