Академический Документы
Профессиональный Документы
Культура Документы
www.jaxenter.com
TM
#12
Apache Wicket
Introducing ZK
Stripes
Editorial
Index
Apache Wicket
The Fun Web Framework
Jeremy Thomerson
10
Introducing ZK
Develop dynamic and robust Java web apps with ZK
Timothy Clare
17
Stripes
A lean, mean Java web framework
Fred Daoud
24
30
Composite Components
Server requirements
The SSE specification specifies requirements for the server response. The specification states that the response content type must be text/event-stream. To send a single line of data (Hello World!): data: Hello World!\n\n. To send multiple lines of data:
data: This is the first line\n data: This is the second line\n\n
Here, a single string of data concatenated with the newline character will be sent to the client side event handler function. It is also possible to send the response as JSON data:
data: {\n data: "msg":"hello world",\n data: "id:"12345\n data: }\n\n
The event data for these events would be pushed over the same EventSource connection. It should be mentioned that SSE (namely the EventSource API) is not yet available in all browsers. Google Chrome is one such browser that has SSE support.
Composite Components
quote information real-time as it is updated on the server. The quote numbers are randomly generated on the server, but real quotes could be retrieved by tying into a quote service. The third area consists of a link to retrieve RSS news feeds for the stocks, and a scrollable area that displays the news feed. The news feed is also pushed from the server real-time. Corresponding to these three areas are three SSE event types: time, ticker and rss. When the user interface is first displayed, the clock will tick real time. You enter space delimited stock symbols, press the Get Quotes button and a table is dynamically displayed with streaming quotes. Pressing the Get Stock News link causes news feeds to stream from the server.
In all cases, notice that two successive newline characters end the response.
26. 27. <br/> 28. <ez:clock/> 29. <br/> 30. <br/> 31. <ez:stock/> 32. 33. <h:messages/> 34. 35. </h:form> 36. 37. <a href="doc.xhtml">Documentation</a> 38. </h:body> 39. </html>
Composite Components
This user interface uses four composite components. Well look at these in more detail later on, but I summarize them here: sse: Server Sent Events component used to establish a connection to a server end point. clock: Clock component displays the ticking time bar stock: Stock component that produces the input field for entering stock symbols, the two buttons, and a placeholder for the dynamic table that will display stock ticker information. rss: Component that produces the link Get Stock News and a placeholder for the dynamic scrollable area that will display stock news. This component is actually contained in the implementation of the stock component which is why you dont see it in this view. The first thing youll notice is that on line 1 were using an HTML5 document type declaration. That makes this document an HTML5 polyglot document because it has an HTML5 document type /namespace and well-formed XHTML syntax which works great for JSF views. This would allow us to use
other HTML5 features as well. Lets take a look at the other relevant sections of this view: Lines 3 7: Here we declare the namespaces used in the user interface. Lines 3 and 4 are just the standard JSF namespaces. Line 5 is the Facelets namespace. Line 6 declares the namespace that well use for our SSE component. Line 7 declares the namespace that will be used for the clock, stock and rss components. Line 25: This is the usage of the SSE component. This component uses two attributes: url: The URL of the server endpoint that we are connecting to events: Specifies event type : event handler pairings. The event handler is the name of the JavaScript function that will handle the associated event type data coming from the server. Line 28: This is the usage of the clock component. Line 31: This is the usage of the stock component. Its easy for a page author to use well defined components in a view. Now lets see how these components are defined.
Recall that JSF composite components consist of two areas: interface section implementation section The interface section is where you define the usage contract for the component. It is where you define the attributes that a page author would use with your component. The implementation section is the code that produces the component. It may consist of HTML markup, JavaScript and/or other JSF components. Lets take a look at the composite components that are used in this application.
Composite Components
55. } 56. } 57. 58. }, 59. 60. /** 61. * Register a callback for error handling. 62. * <p><b>Usage:</b></p> 63. * <pre><code> 64. * JSF.sse.addOnError(handleError); 65. * ... 66. * var handleError = function handleError(data) { 67. * ... 68. *} 69. * </pre></code> 70. * 71. * @member JSF.sse 72. * @param callback a reference to a function to call on an error 73. */ 74. addOnError: function addOnError(callback) { 75. if (eventSource !== null) { 76. if (typeof callback === 'function') { 77. eventSource.addEventListener('error', callback, false); 78. } 79. } 80. 81. }, 82. 83. /** 84. * Register a callback for event handling. 85. * <p><b>Usage:</b></p> 86. * <pre><code> 87. * JSF.sse.addOnEvent('timeEvent', handleEvent); 88. * ... 89. * var handleEvent = function handleEvent(data) { 90. * ... 91. *} 92. * </pre></code> 93. * 94. * @member JSF.sse 95. * @param eventName the event name associated with the message. 96. * @param callback a reference to a function to call for processing 97. *messages with a specific event name. 98. */ 99. addOnEvent: function addOnEvent(eventName, callback) { 100. if (eventSource !== null) { 101. if (typeof callback === 'function' && eventName !== null 102. && typeof eventName !== 'undefined') { 103. eventSource.addEventListener(eventName, callback, false); 104. } 105. } 106. 107. } 108. }; 109. 110. }(); 111. }
Composite Components
a JSF component <h:outputScript> to specify the target location of the JavaScript file. On line 24 we are using a function from the JSF SSE JavaScript library to establish a connection to the server endpoint using the url and events attribute values. So, the idea is that you can drop one of these components in your JSF view to connect to a server endpoint. Lets take a look at the JavaScript library that contains the functions for performing SSE actions (Listing 4): Line 9: JSF namespace declaration. Line 21: A private function that creates an EventSource instance to connect to the server endpoint specified by the provided url. Line 45: The public connect function that is used in the SSE composite component in Listing 3. This function calls through to the private function getEventSource and registers the application callback functions using a public addOnEvent function (line 99).
Composite Components
Line 74: A public utility function for registering an error processing callback function. With SSE you can register a function with the error event type. Line 99: A public utility function for registering callback functions for processing event types.
voke a method on a managed bean to start the clock ticking. The managed bean could just utilize a java.util.Timer method to compose and send the current date/time string back to the client. Line 15 is a placeholder for where we will display the time event data from the server. On Line 16, we load a JavaScript file that contains application callback functions that will process the event data returned from the server. Lets take a look at the relevant section of code from this application JavaScript file for this clock component (Listing 6). Line 5: We obtain the event data Line 6: We obtain our div placeholder element (created in Listing 5, line 15). Line 7: We write the event data string (which is the current data / time)
35. document.getElementById(fields[0]+3).innerHTML = ""; 36. } 37. } else { 38. newRow = stockTable.insertRow(stockTable.rows.length); 39. newCell = newRow.insertCell(newRow.cells.length); 40. newCell.id = fields[0]; 41. newCell.innerHTML = fields[0]; 42. newCell.align = "center"; 43. newCell.width = "20%"; 44. newCell = newRow.insertCell(newRow.cells.length); 45. newCell.id = fields[0]+1; 46. newCell.innerHTML = fields[1]; 47. newCell.align = "center"; 48. newCell.width = "20%"; 49. newCell = newRow.insertCell(newRow.cells.length); 50. newCell.id = fields[0]+2; 51. newCell.innerHTML = fields[2]; 52. newCell.align = "center"; 53. newCell.width = "20%"; 54. newCell = newRow.insertCell(newRow.cells.length); 55. newCell.id = fields[0]+3; 56. if (fields[3] == "UP") { 57. document.getElementById(fields[0]+3).innerHTML = 58. "<img src='resources/up_g.gif'/>"; 59. } else if (fields[3] == "DOWN") { 60. document.getElementById(fields[0]+3).innerHTML = 61. "<img src='resources/down_r.gif'/>"; 62. } else { 63. document.getElementById(fields[0]+3).innerHTML = ""; 64. } 65. 66. newCell.align = "center"; 67. newCell.width = "20%"; 68. } 69. } 70. } 71.}
Composite Components
Line 18: The input text field that will store the entered stock symbol data to a managed bean. Lines 19 21: The button that, when activated, will fire a request over Ajax to invoke a method getStockInfo on a
managed bean. This method could utilize a java.util.Timer method to reach out to a stock quote service periodically, and send the event data back to the client. Lines 29 36: This is the placeholder area for our dynamic table to display the stock data from the server. Line 40: This component uses the rss composite component (more about that next), (Listing 8). The stockHandler function receives the event data, that is the stock quote information and performs some JavaScript magic to dynamically build and display the stock table.
Conclusion
This article has been all about creating JSF composite components that make up the user interface for a stock quote application that uses HTML5 Server Sent Events. JSF 2 introduced the world to composite components, and together with JavaScript, provides a component author with a multitude of options for creating dynamic components. HTML5 Server Sent Events are the second class citizen to HTML5 WebSockets, but it does offer an HTTP solution to event streaming without switching to another protocol.
Roger Kitain is a Principal Software Engineer at Oracle where he has spent most of his time in the web technology space since 1997. Roger has been involved with the JavaServer Faces technology since its inception, and has co-led the JavaServer Faces Specification since JSF 1.2. He has presented at many conferences including JAX, JSF Summit, JavaOne, Dexoxx, Jazoon, Ajax Experience, Ajax World.
Apache Wicket
by Jeremy Thomerson
As you may suspect by the articles title, Wicket is my preferred UI framework for most of the applications I write. Whether you end up sharing that same passion for this great framework or whether it fits the type of application you desire to write is up to you and your circumstances. But, read on and you may find another great technology to add to your go-to stack.
nent, as well as all the others, automatically retain their state throughout the various other requests that the user makes.
What is Wicket?
Wicket is a component-oriented Java web framework, which focuses on making it easy to write reusable code. It requires no XML configuration (aside from a few lines in your standard web.xml deployment descriptor), making it refreshing to work with. It encourages a clean separation between your logic and your markup, appealing to most programmers desire for a sense of order. With Wicket your code is just Java and your markup is just HTML. This makes it much easier for HTML guys to assist with the project without fear of messing something up and without having to learn a complex templating language, parameter names and values, et cetera. Wicket appeals to a person who loves object-oriented programming. Each component on your page is a real Java object. It can hold state and perform useful functions just like any Java object. The nice thing about this is that this state persists through web requests to abstract the complexities of the stateless hypertext transfer protocol from you. Imagine that you have a page that has an accordion-style panel in the side navigation bar, as well as a complex data table in the main content area. A user comes to this page and expands a section of the accordion. In your code, you may call setExpandedSection(index), or you may have setExpanded(true) on it and false on the others. However it is that you model your component, you can store that user-selection as a regular Java member variable inside your accordion component or one of its children. Now, when the user clicks a link to re-sort your complex data table, the table itself does not need to know about the state of the accordion component or any other component on the page. But, the accordion compo-
10
Apache Wicket
sion size explode. We will discuss more about that below in the section What Are Models?. If youre really concerned about being able to validate my assertion that Wicket will not use more memory than other frameworks, you may want to check out http://tinyurl.com/wicket-perf-1 and for a more elaborate comparison of Wicket versus several alternative frameworks, see http://tinyurl.com/wicket-perf-2 (both written by Peter Thomas). If you can just trust me for now, continue on.
controls it you simply add the wicket:id attribute to it. We had two things in this markup that were dynamic. The most obvious was the link. The second is a span that renders the value of the counter on each page render. We use a span tag simply because we need a markup placeholder for the value to be inserted, and a span tag will not disrupt the structure of our other markup (Listing 1). Now, lets look at the associated Java code in Listing 2. The first thing you should note is the name and placement of the files. By default, for any Wicket component (pages, as we are using here, are also components, so the same rule applies), Wicket will look in the same package directory on the classpath for a file with the same name, with the extension .html (if you are rendering XML, this would still apply, but with a .xml extension). This behavior can be re-configured, but for this article we will not get into that. There are online resources that can assist you with that if needed. When you examine the actual code, you will see several parts. The first thing you see is the counterModel. You notice that it implements IModel. Models are extremely important in Wicket. Theyre also generally one of the least understood concepts when programmers first adopt Wicket. Well discuss them more later. For now, suffice to say that they are a wrapper for retrieving or setting a piece of data, and this AbstractReadOnlyModel, as its name implies, is simply a wrapper for retrieving data. We implement the getObject method to retrieve the current value of counter on every invocation. This model is then passed to a Component. In this case the component is
Hello world
The hello world part is already done for you by the archetype. So, lets create a simple example of a link that increments a counter on the page. To get started (assuming you have the simple quickstart above running), find the HomePage.java and HomePage.html files and open them in your editor. Note that unless otherwise mentioned, all examples in this article will be based on the latest stable release, 1.5.1. Lets look at the HTML first. The first thing that you probably notice is that it is just regular HTML. This is by design. Markup should not contain logic thats Wickets philosophy. To tie a piece of markup to corresponding Java code that
Listing 2: HomePage.java
public class HomePage extends WebPage { private static final long serialVersionUID = 1L; private int counter = 0; public HomePage() { IModel<Integer> counterModel = new AbstractReadOnlyModel<Integer>() { private static final long serialVersionUID = 1L; @Override public Integer getObject() { return counter; } }; Label label = new Label("counter", counterModel); add(label); Link<Void> link = new Link<Void>("increment") { private static final long serialVersionUID = 1L; @Override public void onClick() { counter++; } }; add(link); } }
Listing 1: HomePage.html
<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4strict.dtd" > <head> <title>Wicket Quickstart Archetype Homepage</title> </head> <body> <strong>Wicket Quickstart Archetype Homepage</strong> <br/><br/> Click <a href="#" wicket:id="increment">this link</a> to increment the following counter:<br /> Counter: <span wicket:id="counter">123</span> </body> </html>
11
Apache Wicket
a Label. A label simply takes whatever tag it is associated to in the markup and replaces the body of that tag with the value retrieved from the model it is given. In this case, our span tag will replace 123 (its static body in the markup) with the value of our counter, which on the first page render will be zero. The next thing you see is the link. It gives you just a glimpse of the power of Wicket. Were using an anonymous inner class here to implement the link. If you had multiple places in your application that needed to increment an integer, you could replace this with a concrete class simply by extracting the anonymous inner class into a real top-level class. Now, look at how simple it is to implement an action of any sort on a link. You simply override the onClick method and put your business logic in there. Obviously incrementing a counter is a simple example, but you could just as easily call a service-layer method that deleted or edited something in your domain, sent an email, or made a web service call. Wicket does the hard part for you. You implement the logic and you dont have to worry about how to get a URL that will invoke that small bit of logic. You dont have to worry about where to store the current state of the counter. All you have to do is write your code that performs the work that your application is designed to do. Thats why I love Wicket!
12
Apache Wicket
form and you can add wicket:id attributes to the input fields and the form itself. The div with the wicket:id feedback is a placeholder for a built-in Wicket component that shows the user all feedback messages that were generated by the form. In the picture above, the user has just submitted the form with bad data which caused validation messages to appear in our feedback panel component. These can easily be customized to have a feedback panel near each form field that only shows the messages for that particular field a common requirement in many applications. Now looking at the Java code (Listing 5), you see that we created two constructors. This is so the form can be used as both an add person and an edit existing person form. By default, the no-argument constructor is called which makes this an add person form because it is using a model that holds a new person. But, a link could easily be written that shows the user an edit person page simply by passing a model to the page with an existing person. (Note that we dont cover that example in this article, but if you were doing this, you would likely want to use a LoadableDetachableModel, which is discussed more in the section on models.) Next you see that we instantiate our Form object. We implement the onSubmit method and make it contain our
business logic in this case a simple call to a service-layer class. Moving along, you see where we create each field and configure it with our UI rules which fields are required, and what is required of them (for instance, a valid email address format). What you dont see is refreshing no calls to HttpRequest to get strings out, no conversion of strings to dates, no manual retrieval of data from our form post and no manual setting of data on our backing Person object. In long forms, this saves hundreds of lines of copy-and-paste and highly error-prone code. This is because we are dealing with real Java objects not a bunch of strings that have to be manually manipulated. See the form processing image in the top ten list and the link to the full top ten list below for more information. Note that we are using a service-layer object (IPersonService) to do our persistence. This is a Spring-managed bean, and it is extremely simple to use because of Wickets built-in Spring (or Guice, if you prefer) integration.
13
Apache Wicket
2. Pull, dont push: In general, your components should be able to pull their state rather than having some other component push the state on it. The example in Listing 6 demonstrates what this means and why its important. The key is that the constructor only runs once, but many things on the page might change state after the constructor is run. If you push your state into components in your constructor, they are out-of-date as soon as another component changes state. The only alternative is that every component must know about all other components that share state and thats not the Wicket way. Your components should be independent and reusable. 3. Wicket is a UI framework: Dont write middle tier or persistence code in Wicket. 4. Keep markup and code clean and compact: Become familiar with and use the various aspects of Wicket that allow for reusability panels, markup inheritance, and fragments as examples. 5. Match your hierarchies: It wont take you long at all to learn this rule, but the hierarchy of components in your markup (tags with a wicket:id attribute) must match the hierarchy in your Java code. 6. Form processing lifecycle: When dealing with forms, it is important to understand the order of operations of the form post processing. If any step in the process fails the
onError method of your form and submitting button will be called rather than the onSubmit method (figure 2). 7. Java nuances: There are two Java nuances that catch many Wicket newcomers off-guard serialization and the presence of anonymous inner classes. Become more familiar with both. 8. Unit testing: Testing your Wicket code is made relatively easy by using WicketTester. Try it out. 9. i18n: Internationalization is made very easy with Wicket. Become familiar with things like wicket:message and file naming standards for Wickets built-in i18n support. 10. Where to find help: Wicket code is well documented, so reading JavaDocs is a good start. Wicket examples can be viewed online at http://wicketstuff.org/wicket. Additionally, Wicket has one of busiest and most helpful mailing lists of any open source project. For more information on the Wicket mailing lists, see http://wicket. apache.org/help/email.html.
In conclusion
I chose Apache Wicket because other frameworks sucked the fun out of web programming (okay, this is subjective, but I can say that I didnt personally enjoy them). Most of my time seemed to be spent dealing with pushing state around or writing configuration in XML. Every little change required changes in multiple files many times in XML, Java, and specialized markup files. It was hard to make cohesive reusable components. A few days with Wicket made me see how drastically different it was. I was writing real Java object-oriented code, and easily creating reusable components. I hope that you will give it a few days for your own project or experiment so that you too can discover how much fun web programming can be. For more information on Wicket, see http://wickettraining. com/resources
Jeremy Thomerson has helped numerous companies efficiently utilize leading Java open source technologies, including Wicket, Spring, Lucene, Hibernate and more. For the past several years, he has owned and operated the leading U.S. company providing Wicket training and consulting services.
14
www.jaxlondon.com
Java Core JBoss Spring
Technical Architecture
Java EE Agile
Cloud
Android
follow us:
Presented by:
twitter.com/jaxlondon
JAX London
JAX London
Organized by:
TM
Java EE Day:
Modular Apache Java
Java he T
Agile Day:
Think
Blobs: moving big data in and out of the cloud in the Java enterprise
a pull system for Just-In-Time development and Continuous delivery the writing is on the wall
John Stevenson (Atlassian)
Learned from Becoming Agile at Electronic Arts: Using Scrum and Kanban to develop EA's Physics Engine and Romans Top Ten Product Backlog Tips
Roman Pichler (Pichler Consulting Ltd) & Mike Bassett (Electronic Arts)
Spring Day:
Data
Khanderao Kand (GloMantra Inc) Martijn Verburg (Ikasan) & Ben Evans
Spring Roo SpringSource Tool Suite: Choosing the right tool for the right job 3.1 in a Nutshell
Android Day:
uilding B
Learned
Architecture Day:
Thinking Busy
www.jaxlondon.com
ZK
Introducing ZK
ZK is a highly productive open source Java framework for building amazing enterprise web and mobile applications. This article introduces the reader to using ZK for development of dynamic and robust Java web applications. The author explores mainstay ZK features, which are employed for ZK development, and touched upon more complex patterns.
by Timothy Clare
In this article, we will cover the following ZK features and topics: ZK: History and Architecture Implementing a ZK application
Model
This section introduces the implementation of the model using ZK. The model contains two files, Employee.java and EmployeeService.java. These files are a simplified version of the model and therefore do not include any database functionality. As ZK is predominantly server-side, database functionality is trivial to add. Server-side resources can be accessed directly without any need for connectors. In this example application the model's implementation is kept simple using a synchronized list. The following code outlines the service:
package org.zkoss.jtjexample.service; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.zkoss.jtjexample.bean.Employee; import org.zkoss.zul.ListModelList; public enum EmployeeService { INSTANCE; private List<Employee> _employees = Collections.synchronizedList (new ArrayList<Employee>()); private EmployeeService() { _employees.add(new Employee("Roger", "Charles", 21)); _employees.add(new Employee("Gary", "Peters", 27)); _employees.add(new Employee("Edward", "Daniels", 35)); } public ListModelList getModel() {
Implementing a ZK application
This example application follows the MVC architectural pattern, separating business logic from the user interface, enabling the separation of user interfaces (contained in zul files) from java business logic. This separation of concerns leads to cleaner and more maintainable code. The attached example contains numerous files. The table below categorizes these files with respect to the MVC pattern:
17
ZK
other. In general terms the majority of components in ZK can be children of any other components. This is called the nesting of components.
The service is basic Java except for the method getModel(). This method returns a class of type ListModelList which is used to provide ZK with a representation of data. The ListModelList is an intermediary between raw data in Java and ZKs representation of the raw data. When constructing the model, a List is passed along with a boolean. This indicates whether ZKs model will copy the available data into its own internal list or use the provided list. In this case, as the application is simple the model is instructed to use the _employees list directly. If this application was more complex it would be beneficial to add another layer between ZKs representative model and the application's model layer. A good example of this can be found in the ZK Essentials documentation.
Nesting ZK components
In this example it is better to split the functionality between departments and employees and not have the two areas over-
View
In this section, we discuss the planning and implementation of the GUI using the vast array of Ajax controls provided by ZK. Designing the interface: The ZK framework uses ZUML markup to describe GUIs; storing markup within *.zul files. ZUML is a powerful concept enabling developers to rapidly implement complex GUIs. In the following sections we will demonstrate using ZUML to implement powerful interfaces. Using ZK components: Nowadays, most people are familiar with markup languages due to the extensive use of both HTML and XML, hence ZKs ZUML should instantly have a familiar feel. Firstly, a simple div is defined. As shown in Listing 1, attribute id is used to set certain properties of the window. id is ZKs reference to the control. One of the most powerful concepts in ZK is its component model; the model affords developers the ability to place components inside each
18
ZK
lap. Therefore an easy method of separation is needed while enabling one to switch back and forth quickly. Luckily ZK has hundreds of components to choose from. In this instance it would seem like a tabbox would work best. Therefore the
Listing 5: EmployeeController.java
public class EmployeeController extends GenericForwardComposer { /** * */ private static final long serialVersionUID = -9089818718875587898L; private static final Log log = Log.lookup(EmployeeController.class); private Employee _currentEmployee; private ListModelList _model = EmployeeService.INSTANCE.getModel(); Listbox lstEmployee; Textbox txtFirstName, txtLastName; Intbox intAge; public Employee getCurrentEmployee() { return _currentEmployee; } public void setCurrentEmployee(Employee currentEmployee) { this._currentEmployee = currentEmployee; } public ListModel getEmployeeModel() { return _model; } // click events public void onClick$btnAddEmployee() { String firstName = txtFirstName.getText(); String lastName = txtLastName.getText(); int iAge = Integer.parseInt(intAge.getText()); Employee employee = new Employee(firstName, lastName, iAge); if (!_model.add(employee)) { reportError(Messages.getString("EmployeeController.0"), employee); } }
public void onClick$btnUpdateEmployee() { if (lstEmployee.getSelectedItem() != null) { Employee employee = (Employee) (lstEmployee.getSelectedItem() .getValue()); employee.setFirstName(txtFirstName.getText()); employee.setLastName(txtLastName.getText()); employee.setAge(Integer.parseInt(intAge.getText())); } else { UiUtils.showMessage(Messages.getString("EmployeeController.2")); } } public void onClick$btnDeleteEmployee() { if (lstEmployee.getSelectedItem() != null) { Employee employee = (Employee) (lstEmployee.getSelectedItem() .getValue()); if (!_model.remove(employee)) { reportError(Messages.getString("EmployeeController.3"), employee); } } else { UiUtils.showMessage(Messages.getString("EmployeeController.4")); } } private void reportError(String message, Employee employee) { StringBuilder sb = new StringBuilder(message).append(Messages.getString("Em ployeeController.5")).append(employee); final String error = sb.toString(); UiUtils.showMessage(error); log.error(error); } }
19
ZK
tabbox is nested inside the window as demonstrated in Listing 2. The output of Listing 2 is shown in Figure 1. A couple of concepts needs to be highlighted here, first an observer will notice the apply and hflex attribute on the div. An apply attribute is used when one wants to link the ZK UI to a GenericForwardComposer. The GenericForwardComposer will be discussed in detail in the next section. For now, all one needs to know is that it acts as the controller in the MVC paradigm for ZK. The hflex attribute is used to layout components horizontally. This is complimented by vflex which enables vertical layout control. Both hflex and vflex inform ZK how the components parent should distribute the space among its children. In this case, the author used true as the tabbox is the only child of the window. Setting hflex to true indicates that the component should fill the entire space available. Us-
ing hflex and vflex one can create complicated layouts efficiently. Looking into hflex and vflex in more detail is beyond the scope of this article, for more information please refer to the ZK Developers Reference guide. Defining a UI as demonstrated is a simple affair when using ZK. This tutorial uses the XML option, however, it is also possible to define a UI entirely using Java. Having introduced the basic view it is time to move on to discuss the controller and databinder.
Defining a controller
To create a controller the class org.zkoss.zk.ui.util.GenericForwardComposer should be extended. An example controller is shown in Listing 3. The composer then needs to be associated with a relevant view. Previously, the usage of apply was touched upon in Nesting ZK Components. In the following section the relationship is outlined in more detail.
20
ZK
data binding, the apply attribute of a component has to be set. Most components support the apply attribute. Listing 4 demonstrates the usage of the apply attribute. The EmployeeController is responsible for handling the user flow. The code of the EmployeeController is demonstrated in Listing 5. Listing 5 introduces two concepts firstly the ability to capture
Capturing events
Event capturing in ZK is easy to implement, providing developers with enormous power. Listing 7 is a function signature to capture the onClick event from the button named btnAddEmployee. The onClick event is fired when a user clicks a button. The method signature in Listing 7 can be broken down into 3 parts. Firstly, the return type is void and method is public. The method name consists of the event name, followed by a dollar sign ($) and the name of the component whose event you want to trap. Then lastly, in this case the event type is a ForwardEvent. Using this technique it is possible to trap any event from any ZK component in a composer which is properly wired to the view (Listing 6).
To make it easier to explain how data binding works, Listing 8 contains the complete source of index.zul. This article will now step through each relevant part and include, where necessary the relevant parts of the applied controller.
21
ZK
then followed by currentEmployee. Readers should also note that Listing 10 outlines a function and a bean named current Employee. This demonstrates that data binding the syntax to access beans and functions differ. This is outlined in Table 1. The attribute selectedItem is set to keep track of which bean is selected from the Listbox. When the selection is changed, ZK will automatically update the bean accordingly.
Access identifier
fullFunctionName
Description
To access a function in the controller through data binding, it is necessary to use the full function name When accessing a bean from the controller, it is only required to use the bean name, there is no need to include get or set. However, the bean in question must have both a getter and setter.
beanName
22
ZK
lar methodology is applied to the components in the group box which represent the selected item. Each one of them is bounded to specific data of the currentEmployee bean using techniques discussed throughout this chapter. Therefore, as the currentEmployee changes when selecting on a different employee in the listbox, the relevant textboxes are also updated. These techniques apply throughout ZK. The relevant extract is shown in Listing 14. Having loaded and displayed the data, it is time to start manipulating it.
item readers may notice is that each component is referenced directly from the code. To enable this function, a developer only needs to specify the component type and its id in the controller and ZK will do the rest through automatic wiring. For example, Listing 17 shows that declarations are enough to make access to components possible in Listing 16. The last talking point is how ZK would know to update the listbox model after updating, adding or deleting an item. This is simply a matter of assigning load-after in the model attribute on the listbox to the relevant button events which update the model. When one of these events are fired the model will be reloaded. Listing 18 demonstrates the code necessary for this. Breaking it down, it is as simple as specifying the component id followed by a . and the event name.
Conclusion
As demonstrated, implementing a representation of Java data which is editable is very easy in ZK. Using 2 ZK specific files (index.zul and EmployeeController.java) it is possible to leverage the power of ZK to create a general CRUD application which can operate on a Java based datastore. ZKs advantage is due to its server-centric nature which provides easy access to all the servers resources, thus eliminating the need for any glue code.
Going further
The source code for this application is available on Github. If you would like to read more about ZK and other features, such as its transparent server push mechanism, then the documentation is a great place to start.
Timothy Clare is a Technology Evangelist for the Potix corporation which produces the ZK Framework. He has been working with various web and mobile technologies for over ten years.
Imprint
Publisher Software & Support Media GmbH Editorial Office Address Geleitsstrae 14 60599 Frankfurt am Main Germany www.jaxenter.com Editor in Chief: Editors: Authors: Copy Editor: Creative Director: Layout: Sebastian Meyen Jessica Thornsby, Claudia Frhling Timothy Clare, Fred Daoud, Ted Husted, Roger Kitain, Jeremy Thomerson Claudia Frhling, Katja Klassen Jens Mainz Dominique Kalbassi Sales Clerk: Mark Hazell +44 (0)20 7401 4845 markh@jaxlondon.com Entire contents copyright 2011 Software & Support Media GmbH. All rights reserved. No part of this publication may be reproduced, redistributed, posted online, or reused by any means in any form, including print, electronic, photocopy, internal network, Web or any other method, without prior written permission of Software & Support Media GmbH The views expressed are solely those of the authors and do not reflect the views or position of their firm, any of their clients, or Publisher. Regarding the information, Publisher disclaims all warranties as to the accuracy, completeness, or adequacy of any information, and is not responsible for any errors, omissions, inadequacies, misuse, or the consequences of using any information provided by Publisher. Rights of disposal of rewarded articles belong to Publisher. All mentioned trademarks and service marks are copyrighted by their respective owners.
23
Stripes
24
Stripes
public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } }
One feature that makes things easy for you, the developer, is, that action beans are thread-safe. A fresh instance is created to handle each request, so you are free to use instance variables without worry. The next thing to know about action beans is that you handle events with methods of the following signature:
public Resolution eventName()
Now we have a few more takeaways: The class includes a method named showDetail, which matches the value of the event="showDetail" attribute in the JSP. This method will automatically be called when the user clicks on the link. The class also includes a productId property. Its value will be set to the product ID associated with the link, because we have <stripes:param name="productId"> tag in the body of the link. Although all parameter values arriving over HTTP are Strings, we can use Integer for our productId property. Stripes will automatically do the type conversion, and also supports Date, BigDecimal, BigInteger, and all primitive and wrapper types, out-of-the-box. Its easy to write type conversions for your own types, as well. The result returned by the showDetail method is a Resolution, which defines what happens next. Here, we are using ForwardResolution which clearly shows that we are forwarding to productDetail.jsp. The information is all there, no need to search in a separate file to figure out what the "success" string means! Stripes uses Action Beans, which are so named because they combine actions with JavaBean properties. Here, the action is showDetail, and the bean is the inclusion of the productId and product properties. In productDetail.jsp, we can easily show the details of the selected product by taking advantage of the automatic presence of the action bean within the JSP, under actionBean, courtesy of Stripes:
<%-- productDetails.jsp --%> <h1>Product details</h1> <ul> <li>Name: ${actionBean.product.name}</li> <li>Description: ${actionBean.product.description}</li> <li>Price: ${actionBean.product.price}</li> </ul>
The method name determines the name of the event that it handles. We already saw how to indicate the action bean class and event name in a link:
<stripes:link beanclass="..." event="...">
The name in each <stripes:submit> tag indicates which event handler method to call in the action bean (the value is the button label to show in the browser). This makes it really easy to handle multiple submit buttons in a form! Just write an event handler method for each button:
public Resolution save() { // ... } public Resolution cancel() { // ... }
You can have a default event handler in an action bean in two ways: first, if it is the only event handler method in the action bean, it is automatically the default. Second, you can designate an event handler method as the default by annotating it:
@DefaultHandler public Resolution view() { // ... } public Resolution cancel() { // ... }
Knowing that the current action bean is always available in the JSP under ${actionBean} makes it easy to transfer data to the view: just add a getter method in the action bean class for the information that you want to access.
After you have handled an event, you need to tell Stripes where to go next. You do this with a Resolution. Let's talk about those now.
25
Stripes
As you can see, it is simple and lets you do just about anything in order to send a response to the browser. Of course, Stripes has some built-in implementations for common responses: ForwardResolution forwards to a template, typically a JSP RedirectResolution sends a redirect to the browser, typically to another action bean StreamingResolution makes it easy to send a binary file as a response, such as a PDF JavaScriptResolution sends a Java object in JavaScript, which is very useful when using Ajax ErrorResolution sends an HTTP error, with error code and descriptive message One of these will suit your needs most of the time, but you can extend them or write your own from scratch to get any customized behavior that you require. The two most commonly used resolutions are ForwardResolution and RedirectResolution:
// Forwards to a JSP return new ForwardResolution("/WEB-INF/jsp/productDetails.jsp"); // Redirects to another action bean and its default event handler return new RedirectResolution(AnotherActionBean.class); // Redirects to another action bean and a specific event handler return new RedirectResolution(AnotherActionBean.class, "showDetails"); // Redirects to another action bean and a specific event handler, // with request parameters return new RedirectResolution(AnotherActionBean.class, "showDetails") .addParameter("productId", 42).addParameter("showDescription", true); }
public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } /* Address.java */ package com.jaxenter.stripes.model; public class Address { private String streetName; public String getStreetName() { return streetName; } public void setStreetName(String streetName) { this.streetName = streetName; }
Youd like to enable the user to create a person with an address using an HTML form. With Stripes, it would look something like this first, youd have an action bean with a Person property:
/* PersonActionBean.java */ package com.jaxenter.stripes.action; import net.sourceforge.stripes.action.DefaultHandler; import net.sourceforge.stripes.action.ForwardResolution; import net.sourceforge.stripes.action.Resolution; import com.jaxenter.stripes.model.Person; public class PersonActionBean extends BaseActionBean { private Person person; @DefaultHandler public Resolution view() { return new ForwardResolution("/WEB-INF/jsp/personForm.jsp"); } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
Resolutions are expressive and let you know right then and there, exactly what the next step is in responding to the browser.
Notice that we also have a default event handler that forwards to personForm.jsp; this is where we create the HTML form:
<%-- personForm.jsp --%> <stripes:form beanclass="com.jaxenter.stripes.action.PersonActionBean"> <div> First name: <stripes:text name="person.firstName"/>
26
Stripes
</div> <div> Street name: <stripes:text name="person.address.streetName"/> </div> <div> <stripes:submit name="save" value="Save"/> </div> </stripes:form>
public class PersonValidatedActionBean extends BaseActionBean { @ValidateNestedProperties({ @Validate(field="firstName", required=true), @Validate(field="address.streetName", required=true) }) private Person person; @DefaultHandler @DontValidate public Resolution view() { return new ForwardResolution("/WEB-INF/jsp/personValidatedForm.jsp"); } public Resolution save() { // save the person object... return new ForwardResolution("/WEB-INF/jsp/personSaved.jsp"); } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
See how simple and straightforward that is? We use the <stripes:form> tag and indicate the target action bean class name. Then we create text inputs with the name attribute that corresponds to the target property: person. firstName and person.address.streetName. When the user submits the form, Stripes will automatically call getPerson().setFirstName(<value>) and getPerson().getAddress(). setStreetName(<value>) on the action bean to populate the person object and its nested properties. It gets better: you might think that youll get a NullPointerException because we never created a new Person() object. Never fear! Stripes does it for you, even on nested properties. When it gets null from a getter when binding request parameters to properties, Stripes creates a new object and sets it. So you can just add properties and let Stripes take care of the housekeeping. Finally, we just need an event handler for the submit button, which is named save:
By the time the save() method is called by Stripes, the parameters have been bound to the action bean properties, so we can count on the person object to be created, populated, and ready to be saved. Well, that is not entirely true: for that to work, the user has to fill in the form, rather than submit a blank form. We can easily address this with validation.
The @Validate(required=true) annotation marks a property as a required field. We can place this annotation directly on an action bean property if we are submitting a value directly to that property. In our example, were not doing that; we're submitting values to nested properties of the person property. In that case, we need to place our @Validate annotations inside a @ValidateNestedProperties annotation, and use the field attribute to indicate which nested property we are validating. Notice that we also added @DontValidate to the view() method so that the validations are not enforced when the user first arrives to the action beanthey havent had a chance to fill in the form at that point. Now, with these validations, a blank form will not cause our save event handler to be called. Instead, Stripes will catch the validation errors and redisplay the form instead, with error messages at the location where we add the <stripes:errors/> tag:
<%-- personValidatedForm.jsp --%> <stripes:form beanclass="com.jaxenter.stripes.action.PersonValidatedActionBean"> <stripes:errors/> <div> First name: <stripes:text name="person.firstName"/> </div> <div> Street name: <stripes:text name="person.address.streetName"/> </div> <div> <stripes:submit name="save" value="Save"/> </div> </stripes:form>
27
Stripes
MALE("Male"); private String description; private Gender(String description) { this.description = description; } public String getDescription() { return description; } }
Submitting a blank form will result in a page as shown in figure 1. Whats really nice is that if the user fills in one of the fields, but not the other, the page will be redisplayed with the error message concerning the required field that was left blank, but the field that was filled in will automatically be repopulated. Notice that we dont have to do anything in the <stripes:text> tag for that to happen. This keeps your JSP code clean and tight. Stripes comes with the following built-in validations: required field minimum and maximum length minimum and maximum value, for numerical input matching a regular expression mask evaluation of a JSP expression Writing a custom validation method is a breeze, too. Just write a method annotated with @ValidationMethod. For example:
@ValidationMethod public void validateSomething(ValidationErrors errors) { if (!password.equals(confirmPassword)) { errors.add(new SimpleError("The passwords do not match.")); } }
Here is how you could create a form to select one gender with radio buttons, any number of genders with checkboxes, and one gender with a select box:
<%-- formControls.jsp --%> <stripes:form beanclass="com.jaxenter.stripes.action.FormControlsActionBean"> <div> Radio buttons: <c:forEach var="gender" items="<%= com.jaxenter.stripes.model.Gender.values() %>"> <stripes:radio name="radioChoice" value="${gender}"/>${gender.description} </c:forEach> </div> <div> Checkboxes: <c:forEach var="gender" items="<%= com.jaxenter.stripes.model.Gender.values() %>"> <stripes:checkbox name="checkboxChoices" value="${gender}"/> ${gender.description} </c:forEach> </div> <div> Select box: <stripes:select name="selectChoice"> <stripes:option value="">Select...</stripes:option> <stripes:options-enumeration enum="com.jaxenter.stripes.model.Gender" label="description"/> </stripes:select> </div> <div><stripes:submit name="view" value="Send"/></div> </stripes:form>
By adding an error to the errors list, we cause Stripes to return to the form instead of calling the event handler, just like when a built-in validation fails. Finally, note that Stripes only calls the validation method if the built-in validations have passed. Again, this keeps your code clean and tight, because for example you dont have to do null checks on the password and confirmPassword if you made them required fields. If the required validation fails, the custom validation method will not be called. The error messages concerning the fields being required will be shown instead.
As you can see, the Stripes tag library makes it easy to create forms and use form controls. Remember that the form controls automatically repopulate from previous values. Also, Stripes will automatically handle the conversion between String and our Gender enum type. Finally, notice the <stripes:options-enumeration> tag that takes the class name of an enum and automatically creates a list of options from its values. It renders the enumerations constant by default, but we can also use another property as we have done here by specifying the label attribute. Stripes also has a <stripes:options-collection> and a <stripes:options-map> tag to render a list of options from any Collection or Map, respectively.
28
Stripes
One more very nice feature is that we can include a Select... option as the first in the list to prompt the user to select an option, instead of selecting one by default. By doing this, we can make sure that the user makes a selection. Indeed, because the value of the Select... option is a blank string, the corresponding property on the action bean will be null if the user does not make a selection. Using @Validate(required=true), we can enforce the requirement of selecting an option. This is much cleaner than using -1 or some other magic value and then manually checking for that value as meaning no selection was made! We are ready to look at the action bean:
package com.jaxenter.stripes.action; import java.util.List; import net.sourceforge.stripes.action.ForwardResolution; import net.sourceforge.stripes.action.Resolution; import com.jaxenter.stripes.model.Gender; public class FormControlsActionBean extends BaseActionBean { private Gender radioChoice; private List<Gender> checkboxChoices; private Gender selectChoice; public Resolution view() { return new ForwardResolution("/WEB-INF/jsp/formControls.jsp"); } public Gender getRadioChoice() { return radioChoice; } public void setRadioChoice(Gender radioChoice) { this.radioChoice = radioChoice; } public List<Gender> getCheckboxChoices() { return checkboxChoices; } public void setCheckboxChoices(List<Gender> checkboxChoices) { this.checkboxChoices = checkboxChoices; } public Gender getSelectChoice() { return selectChoice; } public void setSelectChoice(Gender selectChoice) { this.selectChoice = selectChoice; } }
stack framework nor a client-side graphical user interface. Its philosophy is that there are many excellent solutions for those tiers of an application, so it does not reinvent the wheel and does not prevent you from choosing your favorite solution for things like persistence, JavaScript widgets, and so on. Here is a summary of more features offered by Stripes: Automatic discovery of your action beans simply by mentioning, once and for all, the root package of your action beans. No need to adjust configuration every time you add, modify, or remove an action bean. Similarly, automatic discovery of your extensions: these are custom type converters, formatters, and other Stripes artifacts that you extend or implement to suit your needs. A layout system that is both simple and powerful. The recurring theme of not requiring configuration holds here as well. Annotations to easily create wizards, forms that span multiple pages. Interceptors, which allow you to tap into the Stripes lifecycle and do virtually anything to customize the framework to meet your requirements. Exception handling to define, in one place, how to handle exceptions, from specific to general. Testing facilities so that you can write automated tests for your Stripes web application. Localization to easily make your application available in multiple languages.
Conclusion
Stripes strikes a perfect balance of powerful features, simplicity, and tight focus to give you a framework that does a lot for you without getting in your way nor suffering from feature bloat. It is easy to learn and intuitive, so you quickly become productive. It is customizable and extendable, so you can tweak it to suit your more advanced or particular requirements. Last but not least, it has one of the friendliest communities in the open source framework space, so if you need help, write a message to the mailing list [2] or drop by the IRC channel [3].
Acknowledgements
Thanks to Jessica Thornsby for contacting me with the idea of writing an article on Stripes for the framework issue of Java Tech Magazine, and to Will Hartung for his review of the article and useful feedback.
Fred Daoud is the author of Stripes ...and Java Web Development is Fun Again [4] and currently uses Stripes at Modernizing Medicine, Inc.[5]. He has been using Java since 1997 and loves web frameworks. He also likes using other JVM-based languages such as Clojure, JRuby, and Groovy. His home page lives at http://www.fdaoud.com.
The properties correspond to the name attributes that we have in the JSP. As I mentioned, Stripes converts from the incoming String to the corresponding Gender. Whats more, for the checkboxes, we have a List<Gender> property. This list will automatically contain the genders that the user has selected. We dont even have to create a new ArrayList; Stripes will do that for us, too!
References
[1] The Stripes main web site: http://www.stripesframework.org [2] Mailing list: http://news.gmane.org/gmane.comp.java.stripes.user [3] The IRC channel: #stripes on FreeNode [4] The Stripes book: http://www.pragprog.com/titles/fdstr [5] Modernizing Medicine: http://www.modernizingmedicine.com
29
by Ted Husted
Struts 2 is directly supported by the three most popular integrated development environments (IDEs) for Java: IntelliJ IDEA by JetBrains [5], MyEclipse by Genutec [6], and NetBeans [7]. For example, to create a Struts 2 application in IntelliJ IDEA, you can select the Struts 2 facet, and the IDE will download the JARs and setup a Java web application structure, ready for you to add your own pages and other Struts 2 elements (figure 1). IntelliJ IDEAs support for Struts 2 includes configuration via dedicated Struts 2 facet with fileset manager, library validator, and configuration detector, dedicated Struts 2 structure tool windows, code inspectors that spot Struts 2-specific code issues, support for inplace Javascript and CSS code, smart coding completion within Struts elements, Struts 2-ready refactorings, and Struts 2-aware project navigation across Java, JSP and XML files. Likewise, with MyEclipse you can add Struts 2 capabilities to a web project, including wizards to create new Struts 2 elements or edit existing elements. The wizards are context-sensitive and provide input assistance and validation as you edit Struts 2 elements. Not to be left behind, NetBeans offers its own set of wizards to create an example Struts 2 application, register the necessary library files, and then edit the various Struts 2 elements. In addition, NetBeans supports hyperlinking and code completion in Struts 2 configuration files. Struts 2 also provides a Maven prototype you can use to bootstrap a project, if you are so inclined, and, of course, the distribution has all the usual assets for hardcore hand-coders.
1. 2. 3. 4. 5. 6. 7.
Pages with Struts Tags Message Properties Actions Interceptors Validators Results Annotations and Deployment Descriptors
The elements can be organized into three groups: Pages and Message Properties are used to create the application user interface. Actions, Interceptors, and Validators process an incoming request, in order to render a page, or some other Result.
30
Figure 2 shows the page rendered in a web browser. The two use cases for Message properties are normalization and localization. Many terms are used more than once, and so should be defined only once. Also, many enterprise applications are presented in multiple languages, and Struts 2 supports adding Message Property files for different locales. The key attribute can also be used as the name of the property used to populate the field from a dynamic value passed to the page.
Figure 2: Struts 2 Data Entry Form Figure 3: Struts 2 Value Stack
Annotations and Deployment Descriptors are used to define the initial state for the other elements of a Struts 2 application. Lets take a look at each element in turn.
31
change its presentation on the fly, by using operations like if, elseif, and else; or generate markup with operations like append, iterator, merge, sort, and subset. The Data tags mange dynamic values and behaviors, with operations like a, action, bean, date, debug, i18n, include, param, property, push, set, text, and url. The Form UI Tags replicate and extend the standard HTML tags, with operations like form, label checkbox, radio, optgroup, textfield, textarea, and password along with enhanced controls like checkboxlist, combobox, doubleselect, among several others. Finally, Non-Form UI Tags, provide error handling with operations like fielderror, actionerror, and actionmessage.
Figure 4: Struts Request/Response. (Image provided under the Apache Software License 2007 Apache Software Foundation http://struts.apache.org/2.0.6/docs/architecture.html)
<td class="tdLabel"><label for="RegistrationSavePassword" class="label"> Password:</label></td> <td><input type="password" name="password" value="" id= "RegistrationSavePassword"/></td> </tr> <tr> <td class="tdLabel"><label for="RegistrationSavePassword2" class="label"> (Repeat) Password:</label></td> <td><input type="password" name="password2" id= "RegistrationSavePassword2"/></td> </tr> </table></form>
32
Listing 3 shows the code the XHTML template generated for our data entry form. The templating system is designed so that you override the markup for a single control, or replace all the standard markup with your own custom theme.
ror", or "cancel". The framework passes the token returned by an Action to a Result handler for further processing.
33
struts-plugin.xml Optional configuration file for plugins. Each plugin JAR may have its own descriptor. Listing 4 shows a typical XML-stylenconfiguration for a logon action. Listing 5 shows the corresponding annotations for the same logon action. Tip Configuration by Java annotation or by XML document are not mutually exclusive. We can use either or both in the same application.
At each step in the cycle, developers can provide their own code to define how their application reacts to a particular request. 1. Accept Request: First, a web browser makes a request. The request might be for a resource like a HTML page, a PDF, or (among other things) the special Action resource that can create a dynamic response. 2. Select Action: The incoming request is transferred to a Struts 2 component called a Filter Dispatcher. Whenever this request is for a Struts Action, our dispatcher passes the request through to the rest of the framework. (Otherwise, the request just passes through our dispatcher, untouched.) 3. Push Interceptors: Whenever a request enters the framework, we might want to validate input, or transfer values to an internal object, or upload a file, or any combination of similar operations. (In other words, we want to intercept the request to do some housekeeping before going on to the main event.) The framework Interceptor components handle operations that need to be applied to more than one request. 4. Invoke Action: Aside from the Interceptor operations, we will also want to invoke an operation unique to the request (the main event). For example, we might want to work with a database to store or retrieve information. After a request passes through the outer layer of Interceptors, a method on an Action class can be called to handle any special processing for the request. 5. Select Result: After the Action method fires, we need to tell the rest of the framework what to do next. We've handled the request, and now we need to create the response (or result). As aforementioned, to bootstrap the response process, the Action method returns a string token representing the outcome of the request, such as success, failure, cancel, or something more specific, like logon. The framework matches that token with the name of a Result component. The Result is responsible for handling the response, either by rendering output for the browser, or by transferring control to another resource that will render the response. Most often, another resource will be a JavaServer Page formatted with Struts Tags. 6. Pop Interceptors: After the Result handler fires, the request passes back through the set of Interceptors. Some Interceptors work with incoming requests, some work with outgoing requests, and some work with both. 7. Return Response: The request exits the framework, and the web server sends our response back to the web browser. Architecturally, Struts 2 is an Enterprise Model View Controller [9] framework (see sidebar: MVC divide and conquer), where the Action provides the Model, the Result represents the Controller, and Tags render the View.
34
To give you a starting place, the Struts 2 distribution bundles a Struts Blank application. Its a ready-to-deploy WAR, designed to serve as a starting place for your applications (figure 5). The Blank application is internationalized and includes a number of example pages, to help you get started (figure 6). Of course, there are a number of online resources for learning more about Struts 2, most of which are documented on the official website at struts.apache.org. Another great resource is Struts 2 in Action [16] by Don Brown, et al. Highly recommended!
applications classpath. Since plugins are contained in a JAR, they are easy to share with others. Heres a sampling of the many plugins available for Struts 2: JQuery [10]: Ajax functionality and UI Widgets based on the jQuery javascript framework. Hibernate [11]: Hibernate Validator integration, Hibernate Core Sessions and Transactions injection capabilities and a Configuration Management Web Tool. Guice [12]: Actions, Interceptors, and Results to be injected by Guice. OSGI [13]: starting an instance of Apache Felix inside a web application, and scanning installed bundles for Struts configuration REST [14]: provides tools to build RESTful applications Sitemesh [15]: Sitemesh templates functionality access Struts information.
Ted Husted is the co-author of Struts in Action (first edition), a former Struts committer, and served as Release Manager for the initial Stuts 2 distribution. When he can, Ted now helps out with the Vosao CMS project (http://www.vosao.org), an open source web content management system for the Google Apps Engine. Ted will be speaking at ApacheCon NA in November 2010 on the Secret Life of Open Source and .NET @ Apache.org. He works as a Business Analyst with NimbleUser, a technology company helping professional and trade associations do more with web-based applications.
References
[1] Apache Struts 2: http://struts.apache.org/ [2] Web Work Released: http://www.mail-archive.com/jsp-interest@java.sun.com/ msg38573.html [3] Struts Release 2.2.3 Release: http://struts.apache.org/download. cgi#struts223 [4] Public Mailing List: http://struts.apache.org/mail.html [5] IntelliJ IDEA: http://www.jetbrains.com/idea/features/struts2.html [6] My Eclipse: http://www.myeclipseide.com/htmlpages-func-display-pid-444. html [7] NetBeans: http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage. jsp?pluginid=23467 [8] Tag Reference: http://struts.apache.org/2.2.3/docs/tag-reference.html [9] Model View Controller: http://en.wikipedia.org/wiki/Model%E2%80%93view% E2%80%93controller [10] JQuery Plugin: https://cwiki.apache.org/S2PLUGINS/2011/05/09/struts2jquery-plugin-version-300.html [11] Hibernate Plugin: https://cwiki.apache.org/S2PLUGINS/full-hibernate-plugin. html [12] Guice Plugin: https://cwiki.apache.org/S2PLUGINS/guice-plugin.html [13] OSGI Plugin: https://cwiki.apache.org/S2PLUGINS/osgi-plugin.html [14] REST Plugin: https://cwiki.apache.org/S2PLUGINS/rest-plugin.html [15] SiteMesh Plugin: https://cwiki.apache.org/S2PLUGINS/sitemesh-plugin.html [16] Struts 2 in Actio: http://amzn.to/rtnT8Q
Next steps
If youre ready to take Struts 2 for a spin, the simplest thing is to fire up your favorite editor or IDE, and start coding.
Vosao CMS
Extensible
The Vosao Content Management System (CMS) is a pure Java software product. And whats more its free, open source software. You can enhance the CMS with your own features and plugins.
Native
Scalability? Hosting? No worries! Vosao CMS was written from the ground up for the GAE platform, the world leader in remote hosting.
Complete
Create striking, interactive sites with standards based templates. Easily find and update pages with a content tree and visual editor. Instantly integrate with other sites, like Recaptcha and Disqus, with plugins from our growing library. http://www.vosao.org
35
ER
S RE COU CLOUD STO PON AU APP ICS T DIGITA GM LYT L PUBLISHING CONTEN ENT ANA ED R WI S EALITY DG L2 RVICE ETS LOCATION BASED SE HTM MOBI NCE HTM ING RIE LE WEB USER EXPE L5 ORK W PLAT NET FORM S VOIP BLUETOOTH
KIN DL E IOS
4G
AD NET WORK
N ET W
WI N D O W S P H
OR KS M
PAYM O B ILE
TS EN
ONE IPH
www.mobile360.com
Brought to you by
TIS
IN G
GO
OGL
ONE 7
AC F
PE LO E EV QR D
ID
NT E
TY
ETI
NG
S TABLETS APP
TI E
E M
NF
MA
RK
SM
A RT P H O N E
MO
NT O
EN
VID
NG
AT
E DI SOCIAL M
IPA
IL E B
EO
CH
AND
R OID
M ES
SA
S OL
UT
GI
ION
S
SO
AD
V
CI A