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

Beginning Apache Struts - From Novice To Professional (2006)

.pdf
Скачиваний:
56
Добавлен:
17.08.2013
Размер:
11.68 Mб
Скачать

318

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

Listing 20-4. Example of Subscriber Implementing MouseClickListener

public class MySubscriber implements MouseClickListener{

public mouseClicked(MouseClickEvent mce){ System.out.println(“x coordinate = “ + mce.getX()); System.out.println(“y coordinate = “ + mce.getY());

}

}

What about Publishers? Do they have to implement any interface? Usually not, but Publishers should have a function to add or remove event listeners. Subscribers would have to know these functions and call them; an example is shown in Listing 20-5.

Listing 20-5. Fragment of a GUI Widget That Generates Mouse Click Events

public class MyGuiWidget{

private Set _mouseClickListeners = new HashSet();

public addMouseClickListener(MouseClickListener mcl){ _mouseClickListeners.add(mcl);

}

public removeMouseClickListener(MouseClickListener mcl){ _mouseClickListeners.remove(mcl);

}

protected void broadcastMouseClick(int x, int y){ if(_mouseClickListeners.isEmpty()) return;

MouseClickEvent mce = new MouseClickEvent(x,y);

for(Iterator listeners = _mouseClickListeners.iterator(); listeners.hasNext();){

((MouseClickListener) listeners.next()).mouseClicked(mce);

}

}

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

319

//other functions that receive mouse clicks from the screen

//and then call broadcastMouseClick()

...

}

IS MOUSECLICKEVENT REALLY NECESSARY?

You might realize by now that the MouseClickEvent class is not strictly necessary. In fact, it’s possible to eliminate this class completely, and instead put in the X and Y coordinates as arguments of mouseClicked():

public interface MouseClickListener{

public mouseClicked(int x, int y);

}

Both approaches are valid, and JSF uses both, but most often the first.

Your Java application would hook up MyGuiWidget and MySubscriber(s) as shown in Listing 20-6.

Listing 20-6. Fragment of a GUI Widget That Generates Mouse Click Events

MyGuiWidget widget = new MyGuiWidget();

MySubscriber sub1 = new MySubscriber();

MySubscriber sub2 = new MySubscriber();

widget.addMouseClickListener(sub1);

widget.addMouseClickListener(sub2);

Now that you understand all this, you might be wondering how it relates to JSF. Well, at each phase (except Restore View and Render Response), events would be generated. These events might relate to the change in phase (PhaseEvent) or indicate that the user requires a certain action to be performed, such as navigating to a next page or calling a function on one of your classes (ActionEvent) or signaling that a UI component’s value has changed (ValueChangedEvent).

Also, as the sidebar points out, it’s possible to flag events without having an explicit “event” object. Validations and Model tier updates are done this way. Essentially, you

320

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

create listeners for each of these and somehow (you’ll see how shortly) register them with JSF. JSF will call these listeners at the appropriate phases.

So, the million-dollar question is how do we register listeners for the various events? In Listing 20-6, you can see how this might be done programmatically using the Java language. This is not the norm for JSF. Instead, you hook up the event producer with the event listener in the JSP page that specifies the UI tree.

We’ll postpone giving you an example of how this might be done until after we’ve addressed a few more loose ends.

JSF Tag Libraries

There are just two standard JSF tag libraries:

The Core tag library deals with JSF functionality that is independent of the way the View is rendered. Remember, JSF is very flexible, and it need not work with just HTML as its view medium. It’s possible to use JSF to render to Wireless Markup Language (WML), for example. The Core taglib contains, for example, tags to run validations or tags to load message resources.

The HTML tag library defines tags specifically describing the View tier, specifically for HTML output.

I'd like to remind you that the set of “standard” UI components and validators is fairly limited. To create complex webapps, you will likely want to extend JSF. There are two ways to do this:

Use third-party UI components and validators. MyFaces comes with a few of these.

Use a framework that specifically addresses deficiencies in JSF. Shale is one such framework.

Value and Method Binding

JSF introduces two very powerful techniques called value and method binding.

You’ve had some experience with value binding in Struts. Remember how you could read and write properties from your form bean in your Struts tags? For example, if you wanted an <html:text> to save a property called userId to a form bean called user, you’d do this:

<html:text property="userId"/>

where the user form bean is implicit. That’s what value binding is about. However, the JSF syntax is different:

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

321

<h:inputText value="#{user.userId}"/>

JSF uses an expression language syntax (see Chapter 10). Note that this is not the same as the JSTL “EL” syntax, since it begins with a hash symbol (#).

Method binding is an extension of this idea. It allows your JSF tags to call functions on your form bean (the JSF term is backing bean). For example:

<h:commandButton action="#{user.Logon}" />

describes a button that, if clicked, calls the Logon() function on the user backing bean. This technique is powerful because you can define multiple buttons on a single form,

and bind them to different functions, each to process the form data differently.

Quick Quiz

How would you do the same thing in Struts?

Because of this, backing beans are sometimes like ActionForm and Action rolled into one. When you read the JSF literature, you will certainly come across the term managed

bean. A managed bean is a backing bean whose instantiation is controlled by JSF.

This is analogous to form beans in Struts. The common characteristic of form beans (in Struts) and managed beans (JSF) is that the instantiation of both is performed by their respective frameworks.

The declaration of a managed bean in faces-config.xml (the JSF config file) is also different. Instead of

<form-bean name="user" type="net.thinksquared.reg.User" />

you’d say

<managed-bean> <managed-bean-name>user</managed-bean-name>

<managed-bean-class>net.thinksquared.reg.User</managed-bean-class> </managed-bean>

The JSF style is more verbose because rather than using attributes, it uses separate tags.

Navigation

In Struts, navigation information (<forward>s) is mixed with processing declarations (<action>s). JSF does a much better job at representing navigation information.

In fact, the whole JSF navigation model is much easier to understand and use compared to Struts. Instead of a specialized class like ActionForward, JSF uses plain Java Strings.

322

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

So, instead of returning the equivalent of ActionForward, the functions on your backing beans that perform navigation would return a String. These are called logical outcomes. As an example, consider Listing 20-7.

Listing 20-7. A Simple Navigation Rule

<navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case>

<from-outcome>register </from-outcome> <to-view-id>/register.jsp</to-view-id>

</navigation-case> <navigation-case>

<from-outcome>success</from-outcome> <to-view-id>/success.jsp</to-view-id>

</navigation-case> </navigation-rule>

This example defines a single navigation rule, with two logical outcomes: register and success. You can define as many navigation rules as you wish, and even add conditions like

<from-view-id>/index.jsp</from-view-id>

which tells JSF what the “previous” page must be in order for this <navigation-rule> to apply.

Example: The Registration Webapp

In this section, we’ll port our old friend the Registration webapp (see Chapter 5) to JSF. First, here are the specs:

There are three pages: one to perform user logon, one for user registration, and the last to indicate successful logon or registration.

The logon page includes the usual form containing user ID and password fields. The page also contains a link to the registration page. If the system detects that the user ID entered doesn’t exist, it forwards to the registration page.

The registration page is similar to the logon page, except that it contains an extra “confirm password” field.

As usual, the fields are validated before the input is accepted for delivery to the Model tier.

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

323

Caution This example does not show you the full power of JSF, nor does it show you how to use it efficiently. We’re using the Registration webapp simply because we’ve implemented it with Struts and Tiles.

I hope this gives you some mental scaffolding with which to pick up the new concepts in JSF. We’ll start with configuring JSF.

Configuring JSF

As with Struts, there are two files to use when configuring JSF: web.xml, the servlet configuration file, and faces-config.xml. Both of these should be in the WEB-INF directory.

The web.xml file contains information telling the servlet container what request extensions (like .jsf) should be shuttled to JSF, and where to store state (client or server). A web.xml file with a default setting is available in the MyFaces distribution. You should be able to use this file without any amendment.

The faces-config.xml file contains information declaring the <managed-bean>, <navigation-rule>, and other tags to plug in third-party UI components and validators, among other things. Listing 20-8 shows the faces-config.xml file for the Registration webapp.

Listing 20-8. faces-config.xml for the Registration Webapp

<?xml version="1.0"?>

<!DOCTYPE faces-config PUBLIC

"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">

<faces-config>

<managed-bean> <managed-bean-name>user</managed-bean-name>

<managed-bean-class>net.thinksquared.reg.User</managed-bean-class> <managed-bean-scope>request</managed-bean-scope>

</managed-bean>

<navigation-rule> <navigation-case>

<from-outcome>register</from-outcome> <to-view-id>/register.jsp</to-view-id>

</navigation-case> <navigation-case>

324

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

<from-outcome>success</from-outcome> <to-view-id>/success.jsp</to-view-id>

</navigation-case> </navigation-rule>

</faces-config>

We’ve declared a single managed bean called user. This bean both holds the user ID and password data and performs complex validation. Note that the user bean is declared to have request scope.

We’ve also declared a single navigation rule, mapping the registration page (register.jsp) to the logical outcome register and, similarly, the page displaying successful registration (success.jsp) to the logical outcome success.

Message Resources

Like Struts, JSF also supports internationalization by using message resource bundles. JSF’s support is much better than Struts. Instead of specifying the application.properties file in the configuration file, we can declare it in a JSP that uses the message resource.

This means that we can use multiple message resources. You’ll see this in action shortly. Listing 20-9 shows the message resource for the Registration webapp. The file is stored as net\thinksquared\reg\messages.properties, and will therefore be referred to as net.thinksquared.reg.messages.

Listing 20-9. Message Resource File for the Registration Webapp

#--------

error prompts --------

user_exists

= The userid {0} is taken. Please try another.

user_not_registered = You have not registered! Please do so now. wrong_password = Incorrect password. Please try again. password_mismatch = The passwords you keyed in don't match.

#--------

button text ----------

register

= Register

logon

= Logon

#--------

other

text ----------

title

=

Please log on

title_reg

=

Please register

new_user

=

Register as new user...

registered_ok =

You''ve registered as {0} with the password {1}!

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

325

logon_ok

= You''re logged on as {0}

user_id

= User ID:

password

=

Password:

password2

=

Re-type password:

Notice that the message for logon_ok begins with You''re. The repeated single quotes are not a mistake—it’s the way to “escape” the single quote.

The User Backing Bean

The user backing bean (which is also a managed bean) is used to store data (the user ID and password) and also has functions to log on an existing user or register a new user, as shown in Listing 20-10.

Listing 20-10. The user Backing Bean package net.thinksquared.reg;

import java.util.Map; import java.util.HashMap;

import java.util.ResourceBundle; import java.text.MessageFormat;

import javax.faces.context.FacesContext; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent;

public class User {

private static Map _dbase = new HashMap(); //our dummy database.

private String _uid,_pwd;

private UIComponent _container;

public User(){ _uid = null; _pwd = null;

_container = null;

}

326 C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

//------------------------------------- data get/set

public void setUserId(String uid){ _uid = uid;

}

public String getUserId(){ return _uid;

}

public void setPassword(String pwd){ _pwd = pwd;

}

public String getPassword(){ return _pwd;

}

//-------------------------------------

UI get/set

public void setContainer(UIComponent container){ _container = container;

}

public UIComponent getContainer(){ //we must return null,otherwise the //saved _container will be grafted //into the new UI tree.

return null;

}

//-------------------------------------

Actions

public String Register(){

//get a handle to the current "context"

FacesContext context = FacesContext.getCurrentInstance();

//get a handle to the desired properties file ResourceBundle bundle =

ResourceBundle.getBundle("net.thinksquared.reg.messages", context.getViewRoot().getLocale());

C H A P T E R 2 0 J A V A S E R V E R F A C E S A N D S T R U T S S H A L E

327

//complex validation: user exists? if(_dbase.get(_uid) != null){

Object[] params = {_uid};

String msg = MessageFormat.format( bundle.getString("user_exists"),params);

String clientId = _container.findComponent("userId").getClientId(context);

context.addMessage(clientId, new FacesMessage(msg));

//returning null causes input page to be re-displayed return null;

}

//everything OK - go ahead and register the user _dbase.put(_uid,_pwd);

Object[] params = {_uid,_pwd}; String msg = MessageFormat.format(

bundle.getString("registered_ok"),params);

context.addMessage (null, new FacesMessage(msg)); return "success";

}

public String Logon(){

//get a handle to the current "context"

FacesContext context = FacesContext.getCurrentInstance();

//get a handle to the desired properties file ResourceBundle bundle =

ResourceBundle.getBundle("net.thinksquared.reg.messages", context.getViewRoot().getLocale());

//complex validations:

//does the user exist?

//does the given pwd match the one in database? Object pwd = _dbase.get(_uid);

if(null == pwd){