One of the advantages of writing a blog post or an article is that the author has an opportunity to think about what he or she writes before publishing it. Presenting at a conference is always more risky because one might have a momentary memory lapse or gap in judgment and say something he or she will later regret. While this can happen in writing, there are usually a few more guards (such as a in a blog posting) to many guards (such as in peer-reviewed writing). One common thread we often see in both writing on technical subjects and presentations on technical subjects is an implied expertise in the subject material and often an associated implication that the author can do no wrong.
It is understandable that an author or presenter would not want to admit to his or her shortcomings or lack of knowledge. Even when a person really is an expert in an area, any sign of weakness can be and unfortunately often is interpreted as a significant hole in that person's expertise. It is natural for authors to only want to present their good side and to only write about or talk about things they understand.
The reason that I include the word "speculations" in my blog title is because I often do mix experiences, observations, thoughts, and cogitations with some speculation. This is also why I include a disclaimer on my blog that essentially states that while I try to be accurate in what I write, there is no way I can guarantee that everything I write will always be correct. I do not knowingly write something that is false or incorrect, but that doesn't mean that I don't make mistakes.
I think much can be learned from mistakes. We can certainly learn from our own mistakes, but it is even better if we can learn from someone else's mistakes without paying the same price that someone else had to pay. The unfortunate side effect of our seeming human nature to emphasize our strengths and hide our weaknesses means that we can lose some benefits of lessons learned from poor choices.
With all of this in mind, I found Ioannis Cherouvim's recent blog posting The * Stupidest Things I've Done in My Programming Job to be especially refreshing. It is not easy to post mistakes one has made publicly and open oneself to potential ridicule. There are some mistakes on this list that are not trivial. However, this post seems to be evidence of what the blog author has learned. It also provides an opportunity from others to learn from his mistakes without having to make the same mistakes themselves.
Predictably, there are some negative feedback comments that do, in effect, ridicule the blog author. Others, however, encourage the blog author for his candor and for describing the types of poor decisions we all see (and in some cases make) fairly often. Perhaps the most interesting facet of the feedback is that some of the acknowledged mistakes are now being debated as to whether they are truly mistakes. I love discussions such as these because, in the end, few things are really as cut and dry as we like to pretend.
UPDATE (7 March 2009): Another refreshingly honest blog posting is How and Why I Missed the Boat as a Developer.
Dustin's Software Development Cogitations and Speculations My observations and thoughts concerning software development (general development, Java, JavaFX, Groovy, Flex, ...). Select posts from this blog are syndicated on DZone and Java Code Geeks and were formerly syndicated on JavaWorld.
Sunday, February 8, 2009
Monday, February 2, 2009
The Java SE 6 NavigableMap
I have written and blogged previously about some of my favorite Java SE 6 features such as the Deque, [Sun's] inclusion of VisualVM, [Sun's] Java HTTP Server, custom JMX MXBeans, String.isEmpty(), and [Sun's] inclusion of JAXB and annotations processing. In this blog posting, I intend to discuss the NavigableMap, a Java Collections interface that I don't use often, but which comes in very handy in certain situations.
The
FavoriteMovies.java
The above code populates a TreeMap implementation of
When the above class is run, the output appears as shown next:

The output screen snapshot shows how several significant
For completeness, I include the simple code listings for the
Movie.java
MovieGenre.java
Conclusion
The Java SE 6 NavigableMap is not something I use on a daily basis, but it has come in handy now and then. It is easy to apply and, in the appropriate situations, makes it really easy to navigate a
In honor of today being Groundhog Day, it is time to watch the eighth movie on today's list: Groundhog Day.
The
NavigableMap extends the SortedMap and adds methods to this interface specifically designed for easy navigation (hence the name). The next code listing, for the FavoriteMovies class, demonstrates how easy it is to apply the NavigableMap.FavoriteMovies.java
package dustin.examples.navigable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
/**
* Demonstrate a NavigableMap.
*
* @author Dustin
*/
public class FavoriteMovies
{
/** New line separator. */
private static final String NEW_LINE = System.getProperty("line.separator");
/** Header bar used for separation of output sections. */
private static final String HEADER_BAR =
"=======================================================================";
/**
* Favorite movies with Integer key represent the movie's rating in the
* favorites and the value being the movie itself.
*/
private NavigableMap<Integer, Movie> favoriteMovies =
new TreeMap<Integer, Movie>();
/**
* Add a movie to my favorites.
*
* @param newRanking Ranking of movie being added to favorites.
* @param newMovie Movie being added to favorites.
*/
public void addMovie(
final Integer newRanking, final Movie newMovie)
{
favoriteMovies.put(newRanking, newMovie);
}
/**
* Write a header with the provided headerText to the provided OutputStream.
*
* @param headerText Text to be written in header.
* @param out OutputStream to which to write header.
* @throws java.io.IOException Thrown if header cannot be written to the
* provided OutputStream.
*/
private void printHeader(
final String headerText, final OutputStream out) throws IOException
{
out.write(NEW_LINE.getBytes());
out.write(HEADER_BAR.getBytes());
out.write(NEW_LINE.getBytes());
out.write("= ".getBytes());
out.write(headerText.getBytes());
out.write(NEW_LINE.getBytes());
out.write(HEADER_BAR.getBytes());
out.write(NEW_LINE.getBytes());
}
/**
* Print the contents of my NavigableMap to the provided OutputStream.
*
* @throws IOException Thrown if my contents cannot be written to the
* provided OutputStream.
*/
public void printContents(final OutputStream out) throws IOException
{
printHeader("Contents of Navigable Map", out);
for (Entry<Integer,Movie> favoriteMovie : this.favoriteMovies.entrySet())
{
out.write("Movie Rank #".getBytes());
out.write(String.valueOf(favoriteMovie.getKey()).getBytes());
out.write(": ".getBytes());
out.write(favoriteMovie.getValue().getTitle().getBytes());
out.write(NEW_LINE.getBytes());
}
}
/**
* Write provided label and provided text to provided OutputStream.
*
* @param label Label to be written to OutputStream.
* @param navigableMapText Text to be written to OutputStream.
* @param out OutputStream to which to write label and text.
* @throws java.io.IOException Thrown if exception occurs writing to the
* provided OutputStream.
*/
private void printNavigableMapApproach(
final String label,
final String navigableMapText,
final OutputStream out) throws IOException
{
out.write(label.getBytes());
out.write(": ".getBytes());
out.write(navigableMapText.getBytes());
out.write((NEW_LINE+NEW_LINE).getBytes());
}
/**
* Write the provided Navigable Map to the provided OutputStream with the
* provided label.
*
* @param label Label to be written to OutputStream.
* @param moviesNavigableMap Movies NavigableMap to be written to OutputStream.
* @param out OutputStream to which NavigableMap and its label are to be written.
* @throws java.io.IOException Thrown if exception occurs when writing to the
* provided OutputStream.
*/
private void printMoviesNavigableMap(
final String label,
final NavigableMap<Integer, Movie> moviesNavigableMap,
final OutputStream out) throws IOException
{
out.write(label.getBytes());
out.write(": ".getBytes());
out.write(NEW_LINE.getBytes());
for ( NavigableMap.Entry<Integer,Movie> entry :
moviesNavigableMap.entrySet())
{
out.write("\t#".getBytes());
out.write(entry.getKey().toString().getBytes());
out.write(": ".getBytes());
out.write(entry.getValue().getTitle().getBytes());
out.write(NEW_LINE.getBytes());
}
out.write(NEW_LINE.getBytes());
}
/**
* Write demonstration of several NavigableMap methods to provided
* OutputStream.
*
* @param out OutputStream to which to write output of examples using
* NavigableMap.
* @throws java.io.IOException Thrown if output cannot be written to the
* provided OutputStream.
*/
public void demonstrateNavigableMap(final OutputStream out) throws IOException
{
printHeader("Select NavigableMap Methods", out);
// Demonstrate NavigableMap.firstEntry()
printNavigableMapApproach(
"First Movie [firstEntry()]",
favoriteMovies.firstEntry().getValue().getTitle(),
out);
// Demonstrate NavigableMap.lastEntry()
printNavigableMapApproach(
"Last Movie [lastEntry()]",
favoriteMovies.lastEntry().getValue().getTitle(),
out);
// Demonstrate NavigableMap.floorEntry
printNavigableMapApproach(
"Floor Entry for '6' [floorEntry(K)]",
favoriteMovies.floorEntry(6).getValue().getTitle(),
out);
// Demonstrate NavigableMap.ceilingEntry
printNavigableMapApproach(
"Ceiling Entry for '3' [ceilingEntry(K)]",
favoriteMovies.ceilingEntry(3).getValue().getTitle(),
out);
// Demonstrate NavigableMap.headMap:
// First (lowest) entry automatically assumed; 3 is inclusive (true)
final NavigableMap<Integer, Movie> topMovies =
this.favoriteMovies.headMap(3, true);
printMoviesNavigableMap(
"Top range of movies '1' through '3' [inclusive] - headMap(K,Boolean)",
topMovies,
out);
// Demonstrate NavigableMap.subMap:
// Make lower (from) number inclusive (true) and higher (to) number
// exclusive (false).
final NavigableMap<Integer, Movie> middleMovies =
this.favoriteMovies.subMap(4, true, 7, false);
printMoviesNavigableMap(
"Middle range of movies '4' through '6' [7 not inclusive] - subMap"
+ "(K,Boolean,K,Boolean)",
middleMovies,
out);
// Demonstrate NavigableMap.tailMap:
// Last (highest) entry automatically assumed; 6 is exclusive (false)
final NavigableMap<Integer, Movie> bottomOfBestMovies =
this.favoriteMovies.tailMap(6, false);
printMoviesNavigableMap(
"Bottom range of best movies '7' through '10' [6 not inclusive] "
+ "- tailMap(K,Boolean)",
bottomOfBestMovies,
out);
}
/**
* Set up an instance of me with pre-populated data.
*
* @return Instance of me with pre-populated data (for testing and
* demonstration of this class).
*/
public static FavoriteMovies setUpFavoriteMovies()
{
final FavoriteMovies movies = new FavoriteMovies();
movies.addMovie(
3,
new Movie.Builder().title("Raiders of the Lost Ark")
.genre(MovieGenre.FANTASY)
.build());
movies.addMovie(
2,
new Movie.Builder().title("Star Wars: The Empire Strikes Back")
.genre(MovieGenre.SCIENCE_FICTION)
.build());
movies.addMovie(
4,
new Movie.Builder().title("Men in Black")
.genre(MovieGenre.SCIENCE_FICTION)
.build());
movies.addMovie(
1,
new Movie.Builder().title("Fletch")
.genre(MovieGenre.COMEDY)
.build());
movies.addMovie(
5,
new Movie.Builder().title("Ocean's Eleven")
.genre(MovieGenre.ACTION)
.build());
movies.addMovie(
9,
new Movie.Builder().title("The Outlaw Josey Wales")
.genre(MovieGenre.WESTERN)
.build());
movies.addMovie(
8,
new Movie.Builder().title("Groundhog Day")
.genre(MovieGenre.COMEDY)
.build());
movies.addMovie(
10,
new Movie.Builder().title("The Sixth Sense")
.genre(MovieGenre.HORROR)
.build());
movies.addMovie(
7,
new Movie.Builder().title("War Games")
.genre(MovieGenre.SCIENCE_FICTION)
.build());
movies.addMovie(
6,
new Movie.Builder().title("The Princess Bride")
.genre(MovieGenre.COMEDY)
.build());
return movies;
}
/**
* Main executable to demonstrate NavigableMap.
*
* @param arguments Command-line arguments; none anticipated.
*/
public static void main(final String[] arguments)
{
final FavoriteMovies movies = setUpFavoriteMovies();
try
{
movies.printContents(System.out);
movies.demonstrateNavigableMap(System.out);
}
catch (Exception ex)
{
System.err.println("Exception encountered: " + ex.toString());
}
}
}
The above code populates a TreeMap implementation of
NavigableMap with information about some of my favorite movies and then invokes several methods on that interface to demonstrate the NavigableMap.When the above class is run, the output appears as shown next:

The output screen snapshot shows how several significant
NavigableMap methods work. It also demonstrates the automatic sorting supported by the NavigableMap as a specialized SortedMap: the values were put into the TreeMap intentionally out of order, but are printed in order when the NavigableMap is traversed using the for-each loop.For completeness, I include the simple code listings for the
Movie class and for the MovieGenre class.Movie.java
package dustin.examples.navigable;
/**
* Class representating a movie.
*
* @author Dustin
*/
public class Movie
{
/** Title of the movie. */
private String title;
/** Year of movie's release. */
private int year;
/** Movie's primary director. */
private String director;
/** Movie's primary genre. */
private MovieGenre genre;
/** No-arguments constructor not intended for public consumption. */
private Movie() {}
/**
* Provide my director.
*
* @return My director.
*/
public String getDirector()
{
return this.director;
}
/**
* Provide my genre.
*
* @return My genre.
*/
public MovieGenre getGenre()
{
return this.genre;
}
/**
* Provide my title.
*
* @return My title.
*/
public String getTitle()
{
return this.title;
}
/**
* Provide my rear of release.
*
* @return The year of my release.
*/
public int getYear()
{
return this.year;
}
/**
* Provide a String representation of me.
*
* @return My String representation.
*/
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
builder.append("Title: ").append(this.title);
builder.append("; Year: ").append(this.year);
builder.append("; Director: ").append(this.director);
builder.append("; Genre: ").append(this.genre);
return builder.toString();
}
public static class Builder
{
private String title;
private int year;
private String director;
private MovieGenre genre;
public Builder() {}
public Builder director(final String newDirector)
{
this.director = newDirector;
return this;
}
public Builder genre(final MovieGenre newGenre)
{
this.genre = newGenre;
return this;
}
public Builder title(final String newTitle)
{
this.title = newTitle;
return this;
}
public Builder year(int newYear)
{
this.year = newYear;
return this;
}
public Movie build()
{
return new Movie(this);
}
}
/**
* Constructor intended to be used in conjunction with Builder.
*
* @param builder Builder for building an instance of me.
*/
private Movie(final Builder builder)
{
this.title = builder.title;
this.director = builder.director;
this.year = builder.year;
this.genre = builder.genre;
}
}
MovieGenre.java
package dustin.examples.navigable;
/**
* Represent a movie genre.
*
* @author Dustin
*/
public enum MovieGenre
{
ACTION,
COMEDY,
DRAMA,
FANTASY,
HORROR,
ROMANTIC_COMEDY,
SCIENCE_FICTION,
WESTERN
}
Conclusion
The Java SE 6 NavigableMap is not something I use on a daily basis, but it has come in handy now and then. It is easy to apply and, in the appropriate situations, makes it really easy to navigate a
Map. Although not covered here, there is also a NavigableSet that does for the Set what NavigableMap does for the Map. In fact, the NavigableMap.navigableKeySet() method returns a NavigableSet.In honor of today being Groundhog Day, it is time to watch the eighth movie on today's list: Groundhog Day.
Saturday, January 31, 2009
DBAs, Developers, Complexity, and the Pot Calling the Kettle Black
Gavin Parish's current blog posting Maude, the developers are infighting again... made me smile for two reasons. First, what he said about developers and what is discussed about developers in the referenced link (Brian 'Bex' Huff's Why Do So Many Developers Love Bafflingly Complex Code?) is often true. Second, for a self-confessed DBA to be talking about developers and complexity is the best example I've seen in weeks of the pot calling the kettle black. In this post, I'll look at the many reasons and motivations that explain the prevalence of overly complicated code before describing the anecdotal evidence I've witnessed that prove that some DBAs love their complexity as much as some developers love it.
Reasons (Some) Developers Like Complexity
There seem to be several reasons that developers (including me at times) have created unnecessarily complex code. These include factors discussed in the two just-referenced blog posts. Here is a quick list of some of the prime reasons some developers (maybe all of us at one time or another) seem to generate overly complicated code.
(Some) DBAs Love Complexity Too
I enjoyed the previously cited blog posts on developers and complexity because I have seen the things they describe. I have also seen that (some) DBAs seem to love complexity as much as (some) developers. Here are some anecdotal examples.
Conclusion
It seems like the software development industry (including database administration) is full of unnecessary complexity. This may be for several reasons as discussed above. Because the reasons are so diverse and so cover so many different types of people, I don't see this going away anytime soon.
RMOUG Training Days 2009
Speaking of database administrators and developers, RMOUG Training Days 2009 is just a little over a week away. This conference is an excellent source of information and networking for both developers and administrators. Because I tend to focus on developer-oriented things, this conference is a nice opportunity to see some things from more of a database administrator perspective.
Ann Horton has put together a blog focused on Training Days 2009. This blog already includes several entries including postings on OTN representatives at Training Days (with OTN Greatest Hits 2008 DVDs), why one should attend Training Days, inviting managers and supervisors to Wednesday afternoon events, logistics such as Littleton Light Rail availability and directions and parking, and more.
Reasons (Some) Developers Like Complexity
There seem to be several reasons that developers (including me at times) have created unnecessarily complex code. These include factors discussed in the two just-referenced blog posts. Here is a quick list of some of the prime reasons some developers (maybe all of us at one time or another) seem to generate overly complicated code.
- Lack of understanding of problem - Code can be more complicated than it should be because the problem is not well understood. I almost always realize after I have completed a piece of code that there are ways I could make it simpler or refactor it. This is often a result of better understanding the problem after working through it.
- Lack of understanding of technologies - Code can sometimes be more complicated than it needs to be because the developer doesn't realize there is an easier way to apply the same or even an alternative technology to the problem. For example, I have seen (and heard of) projects where developers essentially wrote their own web servers, data stores, and application servers. On a smaller level, it is easy to write complicated code that could be greatly simplified by using established libraries and frameworks.
- Job Security - I don't think this is as common as some allege, but there is no question that it does happen. If a developer writes code that only he or she can understand, the thinking goes, he or she will become indispensable. I personally find this a little short-sighted and think it typically will only work for short-term security, but that doesn't mean some people don't believe it works for them.
- Show Off - Ever since Elementary School show-and-tell, many of us have had a desire to show off. This can definitely be a factor in why some code is more complicated than it should be, but it can also be difficult to differentiate this motivation from the next two listed here.
- Guilty Pleasures - It is sometimes tempting to write complex code just to see if we can. I have classified this as one of the software developer's guilty pleasures. After showing ourselves that we can do it, we might look forward to showing it off to others as well (the previous point)
- Hell is Paved with Good Intentions - We sometimes bring the complexity upon ourselves by trying too hard. I think of my own early overuse of implementation inheritance when I first learned about object-oriented programming and my similar overuse/abuse of design patterns when I first started looking into them seriously. Unfortunately, it seems that many of us have a tendency to think if a little is good, a lot is great. We are prone to focusing too much on one -ility (especially flexibility) at the cost of other positive features (cannot see the forest for the trees).
- We Cannot Help Ourselves - Unnecessary complexity is not limited to software developers. One simply needs to look at the United States tax code, at most software licenses, at most warranties, or at many other everyday documents to see how prevalent complexity has become in our daily lives.
(Some) DBAs Love Complexity Too
I enjoyed the previously cited blog posts on developers and complexity because I have seen the things they describe. I have also seen that (some) DBAs seem to love complexity as much as (some) developers. Here are some anecdotal examples.
- Whereas some developers do seem to love overly complex object hierarchies, I've seen data models that are so complex that they could rival any class diagram in terms of size and complexity.
- Whereas some developers do seem to love write complex code that is difficult to understand, the most complicated piece of code I can recall ever seeing was a crazy SQL statement with numerous joins and unions. The only person I knew that could understand it was the DBA that wrote it. Even the other DBAs wanted nothing to do with it.
- It is very easy for we developers to get so enamored with design patterns, best practices, and other rules of thumb that we overly complicate what should have been simple. DBAs seem to have a similar tendency to get carried away in their own chasing of patterns and best practices. I have seen cases where extreme database normalization has led to data models that are difficult to maintain and slow to perform. Database Design ROI discusses some of the costs of overly complex and difficult database design.
Conclusion
It seems like the software development industry (including database administration) is full of unnecessary complexity. This may be for several reasons as discussed above. Because the reasons are so diverse and so cover so many different types of people, I don't see this going away anytime soon.
RMOUG Training Days 2009
Speaking of database administrators and developers, RMOUG Training Days 2009 is just a little over a week away. This conference is an excellent source of information and networking for both developers and administrators. Because I tend to focus on developer-oriented things, this conference is a nice opportunity to see some things from more of a database administrator perspective.
Ann Horton has put together a blog focused on Training Days 2009. This blog already includes several entries including postings on OTN representatives at Training Days (with OTN Greatest Hits 2008 DVDs), why one should attend Training Days, inviting managers and supervisors to Wednesday afternoon events, logistics such as Littleton Light Rail availability and directions and parking, and more.
Tuesday, January 27, 2009
Java Properties in XML
Java properties have been a staple of Java development for many years. Even today, Java properties are used in popular frameworks and tools such as the Spring Framework and Ant. Most of the Java properties that I have seen used frequently follow the tried-and-true name=value paradigm. However, since J2SE 5, it has been easy to load (and save) properties in XML format.
In my experience, the typical properties file looks something like that shown next.
examples.properties
J2SE 5 made it easy to load properties from XML (and store properties to XML). The Javadoc-based API documentation for the Properties class discusses both formats. This documentation shows the DTD used to define the Properties XML grammar:
The DTD shows us that properties stored in XML must have
PropertiesExamples.java
The class shown above reads in the properties file listed earlier and then writes it back out in XML format. The actual lines of code doing most of the work are small in number, but the many checked exceptions associated with file input/output make the code base much larger.
When this code is run, the following output is generated:
examples-xml.properties
This generated XML file contains the same name/value pairs as the traditional properties file shown earlier, can be read in like the traditional version using the Properties.loadFromXML, and includes the comment that was passed to the Properties.storeToXML method.
Conclusion
It is fairly straightforward to load properties from XML and to store them as XML. However, the XML is essentially limited to the same paradigm of name/value pairs as traditional properties files. Therefore, we are unable to take advantage of XML's hierarchical nature to use relationships more complex than one key (name) to one value. The primary reason one might use Java's support for XML-based properties is if XML was being used for other tools or frameworks and the properties in XML were more accessible to the other tool or framework.
In my experience, the typical properties file looks something like that shown next.
examples.properties
url.blog.dustin=http://marxsoftware.blogspot.com/
url.javaworld=http://www.javaworld.com/
url.coloradosoftwaresummit=http://www.softwaresummit.com/
url.otn=http://www.oracle.com/technology/index.html
url.rmoug=http://www.rmoug.org/
J2SE 5 made it easy to load properties from XML (and store properties to XML). The Javadoc-based API documentation for the Properties class discusses both formats. This documentation shows the DTD used to define the Properties XML grammar:
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD for properties -->
<!ELEMENT properties ( comment?, entry* ) >
<!ATTLIST properties version CDATA #FIXED "1.0">
<!ELEMENT comment (#PCDATA) >
<!ELEMENT entry (#PCDATA) >
<!ATTLIST entry key CDATA #REQUIRED>
The DTD shows us that properties stored in XML must have
<properties> as the root element required of well-formed XML and can have zero or one <comment> elements nested in this root tag. We also learn from this DTD that zero to many elements name <entry> are allowed and that an entry element may contain a data body and a single attribute named key. Based on this DTD, we could write a compatible XML-based properties file by hand, but an even easier way to see one is to read in a traditional properties file of name/value pairs and store it back out in XML format. This is exactly what the next Java class, PropertiesExamples, does.PropertiesExamples.java
package dustin.properties;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
public class PropertiesExamples
{
/** No-arguments constructor. */
public PropertiesExamples() {}
/**
* Get traditional properties in name=value format.
*
* @param filePathAndName Path and name of properties file (without the
* .properties extension).
* @return Properties read in from provided file.
*/
public Properties loadTraditionalProperties(
final String filePathAndName)
{
final Properties properties = new Properties();
try
{
final FileInputStream in = new FileInputStream(filePathAndName);
properties.load(in);
in.close();
}
catch (FileNotFoundException fnfEx)
{
System.err.println("Could not read properties from file " + filePathAndName);
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered while reading from " + filePathAndName);
}
return properties;
}
/**
* Store provided properties in XML format.
*
* @param sourceProperties Properties to be stored in XML format.
* @param out OutputStream to which to write XML formatted properties.
*/
public void storeXmlProperties(
final Properties sourceProperties,
final OutputStream out)
{
try
{
sourceProperties.storeToXML(out, "This is easy!");
}
catch (IOException ioEx)
{
System.err.println("ERROR trying to store properties in XML!");
}
}
/**
* Store provided properties in XML format to provided file.
*
* @param sourceProperties Properties to be stored in XML format.
* @param pathAndFileName Path and name of file to which XML-formatted
* properties will be written.
*/
public void storeXmlPropertiesToFile(
final Properties sourceProperties,
final String pathAndFileName)
{
try
{
FileOutputStream fos = new FileOutputStream(pathAndFileName);
storeXmlProperties(sourceProperties, fos);
fos.close();
}
catch (FileNotFoundException fnfEx)
{
System.err.println("ERROR writing to " + pathAndFileName);
}
catch (IOException ioEx)
{
System.err.println(
"ERROR trying to write XML properties to file " + pathAndFileName);
}
}
/**
* Runs main examples.
*
* @param arguments Command-line arguments; none anticipated.
*/
public static void main(final String[] arguments)
{
final PropertiesExamples me = new PropertiesExamples();
final Properties inputProperties =
me.loadTraditionalProperties("examples.properties");
me.storeXmlPropertiesToFile(inputProperties, "examples-xml.properties");
}
}
The class shown above reads in the properties file listed earlier and then writes it back out in XML format. The actual lines of code doing most of the work are small in number, but the many checked exceptions associated with file input/output make the code base much larger.
When this code is run, the following output is generated:
examples-xml.properties
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>This is easy!</comment>
<entry key="url.coloradosoftwaresummit">http://www.softwaresummit.com/</entry>
<entry key="url.rmoug">http://www.rmoug.org/</entry>
<entry key="url.blog.dustin">http://marxsoftware.blogspot.com/</entry>
<entry key="url.javaworld">http://www.javaworld.com/</entry>
<entry key="url.otn">http://www.oracle.com/technology/index.html</entry>
</properties>
This generated XML file contains the same name/value pairs as the traditional properties file shown earlier, can be read in like the traditional version using the Properties.loadFromXML, and includes the comment that was passed to the Properties.storeToXML method.
Conclusion
It is fairly straightforward to load properties from XML and to store them as XML. However, the XML is essentially limited to the same paradigm of name/value pairs as traditional properties files. Therefore, we are unable to take advantage of XML's hierarchical nature to use relationships more complex than one key (name) to one value. The primary reason one might use Java's support for XML-based properties is if XML was being used for other tools or frameworks and the properties in XML were more accessible to the other tool or framework.
Monday, January 26, 2009
A Java Exception Chaining Reminder
Java 1.4 introduced so many highly useful features that many projects were slow to migrate to J2SE 5 or Java SE 6. Even with all of these great new features, the simple exception chaining mechanism stands out as one of those small features that benefits me in my Java development (and especially maintenance) work on a near-daily basis. Many of us were implementing custom exception chaining work-arounds before this, but the standardized approach was highly welcome.
Although Java exception chaining is simple to apply, this does not mean it is applied uniformly. In his blog post A Pox Upon All of Your getMessage Calls, Ian Darwin (author of Java Cookbook) states that he has run across several Java-based web frameworks that do not provide sufficient exception information in wrapped exceptions. He also points out the superior information provided by passing a causal exception's toString()-based String to the constructor of the wrapping exception rather than passing the causal exception's getMessage()-based String.
Joshua Bloch devotes an entire chapter of Effective Java (Chapter 9 in the Second Edition) to discussion of effective practices related to Java exceptions. Bloch points out the problems associated with ignoring exceptions (item #65 in Second Edition) and recommends throwing exceptions that are appropriate for the particular level of abstraction. With these two concepts in mind, I want to move on to some Java code examples demonstrating the various approaches to re-throwing a new exception based on a causal exception.
For my admittedly contrived example, I will be using a custom exception called UninstantiableClassException. Its source data is shown next:
UninstantiableClassException.java
The exception defined above has two constructors. One accepts a String argument and one accepts a Throwable. The constructor accepting a
The AbstractClass that cannot be instantiated is shown next:
AbstractClass.java
Finally, the code that executes the three examples is shown next.
ExceptionExamples.java
The main function above executes three methods defined to catch an intentionally thrown checked exception InstantiationException and re-throw it as the unchecked exception
Each of the three methods constructs the re-thrown exception in a different manner. One approach shown is to use actual exception chaining and provide the causal exception (
There may be times when one does not want to provide the causal exception to the new exception, but still wants to retain at least the name of the original exception in the next exception. This is where it is important to distinguish between
The screen snapshot shown next shows the differences in results for the three approaches demonstrated by the code above. The approach using full exception chaining provides by far the most detail with a complete stack trace. The two String-based approaches show a similar level of detail with the one significant difference being that the use of

Conclusion
The information covered in this blog posting contains nothing new because Java exception chaining was introduced with Java 1.4. However, I have seen enough cases where exception chaining is not used to its full capabilities to think that it is worth a reminder post about the merits and simplicity of exception chaining.
My first choice is to use true and full exception chaining by passing the causal exception itself into the constructor of the new, wrapping exception whenever possible. When that is not desired for some reason (such as the wrapping exception is out of one's control and only has a String constructor1), then it is beneficial (without any extra effort) to provide the causal exception's message AND name via its
1The method Throwable.initCause(Throwable) actually can also be used to set the causal Throwable if the wrapping exception does not provide a constructor for this purpose.
Although Java exception chaining is simple to apply, this does not mean it is applied uniformly. In his blog post A Pox Upon All of Your getMessage Calls, Ian Darwin (author of Java Cookbook) states that he has run across several Java-based web frameworks that do not provide sufficient exception information in wrapped exceptions. He also points out the superior information provided by passing a causal exception's toString()-based String to the constructor of the wrapping exception rather than passing the causal exception's getMessage()-based String.
Joshua Bloch devotes an entire chapter of Effective Java (Chapter 9 in the Second Edition) to discussion of effective practices related to Java exceptions. Bloch points out the problems associated with ignoring exceptions (item #65 in Second Edition) and recommends throwing exceptions that are appropriate for the particular level of abstraction. With these two concepts in mind, I want to move on to some Java code examples demonstrating the various approaches to re-throwing a new exception based on a causal exception.
For my admittedly contrived example, I will be using a custom exception called UninstantiableClassException. Its source data is shown next:
UninstantiableClassException.java
package dustin;
/**
* Exception class indicating situation in which a class cannot be instantiated.
*/
public class UninstantiableClassException extends RuntimeException
{
/**
* Constructor for this exception.
*/
public UninstantiableClassException(final String newExceptionMessage)
{
super(newExceptionMessage);
}
/**
* Constructor accepting an initial cause.
*/
public UninstantiableClassException(final Throwable cause)
{
super("Provided class cannot be instantiated.", cause);
}
}
The exception defined above has two constructors. One accepts a String argument and one accepts a Throwable. The constructor accepting a
Throwable will be used to allow this constructor to completely wrap/chain a causal exception. The other constructor, the one accepting a String, will allow this new exception to be based on any provided arbitrary String.The AbstractClass that cannot be instantiated is shown next:
AbstractClass.java
package dustin;
public abstract class AbstractClass
{
/** Some integer. */
private int someInt;
}
Finally, the code that executes the three examples is shown next.
ExceptionExamples.java
package dustin;
import java.lang.InstantiationException;
/**
* Class for demonstrating comparison of use of {@code toString()} versus
* {@code getMessage()} in exception translation.
*/
public class ExceptionExamples
{
/**
* Throw nested exceptions with getMessage().
*
* @throws UninstantiableClassException Thrown if class cannot be instantiated.
*/
public static void throwNestedExceptionsWithGetMessage()
{
try
{
final AbstractClass instance = AbstractClass.class.newInstance();
}
catch (InstantiationException instantiationEx)
{
throw new UninstantiableClassException(instantiationEx.getMessage());
}
catch (IllegalAccessException illegalAccessEx)
{
throw new UninstantiableClassException(illegalAccessEx.getMessage());
}
}
/**
* Throw nested exceptions with toString().
*
* @throws UninstantiableClassException Thrown if class cannot be instantiated..
*/
public static void throwNestedExceptionsWithToString()
{
try
{
final AbstractClass instance = AbstractClass.class.newInstance();
}
catch (InstantiationException instantiationEx)
{
throw new UninstantiableClassException(instantiationEx.toString());
}
catch (IllegalAccessException illegalAccessEx)
{
throw new UninstantiableClassException(illegalAccessEx.toString());
}
}
/**
* Throw nested exceptions with exception chaining mechanism.
*
* @throws Illeg
*/
public static void throwNestedExceptionsWithChainedException()
{
try
{
final AbstractClass instance = AbstractClass.class.newInstance();
}
catch (InstantiationException instantiationEx)
{
throw new UninstantiableClassException(instantiationEx);
}
catch (IllegalAccessException illegalAccessEx)
{
throw new UninstantiableClassException(illegalAccessEx);
}
}
/**
* Main function for executing exception examples.
*
* @param arguments Command-line arguments; lack of any argument indicates
* use of the {@code getMessage()} approach, an argument "tostring"
* indicates use of the {@code toString()} approach, and any argument
* other than "tostring" indicates use of exception chaining mechanism.
*/
public static void main(final String[] arguments)
{
if (arguments.length < 1)
{
throwNestedExceptionsWithGetMessage();
}
else if ("TOSTRING".equalsIgnoreCase(arguments[0]))
{
throwNestedExceptionsWithToString();
}
else
{
throwNestedExceptionsWithChainedException();
}
}
}
The main function above executes three methods defined to catch an intentionally thrown checked exception InstantiationException and re-throw it as the unchecked exception
UninstantiableClassException defined above.Each of the three methods constructs the re-thrown exception in a different manner. One approach shown is to use actual exception chaining and provide the causal exception (
InstantiationException) as an argument to the constructor of the new exception. The other two approaches both rely on the String-argument constructor of the new exception. Because these approaches don't have access to the original exception, significantly less detail is available in the new exception than that which is available when the original causal exception is provided.There may be times when one does not want to provide the causal exception to the new exception, but still wants to retain at least the name of the original exception in the next exception. This is where it is important to distinguish between
getMessage() and toString. The API documentation for Throwable.toString() clearly indicates that toString prepends the name of the particular exception class to the message associated with the exception. Therefore, as discussed in Darwin's blog post mentioned previously, it seems that a developer should always use toString() rather than getMessage() when providing a String for construction of another (wrapper) exception. Even better yet, it is probably a good idea to generally prefer providing the causal exception itself to the new exception unless there is a reason not to do so.The screen snapshot shown next shows the differences in results for the three approaches demonstrated by the code above. The approach using full exception chaining provides by far the most detail with a complete stack trace. The two String-based approaches show a similar level of detail with the one significant difference being that the use of
toString() on the causal exception rather than getMessage() led to the inclusion of the causal exception in the message of the wrapping exception.
Conclusion
The information covered in this blog posting contains nothing new because Java exception chaining was introduced with Java 1.4. However, I have seen enough cases where exception chaining is not used to its full capabilities to think that it is worth a reminder post about the merits and simplicity of exception chaining.
My first choice is to use true and full exception chaining by passing the causal exception itself into the constructor of the new, wrapping exception whenever possible. When that is not desired for some reason (such as the wrapping exception is out of one's control and only has a String constructor1), then it is beneficial (without any extra effort) to provide the causal exception's message AND name via its
toString() method rather than providing only its message (via the getMessage() method).1The method Throwable.initCause(Throwable) actually can also be used to set the causal Throwable if the wrapping exception does not provide a constructor for this purpose.
Sunday, January 25, 2009
Software Development Communities: Size Matters
When it comes to comparing software development communities, it turns out that size does matter. When comparing two programming languages, most of us focus on things like syntactical differences, supported paradigms (such as object-oriented programming), deployment issues, performance, and many other factors. However, for large enterprise development, and especially for large enterprise development with many unknowns or risk factors, the size of the development community for that particular language can be an important consideration. The same argument can be made for frameworks, libraries, and other development tools.
While newer languages and frameworks with smaller development communities certainly have their advantages, there is no denying that some of their appeal can sometimes be traced to motivating factors such as the Magpie Effect, resume-driven development, Emperor's New Clothes Effect, Lemmings Effect, and other such motivators. Perhaps the most significant downside of working with these newer languages and technologies with smaller development communities is the corresponding lower amount of documentation and assistance that is available. For smaller projects, this is often not all that problematic. For large, long-term projects, however, I sometimes think we developers could be a little more understanding when "they" (management or clients) are somewhat concerned about using the fancy new language, framework, or toolkit. Regardless of whether one likes C, C++, Java, C#, or one of the other such languages, I think we can all understand how management/clients might be more comfortable going with one of these languages on a long-term, large project.
The flavor of the day may be great for a certain type of task for which it was designed. This in no way means that it is best for everything and I understand a manager's or client's hesitation to be one of the first to select a language that has been largely unproven in long-term, enterprise projects. The manager or client knows that any problem encountered with development using something like Java will be able to be dealt with in some way because so many large enterprise projects have used Java already. Furthermore, Java has been around long enough and been used by enough developers that many best practices have been developed. The forums are filled with experience from developers all over the world who have used Java. Even in areas where Java may not be as nice as another language we might compare it to, there are usually well-documented work-arounds for the deficiency.
The significance of size is not limited to programming languages. Most developers and managers are likely to be much more comfortable using the Spring Framework than using a framework that I might start tomorrow. Why? Because whether one likes Spring or not, one understands that Spring is very pervasive in the industry, has been vetted and used by a horde of developers, has significant SpringSource-generated documentation and third-party documentation, and has many forums filled with advice and answers to tough questions.
I'll use Flex as an example of this from my own career. When I began using Flex two years ago, it was new and exciting. The only drawback was that I could not always find the answers I was looking for immediately. I had gotten used to Google searches giving me answers for anything I asked in the Java world and had to get reacquainted with the Google message "Your search ---- did not match any documents" for some of my Flex queries. That was actually part of the excitement as I was able to blog on several tricks and nuances as I discovered them and provide original value for others to use.
Two years later, I still enjoy using Flex, but some of the newness is gone. The Flex development community feels many times larger than it was two years ago. While I was able to easily keep up with at least reading the headlines of any new Flex blog posting or article two years ago, there is no way I can do that now. I rarely see the "Your search ----- did not match any documents message" when Googling on Flex. The positive of all this, of course, is that it is much easier today to find one or more blog postings or articles covering most of the problems one might encounter with Flex. The much larger Flex development community makes it much easier to find answers to Flex questions and problems.
More developers in a given community implies more people to answer questions; more people to write articles, books, and blogs; more people using and testing the software of the given community; more people learning from experience and developing best practices; more people requesting bug fixes and improvements; and more people sharing new ideas. These advantages are especially desirable for large, long-term projects because it is those projects that can least afford to make a bad decision from which retreat is difficult. In these cases, it is understandable that managers and clients feel more comfortable going with a programming language, framework, or toolkit that they know many others have found success with. It doesn't hurt to know that there are developers available with those skills as well.
The size of the community of a particular product is less important when that product is used in such a way that retreat is easy if needed. For example, a toolkit that is only used for a small part of a large project might be easy to justify regardless of its small community because it is easily removed or replaced.
The size of the community is especially important in the open source world. I'm not referring simply to the active developers on the open source project. Rather, I am including all who use the open source product, who write about the open source project, who file bug reports against the open source product, etc. In my opinion, size of the community is especially important to open source because this community size directly relates to many of the advantages associated with open source.
Open source is often touted as advantageous because of advertised benefits such as potentially being tested by more people in more diverse and realistic environments, potentially quicker problem resolution, being subject to potentially greater code review, etc. However, these advertised benefits can only be realized if the community around that open source project is large enough to provide the greater use and testing, greater problem resolution, and greater code review. If I run a one-man open source project and I am the only user of that project, it is doubtful that the product enjoys any of these advantages compared to any serious commercial/proprietary/closed source competitor.
Even programming languages, frameworks, and toolkits with large communities started out with small communities. This reminds us that there is significant merit and potential associated with many products that currently have small communities. I am not saying in this post that we should only use products with large communities. What I am stating is that we should be more understanding when managers or clients prefer products with larger communities and that we should always keep in mind size of the community as one facet of our decision-making process regarding selection of a programming language, toolkit, framework, or other piece of our software development. There are many factors to be considered when selecting a particular product, but size of the community often deserves to be one of them.
My final thoughts are of appreciation for others who do take risks with products in smaller communities to help those communities grow and of my own satisfaction from occasionally playing with these products with smaller communities. While I often understand and even agree with managers or clients that we need to use the safer product with a larger community in our large enterprise projects, I also must admit that there is a thrill from playing with the latest gadget (language, framework, or toolkit). As with so many other parts of the software developer's life, I try to balance these and use them appropriately.
While newer languages and frameworks with smaller development communities certainly have their advantages, there is no denying that some of their appeal can sometimes be traced to motivating factors such as the Magpie Effect, resume-driven development, Emperor's New Clothes Effect, Lemmings Effect, and other such motivators. Perhaps the most significant downside of working with these newer languages and technologies with smaller development communities is the corresponding lower amount of documentation and assistance that is available. For smaller projects, this is often not all that problematic. For large, long-term projects, however, I sometimes think we developers could be a little more understanding when "they" (management or clients) are somewhat concerned about using the fancy new language, framework, or toolkit. Regardless of whether one likes C, C++, Java, C#, or one of the other such languages, I think we can all understand how management/clients might be more comfortable going with one of these languages on a long-term, large project.
The flavor of the day may be great for a certain type of task for which it was designed. This in no way means that it is best for everything and I understand a manager's or client's hesitation to be one of the first to select a language that has been largely unproven in long-term, enterprise projects. The manager or client knows that any problem encountered with development using something like Java will be able to be dealt with in some way because so many large enterprise projects have used Java already. Furthermore, Java has been around long enough and been used by enough developers that many best practices have been developed. The forums are filled with experience from developers all over the world who have used Java. Even in areas where Java may not be as nice as another language we might compare it to, there are usually well-documented work-arounds for the deficiency.
The significance of size is not limited to programming languages. Most developers and managers are likely to be much more comfortable using the Spring Framework than using a framework that I might start tomorrow. Why? Because whether one likes Spring or not, one understands that Spring is very pervasive in the industry, has been vetted and used by a horde of developers, has significant SpringSource-generated documentation and third-party documentation, and has many forums filled with advice and answers to tough questions.
I'll use Flex as an example of this from my own career. When I began using Flex two years ago, it was new and exciting. The only drawback was that I could not always find the answers I was looking for immediately. I had gotten used to Google searches giving me answers for anything I asked in the Java world and had to get reacquainted with the Google message "Your search ---- did not match any documents" for some of my Flex queries. That was actually part of the excitement as I was able to blog on several tricks and nuances as I discovered them and provide original value for others to use.
Two years later, I still enjoy using Flex, but some of the newness is gone. The Flex development community feels many times larger than it was two years ago. While I was able to easily keep up with at least reading the headlines of any new Flex blog posting or article two years ago, there is no way I can do that now. I rarely see the "Your search ----- did not match any documents message" when Googling on Flex. The positive of all this, of course, is that it is much easier today to find one or more blog postings or articles covering most of the problems one might encounter with Flex. The much larger Flex development community makes it much easier to find answers to Flex questions and problems.
More developers in a given community implies more people to answer questions; more people to write articles, books, and blogs; more people using and testing the software of the given community; more people learning from experience and developing best practices; more people requesting bug fixes and improvements; and more people sharing new ideas. These advantages are especially desirable for large, long-term projects because it is those projects that can least afford to make a bad decision from which retreat is difficult. In these cases, it is understandable that managers and clients feel more comfortable going with a programming language, framework, or toolkit that they know many others have found success with. It doesn't hurt to know that there are developers available with those skills as well.
The size of the community of a particular product is less important when that product is used in such a way that retreat is easy if needed. For example, a toolkit that is only used for a small part of a large project might be easy to justify regardless of its small community because it is easily removed or replaced.
The size of the community is especially important in the open source world. I'm not referring simply to the active developers on the open source project. Rather, I am including all who use the open source product, who write about the open source project, who file bug reports against the open source product, etc. In my opinion, size of the community is especially important to open source because this community size directly relates to many of the advantages associated with open source.
Open source is often touted as advantageous because of advertised benefits such as potentially being tested by more people in more diverse and realistic environments, potentially quicker problem resolution, being subject to potentially greater code review, etc. However, these advertised benefits can only be realized if the community around that open source project is large enough to provide the greater use and testing, greater problem resolution, and greater code review. If I run a one-man open source project and I am the only user of that project, it is doubtful that the product enjoys any of these advantages compared to any serious commercial/proprietary/closed source competitor.
Even programming languages, frameworks, and toolkits with large communities started out with small communities. This reminds us that there is significant merit and potential associated with many products that currently have small communities. I am not saying in this post that we should only use products with large communities. What I am stating is that we should be more understanding when managers or clients prefer products with larger communities and that we should always keep in mind size of the community as one facet of our decision-making process regarding selection of a programming language, toolkit, framework, or other piece of our software development. There are many factors to be considered when selecting a particular product, but size of the community often deserves to be one of them.
My final thoughts are of appreciation for others who do take risks with products in smaller communities to help those communities grow and of my own satisfaction from occasionally playing with these products with smaller communities. While I often understand and even agree with managers or clients that we need to use the safer product with a larger community in our large enterprise projects, I also must admit that there is a thrill from playing with the latest gadget (language, framework, or toolkit). As with so many other parts of the software developer's life, I try to balance these and use them appropriately.
Saturday, January 24, 2009
The Java Community: More Useful Java Resources
My last blog post of 2008 covered Useful Online Java Developer Resources. That posting focused on resources for both learning new things and for finding solutions to specific problems. I found it interesting to compare Ted Neward's recently published list of Essential Java resources to the list in my posting. In this blog posting, I'll emphasize the resources that we mention in common as well as some resources that did not make his list that I think would have fit well (in some cases, they should have made my list as well in retrospect).
Neward lists many of the same web sites with good Java information that I listed in my posting: The Source for Java Developers, DZone, TheServerSide, Java.net, and developerWorks. One site that I have found very useful in learning about Java and wish I had mentioned in my original post after seeing it listed in Neward's article is ONJava.com. However, I think Neward missed a couple important Java sites that I did include: Javaworld and Oracle Technology Network's (OTN's) Java Developer Center.
As its name implies, JavaWorld is (and long has been) an important contributor in the world of Java. Meanwhile, Oracle has become one of the dominant influences and vendors in the Java community as well and has published many Java-related articles and how-tos. I have used articles and references from both sites many times in my career. I also admittedly have a bias toward these sites because I have written articles for both.
I did mention general blogs in my posting on useful general Java resources, but Neward lists three specific Java blogs in his posting that I find to be highly useful as well. Besides the well-known Java blogs such as Java Posse, JavaSpecialists, the Sun employees blogs (including Java Technology Blogs), and the Java.net community weblogs, there are numerous other useful Java-related blogs. Because the Java-related blogging community is so large, the search engines (including blog-specific search engines) can be invaluable. This is also where aggregated blog sites like JW Blogs and community sites such as JavaLobby can be helpful.
I agree wholeheartedly with most of the books on Neward's list of books (I only covered online resources in my previous post), but I do have a few Java-related books I'd add to the list: J2EE Design and Development, Java Cookbook, and Thinking in Java.
It is easy to dismiss the value of the somewhat dated J2EE Design and Development (note the J2EE rather than Java EE in the title), the book is so full of hard-earned experience and knowledge about enterprise application architecture, design, and implementation with a Java focus that it is still one of the best books on the subject. The success of the Spring Framework and the evolution of EJB since the publication of this book are two pieces of evidence for the impact this book has had.
The Java Cookbook is a resource for locating examples of how to do various tasks in Java. While it is particularly useful to someone new to Java, I still find it useful when doing something I have not done before or have not done in a long time. It is sometimes easier to find an example of even things I have done before in this book than it is to find it in my own code samples. Powerful search engines have made it easier to find similar examples online, but there are still times when it is easiest for me to locate the example I need in the printed book.
Older versions of Thinking in Java are currently available online at no charge. The Fourth Edition is the current edition that is available for sale. This book is used by many developers as an introduction to Java while being used by many experienced Java developers as a trusted reference and, along with Effective Java, as a sort of arbiter for Java-related debates.
Neward lists some good Java-related conferences in his article, but I think he may have missed one of the best by not listing the Colorado Software Summit. While I obviously have a reason for bias related to this conference, I not the only one to espouse its virtues. I'd also add Java User Group meetings as well. These regional groups can offer invaluable benefits at little or no cost. Many of these feature top-notch and well-known speakers who live in the local area or even sometimes present while visiting the area.
Conclusion
It is reassuring when the same sites make multiple Java developers' lists of useful sources of information. It is also interesting to see how different developers also have different preferred sources of information. I think that one of Java's biggest advantages is its sizable community and the sizable body of work out there related to Java development. The Java-related resources discussed in my previous blog posting, in Neward's article, and in this blog posting are evidence of the quantity and quality of resources in the Java development community.
Neward lists many of the same web sites with good Java information that I listed in my posting: The Source for Java Developers, DZone, TheServerSide, Java.net, and developerWorks. One site that I have found very useful in learning about Java and wish I had mentioned in my original post after seeing it listed in Neward's article is ONJava.com. However, I think Neward missed a couple important Java sites that I did include: Javaworld and Oracle Technology Network's (OTN's) Java Developer Center.
As its name implies, JavaWorld is (and long has been) an important contributor in the world of Java. Meanwhile, Oracle has become one of the dominant influences and vendors in the Java community as well and has published many Java-related articles and how-tos. I have used articles and references from both sites many times in my career. I also admittedly have a bias toward these sites because I have written articles for both.
I did mention general blogs in my posting on useful general Java resources, but Neward lists three specific Java blogs in his posting that I find to be highly useful as well. Besides the well-known Java blogs such as Java Posse, JavaSpecialists, the Sun employees blogs (including Java Technology Blogs), and the Java.net community weblogs, there are numerous other useful Java-related blogs. Because the Java-related blogging community is so large, the search engines (including blog-specific search engines) can be invaluable. This is also where aggregated blog sites like JW Blogs and community sites such as JavaLobby can be helpful.
I agree wholeheartedly with most of the books on Neward's list of books (I only covered online resources in my previous post), but I do have a few Java-related books I'd add to the list: J2EE Design and Development, Java Cookbook, and Thinking in Java.
It is easy to dismiss the value of the somewhat dated J2EE Design and Development (note the J2EE rather than Java EE in the title), the book is so full of hard-earned experience and knowledge about enterprise application architecture, design, and implementation with a Java focus that it is still one of the best books on the subject. The success of the Spring Framework and the evolution of EJB since the publication of this book are two pieces of evidence for the impact this book has had.
The Java Cookbook is a resource for locating examples of how to do various tasks in Java. While it is particularly useful to someone new to Java, I still find it useful when doing something I have not done before or have not done in a long time. It is sometimes easier to find an example of even things I have done before in this book than it is to find it in my own code samples. Powerful search engines have made it easier to find similar examples online, but there are still times when it is easiest for me to locate the example I need in the printed book.
Older versions of Thinking in Java are currently available online at no charge. The Fourth Edition is the current edition that is available for sale. This book is used by many developers as an introduction to Java while being used by many experienced Java developers as a trusted reference and, along with Effective Java, as a sort of arbiter for Java-related debates.
Neward lists some good Java-related conferences in his article, but I think he may have missed one of the best by not listing the Colorado Software Summit. While I obviously have a reason for bias related to this conference, I not the only one to espouse its virtues. I'd also add Java User Group meetings as well. These regional groups can offer invaluable benefits at little or no cost. Many of these feature top-notch and well-known speakers who live in the local area or even sometimes present while visiting the area.
Conclusion
It is reassuring when the same sites make multiple Java developers' lists of useful sources of information. It is also interesting to see how different developers also have different preferred sources of information. I think that one of Java's biggest advantages is its sizable community and the sizable body of work out there related to Java development. The Java-related resources discussed in my previous blog posting, in Neward's article, and in this blog posting are evidence of the quantity and quality of resources in the Java development community.
Subscribe to:
Posts (Atom)