Math.abs has an edge case...
Consider the following Java code:
- int ai = Math.abs( i );
- assert ai >= 0;
Will the assert ever fire?
Yes, it turns out that it can fire if i = Integer.MIN_VALUE, and you have been warned about this in the Math.abs javadoc, which states “Note that if the argument is equal to the value of Integer.MIN_VALUE, the most negative representable int value, the result is that same value, which is negative.’
Keep this in mind when looking at such innocent looking code...
Java Mail: Make sure the mail.smtp.auth property's value is a String...
If you are using the JavaMail API to send out e-mail via an e-mail server that requires authentication and are getting an error like com.sun.mail.smtp.SMTPSendFailedException: 550 You must authenticate, make sure the mail.smtp.auth property is assigned as a String, like this:
- properties.put( "mail.smtp.auth", "true" );
Not exactly the most obvious bug to find...
Using Ant with Cygwin
If you have ever tried building something with ant under Cygwin, you probably have noticed an error such as the following:
build.xml:35: c:\cygdrive\c\Progra~1\java/SunAppServer\jdk\lib\J2EE.jar
The current Ant documentation suggests that you should change your build.xml to wrap all such environment labels and class paths with code that executes cygpath, but this turns out to create some ugly code, especially if your build scripts use a lot of environment labels.
An alternative scheme that I have just come up with is to use a layer of indication. You can create an ant shell script that translates all of your necessary Cygwin environment labels using cygpath, and then call the ant.bat to do the actual build. The advantage of this approach is that more build.xml files are cross-platform out of the box and require very few, if any, tweaks.
The following file is an example of my approach:
#!/bin/sh
# OS-Specific Stuff
export PATH=`/usr/bin/cygpath --path --windows $PATH`
export TEMP=`/usr/bin/cygpath --path --windows $TEMP`
export TMP=`/usr/bin/cygpath --path --windows $TMP`
# Java Specific Stuff
export JAVA_HOME=`/usr/bin/cygpath --windows $JAVA_HOME`
export J2EE_HOME=`/usr/bin/cygpath --windows $J2EE_HOME`
export TOMCAT_HOME=`/usr/bin/cygpath --path --windows $TOMCAT_HOME`
export CLASSPATH=`/usr/bin/cygpath --path --windows $CLASSPATH`
# Ant Specific Stuff
ORIGINAL_ANT_HOME=$ANT_HOME
export ANT_HOME=`/usr/bin/cygpath --path --windows $ANT_HOME`
# Project-Specific Stuff
export WHATEVER_YOU_NEED=`/usr/bin/cygpath --windows $WHATEVER_YOU_NEED`
# Invoke the ANT.BAT via the original ANT_HOME value
$ORIGINAL_ANT_HOME/bin/ant.bat $1 $2 $3 $4 $5 $6 $7 $8 $9
Of course, this technique can be generally applied to other applications that use environment labels. Hope that helps.
C++ Strongly-Typed Enumerations...
With the release on Java 5, we finally have enumerations in Java, but they really outdone themselves, in that an enumerated type is actually a class, where you can add your own methods. Take, for example, this code:
- public enum Color {
- Red,
- Yellow,
- Orange,
- Green,
- Blue,
- Brown,
- Black,
- Purple;
- public boolean isPrimaryColor() {
- switch ( this ) {
- case Red:
- case Blue:
- case Yellow:
- return true;
- default:
- return false;
- }
- }
- }
Instead of having a utility class off to the side that takes an enumeration as a parameter, you can now add that method directly to the enumeration, and I do not think that there would be much controversy that color.isPrimaryColor(); reads better than ColorUtils.isPrimaryColor( color );.
Putting this aside for a moment, C++09 is cleaning up their enumerations by adding support for strongly type enums (PDF Proposal).
In the current version of C++, we have problems with code like this:
- enum Color { RED, YELLOW, ORANGE, GREEN, BLUE, BROWN, BLACK, PURPLE };
- enum TerrorAlertLevel { RED, ORANGE, YELLOW, BLUE, GREEN };
This does not compile, because by the time it gets to TerrorAlertLevel, RED, ORANGE, YELLOW, BLUE, and GREEN are already defined, thanks to the Color enumeration. And worst off, if you have a coding standard that does not require you to have your enumerations all in caps (which makes sense, since part of the reason that Macros are usually done in caps is to discourage their use), then you could have an issue with any variables, such as int red = 0xFF0000; would not compile if the enumeration was all in lower-case.
As such, developers in C++ tend to add a prefix to the definition, such as:
- enum Color { COLOR_RED, COLOR_YELLOW, COLOR_ORANGE, COLOR_GREEN, COLOR_BLUE, COLOR_BROWN, COLOR_BLACK, COLOR_PURPLE };
- enum TerrorAlertLevel { TAL_RED, TAL_ORANGE, TAL_YELLOW, TAL_BLUE, TAL_GREEN };
Oh, that looks friendly.
Well, to help in that friendliness, C++09 is adding strongly typed enums. This is done by adding the class keyword to the enum definition. For example, the first C++ example above could compile under C++09 as this:
- enum class Color { RED, YELLOW, ORANGE, GREEN, BLUE, BROWN, BLACK, PURPLE };
- enum class TerrorAlertLevel { RED, ORANGE, YELLOW, BLUE, GREEN };
The bold emphasizes the difference.
Conceptually, I think this is a great addition to C++, making it easier to reuse enumerations and avoiding the crazy compilation errors that you can get if you accidentally name your variable a name that has already been defined in an enum.
The part that I do not like about this proposal, however, is the fact that it uses the keyword class but it is not a class. You cannot add member functions to it, which because of the power of the Java enumerations, I think this is confusing for developers which experience with Java enumerations.
C# enumerations are strongly typed but you cannot add any methods to them. But at least, they do not use the word class in their definition.
While I more than welcome the strongly-typed nature of enumerations in C++, I think that the implementation could have overloaded a better keyword than class. Since it does seem like this has been accepted already, hopefully a future version of the standard will add the ability to add member functions to the enumeration, minimizing some of this confusion.
Searching for nil in all the wrong places...
A few days ago, I was adding some migrations to a Ruby on Rails project, but when I went to initialize a newly created database, I saw this error:
- rake aborted!
- You have a nil object when you didn't expect it!
- You might have expected an instance of Array.
- The error occured while evaluating nil.first
This being the third or so time working in Ruby, I immediately thought that it was something I did, so I started up the automatically generated scripts/application, pasted my new code into there, and proceeded to use it. The code had a couple issues that I fixed and I was confident enough to try again.
But again, I got the same error message. I then decided to add a couple prints around the new code, and re-run it, but the error above occurred without my print outs. Not sure how to proceed, I asked for some assistance from some co-workers. The advice was great, and I learned a lot about Ruby and Rails, but it still did not resolve my problem.
Finally someone recommended adding --trace to the my command:
- eyt@zaterdag 5031% rake --trace migrate
- ** Invoke migrate (first_time)
- ** Invoke environment (first_time)
- ** Execute environment
- ** Execute migrate
- rake aborted!
- You have a nil object when you didn't expect it!
- You might have expected an instance of Array.
- The error occured while evaluating nil.first
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:253:in `migration_files'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:252:in `sort_by'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:252:in `each'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:252:in `sort_by'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:252:in `migration_files'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:235:in `migration_classes'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:223:in `migrate'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:190:in `up'
- .../lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/active_record/migration.rb:181:in `migrate'
- .../lib/ruby/gems/1.8/gems/rails-1.0.0/lib/tasks/databases.rake:3
- .../lib/ruby/gems/1.8/gems/rails-1.0.0/lib/tasks/databases.rake:2:in `call'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:202:in `execute'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:202:in `each'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:202:in `execute'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:180:in `invoke'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:1454:in `run'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:1454:in `each'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/lib/rake.rb:1454:in `run'
- .../lib/ruby/gems/1.8/gems/rake-0.6.2/bin/rake:7
- .../bin/rake:18:in `load'
- .../bin/rake:18
So that is very interesting. Note how there is nothing in the above points to anything of mine. In pondering this with one of my coworkers, I explained how one of the things that I tried during debugging it was renaming the name of the class and filename, and that struck a cord.
It turns out that the mess above was all caused by my filename. I had named my file revision_add_blah_to_myApp and named my class AddBlahToMyApp. The problem with this is that the Rails convention for class names requires me to either name my file revision_add_blah_to_my_app or name my class AddBlahToMyapp. This very subtle issue caused this most unusable error message to cause a few completely wasted hours.
I have blogged before about great and not so great exceptions and their handling. I think this also falls in that category of not-so-great. It would have been nice to know that this problem originated with an attempt to load a class that did not exist, for example. In that case, I would have looked at that issue more closer than looking for references to first (and indirect references thereof) where the object invoking that method could have been nil.