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

Beginning Apache Struts - From Novice To Professional (2006)

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

298

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

public String getType(){ return _type;

}

}

Listing 19-16. Bean.java

package net.thinksquared.struts.dynaforms.definitions;

import java.util.*;

import org.apache.struts.config.FormBeanConfig;

public class Bean extends HashMap{

protected String _name = null; protected String _type = null; protected String _extends = null; protected boolean _create = true;

public void addProperty(Property p){ put(p.getName(),p);

}

public Property getProperty(String name){ Object obj = get(name);

return (obj == null)? null : (Property)obj;

}

public void setName(String n){ _name = n;

}

public String getName(){ return _name;

}

public void setType(String t){ _type = t;

}

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

299

public String getType(){ return _type;

}

public void setExtends(String e){ _extends = e;

}

public String getExtends(){ return _extends;

}

public void setCreate(boolean b){ _create = b;

}

public boolean getCreate(){ return _create;

}

/**

*Merges a bean with its parent.

*if the bean b is not a parent, then

*the merging is NOT done.

*/

public void merge(Bean b){

//don't merge if there is no common properties //between the beans.

if(_extends == null || !_extends.equals(b.getName())) return;

_extends = b.getExtends();

if(_type == null){

_type = b.getType();

}

Object[] properties = b.values().toArray();

300

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

for(int i = 0; i < properties.length; i++){ Property p = (Property) properties[i]; Object obj = getProperty(p.getName()); if(obj == null){

addProperty(p);

}else{

Property cp = (Property) obj; if(cp.getType() == null){

cp.setType(p.getType());

}

}

}

}

/**

*Gets the FormBeanConfig instance

*that this Bean represents

*/

public FormBeanConfig getFormBeanConfig(){

FormBeanConfig config = new FormBeanConfig(); config.setName(getName()); config.setType(getType());

return config;

}

}

Listing 19-17. Property.java

package net.thinksquared.struts.dynaforms.definitions;

import org.apache.struts.config.FormPropertyConfig;

public class Property{

protected String

_name = null;

protected String

_type

=

null;

protected String

_initial = null;

protected int

_size

=

0;

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

301

public void setName(String n){ _name = n;

}

public String getName(){ return _name;

}

public void setType(String t){ _type = t;

}

public String getType(){ return _type;

}

public void setInitial(String i){ _initial = i;

}

public String getInitial(){ return _initial;

}

public void setSize(int s){ _size = s;

}

public int getSize(){ return _size;

}

/**

*Gets the FormPropertyConfig instance

*that this Property represents

*/

public FormPropertyConfig getFormPropertyConfig(){

return new FormPropertyConfig(getName(), getType(), getInitial(), getSize());

}

}

302

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

Essentially, DefaultDynaFormsLoader’s getLoader() first reads all the form beans from the given file:

Beans beans = readBeans(filename);

The beans object is a collection of Bean objects (see Listing 19-16), which in turn may have Property objects (see Listing 19-17). The beans object corresponds to the <form-beans> root tag. The Bean class corresponds to a <form-bean> declaration, and the Property class corresponds to a <form-property> tag within <form-bean>.

At this point, each bean object in the bean’s container is unresolved, meaning that the bean’s inherited properties have yet to be determined. The next bit of code does just this:

for(Iterator beanz = beans.values().iterator(); beanz.hasNext();){ Bean b = resolve(beans, (Bean)beanz.next());

if(b.getCreate()){

config.addFormBeanConfig(createFormBeanConfig(b));

}

}

This section of code iterates through all bean objects in the bean’s container, resolving each one, then creating a FormBeanConfig instance from this resolved bean. Lastly, this

FormBeanConfig object is added to the ModuleConfig instance using addFormBeanConfig(). Note that

The resolve() function recursively merges the properties of a bean with that of an ancestor bean. The actual merging of properties is performed in bean.merge() (see Listing 19-16).

createFormBeanConfig() creates a FormBeanConfig object from a given resolved bean.

Form beans that are marked create=false (bean.getCreate() returns false) are not added to ModuleConfig.

The source code for the DynaForms plug-in is in the Source Code section of the Apress website at http://www.apress.com, in the file DynaFormsPlugIn.zip, and released under the General Public License (GPL).

Lab 19: Test Driving the DynaForms Plug-in

In this lab session, you’ll take the DynaForms plug-in for a test drive.

1.Copy the file dynaformstest.zip from the Source code section of the Apress website into your development directory and unzip it.

2.Besides the usual Struts JAR files in the lib folder, you’ll see the dynaforms.jar file, containing the DynaForms plug-in classes.

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

303

3.Take a look at the struts-config.xml file. Can you tell what file contains the new form bean declarations?

4.The dynaformstest webapp models the hierarchy of Listing 19-2. Compile and deploy the dynaformstest webapp. Play with the webapp and ensure that you understand how it works. Consult the source code for the DynaForms plug-in in

DynaFormsPlugIn.zip if necessary.

Extra Credit Lab: Handling <set-property>

In this lab, you’ll extend the DynaForms plug-in so that it handles <set-property> tags in form bean declarations. This tag is used to pass initialization parameters into custom

FormBeanConfigs or FormPropertyConfigs.

According to the Struts configuration file 1.2 DTD (available in your Struts distribution), there are two places <set-property> occurs within a form bean declaration:

As a child node of <form-bean>

As a child node of <form-property>

In both cases, Struts assumes that there’s a setXXX function corresponding to each

<set-property>, on the custom FormBeanConfig or FormPropertyConfig. This is similar to how Struts expects a setXXX function on the PlugIn implementation class for each <set-property> tag used in a plug-in declaration.

Of course, such setXXX functions won’t exist on either FormBeanConfig or FormPropertyConfig; they are assumed to exist on custom subclasses of these two.

By default, Struts will use the base config objects (FormBeanConfig or FormPropertyConfig) to hold form bean and form property configuration information. However, if you specify the className attribute in either tag, then the class referred to by className is used instead. For example, the information from

<form-bean name="ordinaryFormBean" ...

which doesn’t specify a className attribute, will be placed in the default FormBeanConfig. But the information from

<form-bean name="specialFormBean"

className="com.myco.myapp.MyFormBeanConfig" ...

will be placed in the class MyFormBeanConfig, which must be a subclass of FormBeanConfig. So, this declaration

304

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

<form-bean name="specialFormBean" className="com.myco.myapp.MyFormBeanConfig">

<set-property property="message" value="Hello World"/>

...

will cause Struts to instantiate MyFormBeanConfig, then call setMessage("Hello World") on this object.

Struts uses the Digester framework to call the appropriate setXXX functions on the MyFormBeanConfig object. Unfortunately, since we use our own custom Bean object to store the form bean data, we cannot take this approach.Instead, I’ll outline a possible solution, which I encourage you to implement.

Solution Outline

1.Add get/setClassName() functions to both the Bean and Property classes. This allows Digester to store the className information.

2.Add a HashMap called _properties to both the Bean and Property classes. This will store the property/value pairs from a <set-property>.

3.Create a new class called Parameter in the DynaForms definitions directory. This class corresponds to a <set-property> tag, and should have getters and setters for property and value.

4.Add identical addParameter(Parameter) functions to both the Bean and Property classes. This function should take the property/value data in a Parameter and put the data on the _properties HashMap.

5.Obviously, we want the <set-property>s to be inherited, too, just like <form-property>s. To do this, amend the merge() function on the Bean class so that it copies a property/value pair from the parent bean into the child bean, only if the property doesn’t exist on the child bean.

6.We need to get Digester to call the addParameter() functions of step 4 when it encounters a <set-property> tag. Amend DefaultDynaFormsLoader’s readBeans() function so that it calls Digester’s addObjectCreate() to create a Parameter object. We need to call this twice, for a <set-property> in each <form-bean> and in each <form-property>. We also need to call addSetProperties() twice, to populate the Parameter object with the property/value pair. Finally, we must call addSetNext() twice so that addParameter is called on Bean and Property.

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

305

7.Change the protected access of DynaFormsLoaderFactory.instantiate() to public access. We’ll use this function to instantiate custom subclasses of FormBeanConfig and FormPropertyConfig.

8.Modify getFormBeanConfig() on the Bean class and getFormPropertyConfig() on the Property class so that they return the correct FormBeanConfig/FormPropertyConfig subclass, based on the value of the className variable of step 1. Use the

DynaFormsLoaderFactory.instantiate() function to do this.

9.At this point, the returned config objects of step 8 are unconfigured, since we haven’t passed them the information from <set-property>s. You can use the Apache Commons Bean framework to do this. Like Digester, the JAR file (commons-beanutils.jar) comes bundled with Struts. The class you want to use is org.apache.commons.beanutils.BeanUtils. To populate the custom config subclass with the information from the _properties HashMap of step 2, you call

BeanUtils.populate(config, _properties), where config is the custom config subclass of step 8. Make this change to both getFormBeanConfig() on the Bean class and getFormPropertyConfig() on the Property class so that the returned config object is properly configured.

As usual, compile and test your work with your own custom FormBeanConfig or FormPropertyConfig classes, each with (for example) a message property. (Remember to make appropriate <set-property> additions to your form bean declaration file.) You can get the message to be displayed when the getMessage() function is called.

Useful Links

“Adding Spice to Struts,” parts 1 and 2 by Samudra Gupta: http://javaboutique. internet.com/tutorials/Dynaform/ and http://javaboutique.internet.com/ tutorials/Dynaform/index-6.html

A good article explaining how to use the Apache Digester framework, by Keld H. Hansen: http://javaboutique.internet.com/tutorials/digester/

Eclipse website: www.eclipse.org

The Struts website: http://struts.apache.org/acquiring.html#Source_Code

306

C H A P T E R 1 9 D E V E L O P I N G P L U G - I N S

Summary

Plug-ins, first introduced in Struts 1.1, are a good way to extend or amend the functionality of Struts.

A plug-in has to implement the org.apache.struts.action.PlugIn interface.

This interface has two functions: init(), which is called when the PlugIn implementation is instantiated, and destroy(), which is called when Struts shuts down.

Additional parameters may be passed to the plug-in before initialization using <set-property> tags in the plug-in declaration. Each <set-property> must have a corresponding setXXX defined on the plug-in implementation.

C H A P T E R 2 0

■ ■ ■

JavaServer Faces and

Struts Shale

In this chapter, we’ll take a look at a web application technology called JavaServer Faces (JSF) that will play a huge role in the Java webapp framework scene for many years to come.

We’ll also preview a newer technology called Struts Shale, which apart from the common moniker, shares little with Struts as you know it. Struts Shale, or Shale, for short, is the first among many new webapp frameworks that have a Struts heritage but embrace the advantages of JSF.

Besides these two, we’ll also give you a peek at the Struts-Faces integration library, which allows you to use JSF alongside “classic” Struts (that is, Struts as described in this book). Before we delve into the exciting details, I’d like to give you a bird’s-eye view of the two

main players in this chapter, JSF and Shale.

JSF Overview

JSF is a webapp framework whose focus is the View tier of the MVC design pattern.

In fact, JSF’s primary goal is to provide a standard architecture for web-based user interface (UI) components.

That’s quite a mouthful, and it might help to tackle the key words one at a time:

Primary goal: Because it is a fully functional webapp framework, JSF does a bunch of other things apart from the View tier, like control flow and validation. But don’t be fooled—it’s primarily about the View tier. I believe having this zeitgeist at the outset helps you understand JSF better. JSF and Struts are quite different things, and you would do well to avoid making premature comparisons between them.

Standard: JSF is not software. It is a specification (like the Servlet specification or the specification for JSP) that anyone can use to create an actual implementation. Two are available now: the JSF reference implementation from Sun, and Apache’s MyFaces, which we’ll use in this chapter.

307