<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-15028967</id><updated>2011-12-08T07:26:32.561-06:00</updated><category term='Bohemia'/><category term='Web Development'/><category term='General Java'/><category term='Metablogging'/><category term='Pure Java'/><category term='Framework'/><title type='text'>Java Planet</title><subtitle type='html'>Handy items for Java programmers
(This page may look weird in Internet Explorer -
you should be using Firefox!)</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>23</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-15028967.post-8663099041167911289</id><published>2009-11-30T14:30:00.001-06:00</published><updated>2009-12-16T10:58:50.991-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Development'/><category scheme='http://www.blogger.com/atom/ns#' term='General Java'/><title type='text'>JSP Considered Harmful</title><content type='html'>&lt;span class="subhead"&gt;Why JSPs aren't good in Model 2 web applications&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Model 1&lt;/strong&gt; is a design approach for web applications. In this approach, one servlet is responsible for handling a browser request from beginning to end - it receives the request, runs the business logic that performs the necessary operations (which more often than not involves database access), and generates output in HTML (or possibly in other forms such as XML, PDF and image data; for the purposes of this article I'm concerned primarily with HTML).&lt;br /&gt;&lt;br /&gt;Model 1 is okay for small web applications but it really isn't very well suited to larger ones; because the view - the part that generates the HTML - is embedded in the same servlet that performs the business logic, making modifications to the view risks damaging the business logic. Imagine that you have a fairly large application and you decide to update the look and feel of the whole website; you'd have to modify every servlet.&lt;br /&gt;&lt;br /&gt;Early Model 1 applications written as servlets had another problem in that the generation of HTML was performed by Java code. That meant that the servlet developers had to write code to generate that HTML, usually working with a web designer who provided the HTML. Obviously this wasn't a very efficient way of working.&lt;br /&gt;&lt;br /&gt;The answer to this was JSP, which has often been described as 'inside-out servlets'. The idea is simple: instead of writing servlets that perform business logic and embed the HTML, a JSP is essentially an HTML file that has the business logic embedded as 'scriptlets' of explicit Java code. At run time a JSP compiler translates the JSP to Java source then compiles that source to class binaries that are then loaded in the normal way. (In some systems the JSP compiler generates bytecodes directly, without the intermediate Java source). This means that HTML designers can produce basic JSPs then hand the result over to a Java developer who can add in the scriptlets to perform the business logic.&lt;br /&gt;&lt;br /&gt;However the business logic and the view are still contained in one file, which can still lead to the same kinds of problems - if you change the page layout you risk damaging the business logic and vice-versa.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Model 2&lt;/strong&gt; is intended to solve this problem. Model 2 decouples the business logic from the view, minimising the dependencies and making it possible, at least in principle, to make changes to the business logic or to the view without requiring changes on the other side of the fence.&lt;br /&gt;&lt;br /&gt;This is where the problems begin for JSP. It's very easy to embed business logic in a JSP - that's the whole point when you consider that JSP is a Model 1 technology. However it's this very same point that makes JSP a bad fit for Model 2; it's simply too easy to drop code that really belongs in the business logic into a JSP. As a result novice developers often do just that and even seasoned developers do it from time to time, especially when a quick'n'dirty fix is needed to cure a production problem in a hurry. The result is business logic that's split between the servlet where it belongs and the JSP that should really contain only view logic. Ideally the scriptlets in a JSP for a Model 2 application should be doing no more than pulling data from the model and formatting it for presentation; any more than that probably belongs in the business logic. Maintaining this separation is a matter of discipline on the part of the developer because there's nothing in JSP to enforce it.&lt;br /&gt;&lt;br /&gt;Struts was probably the first Model 2 framework for Java web applications although it's difficult to be certain. According to an article I read a year or two ago (I don't remember where I read it or who wrote it, and I haven't been able to find it through Google, so I can't back any of this up...), the people who developed Struts recognised that this was a problem. However they released Struts and advocated JSP as the view layer, for a number of reasons: first, there was no other mature view technology available at the time. They could have developed a more suitable package as part of Struts but getting something like that into a stable condition would have delayed the release of Struts by close to a year. Further, it would have meant that web developers wanting to use Struts would have had to learn the new view technology, making Struts a less attractive package. By using JSP, Struts would allow Model 1 developers to continue using a technology they were familiar with - they'd just have to take a more disciplined approach to JSP development.&lt;br /&gt;&lt;br /&gt;The way I understand it (and again, I can't substantiate this), the Struts developers expected a more suitable view layer to become available before too long after Struts was released. Unfortunately it didn't work out quite that way; the first really useful package to fit the need was probably Velocity and that wasn't on the scene until something like two years later. In the meantime the JSP snowball had been rolling down the hill and picking up speed. Thanks to that momentum, JSP is still being used even today for new applications using Struts 2, Spring MVC and others, regardless of the problems it continues to cause in Model 2 environments.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;The Alternatives&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If JSP isn't a good fit for Model 2 applications, then what is?&lt;br /&gt;&lt;br /&gt;The answer is quite simple: templating software. The idea is that you create HTML templates which contain embedded markers that tell the templating engine how to get dynamic data from the data model produced by the business logic and format it for presentation. The templating engine merges the template with the data model to produce the HTML and deliver it to the client browser.&lt;br /&gt;&lt;br /&gt;Velocity and FreeMarker are probably the two most commonly used templating packages in current use. Integrating Velocity and/or FreeMarker is pretty simple. Both packages include ready-to-use servlets that fit with Struts 1, so in both cases all you have to do is configure your application to forward the request/response to the relevant servlet instead of the JSP servlet. Struts 2 has "Results" classes to provide Velocity and FreeMarker views; similarly Spring MVC includes view resolvers and views for Velocity and FreeMarker.&lt;br /&gt;&lt;br /&gt;Which is better, Velocity or FreeMarker? I'll probably be writing an in-depth article comparing the two quite soon, but as a short answer... both have their own advantages and drawbacks. Velocity is simpler to learn, but FreeMarker offers more built-in functionality. Velocity is a bit more forgiving than FreeMarker but that may not be such a good thing, especially for larger applications. If I were asked to lay my cards on the table right now, I'd have to say that while I've used Velocity extensively over the last few years, I will almost certainly be using FreeMarker in the future.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;In Summary...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Don't use JSP for Model 2 applications.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use Velocity or FreeMarker instead.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Once you've developed a web application with Velocity or FreeMarker, I can almost guarantee you'll never want to write another JSP again.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-8663099041167911289?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/8663099041167911289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=8663099041167911289' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8663099041167911289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8663099041167911289'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2009/11/jsp-considered-harmful.html' title='JSP Considered Harmful'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-4887032775890165383</id><published>2008-06-27T10:54:00.003-05:00</published><updated>2010-06-19T13:33:32.897-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Development'/><title type='text'>ClassLoaders and Web Applications</title><content type='html'>&lt;strong&gt;&lt;em&gt;This post has changed to correct some errors&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;In the previous posting I described the class loader heirarchy, and mentioned that you can use standard Java API classes and methods to create a branching tree structure of loaders. I also touched on the fact that Java EE application servers use this to ensure that each web application gets its own private load path for classes.&lt;br /&gt;&lt;br /&gt;However, the loading rules as I described them don't quite apply to web applications, so this article is intended to cover the differences.&lt;br /&gt;&lt;br /&gt;I'm describing the way things work in Tomcat; other servers should be doing something similar.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Tomcat Loader Heirarchy&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As I mentioned in the previous post, ordinary Java applications have three loaders at run time: the Boot loader, which is usually native, platform-specific code built into the JVM and searches the standard class libraries; the Extension class loader, which searches JARs in the &lt;code&gt;$JAVA_HOME/jre/lib/ext&lt;/code&gt; directory; and the System class loader, which searches directories and JARs specified in the &lt;code&gt;CLASSPATH&lt;/code&gt;. The Extension loader is the parent of the System loader, and the Boot loader is (in practice) the parent of the Extension loader. &lt;span style="background-color:pink;font-style:italic;"&gt;Slight correction: some JVMs combine the Boot and Extension loaders into one.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tomcat adds two more levels to this heirarchy for web applications: at the bottom, a &lt;code&gt;WebappClassLoader&lt;/code&gt; (one for each deployed web application) with the application's &lt;code&gt;WEB-INF/classes&lt;/code&gt; and all the archive files in the &lt;code&gt;WEB-INF/lib&lt;/code&gt; directory set as the search path; and above that a &lt;code&gt;StandardClassLoader&lt;/code&gt; (one only for the whole server), with its search path set as Tomcat's &lt;code&gt;lib&lt;/code&gt; directory and all the archive files in it. &lt;br /&gt;&lt;br /&gt;Tomcat's system class loader is the parent of this &lt;code&gt;StandardClassLoader&lt;/code&gt; and has its search path set to include Tomcat's &lt;code&gt;bootstrap.jar&lt;/code&gt; file and little else.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;StandardClassLoader&lt;/code&gt; class is a subclass of &lt;code&gt;URLClassLoader&lt;/code&gt; but doesn't add or override anything from that class - it's functionally identical.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;WebappClassLoader&lt;/code&gt; class, on the other hand, subclasses &lt;code&gt;URLClassLoader&lt;/code&gt; but reimplements most if not all of the methods. Here's why...&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Servlet API loader rules&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Servlet API specification is the root of things here; it says that the loader search algorithm for web applications should ensure that classes and JARs packaged in the WAR should be searched &lt;em&gt;before&lt;/em&gt; any of the servlet container's library JAR files, but should not allow the application to override any of the standard Java classes.&lt;br /&gt;&lt;br /&gt;Tomcat's &lt;code&gt;WebappClassLoader&lt;/code&gt; achieves the first part of this (searching the WAR contents first) by not delegating searches to its parent until &lt;em&gt;after&lt;/em&gt; it has already searched its own repositories - the opposite of the usual procedure. (I'm not clear on how it ensures that standard classes don't get overridden - the code in that area is tricky to follow.) &lt;span style="background-color:pink;font-style:italic;"&gt;Major correction required here: In fact the &lt;code&gt;WebAppClassLoader&lt;/code&gt; delegates directly to the System class loader first, &lt;en&gt;then&lt;/en&gt; searches its own repositories, then finally delegates to its parent. This ensures that JRE classes can't be overridden (they get searched first) and that repositories are searched before Tomcat's &lt;code&gt;lib&lt;/code&gt; contents.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That covers the important differences that apply to web applications. There is one other small aspect that I'd like to mention.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Getting Resources&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Servlet API specification also mandates that web applications must be able to locate their own resources using &lt;code&gt;ClassLoader.getResource()&lt;/code&gt;. The heirarchy as described achieves this. However the specification says nothing about the static &lt;code&gt;ClassLoader.getSystemResource()&lt;/code&gt; method and in fact this method is of little use in web applications because the System class loader doesn't know anything about the web application's resources (as you can see from where it is in the heirarchy as described).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-4887032775890165383?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/4887032775890165383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=4887032775890165383' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/4887032775890165383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/4887032775890165383'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2008/06/classloaders-and-web-applications.html' title='ClassLoaders and Web Applications'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-4691469712819293855</id><published>2008-06-06T12:29:00.008-05:00</published><updated>2008-06-16T11:22:57.643-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>Modifying the CLASSPATH at run time</title><content type='html'>Here's a question that comes my way occasionally: How can you change the search path for class loading at run time?&lt;br /&gt;&lt;br /&gt;For example, let's say I have an application that reads the name of a JAR file from an external source, and then needs to add that JAR to the classpath so that it can load classes from it. This is something that's more likely to come up in a server environment, where you need the server to be able to add plug-in classes dynamically. For example, application servers like Tomcat need to be able to unpack a WAR file when requested; after unpacking there will be a 'classes' directory and a 'lib' directory full of JARs, all of which have to be added to the loading path so that the application can be started.&lt;br /&gt;&lt;br /&gt;The solution to this problem requires an understanding of how the &lt;code&gt;ClassLoader&lt;/code&gt; heirarchy works, so I'm going to cover that in some detail first.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;ClassLoaders&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The JVM includes a loader, usually referred to as the &lt;em&gt;Boot&lt;/em&gt; loader. The default search path that this loader uses includes the Java runtime classes - java.lang, java.util, etc.&lt;br /&gt;&lt;br /&gt;When the JVM is started it creates a &lt;code&gt;ClassLoader&lt;/code&gt; object (loaded by the boot loader), usually referred to as the &lt;em&gt;Extension&lt;/em&gt; ClassLoader. Its search path includes several JAR files found in the JVM's &lt;code&gt;jre/lib/ext/&lt;/code&gt; directory.&lt;br /&gt;&lt;br /&gt;Then, the &lt;em&gt;System&lt;/em&gt; loader is created. The search path for this loader is initialized from the CLASSPATH environment variable or from the value passed as the &lt;code&gt;-cp&lt;/code&gt; option on the command line. (The label 'System' is a bit confusing; personally I think 'Application' class loader would be a more accurate and descriptive name.)&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ClassLoader&lt;/code&gt;s are arranged in a heirarchy; each loader has a parent loader. The extension loader is the parent of the system loader; the parent of the extension loader is usually set as &lt;code&gt;null&lt;/code&gt;. The boot loader is something of an exception in this respect - it's technically the parent of the extension loader but because it's part of the native implementation of the JVM and hence not a Class in the usual sense, it normally can't be accessed as a Java object.&lt;br /&gt;&lt;br /&gt;When the JVM recognizes that it needs to load a new class, it calls a loader to do that. The loader it chooses is the same loader that loaded the class where the new class is first referenced at run time - that means that by default, when your application code first references a class that hasn't yet been loaded, it will call the system loader (the one that loaded your application classes).&lt;br /&gt;&lt;br /&gt;The first thing the loader does is to delegate the request to its parent if it has one. The result of this is that all requests for new class references get delegated all the way up to the boot loader. So, if your code has requested a class that's to be found in the Java runtime, such as &lt;code&gt;java.util.Map&lt;/code&gt; or &lt;code&gt;java.text.Format&lt;/code&gt;, the boot loader will find and load the class.&lt;br /&gt;&lt;br /&gt;If the loader can't locate the class it tells the caller - so if the class you requested is not in the boot loader path, it tells the extension loader that called it so. If the class isn't in any of the extension JARs, it gets passed back to the system loader. The system loader then attempts to find the class and in the case of your application classes, this would be where those get resolved. (Of course, if the request makes it all the way back down the heirarchy without the class being found, you'll get a &lt;code&gt;ClassNotFoundException&lt;/code&gt;.)&lt;br /&gt;&lt;br /&gt;To expand slightly: when a loader is called to load a class, this is the sequence of actions:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;1 - delegate to the parent loader if there is one. If the parent finds the class, the loader returns to its caller at this point.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;2 - if the parent doesn't find the class, or if there is no parent, the loader checks its local data to see if it already loaded the class. If it finds it, the loader returns at this point.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;3 - if the class definition isn't found in the local data, the loader attempts to find the class definition in its search path. If the class definition is found in the path, the class is loaded and added to the loader's local data, and the loader returns.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;4 - if this point is reached, the class hasn't been found - the loader returns control to its caller indicating such.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;The Answer&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Back to the original question: How to add more places to the search? The way to do that is to create a new loader with the locations you want to search set as its search path, and add this new loader into the heirarchy.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ClassLoader&lt;/code&gt; is an abstract class, and so can't be instantiated. Instead you'd normally use a &lt;code&gt;URLClassLoader&lt;/code&gt;, which is basically &lt;strong&gt;&lt;em&gt;the&lt;/em&gt;&lt;/strong&gt; class to use - it does everything you would usually need. You can create your own loader classes by extending &lt;code&gt;ClassLoader&lt;/code&gt;, but normally this is unnecessary.&lt;br /&gt;&lt;br /&gt;The search path for &lt;code&gt;URLClassLoader&lt;/code&gt; is provided as an array of &lt;code&gt;java.net.URL&lt;/code&gt; objects; each URL identifies a directory or an archive file (.jar or .zip) to be searched when loading.&lt;br /&gt;&lt;br /&gt;Let's say I have a JAR named &lt;code&gt;/tmp/my-jar.jar&lt;/code&gt; and it contains a class called &lt;code&gt;com.example.MyClass&lt;/code&gt;. I need to create an instance of this class. This code should do the trick:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;    // First, set the search path&lt;br /&gt;    URL[] searchPath = new URL[1];&lt;br /&gt;    searchPath[0] = new File("/tmp/my-jar.jar")&lt;br /&gt;                            .toURI()&lt;br /&gt;                            .toURL();&lt;br /&gt;&lt;br /&gt;    // Now create a new loader&lt;br /&gt;    ClassLoader cl = new URLClassLoader(searchPath);&lt;br /&gt;&lt;br /&gt;    // Now we can load from the JAR:&lt;br /&gt;    Object o = Class.forName("com.example.MyClass",&lt;br /&gt;                             true,&lt;br /&gt;                             cl)&lt;br /&gt;                    .newInstance();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A few notes about this code:&lt;br /&gt;&lt;br /&gt;First, the URL array can contain URLs for directories as well as JAR and ZIP files. The example here has only one entry but you could provide an array containing hundreds of entries if you needed to. Note that you can't use wildcards here - each entry must point to a single archive file or directory.&lt;br /&gt;&lt;br /&gt;Second, the loader created by the &lt;code&gt;URLClassLoader&lt;/code&gt; constructor will have the system loader as a parent by default. You can provide a different parent as a second parameter to the constructor - this allows you to build a full-blown heirarchical tree of loaders within your application if you so wish.&lt;br /&gt;&lt;br /&gt;Third, note the three-parameter call to &lt;code&gt;Class.forName()&lt;/code&gt; - the first parameter is the class name, of course, as in the one-parameter call. The third parameter specifies our new loader as the one to use to load our class; the default is to use the same loader that loaded the calling class (&lt;code&gt;this.getClass().getClassLoader()&lt;/code&gt;). The second parameter determines whether or not the class should be initialized (i.e. have its static initializer called) and you'd normally set this to &lt;code&gt;true&lt;/code&gt; (offhand I can't think of a circumstance where you wouldn't want to to this).&lt;br /&gt;&lt;br /&gt;Lastly, note that the new loader becomes the default for classes referenced by the newly-loaded classes. This means that &lt;code&gt;MyClass&lt;/code&gt; can reference other classes in &lt;code&gt;my-jar.jar&lt;/code&gt; implicitly or explicitly (i.e. using the one-parameter &lt;code&gt;Class.forName()&lt;/code&gt; method) and the classes will be loaded correctly.&lt;br /&gt;&lt;br /&gt;Using this you can create a structure of loaders organized as you need to implement different search paths for different requirements (for example, Tomcat uses one branch of a loader tree for its own server classes and another as a connection point for loading web applications; each webapp gets its own subtree. That's how multiple webapps can exist even with conflicting class names or versions, and without being able to access the server's internal classes).&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Where the Class definitions are kept&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Each loader keeps the &lt;code&gt;Class&lt;/code&gt; objects that it loads in its own local space.&lt;br /&gt;&lt;br /&gt;This means that if you create two loaders, each with the system loader as parent but with common directories and/or archive files in their search paths, it becomes possible to load the same class twice by invoking both class loaders to load the same class.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Other things URLClassLoader can do&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To finish up, here are a couple of other useful things that you can do:&lt;br /&gt;&lt;br /&gt;First, there's a method &lt;code&gt;URLClassLoader.getURLs()&lt;/code&gt; that returns the loader's current search path as an array of URLs. This can be useful for debugging.&lt;br /&gt;&lt;br /&gt;Second, loaders aren't limited to finding &lt;code&gt;.class&lt;/code&gt; files - you can use them to find other resources that are in the search path. This applies to all loaders (i.e. &lt;code&gt;ClassLoader&lt;/code&gt; and all its subclasses, not just &lt;code&gt;URLClassLoader&lt;/code&gt;). This is extremely useful because it allows you to, for example, read from a property file embedded inside a JAR. Some methods that are especially useful are:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ClassLoader.getResource()&lt;/code&gt; - returns the URL of a named resource;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ClassLoader.getResourceAsStream()&lt;/code&gt; - returns an &lt;code&gt;InputStream&lt;/code&gt; allowing you to read a named resource directly (handy for loading .properties files);&lt;br /&gt;&lt;br /&gt;&lt;code&gt;ClassLoader.getSystemResource()&lt;/code&gt; and &lt;code&gt;ClassLoader.getSystemResourceAsStream()&lt;/code&gt; - static methods that do the same as the above methods, but use the system loader rather than a specific one that you may have created.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-4691469712819293855?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/4691469712819293855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=4691469712819293855' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/4691469712819293855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/4691469712819293855'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2008/06/modifying-classpath-at-run-time.html' title='Modifying the CLASSPATH at run time'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-6021677723774058167</id><published>2008-03-02T00:46:00.001-06:00</published><updated>2008-06-06T14:18:24.188-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>Ginger 1.4 released</title><content type='html'>I've just made the latest release of Ginger available on SourceForge.net. This version removes the code that was deprecated in version 1.3, cleans up a few comments (to fix references to the old "Point" classes and methods that got held over from Lynx, Ginger's predecessor). I've also replaced use of the &lt;code&gt;StringBuffer&lt;/code&gt; class with the &lt;code&gt;StringBuilder&lt;/code&gt; class that was introduced in Java 5, and done some other general code cleanup (nothing that affects the way it works, though - it's fully backward compatible with Ginger 1.3).&lt;br /&gt;&lt;br /&gt;This is likely to be the final version of Ginger, as it appears to work just fine and I see no need to add or change anything more at this time. I want to change gears a bit and start working on one or two other small packages I have in mind for the Bohemia project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-6021677723774058167?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/6021677723774058167/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=6021677723774058167' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/6021677723774058167'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/6021677723774058167'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2008/03/ginger-14-released.html' title='Ginger 1.4 released'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-6697338336181374352</id><published>2007-12-15T15:38:00.001-06:00</published><updated>2009-11-30T14:47:08.728-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Development'/><category scheme='http://www.blogger.com/atom/ns#' term='Framework'/><title type='text'>How to set up a Derby server as a Tomcat application</title><content type='html'>&lt;span class="subhead"&gt;What is Derby?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://db.apache.org/derby/" target="_new"&gt;Derby&lt;/a&gt; is a 100% pure Java database engine. Its main strength is that it's embeddable, so if you have a standalone application that could benefit from keeping persistent data in a database but you don't want to have to set up a separate database server just to support it, Derby may be the answer. In the embedded mode your application starts Derby during initialization and shuts it down during termination. In the meantime it can work with database tables using SQL statements through a JDBC driver in the usual way.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Why this article?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Derby can also run as a network server, like any other database. Not only that, you can install it as a web application in a server such as Tomcat so that your database server is available whenever your web server is. In fact, the Derby distribution includes a WAR file that you can deploy that makes this very easy.&lt;br /&gt;&lt;br /&gt;The problem is that Derby's documentation seems to concentrate on the embedded mode of operation and it's not easy to find information about setting it up as a Tomcat application without quite a bit of digging. Since I've been through this particular loop I decided to write this, partially as a reference for myself in case I need to do it again, and also for others needing to do the same thing.&lt;br /&gt;&lt;br /&gt;This article describes the setup with Tomcat 6 running on Windows (XP Pro, specifically). Unix/Linux setup is similar and it shouldn't be a problem for anyone familiar with those systems to adapt the steps as required.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Download and unpack the distribution&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Derby is distributed as a .zip or .tar.gz archive. There are a couple of different downloads available depending on whether you just want the executable JAR files or the full package including documents and sample code. Download the appropriate one then simply unpack it into a convenient place. I used the 'bin' distribution which includes everything.&lt;br /&gt;&lt;br /&gt;The archive unpacks such that everything is inside one directory (the current version puts itself into 'db-derby-10.3.2.1-bin' for example, for the 'bin' distribution). This is Derby's 'home' directory, which we'll need to know in a moment.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Setting the environment and Java system properties&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Derby only really needs one environment setting: &lt;code&gt;DERBY_HOME&lt;/code&gt; should be set to the path of the home directory as noted above. On a Windows system use 'Control Panel-&amp;gt; System' then open the Advanced tab. You can add the new environment setting as a System or User value in the dialog there. 'System' is probably better, especially if you run Tomcat as a service.&lt;br /&gt;&lt;br /&gt;You may also want to add &lt;code&gt;%DERBY_HOME%\bin&lt;/code&gt; to your PATH setting; this isn't required but it makes it easier to run the Derby command-line tools.&lt;br /&gt;&lt;br /&gt;One Java system property really needs to be set, too: &lt;code&gt;derby.system.home&lt;/code&gt; identifies a directory where Derby will keep all its databases. Without this, when you run Derby under Tomcat it will default to '.', which equates to the Tomcat installation directory - the place where it keeps its &lt;code&gt;conf&lt;/code&gt; and &lt;code&gt;WebApps&lt;/code&gt; directories. This is almost certainly not a good idea (imagine what might happen if an application tries to create a database called 'conf').&lt;br /&gt;&lt;br /&gt;I created a "DerbyData" directory to hold all my databases. Setting the &lt;code&gt;derby.system.home&lt;/code&gt; property is a little bit of a problem, but I found a solution that works fine; Tomcat 5 and 6 include a "Monitor" program that you can run, at least on Windows. You can use this to set up the system properties (run the monitor, right-click on the system tray icon that appears, then open the 'Configure' option and click the 'Java' tab). Add &lt;code&gt;-Dderby.system.home=&lt;em&gt;your-database-directory&lt;/em&gt;&lt;/code&gt; to the system properties and click OK. Tomcat doesn't need to be running to do this.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Deploying derby.war&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Start Tomcat (if it's already running you should probably shut it down and restart). Start your browser, find the Tomcat Manager page and log in.&lt;br /&gt;&lt;br /&gt;Deploy the Derby WAR file in the usual way. You'll find the WAR file (&lt;code&gt;derby.war&lt;/code&gt;) in Derby's &lt;code&gt;lib&lt;/code&gt; directory.&lt;br /&gt;&lt;br /&gt;You should now be able to hit the Derby service in your browser - it'll be at &lt;code&gt;/derby/derbynet&lt;/code&gt;. This starts the database running, and if everything's ok you'll see a status page.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Modifying Derby's web.xml&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We're not quite done yet. With the default installation you have to hit the Derby URL as above to start the database running every time you restart Tomcat. What's happening is that the database is started by the servlet's &lt;code&gt;init()&lt;/code&gt; method, and that isn't happening until Tomcat gets a request. This probably isn't what you want, especially if you're planning to create other web applications that use the service. You probably want the thing to come up on its own.&lt;br /&gt;&lt;br /&gt;You'll find Derby's web.xml file under your Tomcat installation at &lt;code&gt;WebApps/derby/WEB-INF/web.xml&lt;/code&gt;. Open the file in a text editor and add the following tag inside the &lt;code&gt;&amp;lt;servlet&amp;gt;&lt;/code&gt; tag:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;&amp;lt;load-on-startup&amp;gt;0&amp;lt;/load-on-startup&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will cause Tomcat to call the servlet's initialization during startup instead of waiting for a request.&lt;br /&gt;&lt;br /&gt;Save your text change and restart Tomcat.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Testing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After restarting Tomcat you should see a 'derby.log' file appear in your database directory. It may take a few seconds for this to happen, so give it time - if it hasn't shown up after, say, a minute, you may want to check your Tomcat logs to see if anything bad happened. Also, check your Tomcat directory to make sure the log file didn't show up there - if it did, it means that the &lt;code&gt;derby.system.home&lt;/code&gt; setting hasn't taken for some reason, in which case you should go back and check for misspellings, etc.&lt;br /&gt;&lt;br /&gt;If the log file appears in the right place, that probably means everything's ok so far. You can test the installation using Derby's &lt;code&gt;ij&lt;/code&gt; command-line tool. Open a command window and do this (this assumes that you set the PATH environment as suggested earlier):&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;&amp;gt; ij&lt;br /&gt;ij version 10.3&lt;br /&gt;&amp;gt; connect 'jdbc:derby://localhost/testdata;create=true';&lt;br /&gt;&amp;gt; exit;&lt;br /&gt;&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This tells ij to open a connection to the database named 'testdata', creating it if it doesn't exist (which it shouldn't, at this point).&lt;br /&gt;&lt;br /&gt;Now check your database directory - there should be a subdirectory called 'testdata' if all went well. You can delete it manually, although I'd recommend shutting Derby down first - either shut down Tomcat or hit the Derby URL as above and click the Stop button.&lt;br /&gt;&lt;br /&gt;Congratulations! You now have a database server that will be available whenever Tomcat is running.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-6697338336181374352?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/6697338336181374352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=6697338336181374352' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/6697338336181374352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/6697338336181374352'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/12/how-to-set-up-derby-server-as-tomcat.html' title='How to set up a Derby server as a Tomcat application'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-8409608301414728487</id><published>2007-12-15T12:17:00.000-06:00</published><updated>2008-06-06T14:18:24.189-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>Ginger 1.3 adds better handling of HTTP request methods</title><content type='html'>&lt;span class="subhead"&gt;No plans for more changes in the near future&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I realized after some thought that up until now Ginger was written with a bad assumption in the back of my mind - the assumption that it would only ever have to deal with GET and POST requests. This is not a good assumption, because there are six other types of request that it may have to deal with depending on how your servlet mappings are set up in the deployment descriptor.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;HttpServlet&lt;/code&gt; is designed to identify the request type for you and takes default actions for the ones you don't explicitly set up to handle in a subclass, but for several reasons Ginger couldn't subclass from that. In fact, Ginger isn't a subclass at all, really - it just implements the &lt;code&gt;Servlet&lt;/code&gt; interface directly.&lt;br /&gt;&lt;br /&gt;So, in Ginger 1.3 I've added a mechanism to give you control over which HTTP request methods will be handled. By default, it will deal with GET, POST, HEAD, OPTIONS and TRACE. If it receives a request with a type not in that set, it'll respond with a 405 error. That means that by default your subclass servlet won't have to worry about CONNECT, DELETE and PUT requests.&lt;br /&gt;&lt;br /&gt;You can change the set in your &lt;code&gt;servletInit()&lt;/code&gt;, to define the set of requests you want your servlet to accept. Note though that Ginger itself will handle OPTIONS and TRACE requests in a way you can't override; it will automatically create responses and never calls any command methods for these requests. Also, by default it handles HEAD requests by running your configured commands in the normal way and rendering output from your templates, but the rendered data is only used to generate the character count for the response header - it isn't sent back to the client. (A nice benefit of the code change that makes this work is that Ginger now buffers the output internally for GET and POST responses; this makes it possible to set the response length, which in turn makes it possible for the servlet container to use a persistent connection, which in turn improves response times.) It is possible for your command methods to modify their behaviour for HEAD requests, but doing that really isn't recommended.&lt;br /&gt;&lt;br /&gt;Another fairly big change I've made is to deprecate the &lt;code&gt;Context&lt;/code&gt; class and replace it with a new class called &lt;code&gt;Dynamic&lt;/code&gt; which is otherwise identical.  The reason for this is that the name 'Context' is used in several packages all related to web applications - it's used in JNDI, servlet containers use the name to refer to a web application (Tomcat does, anyway), Velocity uses it to mean something completely different again, and there are probably other places too. That means you may have to fully-qualify &lt;code&gt;com.codexombie.bohemia.ginger.Context&lt;/code&gt; in every command method that also accesses a JNDI context, for example. That's messy and error-prone, which is why I decided to make the change. &lt;code&gt;Dynamic&lt;/code&gt; is so named because it's intended to hold dynamic data for the template merge (among other things) and I couldn't think of a better name that wasn't already in common use. Existing code can still use the old &lt;code&gt;Context&lt;/code&gt; class but you'll get deprecation warnings, and in any case I don't intend to leave the deprecated code in place forever - it'll be removed entirely in a future release.&lt;br /&gt;&lt;br /&gt;On the subject of future releases, I'm not planning to make any more changes for a while; the 1.3 release is the fourth in just a few weeks, which is a bit rushed. Unless I find that there's a major problem that needs fixing, there won't be any new releases for at least a couple of months (and even then, the only change may be to remove that deprecated code I mentioned).&lt;br /&gt;&lt;br /&gt;The new release is, of course, available for download at &lt;a href="http://bohemia.sourceforge.net" target="_new"&gt;the usual place&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-8409608301414728487?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/8409608301414728487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=8409608301414728487' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8409608301414728487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8409608301414728487'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/12/ginger-13-adds-better-handling-of-http.html' title='Ginger 1.3 adds better handling of HTTP request methods'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-1549627247611306829</id><published>2007-12-07T01:02:00.001-06:00</published><updated>2008-06-06T14:18:24.189-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>Ginger 1.2</title><content type='html'>&lt;span class="subhead"&gt;Adds basic JSP support&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've added very basic support for JSPs to Ginger through the addition of a new template processor. It's not intended to be a replacement for Velocity and doesn't indicate a shift in that direction - it's just to provide some level of support in case JSP is necessary for one or two pages in an application, and for some reason Velocity won't do what's needed. Check it out &lt;a href="http://bohemia.sourceforge.net" target="_new"&gt;in the usual place&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I have no plans for further releases in the near future - I'm hoping it's stable enough to stand without more changes for a while to come.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-1549627247611306829?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/1549627247611306829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=1549627247611306829' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/1549627247611306829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/1549627247611306829'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/12/ginger-12.html' title='Ginger 1.2'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-8304350788340352270</id><published>2007-11-30T00:05:00.001-06:00</published><updated>2008-06-06T14:18:24.190-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>New Ginger Version Released</title><content type='html'>&lt;span class="subhead"&gt;Version 1.1 is now available&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The new version adds three new attributes to render command configuration; these allow control of content-type, character set encoding and also allow for a 'no-render' mode of operation in which the final rendering phase is not performed. This makes it possible for a render command to set up the HTTP response with a redirection URL or an error code return, without needing to specify a superfluous template to be rendered.&lt;br /&gt;&lt;br /&gt;The demo application has also undergone a facelift and adds the ability to switch between HTTP and HTTPS protocols. The primary purpose of this isn't to make the demo any more functional; it's so that developers can read the source code to see how it's done under Ginger.&lt;br /&gt;&lt;br /&gt;Last, the Quick Start manual has been updated to include the changes.&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://bohemia.sourceforge.net" target="_new"&gt;the Bohemia home page&lt;/a&gt; for full details.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-8304350788340352270?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/8304350788340352270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=8304350788340352270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8304350788340352270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8304350788340352270'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/11/new-ginger-version-released.html' title='New Ginger Version Released'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-104011213746387233</id><published>2007-11-06T23:40:00.000-06:00</published><updated>2008-06-06T14:18:24.190-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>The Bohemia Project</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_ZaZYVqXpXFU/RzFP9S0LJeI/AAAAAAAAABk/P_FBDdOmfZs/s1600-h/Cattitude100x88.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_ZaZYVqXpXFU/RzFP9S0LJeI/AAAAAAAAABk/P_FBDdOmfZs/s320/Cattitude100x88.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5129969364992992738" /&gt;&lt;/a&gt;&lt;span class="subhead"&gt;Lynx is renamed and moved to SourceForge.net&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The last stable release of Lynx was version 5.4. For what was intended to be version 5.5, I decided on some rather far-reaching changes that stemmed from using the package to build some applications. As I was making the changes I thought of other changes to simplify the internal data structures and the API.&lt;br /&gt;&lt;br /&gt;In the end, the changes would make 5.5 incompatible with applications developed for 5.4 and earlier. My first thought was to renumber the updated package as Lynx 6.0, but instead I decided to rename the package and make it available on SourceForge.net. Among other things, I have no way of knowing how many copies of Lynx were downloaded from the CodeXombie site because there were no tools to gather those stats; SourceForge includes those tools so I can keep tabs on how much interest there is.&lt;br /&gt;&lt;br /&gt;I've had ideas for several lightweight packages that would help with developing web applications, and rather than setting up several separate SourceForge projects, I took a leaf from the Apache book; I've created one umbrella project to encompass all these related packages.&lt;br /&gt;&lt;br /&gt;And so I'm pleased to announce the inception of &lt;a href="http://bohemia.sourceforge.net" target="_new"&gt;The Bohemia Project&lt;/a&gt;, and as its first downloadable product what was going to be Lynx 6.0 is now &lt;strong&gt;Ginger 1.0&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;There's also a downloadable demo that uses the new package - it's a functional application but its primary function is to show source code written to use the Ginger API, and how to set up the configuration XML file.&lt;br /&gt;&lt;br /&gt;The older Lynx packages have been removed from &lt;a href="http://www.codexombie.com" target="_new"&gt;the CodeXombie web site&lt;/a&gt; and the links have been updated to point to the new home on SourceForge.&lt;br /&gt;&lt;br /&gt;I'll continue to post here about updates and new packages as they happen.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-104011213746387233?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/104011213746387233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=104011213746387233' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/104011213746387233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/104011213746387233'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/11/bohemia-project.html' title='The Bohemia Project'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_ZaZYVqXpXFU/RzFP9S0LJeI/AAAAAAAAABk/P_FBDdOmfZs/s72-c/Cattitude100x88.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-1481559560682764404</id><published>2007-09-10T21:50:00.000-05:00</published><updated>2008-06-06T14:18:24.190-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>Another version of Lynx</title><content type='html'>&lt;span class="subhead"&gt;Another one so soon?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've added a new feature - it's a simple one but possibly useful, so I went ahead and created a new version. I'd forgotten that the &lt;code&gt;Properties&lt;/code&gt; class, as of Java 5, includes methods to load and store from XML files. I've added a new Points loader to handle XML format. Details are &lt;a href="http://www.codexombie.com/lynxtutorial3.html" target="_new"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-1481559560682764404?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/1481559560682764404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=1481559560682764404' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/1481559560682764404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/1481559560682764404'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/09/another-version-of-lynx.html' title='Another version of Lynx'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-8214647426684886346</id><published>2007-09-05T00:32:00.000-05:00</published><updated>2008-06-06T14:18:24.191-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>New Lynx version available now</title><content type='html'>&lt;span class="subhead"&gt;Lynx 5.2 is available for download&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A new version of Lynx is now available for download from &lt;a href="http://www.codexombie.com" target="_new"&gt;my main site&lt;/a&gt;. The new version should be compatible with version 5.1, so if you have existing servlets written with the older version they should work without changes.&lt;br /&gt;&lt;br /&gt;Version 5.2 adds support for forward references so it's no longer necessary to define the points in "reverse order", as it were. It also means that you can set up LogicPoints in a way that implements loops and branches.&lt;br /&gt;&lt;br /&gt;The other major change is that you can now define your Points configuration in a Properties file instead of having to hard-code the calls. This makes it easier to see your configuration.&lt;br /&gt;&lt;br /&gt;There are also a handful of smaller changes. For full details see the (new) &lt;a href="http://www.codexombie.com/lynxtutorial3.html" target="_new"&gt;third page of the Lynx tutorial&lt;/a&gt;, which describes the changes in detail together with examples of use.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-8214647426684886346?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/8214647426684886346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=8214647426684886346' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8214647426684886346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8214647426684886346'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/09/new-lynx-version-available-now.html' title='New Lynx version available now'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-2770267599251767282</id><published>2007-09-01T01:14:00.000-05:00</published><updated>2008-06-06T14:18:24.191-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Bohemia'/><title type='text'>New Lynx version coming soon/Page updates</title><content type='html'>&lt;span class="subhead"&gt;Lynx 5.2 is under test&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'm in the process of updating Lynx with a couple of relatively minor changes, and one fairly major change. None of these should impact any existing applications.&lt;br /&gt;&lt;br /&gt;One change is purely internal: I've changed the key that's used in RenderPoints to identify the Velocity template for rendered pages. The old key was purely alphabetic and there was a small chance that a developer might use the same key for something else in the context. The new key adds some underscore characters to minimize the risk.&lt;br /&gt;&lt;br /&gt;Another change is to allow forward references for Point names. This adds a little flexibility, but the main reason was that it makes the third change possible.&lt;br /&gt;&lt;br /&gt;That third change is the addition of a new feature that will allow developers to use a Properties file to specify the Points and Mappings.&lt;br /&gt;&lt;br /&gt;This last feature is still under development. The new version should be available from &lt;a href="http://www.codexombie.com" target="_new"&gt;the CodeXombie.com web page&lt;/a&gt; in a week or two.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Page Updates&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The CodeXombie.com web page has undergone a facelift. The old page was thrown together in a bit of a hurry, just to get something there. I've made some changes to the background and banner images. I think you'll agree it's a big improvement.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-2770267599251767282?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/2770267599251767282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=2770267599251767282' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/2770267599251767282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/2770267599251767282'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/09/new-lynx-version-coming-soonpage.html' title='New Lynx version coming soon/Page updates'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-8135717786744488243</id><published>2007-08-09T02:18:00.000-05:00</published><updated>2008-06-06T14:16:34.888-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metablogging'/><title type='text'>CodeXombie.com is alive!</title><content type='html'>&lt;span class="subhead"&gt;More Java content than I can post here&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've moved my domain, &lt;a href="http://www.codexombie.com" target="_new"&gt;CodeXombie.com&lt;/a&gt; to new hosting and while I was at it I've completely rewritten the whole thing. The colours and images need some work, but it'll do for now and I'll be tidying the presentation up as time goes on.&lt;br /&gt;&lt;br /&gt;Java Planet will continue to be the place I post code snippets, tricks and tips, while the new site will be the place for complete packages, lightweight frameworks and so on.&lt;br /&gt;&lt;br /&gt;To start things off I've placed a link to JSpasm, which is a small but pretty complete package for developing applications based on state machines. Since JSpasm is hosted on SourgeForge.net I didn't see the point of having copies on the new site, so there's minimal information about that.&lt;br /&gt;&lt;br /&gt;I've also put up my Lynx package. Lynx is a lightweight alternative to Struts and JSP that's designed for small web applications. It gives you complete Model/View separation, using Velocity for the presentation side. I've included a two-page tutorial to get you started. Take a look!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-8135717786744488243?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/8135717786744488243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=8135717786744488243' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8135717786744488243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/8135717786744488243'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/08/codexombiecom-is-alive.html' title='CodeXombie.com is alive!'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-1345322188104251182</id><published>2007-07-25T13:57:00.000-05:00</published><updated>2008-06-06T14:15:44.681-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>More of the same</title><content type='html'>&lt;span class="subhead"&gt;Another Example&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Continuing from yesterday, here's another example of the quick-n-dirty development technique I was describing. Before I go too much further, I need to explain a couple of things:&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Velocity&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I mentioned Velocity yesterday. For those that don't know, Velocity is a text templating package. You create templates containing tags (marked with $ signs), and provide a Context, which is basically a Map. Velocity can then merge the template with the context to create output which is usually written directly to a &lt;code&gt;Writer&lt;/code&gt;. As an example, I might have this template (let's call it &lt;code&gt;MyTemplate.txt&lt;/code&gt;):&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;Dear $firstname,&lt;br /&gt;&lt;br /&gt;Here is the list of books you have ordered:&lt;br /&gt;&lt;br /&gt;#foreach( $book in $booklist )&lt;br /&gt;$book&lt;br /&gt;#end&lt;br /&gt;&lt;br /&gt;Regards...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In my code, I can create the context and merge with the template:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;// Create the Context&lt;br /&gt;Map&amp;lt;String, Object&amp;gt; map = new HashMap&amp;lt;String, Object&amp;gt;();&lt;br /&gt;map.put("firstname", "Fred");&lt;br /&gt;List&amp;lt;String&amp;gt; books = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;books.add&lt;br /&gt;    ("Quidditch Through the Ages");&lt;br /&gt;books.add&lt;br /&gt;    ("Fantastic Beasts &amp;amp; Where to Find Them");&lt;br /&gt;map.put("booklist", books);&lt;br /&gt;VelocityContext ctx = new VelocityContext(map);&lt;br /&gt;&lt;br /&gt;// Merge - assumes we already have a VelocityEngine&lt;br /&gt;velocityEngine.mergeTemplate("MyTemplate.txt", ctx, writer);&lt;/pre&gt;&lt;br /&gt;    &lt;br /&gt;The result would look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;Dear Fred,&lt;br /&gt;&lt;br /&gt;Here is the list of books you have ordered:&lt;br /&gt;&lt;br /&gt;Quidditch Through the Ages&lt;br /&gt;Fantastic Beasts &amp;amp; Where to Find Them&lt;br /&gt;&lt;br /&gt;Regards...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;iText&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;iText is an open-source package that (among other things) makes it easy to create PDF documents. You use it by creating a &lt;code&gt;Document&lt;/code&gt; object then calling API methods to add paragraphs, images, and tables, set fonts, colours, text alignments and so on.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Back to the plot...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Let's say you want to be able to use Velocity templates to generate PDF documents. As things stand there's an immediate problem: Velocity needs a &lt;code&gt;Writer&lt;/code&gt; to deliver its output to, but iText doesn't include one - and even if it did, how would it know when to insert tables, text font/colour changes, page breaks and so on?&lt;br /&gt;&lt;br /&gt;Here is one possible solution: create an &lt;code&gt;ITextWriter&lt;/code&gt; class, subclassing &lt;code&gt;Writer&lt;/code&gt;, which will bridge the gap. It makes the necessary calls to the iText API according to directives it finds embedded in its input data.&lt;br /&gt;&lt;br /&gt;The way I did this was to add the input characters from the &lt;code&gt;write()&lt;/code&gt; calls to a &lt;code&gt;StringBuffer&lt;/code&gt;, then look at the buffer to see if I had a complete line of text. If the line starts with a '[' and ends with a ']', it's taken to be a directive line. All other lines are treated as text to be added to the document using the current style (font, colour, alignment etc.).&lt;br /&gt;&lt;br /&gt;So let's say I have a Velocity template that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;Here is the report for&lt;br /&gt;[bold]&lt;br /&gt;[color red]&lt;br /&gt;$date&lt;br /&gt;[normal]&lt;br /&gt;[newline]&lt;br /&gt;at&lt;br /&gt;[font verdana 16]&lt;br /&gt;[bold]&lt;br /&gt;[color blue]&lt;br /&gt;$time&lt;br /&gt;[normal]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Velocity processes this and directs the output into the &lt;code&gt;ITextWriter&lt;/code&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;Here is the report for&lt;br /&gt;[bold]&lt;br /&gt;[color red]&lt;br /&gt;Wednesday, July 25 2007&lt;br /&gt;[normal]&lt;br /&gt;[newline]&lt;br /&gt;at&lt;br /&gt;[font verdana 16]&lt;br /&gt;[bold]&lt;br /&gt;[color blue]&lt;br /&gt;12:47pm&lt;br /&gt;[normal]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, here's where the technique I described yesterday comes into play. When a directive line is found the code breaks out the first keyword (&lt;code&gt;color&lt;/code&gt;, for example) as the directive name and places the rest into a &lt;code&gt;String[]&lt;/code&gt; array. Then it locates a method with the directive name that accepts a &lt;code&gt;String[]&lt;/code&gt; argument and calls it, passing the array as the argument. The method makes the necessary calls to the iText API to take care of getting the data out to the PDF document in the specified format. The result:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;Here is the report for &lt;strong&gt;&lt;font color="red"&gt;Wednesday, July 25 2007&lt;/font&gt;&lt;/strong&gt;&lt;br /&gt;at &lt;strong&gt;&lt;font color="blue"&gt;12:47pm&lt;/font&gt;&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The great thing about this is that if I need some new formatting control, all I need to do is add a new method with a meaningful name - and the job's done:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;void pagebreak(String[] args)&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In fact, the first version of the writer only had perhaps half a dozen directive methods and I added new ones as I went along, as I found I needed them for the project I was working on. The current version has about twenty methods, and most of the new methods took only minutes to create and test.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-1345322188104251182?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/1345322188104251182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=1345322188104251182' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/1345322188104251182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/1345322188104251182'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/07/more-of-same.html' title='More of the same'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-6811861038895488157</id><published>2007-07-24T16:53:00.000-05:00</published><updated>2008-06-06T14:15:44.682-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>A Technique for Faster Development</title><content type='html'>Take a look at this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;import static java.lang.Integer.parseInt;&lt;br /&gt;import static java.lang.System.out;&lt;br /&gt;&lt;br /&gt;import java.lang.reflect.Method;&lt;br /&gt;&lt;br /&gt;public class DynamicMethods&lt;br /&gt;{&lt;br /&gt;    public int operation(String op,&lt;br /&gt;            int a,&lt;br /&gt;            int b) throws Exception&lt;br /&gt;    {&lt;br /&gt;        Class&amp;lt;? extends DynamicMethods&amp;gt; c = this&lt;br /&gt;                .getClass();&lt;br /&gt;        Class&amp;lt;?&amp;gt;[] argtypes = {&lt;br /&gt;                int.class, int.class};&lt;br /&gt;        Method m = c&lt;br /&gt;                .getDeclaredMethod(op,&lt;br /&gt;                        argtypes);&lt;br /&gt;        Object[] args = {a, b};&lt;br /&gt;        int result = (Integer) m&lt;br /&gt;                .invoke(this, args);&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    int add(int a, int b)&lt;br /&gt;    {&lt;br /&gt;        return a + b;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args)&lt;br /&gt;            throws Exception&lt;br /&gt;    {&lt;br /&gt;        DynamicMethods dm = new DynamicMethods();&lt;br /&gt;        int c = dm.operation(args[0],&lt;br /&gt;                parseInt(args[1]),&lt;br /&gt;                parseInt(args[2]));&lt;br /&gt;        out.println("Result = " + c);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Look first at the &lt;code&gt;operation()&lt;/code&gt; method. It takes a String ("op") and locates a Method within this class that has that name and takes two &lt;code&gt;int&lt;/code&gt;s as arguments. It then calls that method, passing in the arguments it was given.&lt;br /&gt;&lt;br /&gt;Now, look at the &lt;code&gt;main()&lt;/code&gt;. The first argument is used as the &lt;code&gt;op&lt;/code&gt; value, while the other two are parsed as integer values and passed as the other two arguments in a call to &lt;code&gt;operation()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;So, if we run this little program with the arguments "add 4 8", &lt;code&gt;operation()&lt;/code&gt; calls &lt;code&gt;add()&lt;/code&gt;, passing 4 and 8 as arguments. &lt;code&gt;add()&lt;/code&gt; returns the sum (12), which is printed out by &lt;code&gt;main()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;This may seem trivial but it is the core of a powerful technique that I've found can reduce development time significantly on certain types of projects. Here's the important part: let's say I want to add a "multiply" operation. All I need to do is add the new method...&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;int multiply(int a, int b)&lt;br /&gt;{&lt;br /&gt;    return a * b;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and I'm done. Now I can run the application again with "multiply 4 8" as arguments and it works right away. I don't need to add lookup table entries, or change some XML file to configure in the new function. I can add as many new operations as I like just as easily - all I have to do is make sure that the new methods have a signature that has this pattern:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;int &lt;em&gt;operationName&lt;/em&gt;(int a, int b)&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here's an example with a bit more meat. Recently I developed a small web application to run in Tomcat. It only needed about four different page layouts, so I didn't see the need to mess around with the complication of Struts, JSPs and Action Classes (in my opinion Struts introduces way more complication than is necessary to do what it does anyway, and this only gets worse as the projects get larger). Instead, the HTML is generated from Velocity templates.&lt;br /&gt;&lt;br /&gt;Tomcat is configured to pass all URIs with pathnames ending in "*.do" to the application's one and only servlet. Here is the servlet's &lt;code&gt;service()&lt;/code&gt; method (with try/catch blocks removed for clarity):&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public void service(ServletRequest request,&lt;br /&gt;            ServletResponse response)&lt;br /&gt;            throws ServletException,&lt;br /&gt;            IOException&lt;br /&gt;    {&lt;br /&gt;        // Cast req/resp to more useful types&lt;br /&gt;        HttpServletRequest rq =&lt;br /&gt;            (HttpServletRequest) request;&lt;br /&gt;        HttpServletResponse rs =&lt;br /&gt;            (HttpServletResponse) response;&lt;br /&gt;&lt;br /&gt;        // Get the URI, strip off the leading '/'&lt;br /&gt;        // and the ending ".do"&lt;br /&gt;        String uri = rq.getServletPath();&lt;br /&gt;        String methodName = uri.substring(1,&lt;br /&gt;            uri.indexOf(".do"));&lt;br /&gt;&lt;br /&gt;        // Find the right method&lt;br /&gt;        Class&amp;lt;? extends MyServlet&amp;gt; c = this.getClass();&lt;br /&gt;        Class&amp;lt;?&amp;gt;[] argTypes = {&lt;br /&gt;                HttpServletRequest.class,&lt;br /&gt;                HttpServletResponse.class};&lt;br /&gt;        Method method = c.getDeclaredMethod(methodName,&lt;br /&gt;                        argTypes);&lt;br /&gt;&lt;br /&gt;        // Call the method, get back a Map&lt;br /&gt;        Object[] args = {rq, rs};&lt;br /&gt;        Map&amp;lt;String, Object&amp;gt; m =&lt;br /&gt;          (Map&amp;lt;String, Object&amp;gt;) method.invoke(this, args);&lt;br /&gt;&lt;br /&gt;        // The map contains the required Velocity&lt;br /&gt;        // template name&lt;br /&gt;        String templateName =&lt;br /&gt;            (String) m.get("templateName");&lt;br /&gt;&lt;br /&gt;        // Use the map as a Velocity Context and merge -&lt;br /&gt;        // output streams to the client browser&lt;br /&gt;        VelocityContext ctx = new VelocityContext(m);&lt;br /&gt;        velocityEngine.mergeTemplate(templateName,&lt;br /&gt;                        ctx,&lt;br /&gt;                        response.getWriter());&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's say I need to add a "/menu.do" operation. All I need do in the servlet is add one method:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;Map&amp;lt;String, Object&amp;gt; menu(HttpServletRequest request,&lt;br /&gt;    HttpServletResponse response)&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This method performs whatever business logic is required and returns a Map that can be used as a Velocity context. The map must also contain a key, "templateName" identifying the Velocity template that the context is to be merged with. &lt;code&gt;service()&lt;/code&gt; takes care of the rest.&lt;br /&gt;&lt;br /&gt;This isn't a 100% ideal solution because part of the presentation logic (building the map) is in the methods along with the business logic - but the actual rendering is controlled by the template, so the methods don't need to mess with that. But for small applications I've found this is an excellent approach.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-6811861038895488157?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/6811861038895488157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=6811861038895488157' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/6811861038895488157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/6811861038895488157'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2007/07/technique-for-faster-development.html' title='A Technique for Faster Development'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-113107764547423815</id><published>2005-11-03T22:10:00.000-06:00</published><updated>2008-06-06T14:16:34.888-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metablogging'/><title type='text'>Java Planet is on extended vacation</title><content type='html'>&lt;span class="subhead"&gt;No new posts for the foreseeable future, folks...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sorry to say I don't really have the time any more to maintain two blogs. Many people evidently put blog-writing high on their priority lists, but I'm not one of those people; I write as and when I feel like it and have the time outside my other interests.&lt;br /&gt;&lt;br /&gt;I'll leave the existing posts in place just in case anyone has them bookmarked for reference. And anyway, because I may still post occasionally as I come across interesting Java hints, tips and ideas, I have no intention of deleting the posts.&lt;br /&gt;&lt;br /&gt;So, until such time as I have more to add, I will bid you all "Happy Java" :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-113107764547423815?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/113107764547423815/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=113107764547423815' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/113107764547423815'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/113107764547423815'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/11/java-planet-is-on-extended-vacation.html' title='Java Planet is on extended vacation'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112750613025417521</id><published>2005-09-23T15:07:00.000-05:00</published><updated>2008-06-06T14:16:34.889-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metablogging'/><title type='text'>Taking a break</title><content type='html'>No post last week because we were &lt;a href="http://the-r-blog.blogspot.com/2005/09/chicago-chicago.html"&gt;out of town&lt;/a&gt;. I'm working on this coming week's post now but I'm not feeling great so it may be delayed a day or two.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112750613025417521?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112750613025417521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112750613025417521' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112750613025417521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112750613025417521'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/09/taking-break.html' title='Taking a break'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112610694829387289</id><published>2005-09-07T10:28:00.000-05:00</published><updated>2008-06-06T14:17:12.746-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='General Java'/><title type='text'>Java isn't just for applets</title><content type='html'>&lt;span class="subhead"&gt;How Java could change just about everything&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This article is a little late because of the Labor Day holiday weekend. Also, this is not a technical article as such; instead this will be, in part, an answer to a question that was posted in a comment on my &lt;a href="http://the-r-blog.blogspot.com/"&gt;Augmented Reality&lt;/a&gt; blog. The question, from &lt;a href="http://www.blogger.com/profile/3508227"&gt;QiSoftware&lt;/a&gt;, was:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;You write a lot of Java programs for non-internet use? Is there an audience for Java at this level?&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;The short answer that I gave was basically that Java is no longer just for Internet applications and web-page applets. The full answer is a little more involved. What follows is my opinion of the current Java landscape.&lt;br /&gt;&lt;br /&gt;Most of the Java applications I develop these days are for Internet or intranet use - but in our networked world, the same is probably true of applications that C and C++ developers are creating. Java is also just as useful for creating stand-alone applications, including those with GUIs. The main advantage of Java over C/C++ for almost any application can be summed up in a single word: Portability. I can develop and test an application on a Windows system and be almost certain that I can move the compiled binary to any other platform and it will work.&lt;br /&gt;&lt;br /&gt;Of course, it's not all wine and roses; Java has its drawbacks too. For one thing, because Java compiles to interpreted bytecodes it's not as efficient as a purely compiled language. Things are getting better all the time on that front, because the JVMs can translate bytecodes to native machine code and the result is that a Java application running on a modern JVM isn't much slower than a native-compiled equivalent. For anything but the most high-performance applications, Java is fast enough.&lt;br /&gt;&lt;br /&gt;Other drawbacks aren't so much technical as, for want of better terms, legal and political. For example, because of Sun's licensing restrictions you can't guarantee that any target platform will have a Java runtime installed, which means that you can't just give a Java application to a non-technical user and have them install and use it as easily as a native application - they may have to go to Sun's Java website and download and install a JRE (making sure to sign the license agreements) before they can run any Java applications.&lt;br /&gt;&lt;br /&gt;One day, I hope, we will see JVMs being packaged as part of all major operating systems so that it's there and usable right out of the box. You'll be able to install Java applications as easily as native Windows or Linux applications, and the OS will recognize executable JAR files as such and know when you click on an application icon that it needs to fire up the JVM to run it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112610694829387289?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112610694829387289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112610694829387289' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112610694829387289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112610694829387289'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/09/java-isnt-just-for-applets.html' title='Java isn&apos;t just for applets'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112529156706917993</id><published>2005-08-28T23:56:00.000-05:00</published><updated>2008-06-06T14:15:44.682-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>Static Initializers</title><content type='html'>&lt;span class="subhead"&gt;An extremely powerful feature&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here is a very simple class:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public class Sample&lt;br /&gt;{&lt;br /&gt;    public static int x = 6;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is straightforward enough - any code that reads &lt;code&gt;Sample.x&lt;/code&gt; will get the correct value. The question is, where is the value of &lt;code&gt;x&lt;/code&gt; actually set?&lt;br /&gt;&lt;br /&gt;The answer is that the Java compiler generates code to initialize the static data, and this code is included in the resulting class file. The JVM executes this code as soon as the class has been loaded into memory. In the case of our &lt;code&gt;Sample&lt;/code&gt; class, the first reference to the class causes the JVM to locate the class file, load it into memory then run the initialization code. All of the initialization code for a given class is encapsulated into a single "method", called the &lt;em&gt;static initializer&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;Static initializers do not have names, arguments, or access modifiers and do not return any values - in other words, you can't call static initializers as if they were methods. A class's static initializer is run once and once only during the lifetime of an application, at the point where the class is loaded.&lt;br /&gt;&lt;br /&gt;If initializing data fields were all that static initializers did, they wouldn't be particularly interesting. What makes them far more powerful is that you can write your own code to be added to the static initializers of your own classes. Here is an example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public class Sample2&lt;br /&gt;{&lt;br /&gt;    public final static Properties props;&lt;br /&gt;    static&lt;br /&gt;    {&lt;br /&gt;        props = new Properties();&lt;br /&gt;        try&lt;br /&gt;        {&lt;br /&gt;            InputStream is = new FileInputStream(&lt;br /&gt;                    "my.properties");&lt;br /&gt;            props.load(is);&lt;br /&gt;            is.close();&lt;br /&gt;        }&lt;br /&gt;        catch (Exception e)&lt;br /&gt;        {&lt;br /&gt;            e.printStackTrace();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first thing to note is the &lt;code&gt;static&lt;/code&gt; keyword followed by a block of code - this is how we tell the compiler that the code is to be added to the static initializer. In this case loading the class causes it to load the &lt;code&gt;Properties&lt;/code&gt; object from a file.&lt;br /&gt;&lt;br /&gt;Some other things to know about static initializers: first, you can include as many &lt;code&gt;static{...}&lt;/code&gt; sections as you like in a class source; each section of compiled code is added to the static initializer in the order the static blocks appear in the source. Second, static initializer code can throw only uncaught exceptions (hence the &lt;code&gt;try/catch&lt;/code&gt; block in the example). Third, note that any &lt;code&gt;final static&lt;/code&gt; data must be initialized; in the example the final &lt;code&gt;props&lt;/code&gt; field isn't initialized at the point where it's declared, which would normally be an error, but it's assigned in our static initializer so the compiler doesn't complain.&lt;br /&gt;&lt;br /&gt;You can put pretty much any code you like into static initializers - this simple fact makes them surprisingly powerful.&lt;br /&gt;&lt;br /&gt;For example, if you've ever used JDBC you may have wondered how it manages to select the correct driver for a given database URL when there is more than one driver loaded. The trick is that all JDBC drivers' static initializers include code that creates an instance of the driver and then passes this object to a static method in the &lt;code&gt;DriverManager&lt;/code&gt; class, which adds the driver to an internal list; when your application passes a URL string to &lt;code&gt;DriverManager.getConnection()&lt;/code&gt;, that method iterates through the list of drivers and passes the URL to each driver in turn, until one of them recognizes the URL format and opens and returns a database connection.&lt;br /&gt;&lt;br /&gt;Another example is Log4J, whose &lt;code&gt;Logger&lt;/code&gt; class has a static initializer that by default scans the directories in the classpath looking for a file named "log4j.xml" or "log4j.properties". If it finds such a file it reads the logging configuration from it. This means that you don't need to write a single line of configuration code - the configuration can be completely defined in a file, and this will be loaded automatically the first time your application references the &lt;code&gt;Logger&lt;/code&gt;. &lt;code&gt;Velocity&lt;/code&gt; - a templating package from the Apache Jakarta project - uses a similar technique to load its default configuration settings.&lt;br /&gt;&lt;br /&gt;I leave it to you, adventurous reader, to think of more applications for static initializers.&lt;br /&gt;&lt;br /&gt;Considering that static initializers are so powerful and also that it would be impossible to understand some commonly-used packages without having an understanding of them, it is strange and surprising that there are a number of (otherwise excellent) books about Java programming that that make no mention of them whatsoever. My recommendation, when choosing a book, is to check the index for mention of static initializers; if there's no reference, move on to the next book.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112529156706917993?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112529156706917993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112529156706917993' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112529156706917993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112529156706917993'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/08/static-initializers.html' title='Static Initializers'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112465270639310897</id><published>2005-08-21T14:27:00.000-05:00</published><updated>2008-06-06T14:15:44.683-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>How to set up a simple LRU cache using LinkedHashMap</title><content type='html'>&lt;span class="subhead"&gt;Caches&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Caches are a simple way to improve the performance of an application that reads data from a "slow" source such as files on disk or rows of data from a database table, but may need to re-read the same data multiple times. The idea is simple: instead of discarding the data after using it, keep it in memory so that it doesn't have to be re-read later.&lt;br /&gt;&lt;br /&gt;For example, a simple way to organize this for files might be to create a &lt;code&gt;HashMap&lt;/code&gt; that maps the file names to objects containing the file data. When your application needs a particular file it first checks the map to see if it already has the data; if it doesn't, it reads the file and places it in the map in case it needs it again later.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;LRU caches&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The problem with this simple method is that your application could use a vast amount of memory. Once read in, a file is in the cache for the lifetime of the application whether or not it is ever used again. For an application such as a server that is intended to stay up and running for weeks or months at a time, this is probably not acceptable.&lt;br /&gt;&lt;br /&gt;What's needed is a cache that automatically discards entries that haven't been accessed for some time so as to limit the amount of space being used. Such as cache is called an &lt;em&gt;LRU Cache&lt;/em&gt;. LRU stands for "Least Recently Used", which refers to the policy of removing the oldest, or least-recently used entries to make space for new data.&lt;br /&gt;&lt;br /&gt;LRU caches have a maximum number of data items that they will hold and these items are usually arranged in a list. When an item is added to the cache, and every time it is accessed after that, it is automatically moved to the head of the list. If the cache is full and a slot is required for a new item, the cache makes room by discarding the entry at the tail of the list - the least-recently used item.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;The &lt;code&gt;java.util.LinkedHashMap&lt;/code&gt; class&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;LinkedHashMap&lt;/code&gt; is a subclass of &lt;code&gt;java.util.HashMap&lt;/code&gt; that adds a couple of useful features. One is that by default the iteration order reflects the order that entries are added to the map, rather than the rather haphazard order of a HashMap.&lt;br /&gt;&lt;br /&gt;The other feature - the one we're interested in here - is that &lt;code&gt;LinkedHashMap&lt;/code&gt; has an option to use access-order instead of insertion order, and includes a way to remove the least-recently accessed entries automatically. This makes it well suited for creating LRU caches.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Creating a cache class&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The simplest way to create a cache using &lt;code&gt;LinkedHashMap&lt;/code&gt; is to extend it. Here is an example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public class Cache extends&lt;br /&gt;    LinkedHashMap&lt;br /&gt;{&lt;br /&gt;  private final int capacity;&lt;br /&gt;&lt;br /&gt;  public Cache(int capacity)&lt;br /&gt;  {&lt;br /&gt;    super(capacity + 1, 1.1f, true);&lt;br /&gt;    this.capacity = capacity;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected boolean removeEldestEntry(Entry eldest)&lt;br /&gt;  {&lt;br /&gt;    return size() &gt; capacity;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that the constructor takes as an argument the maximum number of entries we want a Cache object to hold. The superclass constructor has three arguments: the initial capacity of the map, the load factor, and a boolean argument that tells the &lt;code&gt;LinkedHashMap&lt;/code&gt; constructor to keep entries in access order instead of the default insertion order. (See the &lt;code&gt;java.util.HashMap&lt;/code&gt; API documentation for a description of the initial capacity and load factor.) In this case we set the initial capacity to be one more than our required cache size - this is because new entries are added before any are removed, so for example if we want to hold 100 entries the cache will actually contain 101 for an instant when new data is added. Setting the load factor to 1.1 ensures that the rehashing mechanism of the underlying &lt;code&gt;HashMap&lt;/code&gt; class isn't triggered - this isn't a vital point but helps a little with efficiency at run-time.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;removeEldestEntry()&lt;/code&gt; method overrides a default implementation in &lt;code&gt;LinkedHashMap&lt;/code&gt; and is where we determine the policy for removing the oldest entry. In this case, we return &lt;code&gt;true&lt;/code&gt; when the cache has more entries than our defined capacity.&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;Using the cache&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Using the cache is simple - just use a suitable key to access the cache; if the data is in the cache we can read it from there. If it's not there we pull it from the slow medium and add it to the cache so that it's in place if needed later:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;    Cache cache = new Cache(100);&lt;br /&gt;    // ...&lt;br /&gt;&lt;br /&gt;    String filename = "file.txt";&lt;br /&gt;    String filedata = (String) cache&lt;br /&gt;        .get(filename);&lt;br /&gt;    if (filedata == null)&lt;br /&gt;    {&lt;br /&gt;      // Read filedata from file system here...&lt;br /&gt;      cache.put(filename, filedata);&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="subhead"&gt;How does it perform?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Our basic implementation should work just fine but we may need to know how effective it is in a given application. One measure of how well a cache is performing is &lt;em&gt;Hit Rate&lt;/em&gt;, which tells us how many cache accesses are "hits" - that is, how many times the required data was found in the cache for a given number of accesses. Hit rate is usually expressed as a percentage - a hit rate above 80% is usually pretty good.&lt;br /&gt;&lt;br /&gt;We can add a few things to our Cache class to make it possible to monitor performance so that we can tune the cache by setting an optimum size. We need a counter for the number of accesses and another for the number of hits. We will need getter methods to allow us to retrieve those values after the cache has been running for a time, and finally we must override the &lt;code&gt;get()&lt;/code&gt; method to update the counts. Here is our Cache class complete with these new members:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public class Cache extends&lt;br /&gt;    LinkedHashMap&lt;br /&gt;{&lt;br /&gt;  private final int capacity;&lt;br /&gt;  private long accessCount = 0;&lt;br /&gt;  private long hitCount = 0;&lt;br /&gt;&lt;br /&gt;  public Cache(int capacity)&lt;br /&gt;  {&lt;br /&gt;    super(capacity + 1, 1.1f, true);&lt;br /&gt;    this.capacity = capacity;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Object get(Object key)&lt;br /&gt;  {&lt;br /&gt;    accessCount++;&lt;br /&gt;    if (containsKey(key))&lt;br /&gt;    {&lt;br /&gt;      hitCount++;&lt;br /&gt;    }&lt;br /&gt;    Object value = super.get(key);&lt;br /&gt;    return value;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected boolean removeEldestEntry(Entry eldest)&lt;br /&gt;  {&lt;br /&gt;    return size() &gt; capacity;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public long getAccessCount()&lt;br /&gt;  {&lt;br /&gt;    return accessCount;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public long getHitCount()&lt;br /&gt;  {&lt;br /&gt;    return hitCount;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One point to note is that calls to the &lt;code&gt;containsKey()&lt;/code&gt; method don't update the access counts; we may want to override that method also, so that the hit rate isn't skewed by code such as this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;    if (cache.containsKey(filename))&lt;br /&gt;    {&lt;br /&gt;      filedata = (String) cache&lt;br /&gt;          .get(filename);&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      // Read filedata from file system here...&lt;br /&gt;      cache.put(filename, filedata);&lt;br /&gt;    }&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112465270639310897?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112465270639310897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112465270639310897' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112465270639310897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112465270639310897'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/08/how-to-set-up-simple-lru-cache-using.html' title='How to set up a simple LRU cache using LinkedHashMap'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112381965280428636</id><published>2005-08-11T22:44:00.000-05:00</published><updated>2008-06-06T14:15:44.683-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>Neat Trick #1</title><content type='html'>&lt;span class="subhead"&gt;How to log a message identifying the location in code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Consider this code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public class Debug&lt;br /&gt;{&lt;br /&gt;    public static void debug(String message)&lt;br /&gt;    {&lt;br /&gt;        StackTraceElement ste =&lt;br /&gt;            new Throwable().getStackTrace()[1]; // &amp;lt;==&lt;br /&gt;        System.out.println("[" + ste.getFileName()&lt;br /&gt;                + ":"&lt;br /&gt;                + ste.getLineNumber()&lt;br /&gt;                + "] "&lt;br /&gt;                + message);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we can call this from other code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;public class LoggerDemo&lt;br /&gt;{&lt;br /&gt;    public static void main(String[] args)&lt;br /&gt;    {&lt;br /&gt;        Debug.debug("Test Message");&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The output will look something like this - note that we get the source file name and line number where the call was made:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;[LoggerDemo.java:16] Test Message&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This can be an extremely useful trick. The secret to how this works is all in the line marked with the "&lt;code&gt;&amp;lt;==&lt;/code&gt;" marker comment.&lt;br /&gt;&lt;br /&gt;Whenever a &lt;code&gt;Throwable&lt;/code&gt; object is created (or an &lt;code&gt;Exception&lt;/code&gt;, which is a subclass of &lt;code&gt;Throwable&lt;/code&gt;), the JVM creates a stack trace in the form of an array of &lt;code&gt;StackTraceElement&lt;/code&gt; objects. (Note that you can create a &lt;code&gt;Throwable&lt;/code&gt; object using &lt;code&gt;new&lt;/code&gt; just like any other class, and just because it's &lt;code&gt;Throwable&lt;/code&gt; doesn't mean that it &lt;em&gt;must&lt;/em&gt; be thrown.) The &lt;code&gt;getStackTrace()&lt;/code&gt; method returns the stack trace array.&lt;br /&gt;&lt;br /&gt;The elements of the array are ordered starting at the point in the code where the &lt;code&gt;Throwable&lt;/code&gt; was created, so element 0 would contain details about the line containing the &lt;code&gt;new Throwable()&lt;/code&gt; call. Element 1 is the one we want because it tells us about where the &lt;code&gt;debug()&lt;/code&gt; method was called from.&lt;br /&gt;&lt;br /&gt;As you can see the &lt;code&gt;StackTraceElement&lt;/code&gt; contains information about the source file and line number. It also contains the class and method name, so our &lt;code&gt;debug()&lt;/code&gt; method could report that too if it was useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112381965280428636?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112381965280428636/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112381965280428636' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112381965280428636'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112381965280428636'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/08/neat-trick-1.html' title='Neat Trick #1'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112326149686620699</id><published>2005-08-05T13:33:00.000-05:00</published><updated>2008-06-06T14:15:44.684-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>Character development</title><content type='html'>&lt;span class="subhead"&gt;Representing characters for global software&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When we used to write in C, we didn't often concern ourselves with the character set we worked with. In general you can write a string constant in the usual way like this: &lt;code&gt;"Hello, world"&lt;/code&gt; - and the compiler converts it to an array of encoded bytes using the character set of the underlying platform. Because most computers in the western world use ASCII or a compatible derivative, this works just fine most of the time for those of us working in ASCII-speaking regions such as Britain and the US.&lt;br /&gt;&lt;br /&gt;Things start to go wrong when you start to get a little more adventurous. For example, to check that a character is an upper-case alphabetic you might do something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;  if ((c &gt;= 'A') &amp;&amp; (c &lt;= 'Z'))&lt;br /&gt;  {&lt;br /&gt;    ...&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will work fine on a PC and many other systems, but what if you try to run this on an IBM AS/400 system? That system didn't use ASCII - it used a different standard called EBCDIC instead (and may still do, if there are any of these machines still around today). On such a machine some characters, such as '}' and '&amp;uuml;', will pass the test in that example code as if they were upper-case alphabetic characters.&lt;br /&gt;&lt;br /&gt;Ok, that's a contrived example. But what about writing code that will work in German, with umlauts over some of the vowels? Or Danish, which has 29 characters in the alphabet? Or in Korean or Arabic, which don't even use the same alphabets?&lt;br /&gt;&lt;br /&gt;Java handles this whole problem by working internally with Unicode no matter what the underlying platform uses. Unicode is a character encoding that's designed to allow any characters in use anywhere in the world to be represented. The problem from a developer perspective is how to get those Unicode characters in and out of your applications, because most systems as yet don't use Unicode as a native format for storing files or displaying text on the screen.&lt;br /&gt;&lt;br /&gt;Mostly you don't have to worry about this. The &lt;code&gt;InputStreamReader&lt;/code&gt; and &lt;code&gt;OutputStreamWriter&lt;/code&gt; classes, for example, convert characters between Unicode and the platform's default character set without you needing to take explicit action.&lt;br /&gt;&lt;br /&gt;But in today's networked world, many applications aren't designed to handle just one system - you may be developing client/server applications. What if your server is running on a system where the platform character set is Latin-9, and the client is on a Latin-1 system? The Unicode in the client application will be converted to Latin-1 and sent to the server, but the server will interpret the bytes as Latin-9. Since the mappings between Latin-1 and Latin-9 don't quite line up, some characters from the client will be wrong by the time they get into the server as Unicode.&lt;br /&gt;&lt;br /&gt;As it happens this is quite easy to fix. &lt;code&gt;InputStreamReader&lt;/code&gt; and &lt;code&gt;OutputStreamWriter&lt;/code&gt; both have constructors that will let you specify the encoding you want to use. In this case you could set the encoding on both sides to be Latin-9 explicitly by putting something like this in the client and server sides:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;  String charset = "ISO-8859-15"; // a.k.a. Latin-9&lt;br /&gt;  reader = new BufferedReader(new InputStreamReader(socket&lt;br /&gt;         .getInputStream(), charset));&lt;br /&gt;  writer = new BufferedWriter(new OutputStreamWriter(socket&lt;br /&gt;         .getOutputStream(), charset));&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now it doesn't matter what the default encoding of the client and server platforms are; your applications will communicate using Latin-9 regardless.&lt;br /&gt;&lt;br /&gt;Should you use Latin-9? Probably not. For one thing, the Java specification defines a minimal set of encodings that all Java implementations must support, and Latin-9 isn't one of these. A better choice is probably UTF-8 - this &lt;em&gt;is&lt;/em&gt; mandated in the specification so all Java runtimes must support it, and better still it allows &lt;em&gt;all&lt;/em&gt; Unicode characters to be encoded and transferred so your application would be able to handle Russian, Greek and even Arabic and Japanese characters.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112326149686620699?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112326149686620699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112326149686620699' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112326149686620699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112326149686620699'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/08/character-development.html' title='Character development'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15028967.post-112304747696724711</id><published>2005-08-03T00:35:00.000-05:00</published><updated>2008-06-06T14:15:44.684-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pure Java'/><title type='text'>Waiting as fast as I can</title><content type='html'>&lt;span class="subhead"&gt;How to avoid an occasional problem with &lt;code&gt;Thread.sleep()&lt;/code&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Every so often you find that you need a background thread that runs at intervals to perform some background task. Of course the simplest way to do this is to use &lt;code&gt;Thread.sleep()&lt;/code&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;  while (true)&lt;br /&gt;  {&lt;br /&gt;    // Perform essential task here...&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    // Now wait for 10 seconds before continuing&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;      Thread.sleep(10000);&lt;br /&gt;    }&lt;br /&gt;    catch (InterruptedException ie)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will work fine - it will suspend the thread for ten seconds each time round the loop. However, if it takes two seconds to perform the "essential task", then the task will actually be performed once every twelve seconds instead of ten. What if it's important to run every ten seconds regardless of how long it takes to run the task? Well, that's easy enough to fix:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;  while (true)&lt;br /&gt;  {&lt;br /&gt;    // Perform essential task here...&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    // Now wait for next 10-second interval&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;      // This calculates how long to wait&lt;br /&gt;      long interval = 10000 -&lt;br /&gt;        (System.currentTimeMillis() % 10000);&lt;br /&gt;      Thread.sleep(interval);&lt;br /&gt;    }&lt;br /&gt;    catch (InterruptedException ie)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This calculates how many milliseconds have elapsed since the clock last showed a time which was a multiple of ten seconds ("&lt;code&gt;System.currentTimeMillis() % 10000&lt;/code&gt;") then subtracts that from 10000, which tells us how many milliseconds until the next 10-second multiple. Now the code suspends for that many milliseconds before returning to the top of the loop and running the core task again. The task will therefore run when the system clock shows 0, 10, 20, 30, 40 and 50 seconds after each minute, regardless of how long the task takes (as long as it's less than ten seconds, of course).&lt;br /&gt;&lt;br /&gt;There is just one slight problem with this code: it doesn't always work. I used code just like this in a server application and found that quite often it would run the core task twice in quick succession every ten seconds, instead of once. It turned out that the thread was coming out of the &lt;code&gt;Thread.sleep()&lt;/code&gt; early by as much as fifteen or twenty milliseconds. It would then run the core task in just a couple of milliseconds, so when it recalculated the wait interval there was still ten milliseconds or so before the next clock "tick" - actually the &lt;em&gt;same&lt;/em&gt; clock tick that it was waiting for on the previous call. The task would then be re-executed just a few milliseconds later.&lt;br /&gt;&lt;br /&gt;I never did figure out the root cause, although it did seem that this problem never showed up on the Windows desktop box I develop on or the small Linux system I use as a test platform. It only happened when I moved the code to the 4-processor Sun server, and that makes me think that maybe this is something to do with concurrent thread synchronization on multiprocessor systems (although I think it's more likely that it's something to do with the clock granularity).&lt;br /&gt;&lt;br /&gt;The final fix? I wrote this as a general method that can be used to synchronize on any millisecond interval:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;  public static void syncSleep(long ms)&lt;br /&gt;  throws InterruptedException&lt;br /&gt;  {&lt;br /&gt;    long now = System.currentTimeMillis();&lt;br /&gt;    long interval = (ms - (now % ms));&lt;br /&gt;    long expectedEndTime = now + interval;&lt;br /&gt;    do&lt;br /&gt;    {&lt;br /&gt;      Thread.sleep(interval);&lt;br /&gt;      now = System.currentTimeMillis();&lt;br /&gt;      interval = expectedEndTime - now;&lt;br /&gt;    }&lt;br /&gt;    while (interval &gt; 0);&lt;br /&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this, if the &lt;code&gt;Thread.sleep()&lt;/code&gt; returns early it doesn't matter - the code detects the fact and waits again. Now we can recode the original thread code like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="codelet"&gt;  while (true)&lt;br /&gt;  {&lt;br /&gt;    // Perform essential task here...&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    // Now wait for next 10-second interval&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;      syncSleep(10000);&lt;br /&gt;    }&lt;br /&gt;    catch (InterruptedException ie)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;  }&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15028967-112304747696724711?l=java-planet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://java-planet.blogspot.com/feeds/112304747696724711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15028967&amp;postID=112304747696724711' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112304747696724711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15028967/posts/default/112304747696724711'/><link rel='alternate' type='text/html' href='http://java-planet.blogspot.com/2005/08/waiting-as-fast-as-i-can.html' title='Waiting as fast as I can'/><author><name>Pete Ford</name><uri>http://www.blogger.com/profile/13707292131480257729</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://www.codexombie.com/SouthParkMe.jpg'/></author><thr:total>0</thr:total></entry></feed>
