Controller 


I would like to catch all of our project specific exceptions in one place (as a  last resort, if nobody else catches them earlier on) and handle them in a  generic way. The obvious way to do this, I think, is by overriding  processActionPerform in our own <Project>ActionServlet and catch  <Project>Exception's (superclass for all our own exceptions) there. But the Action.perform method only throws IOException & ServletException, so either all our own exceptions will have to inherit from either of those two classes (can't be the right thing to do), or I will have to change the Action.perform definition to also throw our <Project>Exception's (doesn't seem right either, I'd prefer not to have to alter any Struts code)? What should I do?

[Craig McClanahan] How about having the base class for you project exceptions be a subclass of ServletException? That way, you can throw it if you really want to. Then you could declare an <error-page> element in your web.xml file to define common handling (if you don't the default will be an ugly stack trace on most containers).

[Ted Husted] The place to catch your project excpetions is within your Action. All of your project exception code is going to be invoked from an Action, so this is where they all would be thrown, and so this is the last place where you can actually handle them. If you catch them here, you can then forward to an error page or take some other action. If your Action code is already looking for a specific project exception, then catch the root exception too, as a last resort.


Dummy Action

The current file name is set in my action before the page is called. BUT, I want to give the user the option to change the filename, and forward back to this action to display the filter for the new file. Hence, my action doesn't need to DO anything. I just need Struts to set the filename property in my form bean.  Any ideas whether this is possible, or do I need to have a dummy action for it?

[Ted Husted] You could use a "standard" action that just forwarded to continue. This would populate and validate the bean and send it back to the JSP, and could be useful elsewhere. 

You might also be able to use your JSP in lieu of an Action, so long as you also specified an ActionForm wioth the file name property. Never actually tried this though.


You can't go home again

So, there's a input property which  is the submitting page, hardcoded into the struts-config.xml. If I came from another page, however, I will STILL be forwarded to the uri of the input property. I believe this is not the behavior we want. I believe what we want is to simply return where we submitted, right? 

[Ted Husted] Typically, pages are not accessed directly, but through an Action. So all the browser knows is the URI the generated the input page. It doesn't usually know the name of the JSP (which is why we need things like the base tag). It might be possible to code something that could check the request for a referrer and use that for the input property, but any number of things could go wrong with that. Though, if someone submitted a patch, I'm sure there would be a clamor for it to be adopted as an alternative to hardcoding input. 

I just don't  understand the value in the "input value" parameter. There could be MANY pages that got you to the page you are on. The "input" value just makes one possible origin available for you to have as a return target should things go fowl. I had been saying on this discussion that there should be more or less 4 different destinations:

1) where you came from (referrer, or if there is no referrer a default page...see below)
2) where you were originally going when you were interrupted
3) a specific page - (can be success or failure page or could be the first page in a form)
4) a default page

[Ted Husted] As it stands, the "input" property in the Action Mapping is (4) a default page. It is not meant so much to represent where the input came from, but where to go to get more, so the ActionServlet knows  where to bounce the user if ActionForm validate fails. 

[Craig McClanahan] The "input" parameter defines where the Struts controller servlet will forward control to if your form bean's validate() method returns errors.

1) where you came from (referrer, or if there is no referrer a default page...see below)
2) where you were originally going when you were interrupted
3) a specific page - (can be success or failure page or could be the first page in a form)
4) a default page

One thing to note is that "where you came from" is not particularly obvious. If every request goes through the controller servlet (the usual case in an MVC-based application), then the "referer" value will be the name of an action (i.e. an xxx.do if you use the default naming approach), not the name of a page. 


Alternate Response

I need to send a string which represents a xml file to browser, I need to call response.setContentType("text/xml"). But it is at ActionServlet level and is default to "text/html" thus browser ignores xml tags, "<text>long</text>" will only show "long". I can do the output at MyAction.perform() level, but how to bypass the return part of this method?

[Ted Husted]

public ActionForward perform(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response)
throws IOException, ServletException {

response.setContentType("text/xml");
PrintWriter writer = response.getWriter();

// write here
return(null);

} // ---- End perform --
--


Example cleanup

Why does the struts-example perform some scoped- variables cleanup at the end of every Action ?

[Ted Husted] Craig considers it defensive programming, but it really shouldn't be necessary for request-scope resources, and is certainly not mandatory.


Token, token

I would like to know what is the significant of the token in the Action class. Also I would like to know when/Why/Where to use it.

[Ted Husted] Mostly to keep people from pressing submit more than once, which can
result things like duplicate sales. 

1. Define an Action to be called before the form you want to protect.

2. Before the Action returns with a forward to the form, call saveToken(request).

3. Later, the form will submit back to an Action, in that action call isTokenValid (request) to see if you have a valid token. If so, call resetToken; if not return a forward to an error page. 

See also SaveRegistrationAction in the example application.


Lazy Config

I have at present 45 Action classes in my project. In the future this number may climb to more than 100. We just completed requirements and want to make sure that struts can really handle such a heavy load (flow). For some of the forwards in the struts_config.xml file there is not yet a defined stage as to where to take the user to. For that reason the path is set to an empty string.

syntax : <forward name="advanced" path=""/>

During the demo i noticed that i get a 404 error when trying to reach this page (which is as it should be). I want to keep an indefined state page to these. My problem is that i don't want to write the 'undefined.jsp' in each and every forward (i confess i am lazy, besides it is dull and monotonous).  I want to know if there is a flag i can set in the struts-config.xml which defines a global undefined state (similar to the 'unknown' attribute for an action class). Please note that the forward names are different (in different actions). So i can't use a single global forward.

[Ted Husted] If you are truly lazy, then you should already be using an editor that supports macros. You can then setup a macro to spit out a line like 

 <forward name="" path="/undefined.html"/>

with a single keystroke. You can probably also get it to back up the cursor so that it rests between the "", ready for you tot type in the name.

This clearly documents that the forward in question is undefined, and you can create a page that says that.

For the ones you've already done, a quick search and replace on path="" cures all.


Standard Admin Actions

The Struts Blank and Example applications include a number of  default "administrative" Actions. I understand what the end result of these do, but how do they do them, and how are they used? Do they modify the struts-conf.xml or just the ActionMapper?

Struts "digests "the Struts config file when it loads, using it as a "script" to create the mappings. At runtime, Struts refers to the mappings (loaded in memory) -- not to the actual configuration file stored on disk. (Hence, the need for the reload administrative action.)

These actions  affect the mappings stored in memory, and kept as a protected property of the ActionServlet. For more on how each one works, see the Javadoc for the actions package. 

http://jakarta.apache.org/struts/api-1.0/org/apache/struts/actions/package-summary.html


Errors go round

I'm having difficulity understanding how Struts handles errors.

[Ted Husted] Since Struts is a framework, there a number of inter-related parts that work together, the error handling is a good example of that. 

When you detect an error in your Action, you need a way to display it to the user. Since most Actions are not designed to be "views", this is a problem. 

Struts solves this problem using four (count'em 4) components, the ActionErrors class, the message resource, the SaveError method, and, finally, the html:errors tag.

The ActionErrors class is a way to "log" an error. Rather have you embed a string, the ActionErrors class lets you insert a message resource key. This is the same resource that bean:message tag uses to provide internationalization. The Action.SaveError method puts your ActionErrors object under a reserved key in the request context, where the html:error tag can find it. The ActionForm.validate method returns an ActionErrors object, which the ActionServlet saves for you (when it is not null).

In your Action or ActionForm, you would usually have code snippets that look like this:

// create a error message buffer
ActionErrors errors = new ActionErrors();

// process input
// detect error

if (detect_error) {
errors.add(ActionErrors.GLOBAL_ERROR,new
ActionError("error.detected"));
}

/* 

If the error concerns a particular field on a form, you can also use

if (field_error) {
errors.add("field name",new ActionError("error.fieldname"));
}

*/

An Action would also include something like:

// On error, return to input form
if (!errors.empty()) {
    // put the error(s) where the htlm:error tag can find them
    saveErrors(request, errors); 
return (new ActionForward(mapping.getInput())); // go home
}

And an ActionForm would just do this instead:

return errors;

An important thing to realize is that "error.fieldname" is not what the tag will display. The html:error tag assumes that the strings are the name of a key in your application's message resource, which might look like this:

; application.properties
errors.header = <ul>
error.fieldname = <li>Password required</li>
error.detected = <li>Database not available</li>
error.username = <li>That username was not found</li>
errors.footer =</ul>

If the key is missing, by default, nothig will display. You can change this behaviour in the web.xml

<init-param>
<param-name>null</param-name>
<param-value>false</param-value>
</init-param>

For more complex needs, ActionErrors can also use "replacement values" to build customized messages, or to build messages from stock phrases. 

To display an arbitrary message, add a line like this to your file:

errors.dynamic = {0}

You could then use something like

 actionErrors.add(ActionError.GLOBAL_ERROR, new ActionError("errors.dynamic","Please type the
  valid password"
);

However, burying message strings in class files this way is a poor design strategy. They should either be gathered together in a static class, or placed in the Application Resources file. 

For more on this gang of four,  see

< http://jakarta.apache.org/struts/api-1.0


What are the key issues when using form-based login?


ActionServlet Exceptions

I am seeing that I can only throw IOException and ServletException from my Action classes. Now I was thinking about adding Exceptions to the Action class but that would make it proprietary. I could add just Exception, but I dont want to change anything without good reason.  Can you all telll me why I am limited to these two exceptions and what you all are doing about this. The reason is that I have my own exceptions for my app, and will need to throw them sometimes within the perform method.

[Craig McClanahan] You can subclass ServletException with your own exception classes, but I would recommend against this approach. What it will cause is that your user will see an ugly stack trace. It would be better to forward control to some error handling action (or page) that deals with this kind of a problem more gracefully.


Should I specify /do/ or *.do in the path in the html:form tag?

[Craig McClanahan] Preferably, not. The new recommended approach is to set the action attribute of <html:form> to exactly the path of the Action to which you wish the form to be submitted. The <html:form> tag will understand how the action servlet is being mapped, and will construct an appropriate URL accordingly.

For backwards compatibility, the existence of the extension-mapping extension on an action attribute still works, so either of the following tags would work in the Struts example application:

Allowed: <html:form action="/logon.do" ...>

Preferred: <html:form action="/logon" ...>


Multiple Controllers

What are the reasons to have only one instance of the ActionServlet (controller servlet) ?

[Craig McClanahan] To clarify slightly, there is one instance of the controller servlet per web application. The reasoning is that some resources are truly application wide, and there is zero benefit in duplicating them. For example, because servlets run in a multithreaded environment, having more instances of them would not make it run any faster.

Also, a single controller servlet gives you a central point of control where you can guarantee that functions you want performed happen on every single request.

Can I subclass from ActionServlet and have one servlet for each module in my web application

You could if you want, but you're going to run into problems unless you run each module in an independent web app. If you do that, there is no particular need to subclass ActionServlet either, unless you need some specialized functionality that is not
already provided.

If you run your modules in separate webapps, then each webapp is pretty much autonomous -- servlet context attributes,
sessions, and even static variables in Java classes do not cross the boundary between webapps.

What are the implications ?

Trying to run mutliple instances of ActionServlet in the same web application (whether subclassed or not) would cause the
servlet context attributes set up by the ActionServlet instances that started first to be wiped out by the attributes created
in the ActionServlet instance that started last. This would be a Bad Thing (tm) :-)


Digester and SAX2

Why is the Digester built on the deprecated HandlerBase instead of the SAX2 DefaultHandler? Switching over to the newer SAX2 classes doesn't required too much work. I did it with one other application. It's mostly changing AttributeList to Attributes and changing the parameter list on a few methods.

[Craig R. McClanahan] The primary issue for Struts is to be compatible with as wide a range of XML parsers as possible, because different apps will have different preferences. To date, there are a few SAX2 parsers, but only one compliant one that conforms to the JAXP APIs (see next paragraph), which is also matters.

Also I'm curious why Struts doesn't use theorg.apache.xerces.parser.SAXParser insteadof the one in javax.xml.parsers. I would think that apache projects would try leverage work from other apache projects. I'm in the middle of building a software application based on Struts for an internet consulting company. I love the features and  flexibility of Struts, but it's nice to keep current with technology.

The javax.xml.parsers family of interfaces are part of the Java API for XML Processing (JAXP) suite, which ensures applications that they can be independent of the actual parser being used. Using org.apache.xerces.parser.SAXParser directly would require a Struts app to use only Xerces, which is not optimal for every environment.

In addition, the Apache-specific APIs have been in substantial churn over the last few versions -- my preference will be to let this settle down (and let Xerces catch up to implementing the standard JAXP 1.1 interfaces) before updating the technology that Digester uses.


Multiple Struts-Config files

If I want to refer to multiple xml resource files containing config info, how would I go about doing this? It seems my only alternative for now is to keep everything in struts-config.xml, but I was hoping not to have to keep everything that one file.

[Craig R. McClanahan] Two approaches that I have heard about for dealing with this:


Presetting User Preferences

From a design standpoint what is the best way to set the locale information regardless of browser or machine settings to build the initial Accept-language header? Another servlet that reads a cookie and builds the headers before directing the request to the struts action servlet? 

[Craig McClanahan] A persistent cookie is the only way I can think of to know the user's preference before they log on. 

In a Servlet 2.3 environment, this would be a perfect task for a Filter, which could examine each request for a persistent cookie, and set the Locale based on that cookie's values if it was not there yet. You could, of course, automatically log the user on as well. 


Is the Struts object-orientated appropriate for small, heavily-loaded servers?

Compared to embedding the application logic in your servlet, Struts doesn't cost very much in terms of object creation. Struts only creates an instance of an Action class once, and then reuses it continually. You will also find that, with more recent JVMs, object creation overhead has been dramatically reduced -- it may not longer be the dominant factor in performance.

ActionForm beans in Struts can be stored in the user's session (this was the original design), but also as request attributes. This is particularly useful when the entire form is displayed on one page, and the page contains every property as either a hidden field or a visible field. In such cases, there is no need to keep the form bean instance around in between requests.

At the end of the day, though, you have to have an incredibly small server for the performance issues like single object versus lots of objects matter very much.

Can we configure a separate log for each application?

There is a restriction in Tomcat 3.2 related to how you declare log files. In Tomcat 4.0 this has been enhanced, letting you declare a separate destination for the servlet log per application, instead of globally.

If you need to run under Tomcat 3.2, I suggest that you add some sort of application identifier to the log messages being created. A common pattern that I use creates log files like this:

[Application1] This event happened
[Application2] That other event happened

Is there is some kind of "j2ee compliant" way of doing user authentification and role-assignment - are there any standards one is supposed to use?

The J2EE recommendation for this is to use container-managed security (i.e. the elements defined in web.xml) for user authentication and access control. Doing so means that the container is taking care of these issues for you, and (perhaps more importantly) that user identities are shared between the web layer and the EJB layer, because the server will be using the same underlying "database" of users and roles.

The only downside to this approach is that there is not yet a standardized API for portably accessing and maintaining a "database" of users and roles (I'm putting "database" in quotes because the actual implementation could be pretty
much anything, including static text files or directory servers).

Instead, most servers provide a server-specific API to do this kind of thing. For example, in Tomcat you can implement an API called a Realm that talks to your existing database of users, and then tell Tomcat to use this Realm
implementation for user authentication. In that way, new users added to the database (by whatever means) become instantly authorized for web use also. If you were to switch to a different engine, you would need to re-implement this
function according to the APIs provided by the new server, but you would not need to update your applications.

How can Struts check for a set of global conditions before processing an action?

This often comes up in the context of authenticating users or with other control flow issues. 

The direct approach would be to subclass the ActionServlet controller servlet, and override a method like processActionPerform (which actually calls the selected Action) or processActionForward (which forwards control to the returned logical page name). You could then inspect the current state of the world, and inject flow-of-control changes like this still utilizing the superclass's basic functionality.

To make the types of things you inject somewhat configurable, you might also want to use your own custom ActionMapping subclass, with extra bean properties that describe (to your override method) what exceptional things to do and when to do them -- the selected ActionMapping instance is made available to most of the processing calls, so this information would be easily accessible.

If, as part of injecting control follow changes, you need to track where the user was in your application, you might have your ActionServlet subclass maintain a request-scope attribute with a well-known name, containing the value returned by request.getServletPath(). This would act something like the "referer" header in an HTTP request, and provide some history tracking.


How can I  initialize custom "application scope" objects for use in a Struts application?

One way would be to subclass the ActionServlet, to to initialize whatever application scope objects you might need. Just remember to call the super.init() method!

A better approach would be to write another servlet, and auto-load both the Struts controller and your own servlet through the web.xml file. The Example application does this to load a "database" servlet, which is then accessed throughout the application. This way you can initialize application-specific things through your specialized servlet, and leave the normal Struts controller servlet alone. 

In a servlet 2.3 environment (such as Tomcat 4.0), you will also be able to use the new application event listener APIs to catch the contextInitialized() and contextDestroyed() events. This will become the preferred method, once servlet 2.3 containers are widely available.