Standing in the Field
Notes from SJS Application Server Field Engineering
Roughly four years ago, Scott McNealy addressed a group of iPlanet employees, including myself. This was in the middle of the dot com boom, and there were still a lot of questions about what direction the internet was going to take. At the end of his speech, Scott commented that there were a lot of companies trying to make the web proprietary and trying to lock customers into proprietary solutions. He said that Sun was in a battle to keep the network open and to develop solutions that favored customers and not vendors. The last part I remember exactly. He said, "isn't it nice to be working for the company wearing the white hat."*
That has always been important to me. It has been nice to be working for the company in the white hat. Sun has had its ups and down since then and so has its stock price. I've been re-org'd more times than I care to count. But Sun has never wavered from its commitment to openness, to its customers, and to operating with integrity.
There are lots of companies out there that claim to be open. But most of them are just interested in how they can make money from the open source contributions of others. A lot of companies "talk the talk, but don't walk the walk" when it comes to contributing to the commons. Executive management at Sun may not have always been effective at "talking the talk", but Sun has always "walked the walk" about open source and open platforms.
However, the time has come for me to leave Sun.
After working with Java since version 1.1 and J2EE from the very beginning, it's time for me to move up the software stack. I've accepted a position with Fuego, a leader in the exciting market of BPM software (business process management). I see in Fuego the same commitment to customers, to innovation, and to openness. And I believe that BPM software will be the key to making Service Oriented Architecture (SOA) a reality. BPM is the glue that will make everything (people/processes/systems/services) work together.
Anyway, thanks to all of those of you who have read my blog or had me come to your company. You can now find me blogging at BPM Blog. ( RSS feed ) My first couple of posts will be about why I've made this move and why I think that people who are interested in J2EE should be learning about BPM. (I promise I'll be better at keep the blog updated. I have a lot of plane rides ahead of me: lots of time to be writing blog posts. :-) )
Thanks again for reading and I hope to see you at BPM Blog. (If you have trouble connecting today, the DNS may not have propogated to you yet. Try again tomorrow.)
David
*For those non-US readers not familiar with the expression "white hat", old western movies stereotypically had the good characters wearing white cowboy hats and the villains wearing black hats.
(2005-05-20 10:44:57.0) Permalink Comments [1]So everyone is calling IBM's acquisition of Gluecode a move to create a "JBoss killer". Gluecode is a very small company that provides an integrated application server suite around the Apache Geronimo application server, some other related Apache projects, and some management and monitoring tools.
Since there have been rumors of IBM trying to create an open source implementation of J2SE, it's entirely reasonable that IBM would want an open source application server to go with their J2SE implementation. All in all, I find the acquisition interesting, but not earth shattering. Geronimo and the other Apache projects have existed for some time now and I don't think that IBM's acquisition of Gluecode really changes the industry that much.
But Marc Fleury, Founder/Chairman/CEO of JBoss, is his usual acerbic self. (Fleury was the one who called the Apache Foundation “a bunch of fat ladies drinking tea” and who got caught "astroturfing" TheServerSide.)
Like Rich, I can't follow Marc's argument about why this acquisition is bad for Sun. Sun would love to see an open source implementation of J2EE that is more than grudgingly J2EE certified. (When Marc spoke at my JUG he complained at length about what a waste of time the J2EE certification is.) And with a couple million downloads of Sun Application Server, I'm not sure that Sun has to worry about Gluecode suddenly becoming a volume play that takes Sun out of the picture. All things considered, IBM has been a good friend to Java and I can't see IBM making an investment in open source as any kind of threat to the integrity of the Java Community Process.
(2005-05-13 20:21:10.0) Permalink Comments [1]
Sun has a very colorful past when it comes to open letters. We write them. We receive them. The press has a field day talking about them.
So, given the fact that "Standing in the Field" gets a reasonable amount of traffic from the publicity of blogs.sun.com, I'd like to continue this Sun tradition of open letters.
To: Jane Ogren
janeogren.com
Dear Mom:
I wanted to take advantage of this public space to tell you how much I love you and I how proud I am to be your son.
I am proud of the values you have instilled in me. I am even more proud that you were able to instill those values so subtly while still allowing me to pick my own paths and make my own mistakes. Growing up I always thought I took after Dad. But as an adult I see how much of you there is in me.
I am proud of your art. I am proud of all of your work: as a mom, as a teacher, as an artist, as a volunteer, and as a citizen.
Thank you for continuing to teach me things as an adult. Thanks for listening to me. Thank you for being an example I can be proud of. Thank you for always being there for me.
In the end, the results speak for themselves. Your children love you and we've both managed to be happy and well adjusted. Our family has a deep bond. We may live further away that you like, but we always feel you near.
Happy Mother's Day. You are the best Mom ever.
Love,(2005-05-08 07:58:52.0) Permalink
David
Lots of people are familiar with the Sun Java System Application Server Platform Edition as the J2EE server they downloaded as part of the J2EE SDK. But I bet a lot of those people don't realize that you can use that application server in production for free.
Sun has now published some impressive SPEC benchmarks for SJS Application Server Platform Edition. I can't wait to see the price/performance numbers for this configuration. With a free application server and the price/performance of the V20Z Opteron servers it's going to be very impressive.
Thanks to Rich for both the original link and for all of the work he and his product team have done to make this possible.
(2005-04-28 21:33:50.0) PermalinkNews.com had an article recently about how many PC users do a bad job of wiping their hard drive before selling, donating, or trashing their PC. Obviously this is a bad thing considering the sensitive information the average person keeps on their PC.
I understand why this is difficult for the average PC user. Wiping a hard drive is a tedious process for a PC. Properly wiping a hard drive is very easy on a Mac, however. If you are planning on selling or giving away your Mac, here are some easy steps,/p>
Things you will need:
Steps to permanently erase the hard drive of an old Mac.
Nothing really too much different than a PC, but the built-in secure disk overwrite and firewire target mode makes it much more convenient on a Mac.
(2005-04-22 10:10:34.0) PermalinkSeveralpeople have blogged this new java.net project that is a DTrace agent for Java. Since I've talked about DTrace and Java previously I figured that I should throw in my two cents. (Especially since I've been less than diligent in posting recently.) This project is very interesting and I'm hoping that it will be available as part of the standard JDK in the future. Most interestly, it has several probes that are going to allow DTrace to instrument method calls and GC events.
But it's important for me to note that you can already use DTrace to probe Java applications. Most notably jstack() does a reasonably good job of translating stacks into Java method names. Don't assume that you need this module to use DTrace effectively on a Java application. The new module does allow you to be more proactive and has some very useful trace points for doing active troubleshooting, but don't discount the out of the box DTrace functionality.
Also, the downside of this module is that it has to be actively installed into the JVM. Which takes away one of the advantages of DTrace: the fact that it is pre-integrated into the OS.
It is great to see this module. And it's especially great to see this as a java.net project with CDDL licensed source code. But it's probably best to just see this as a technology preview for what we will see as far as DTrace integration in Mustang.
(2005-04-20 11:18:57.0) PermalinkOne of my projects this month is to review the Sun Java System Application Server Performance Tuning Guide. This is part of a combined effort between the field and the product team to review the complete documentation set.
It's interesting work. I've always found this document helpful but sitting down and reviewing it page by page has been a much different experience. There are a lot of places I'd make it more concise or differently organized.
So, take a look at the Perf Guide with me. I'll include any comments and feedback with my own review for the documentation team.
(2005-04-05 21:31:38.0) Permalink Comments [1]Tim Bray (the co-worker I developed my Jython instructions for) tells me that he needs to run his installation headless. Which I didn't document last time. So here is an addendum to my previous instructions if you don't have X11 access to your box.
When executing the SJSAS installer (j2eesdk-1401_2005Q1-solaris-i586.bin or similar), just add the argument of "-console". This runs the installer in text mode. Using the installer is generally self-explanatory but there some basic instructions displayed when the installer begins. Just type in the same options you would have selected in the graphical installer.
When executing the Jython installer add the argument "-o /install-jython". Adjusting for the directory where you want Jython, of course. ("java jython-21 -o /install-jython") This skips the installer completely and makes an installation in the specified directory.
At this point you can perform the rest of the installation instructions as they are all browser and command line oriented. However, just for reference, I'll show how to use the command line to deploy the application template rather than the browser based admin GUI.
The deployment command is as follows: "/install-dir/bin/asadmin deploy --name jythondemo --contextroot jythondemo /install-dir/samples/quickstart/hello.war " The admin server must be started for this command to work.
(2005-03-28 09:22:15.0) Permalink Comments [1]A co-worker of mine was doing a project that used servlets written. Sean McGrath wrote a tutorial on getting this up and running. Unfortunately, the tutorial assumes that you will be deploying to Tomcat. I wanted to encourage my co-worker to use Sun Java System Application Platform Edition, so I volunteered to get Jython servlets running on Sun's appserver.
There really isn't any significant difference in the way Jython works on these two platforms, but for those who are new to SJS Application Server's filesystem layout and administrative interface this document may be helpful. After following these instructions you will want to go read Sean's original documentation as this document only covers the server setup and not the Jython programming.
All code is the property of Sean McGrath as I just tweaked the install instructions a little.
This document was developed using Sun Java Application Server Platform Edition 8.1 2005Q1 UR1 on Solaris 10 x86 Edition. However, these instructions should work for any platform, and for any edition of SJS Application Server. (This document is written with UNIX style PATH names and command prompts, however. Adjust appropriately for Windows machines.)
These instructions do not assume root privileges, although you will need to choose directories and ports appropriate to your account. It is assumed that you have a monitor. There are headless installation alternatives available for both SJS Application Server and Jython, but they are not covered in this document.
Download Sun Java System Application Server from http://www.sun.com/downloads. These instructions assume that you are using the "All-In-One-Bundle".
Make the downloaded file executable : "chmod +x j2eesdk-1401_2005Q1-solaris-i586.bin". (Your filename may differ if you are running a different patch level or platform.)
Now start your application server by executing "/install-dir/bin/asadmin start-domain" (adjusting for your installation directory). You should now be able to reach the application server on http://localhost:8080 and the adminstrative interface on http://localhost:4848 .
Download Jython 2.1 from http://www.jython.org/download.html .
(More detailed instructions can be found at http://www.jython.org/install.html . These documents include some platform specific notes and troubleshooting tips if you run into trouble.)
Jython is distributed as a self-extracting Java class file. You can run the installer by executing "java jython-21". If you do not have java in your PATH, you can use the java installed as part of the Application Server "/install-dir/jdk/bin/java jython-21".
You should now be able to bring up a jython command line. Execute "/install-jython/jython" (substituting your own install directory) and jython will give you an interactive command line after doing some initial processing. Type ^c twice or ^d once to exit from the command line.
Rather than compiling and deploying a web application from the command line as the Sean does in his Tomcat instructions, we will use one of the sample applications that comes with SJS Application Server as a template.
We will expand this simple hello world application with Jython in the next section.
First we will add the jython library to the CLASSPATH of the server by placing it in the server's lib directory. Copy the jython.jar directory from your Jython install into the application server's lib directory. ( "cp /install-jython/jython.jar /intall-dir/lib/" )
Change your directory to the home of the application you just deployed by executing "cd /install-dir/domains/domain1/applications/j2ee-modules/jythondemo/WEB-INF". (As always, adjusting for your installation directory.) Here you will find the web.xml file that describes the application to the application server.
We will add a line to this configuration file that tells SJS Application to use Jython to execute requests ending in ".py". Replace the existing web.xml with the following:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http:/
/www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml
/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>hello</display-name>
<distributable/>
<servlet>
<servlet-name>PyServlet</servlet-name>
<servlet-class>org.python.util.PyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>PyServlet</servlet-name>
<url-pattern>*.py</url-pattern>
</servlet-mapping>
</web-app>
While we are in this directory we will also copy the Jython libraries into the application's web context so that we can use later. ("cp -r /install-jython/Lib lib")
We will now use Sean's first Jython servlet to test that everything is working. Go up one directory to /install-dir/domains/domain1/applications/j2ee-modules/jythondemo/ ("cd ..") and create a JythonServlet1.py file with the following code:
from javax.servlet.http import HttpServlet
class JythonServlet1 (HttpServlet):
def doGet(self,request,response):
self.doPost (request,response)
def doPost(self,request,response):
toClient = response.getWriter()
response.setContentType ("text/html")
toClient.println ("<html><head><title>Servlet Test</title>" +
"<body><h1>Servlet Test</h1></body></html>")
You will now need to restart the server so that it picks up the changes to the web.xml file. (You can add more Jython Servlets without restarting: it is only the web.xml changes that require a restart.) Execute "/install-dir/bin/asadmin stop-domain" to stop the server and "/install-dir/bin/asadmin start-domain" to restart it.
Once the server has restarted you should be able to access your test Jython servlet from "http://localhost:8080/jythondemo/JythonServlet1.py".
Just to make sure that the Python library is accessable we will also deploy Sean's third sample. Put the following code in JythonServlet3.py
import sys,calendar,time
from java.io import *
from javax.servlet.http import HttpServlet
from JythonServletUtils import *
class JythonServlet3 (HttpServlet):
def doGet(self,request,response):
self.doPost (request,response)
def doPost(self,request,response):
toClient = response.getWriter()
response.setContentType ("text/html")
toClient.println ("<html><head><title>Servlet Test 3</title>")
toClient.println ("<body><h1>Calendar</h1><pre>%s</pre></body></html>" %
calendar.calendar(time.localtime()[0]))
if __name__ == "__main__":
JS3 = JythonServlet3()
dummyRequest = DummyHttpRequest()
dummyResponse = DummyHttpResponse()
JS3.doPost (dummyRequest,dummyResponse)
You will also need to create the JythonServletUtils module referenced in that code. Create another file called JythonServletUtils.py with the following code:
from java.lang import System
class DummyHttpRequest:
pass
class DummyHttpResponse:
def setContentType(self,t):
System.out.println ("Content-Type:%s" % t)
def getWriter (self):
return System.out
After creating these files you should be able to access Sean's calendar sample on http://localhost:8080/jythondemo/JythonServlet3.py .
Hopefully all of these instructions worked for you and you are now productively writing Jython servlets. Make sure to read Sean's tutorial for more about programming servlets in Jython and keep watching his blog for more updates to the tutorial.
(2005-03-24 13:48:16.0) Permalink Comments [2]Yeah, I know. Almost a month since my last post and a long time since I've been posting regularly. February was insane. In addition to just being a busy month in general, I took on a major project for my boss's boss that I knew I didn't have time for. Plus I was a speaker at CEC and had a huge amount of prep work for that. And then I got sick (probably from working the crazy schedule I was working).
But things are better now. Once I clear some backlog I have lots of stuff I want to post. But one thing caught my attention today that should be quick to post.
Tim Bray points out the benefits of blogging on your personal career. It made me laugh because it reminded me so much of Jonathan's talk at CEC.
Like many of Jonathan's talks, his opening line was: "Do you read my blog? You should.". His next comment (if I recall correctly) was encouraging the audience to blog. Immediately afterwards, he asks if John Clingan is in the audience. I've seen him do this multiple times now.
John is clearly a rock star. (Or techno celeb if you prefer Mary's terminology.)
When your boss's boss's boss's boss's boss's boss asks for you by name when walking into a crowd of thousands because of your celeb status, that has to be good for both your career and your job satisfaction.
I'll take it as a lesson learned and try to get back to blogging regularly. :-)
(2005-03-10 12:56:09.0) PermalinkJust a quick link, as I'm working on a couple of big deadlines that have kept me away from the blog.
GTD is fundamentally about lists and trusting those lists. Jason Clarke makes some insightful points about how people use email inboxes as lists. He says that there are "filers" who use folders to archive messages and "pilers" who just stack stuff in their inbox. And that pilers tend to be lose todo items as email drops off of the first screen of the inbox.
How true. Even though I'm working a ninety hour week this week I don't feel like I can let my inbox grow out of control. If I don't have my inbox completely empty once a day I feel like I'm losing control. Every email isn't "done", but I've at least done a GTD workflow on every item. In other words, I've either deleted, filed, done, delegated or deferred each item before 24 hours has passed.
Jason also has some ideas about how to better use email folders. I think I'll follow his advice and simplify my IMAP folders to make searching my mail archives easier.
(2005-02-11 07:32:34.0) Permalink Comments [2]This is the wrap up to my series about Java Metadata (aka annotations). Part one here, part two here.
In the previous two sections I discuss the basics of metadata and some of the interesting advanced functionality available through the apt annotation processing tool. In this section I'll present some of my thoughts about how metadata affects the future of Java and some of the pros and cons of using metadata today.
If you've ever heard the Java engineering folks (both J2SE and J2EE) speak, you'll know that they are very excited about the possibilities that metadata brings to Java. Both the J2SE and J2EE teams are looking for ways to make Java easier to learn and use. Metadata is a way for them to reduce lines of code required while at the same time as improving code readability and tools support.
There are lots of examples of using metadata in active JSRs. Metadata is going to be used to enable declarative web services, O/R Mapping, and the next version of CMP. There is also the hope that tools vendors are going to find interesting ways to use annotations. Perhaps custom annotation libraries. Perhaps GUI tools that use annotation markers to enable full lifecycle development.
But it is easy to see that while metadata may not be commonplace in production code yet, a couple of years it is going to be an integral part of Java. Knowing how to use metadata will be as important as knowing how to use the Collection library or deployment descriptors.
Unfortunately, however, metadata is still on the bleeding edge of Java. Java 5.0 has dipped its toe into the water of declarative programming by introducing metadata into the language. But using the more advanced features, like modifying the behavior of the compiler, requires you to use apt and APIs like com.mirror.* that aren't guaranteed not to change. Not to mention that tools like apt do not have any tools support and have some known bugs. It was very frustrating not being able to use tools like ant in my demo because of their lack of apt support. Not to mention the fact that my IDE wasn't smart enough to know that the Factory classes were going to be autogenerated.
As much as the J2SE team would like to see apt begin to replace other tools like XDoclet, there are still significant gaps that have to be addressed before apt becomes a mainstream tool. It reminds me of the early days of JSP where you had to be careful about which API's you used because of the differences between the 0.92, 1.0, and 1.1 specifications.
I can't really recommend that creating customer annotation processors be a part of enterprise application development yet. The tool support and API stability isn't there yet. But there isn't any question that metadata is going to be a fact of life for Java developers. Nor is there any doubt about the power that annotations will be able to bring to developers and tool vendors.
So I recommend that everyone get familiar with the basic syntax of Java annotations. It's going to be an absolute requirement for using J2EE 5.0. But I'd also recommend that people start to experiment with what is possible in apt. When you are architecting your code, think about what would be better expressed as declarative functionality and how you might implement it as an annotation. Write a demo application or step through mine to learn some of the possibilities for metatdata.
Because, based on the current JSRs, metadata is going to be adopted very rapidly by the Java community. Being able to demonstrate your ability to extend the functionality of Java via metadata will be a very powerful competitive differentiator for developers.
(2005-02-01 19:16:52.0) PermalinkSo I have a couple of websites I maintain as side projects. The one I've been spending the most time recently is my Mom's fabric arts page. Because of some of the precise layout that my Mom wanted, I used XHTML and CSS to layout the pages. Which is great: CSS is easier to do than table based layout and greatly reduces the page size. But browser compatibility has haunted me somewhat.
The nastiest example so far was that my Mom updated a couple of the pages to make a couple of minor tweaks. But once she made the changes the pages no longer rendered correctly in Internet Explorer. I was trying to troubleshoot the problem, but couldn't find anything wrong with the code. It was the exact same CSS style code as all of the other pages.
It turns out that Outlook Express had added an HTML comment to the top of the HTML page when my Mom saved some versions of the page we had been emailing back and forth. I hadn't noticed it because it was just a comment. But as HTML gurus know, browsers look at the first line of the HTML to determine the DOCTYPE and sometimes adjust their rendering based on that DOCTYPE. IE was rendering most of the pages correctly because it was using a strict XHTML mode. But with the comment at the top it must have fallen back to a quirks mode that didn't render the page correctly.
This triggered one of my longtime frustrations: comments that affect behavior. In everyone's first computer language class they are taught that comments are for increasing code readability but do not affect the functionality of the application. Shortly thereafter, however, we start learning all of the little exceptions to that rule. UNIX scripts declare their interpretive shell in comments. Java embedds its API documentation in comments. HTML embeds style sheet and script information in comments.
This has always bothered me. It seems a dangerous practice that violates the "contract" between developers and their code. This IE troubleshooting was a perfect example: I completely ignored the comment because my mind just automatically ignored it. And Dreamweaver helped me ignore it by greying it out. Both Dreamweaver and I were making the bad assumption that comments don't affect the layout. I've learned to just accept this practice because we've needed these hacks. JavaDoc is a great feature of Java even if it violates my aesthetics about comments.
Metadata, however, has the chance to clean up some of these hacks, at least in the Java world. It gives a way to let tools interact with source code without having to hide the interaction from the compiler using comments. HAll of the examples above (JavaDoc, shell interpreters, HTML styles/scripts, and HTML DOCTYPES) are really examples of application metadata. Now that Java has a declarative method for Metadata, hopefully we can move forward with cleaner code.
(2005-01-31 12:02:28.0) PermalinkThis week I'm out at Sun's corporate offices in California. A bunch of us software folks are getting some advanced training and some face time with the product engineering folks. This is a good thing, especially considering the new versions of Java Enterprise System and Sun Java System Application Server around the corner.
Unfortunately, I live in an area with relatively poor access to airports. I can drive east an hour and a half to a major airport or I can drive west an hour and a half to a smaller airport. Driving to the major airport is tough during rush hour and is susceptible to all kinds of traffic, parking, and security delays. The smaller airport, Harrisburg International, is a much more pleasant drive, the service is great, and it's generally just easier getting in and out. They even promote themselves as the "antidote to the big airport".
So, when the flight schedules work out, I choose to fly out of the smaller airport.
Now I'm a pretty seasoned business traveler. I think that I'm pretty savvy about the whole process. I pack light. I always have my iPod so that I can kill time. But most importantly I try to keep a positive attitude about the whole experience. Because when you travel frequently, you are going to run into problems sooner or later. My cardinal rule for travel is to be nice to gate agents and to trust them to help you. I've had lots of issues when traveling, some of them my own fault, but the airlines have always done the best the could for me. (Everyone should have to watch the TV show Airline before getting upset with a gate agent.)
So when I ran late on Sunday and missed my flight I was optimistic that the airline would be able to help me out. Unfortunately, however, being at a smaller airport does limit your choices significantly. Harrisburg only has a dozen or so gates, and they are divided between a lot of airlines. Combine that with a flood of people heading to San Francisco for MacWorld and some bad weather on the west coast and I was pretty much out of luck. I'm stunned at how crazy San Francisco is over MacWorld. The flights are packed. Changing car and hotel arrangements has been near impossible.
Anyway it's been a stressful day and a half getting here. I finally made it to my hotel room. I guess the lesson to learn from this experience is to know when the system has a small tolerance for failure and to plan accordingly. I suppose the same thing could be said about software system design. A skilled system architect should be able to know in advance where to the stress points of a system will be and to put plans in place to reduce those risks.
That's a big stretch to try and create an analogy, but you'll have to forgive me. It's a been a long day.
(2005-01-10 23:42:08.0) PermalinkI really have to stop taking on big blog articles like this. They always take a lot longer to write than I expect. It's been a month since the last article in this series, but here we go with part two on my annotations preso, converted to blog format. Sorry it's so long, I really should have broken it into smaller posts. But I got on a roll and since I'm a month late I figured I might as well get it done.
Last article was just a basic introduction to the concept of Metadata. We introduced annotations, the reason behind introducing them into Java, the basic syntax for using annotations, and the built-in annotations.
Now that we understand the basics of metadata, we can now take a look at the more interesting topic of how to create our own metadata. The pre-built annotations have a certain amount of value, but creating your own annotations is where metadata gets interesting. (At least until EJB 3.0 and J2EE 1.5, which will have lots of time saving new annotations.)
Why would we want to create a new annotation?
@inject (arg="ObjectPoolSize", field="size")
@todo (owner="David F. Ogren", note="upgrade")
There are several articles on the web about how to add markers to code and how to use reflection to allow code to inspect itself. I'd recommend starting with the annotations section of the language guide. It walks through creating a new marker interface @Preliminary and creating a single valued interface of @Copyright. It also creates a @Test marker interface and then shows how to use reflection to determine if that marker exists for a given class.
I'm not going to take a lot of time detail those simple examples again here. In summary, creating new annotations is a lot like creating new interfaces. The major difference (besides the @ symbol) is that you have to be conscious of the meta annotations such as @Retention and @Target. And there are some new methods and in the java.lang.reflect package such as isAnnotationpresent and getDeclaredAnnotations that allow you to detect and manipulate annotations. Here's a quick example of a single value custom annotation with a default value.
package AnnotationDemo;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Maintainer {
String value() default "unknown";
}
And here is a little command line app to parse for those annotations:
package AnnotationDemo;
//This class accepts a class and returns the value of the @maintainer
//annotation. (If the targeted class has one.)
@Maintainer(value="ogren")
public class SingleValueDemo {
public static String findMaintainer(String className) {
String maintainer;
try {
Maintainer notation = (Maintainer)
Class.forName(className).getAnnotation(Maintainer.class);
if (notation!=null) {
maintainer="Maintainer is " + notation.value();
}
else {
maintainer="Class exists but is not annotated with maintainer.";
}
} catch (ClassNotFoundException e) {
maintainer= "No such class found";
}
return maintainer;
}
public static void main(String[] args) {
if (args[0]!=null) {
System.out.println(findMaintainer(args[0]));
} else
System.out.println("Usage: SingleValueDemo classname");
}
}
But if you want to change the compiler behavior, the most interesting part of annotations in my opinion, you have to venture into the world of apt and annotation factories.
apt is a javac replacement that finds and executes annotation processors. Yes, this means that you have to change your build chain if you want to use these new features. If you use javac directly it will not perform all of your custom metadata enhanced behavior.
Annotation Factories implement a simple interface with three methods: getProcessorFor, supportedAnnotationTypes, and supportedOptions. These three methods allow the apt tool to determine which factories are interested in which annotation types and to obtain a AnnotationProcessor object for a given annotation type.
Here is the bulk of the code from the annotation factory I use in my demo: (for the reasons I explained a while ago, I'm not going post all of the code.)
public class PooledFactoryApf implements AnnotationProcessorFactory {
private static final String FQTAGNAME = "AnnotationDemo.PooledFactory";
//(This commented line is how you would declare the annotations you support.
//So that this demo can watch all
//annotations, however, this factory tells apt we process all ("*") annotations.
// private static final Collection supportedAnnotations =
// Collections.unmodifiableCollection(Arrays.asList("AnnotationDemo.PooledFactory"));
private static final Collection supportedAnnotations =
Collections.unmodifiableCollection(Arrays.asList("*"));
private static final Collection supportedOptions = emptySet();
public Collection supportedAnnotationTypes() {
return supportedAnnotations;
}
public Collection supportedOptions() {
return supportedOptions;
}
public AnnotationProcessorFor(
Set atds, AnnotationProcessorEnvironment env) {
return new PooledFactoryProcessor(env);
}
}
The code is pretty self explanatory. You return collections of Strings defining the annotations and options you support. You can use wildcards in those Strings to support ranges of items. My particular demo accepts all annotations (just for demo purposes) and no options. And when asked for a AnnotationProcessor the factory blindly returns the PooledFactoryProcessor which I define below, regardless of what the annotation is or what is going on in the environment.
The AnnotationProcessor is the place where we we actually place our customized behavior. AnnotationProcessors support a simple interface with only one method: process(). Once the apt tool has obtained the correct AnnotationProcessor from the factory, it will call the process method. Once we receive this call to the process method we can use the environment (which we received during construction) to search for instances the annotation we are looking for and perform our custom behavior.
For my demo application I developed an annotation that would automagically generate the code to implement a factory pattern classes it decorates. In theory, this could be used to automatically make any class a pooled resource, although I built this as a proof of concept and didn't actually add the logic to implement pooling. Here is the declaration for my annotation:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface PooledFactory {
int poolSize();
}
As you can see it is a single valued annotation that marks types (classes and interfaces) and is discarded after compilation. The annotation is discarded after compilation because its job generating the factory code is complete after compile time. Another thing to note is that legally this annotation could be placed on an interface since the annotation syntax considers both classes and interfaces to be types. My demo does throw an error at compile time if an interface is marked with @PooledFactory, but it will not show up as syntactically invalid in an IDE. The following is a simple illustration of how the @PooledFactory annotation might be used:
@PooledFactory(poolSize=4)
public class ExpensiveClass {
ExpensiveClass() {
System.out.println(
"For some reason, I take a long time to create so I am being pooled.");
}
public void someMethod() {
System.out.println("Some business method is executing");
}
}
Clients would then access an instance via the automatically generated factory class:
ExpensiveClass myClass = ExpensiveClassFactory.getInstance(); myClass.someMethod(); ExpensiveClassFactory.returnInstance(myClass);
To me, this is why annotations are exciting. We have just extended Java with a new piece of declarative functionality. If we are using a profiler and discover a bottleneck creating ExpensiveClass objects we can attempt a fix with three lines of code (one to add the annotation, one to change the allocation, and one to return the object to the pool). We also get the added benefits of code readability and code reuse.
So let's implement the code generation in the AnnotationProcessor. The first step is to receive the process() call from apt and find all of the classes that are marked with our annotation:
private final AnnotationProcessorEnvironment env;
private final AnnotationTypeDeclaration myType;
PooledFactoryProcessor(AnnotationProcessorEnvironment env) {
this.env = env;
this.myType = (AnnotationTypeDeclaration)
env.getTypeDeclaration("AnnotationDemo.PooledFactory");
}
public void process() {
Collection annotatedClasses =
env.getDeclarationsAnnotatedWith(myType);
for (Declaration decl : annotatedClasses) {
if (decl instanceof ClassDeclaration)
//this method creates the new source file
createFactorySource((ClassDeclaration) decl);
else
env.getMessager().printWarning(
"An interface was marked with @PooledFactory");
}
}
In the constructor we save the environment we receive from the factory and save the AnnotationTypeDeclaration we are looking for. Once in the process method all we have to do is use the com.sun.mirror.apt.AnnotationProcessorEnvironment to get a list of types that implement our annotation and then iterate over that Collection (using the new foreach style of for loop). As mentioned before we have to check to make sure that our types are classes and then we can execute our custom method (below) to generate the new java source. Notice how that we can print a compiler warning via the AnnotationProcessorEnvironment object. We could use the same technique to implement annotations that perform syntax checking or analysis.
The createFactorySource method is were we actually generate the new source file for the factory class. This demo uses a very simplistic method for doing so, shown below:
void createFactorySource(ClassDeclaration decl) {
//Determine new class name
String realClass = decl.getSimpleName();
String newClassName = realClass + "Factory";
String newQualName = decl.getQualifiedName() + "Factory";
System.out.println("Attempting to create factory :" + newQualName);
try {
PrintWriter out = env.getFiler().createSourceFile(newQualName);
out.println("//autogenerated by PooledFactoryProcessor");
out.println("package " + decl.getPackage() + ";");
out.println("public class " + newClassName +" { ");
out.println("public static " + realClass +
" getInstance() { return new " + realClass + "(); }");
out.println("public static void returnInstance( " + realClass +
" old) { }");
out.println("}");
} catch (java.io.IOException e) {
System.out.println("Factory already exists or cannot be created");
}
}
This class is a bit of a hack, but it shows the basics of source code generation. We figure out what class name (and qualified class name) to use and then use the AnnotationProcessorEnvironment to get a PrintWriter leading to a new source file. (The new source file ends up getting created in a temp directory that can be specified using the -s apt argument.) We then write out the new code to PrintWriter and apt handles the rest. It will automatically pass the new code through the AnnotationFactory again since we might have annotations in our generated code. After that recursive step completes it will then use javac to compile all of the code, including our generated code. At that point ExpensiveClassFactory exists just as if it had been coded by hand.
Obviously this is just a little demo hack and doesn't do any real pooling. Real world code generation takes more than six println statements. But it illustrates the basics of modifying compile time behavior and its a lot more fun than the little reflection samples I see in most annotation tutorials. The same techniques cold be used to do all kinds of powerful compile time behavior.
In the next an final chapter, I'll share some my closing thoughts about metadata. I'll talk about the pros and cons of apt and the future of annoations, such as how metadata will be used in J2EE 1.5 and EJB 3.0.
(2005-01-06 06:47:40.0) Permalink| « August 2008 | ||||||
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
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 | ||||||
| Today | ||||||