Jan 1

pc.jpgToday, I came across an interesting article in the Forbes Magazine entitled “Computing 2010″. The interesting part is that this article was written ten years ago. The author, Kip Crosby, imagined what computers would look like in 2010: optical circuits instead of silicon, with a CPU running at 100 GHz, holographic mass storage offering several TB capacity, 256 GB optoelectronic RAM, biometric authentication, voice control, completely wireless and shaped like a frisbee. Whew! Looks like Kip was just a tad too optimistic. Optoelectronics hasn’t caught on and most computers are still boxy rather than frisbee-ish. In fact, todays’s PC looks pretty much like that of 2000, except that its capacity has increased roughly following Moore’s law. The only accurate prediction is about mass storage capacity, although that didn’t require optical technology.

Personally, I’ve begun the new computing year with a major upgrade, though still far away from Kip’s 2010 vision. I have replaced my 32-bit Windows OS with a 64-bit Linux OS, doubled RAM from 4 GB to 8 GB and added another external USB hard drive for backups. The Ubuntu installation turned out a little difficult, because Linux did not want to cooperate with the BIOS RAID-1 configuration, so I had to switch to SATA mode and wipe out the Windows installation. The rest was easy, however. I used to worry about not being able to make my 3G USB modem work with Linux, but our maid has solved this problem for me. She obliterated the device by putting it into the washing machine. Can’t really blame her for that. I probably shouldn’t have carried the modem in the pockets of my shorts.

Back to the topic. What are the computing trends in 2010? Just off the top of my head: cloud computing is becoming a mainstream technology (or perhaps a mainstream buzz; time will tell). Along with that, virtualisation is now widely used. Supercomputers have broken the petaflop mark and now operate in the range of large clouds (> 1 PFlop). CRT monitors are quickly becoming relics of a past epoch. Single-core CPUs are headed the same way. Functional programming languages are beginning to catch on. 64-bit hardware and software are overtaking 32-bit systems in mainstream IT. Java 7 is announced for 2010. It surely looks like an interesting year.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Technorati
  • Facebook
  • Mixx
  • Google
  • YahooMyWeb
  • Slashdot
  • LinkedIn
  • blogmarks
  • Live
  • description
  • StumbleUpon
  • Ma.gnolia
  • MisterWong
  • NewsVine
  • Reddit
  • Spurl
  • Yigg
  • E-mail this story to a friend!
Dec 1

jsp-nightmare.jpgOnce upon a time as a Java newbie, I thought that Java Server Pages were great. Back then I had done web development in Perl and PHP and I was pleasantly surprised by the similarity of the development process. It’s as simple as write and run. No bothersome compiler runs and deployment cycles. Java Server Pages seemed like PHP on steroids. Actually, I still think this is a fairly accurate description. It’s what the designers of JSP intended - Java’s answer to dynamic web scripting languages. The question is whether the JSP design is sound. After having spent several years in the Java Enterprise world, and having maintained large amounts of JSP code, I am convinced that it isn’t. So I am writing this article to tell you the untold nightmares of JSP programming.

Before we are going into that, let’s briefly look at the bright side. JSP technology is tempting for several reasons. First, it is mainstream and it bears the official stamp of approval by Sun. Second, it is an integral part of JEE and widely supported by application servers and tools. Third, it it is relatively easy to whip up dynamic content with JSP. So, why not use Java Server Pages? Well, there are several problems, the biggest of which are scriptlets. Scriptlets are embedded Java code. Because JSP allows you to mix Java code with markup, you get hybrid files which tend to become messy and difficult to maintain. Few HTML designers understand what’s going on in a JSP file. Few programmers are comfortable with mixing their code with reams of markup.

The typical production process for a JSP-based application looks like this: the UI designers finish their protoypes and hand over the resulting markup to the programmers. The programmers tear apart the HTML to make it easier to insert code, repeat  sequences, and insert display logic. Once the programmers are finished, there is absolutely no chance that the designers will recognise the markup they produced. It is therefore also unlikely that they will ever touch it again. Even if the finished product contains no scriptlets, JSTL, expression language and (God help us) custom tags will confuse the hell out of designers. This means that the entire process has to be repeated,  everytime a change of visuals is requested. This is however not the worst problem.

The biggest drawback is that there is absolutely no way to prevent application logic from creeping into the JSPs which ultimately leads to spaghetti code. You might think, “Oh well, I know how to code my JSPs properly.” At this point you should remember Murphy’s law, especially the part that says: “if something can go wrong it will go wrong.” Perhaps you are a disciplined individual who wouldn’t even dream about putting application logic into a JSP file. But can you say the same about the other programmers in your team? What if work needs to be completed under time pressure? Can you resist the temptation to solve a problem quick-and-dirty by putting a hack into a JSP file along with a “will fix this later” note? I have seen way too many “will fix this later” notes in JSPs and most of them were several years old.

My project consisted of an application with roughly 1.5 million LOC where about half of the code was implemented in JSPs. That’s about 1500-2000 JSP files. Most files were less than 500 lines, but some were in excess of 2000 lines. Once you hit upon an XL-sized JSP, it’s a safe bet that some crucial functionality is buried in it. Reading and understanding a 2000 line JSP can take several hours. Modifying and maintaining it is quite another deal. The project did not attempt to separate business logic from display logic in JSPs, thus XML was liberally mixed with scriptlets. While this approach works OK for narrow functionality that can be coded  in a single JSP, it becomes very unwieldy for functionality that spans a larger problem space and, hence, many JSPs.

The “solution” for this was to use scriptlet fragments which are shared by multiple JSPs. This isn’t a real solution, however, because it forgoes almost all advantages of using an OOP language, such as abstraction, encapsulation, inheritance, and as a side effect it produces “ultra-tight coupling” between JSPs that use the same fragments. In theory, this can be made slightly less painful by defining inner classes in fragments for shared functionality. These class fragments are then included in the consuming JSPs at compile time. However, inner classes have their own limitations and compile time includes bulk up the resulting byte code. Since inner classes can access variables and methods in the outer scope, they lack proper encapsulation. Tying them to multiple consumers can lead to some weird dependencies and logical errors. Finally, there is no way to produce unit tests for scriptlets. The only way to test scriptlets is by inserting the test code directly into the JSP, which is obviously insane. In summary, there is no way to code scriptlets cleanly, so it’s best to avoid them.

But even without scriptlets, there’s plenty of trouble. For example, there is the Java standard tag library (JSTL) and the expression language (EL) which are supposed to replace scriptlets as coding instruments. In particular, the EL has been praised as enabling clean coding for MVC applications with Java Server Pages as view component. - Well, I disagree. - JSTL+EL are neither very clean nor very concise. What is worse, they are too powerful for their own good. JSTL+EL are Turing-complete, just like XSLT which they resemble, which means that replacing scriptlets with JSTL+EL is like jumping from the frying pan into the fire. In addition, JSTL provides tags that allow programmers to access a database and execute queries. If you see MVC going out of the window at this point, you have recognised the problem. In summary, JSTL+EL produce the same problems as scriptlets, but with an XML syntax.

In conclusion, Java Server Pages can lead to maintenance nightmares, especially when used in a large project. While it is possible to code JSPs cleanly, it is apparently not a widespread practice, which is probably Murphy’s law taking its toll. Hence, if you are building a new Java web application, think twice about using JSP. If you have a legacy application, you might want to replace JSPs by something more appropriate. In most cases, this can be done gradually whereas legacy JSPs can run side by side with an alternative view technology.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Technorati
  • Facebook
  • Mixx
  • Google
  • YahooMyWeb
  • Slashdot
  • LinkedIn
  • blogmarks
  • Live
  • description
  • StumbleUpon
  • Ma.gnolia
  • MisterWong
  • NewsVine
  • Reddit
  • Spurl
  • Yigg
  • E-mail this story to a friend!
Nov 22

Today I’d like to present another Ant script for Scala development. I promise it’s going to be the last one, but I felt this mini series wouldn’t be complete without it. This script is for developing a Lift application with Scala. I became interested in Lift in the first half of this year, but unfortunately, I wasn’t able to install it when I wanted to give it a try, because of some besetting problems with Maven. Deplorably, the Lift developers have made Maven a requirement for Lift. If you go to the Lift download page, you wont find a Lift package but a Maven package which you must install in order to retrieve Lift from the central repository. Alternatively, you can fetch the latest Lift sources from GitHub, but then you still need Maven to get the examples to work. Since Maven and I have a difficult relationship, I mentioned this to the Lift community earlier this year. The community seemed quite pleased with Maven, however, and had little interest in supporting alternatives. Hence, I’ve adapted the Ant script from the last article in order to work with Lift and -while we are at it- replaced the Jetty server that is bundled with Lift with Tomcat.

I hope this will be useful for people interested in tinkering with Lift who want to avoid the aggravation of having to deal with Maven. As usual, you can download the project sekeleton and Ant build file right here. I have packaged the Lift 1.0 jars as well as its dependencies (commons-fileupload.jar and log4j.ar), so that you can get the helloworld example to run without additional installation steps. If you plan to use this for your work, you should replace the Lift jars with the most recent versions from GitHub. The Ant script has the same target definitions as the one I presented for general Web development including deploy, undeploy and reload. The Lift dependencies are likewise handled in the same way, as the script uses everything it finds in the WEB-INF/lib directory. Your only responsibility is to put the right jars in there. The main difference consists of a different directory tree structure that reflects the layout of a standard Lift project. This means you won’t have to rearrange things if you move a project from Maven to Ant. In fact all of the Lift examples should run simply by dropping in the sources (I’ve tried this for a few but not all examples). So, here’s the Ant script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<?xml version="1.0"?>
<project name="hellolift" default="build">
 
  <!-- root directory of this project -->
  <property name="project.dir" value="."/>
 
  <!-- root directory of Scala installation -->
  <property name="scala.home" 
    value="C:\\Program Files\\Scala"/>
 
  <!-- root directory of Tomcat installation -->
  <property name="server.home" 
    value="C:\\path\\to\\Tomcat\\installation"/>
 
  <!-- URL for Tomcat's manager application -->  
  <property name="server.manager.url" 
    value="http://dev:8080/manager"/>
 
  <!-- account name for Tomcat's admin account -->  
  <property name="server.manager.username" value="admin"/>
 
  <!-- password name for Tomcat's admin account -->  
  <property name="server.manager.password" value="admin"/>
 
  <!-- location of scalatest.jar for unit testing -->
  <property name="scalatest.jar" 
    value="C:\\path\\to\\scalatest-1.0\\scalatest-1.0.jar"/>
 
  <target name="init">
 
    <!-- derived path names -->
    <property name="source.dir" value="${project.dir}/src"/>
    <property name="web.dir" value="${project.dir}/web"/>
    <property name="classes.dir" 
      value="${web.dir}/WEB-INF/classes"/>
    <property name="lib.dir" 
      value="${web.dir}/WEB-INF/lib"/>
    <property name="webapp.dir" 
      value="${server.home}/webapps/${ant.project.name}"/>
    <property name="test.dir" value="${project.dir}/test"/>
 
    <!-- scala libraries for classpath definitions -->
    <property name="scala-library.jar" 
      value="${scala.home}/lib/scala-library.jar"/>
    <property name="scala-compiler.jar" 
      value="${scala.home}/lib/scala-compiler.jar"/>
 
    <!-- classpath for the compiler task definition -->   
    <path id="scala.classpath">
      <pathelement location="${scala-compiler.jar}"/>
      <pathelement location="${scala-library.jar}"/>
    </path>
 
    <!-- classpath for project build -->
    <path id="build.classpath">
      <pathelement 
        location="${server.home}/lib/servlet-api.jar"/>
      <pathelement location="${scala-library.jar}"/>
      <fileset dir="${lib.dir}">
        <include name="*.jar"/>
      </fileset>
      <pathelement location="${classes.dir}"/>
    </path>
 
    <!-- classpath for unit test build  -->
    <path id="test.classpath">
      <path refid="build.classpath"/>
      <pathelement location="${scalatest.jar}"/>
    </path>
 
    <!-- definition for the 
      "scalac" and "scaladoc" ant tasks -->
    <taskdef resource="scala/tools/ant/antlib.xml">
      <classpath refid="scala.classpath"/>
    </taskdef>
 
    <!-- definition for the "scalatest" ant task -->
    <taskdef name="scalatest" 
      classname="org.scalatest.tools.ScalaTestTask">
      <classpath refid="test.classpath"/>
    </taskdef>
 
    <!-- definition for the "reload", "deploy" 
      and "undeploy" Tomcat tasks -->
    <taskdef name="deploy" 
      classname="org.apache.catalina.ant.DeployTask">
      <classpath 
        path="${server.home}/lib/catalina-ant.jar"/>
    </taskdef>
    <taskdef name="reload" 
      classname="org.apache.catalina.ant.ReloadTask">
      <classpath 
        path="${server.home}/lib/catalina-ant.jar"/>
    </taskdef>
    <taskdef name="undeploy" 
      classname="org.apache.catalina.ant.UndeployTask">
      <classpath 
        path="${server.home}/lib/catalina-ant.jar"/>
    </taskdef>
 
  </target>
 
  <!-- compile project -->
  <target name="build" depends="init" description="build">
    <buildnumber/>
    <tstamp/>
    <mkdir dir="${classes.dir}"/>
    <mkdir dir="${lib.dir}"/>
    <copy file="${scala-library.jar}" todir="${lib.dir}"/>
    <scalac 
      srcdir="${source.dir}" 
      destdir="${classes.dir}"
      classpathref="build.classpath"
      force="never"
      deprecation="on"
    >
      <include name="**/*.scala"/>
    </scalac>
  </target>
 
  <!-- create a deployable web archive -->
  <target name="war" depends="build" description="war">
    <war destfile="${project.dir}/${ant.project.name}.war"
      webxml="${web.dir}/WEB-INF/web.xml"
      basedir="${web.dir}"
    />
  </target>
 
  <!-- creates a deployable web archive with 
    all classes packed into a single jar file -->
  <target name="packedwar" 
    depends="build" description="packedwar">
    <jar destfile="${lib.dir}/${ant.project.name}.jar" 
      basedir="${classes.dir}"
      duplicate="preserve">
      <manifest>
        <section name="Program">
          <attribute name="Title" 
            value="${ant.project.name}"/>
          <attribute name="Build" value="${build.number}"/>
          <attribute name="Date" value="${TODAY}"/>
        </section>
      </manifest>
    </jar>
    <delete dir="${classes.dir}"/>
    <war destfile="${project.dir}/${ant.project.name}.war"
      webxml="${web.dir}/WEB-INF/web.xml"
      basedir="${web.dir}"
    />
    <delete file="${lib.dir}/${ant.project.name}.jar"/>
  </target>
 
  <!-- deploy project on Tomcat server -->
  <target name="deploy" depends="war" description="deploy">
    <mkdir dir="${webapp.dir}"/>
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}"/>
    </copy>
    <deploy url="${server.manager.url}"
      username="${server.manager.username}"
      password="${server.manager.password}"
      path="/${ant.project.name}"
      localWar="${project.dir}/${ant.project.name}.war"
      />
  </target>
 
  <!-- update and reload project on Tomcat server -->
  <target name="reload" 
    depends="build" description="reload">
    <copy file="${scala-library.jar}" todir="${lib.dir}"/>
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}"/>
    </copy>
    <reload url="${server.manager.url}"
      username="${server.manager.username}"
      password="${server.manager.password}"
      path="/${ant.project.name}"/>
  </target>
 
  <!-- remove project from Tomcat server -->
  <target name="undeploy" 
    depends="init" description="undeploy">
    <undeploy url="${server.manager.url}"
      username="${server.manager.username}"
      password="${server.manager.password}"
      path="/${ant.project.name}"/>
  </target>
 
  <!-- build unit tests -->
  <target name="buildtest" depends="build">
    <mkdir dir="${test.dir}/build"/>
    <scalac 
      srcdir="${test.dir}" 
      destdir="${test.dir}/build" 
      classpathref="test.classpath"
      force="never"
      deprecation="on"
    >
      <include name="**/*.scala"/>
    </scalac>
  </target>
 
  <!-- run unit tests -->
  <target name="test" 
    depends="buildtest" description="test">
    <scalatest runpath="${test.dir}/build">
      <reporter type="stdout" config="YFABRT"/>
      <membersonly package="suite"/>
      <!-- <reporter type="graphic" config="YFABRT"/> -->
      <!-- <suite classname="suite.TestSuite"/> --> 
    </scalatest> 
  </target>
 
  <!-- delete all build files -->
  <target name="clean" depends="init" description="clean">
    <delete dir="${classes.dir}"/>
    <delete dir="${project.dir}/doc"/>
    <delete dir="${test.dir}/build"/>
    <delete file="${lib.dir}/scala-library.jar"/>
    <delete file="${project.dir}/${ant.project.name}.war"/>
  </target>
 
  <!-- create API documentation in doc folder -->
  <target name="scaladoc" 
    depends="build" description="scaladoc">
    <mkdir dir="${project.dir}/doc"/>
    <scaladoc 
      srcdir="${source.dir}" 
      destdir="${project.dir}/doc" 
      classpathref="build.classpath" 
      doctitle="${ant.project.name}" 
      windowtitle="${ant.project.name}"/>
  </target>
 
</project>
Share and Enjoy:
  • Digg
  • del.icio.us
  • Technorati
  • Facebook
  • Mixx
  • Google
  • YahooMyWeb
  • Slashdot
  • LinkedIn
  • blogmarks
  • Live
  • description
  • StumbleUpon
  • Ma.gnolia
  • MisterWong
  • NewsVine
  • Reddit
  • Spurl
  • Yigg
  • E-mail this story to a friend!

« Previous Entries Next Entries »