Hi,

This post is quite long but it traces the situations I encountered when
moving from Struts version 0.5 to 1.0Beta. I hope it can be of help for
others having to do the move.
It took me 2 days to port a small application (46 java, 20 jsp files)
clearing sometimes the code to benefit from new features when I was
debugging a file. I have some work left to check that all features are
running like before, but I think the most tedious part of the work is gone.

Pierre Métras

PS: I include my version of LinkTag.java to support JavaScript events, too.
Though not HTML 4.0 conformant, I hope it will find a path in version1.0;-)


~~~~~~~~~~~~~From 0.5 to 1.0B~~~~~~~~~~~~~~~~
Binaries
=====
- Download tomcat 3.2B7
- Download Struts 2000-11-20
- Susbtitute to existing files and directories.

Java files
======
ActionForm beans
-------------------
- Change "class X implements ActionForm" to "class X extends ActionForm".

- Change "class X implements ValidatingActionForm" to "class X extends
ActionForm".

ValidatingActionForm beans
-----------------------------
- Change "import org.apache.struts.ValidatingActionForm" to "import
org.apache.struts.ActionForm".

- In validating classes, change "validate" method to return an ActionErrors
instead of String[]. Clean the code accordingly. Add "import
org.apache.struts.ActionErrors; import org.apache.struts.ActionError;"

On ActionForm
- Change checkboxes processing in "reset" method. Usually, just set to null
the corresponding property.

- Add reference to ActionMapping for "reset" method: "import
org.apache.struts.ActionMapping".

Action
-------
- Class "ActionBase" is now deprecated. Change to "Action"

- Change "MessageResources resources = getResources(servlet);" to
"MessageResources resources = getResources();". This change could have been
flagged deprecated to ease the transition...

- "ActionMapping.getInputForm" is now deprecated. Use "getInput" in place.

- "BeanUtils.getPropertyValue" changed to "PropertyUtils.getProperty".

- "ActionMapping.getFormAttribute" changed to "ActionMapping.

- ATTENTION: "Action.perform" has lost its servlet argument. If you miss the
change, your source will compile but your "perform" method will never
called! The symptom: an empty page in your browser. The old "perform"
signature should at least exist with a deprecated signal to ease the
transition form 0.5 to 1.0 code.

- The code can be cleaned here with the new classes and methods ActionError,
ActionForm.reset...
For instance, if you have a menu Action class, the perform method will only
have to do the switch to return the mapping. You don't need anymore to
retrieve ActionForm instance attributes (was it from session or from
request?) and initialize their fields (and don't forget to set the attribute
again): all this can be done in one place, the ActionForm.reset method.


First run
======
At that point, everything compiles. Copy the class files to the server.
Start Struts. Wow, there's new traces while the action.xml file is
processed.

- 404: /index.jsp not found!
OK. Right access on directory tomcat/work is not set properly.

- org.apache.jasper.compiler.CompileException: Attribute onMouseOut invalid
according to the specified TLD
<struts:link> has not yet been modified to include JavaScript events
handlers (I have to modify the TLD file a generate a personalized
struts.jar). Hope this will be done in final v1.0.

It couldn't have run at the first shot. Ok, now let's look at the JSP...


JSP
----
- Change <%@taglib uri="struts" prefix="s" %> to <%@taglib uri="struts-form"
prefix="sf" %>, etc.

- Update <s:tags> to the appropriate <sf:tag> or <st:tag> or <sl:tag>...

- Change every JavaScript event handler to use lower case: "onMouseOut"
becomes "onmousout" now! Mama mia...
    <s:message />            <sb:message>
    <s:link> </s:link>        <sf:link> </sf:link>

- org.apache.jasper.compiler.ParseException: Unterminated user-defined tag:
ending tag </sf:link> not found or incorrectly nested
Argh! The LinkTag in org.apache.struts.taglib.form is brand new and
different from the one in org.apache.struts.taglib. Well, I have to patch it
again and regenerate the struts.jar.

- org.apache.jasper.JasperException: Unable to compile class for JSP:
java.lang.NullPointerException
Oops! I forgot to move the LinkTag from "org.apache.struts.taglib" to
"org.apache.struts.taglib.form". Not a really usefull error message from
tomcat!

- Change <s:ifPropertyEquals> to <sl:equal>, </s:ifPropertyEquals> to
</sl:equal>, <s:form> to <sf:form>...
But now with <sl:equal>, an exception is thrown if the value is null. In
version 0.5, <s:ifPropertyEquals> would have returned false... So, don't
forget to enclose the tests in <sl:present> ... Test ... </sl:present>

- Change <s:htmlProperty...> to <sb:write name="" property=""
filter="html"/>
I would suggest using a filter="html", instead of any arbitrary value as
suggested in the documentation, as that will open the door for other
filtering functions (JavaScript escape, WML...).

Great care must be done while renaming tag libraries: if an error is done in
the library prefix, the tag is not interpreted (rendered as HTML text) and
no error is raised.
Well, there's some work here! Unix champions will write a sed script to do
the replacements in the files...

Methodology:
- Check your JSP pages individually to verify that struts tags are correctly
interpreted.
- Then, and only then, go through action url (*.do)...


Action.xml
-----------
- Rename "action.xml" to "struts-config.xml"

- Enable DTD validation: include <!DOCTYPE struts-config PUBLIC "-//Apache
Software Foundation//DTD Struts Configuration 1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd"> at the top of
struts-config.xml

- Complete change in the syntax, now clearer (in my opinion). Must rewrite
the whole file...

The impact of the new struts-config.xml file should have consequences in the
Java code (in particular, when one change the scope of mappings). But the
features will clear some code...


Web.xml
---------
- References the new taglibs:
    <taglib>
      <taglib-uri>struts-bean</taglib-uri>
      <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
    </taglib>
    ...

- Change in "action" servlet to reference "struts-config.xml" instead of
"action.xml".

- Enable locale detection. Suppress application tag in every form.
      <init-param>
        <param-name>locale</param-name>
        <param-value>true</param-value>
      </init-param>


Second (well, times later) run
====================
- java.lang.NullPointerException
at
org.apache.struts.action.ActionServlet.processActionPerform(ActionServlet.ja
va:1375)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1241)
at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:417)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:740)
etc...

I used to write:
            return new ActionForward(mapping.getInput());
in "Action.process" when I wanted my action to return to the calling form
(partial validation, error...).
Now, the getInput call returns null, instead of the original path, so the
RequestDispatcher in ActionServlet fails to forward the request, and thus
the NullPointerException.
I didn't investigate enough to find if this is now a "normal" feature of
version 1.0. There was a similar bug discussed in the mailing list the 15th
Aug 2000, but it was diagnosed to an action.xml error. I doubled check my
struts-config.xml and it seems correct.

Change it to:
            return mapping.findForward("forwardName")
But don't forget to add the new "forwardName" in struts-config.xml

At least, ActionServlet could protect the call (line 1375 and before) to
check if the mapping has a path != null, just to help debugging.

- ATTENTION: "ActionForm.validate" accepts now two arguments. So my old
validate methods are not called... These too should have been flagged
obsolete.


Last candy
========
During that version evolution, new version 3.2B8 of tomcat and 20001122 of
Struts were released. The tomcat one seems to correct a bug I had with
session management when cookies are not set and Struts added a tag I had
already in my application library. So, let's do the jump again...

- 2000-11-23 10:14:00 - Ctx( /myapp ): Exception in init  Can't happen -
classname is null, who added this ? - java.lang.IllegalStateException: Can't
happen - classname is null, who added this ?

Yahoo! Appart form this slight exception when a picture file is missing, my
application is running now with Struts 1.0. Champagne for everybody!


Some comments and remarks for discussion (perhaps off topic):
===========================================
- If I had used optional imports (import org.apache.struts.*) instead of
explicit ones (import org.apache.struts.ValidatingActionForm), I would not
have to check every files for the new classes. Also, the recompilation
process would have gone quicker because changing "String[] validate()" to
"ActionErrors validate()" would have not needed 2 compilations: one for the
change, and another one to add the "import org.apache.struts.ActionErrors" I
forgot the previous time!

- I decided to adopt Struts style for naming getter/setters arguments and
properties:
class X implements ActionForm {
    String property;
    public void setProperty(final String propety) {
        this.property = property;
    }
...
}
Now, the argument of the method is named with the name of the relevant
property. Scope is discrimined with "this." prefix, so one can refer easily
to the updated property.
But this change can mask subtile errors: in my example, the argument has an
error (missing "r" in "property"), and this error can go undetected at
compile time.
What are the advantages of this naming convention?

- The monolitic tag library is exploded in 4 specialized (form, logic, bean,
template) tag libraries. Appart from the logical and design strutcture, is
there another advantage?
When you reference <struts:link> or <struts-form:link>, the custom tag is
compiled once and the resulting servlet file is saved for future use. In
either case, the resulting class is identical, so there's no performance
benefit (appart from the first compilation time, perhaps). So, in order to
manage future evolutions, is there a possibility to declare <%@taglib
uri="struts-*" prefix="s" %> and have a factorization of the various tag
libraries?

The separation of the libraries make you juggle with the prefixes:
For example, in version 0.5, you had to write:
    <struts:form name="myForm" ...>
        <struts:text property="foo" />
        <struts:property property="bar" />
    </struts:form>
and you obtained the value of the property "bar" of the current form.

With the generalization of version 1.0, you have to explicitely reference
the bean you want the property from, as there's no more reference to the
default form:
    <form:form name="myForm"...>
        <form:text property="foo" />
        <bean:write name="myForm" property="bar" />
    </form:form>



LinkTag.zip

Reply via email to