Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Java.JDK.5.Edition (Wrox)

.pdf
Скачиваний:
39
Добавлен:
29.02.2016
Размер:
12.07 Mб
Скачать

Chapter 8

is often described in the context of Swing, so you may be wondering, “But these are Web applications, how could they have much to do with each other?” So, you should remember that the Model-View- Controller Architecture simply refers to breaking your system into distinct components to satisfy three concepts:

The Model refers to the real-world representation of your domain. For example, if I have a golf scoring system, I would have objects to represent things like a golf hole, a score, and so on.

The View refers to the ways that you view the data you are managing. For example, you may have a view of every player on a given hole, or you may have a scorecard for a given player over the whole course.

The Controller refers to the actual discrete actions that the system can perform. For example, “enter a score,” “generate a leaderboard,” and so on.

Of course, there are wide debates about where the divisions really exist — is your model just data objects and does your controller handle the business logic? For purposes of this book, just simplify it down to three basic concepts: The model is “what it is”; the view is “what it looks like”; and the controller is “what it does.”

So, how does the Model 2 Architecture actually work? Figure 8-1 demonstrates the Model 2 Architecture in action.

Contact

 

 

 

 

 

 

Phone

-lastName : String

 

 

 

 

 

 

 

-phoneNumber : String

-firstName : String

 

 

-im : String

 

 

-phoneType : String

 

 

-email : String

1

*

-id : int

-phones : Set

 

 

 

 

-expertises : Set

 

 

 

 

 

 

 

 

 

 

 

1

*

Expertise

-id : int

-title : String -description : String

Figure 8-1

366

Developing Web Applications Using the Model 2 Architecture

The Model 2 Architecture works like this:

1.

2.

3.

4.

5.

The Request comes into the Controller.

The Controller performs a given action with the provided parameters.

The Controller forwards control to the View in order to give the response.

The View refers to the domain model to build the presentation.

The View is passed back in the Response to the user.

A critical thing that is usually missed by a lot of people who look at this diagram is the concept of “scope.” Think of it like this. There are objects that are along for the ride, whether they are along for the duration of the request, the session, or the application.

Those are the key principles of Model 2 Architectures; now you will learn why the Model 2 Architecture is good for use in Web applications.

Why Use Model 2?

Now that you have a good sense of what the Model 2 Architecture is, you may be asking, “Why do I need this?” or “Isn’t that a lot of effort for a Web page?” Well, there are a number of significant advantages to the Model 2 architectures, particularly in large-scale applications. Here are several of the advantages of the Model 2 Architecture.

Advantage

Description

 

 

Flexibility

Model 2 is flexible because it separates your application into components

 

by their relevant piece of what they do. This allows you to plug in new

 

views or actions as needed without having to rewrite everything. You can

 

even reuse your components in other application platforms like Swing.

Reuse

Since Model 2 is componentized by definition, you can reuse a framework

 

to provide a lot of the glue that holds your application together. A couple of

 

examples of Model 2 frameworks are Apache Struts and OpenSymphony’s

 

WebWork.

Scalability

Because you have separated out the components, it is easy to add more

 

components where necessary. Plus, you can cache your data components

 

more easily because of the separation of concerns — your view doesn’t care

 

if it is handling a cached version of an object or a real version.

Security

By handling all actions through a central controller, you can easily config-

 

ure and manage access control to your data and actions.

 

 

However, there is no perfect solution. There are disadvantages to using the Model 2 Architecture, which are illustrated in the following table.

367

Chapter 8

Disadvantage

Description

 

 

Learning curve

You cannot use the Model 2 Architecture if you do not under-

 

stand it. Furthermore, if you want to reuse a framework, then

 

you must learn the particulars of that application. Of course,

 

you are reading this chapter, so this should be fairly well miti-

 

gated after you read all about WebWork.

Complexity

There are many things to learn about Model 2 Architecture in

 

order to use it effectively. Learning curves are different, but

 

compared to JSP, it can be quite intimidating to the average

 

Web developer who has been building page-centric database-

 

driven Web applications for quite a while.

Programming vs. scripting

Many Web developers are used to developing their applica-

 

tions interactively — as if they were scripting their Web site.

 

The concept of compiling and dependencies is simply some-

 

thing foreign to them, particularly if they came to Web devel-

 

opment out of graphical design rather than programming.

 

Now, you can still separate responsibilities among the team

 

and allow these scripters to handle the views of the applica-

 

tion, which are still conventional JSP.

 

 

The critical concept in deciding whether to go with Model 2 is to decide whether it is overkill or not. A rule of thumb could be that if you have more than five or six pages in your Web application, you really should use Model 2 Architecture. Note that this assumes that you will never have more than five or six pages, or that you are building a throwaway application.

The example application in this chapter deals directly with this issue of Model 1 versus Model 2. Too many explanations of Model 2 find it necessary to describe a system sufficiently complex to demonstrate the utility of Model 2, while ignoring the fact that the bigger the scope, the harder it is to wrap your mind around it. This application, a contact manager, would probably be a good candidate for Model 1, if it were not going to change or grow.

That is the fundamental distinction in Model 1 versus Model 2 — “Pay me now or pay me later.” Either way you are not really saving any effort with Model 1 unless you intend not to be around later — because the project is not expected to undergo further development (as opposed to some untimely demise).

Now that you understand the concepts, advantages, and disadvantages of the Model 2 Architecture, you will want to look at an implementation of a Model 2 Architecture. For this chapter, you will see a simple example of building a Model 2 application using the popular Web application framework called WebWork.

Developing an Application with WebWork

Building applications with the Model 2 Architecture is not very helpful if you have to build all of this additional glue code that provides the framework that implements the architecture. It is far better if you use a framework like Struts to implement Model 2. In this chapter, you will see one of the more popular emerging frameworks known as WebWork, or more specifically, WebWork2. WebWork is a Web application

368

Developing Web Applications Using the Model 2 Architecture

framework built upon a generic command framework that provides for modularizing code through a concept known as Inversion of Control (IoC). While WebWork could be used to build a Model 1 Web application, it is really geared towards being a great Model 2 framework.

What Is Inversion of Control and Why Is It Useful?

To explain Inversion of Control, you should be familiar with a couple of concepts that are widely used by software/system architects to categorize components and services of a bigger system. These categories are

Vertical. When a component or service is referred to as being vertical, it is focused on a business process. For example, a billing application would be a vertical application.

Horizontal. Conversely, a component or service that is horizontal provides something that is relevant to all of the vertical services and components. A security manager and database connection pool are examples of horizontal components.

What has become increasingly painful in developing enterprise applications, that is, vertical components, is interfacing to horizontal services and components. Outside of the obvious performance benefit, how much better is it to have to do a custom, configurable lookup of a database connection pool than just creating the connection yourself? Furthermore, you don’t want to have to account for all of the horizontal services in all of your application components, so you end up creating another layer of indirection on top of the horizontal service in order to provide the role for that service in your application.

Here is what that would look like in code:

package org.advancedjava.ch08; import javax.naming.Context; import javax.naming.InitialContext;

import javax.naming.NamingException; import javax.sql.DataSource;

public class LookupMethod { private DataSource ds; public LookupMethod() {

try {

Context initCtxt = new InitialContext();

ds = (DataSource) initCtxt.lookup(“/jdbc/DS”);

}catch (NamingException e) { e.printStackTrace();

}

}

/**

* @return */

public DataSource getDs() { return ds;

}

/**

* @param source */

public void setDs(DataSource source) { ds = source;

}

}

369

Chapter 8

Note in the constructor how you must go to the trouble to look up a component using the Java Naming and Directory Interface — and you tie yourself to that name. Now, this wouldn’t be such a big problem in such a limited circumstance, but consider that this class is not the start point for your application, rather it is just a component of the application. You may have many of these components, all looking things up for themselves.

What if you inverted the whole equation and you simply declared your need for a given horizontal service? This is what Inversion of Control does; it allows you to develop your application components independent of how they will actually be provided. You simply declare the need for a given component, and allow the framework to inject the dependency — that is, provide the needed component at run time

for you.

Instead, you could write it as what they call a Plain Old Java Object (POJO), which simply declares a member variable for the needed dependency and leaves the how and where of satisfying that dependency to the framework that runs it. So, your class would look something like this:

package org.advancedjava.ch08; import javax.sql.DataSource;

public class InjectorMethod implements Injector { private DataSource ds;

public InjectorMethod() {

}

/**

* @return */

public DataSource getDs() { return ds;

}

/**

* @param source */

public void setDs(DataSource source) { ds = source;

}

}

Note how you have implemented an interface known as Injector, to declare the method through which you expose your dependency. Here is what that interface looks like:

package org.advancedjava.ch08;

import javax.sql.DataSource;

public interface Injector {

public void setDs(DataSource ds);

}

By doing this, you are keeping your objects based more purely on solving the domain problems of your application, and deferring the context (setting up resources and finding them, and so on) to another part of the application. You have inverted the control, that is let the infrastructure call your code to do its specific part, rather than having your code and every other piece of code call the infrastructure to suit their needs.

370

Developing Web Applications Using the Model 2 Architecture

Before you stop reading under the pressure of having to understand every nuance of all these complex pieces and abstract concepts, realize that you are learning these things as the background to understanding how things work under the hood. The nice thing about frameworks is that they handle a lot of the heavy lifting for you.

Now that you have learned the foundation concepts of WebWork — Inversion of Control and Model 2 Architecture — you will get some background in the WebWork framework in particular.

Architecture

You don’t need to know every intricacy of WebWork in order to use it, but this text will provide you with some of the fundamental concepts so that you understand how it all fits together. Figure 8-2 demonstrates how the framework fits together.

Webwork Components

XWork

Command

Framework

Figure 8-2

Note how the WebWork components (JSP tags, servlets, listeners, and servlet filters) are really just Web extensions to the generic command pattern framework known as XWork. In fact, you could easily wrap your exact same XWork Actions with RMI or Web service interfaces, or even embed them within a Swing application. That is the key concept; you are only applying the Web context to your core POJOs that represent your domain.

The essence of WebWork (and XWork) is that you write basic Java objects called Actions, and then allow the framework to inject the dependencies that you need. By configuring interceptors, you can inject the dependencies that your Java object requires. Figure 8-3 shows how the request-response flow works in WebWork.

371

Chapter 8

Request

Interceptor A request processing

Interceptor B request processing

Interceptor n request processing

Action

Action result

Interceptor n response processing

Interceptor B response processing

Interceptor A response processing

Response

Figure 8-3

The request comes into the interceptor stack and is processed in the order of the interceptors until it gets to your developed Action class. Then, in reverse sequence, it makes its way back out through the response processing. This allows you to configure interceptors to provide facilities that are independent of the domain action, you can thus inject those dependencies, without burdening your Action with unneeded code.

Interceptors

Several Interceptors come with the WebWork framework; here is what each of them provides.

372

Developing Web Applications Using the Model 2 Architecture

Interceptor

Description

 

 

LoggingInterceptor

Provides a logging statement before and after the execu-

 

tion of an action. Helpful for tracing through the applica-

 

tion.

TimerInterceptor

Tracks the time taken to execute a given action. Useful

 

for isolating bottlenecks, particularly in multiple action

 

chains.

StaticParametersInterceptor

Maps the configuration parameters provided in your

 

xwork.xml to the Action.

ParametersInterceptor

Populates the Action with the parameters passed in with

 

the request.

ModelDrivenInterceptor

Unlike the other parameter interceptors, which only

 

apply parameters directly to the member variables in the

 

Action, this interceptor will allow you to map them into

 

more complex domain objects in your action.

ChainingInterceptor

Applies the result of the previous action to the next

 

action, useful for tying together multiple actions to form

 

a useful composite. For example, you may have an action

 

for calculating the sales tax which is chained to an action

 

that handles the totaling of the whole bill.

DefaultWorkflowInterceptor

If the Action implements Validateable, it will call

 

validate(). If the Action implements Validation-

 

Aware, it will call hasErrors() to see if there are any

 

registered error messages; if there are, it will return the

 

INPUT status. If neither of these occur, it will invoke the

 

Action execute method.

 

 

Of course, you can implement your own interceptors, and later in the chapter, you will see this in action, as it is used to provide support for Hibernate.

ValueStack

Remember how you learned earlier in the chapter about how your domain specific objects are “along for the ride?” The ValueStack is where they ride. Much like Java uses a stack to hold the relevant objects it uses within a given method or code block, XWork uses the ValueStack to accumulate the results of what has happened through your request’s life cycle. This is useful because the View components can simply build themselves using JSP (or Velocity) tags that interact with the ValueStack.

The way that your views interact with the ValueStack is through something called the Object Graph Navigation Language (OGNL).

OGNL

OGNL, pronounced like it would sound if you tried to say it, provides you with a useful and easy way to express how you retrieve objects from an object graph. It also does things like automatic type conversion. (You think to yourself, “Sure, the request sends all of its parameters as String objects and yes, the domain

373

Chapter 8

object takes an int, so why do I have to always tell it to execute Integer.parseInt()?”) In effect, it is very useful for providing simple expressions for manipulations of objects that otherwise take several lines of code.

Not only does the WebWork tag library make great use of OGNL for traversing the ValueStack, but WebWork also uses it to populate Action objects from request parameters.

Components

As you learned earlier, WebWork is built upon IoC. The first way that IoC is used is in injecting dependencies into Actions. However, WebWork also provides the ability to use a Component to inject resources into various scopes. You can configure your WebWork application to inject components into three different scopes:

1.Request. The Component will be attached to every user request, making it accessible to each Action.

2.Session. The Component will be attached when each new user session is created.

3.Application. The Component will exist throughout the life cycle of the application.

However, a practical example would probably make it easier to understand how components are used. Next, you will see how Hibernate is configured as a set of WebWork components to provide a complementary solution for persisting your objects.

Extending the Framework to Support Hibernate

The nice thing about Model 2 Architecture frameworks in general and WebWork in particular is that they are easily componentized and extended. One of the more exciting open source tools available online is the Hibernate tool, which provides object/relational persistence. Put more simply, it allows you to persist your objects more easily to a relational database than conventional SQL. For more discussion of the problem that Hibernate seeks to address, see Chapter 6: “Persisting Your Application Using Databases.

The genius behind Hibernate is a guy named Gavin King. King wanted to demonstrate how easily Hibernate pulled into a Model 2 Framework, so he created a sample application to provide user and role administration for Tomcat by linking Hibernate to WebWork.

In order to make it more focused, King’s example has been stripped down to the point that it would probably not be recognizable to him, but he still deserves credit for the concepts. Plus, you can refer to his application if you want to see a more sophisticated use of these techniques. It can be downloaded at hibernate.org.

The fundamental component in building your Hibernate applications is the HibernateFactory object. This object performs the setup and configuration of Hibernate, and then provides Session objects for users to interact with the framework. As you might imagine, you only need one of these for your application, any more would be excessive and inefficient.

So what if you could create one at startup time and share it among all of your applications? That is where WebWork components come into play. As demonstrated in Figure 8-4, WebWork will initialize a HibernateFactory at startup and inject that dependency into your application’s context.

374

Developing Web Applications Using the Model 2 Architecture

Web Container

starts the Web Web Application Application

WebWork injects Hibernate

Factory into the Application’s

Context

Figure 8-4

But WebWork doesn’t stop with just injecting a HibernateFactory into your application; it also provides the ability to inject a Hibernate Session object into every user request. Figure 8-5 demonstrates how a Session object is injected into the request object that is created to service a given request.

Web Container

creates Request Request Object Object to handle a

request

WebWork injects Hibernate

Session into the Request

Figure 8-5

Now that you have learned how WebWork components inject Hibernate into your Model 2 Architecture application, you will see how that comes in very useful.

Preventing the Hanging Session

A tough problem in matching up Model 2 Architecture applications with object/relational mapping tools is that they tend to have two common operating models that can work against each other:

A Model 2 application wants to conduct an action to retrieve a given graph of objects in the domain model and then forward it along to the view. In effect, it wants to disconnect from the database to prevent unnecessary binding. Also, it allows for clear packaging and handling of unanticipated actions in processing, namely exceptions and errors. Model 2 wants to consider the View part of the model to be about rendering data, independent of where it comes from.

An ORM tool (like Hibernate) tends to prefer deferring initializing all of the objects on a given graph until they are needed, to avoid performance problems, data latency issues, and so on. Basically, the more data you pull, the slower and less efficient your application can become. Furthermore, the longer it is disconnected, the less likely it is to be current.

375

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]