打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Enterprise Java Community: JBoss Seam: A Deep Integration Framework
userphoto

2007.07.29

关注




Software frameworks are very useful tools for enterprise Java developers. They are widely used to package reusable software components and services. Each framework provides a set of design patterns, APIs, and component models, which can be used in applications built on top of the framework. Examples of popular Java EE frameworks include both open source projects such as Hibernate, Spring, Struts, etc., and standard-based products such as various implementations for Servlet/JSP, JSF, EJB, JMS, Web Services etc. A typical enterprise Java application can use more than a dozen frameworks at the same time. Hence, a key competence of Java EE developers is the skill to use those frameworks.

However, the problem with too many frameworks is that they each provides a different programming model (or component model). To make them work together in a single application, the developer typically needs to write a lot of "glue" code (e.g., data transfer objects, component management etc.) and a lot of "glue" configuration files, just to keep the frameworks happy. That is certainly counter-productive. That has given rise to a special kind of frameworks known as "integration frameworks", which are designed to reduce glue code and make various frameworks work together in a consistent programming model.

What is an Integration Framework

Java EE itself can be considered as an integration framework. It specifies how various frameworks, such as servlet/JSP, JSF, EJB, JMS, JTA, JCA etc., work together in an enterprise application. However, as a standard specification, Java EE evolves very slowly. It is very slow to fix design problems in Java EE. New ideas and cutting edging technologies often emerge as open source projects before they become part of the standard. Another widely used integration framework is the Spring framework. Spring provides a thin wrapper around other frameworks and allows application developers to use XML configuration files to manage components across the entire application. However, with Spring, the developer still needs to mix and match the different component programming models from the integrated frameworks. In addition, the XML-heavy approach in Spring produces very verbose applications with lots of "XML code".

JBoss Seam is an open source "deep integration" framework that tries to have the best of both the Java EE and Spring worlds. JBoss Seam is firmly rooted in Java EE standards. It started out to address the design flaws between two key Java EE frameworks: JSF and EJB3. Some of its core features are being adopted as future official Java EE standards such as JSF 2.0 and WebBeans. As more and more users start to adopt Seam, it has expanded far beyond the scope of Java EE.

Seam does integration in ways very different from earlier frameworks like Spring. Seam provides a unified component model for all frameworks it integrates. Developers typically works with the Seam components, instead of the component management API of each individual framework. For developers, the Seam API often represents a significant improvement from the framework it integrates. The Seam annotation / API design is very similar to Java EE 5.

The frameworks Seam integrates can stay behind the scene. In some case, it is possible to switch between competing frameworks without changing the Seam application itself -- the same way Java EE permits multiple implementations of the same API.

Now, all this integration talk must sound boring and abstract. So, let‘s look at a few examples to see exactly how Seam makes your life as a developer easier.

Use EJB3 Beans or POJOs to Handle JSF Events

One of the very first (and still compelling) feature Seam has is to use EJB3 (Enterprise JavaBean) components directly as JSF (JavaServer Faces) backing beans. JSF and EJB3 are both key elements in Java EE 5. However, the integration between the two frameworks are poor in Java EE 5. They have very different component models: JSF uses POJO-based "backing beans" to handle UI events, and makes heavy use of XML to configure those backing beans; EJB3 uses annotated POJOs for persistence and transactional logic and uses little XML. In order to perform a database operation from a JSF web page, you typically need to write a JSF backing bean to handle the UI event, and then invoke methods on the EJB3 session bean. If you need to pass complex data from the backing bean to the EJB3 bean, you will probably need to construct data transfer objects between the frameworks. In another word, it requires a lot of boilerplate code to keep the JSF and EJB3 frameworks working together.

Using Seam, you can directly use EJB3 beans as JSF backing beans. For instance, the following JSF page refers to a Seam component named "echo". When the user clicks on the Echo button, the page simply echos the input to all upper-case letters on the page.

My name is: <h:inputText value="#{echo.name}"/><br/>                        Hello #{echo.name} <br/>                        <h:commandButton type="submit" value="Echo"                        action="#{echo.toUpperCase}"/>                        

If you are not familiar with JSF, here is how the echo component works on the page: When the server renders the page, it replaces the #{echo.name} expression with the return value of the getName() method on the echo named object. When you click on the Echo button, the server first fills the text in the input field to #{echo.name} by calling the setName() on echo and then invoke the toUpperCase() method on echo. The #{...} style EL (Expression Language) is a key component in the Seam programming model as it can be used not only on web pages but also in any text environment, such as in XML configuration files, process / rules definitions, test scripts etc. We will see more on this later.

In plain JSF, the echo component would have been implemented as a JSF backing bean. With Seam, we can directly use a EJB3 bean (or a simple POJO) to implement this component. All you need is a @Name annotation to give the component a name as follows. When a web request comes in, Seam creates a EchoBean instance under name "echo", populates any input data to it, invokes action methods on it (when you clicks on the button), and finally destroys the component after the response is rendered. There is no XML code or object lifecycle management code to write.

@Stateful                        @Name("echo")                        public class EchoBean implements EchoInt {                        private String name;                        public String getName () { return name; }                        public void setName (String name) { this.name = name; }                        public void toUpperCase () { name = name.toUpperCase (); }                        @Remove destroy () {}                        }                        

Of course, for this simple use case, the EJB component is a overkill. EJBs have required interfaces, methods, and annotations. So, an easier way to code the echo component is to just write a POJO class for it. All you need on the POJO is the @Name annotation to specify its name.

@Name("echo")                        public class EchoPojo {                        private String name;                        public String getName () { return name; }                        public void setName (String name) { this.name = name; }                        public void toUpperCase () { name = name.toUpperCase (); }                        }                        

In Seam, annotated POJOs can almost completely replace EJB3 session beans. We will use Seam POJOs for the examples in the rest of this article.

Dependency Bijection

To really appreciate Seam‘s component management capabilities, we have to look at an example with multiple components interacting with each other. The following page shows a text field backed by an entity bean, a button backed by a POJO, and data table backed by a List object.

Your name: <h:inputText value="#{person.name}"/><br/>                        <h:commandButton type="submit" value="Say Hello"                        action="#{manager.sayHello}"/>                        <h:dataTable value="#{fans}" var="fan">                        <h:column>                        <h:outputText value="#{fan.name}"/>                        </h:column>                        </h:dataTable>                        

Below are the code for the Person entity bean and the Manager POJO.

@Entity                        @Name("person")                        public class Person implements Serializable {                        private long id;                        private String name;                        @Id @GeneratedValue                        public long getId() { return id;}                        public void setId(long id) { this.id = id; }                        public String getName() { return name; }                        public void setName(String name) { this.name = name; }                        }                        @Name("manager")                        public class Manager {                        @In @Out                        private Person person;                        @Out                        private List <Person> fans;                        @In                        private EntityManager em;                        public String sayHello () {                        em.persist (person);                        person = new Person ();                        fans = em.createQuery("select p from Person p")                        .getResultList();                        return null;                        }                        }                        

Notice that in the manager component, the person component is "injected" when the sayHello() method runs and the fans component is "outjected" when the method exits. Likewise, the JPA (Java Persistence API) EntityManager is also injected to provide access to the database. The injection and outjection ("dependency bijection") both happen in the current web request scope, so that the #{person.name} value from the user input is used by the sayHello() method, and the fans value can be displayed on the result page. Dependency bijection allows us to manage complex relationships among components using very simple annotations.

From the integration point of view, there are more than what meets the eyes here. Notice that the Person entity bean is directly used to back UI elements. In fact, the entity bean is just a Seam component. You can use it to back UI actions (e.g., button clicks) as well. That breaks down the arbitrary barrier between components from different frameworks (i.e., the entity bean and session bean). Furthermore, by supporting "business logic" methods in the entity bean, we can construct rich domain models that encapsulate both data and behavior. Rich domain model is how OO programming should have been done but it was nearly impossible in web frameworks before Seam.

Support JPA Lazy Loading in JSF

One of the most important features in an Object-Relational Mapping framework is to support "lazy loading" of associated objects. Without lazy loading, a simple object query could drag out a substantial chunk of the database into the result set, since all the tables could potentially be inter-linked in the mapped object graph.

For instance, in a web application that displays a list of orders, you might query for the Order objects in the controller. Then, when the page is rendered, you might find that you will need to show individual items in the order. Instead of loading all the associated objects into the Order objects from the query in the controller, we should lazy load the ordered items into the Order objects as the web page is rendered. However, lazy loading is not easy to implement in traditional MVC web frameworks. In a pre-Seam MVC framework, the controller opens a session in the persistence context to run the query in a transaction, and it closes the session when the transaction commits. There is no persistence context available when the web page is rendered after the controller exits, hence the persistence engine throws a "lazy initialization exception" if the view page tries to lazy load data.

To make lazy loading work, you can adopt a design pattern called "Open Session In View" -- to keep the persistence session open in the page rendering phase. However, it is often not trivial to implement this pattern as it requires a non-trivial amount of integration code between the web framework and the persistence framework.

Seam supports Open Session In View by default. All Seam components, with the exception of EJB3 Stateless Session Beans, are stateful. They keep an open persistence session from the moment when a database query is first executed in a UI event handler method until after the response page is fully rendered. In a Seam application, lazy loading just works without any additional code.

Support Hibernate Validators on JSF Input Pages

In a multi-tiered enterprise application, the web framework and the ORM persistence framework typically have different data validation mechanisms. The web framework validates user input when the wen form is submitted, and the persistence framework validates data before they are saved into the database. They are clearly redundant in most use cases.

Seam allows you to annotate data validation constraints directly on entity beans, and then automatically uses those validator annotations to constrain input data when the entity bean is used to back JSF input fields. For instance, the following example shows that the Person object‘s name must be two words and the its age must be between 3 and 100.

@Entity                        @Name("person")                        @Table(name="extperson")                        public class Person implements Serializable {                        private long id;                        private String name;                        private int age;                        @Id @GeneratedValue                        public long getId() { return id;}                        public void setId(long id) { this.id = id; }                        @NotNull                        @Pattern(regex="^[a-zA-Z.-]+ [a-zA-Z.-]+",                        message="Need a firstname and a lastname")                        public String getName() { return name; }                        public void setName(String name) {this.name = name;}                        @NotNull                        @Range(min=3, max=100,                        message="Age must be between 3 and 100")                        public int getAge() { return age; }                        public void setAge(int age) { this.age = age; }                        }                        

The following JSF page is automatically "wired" with the validation logic. If the user submits invalid values, the page would would automatically re-display itself with the invalid fields highlighted.

<s:validateAll>                        Your name:<br/>                        <s:decorate>                        <h:inputText value="#{person.name}"/>                        </s:decorate>                        Your age:<br/>                        <s:decorate>                        <h:inputText value="#{person.age}"/>                        </s:decorate>                        </s:validateAll>                        

The error highlight can be configured via simple JSF facets and CSS styles. You can add an image before the invalid field, surround it with a CSS box as follows. The <s:message/> displays the message attribute on the validator annotation when the validation fails.

<f:facet name="beforeInvalidField">                        <h:graphicImage styleClass="errorImg" value="error.png"/>                        </f:facet>                        <f:facet name="afterInvalidField">                        <s:message styleClass="errorMsg" />                        </f:facet>                        <f:facet name="aroundInvalidField">                        <s:div styleClass="error"/>                        </f:facet>                        

Using Ajax-based JSF controls, you can easily write input fields that validate itself without any form submission.

Many Ways to Ajax

Being a modern application framework, Seam provides first class support for Ajax applications. There are several ways to do Ajax in Seam. For starters, Seam nicely integrates leading Ajax JSF component suites such as Ajax4jsf, RichFaces and IceFaces. They provide Ajax-enabled JSF controls, such as input fields, data tables, interactive panels, drag and drop panels etc., which you can use directly on your web page. Those Ajax controls allow you to write Ajax web applications without a single line of JavaScript code. Seam takes care of the plumbing for those Ajax components, and makes them easier to setup than in the plain JSF environment. The example below shows Ajax data input fields. The values in the field is validated and invalid fields highlighted as soon as you click out of the field (i.e., the onblur event).

<s:validateAll>                        Your name:<br/>                        <a4j:outputPanel id="nameInput">                        <s:decorate>                        <h:inputText value="#{person.name}">                        <a4j:support event="onblur" reRender="nameInput"/>                        </h:inputText>                        </s:decorate>                        </a4j:outputPanel>                        Your age:<br/>                        <a4j:outputPanel id="ageInput">                        <s:decorate>                        <h:inputText value="#{person.age}">                        <a4j:support event="onblur" reRender="ageInput"/>                        </h:inputText>                        </s:decorate>                        </a4j:outputPanel>                        </s:validateAll>                        

For developers who‘d like to work with JavaScript directly, instead of JSF controls, Seam provides direct integration between JavaScript and server-side components. You can invoke methods on Seam components as if they are local JavaScript methods and use the return value locally. That has made it possible to integrate Seam services with popular JavaScript libraries. For instance, the following code shows that a Dojo "inline text editor" is backed by a Seam component. When you double click on the "Hello World" text, an inline editor opens up. After you finished editing, the new value is sent back to the server for processing.

<script language="javascript">                        // Seam.Remoting.setDebug(true);                        // don‘t display the loading indicator                        Seam.Remoting.displayLoadingMessage = function() {};                        Seam.Remoting.hideLoadingMessage = function() {};                        // Get the "manager" Seam component                        var manager = Seam.Component.getInstance("manager");                        function init () {                        var commentEditor = dojo.widget.byId("comment");                        commentEditor.onSave = submitComment;                        }                        function submitComment (newValue, oldValue) {                        manager.setComment (newValue);                        }                        dojo.addOnLoad(init);                        </script>                        <div id="comment" dojoType="inlineEditBox">Hello Seam</div>                        

Below is the code for the manager component that is accessed via the JavaScript remoting call.

@Name("manager")                        public class Manager {                        @In @Out                        private Person person;                        public void setComment (String comment) {                        person.setComment (comment);                        }                        ... ...                        }                        

Integrate Business Process in Web Applications

Business processes and rules are pervasively used in most enterprise applications. For instance, in a simple e-commerce web site, the customer logs in to go through the shopping process, the store manager logs in to execute the approval process, and the warehouse employee logs in to do the shipping process. Different actors have different roles to play and have different tasks available to them. They all work together to implement a business vision ("online shopping" in the above example).

In an enterprise application, the business processes and rules are often defined by business analysts. They are expressed in graphs that can be consumed in specialized software. The application developer implements those designs. However, since most web application frameworks have no integration with popular business process and rules engines, the developer often has to come up with home brew solutions, which are difficult for business analysts to review and verify. Seam provide superb support for business processes and rules through integration with jBPM and JBoss Rules (formerly known as Drools).

In a Seam application, you can specify a UI action (e.g., a button click) to trigger a business process. You just need to mark the UI event handler method with the @CreateProcess annotation. A business process is a series of tasks that has to be completed in certain order by various users. You can use @BeginTask and @EndTask to mark UI actions that start or end each task. Once a task is ended, the jBPM engines knows to move the process forward and make the next task available to its actor.

@Name("ticketSystem")                        public class TicketSystemAction {                        @CreateProcess(definition="TicketProcess")                        public String newTicket() {                        return "home";                        }                        @BeginTask                        public String reply() {                        return "reply";                        }                        @EndTask                        public String sendAnswer() {                        System.out.println("Answered");                        return "home";                        }                        }                        

Seam provides a set of components that let each user see his/her current task list and next actions to complete the tasks. The task list is generated based on the role of the currently logged in user, and it is tightly integrated into the Seam security framework for user authentication and authorization.

<h1>Assigned Tickets - #{login.user.username}</h1>                        <h:dataTable value="#{taskInstanceList}" var="task">                        <h:column>#{task.description}</h:column>                        <h:column>Title: #{ticket.title}</h:column>                        <h:column>                        <h:commandLink action="#{ticketSystem.reply}">                        <h:commandButton value="Reply"/>                        <f:param name="taskId" value="#{task.id}"/>                        </h:commandLink>                        </h:column>                        </h:dataTable>                        

The Seam jBPM / JBoss Rules integration allows developers to use Seam annotations and components to drive the business process and rules engine. There is no need to master the specific Java APIs for jBPM and JBoss Rules separately.

Use iText for Alternative View

The iText library is a widely used open source Java library for generating PDF documents. However, using its programatic API to construct a PDF document is time consuming (think building a XML document using DOM or construct a UI using Swing). Seam neatly ties iText, JSF, and Facelets together and allows developers to declaratively write PDF pages with dynamic content, just as you would do for JSF web pages. Furthermore, you can now use templates in PDF pages.

The key here is to construct a special XHTML tag library for PDF elements, and then transparently call iText when the page is rendered. Below is an example PDF page in a Seam application with digital signature support.

<p:document ... title="Why Seam? keywords="mykeyword"                        subject="seam? author="Seam Team? creator="Seam PDF example app">                        <p:image alignment="right" wrap="true" value="/jboss.jpg" />                        <p:font size="24"><p:paragraph spacingBefore="16" spacingAfter="40">                        Order #{currentOrder.orderId}                        </p:paragraph></p:font>                        <p:paragraph>Dear #{currentOrder.customerName},</p:paragraph>                        <p:paragraph>... </p:paragraph>                        <p:barCode type="code128" code="My BarCode" />                        <p:signature field="My Signature"  size="200 200 400 400"  />                        </p:document>                        

The integration here is so seamless that the view page has no dependency on iText. In fact, we can substitute iText with another commercial PDF library and the same view pages could still work. That is the power of Seam integration.

Advanced Job Schedulers

Support for automatically recurring tasks is important for many enterprise applications. In standard EJB, there is an EJB Timer API, which you can use to schedule recurring events at fixed interval. However, in real world applications, we often need more sophisticated scheduling service than fixed interval triggers. A popular open source Java scheduling library is the Quartz library. However, Quartz has its own APIs and object model that typically require "glue code" to fit into your own application.

Seam integrates Quartz to schedule asynchronous recurring jobs. You just need to annotate @Asynchronous on the method that executes the recurring task. You can pass in the job‘s start / end time, interval, and even a cron string as parameters. Those special-purpose parameters are annotated in the method definition as well. The return value is a QuartzTriggerHandler object, which you can use to stop or cancel the job later. You can even save the handler to database for later use.

@Asynchronous                        public QuartzTriggerHandle schedulePayment(                        @Expiration Date when,                        @IntervalCron String cron,                        @FinalExpiration Date stoptime                        ... any other call parameters ...) {                        // do the repeating or long running task                        }                        

The following example shows that schedulePayment() method is scheduled to run at 2:10pm and at 2:44pm every Wednesday in the month of March. You can place this call in a web UI event handler method so that the recurring event is scheduled when a button is pressed.

QuartzTriggerHandle handle =                        processor.schedulePayment(payment.getPaymentDate(),                        "0 10,44 14 ? 3 WED?,                        payment.getPaymentEndDate(),                        payment);                        payment.setQuartzTriggerHandle( handle );                        // Save payment to DB                        // later ...                        // Retrieve payment from DB                        // Cancel the remaining scheduled tasks                        payment.getQuartzTriggerHandle().cancel();                        

As in previous examples, there is no need to manually bootstrap the Quartz scheduler and then construct Quartz triggers and jobs. Everything just works when you annotate POJOs with Seam annotations.

The One Programming Model that Rules Them All

So far in this article, we have seen a few examples on how Seam integrates various frameworks together under a consistent programming model. There are many other frameworks Seam integrates. However, we do not have space in this article to dig into all those cases. To conclude this article, let‘s summarize the Seam programming model that unites all those frameworks. The key indegreins of Seam programming model are three artifacts:

+ Annotated POJOs: All Java components in a Seam application are in annotated POJO classes. Their interactions are managed by the dependency bijection facilities in Seam. There is no other component model in Seam.

+ XHTML view pages: All view (or UI) pages are written in XHTML files. In addition to standard JSF tags, Seam defines many of its own UI tags including the above discussed PDF UI tags. Seam also incorporates Ajax JSF component libraries such as Ajax4jsf, RichFaces, and IceFaces.

+ Expression language: The JSF Expression Language (EL) is used to reference Seam Java components from XHTML pages. Seam has enhanced the standard EL syntax to support method parameters etc. It has also expanded the use of EL to all XML configuration files and test scripts.

With all the cool features in Seam, the programming model is remarkably simple. It has a rather gentle learning curve after you understood the basics of JSF, as you can learn the integration piece by piece. Go download Seam now, play with the examples, and have fun coding!

Resources:

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
深入浅出JBoss Seam
JSF中Backing Bean的管理
JavaServer Faces (JSF) vs Struts
Wiring Your Web Application with Open Source Java
十一、 自訂轉換器
JSF VS Tapestry 全面比较(三)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服