| Author |
Message |
|
|
Because validation is dynamic, in other words, the validation code is executed per each request, you should avoid creating new rule objects inside your validation code. Check the code below:
The code above is perfectly correct and legal, but we are creating too many new objects on each request without a real need for it. A rule is thread-safe by design and you should re-use the same instance across different requests. A possible fix for this problem is illustrated in the code below:
However note that it is not very easy to read the validation code when the rules are defined outside the prepareValidator() method. A better approach is to make the rule constructor private and create a static getInstance() method that will return instances of the rule and it will be smart enough to return the same instance when the same object is requested. Check below how we would accomplish this with the NegativeRule introduced in the previous chapter.
Check how we are using a cache to return the same instance for each "min" parameter passed to the getInstance() method. This will avoid creating unnecessary duplicates of the same object.
Most of the provided rules inside the org.mentawai.rule package implement the getInstance() method. So when using them you should make use of this method to obtain instances of the rule. Check the example below:
When coding your own rules, you should provide a getInstance() method whenever possible. But this is not really a necessity. It is just a good practice that it is worth mentioning for the competent programmer.
|
 |
|
|
A rule validates whether an action input value is valid or not to be passed along to the action. To learn about rules let's code a simple NegativeRule class that validates only negative numbers.
When you extend BasicRule you should implement two methods: check() and getTokens().
check(String value): In this method you receive the value you are suppose to validate and just do it, however you want to, returning true or false.
getTokens(): In this method you return a map with tokens and their values to be used by error messages. The default implementation in BasicRule is to return null, so you should only implement this method if you have tokens to return.
A token is used inside error messages so that it can be substituted for values associated with the rule. For example, in our simple rule above, the negative number has no minimum boundary. Let's change it to include a possible minimum:
Note that now we have the possibility to validate the input value based on a minimum number and we are returning the token "min" with whatever value is given to that minimum number. Therefore you may have an error message like the one below:
The %min% token is substituted with the value returned in the getTokens() method. The IntegerRule that comes with Mentawai also returns tokens for its min and max values. The tokens of a rule are nothing more than its properties that you may want to display along with the error messages.
There are other types of validation that are not solely based in the value itself. For example you may want to perform validation based on the user locale and you may want to perform validation of one field based on another field.
The most common example of a validation that depends on the locale is date validation. Depending on the user locale the date 12/30/2008 may be valid or not. Below is the source code of a simple rule to validate dates. Note that we are now extending LocaleRule instead of BasicRule.
Note that now the check() method receives not just the value but also the locale of the user performing the request. You should use the locale in any way you want to perform the validation. Mentawai already comes with a complete DateRule so you should not need to code a rule for dates. This was just an example for you to understand how the LocaleRule works in case you want to write your own.
The last form of common validation is when you need to compare two fields to determine whether one field is valid or not. If you thought about password and password confirmation you were right. For that we can extend the CrossRule class. Check the source below for a simple EqualRule that validates whether two fields have the same value:
When inheriting from CrossRule you should implement two abstract methods:
getFieldsToValidate() should return an array of the field names that will be compared.
check(String[] values) receives the values of the fields indicated by the getFieldsToValidate() method, so that you can perform any comparison you want to validate them.
Note that Mentawai gets the field values for you from the action input and pass them along with the check method so that you can compare them and return the validation result. Mentawai already comes with a complete EqualRule in the org.mentawai.rule package.
Another very useful built-in rule that you can use for your advantage is the RegexRule. Check below how a SocialSecurityNumberRule could be implemented by inheriting from RegexRule:
You could have also used the RegexRule with the Social Security Number pattern without the need to create a new rule for it. See below:
As you can see, by inheriting from BasicRule, LocaleRule, CrossRule and RegexRule you can perform any kind of validation for your action input values.
|
 |
|
|
Instead of using the Validatable interface, which has to be implemented by the action, you can code a separate filter that will be applied to the action in the application manager. You may choose that approach if you want your action to be clean from any validation code or if you want to apply the same validation to different actions that may depend on the same form data.
To code your validation filter, you should inherit from org.mentawai.filter.ValidationFilter and implement the abstract method prepareValidator(). Check the example below:
The method prepareValidator() is very similar to the same method of the Validatable interface, the only difference being that it also passes as a parameter the action object that the filter is being applied to. The rest is the same. You can use an i18n file or you can place the error messages in the filter code, pretty much like you can do when using the Validatable interface. One thing to note is that if you use an i18n file, the name of the file will be /validation/filtername_localehere.i18n. So for the filter above it would be /validation/HelloValidator_pt.i18n, /validation/HelloValidator_en.i18n, etc.
Then you can apply the filter to one or more actions inside the application manager. Below is an example:
|
 |
|
|
It is important to realize that the validation code inside the prepareValidator() method is executed for each request. Therefore you can dynamically perform your validation based on the action parameters and characteristics. Check the example below:
In the example above, only a POST request will be validated. A GET will not have any validation. You can also perform validation based on the inner action, which is very common as each inner action will have its own validation. Check below:
In the code above, only the "addUser" innerAction will be validated. All others, including the default (no inner action) execute() method will not be validated. Recall that innerAction == null means the execute() method was called.
Because of the dynamic characteristic of the validation, you should avoid creating new rule objects on each request. That's why we have been using the getInstance() method instead of the new constructor for our rules. The getInstance() method returns the same instance every time, avoiding the creation of new objects on every request. Despite the fact that the Java garbage collector is very powerful nowadays, you should try to avoid creating too many Java objects unnecessarily.
Most of the Mentawai built-in rules already provide the getInstance() method. If possible, and as a good practice, you should provide this method with any new rule you code. We will see how to create new validation rules in a bit.
|
 |
|
|
Although not a good practice, you are free to place your validation error messages inside your source code instead of using an i18n file. If you application is a simple one that does not plan to use internationalization you may prefer this approach.
If Mentawai does not find the i18n file or the key inside the i18n file, it will just display the key as the error message. So just place your error message as the key and you are ready to go. See the example below:
Note that we are just using the error message as the key. Mentawai executes the following logic to find the error message.
Look for the i18n file. If you don't find the i18n file, use the key as the error message.
If you found the i18n file, look for the key. If you don't find the key, use the key as the error message.
If you found the key, print the associated text as the error message.
As you can see, it is very easy to place your error messages inside your source code instead of using an i18n file.
|
 |
|
|
You can set the ValidatorFilter() as a global filter so that all your actions implementing the Validatable interface will be validated. Here is how you set this global filter inside your application manager.
Below is an example of how to validate your action using the Validatable interface. Note that you are not using annotation or xml. You are using programmatic configuration to setup the validation of your form fields.
To each form field is applied a set of validation rules. For example, for the field "age" we are applying two rules:
RequiredRule: enforces that a field is present, in other words, not blank or null
IntegerRule: enforces that a field is an Integer between a min and a max value
Mentawai validation rules are as flexible as Java code because they are Java code, not XML or Annotation. If you check the javadoc of the IntegerRule class you will find out that it has three different constructors:
IntegerRule(): meaning any integer will be accepted, even negative integers
IntegerRule(int min): meaning any integer >= min will be accepted
IntegerRule(int min, int max): meaning any integer >= min and <= max will be accepted
Mentawai comes with many read-to-use rules in the package org.mentawai.rule like StringRule, NumberRule, EmailRule, FileRule, etc. Plus it comes with a very handy rule called RegexRule which allows you to validate any field based on a regular expression. You will not be surprised to find out that many other Mentawai rules (like the EmailRule) inherit from RegexRule to do its job. As we will see later in this chapter, it is very, very easy to create your own rules for validation as well in case the provided ones do not suit your needs.
If a validation rule fails, a error message associated with it is displayed to the user. Because these messages are usually internationalized (translated into many languages) and more strongly because you don't want to mix content with source code, they should reside in a text file we call i18n file. (Soon you will see how to bypass this good practice and place your error messages together with your code if you prefer to)
The default location for your validation error messages are /validation/actionamehere_localehere.i18n. However if you prefer to keep all your messages in a single file (that we call master) you can invoke the following method inside your application manager:
If you do this, all your messages, not just for validation but all internationalized messages in your application will be stored in the file /i18n/master_localehere.i18n.
So for example if your are NOT using the master file, your action is MyAction (like our example above) and you have two languages pt and en, you would have the following files: /validation/MyAction_pt.i18n and /validation/MyAction_en.i18n.
But if you are using the master file, then all your messages from all your actions would be stored in the files: /i18n/master_pt.i18n and /i18n/master_en.i18n.
Some people will prefer to store all text messages in a single file what makes things easier when it comes the time to translate everything to a new language. Other people will prefer to have a separate file for each action. It is up to you what approach to take.
An example of the validation messages for the action above is listed below:
Note how we are using the tokens %min% and %max% to place some validation information inside our error messages. You should already be familiar with the i18n file syntax because it is the same of the Java Properties class.
Now to display these messages inside a JSP page, all you have to do is make use of some very useful mentawai tags. See our example below:
Note that this tags are conditional tags, in other words, nothing inside the tag will be shown if there is no error for the field. If you want to display all field errors in the top of your page instead of near each field in your form, you can use another menta tag, the <mtw:outFieldErrors> tag.
To conclude, an important tip that we will see later in the book: Use the Mentawai HTML form tags <mtw:input>, <mtw:select>, <mtw:checkboxes>, etc. so you don't have to worry about re-displaying the values that the user typed in case of validation error. We know you don't want to place a bunch of scriptlets in your JSPs to do this.
|
 |
|
|
In this chapter we discussed about the Mentawai architecture and its main interfaces like Action, Input, Output, Context, Filter, Consequence, etc. You learned how filters work and how to create your own filters. You also learned about inner actions and how to configure your actions inside the application manager. You were also introduced to the Mentawai Debug Mode, that can show you everything that is happening under the hood inside the action invocation chain.
You also saw the main consequences of any web application: Forward and Redirect. You were also introduced to the Chain, Stream and Ajax consequences.
Now it is time to dive into the many Mentawai features. Remember that one of the goals of the Mentawai framework is to abstract all the repetitive tasks of any web project. Stuff like Validation, Authentication, Form Handling, Sending Email, etc. are boring and repetitive, so the framework has to offer you a very simple and intuitive way of dealing with that. With Mentawai you should never waste time re-inventing the wheel. It tries to do all the boring work for you so you can have fun getting your project done and making your boss happy.
|
 |
|
|
Forwarding to JSP pages
The basic and most common consequence you will use in your web projects is the Forward. In Mentawai, this is the org.mentawai.core.Forward class, which implements the org.mentawai.core.Consequence interface. The interesting aspects about a forward are listed below:
A forward always happens in the server-side, in other words, the client (the browser) does not realize that a forward is happening. For example, if you make a call to the action http://www.mysite.com/Hello.mtw and the result of the action Hello.class is a forward to /hello.jsp, the location in the address bar of your browser will not change to /hello.jsp. That's because the forward is really happening on the server-side. No other request is needed from the browser. The request is passed over to the JSP page and the result is returned in the same request that asked for the Hello action. This is exactly the opposite of what happens when you do a Redirect.
A forward will always pass the action and all its contexts (Input, Output, Session, Cookies, etc.) to the JSP page. When you do a forward, the results of the action execution are made available to the JSP page. This is the opposite of what happens when you do a Redirect. When you do a redirect, a new request is done by the browser to a new action or JSP page. All the previous action information before the redirect is discarded.
The classic scenario is:
<ul>WebRequest ---------> Action generates some results ----------> Forward to JSP to display the results</ul>
For example for the simple action below you can configure some forwards:
Inside the application manager:
Just keep in mind that whenever you want the action information to be displayed in the JSP page you will most likely want a Forward consequence. Another point to keep in mind is that if the user hits the Reload button in the browser, the whole action is executed again.
Redirecting to a new URL
In Mentawai, the Redirect consequence is implemented by the core class org.mentawai.core.Redirect, which implements the core interface org.mentawai.core.Consequence. When you issue a Redirect consequence, you are really telling the browser: "Hey! Please do another web request to the this URL here!". As a result, the URL location in the address bar of the browser will change to the redirect URL and all the information about the action that performed the redirect will be discarded.
Contrary to the Forward, a Redirect happens in the client-side, in other words, it is the browser that performs another web request to the JSP page or action. You could use a redirect, for example, when you want to avoid having the user accidentally hitting the reload button and executing the action again.
There are many ways to a redirect in Mentawai. Below I will explain each one:
Regular redirect
This is the redirect you will be using 99% of the time and it is always related to the context path of your web application. The context path is the directory where your application is located inside the Tomcat webapps directory. It is what allows the same Tomcat to run a bunch of different web applications together and independently.
Below are some examples:
The first redirect will tell the browser to perform another web request to the following URL:
http://www.mysite.com/DVDStore/congrats.jsp
The second redirect will tell the browser to perform another web request to the following URL:
http://www.mysite.com/DVDStore/user/users.jsp
This is assuming that your context path is "DVDStore". If your context path was the ROOT context path, which is the ROOT directory inside the Tomcat webapps directory, your redirect URLs would be:
http://www.mysite.com/congrats.jsp
http://www.mysite.com/user/users.jsp
Redirect relative to the web server
You can use this redirect when you want to perform a redirect to another web application running in the same web server. For example:
When you start the redirect with a double slash ("//"), you are just saying: "Hey, I don't want to be relative to the context path!" Therefore, even if you context path is "DVDStore", the resulting redirect URLs for the examples above will be:
http://www.mysite.com/BookStore/showBooks.mtw
http://www.mysite.com/index.jsp
This type of redirect is rare. It is only needed when you are running two different applications in the same servlet container that are somehow related to each other.
Redirect with dynamic parameters
Sometimes you want to perform a redirect, but you will only know the parameters of the URL at runtime. For example, you want to redirect the browser to the action that shows all the books for a certain category. This could be something like:
http://www.mysite.com/BookStore/Book.show.mtw?cat=<CATEGORY_ID>
To setup this redirect with a dynamic parameter, you can do this:
The true above indicates that this is a redirect with dynamic parameters that will come from the action output. So our action will just do something like this:
Assuming that book.getCategory() returns 43, the resulting URL would be:
http://www.mysite.com/BookStore/Bood.show.mtw?cat=43
Note that all the values from the action output will be added as parameters to the redirect URL.
Redirect to a dynamic URL
Sometimes you will not even know the URL you want to redirect to. This is rare, but can happen, for example, when you want to redirect your user to a URL saved in a database. Here is how it works:
When you create a redirect using the constructor with empty parameters (or make use of the helper method redir() with empty parameters), the redirect will expect the URL to come from the action output. Here is an example of an action:
Note that you are using the REDIRURL_PARAM from the Redirect consequence to place the dynamic URL inside the action output. You may have other values inside the action output and that's how the redirect class knows how to find the dynamic URL.
Last but not least about the Redirect consequence:
If you redirect contains the string "://" inside it, it is assumed that you want to do a redirect to a absolute address. Examples:
The redirect understands that the above redirect are all absolute URLs, not relative to anything.
There are no redirects relative to the request
This is on purpose. If you try to do something like on(SUCCESS, redir("loosepage.jsp")), Mentawai will automatically assume that you meant on(SUCCESS, redir("/loosepage.jsp")). Redirects relative to the request are not needed and only introduce confusion.
You can also use a dynamic URL with dynamic parameters. Below is how to configure this redirect in the application manager:
Dynamic parameters will be automatically encoded. Dynamic URLs will not be encoded, in other words, if they have any hardcoded parameter it is assumed that it is already encoded.
Chaining actions
For a single web request you can chain different actions in the server-side before invoking a consequence that will return a result to the browser. For example you can have a request handled by action A chained to action B chained to action C and then action C returns a result that will invoke a consequence. You should note that like a forward, chaining actions happens in the server-side. Below is an example of how to configure a chain consequence:
Or you can use the less verbose style:
Before you configure any chain consequence, you must understand that action chaining is rare and in most cases there is no need for you to make use of it. Let's take a classic example where you have an action that adds an user and another action that lists all the users. You may get tempted to do a chain like:
Although definitely not a sin, this could have been done much better with a redirect consequence:
So now after the add inner action is done, it will redirect (not chain) to the show inner action. <u>If the user hits the reload button, only the show inner action will be executed again, and not the whole chain.</u> Note that we are supporting dynamic parameters for the redirect, that's because we may need to add some parameters to the URL before redirecting, for example:
http://www.mysite.com/User.add.mtw?category=34
As we saw in the previous chapter about redirect, these parameters will come from the action output.
To conclude, here is what you should keep in mind when using a chain consequence:
Think if you don't really want a redirect to an action instead of a chain. The question you should ask yourself is: Do you want to execute everything again if the user press the reload button?
When two actions are chained, the input and output of the first are passed to the second.
Each action will be executed with its own set of filters. If we have a global filter, then it will be executed twice, one time for each action.
You can also pass an inner action to the chain controller:
Other consequences
Mentawai comes with other consequences and you can also code your own consequences. For example, to return an image or a binary file you can use the StreamConsequence:
In the action:
In the ApplicationManager:
You can place a byte array or a InputStream as the stream key in the action output. If for example the file is a PDF, you don't want to load everything in memory in a byte array:
An in the ApplicationManager:
For Ajax, you can use the AjaxConsequence that uses a renderer to format the action response into an ajax response (JSON, XML, etc.)
Action:
ApplicationManager:
|
 |
|
|
This chapter is important because it will show you all the ways you can use to setup your actions in the application manager. If you decide not to configure anything, then Mentawai will use its conventions, for example a request to http://www.mysite.com/HelloAction.hello.mtw will call the hello() method of the HelloAction class. The default consequence for the action will be a forward to the /HelloAction/hello.jsp.
As we have discussed in earlier chapters, sometimes there is no way out and you have to configure your actions. This chapter will show you how to do it.
Note that we are adding a global filter that will be applied to all actions. We are also adding a filter specific to the action (VOFilter). Two consequences (one for JSP and one for SUCCESS) and a global consequence for the result LOGIN are being added as well.
No inner action was specified, so by default this configuration will be considered for all inner actions inside UserAction.class. The following actions will be executed with the filters and consequences configured above:
http://www.mysite.com/User.mtw
http://www.mysite.com/User.add.mtw
http://www.mysite.com/User.del.mtw
Basically all inner actions inside the UserAction class will have the configuration specified for the UserAction class. Now let's get more specific.
You can add consequences and filters specific for a single inner action. Let's add a filter and a consequence specific for the add inner action, in other words, for requests to http://www.mysite.com/User.add.mtw:
So for our example, we will have the following filters being executed, in the following order:
AuthenticationFilter -> VOFilter -> AuthorizationFilter -> UserAction.add
Note that global filters are always executed first, then comes the action specific filters, then comes the inner action specific filters.
For consequences, the rule is simple:
First try to get a consequence specific to the inner action. If not found, try to get a consequence specific to the action. If not found, try to get a global consequence. If still not found, go by the Mentawai conventions: forward to /User/add.jsp.
You can get even more specific creating a whole ActionConfig object just for the inner action you want to configure. The filters and consequences you define will be applied only for the inner action specified in the action config. Below is an example:
You can play with the order of the filters with some methods like ActionConfig.addFilterFirst(), that will add a filter to be executed BEFORE the global filters. You can also make your action implement the GlobalFilterFree interface, to signal to the controller that it does not want any global filters. And you can also add a global filter that will be executed LAST, in other words, after all other filters have been executed. This will be rare cases, when for some very particular scenario you need to take more control in the invocation chain of filters.
Below are some examples of these rare methods:
|
 |
|
|
In Mentawai an action is a piece of code that gets executed when the Mentawai controller receives a HTTP request from the browser. As we have seen before, this can be a class that extends BaseAction, a class that implements Action or any regular POJO (Plain Old Java Object).
By default, if you make a request to http://www.mysite.com/HelloAction.hello.mtw, the controller understands that you want to call the hello() method of the HelloAction class. You can also omit the method part, like in http://www.mysite.com/HelloAction.mtw and the controller assumes that you want to call the method execute() from the HelloAction class.
Mentawai uses the term inner action for the hello() method. An action class such as HelloAction.java can have many inner actions inside it. These inner actions are nothing more than the instance methods of the action class.
For example, we may have an action class like this:
We say that the LoginAction has no inner actions. The LoginAction class is the action itself, in other words, you can call it with the following URL:
http://www.mysite.com/LoginAction.mtw
You can also call it like this, although it would not make much sense:
http://www.mysite.com/LoginAction.execute.mtw
Now we can add a new method to the LoginAction, in other words, we are adding an inner action to the LoginAction class.
An now to call the inner action sayHello you would use the following URL:
http://www.mentaframework.org/HelloAction.sayHello.mtw
It is important to understand the inner action concept because some methods take a String as the innerAction parameter. For example, we can make our action implement the Validatable interface, to setup some validation code:
Note that for the execute method, we consider that there is no inner action, so the innerAction is null and not "execute". The execute() method is kind of the natural method of an action class, in other words, it is the method that gets executed when no inner action is specified in the request URL.
The only way to get the innerAction parameter to be equal to "execute" is to call the action with the following URL: http://www.mysite.com/HelloAction.execute.mtw, which does not make much sense. If you want to call the "execute" method, you should make a request like: http://www.mysite.com/HelloAction.mtw, and the innerAction will be null.
|
 |
|
|
The Mentawai Debug Mode is an useful feature that lets you know and see what is happening when your action, together with all its filters, gets executed. As we saw in the previous chapter, a filter can alter the action input and output before and after the action gets executed. Wouldn't it be great to see all the filters that are being executed before the action as well as how the input and output look like after each step? That's exactly what the Mentawai Debug Mode will do for you. You can even choose to show the debug information at the bottom of your JSP page or inside your HTML code as a HTML comment. Here is how you set up and turn on the debug mode.
First you need to configure the DebugServletFilter as a servlet filter. This is a servlet filter (from the Servlet API) and not a Mentawai Filter, so we need to get inside the web.xml file. Luckily all you have to do is paste some XML code inside this file. Below is the web.xml file from the example in the previous chapter:
After you setup the DebugServletFilter inside the web.xml file, all you have to do is turn it on in your application manager:
This will make the debug text appears in the bottom of your HTML pages. Not something that you would want in a production site, for example. So you can also output the debug mode as a comment inside your HTML page:
Run your application again and now you will see the debug information of your actions. For the web site http://www.secretcrushonline.com I see the following:
- - - - - - - - - - - - - - - - Mentawai DEBUG - - - - - - - - - - - - - - - -
ActionName = CupidoAction
InnerAction = answerArrow
ActionClass = com.amorsecreto.action.CupidoAction
Browser Locale = en_US
Action Locale = pt_BR
Default Locale = pt_BR
Input:
empty
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filter = org.mentawai.filter.AuthenticationFilter@145eca6
Input:
empty
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filter = org.mentawai.filter.ValidatorFilter@1b3c169
Input:
empty
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filter = org.mentawai.filter.ConnectionFilter@108fbd4
Input:
empty
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filter = IoCFilter
Input:
empty
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filter = DIFilter: tryField=true
Input:
empty
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Filter = org.mentawai.filter.InjectionFilter@dcc20c
Input:
conn = com.mchange.v2.c3p0.impl.NewProxyConnection@efa9fb
loc = pt_BR
userDAO = com.amorsecreto.dao.mysql.MySQLUserDAO@179fb8d
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Action = answerArrow()
Input:
conn = com.mchange.v2.c3p0.impl.NewProxyConnection@efa9fb
loc = pt_BR
userDAO = com.amorsecreto.dao.mysql.MySQLUserDAO@179fb8d
Output:
empty
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Result = jsp
Consequence = Forward to /answerArrow.jsp
Total Time = 1ms
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Mentawai Web Framework - Version:1.11 Build:20070911
|
 |
|
|
Filters are the building blocks of Mentawai through which all the main feature of the framework are implemented. A filter wraps or intercepts an action so that it can change everything about the action before it is actually executed. A filter always gets executed before the action, but as we will see in a moment it can also modify the action after it has been executed. For a single action you will usually have a stack of filters (InvocationChain) being executed before the action. You can define global filters that will be applied on all actions or you can define action specific filters that will be applied only on that action. Filters are powerful, simple and flexible. They favor the Separation of Concerns pattern, allowing your action to do one thing and do it right! For example, there is no need for your action code to get convoluted with validation code, file upload code, connection pooling code, etc. This can and should be abstracted outside your action through filters.
Mentawai comes with many ready-to-use filters, but you should understand how they work and how to build your own filters. Building filters are very simple and you should have this weapon ready to kill unnecessary complexity in your code. Let's start coding some filters.
A filter that gets a Map from the application and places it in the action input:
This was a somewhat unrealistic example as your action can just access the application context and get the cache map itself. However it is useful to understand the basic functionality of filters.
Through the InvocationChain object passed as a parameter to the filter you can get the action before it has been executed. Having the action, you can now access its Input, Output and contexts such as the Application context. After the filter is done, it should call chain.invoke() unless it wants to halt the action execution. The invoke method will call the next filter in the filter stack or the action if there are no more filters in the stack. It returns the action result (String) which will be in turn returned by the filter.
There will be cases when you do want to block the access to the action, for example when you are using an authentication filter. An authentication filter may look like the filter below:
In the filter above we are checking if the use is logged in the system, before allowing the action to be executed. If we find an object in the session for the key "user" we assume that the user is logged and call invoke(). If the user is not logged than we return the result LOGIN which may redirect the browser to a login page.
A filter can modify the action before and also after the action has been executed. Check the example below which calculates the total time the action has taken to execute:
Note how we are modifying the action output before and after the action has been executed. Another possibility is to use the AfterConsequenceFilter, which inherits from the Filter interface, to perform some task after the consequence has been executed. Let's change our TimerFilter to compute the time it takes for the consequence to execute:
Why didn't we place the total consequence time in the action output as well, so that we could show it in the JSP page? That's because after the consequence is executed the JSP page is history. The page was already processed so placing new things in the action output will not do us any good. That's why we chose to use the System.out to display the total consequence time.
The AfterConsequenceFilter is useful for example for the HibernateFilter so that the hibernate session is closed only after the view layer has been processed and any lazy loading has been done.
Filters can do many other things as we will see in later chapters. It can do input validation, handle exceptions, open transactions, inversion of control, etc. Filters prepare everything for the action, so that when it is time for the action to get executed, everything is ready, organized and beautiful.
Below is an example of an ApplicationManager that is using the CacheFilter and the TimerFilter described above. You can also download by clicking here an application that makes use of both filters. Run this application and you will be surprised by the Mentawai Debug Mode, which is our next topic.
|
 |
|
|
Mentawai has a very simple architecture following the KISS principle. The framework is all based on interfaces so it is extremely flexible. The basic concept is the notion of an Action that executes some task. An action has an Input and an Output. All the information from the web request is contained in the action input. The results produced by an action are placed in the action output so that it can be displayed in the view layer (JSP page, velocity, etc.). An action also has access to Contexts such as Session, Application and Cookies. After the execution, an action should return a Result. For each result you can configure a Consequence such as Forward, Redirect and Chain.
The core interfaces of the Mentawai framework are listed below with a brief explanation:
(Click in the interface to see its javadoc)
org.mentawai.core.Action: It just has setters and getters for the Input, Output, Session, Application, Cookies and Locale. It also defines some standard Results like SUCCESS, ERROR, INDEX, etc. The default implementation is BaseAction.
org.mentawai.core.Input: It provides methods to get values from the input as Object, int, float, double, boolean, etc. It performs the necessary conversions and also have versions that return a default value. It also has methods to return properties of a request (through reflection) and headers of a request. You can also use the method keys() to list all keys in the action input. The default implementation is RequestInput. Below are some examples:
org.mentawai.core.Output: It behaves pretty much like a Map<String, Object> where you can place values by a String key. You can also check if it is empty with the isEmpty() method and get all the keys with the keys() method. The default implementation is ResponseOutput. Below are some examples:
org.mentawai.core.Context: It behaves pretty much like a web application context where you can set attributes by name. A context also supports the reset() method (optional) and the keys() method. Some of its implementations are the SessionContext, ApplicationContext and CookieContext. Below are some examples:
org.mentawai.core.Consequence: A consequence is executed after the action has executed and returned a result. A consequence is an interface that has only the execute() method that receives the action, the HttpServletRequest and the HttpServletResponse. The main consequences implemented by the framework are: Forward (forwards to a JSP page), Redirect (redirects to a JSP), Chain (chains the action with another action) and StreamConsequence (returns a byte stream through the HttpServletResponse). You can implement your own consequences as well.
org.mentawai.core.Filter: A filter is the main building block of the framework. We will talk more about filters in the next chapter, but for now you should know that it is an interface with two methods: destroy() for cleanup and filter() that receives an InvocationChain as a parameter and returns a result. The signature of the filter() method is:
org.mentawai.core.AfterConsequenceFilter: This interface inherits from the Filter interface to add an extra method that is executed after the Consequence of the action is executed. The method signature is:
The big picture is more or less like this:
You don't need to fully understand the Mentawai architecture to start building web applications with it. However it does not hurt to have a good foundation and to really understand how things are working under the hood.
|
 |
|
|
In this chapter you saw how to create a simple Mentawai web application and touched the surface of topics such as actions, input, output, consequence, results, filters, tags, html forms, etc. All these topics will be explored in greater details in later chapters.
You learned that an action (unless it is a POJO action, which can be a misleading name) should not implement the business model of an application. The action should provide the link between the HTTP web request and the business model of a web application.
You also learned about POJO actions, which are actions that are not coupled in any way to the Mentawai framework. The classic scenario is to make your business model become a POJO action so that it will handle the HTTP web requests with the help of filters and without any need for an action.
Before we start exploring some of the Mentawai main features, let's discuss in greater detail the Mentawai architecture so you can have a better foundation to move on.
|
 |
|
|
In this chapter I want to give you a taste for POJO actions. In a web application, actions are meant to process the HTTP request and to invoke the business model of your application. A common misconception is to assume that your action is implementing your business model. Although this is possible, it is not recommended for serious applications for the reason that it will couple (tie) your business model to the framework being used, making testing and re-use in another application or environment more difficult. It is considered a good practice to keep your business model as decoupled as possible from the framework being used as well as from the rest of your application.
The common scenario is to have your actions performing a link, a glue or a bridge between the HTTP web request and the application business model. However, in some cases but not in all cases, the role of the action becomes predictable as it is just passing data back and forth to the business model. That's the reason, in my opinion, that frameworks like JSF want to make web development become desktop development by eliminating the need for actions and making use of event-based components instead.
Although Mentawai is an action-based framework (in contrast of a component-based or event-based framework), it allows the use of an action to be bypassed when desired. In our previous example we have coded the action HelloMenta.java and, because it was a simple example, we did not code any business logic outside the action. A more desirable approach would have been to have a separate class implementing the business model. I like to call it HelloService, but you can call it HelloFacade or HelloLogic or anything else if you prefer.
The code for our examples/helloworld/service/HelloService.java can look like this:
Note that it is not coupled to any Mentawai class or annotation. It is a simple POJO (Plain Old Java Object) that implements the logic of our application.
Now let's modify our action to make use of the HelloService class (our business model):
Notice how our action now creates an instance of HelloService to perform its duties. You can now say that the action is using the business model and not implementing it.
If you stop for some time to contemplate our code (yes, source code is a form of art!) you may come to the conclusion that the action here is not really necessary. It is just passing data back and forth to the business model. Let's try to eliminate our action and use the HelloService as a Mentawai Pojo Action. In the application manager, let's specify that our action will be the HelloService.class and not the HelloMenta.class anymore.
Note that we are specifying the action name as "/HelloMenta" because if we don't specify anything Mentawai will assume by convention that the name is the same as the action class, in this case "/HelloService". We want to keep the same action name so that we don't need to change it inside our JSP page (HTML form).
You don't need to, but you can now delete the file HelloMenta.java. We are bypassing the action and using our HelloService.class as our POJO action! Try to execute the web application with the latest changes and you will get the following exception when submitting the HTML form:
What happened here? Clearly the username was null when the method hello() from HelloService was called. But why? Probably because nobody called the method setUsername to set this variable before the method hello() was called. Remember that this was a duty of our action HelloMenta, which is not in the picture anymore. But don't dispair, Mentawai can easily solve this injection problem for you through its InjectionFilter. We will come back to the topic Filters later on, but for now it should be enough for you to know that a filter gets executed before the action so that you can perform any task before the action gets executed. Let's add a global filter that is applied to all actions in our application manager:
We have added two global filters. They are global because they are not associated with any action in particular, so they will be applied to all actions defined in the application manager. The job of the InjectionFilter is to inject all values from the action input inside the action (or POJO action). That's exactly what we want, in other words, we want the username value inside the action input to be injected inside the HelloService POJO action. The job of the OutjectionFilter is to take (or outject) all properties from the HelloService POJO action and to place them in the action output. That's exactly what we want, in other words, we want to take the "time" and "username" properties of HelloService and place them in the action output so that the tag mtw:out can display them in the JSP.
Redeploy your web application with the latest changes and double check that everything is working! Great! We have just used a POJO action with Mentawai! Before we conclude this chapter, let's make some improvements to the HelloService class.
You should know that the InjectionFilter is capable of injecting a value straight into a public or private instance variable, in other words, it does not need the setter method (setUsername) to perform the injection. So let's get rid of that method.
Another possibility is that you want to pass the action input values as parameters of the HelloService method hello(). Let's give it a try:
We will come back to all these neat features in later chapters, but for now you should realize that Mentawai offers you a great deal of flexibility to do things the way you want. You can download the complete war file for our final HelloWorld application by clicking here.
|
 |
|
|
|
|