Skip to content

CalendarTag: A simple JSP Calendar Component

Update, 8/10/2006: There’s at least one other open source calendar generation taglib that looks like it’s worth a download (even though it’s older). That author hoped that code would make it into the Jakarta Taglibs but I didn’t find any evidence of that.

I was looking around at jsp calendar components for a client. I’d previously had a hard time finding a decent one, so I didn’t hold out much hope. Luckily, this search was slightly different–I don’t need any fancy features, just a simple calendar display rendered in html, with links on the day numbers. In addition, the calendar needs to be indexable, so FlatCalendarXP, which I’ve used before, is not an option.

I looked at the HtmlCalendarBean and the Calendar Taglib, both from ServletSuite. I expected the price to be reasonable, but didn’t expect source, which was important for the client.

A bit of digging around on SourceForge found the perfect project: CalendarTag. This project looks abandoned, but the author got things into good shape before ceasing development. I’d say the project is mature, rather than abandoned. In addition, he responded to my questions (including “is this project alive?”) in less than 24 hours.

I found the documentation to be very useful. In addition, you can customize the
calendar’s output to a very large degree. I especially like the decorator, which lets you control exactly how each day is displayed by implementing a relatively simple interface or extending a a class.

Make no mistake. This is a simple component which just displays a calendar in html. There’s no support for users or events (Update 3:24. I should have read the docs more closely and said, there’s no built in support for events or users. From within the calendar, you have access to the request and so can code up event handling and/or user specific calendars.). If you want those features, I’d look at a more complex calendaring system. Calendartag does something simple and does it well.

One tip–you’ll need to have the standard-1.0.4.jar file available to your web application, as well as the calendartag-1.0-rc4.jar file, otherwise you see this rather fearsome exception:

org.apache.jasper.JasperException: org/apache/taglibs/standard/tag/el/core/ExpressionUtil
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:254)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:295)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:199)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:828)
at
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:700)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:584)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)
at java.lang.Thread.run(Thread.java:595)

root cause

javax.servlet.ServletException: org/apache/taglibs/standard/tag/el/core/ExpressionUtil
at org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:536)
at org.apache.jsp.index_jsp._jspService(index_jsp.java:59)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:137)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:210)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:295)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2422)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:163)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
at
org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:199)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:828)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:700)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:584)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)
at java.lang.Thread.run(Thread.java:595)

Jini and JavaSpaces at BJUG

I went to BJUG last week to see a presentation about Jini by someone from GigaSpaces. It was an intensely interesting presentation for a number of reasons. First off, I knew the presenter, Owen Taylor. About 6 years ago, I took a class from him, along with a few other people. The class covered BEA Weblogic and EJBs. I’ve attended (and given a couple) technical presentations in my time including some conferences. I don’t think I’ve ever met someone who was more energetic and practiced at conveying hard concepts than Owen Taylor. Owen! Start blogging!

Another reason it was interesting is that Brian Pontarelli, an old friend, really likes Jini and has told me about some of his experiences. I actually looked into it when Bill DeHora published his entry two classic hardbacks. I downloaded Jini and JavaSpaces (Jini is the framework, JavaSpaces is the tuple space repository.) and started playing with it. The final reason that it was an interesting presentation is that JavaSpaces is something that I have never had a chance to use, and didn’t foresee using in the future. By the end of the presentation, I was convinced that this concept deserved more research, if nothing else.

What follows are my scribbled notes from that meeting, along with a smattering of other comments and thoughts regarding these technologies. More information is here, however no presentation artifacts are available, unfortunately.

The problem with distributed systems is that they move data around a lot. What you really want is for the processing and the data to be at most one step apart. Stored procedures do this, but you can’t change the logic easily.

Jini was originally developed for pervasive computing, but the focus of the presentation was on the enterprise applications that can be built based on that spec. This class of applications has some amazing features, including low latency, extremely high throughput and ‘100%’ uptime capability.

For that reason, many large institutions are looking at replacing or augmenting JEE (nee J2EE) applications with JavaSpace applications. He mentioned that GigaSpaces recruited him with the notion that a laptop could run 3 million events an hour. This kind of blew his mind.

JavaSpaces is the command pattern–code and data are distributed, based on Linda. Orbitz uses the technology and talks about 100% uptime. Anyplace where you are batching, you can now do it in real time. The key is to keep everytihing in memory and use replication for persistance, rather than disk. (Eventually you want to push it to disk, for reporting and auditing purposes, but you can do that asynchronously.) Databases tend to be used as a bus between in memory processes right now, and you can replace that with a JavaSpace.

Jini is composed of discrete objects that can run anywhere; more to the point, they don’t care where they run. It also expects failure, as opposed to many other technologies that simply assume that things will run correctly. Jini is a LAN based technology, though Owen mentioned that there are ways to turn it into a WAN technology and cited several examples. I am not competent to give a general overview of Jini–please check out this tutorial for more information.

One thing that really struck me is that all of the complexity that EJB and other JEE technologies hide (clustering, transaction management, thread management, lifecycle), JavaSpaces revels in. Owen actually mentioned that JavaSpaces brings skills that JEE developers currently rarely need to use, like threading and classloading, back into the toolbox, rather than depending on a vendor. That can be a plus and a minus, right? The whole point of not trusting servlet threading to a business developer is that it allows them to focus on the business logic. The problem with much of JEE is that it hasn’t done a very good job of doing this. Do you remember the ‘deployer’ role?

Jini has only interfaces; the named implementations are shipped around transparently. Ha ha, just like EJB remote calls are transparent. However, one very nice aspect of Jini is that when you register an implementation of an interface, you say how long the implementation is going to be available (the lease length). As a service provider, you can keep track of that lease and re-register yourself when it is near to up. Of course, if the service is no longer available, for whatever reason, it is not provided to clients–there’s no need for the JVM to garbage collect. The clients do need to be a bit smart about things though.

As for licensing, version 2.0 has been released under an apache license, as opposed to the Sun Community Source License, which was the previous license. This should grow the jini.org community significantly.

Configuration of Jini takes place with a java syntax, which can be a bit confusing, since you don’t compile and execute it. The names of the services (reggie, webster) are a bit cutesy. Webster is the web server which serves implementation classes, but shouldn’t be used in a production environment. Use Tomcat.

Spring and JavaSpaces are complementary; work is in progress to integrate them and completion is expected in the next few months. GigaSpaces has scaled implementations (linearly!) to 2000 cpus on 500 machines….

At this point Owen began talking about various architectural patterns that could be used with Jini; he also covered some war stories. However, I didn’t take any notes–you’ll have to see him talk sometime.

Issues include (so my friend says) versioning. Owen mentioned that debugging isn’t a strong suit. And I did some parallel computing for my senior thesis so I know that splitting up problems so they can be parallelized is not always as easy as you’d like. However, the web paradigm is actually rather suited to parallelization, since you do have the request/response model. The problem is, as it so often is, state.

Eclipse impressions

I have previously espoused opinions about IDEs. But, I’ve heard great things about Eclipse, including this rather direct statement from a developer who I respect:

Having a solid IDE like IntelliJ or Eclipse so radically improves your productivity that I quite simply don’t see how you can call yourself a professional developer without using one.

So, I thought I’d give Eclipse a try. Again. The latest version is Eclipse 3.1. This time I wasn’t going to try to get by with just the free tutorials. I did some browsing on Amazon and found Eclipse Distilled. This book, while aimed at Eclipse 3.0, is eminently readable and quite informative on the Eclipse way of doing things. All the views and perspectives and projects and jargon can be a bit confusing, so I was happy to pay $35 for this guide.

After using Eclipse for a few weeks, I have some likes and dislikes:

Likes:

  • Code completion: huge. Hitting control-space and choosing a method rather than having to remember exactly what it is named is big. (Charles Petzold talks about a similar feature called IntelliSense in Visual Studio and some of the ramifications. Not sure if all of them apply to Eclipse.)
  • Integration with existing projects: while you can easily start new projects with Eclipse, I was also very impressed with how easy it was to bring an existing codebase into the system and begin using Eclipse to modify it.
  • Refactoring: again, huge. I find that I use the ‘rename method’ refactoring most often. The ability to just change the name of a method in one place and have it propagate allows you much more flexibility.

Dislikes:

  • Using CVS externally confuses Eclipse: I consider myself a power user of CVS. This means it’s often easier for me to drop down and run commands from the prompt. This seems to confuse Eclipse, especially when I’m adding files.
  • No support for local CVS repositories: it’s a known bug, with some workarounds available.
  • Memory usage: 150M of memory is used, even when it is doing nothing. Now, I realize that most new boxes are shipped with gigs and gigs of memory, if you run Eclipse inside VMware with Oracle and Tomcat, eventually things start to get a bit crowded.
  • I have a few other quibbles, but above are the main ones I’ve run into so far.

So, ok, ok. I was wrong. Those of you who have used Eclipse or NetBeans or VisualStudio or IntelliJ or Visual SlickEdit are snoozing right now, but I’ve learned something important. IDEs can be very good and when a free cross platform IDE is available and paired with an external build tool, the results can be powerful indeed.

“Eclipse Distilled” at Amazon.

[tags]eclipse,IDE[/tags]

Exceptions in API design

Here’s an old but fantastic post about API (mis)design.

To handle an exception, you have four choices, one of which is:

Log it! We bought big hard disks for those servers, let’s use them! Log the exception toString() and print its stack trace, but only if you expect the exception to be thrown over 1,000,000 times each day. Alternatively, if you think that the exception would only occur rarely and that it could indicate a problem worth looking at, just print the exception class name … since stack traces just confuse people!

Hilarious. And, it looks like it’s part 4 of a 7 part series. Via Dejan Bosanac.

Running Tomcat on port 80

The typical java web application is fronted by a web server (usually Apache) for a number of reasons. Apache handles static content well, and also is easier to configure to listen on privileged ports (under 1024). I’ve written before about different options for connecting Tomcat and Apache, but there are times when all you need is a servlet engine, and installing Apache is overkill. If you don’t want users to see a nonstandard port in their url (http://foo.com:8080/webapp/), then you have a couple of options.

You can run tomcat as root. This is probably not a good idea, since anyone who can write a jsp can now execute arbitrary commands as root. I don’t know how Tomcat’s security is, but in general, the fewer applications running with super user privileges, the better.

If you share my dislike of Tomcat running as root, here’s an excellent rundown of the options for running Tomcat on port 80. I went the route of jsvc. This seemed to work just fine, though every time we shut down tomcat, we would get an entry in the error log file: jsvc.exec error: Service exit with a return value of 143.

That didn’t start to disturb me until I realized that the destroy method of our servlets weren’t being called. This method cleaned up after the servlet and it was important that it get executed. A bit of googling turned up a discussion of this very problem. The version of jsvc that ships with Tomcat 5.0.27 doesn’t shut down Tomcat very nicely.

I downloaded and compiled subversion, because that’s the version control system that the daemon jakarta project (of which jsvc is a part) used. I then checked out the version of the source tagged daemon-1_0_1 (svn co http://svn.apache.org/repos/asf/jakarta/commons/proper/daemon/tags/daemon-1_0_1/) and rebuilt jsvc. This new version allows tomcat to call the destroy methods of servlets, and everything seems to be happy.

JBoss at Work ships

JBoss at Work by Thomas Marrs and Scott Davis and a book I technically reviewed, is shipping. Having read all of it, I’d say it’s worth a look both for the technical content–the authors take the reader all the way through a standard J2EE application, pointing out all the JBoss specific configurations and gotchas–and for the slightly whimsical and easy to read style. Sometimes it was a be repetitive, but that’s not bad for a book aimed at getting folks without much experience up and running. Read the sample chapter on ear building and deployment and see if it fits with your needs.

InstallAnywhere Impressions

I helped write a java program a few months ago, a product designed to run on mail servers. Now, we had the program packaged up as a zip file (for windows) and a tarball (for unix). That wasn’t my decision, but it makes sense to me–if you are deploying a program on a mail server, you should know how to unzip files and edit configuration files.

But, that’s not what the client wanted. They came back recently with a few changes, and a desire to install via InstallAnywhere. I am no expert at InstallAnywhere, but the client didn’t have the engineering cycles to repackage the program, so they paid me to do it. What follows is my overall impression of InstallAnywhere, and a few tips and tricks.

Overall, I like InstallAnywhere. This program makes it easy to build java program installers for a variety of different platforms (most of the unices, Macs and Windows), execute sundry actions pre and post install, and grab user input while installing. It supports both GUI and console installation procedures. In particular, the Windows installer was a snap, and I didn’t have to learn the first thing about the registry–InstallAnywhere took care of that, even to the point of having the program show up on the ‘Add/Remove Programs’ Control Panel.

On the downside, there are a bevy of options and the help file wasn’t exactly the best. They have a free trial version, but it complains every time you install from a file built with the trial version; such installers stop working around 10 days after you build with the trial version as well–but the trial version doesn’t tell you about the future failure.

A few tips:

* It’s possible to keep the install configuration file in CVS, except for the fact that it hardcodes paths to resources that it includes in the install file. I was able to work around that by using ant’s replace task.

* When you start up the program (on unix), you can’t kill it normally, via either cntrl-c or backgrounding it and running the kill command on the process. I believe this is because the default behavior of a launcher is to listen to the console for stdin. You can change this easily enough, but the default really should be correct.

* The installer builder doesn’t default to generating a log, even though many of the default log messages point you to the install log file. You have to click a checkbox on the Project Info Pane in the Advance Installer.

* The console installer insisted that there were errors in the installation process even though the program, post install, worked fine and there were no errors written in the installer log. I think this is due to the fact that I’m using the trial version, but am not sure.

* There doesn’t seem to be any way in the InstallAnywhere GUI to specify that if the DISPLAY variable is set (on unix systems), the GUI installer should run, otherwise the console installer should run. If you want, you can edit the generated install.bin installer script–search for ‘FAILSAFE’ and use a modern editor capable of long lines–but I couldn’t figure out a way to automate this change. This is my biggest gripe, since this is a very typical demand. If you don’t run install.bin -i console to specify a console installation, you get a lovely exception:

Stack Trace:
java.awt.HeadlessException:
No X11 DISPLAY variable was set, but this program performed an operation which requires it.
        at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:159)
        at java.awt.Window.(Window.java:317)
        at java.awt.Frame.(Frame.java:419)
        at java.awt.Frame.(Frame.java:384)
        at javax.swing.JFrame.(JFrame.java:150)
        at com.zerog.ia.installer.LifeCycleManager.f(DashoA8113)
        at com.zerog.ia.installer.LifeCycleManager.g(DashoA8113)
        at com.zerog.ia.installer.LifeCycleManager.a(DashoA8113)
        at com.zerog.ia.installer.Main.main(DashoA8113)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at com.zerog.lax.LAX.launch(DashoA8113)
        at com.zerog.lax.LAX.main(DashoA8113)
This Application has Unexpectedly Quit: Invocation of this Java Application has caused an InvocationTargetException. This application will now exit. (LAX)

Overall, I’d say if you’re delivering a java based product to typical users, InstallAnywhere is a good choice. I don’t know how much it costs, but the experience for the users is as seamless as you want it to be, the width and breadth of installer customization is impressive, and it didn’t take too long to get up to speed.

Webtop customizations and a java.lang.NoClassDefFoundError

So, a few weeks back, I was working with a webtop customization (webtop is a web interface to Documentum). We were getting a weird error whenever we tried to access it. When I showed details, I saw this message:

com/documentum/web/formext/action/IActionPrecondition

After closing this window, press the Refresh or Reload button on your browser to continue.

Hide Details

Stack Trace:

java.lang.NoClassDefFoundError: com/documentum/web/formext/action/IActionPrecondition at java.lang.ClassLoader.defineClass(Ljava.lang.String;[BIILjava.security.ProtectionDomain;)Ljava.lang.Class;(Unknown Source)
at java.security.SecureClassLoader.defineClass(Ljava.lang.String;[BIILjava.security.CodeSource;)Ljava.lang.Class;(SecureClassLoader.java:123)
...

We spent the better part of the day debugging it. The IActionPrecondition class was in the WEB-INF/classes directory of the webtop web app. It looked just the same as it had before (no recent modifications). When we commented out the Precondition, we didn’t see this error, but still couldn’t use the customization. It happened on Unix and on Windows.

We were msytified, and ended up having to go back to a system that worked, then move forward very slowly until the system didn’t work, then focus on those changes (all hail CVS).

What we found suprised the heck out of me. Basically, we have a jar file of all the TBOs and documentum utilities that we have to put in the system classpath, because it needs to be loaded when the dfc.jar is loaded. (These are the words of the documentum experts I was working with.) The webtop custom classes were inadvertantly included in this jar file, and were loaded by the system classloader. But some of these classes depended on classes in the webtop web apps classes directly, loaded by that descendant classloader. Hence, the custom classes were couldn’t find their super classes and were invalid. Once we moved the custom webtop classes out of the TBO jar files, everything was good as gold.

I once worked with a colleague who said, rather than calling her a senior software developer, she sometimes felt she should be called a senior classpath debugger. Indeed.