| Sign In/My Account | View Cart |
Recently with my developer friends, there's been much talk about various other frameworks versus Java. It seems that in some circles (I won't go into what circles, but a quick Googling will pull up plenty of matches), Java is becoming a tool for the old guard of the Web for a lot of reasons, one of which is lines of code (LOC). While some of the arguments of Java needing twice as much code as another language to get the same thing done may be accurate, I personally feel LOC is only an issue if you're spending significant amounts of time doing things you might not have to do. Like coding all of your beans by hand, for instance. Or you could be doing that, plus adding all of your framework XML definition entries by hand as well! I'll admit, I was.
The other day I was playing with Ruby on Rails, and one of the things I liked about it was the ability to easily generate complete code, as well as stub code, from simple scripts. This started me thinking about my current project and how effective I am during my coding day. The "how much work do I get done versus how much time do I spend trying to get work done" thought has been key in my mind more and more over the past few weeks. While frameworks exist to make our jobs seem easier--I use the Spring framework, for instance--it can sometimes seem like it still takes too long to just do one page or module for a web app. So after some thought, I decided to do something about it.
I had known about XDoclet for years and even played around with it when it first came out. But I didn't give it much long-term consideration, as I was not big on code generation back then. I wanted to really understand how things worked "under the hood," so I spent a long time coding everything by hand, including config and deployment files. However, today I see two main benefits for code generation:
gencontrollers or genmodules to
generate the proper controller code or an entire module containing
all of your beans, controllers, services, and DAOs.Based on past experience, I wasn't sure how XDoclet was going to help me. I was looking for a tool that would allow me to spend the least amount of time for the maximum amount of code. Most of the examples I had seen on the XDoclet site and other sites all dealt with the generation of EJBs, which is what XDoclet was originally designed to do. However, I don't have any EJBs in my project, and in fact, Spring is all about using POJOs, so another EJB tool was not what I was looking for. And while XDoclet does have some Spring tags that it recognizes, I was still looking for something a little different.
Then I stumbled upon templates. Templates are a way to extend
the functionality of XDoclet to better match your code-generation
needs. For instance, if you have a particular type of class
that doesn't quite fit in with anything the XDoclet offers out of
the box (in my case, these were custom controller classes), then
it's fairly easy to add your own template files using XML markup
only! Ideally, I just wanted to define a class with a
minimal set of properties and generate not only my
controller code, but additional XML that would be required by
Spring. After some tweaking, I was pleased with the results. Below
is an annotated pseudo-code test controller class. Long lines are
truncated with a \ character.
TestController.java
1. package com.mytest.account.controller;
2.
3. /**
4. * @mytest.controller mapping="/acct.ctrl"
5. * @mytest.controller actionmethod="create"
6. * @mytest.controller actionmethod="read"
7. * @mytest.controller actionmethod="update"
8. * @mytest.controller actionmethod="delete"
9. */
10. public class TestController {
11.
12. /**
13. * @mytest.controller property="privateView" \
propertyValue="priv1" propertyMethod="PrivateView"\
propertyType="String"
14. */
15. private String privateView = null;
16.
17. /**
18. * @mytest.controller property="publicView" \
propertyValue="priv2" propertyMethod="PublicView" \
propertyType="String"
19. */
20. public String publicView = null;
21. }
The first thing that you might notice are the custom namespace
@mytest.controller tags. This is how XDoclet will know
what to look for when generating specific sections of our class.
The positions of these tags are very important. Some appear above
the class keyword and others appear right above property
declarations. XDoclet uses tags based on these positions to
populate Class or Field objects that
we'll use in our template. This is an important distinction, as
we'll see later.
Looking at the above code we want to do the following:
SimpleUrlHandlerMapping, which could be declared in a
mytest-servlet.xml file (the Spring equivalent of the
Struts struts-config.xml file).actionmethod parameter will allow us to
specify a method name used in the controller, which extends the
Spring MultiActionController (in Struts, this would be
a DispatchAction). In our case, these will be CRUD
(Create, Read, Update, Delete) method names. XDoclet will iterate
over multiple parameters with the same name, which will allow our
template to output n number of methods.property tag lets us name a typical bean
property, like you would normally do in XDoclet, but since we're
using a custom template tag here, we're adding a few more
attributes. The propertyValue is the value assigned to
that property in the mytest-servlet.xml file. For our
custom template, we're wiring up the private and public JSP views
for this controller, which I usually call privateView
and publicView--one for subscribers, and one
for everyone else. The propertyMethod will give us an
easy way to output the method name this property belongs to without
trying to jump through hoops to adhere to the bean-method-naming
convention. And the propertyType attribute specifies
what object type we'll be getting and setting for our
method.It could be argued that I could have placed all of my property
definitions at the class level instead of the field level. This is
true; however, that would also entail extra code on the template
side to make sure I'm picking up the correct properties value,
method, and type attributes when generating a method. I felt it was
quicker and easier to use the XDoclet Field class
rather than adding more complicated code to my template.
Now it's time to get our hands dirty. As I mentioned before, the
reason I chose XDoclet to do my code generation was the fact that I
could define a custom template (not some custom class or
more code) and use XDoclet XML tags to fill in the blanks
for me. Below are the relevant portions of the template we want to
generate. Long lines are truncated what a \ character.
MultiController.xdt
1. package <XDtPackage:packageName/>;
2. import javax.servlet.http.HttpServletRequest;
3. import javax.servlet.http.HttpServletResponse;
4.
5. import org.apache.commons.logging.Log;
6. import org.apache.commons.logging.LogFactory;
7. import org.springframework.validation.BindException;
8. import org.springframework.web.servlet.ModelAndView;
9.
10. import com.mytest.system.spring.BaseController;
11.
12. /**
13. * MultiAction controller class for \
<XDtClass:className/>.
14. *
15. * @author
16. * @since
17. * @version $Id$
18. */
19. public class <XDtClass:className/> \
extends BaseController {
20.
21. // CUT AND PASTE THIS INTO THE mytest-servlet.xml \
FILE
22. //
23. // <!-- ==== <XDtClass:className/> \
Bean Definition ==== -->
24. // <bean id="contr<XDtClass:className/>" \
class="<XDtPackage:packageName/>.<XDtClass:className/>">
25. // <property name="methodNameResolver" \
><ref bean="actionResolver"/></property>
26. <XDtField:forAllFields>
27. <XDtField:ifHasFieldTag tagName="mytest.controller">
28. // <property name="<XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="property"/>"><value><XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="propertyValue"/></value></property>
29. </XDtField:ifHasFieldTag>
30. </XDtField:forAllFields>
31. // </bean>
32. //
33. // === PUT THIS UNDER THE URL MAPPINGS SECTION ===
34. // <prop key="<XDtClass:classTagValue \
tagName="mytest.controller" \
paramName="mapping"/>">contr<XDtClass:className/></prop>
35. //
36.
37. protected final Log log = LogFactory.getLog(getClass());
38.
39. <XDtField:forAllFields>
40. <XDtField:ifHasFieldTag tagName="mytest.controller">
41. public <XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="propertyType"/> \
get<XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="propertyMethod"/>(){
42. return <XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="property"/>;
43. }
44.
45. public void set<XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="propertyMethod"/>\
(<XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="propertyType"/> value) {
46. <XDtField:fieldTagValue \
tagName="mytest.controller" \
paramName="property"/> = value;
47. }
48. </XDtField:ifHasFieldTag>
49. </XDtField:forAllFields>
50.
51. public ModelAndView init(HttpServletRequest request, \
HttpServletResponse response)
52. throws ServletException, IOException {
53. if(log.isDebugEnabled()) log.debug("entered");
54. // YOUR INIT CODE GOES HERE
55. if(log.isDebugEnabled()) log.debug("exited");
56. // REPLACE THIS HERE IN YOUR CODE
57. return null;
58. }
59.
60. <XDtClass:forAllClassTags \
tagName="mytest.controller">
61. <XDtClass:ifHasClassTag \
tagName="mytest.controller" \
paramName="actionmethod">
62. public ModelAndView <XDtClass:classTagValue \
tagName="mytest.controller" \
paramName="actionmethod"/>(\
HttpServletRequest request, HttpServletResponse \
response)
63. throws ServletException, IOException {
64. if(log.isDebugEnabled()) log.debug("entered");
65.
66. if(log.isDebugEnabled()) log.debug("exited");
67. // RETURN THE PROPER VIEW HERE
68. return null;
69. }
70.
71. </XDtClass:ifHasClassTag>
72. </XDtClass:forAllClassTags>
73.
74. // Your (other) multiaction methods go here
75.
76. } // <XDtClass:className/>
Phew! But when it's all done, I have a controller class that contains the proper XML snippets for the Spring mytest-servlet.xml bean definition and URL mappings, the proper get/set methods for view properties, and my multi-action methods, complete with debug logging statements. 76 lines of code from 21, including the XML (which will be cut out), can save significant typing time and also eliminate bugs.
Note also the use of the XDtField and
XDtClass tags. It should be pretty clear now why we
placed the various tag values where we did. All of our class-level
tags either defined class methods or XML config file values, which
the field level tags used to generate our basic getters and
setters. Like I said before, our use of the field tags here are a
little different, as we're also using them to define some of the
controller mapping values in our mytest-servlet.xml
file for Spring. When our controller stars up, Spring will inject
the privateView and publicView methods
with their proper view values.
Pages: 1, 2 |