1. Trang chủ
  2. » Công Nghệ Thông Tin

Apress Pro Apache Struts with Ajax phần 9 docx

53 222 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 53
Dung lượng 0,91 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

As we promised, we will be showing you a better solution to this problem; however, before we con-tinue along the road with the RequestProcessor class, we want to show you how to provide

Trang 1

Now on to the implementation of JavaEdgeRequestProcessor:

public JavaEdgeRequestProcessor() {super();

helper = new RequestProcessorHelper();

}public void process(

HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {if(helper.checkMember(request, response)) {super.process(request, response);

}}}

First off, notice that this class extends org.apache.struts.action.RequestProcessor andalso that you need to define a private field to hold an instance of RequestProcessorHelper,which is actually instantiated within the constructor The important part of this class is theprocess()method Within process(), you use the RequestProcessorHelper class to check forthe existence of the MemberVO in the session and to create one as appropriate The importantthing to notice here is that if checkMember() returns true (that is, it executed successfully), thenyou allow the request chain to continue with a call to super.process() If the checkMember()method does not succeed, most likely because the anonymous user is not in the database andthere is no MemberVO in the session, then checkMember() sends the request elsewhere with thiscode:

Trang 2

checkMember() So to recap this, if you want to send the user somewhere else within the

process()method, do not call super.process(); simply set the response as appropriate

with RequestDispatcher and then allow the process() method to end The code for the

JavaEdgeTilesRequestProcessoris almost identical, other than the obvious difference in

parent class

That is the actual code; now on to how to configure it First off, you want to remove the

<filter>and <filter-mapping> tags for MemberFilter in the web.xml file so you can test the

processor Now all you need to do is change the controller definition within struts-config.xml

depending on which controller you plan to use If you want to use tiles, then you must have the

Tiles plug-in defined in the configuration file and you need to change the controller definition to

Verifying Host Access with RequestProcessor

Earlier on in the chapter we presented a solution to restrict access to the web application based

on the host address The solution used a customized base Action class to perform the check

before the execute() method of an Action was run There were two main problems with this

solution First, it required almost as much code to hook into the base Action as to perform the

check manually within each Action Second, it relied on developers to remember to derive their

actions from the correct base class and to call super.execute() before any other processing As

we promised, we will be showing you a better solution to this problem; however, before we

con-tinue along the road with the RequestProcessor class, we want to show you how to provide

custom configuration handling for your Action classes so you can then use this knowledge to

provide a much more reusable solution to the secure page problem

Creating Configuration Beans

If you have read this book from the beginning, it may not have escaped your notice that in

Chapter 2 we described the configuration attributes for the <action> tag and left one of them

with no more than a cursory description, the className attribute Well, now we’re going to

explain exactly what it is for

Struts uses another Jakarta project called Commons Digester to handle the reading of theconfiguration file and its transformation into a set of Java objects The Digester project actu-

ally started out as part of Struts, but proved so useful that the Struts team separated it out into

a entirely new project

Trang 3

The Struts team realized that extension capabilities in the world would be of no use without some way to provide additional configuration details for the extensions people werebuilding To this end, when integrating Digester into Struts, the Struts team left an extensionpoint in the configuration logic so that you can replace the configuration beans with your ownimplementation and provide them with additional parameters.

In this section, we are going to build a configuration bean that will be used in conjunctionwith the RequestProcessor code in the next section to provide pages that are accessible onlyfrom the host machine

Building the JavaEdgeActionMapping

As of Struts 1.1, the default configuration bean for an <action> node is the ActionMappingclass By extending this class, you can provide additional configuration data to your customRequestProcessors or actions

Okay, so that’s the theory behind it; now for an example In this example, we are going toshow you how to build a configuration bean that will allow you to specify whether or not aparticular action mapping should be restricted to the local host only

The code for the actual configuration bean is fairly basic, so here it is in full:

package com.apress.javaedge.struts.config;

import org.apache.struts.action.ActionMapping;

public class JavaEdgeActionMapping extends ActionMapping {

private boolean secure = false;

public JavaEdgeActionMapping() {super();

}public boolean isSecure () {return secure;

}public void setSecure(boolean isSecure) {secure = isSecure;

}}

The JavaEdgeActionMapping class derives from org.apache.struts.action.ActionMapping,which is the default configuration bean for <action> nodes Other than an explicit call to theparent constructor, the only thing this class has is a single boolean property, secure, with bothget()and set() methods

That’s all there is to the Java code Now all you need to do is add the appropriate ration details to the struts-config.xml file To secure the home page, you just set the classNameattribute to the full name of the custom configuration bean, in this case com.apress.javaedge

Trang 4

configu-struts.config.JavaEdgeActionMapping, and then use the <set-property> tag set the secure

<set-property property="secure" value="false" />

<forward name="homepage.success" path="/WEB-INF/jsp/homePage.jsp" />

</action>

Now when you request the home page from a remote machine, what happens? It is stilldisplayed At this point, all you have done is provide the configuration data, nothing more In

the next section, we are going to revisit the RequestProcessor classes, this time to implement

the processActionPerform() method to make use of this additional configuration data

Revisiting RequestProcessor

At this point, we have taken you through the basic mechanics of extending the RequestProcessor

class and through building custom configuration beans In this section, we are going to combine

that knowledge to build a much more comprehensive solution to the secure page problem that

was highlighted in the “Extending Action and ActionForm” section

To recap the last section, we showed you how to build a custom configuration bean thatallows you to specify whether or not a page should be restricted to being viewed on the host

machine, by setting the secure property accordingly Now you need to implement the code

within your RequestProcessor to read this configuration data and act appropriately

You already have the basics of the RequestProcessor classes in place for both Tiles-basedand non–Tiles-based applications All you need to do is extend these classes to provide the

desired features To start, implement the checkHost() method in the RequestProcessorHelper

class:

public boolean checkHost(

HttpServletRequest request,HttpServletResponse response,ActionMapping mapping) throws IOException, ServletException {

if (mapping instanceof JavaEdgeActionMapping) {JavaEdgeActionMapping jeMapping = (JavaEdgeActionMapping) mapping;

if (jeMapping.getSecure()) {String hostAddress = request.getRemoteHost();

if (!hostAddress.equals("localhost")) {RequestDispatcher rd =

Trang 5

return true;

}} else {// Not a secure action, allow access

return true;

}} else {// Not a secure action, allow access

return true;

}}This method is quite complex, so we’ll take you through it step by step First off is the list

of arguments the method accepts and its return type:

public boolean checkHost(

HttpServletRequest request,HttpServletResponse response,ActionMapping mapping) {You define the checkHost() method as returning a boolean, which will be true if the users areallowed access to the resource and false if they are not In this way, you indicate when calling themethod from your custom RequestProcessor class whether to allow Struts to carry on processing

or not As you can see, you include HttpServletRequest and HttpServletResponse arguments,along with an ActionMapping argument If you recall from the previous section, “Creating Configu-ration Beans,” ActionMapping is the default configuration class for all action mappings in theStruts framework As such, Struts will pass this argument to the RequestProcessor; it is up to you

to check it to see if it is actually your custom configuration bean, which is exactly what occurs asthe first line of code for this method

if (mapping instanceof JavaEdgeActionMapping) {

JavaEdgeActionMapping jeMapping = (JavaEdgeActionMapping) mapping; } else {

// Not a secure action, allow access

return true;

}

Trang 6

If ActionMapping is an instance of JavaEdgeActionMapping, then you cast it to that type,ready for additional checks; if not, then you assume that the resource the user is requesting is

not intended to be secure, so you return true, indicating that the user is allowed access If you

are dealing with an instance of JavaEdgeActionMapping, then you first check the return value of

getSecure()

if (jeMapping.getSecure()) {

String hostAddress = request.getRemoteHost();

if (!hostAddress.equals("localhost")) {RequestDispatcher rd =

return true;

}} else {

// Not a secure action, allow access

return true;

}

If getSecure() returns false, then although the configuration bean has been set toJavaEdgeActionMapping, the secure property is false, and this resource is intended for

public access If, however, getSecure() returns true, then you perform further checks to see

if the host name of the requesting user is localhost If the user is requesting from localhost,

then you return true to allow that user access; otherwise, you forward the request to the

accessDenied.jsppage and return false

As far as explanations go, that was pretty intense, so just to recap: checkMember() will bepassed an instance of ActionMapping If this ActionMapping instance is in fact an instance of

JavaEdgeActionMapping, then the method will perform further checks on the request;

other-wise, the method returns true If the ActionMapping argument is a JavaEdgeActionMapping

instance, then the method checks to see if the getSecure() method returns true or false If

getSecure()is false, then the user is cleared to view the resource and the method returns

true If getSecure() is true, then checkMember() checks the host address of the requesting user

If the user is requesting from localhost, then they are allowed access and checkMember()

returns true; otherwise the request is forwarded elsewhere and checkMember() returns false

Now all that is left for us to do is hook into the appropriate method in theJavaEdgeRequestProcessorand JavaEdgeTilesRequestProcessor classes If you use forwards

or includes in your application as well as normal actions, then you will actually want to hook

into three methods in the RequestProcessor: processActionPerform(), processForward(),

and processInclude(); since processActionPerform is only called for actions, includes and

Trang 7

forwards have their own methods to hook into Here is the code for processForward() and processInclude()taken from JavaEdgeRequestProcessor:

protected boolean processForward(

HttpServletRequest request,HttpServletResponse response,ActionMapping mapping)throws IOException, ServletException {

if (helper.checkHost(request, response, mapping)) {return super.processForward(request, response, mapping);

} else {return false;

}}protected boolean processInclude(

HttpServletRequest request,HttpServletResponse response,ActionMapping mapping)throws IOException, ServletException {

if (helper.checkHost(request, response, mapping)) {return super.processInclude(request, response, mapping);

} else {return false;

}}

As you can see, the methods are very similar, only differing in which method of the class they call internally The logic here is quite simple If checkHost() returns true, then theuser is allowed access to the resource, and the method defers control to the superclass; thisallows Struts to process as normal and will return the value of the corresponding method ofthe superclass However, if checkHost() returns false, then the method returns false to stopStruts from performing any more processing and causing an error, since the response hasalready been committed by the call to RequestDispatcher.forward() in the checkHost()method

super-The code for processActionPerform() does not differ that much:

protected ActionForward processActionPerform(

HttpServletRequest request,HttpServletResponse response,Action action,

ActionForm form,ActionMapping mapping)throws IOException, ServletException {

if (helper.checkHost(request, response, mapping)) {

Trang 8

return super.processActionPerform(

request,response,action,form,mapping);

} else {return null;

}}Aside from the noticeable increase in method arguments, the only difference here is thatthe method returns ActionForward instead of boolean So, as with the previous method, if the

user is allowed to view the resource, then control is passed to the superclass and the

appropri-ate result is returned from the method, resulting in Struts carrying on processing as normal

However, if the user isn’t allowed to view the resource, then the method returns null, which

will instruct Struts to stop any more processing, thus avoiding any errors

As you can see from the code examples, you don’t actually need very much code to buildyour own RequestProcessor and custom configuration beans If you follow the patterns for

managing the response, then you shouldn’t come across any errors in which Struts tries to

manipulate a response that you have already committed earlier on Just remember in this

situ-ation that if you send the request elsewhere, then you have to instruct Struts to stop

processing using the methods described

One point of interest before we move on to the recap is that the process() method executesbefore the processActionPerform, processForward(), and processInclude() methods In fact,

the process() method is responsible for calling those methods So in the case of the JavaEdge

application, the session will be checked for the MemberVO and have one added if appropriate

well before the user’s right to access the resource is verified You may find that this could have

an impact on your application, in which case you can move any logic from process() into

processActionPerform(), processForward(), and processInclude()

The last few sections have given you an in-depth look at how to extend the StrutsRequestProcessorclass and how to provide additional configuration data to your custom

RequestProcessorclasses using custom configuration beans The next section describes the

fourth and final method of extending the Struts framework

Building a Plug-In

Perhaps the most widely known method of extension in Struts is the plug-in method In fact,

many of the additional features for Struts, such as Validator and the Tiles framework, use

plug-ins to add their functionality to the framework Building a plug-in differs from building a

RequestProcessorin that you are not intercepting each individual request; instead, you are

hooking into the framework as it loads Generally, plug-ins are used to load in some kind of

application-specific data or to perform startup actions that are needed to ensure that the

application will run correctly We have also found that using plug-ins is an ideal way to run

some background processes within the context of the servlet container, without having to

fudge some kind of solution in which you have a scheduled task on the OS that requests a

specific URL at a specific interval

Trang 9

In this section, we are going to take you through the entire process of building a plug-inand configuring it within the Struts framework The plug-in we are going to show you how tobuild will send out an e-mail newsletter of the top stories to all members in the JavaEdgeapplication at a set interval.

Newsletter Service Basics

Before we get to the details of the actual plug-in implementation, we want to discuss the exactbehavior of the Newsletter Service and look at the classes that actually implement the servicebefore demonstrating how to hook these classes into Struts with a custom plug-in implemen-tation

The basic aim of the Newsletter Service is to send the list of top stories, via e-mail, to eachmember registered in the JavaEdge database The logic for loading the top stories from thedatabase is already built and is explained in Chapter 2 On top of this, we want to make theinterval between sending the e-mails, the address of the SMTP server used, and the senderaddress of the e-mail externally configurable so they can be changed without having to goback to the code

Thankfully, the Struts plug-in model makes it quite easy for you to create the tion that you want As you will see, building the logic for the actual Newsletter Service is muchmore complex than building the plug-in

implementa-NewsletterManager

When you are building a plug-in, you should really consider refactoring the logic that plug-in

is intended to perform into a separate class If you have to use the logic elsewhere or for somereason you want to move from Struts to another technology, then you will have a much easiertime of it For the Newsletter Service, you create the NewsletterManager class to take care ofthe newsletter construction and the sending of e-mails

The code for NewsletterManager is quite long, so we will go through each method rately, instead of giving one big block of code and attempting to explain it in one go The basicclass looks like this:

sepa-public class NewsletterManager {

private static Log log =ServiceLocator.getInstance().getLog(NewsletterManager.class);

private String _smtpServer = "";

private String _fromAddress = "";

public NewsletterManager(String smtpServer, String fromAddress) {_smtpServer = smtpServer;

_fromAddress = fromAddress;

}}

Notice that you define a Commons Log instance so that you can log any errors that occurwhen trying to build or send the mail Also note that there are two private fields to hold theaddress of the SMTP server and the e-mail address to use as the sender address for the outgoing

Trang 10

mail The class has a single constructor that is used to pass in values for the _smtpServer and

_fromAddressfields

The class contains one public method, sendNewsletter(), which when called by the clientapplication will build the newsletter and send it via e-mail to the JavaEdge members:

public void sendNewsletter() throws ApplicationException {

String mailContent = getNewsletterContent();

Session mailSession = getMailSession();

Collection recipients = loadRecipients();

Message msg = new MimeMessage(mailSession);

try {// From addressAddress fromAddress = new InternetAddress(_fromAddress);

msg.setFrom(fromAddress);

// Subject linemsg.setSubject("JavaEdge Newsletter");

// Body contentmsg.setText(mailContent);

// Recipient addressesIterator iter = recipients.iterator();

while(iter.hasNext()) {MemberVO member = (MemberVO)iter.next();

if(member.getEmail().length() > 0) {Address bccAddress = new InternetAddress(member.getEmail());

msg.addRecipient(Message.RecipientType.BCC, bccAddress);

}}// Send

Transport.send(msg);

} catch (AddressException e) {log.error("AddressException in NewsletterManager", e);

throw new ApplicationException("AddressException in NewsletterManager", e);

} catch (MessagingException e) {log.error("MessagingException in NewsletterManager", e);

throw new ApplicationException("MessagingException in NewsletterManager", e);

}}

Trang 11

The first line of this method creates the content for the newsletter with a call to getNewsletterContent():

private String getNewsletterContent(){

// Load the top stories

IStoryManager manager = StoryManagerBD.getStoryManagerBD();

Collection stories = manager.findTopStory();

// Now build the content

StringBuffer buffer = new StringBuffer();

// Headerbuffer.append("Dear Member,\n\n").append("Here are the top stories from the JavaEdge web site:\n\n");// Body

Iterator iter = stories.iterator();

while(iter.hasNext()) {StoryVO story = (StoryVO)iter.next();

buffer.append("***************************************************\n").append(story.getStoryTitle())

.append("\n").append(story.getStoryIntro()).append("\n")

.append("<http://localhost:8080/JavaEdge/execute/storyDetailSetup?storyId=").append(story.getStoryId())

.append(">").append("\n");

}// footerbuffer.append("***************************************************");

return buffer.toString();

}The getNewsletterContent() method retrieves the list of top stories from the JavaEdge database with a call to IStoryManager.findTopStory() Once the list is loaded, the getNewsletterContent() method builds the newsletter content in a StringBuffer object

Note In a real application, you would probably use something like Jakarta Velocity to externalize the mailcontent and make maintenance much easier For more information on Jakarta Velocity, see Chapter 10

Trang 12

The content itself is fairly basic: Each story has the title and intro listed along with the linkneeded to launch the JavaEdge application on the local machine with the appropriate story

displayed Back to the sendNewsletter() method, the next line constructs a mail session with acall to getMailSession():

private Session getMailSession() {

// Set propertiesProperties mailProps = new Properties();

mailProps.put("mail.smtp.host", _smtpServer);

return Session.getDefaultInstance(mailProps);

}This method is very basic—it simply sets the required property for the SMTP server usingthe value stored in the _smtpServer field, and then returns a standard instance of javax.mail

Sessionconfigured with the SMTP server address Back in the sendNewsletter() method, the

next line retrieves a collection of MemberVO objects representing the entire list of members in

the JavaEdge application with a call to loadRecipients():

private Collection loadRecipients() throws ApplicationException {

MemberManagerBD manager = new MemberManagerBD();

Collection result = null;

result = manager.getAll();

return result;

}The loadRecipients() method simply gets the list of recipients with a call to MemberManagerBD.getAll()

Note If you choose to implement something like this in your own application, you will probably have to

implement some kind of opt-in/opt-out feature, as most people expect it and many countries now require it

by law

The MemberManagerBD.getAll() method was not created in previous chapters, so we haveincluded it here:

public Collection getAll() throws ApplicationException{

MemberDAO dao = new MemberDAO();

try {return dao.findAll();

} catch(DataAccessException e) {

Trang 13

log.error("Error in MemberManagerBD.getAll()", e);

throw new ApplicationException(

"An application exception has been raised in MemberManagerBD.getAll()",e);}

}And here is the code for findAll():

public Collection findAll() throws DataAccessException {

log.debug(

"********************Starting MemberDAO.findAll()********************");PersistenceBroker broker = null;

Collection result = null;

try {Query query = QueryFactory.newQuery(MemberVO.class, new Criteria());broker = ServiceLocator.getInstance().findBroker();

result = broker.getCollectionByQuery(query);

} catch(ServiceLocatorException e) {log.error("ServiceLocatorException thrown in MemberDAO.findAll()", e);throw new DataAccessException(

"DataAccessException error in MemberDAO.findAll()", e);

} finally {if(broker != null) broker.close();

}log.debug(

"******************Leaving MemberDAO.findAll()*****************");

return result;

}You will find an explanation of the code in these methods in Chapters 4 and 5, respectively.Back in the sendNewsletter() method, the newsletter content has now been created, a mail ses-sion instance has been created, and the list of recipients has been loaded from the database Thefinal block of code in the sendNewsletter() method builds a MimeMessage instance, populates thesender address and subject fields, and adds a bcc recipient for each MemberVO loaded from thedatabase with a valid e-mail address Once the body content is added to the MimeMessage, all thatremains is for the e-mail message to be sent with a call to Transport.send()

Notice that all exceptions generated by JavaMail are caught, wrapped, and rethrown asApplicationExceptions This will simplify the exception-handling code within the client code

It also means that if you want to use a different mail implementation than JavaMail, you can

do so without having to worry about editing client code to capture additional exceptions

Trang 14

Since the newsletter will be sent automatically at set intervals, you need some way to schedule

a task to execute the NewsletterManager.sendNewsletter() method at the appropriate time As

of version 1.3, Java has included the Timer and TimerTask classes to allow for scheduled tasks

By deriving a class from TimerTask and implementing the run() method, you can build a task

class that can then be scheduled to run using the Timer class For the Newsletter Service, you

need to build the NewsletterTask class, which implements TimerTask.run() to create an

instance of NewsletterManager and call its sendNewsletter() method:

public class NewsletterTask extends TimerTask {

private static Log log =

ServiceLocator.getInstance().getLog(NewsletterTask.class);

private NewsletterManager manager = null;

public NewsletterTask(String smtpServer, String fromAddress) {manager = new NewsletterManager(smtpServer, fromAddress);

}public void run() {log.info("Newsletter.run() started");

try {manager.sendNewsletter();

} catch(ApplicationException e) {log.error("Could not send newsletter", e);

}log.debug("Newsletter.run() completed");

}}

Trang 15

Notice that the constructor for the NewsletterTask class accepts the same set of arguments

as the NewsletterManager class, and in fact simply uses the arguments to create its own internalinstance of NewsletterManager In the run() method, you log the start and end of the method toease debugging and wrap the sendNewsletter() call in a try/catch block You don’t want anyexceptions to escape the run() method; instead, they are all caught and logged If you don’tcatch and log the exceptions here, Struts will do it anyway, so you can’t crash your applicationwith a plug-in; but you want to be able to reuse this task in any framework, and you cannot rely

on that behavior existing in every framework

The destroy() method will be called whenever your application is stopped or your cation server shuts down, provided that this occurs via the normal process and not because of

appli-a crappli-ash You cappli-an use the destroy() method to teappli-ar down appli-any resources thappli-at you happli-ave open in

an orderly manner, but you cannot guarantee that this method will actually execute

The init() method will always be executed each time your application starts up You can

be sure that the init() method will have executed before any actions are processed since it

is indirectly called via the ActionServlet.init() method The init() method is passed twoarguments: a reference to the ActionServlet instance for your application, and a reference tothe ModuleConfig instance for your application that contains all the configuration data for theentire Struts application and can be used to get the properties specified for your plug-in Sinceall the logic for actually sending the e-mail is contained within the NewsletterManager class,the NewsletterPlugIn class simply has code to read in the configuration data and to initialize aTimerwith the NewsletterTask class

public class NewsletterPlugIn implements PlugIn {

private static Log log =

Trang 16

private Timer timer = null;

private long intervalFactor = 1000 * 60;

private long interval = (60 * 72);

private String smtpServer = "localhost";

private String fromAddress = "javaedge@apress.com";

public void init(ActionServlet servlet, ModuleConfig config)throws ServletException {

log.info("NewsletterPlugIn.init() called");

loadConfigData(config);

startTimer();

}public void destroy() {log.info("NewsletterPlugIn.destroy() called");

}private void loadConfigData(ModuleConfig config) {PlugInConfig[] pluginConfigs = config.findPlugInConfigs();

for(int x = 0; x < pluginConfigs.length; x++) {if(pluginConfigs[x].getClassName().equals(this.getClass().getName())) {log.debug("Found Plug-In Configuration");

Map props = pluginConfigs[x].getProperties();

// Load in the interval property

if(props.containsKey("interval")) {try {

interval = Long.parseLong(props.get("interval").toString());

log.debug("Interval set to: " + interval);

} catch(Exception ignored) {log.debug("Specified Interval was not a valid log value");

}}// Load the smtp server property

if(props.containsKey("smtp.server")) {smtpServer = props.get("smtp.server").toString();

Trang 17

log.debug("smtpServer set to: " + smtpServer);

}// Load the from address property

if(props.containsKey("fromAddress")) {fromAddress = props.get("fromAddress").toString();

log.debug("fromAddress set to: " + fromAddress);

}break;

}}}private void startTimer() {timer = new Timer();

long timerInterval = (interval * intervalFactor);

timer.schedule(

new NewsletterTask(

smtpServer, fromAddress),timerInterval, timerInterval);

}}

The NewsletterPlugIn class has a variety of field variables to store the configurationdetails for the NewsletterManager, the Commons Log instance used for logging within theclass, and the Timer instance that the plug-in will use to schedule the sending of the newslet-ter Notice that you also need to define a private field, intervalFactor The reason for this field

is that in the configuration you want to be able to specify the interval between newsletters inminutes, but the Timer works in milliseconds The intervalFactor stores the number of mil-liseconds in a minute and is used to convert the interval value from the configuration intomilliseconds for the Timer Both the init() and destroy() methods write log entries to enableyou to verify that the plug-in is actually being loaded into Struts The init() method loads theconfiguration data with a call to loadConfigData() and then starts the Timer with a call tostartTimer()

For loadConfigData(), the init() method passes in the ModuleConfig for the Struts cation The ModuleConfig object contains the configuration details for the entire application,not just the plug-ins To get at the plug-in configuration, you need to call ModuleConfig.findPlugInConfigs()to get an array of PlugInConfig objects, one for each plug-in configuredwithin the application You can then loop through this array to find the PlugInConfig objectfor your plug-in by comparing the getClassName() property of the PlugInConfig object withthe full name of your plug-in class Once you have the correct PlugInConfig object, you cancall getProperties() to retrieve a Map of the configuration properties specified for the plug-in.With the Map of configuration properties retrieved, getting the configuration data is a simplematter of accessing the elements in the Map The loadConfigData() method follows this pattern

Trang 18

appli-and reads in three properties from the configuration: one for the SMTP server address, one for

the sender address, and one for the interval between newsletters

The last method of the NewsletterPlugIn class is startTimer() This method doesn’t reallyhave much to do other than to create the Timer instance and then schedule the NewsletterTask

to run In the call to Timer.schedule(), you will notice that the interval is specified twice The

first interval is the delay before the Timer runs the task the first time, and the second interval is

the delay between runs thereafter This means that you set the task to run five minutes after the

plug-in starts and then maybe once an hour after that

As you can see, the actual plug-in code is very simple; the main bulk of the code for thisplug-in was the logic required to actually send the newsletter Creating the plug-in and start-

ing the Timer requires very little code—in fact, the largest amount of code for the plug-in is

the configuration load code All that remains now is to configure the plug-in within the Struts

application

Configuring the Plug-In

If you have read either Chapter 6 or 7, then you will no doubt recognize the syntax used to

configure the plug-in To configure the basic plug-in, you simply need to add the following

entry to the struts-config.xml file:

<plug-in className="com.apress.javaedge.struts.plugin.NewsletterPlugIn"/>

This will run the plug-in with the default set of parameters Since you have specified thedefault period between newsletters to be 72 hours, you need to specify a much smaller period

than this for debugging Also, you don’t use localhost as the SMTP server, so you can use the

configuration properties to set the values without having to change the code

<plug-in className="com.apress.javaedge.struts.plugin.NewsletterPlugIn">

<set-property property="smtp.server" value="tiger"/>

<set-property property="interval" value="1"/>

</plug-in>

As you can see, configuring the plug-in is very easy, and you are free to configure as manyplug-ins as you like within your application

Summary

Throughout this chapter, we have taken you through various extension mechanisms for the

Struts framework, each with its own distinct advantages and disadvantages First we presented

a simple solution for providing typed access to session parameters using a base Action class,

and also a method of securing resources using the Action class From this discussion, you

have seen that extending the Action class does not provide the most flexibility when extending

Struts, nor does it reduce the amount of code needed to implement simple tasks in every

action

Next, we introduced custom RequestProcessor classes and how they can be used to hookinto the request processing flow for Struts We combined the knowledge of RequestProcessors

gained earlier in the chapter with that of building custom configuration beans in order to

provide a much more elegant solution to the problem of secure pages From this, we have

Trang 19

demonstrated that for the most part using a custom RequestProcessor is a much more able solution than using a custom Action class when you want to hook into the request flowwithin your Struts application.

desir-Lastly, we focused on providing applicationwide startup or background services using the Struts plug-in model The plug-in model is a very simple mechanism for you to provideservices within your application that run outside the context of a user request Any automatedprocesses such as cleanup operations, marketing, or auditing that you would normally do viasome kind of OS scheduled task can now be done using a Struts plug-in

Trang 20

Struts and Ajax

Ajax, or Asynchronous JavaScript and XML, was introduced in 2005 by Jesse James Garrett,

sometimes referred to as the “father of Ajax.” Ajax is not a single technology; rather, it is a

col-lection of concepts and technologies that allow richer, more interactive user interactions with

web applications The term Ajax has now grown to refer to any native browser technologies

that allow for asynchronous communication with a back-end server

The fundamental concept behind Ajax is that when a portion of a web page changes, theentire page does not need to be refreshed For example, when a user selects a country from a

Country drop-down list, the States drop-down list is automatically populated with the

appro-priate list of states for that country In a typical web application, this would require a round

trip to the server and a page refresh Using Ajax, the round trip to the server is done

asynchro-nously and only the section of the page is refreshed behind the scenes The fundamental

technologies that allow this to happen are XML, JavaScript, and XHTML

In this chapter, we will expose you to the new Ajax technology that takes web applicationdevelopment to a completely new level We will show you how Ajax can be used in your Struts

applications Let us first describe what Ajax is in a little more detail

Ajax Dissected

The basic technology behind Ajax is JavaScript It allows

• Data to be exchanged with a server using XML or other technologies such as JavaScriptObject Notation (JSON)

• Dynamic display of new or changed data using DHTML and the Document ObjectModel (DOM)

• The use of data display standards such as Cascading Style Sheets (CSS)Let’s look at a few examples of applications in which Ajax is being used today, just to giveyou a flavor of what Ajax can really do

421

C H A P T E R 1 2

■ ■ ■

Trang 21

Ajax on Google

Of course, as one might expect, Google is one of the biggest users of the new Ajax technologies.Google Gmail, Google Calendar, and the Google Personalized Home page are some prime exam-ples of web applications that implement Ajax

Google Calendar, for example, uses Ajax to quickly add and update calendar entries

If you use Gmail, it uses Ajax to display the little “loading” text in the top-right corner

Ajax on Yahoo

Yahoo’s new home page also implements Ajax A lot of personalization capabilities, and featuressuch as quick preview of e-mail, have been added to it recently, using the Ajax technologies

Where Should I Use Ajax?

Here are several ideas about where Ajax might be worth using:

• Forms: This is a given Web-based forms are slow! It should be a no-brainer to see how

and why Ajax can dramatically improve the performance of web-based forms

• User communications: Ajax can be a very useful technology in designing user

commu-nication features such as chat pages, voting buttons, message threads, ratings, etc Anexample of this sort of feature is the Netflix movie ratings buttons

• News: RSS feeds is another popular concept that can really leverage Ajax technologies

A few examples of this have emerged on the web recently, such as Google News

• Data manipulation: An example is sorting or filtering on columns in a table Another

example is form completion with hints, such as the Google Suggest feature (you will seesome code for this later in the chapter)

Note Ajax should not be thrown at every problem Replacing large amounts of data with Ajax can lead toperformance and other issues Use Ajax only when traditional JavaScript widgets don’t suffice, and when youhave to do data manipulations involving round trips to the server

Here is a good blog you can read to find out when not to use Ajax: http://alexbosworth.

backpackit.com/pub/67688

Trang 22

Ajax and Web 2.0

The Internet has grown exponentially in the last decade Web 1.0 is and was the era of primarily

static web sites transforming themselves to business processes/dynamic web applications,

con-tent management driven sites, and more recently portals Even in the best of portals, there is still

some level of intermixing between the layers (presentation, logic, business process, and so forth)

Web 2.0 is the new buzzword This concept is truly separating out the presentation fromthe business logic Ajax is one technology that really enables this vision—by allowing the pres-

entation to be driven by asynchronous calls to the server for data Web Services technologies

and Service Oriented Architecture (SOA) make this vision even easier to implement

Ajax and SOA

So what does Ajax have to do with SOA? Ajax allows pieces of a web page to be asynchronously

refreshed with new data This data is typically retrieved by making a call to some back-end

server, such as a WebLogic or Tomcat server The code running behind the scenes can be

non-service oriented and this would still work However, if implemented as non-services, the boundaries

for the use of Ajax become close to limitless It opens up a whole new level of data presentation

options and gives birth to a new generation of aggregated portal capabilities

We have spent some time going over the basics of Ajax—what it is and what is does Let’sdive in and talk technology In the next section, we explore the internals of Ajax

Ajax Internals

Ajax is not a single technology, as mentioned earlier in this chapter It is important to remember

that Ajax is not Java or NET dependent You can write Ajax code (in JavaScript) to communicate

with any sort of back-end code—Java, NET, PHP, or just about anything else From a technical

perspective, the single biggest benefit of Ajax is that it helps speed up your web application It

does this in three basic ways:

• Better utilization of the browser cache

• Batching up of network requests in a single packet to reduce network latency issues

• Decreasing the workload on the server by not requiring it to process the entire pageLet’s look at a typical Ajax request-response cycle

Trang 23

Ajax Request-Response Cycle

Figure 12-1 shows a typical user request-response cycle when using Ajax

In this example, the user initiates a request by moving their mouse over some elementonscreen (let’s say they moved their mouse over the Password field, and you want to provide atool tip that displays the password rules that you would like to enforce) Using JavaScript, theapplication would recognize the mouseOver and create an XMLHttpRequest object This wouldthen interact with your back-end server and respond in XML The client browser then parsesthis XML and shows the tool tip to the user

This is a typical request-response cycle using Ajax The key thing here is the XMLHttpRequestobject, which we will examine next

Figure 12-1.Ajax request-response cycle

Trang 24

XMLHttpRequest Object

The XMLHttpRequest object was introduced by Microsoft in Internet Explorer 5.0 Recently,

Mozilla and Apple have included support for this in their web browsers (Firefox and Safari,

respectively) This object is the fundamental basis for Ajax Microsoft’s implementation is

dif-ferent from that of other browsers, so when you create this object in your code, you need to do

a typical browser check For Internet Explorer, create this object using

var req = new ActiveXObject("Microsoft.XMLHTTP");

For Firefox and Safari, it’s just a native object:

var req = new XMLHttpRequest();

You will see detailed code samples in the next section

There is now a working draft in the W3C to make XMLHttpRequest a standard The ing is the interface definition that is proposed by W3C as the standard:

follow-interface XMLHttpRequest {

attribute Function onreadystatechange;

readonly attribute unsigned short readyState;

void open(in DOMString method, in DOMString uri);

void open(in DOMString method, in DOMString uri, in boolean async);

void open(in DOMString method, in DOMString uri,

in boolean async, in DOMString user);

void open(in DOMString method, in DOMString uri,

in boolean async, in DOMString user, in DOMString password);

void setRequestHeader(in DOMString header, in DOMString value)

DOMString getResponseHeader(in DOMString header);

attribute DOMString responseText;

attribute Document responseXML;

attribute unsigned short status;

// raises(DOMException) on retrievalattribute DOMString statusText;

// raises(DOMException) on retrieval};

This should give you some idea of what features are available as part of the XMLHttpRequestobject Enough fun and games, let’s look at an example of Ajax using Struts and see how the

XMLHttpRequestobject is really used

Trang 25

Ajax and Struts in Action

In this section we will build an example of a simple Struts application that uses Ajax Thisapplication is similar to the Google Suggest feature, which offers to the user search keywords

as they start typing Think of a City text field in your web application Imagine how much ier it would be for your users if you could “suggest” a list of cities as they started typing Forexample, if they typed “Ba” you could show them all the cities that started with “Ba,” as shown

eas-in Figure 12-2, which assumes that the country is India

Figure 12-2.Ajax “suggest” drop-down list

The rest of the chapter focuses on the code to build this feature using Ajax and Struts Wewill build some basic Struts code that performs the same functions as shown in Figure 12-2

It will use the same request-response cycle for Ajax invocation as shown in Figure 12-1

var xmlreq = false;

// Create XMLHttpRequest object in non-Microsoft browsers

if (window.XMLHttpRequest) {xmlreq = new XMLHttpRequest();

} else if (window.ActiveXObject) {try {

// Try to create XMLHttpRequest in later versions// of Internet Explorer

Trang 26

xmlreq = new ActiveXObject("Msxml2.XMLHTTP");

} catch (e1) {// Failed to create required ActiveXObjecttry {

// Try version supported by older versions// of Internet Explorer

xmlreq = new ActiveXObject("Microsoft.XMLHTTP");

} catch (e2) {// Unable to create an XMLHttpRequest by any meansxmlreq = false;

}}}return xmlreq;

}

/*

* Returns a function that waits for the specified XMLHttpRequest

* to complete, then passes it XML response to the given handler function

* req - The XMLHttpRequest whose state is changing

* responseXmlHandler - Function to pass the XML response to

*/

function getReadyStateHandler(req, responseXmlHandler) {

// Return an anonymous function that listens to the XMLHttpRequest instancereturn function () {

// If the request's status is "complete"

if (req.readyState == 4) {// Check that we received a successful response from the server

if (req.status == 200) {// Pass the XML payload of the response to the handler function

responseXmlHandler(req.responseXML);

} else {// An HTTP problem has occurredalert("HTTP error "+req.status+": "+req.statusText);

}}}}

Ngày đăng: 12/08/2014, 22:22

TỪ KHÓA LIÊN QUAN