Marc Schwieterman

That depends...

Developing Spring Roo Addons

As of Roo 1.1.0, addons are created with the addon command. This is slightly different than previous versions, where addons were created with a template argument to the project command. If you find documentation referencing this old style, it’s probably out of date.

The first thing you need to do, is checkout the Roo source code. The Git repository is located at git://git.springsource.org/roo/roo.git. There are very good instructions explaining how to build Roo in the readme.txt file at the root of the source tree. Once Roo is built, you’ll need to create a new directory and fire up the Roo shell.

1
2
3
mkdir roo-addon-example
cd roo-addon-example
roo-dev
    ____  ____  ____  
   / __ \/ __ \/ __ \ 
  / /_/ / / / / / / / 
 / _, _/ /_/ / /_/ /  
/_/ |_|\____/\____/    1.1.1.RELEASE [rev 3057660]

roo>

With the shell running, you can create either a simple, advanced or i18n addon.

roo> addon create 

addon create advanced    addon create i18n        addon create simple
1
addon create simple --topLevelPackage com.example.roo.addon.example
Created /Users/marc/src/roo-addon-example/pom.xml
Created /Users/marc/src/roo-addon-example/readme.txt
Created /Users/marc/src/roo-addon-example/legal
Created /Users/marc/src/roo-addon-example/legal/LICENSE.TXT
Created SRC_MAIN_JAVA
Created SRC_MAIN_RESOURCES
Created SRC_TEST_JAVA
Created SRC_TEST_RESOURCES
Created SRC_MAIN_WEBAPP
Created SRC_MAIN_RESOURCES/META-INF/spring
Created SRC_MAIN_JAVA/com/example/roo/addon/example
Created SRC_MAIN_JAVA/com/example/roo/addon/example/ExampleCommands.java
Created SRC_MAIN_JAVA/com/example/roo/addon/example/ExampleOperations.java
Created SRC_MAIN_JAVA/com/example/roo/addon/example/ExampleOperationsImpl.java
Created SRC_MAIN_JAVA/com/example/roo/addon/example/ExamplePropertyName.java
Created ROOT/src/main/assembly
Created ROOT/src/main/assembly/assembly.xml
Created SRC_MAIN_RESOURCES/com/example/roo/addon/example
Created SRC_MAIN_RESOURCES/com/example/roo/addon/example/info.tagx
Created SRC_MAIN_RESOURCES/com/example/roo/addon/example/show.tagx

After the addon is created, exit the roo shell, then build and install the addon. You can package the addon from within Roo with perform package, but you’ll see why we installed it shortly.

1
2
quit
mvn clean install

Having installed the addon, you’re almost ready to use it. There are a few ways to load your addon into the Roo runtime. The one I like the best is to set up your local maven repository as an OSGI Bundle Repository (OBR). The project created with the addon create command is configured to use the Maven Bundle Plugin, which will create a repository.xml file in your local Maven repository. To tell Roo to use your local Maven repository as an OBR, simply add a line to the config.properties file as shown below, where ROO_HOME is the Roo source directory.

$ROO_HOME/bootstrap/src/main/conf/config.properties
1
obr.repository.url=file:/home/whoever/.m2/repository/repository.xml

If you’ve recently built Roo from source, you may want to null out the repository.xml file before building your addon, as it will be full of the core Roo addons. Keep in mind that you can use git stash to clean your working directory if you want to pull down the latest development changes later on. Now that Roo knows where to look for your addon, you can start the shell back up.

1
roo-dev

If you set everything up right, your local Maven repository should now show up as an OBR, and the addon you just installed should be available within it.

roo> osgi obr url list
file:/home/whoever/.m2/repository/repository.xml

roo> osgi obr list
com-example-roo-addon-example [com.example.roo.addon.example] (0.1.0.BUILD-SNAPSHOT)

The main advantage of this approach is that you don’t have to type the path to addons when starting them. Roo will tab-complete the addon bundle name, so you should only have to type enough to make it unique.

roo> osgi obr start --bundleSymbolicName com.example.roo.addon.example
[Thread-2] [com.example.roo.addon.example [73]] BundleEvent INSTALLED
[Thread-2] [com.example.roo.addon.example [73]] BundleEvent RESOLVED
[Thread-2] [com.example.roo.addon.example [73]] BundleEvent STARTED
roo> Target resource(s):
-------------------
   com-example-roo-addon-example (0.1.0.BUILD-SNAPSHOT)

Deploying...done.

[Thread-2] [com.example.roo.addon.example [73]] ServiceEvent REGISTERED

Your addon should now show up in the output of osgi ps.

roo> osgi ps
...
[  75] [Active     ] [    1] com-example-roo-addon-example (0.1.0.BUILD-SNAPSHOT)

roo> say hello --name marc
Welcome marc!
Country of origin: None of your business!
It seems you are a running JDK 1.6.0_22
You can use the default JDK logger anywhere in your add-on to send messages to the Roo shell

You can make changes to your addon and reload it without restarting the Roo shell. To change the command output for the example addon, you can modify the ExampleCommands.java file, change the sayHello method, then reinstall the addon.

1
mvn clean install
roo> osgi uninstall --bundleSymbolicName com.example.roo.addon.example
[Thread-2] [com.example.roo.addon.example [73]] ServiceEvent UNREGISTERING
[Thread-2] [com.example.roo.addon.example [73]] BundleEvent STOPPED
[Thread-2] [com.example.roo.addon.example [73]] BundleEvent UNRESOLVED
[Thread-2] [com.example.roo.addon.example [73]] BundleEvent UNINSTALLED
[Thread-2] [org.apache.felix.framework [0]] FrameworkEvent PACKAGES REFRESHED

roo> osgi obr start --bundleSymbolicName com.example.roo.addon.example
[Thread-2] [com.example.roo.addon.example [74]] BundleEvent INSTALLED
[Thread-2] [com.example.roo.addon.example [74]] BundleEvent RESOLVED
Target resource(s):
-------------------
   com-example-roo-addon-example (0.1.0.BUILD-SNAPSHOT)

Deploying...done.

[Thread-2] [com.example.roo.addon.example [74]] ServiceEvent REGISTERED
[Thread-2] [com.example.roo.addon.example [74]] BundleEvent STARTED
[Thread-2] [com.example.roo.addon.example [74]] ServiceEvent REGISTERED

roo> say hello --name marc                                            
This is my new message

One downside to the OBR approach, is that you can’t just use the osgi update command to reload your addon. I think this is a bug, as it works fine if you load the addon from an explicit file system path via osgi start --url. Until that gets fixed, you need to uninstall and start the addon to reload it as was shown above.

If you need to debug Roo or your own addon, you can uncomment the DEBUG line towards the end of the roo-dev script. Roo will suspend on startup until you connect to it as a remote application with your IDE of choice.

1
DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y"

That’s all you need to know to get started. The Spring Roo Forum is a good place to go if you need additional information. This post in particular has some useful direction. You can also build the project documentation locally with mvn site, but be prepared to wait a while. The documentation will be at $ROO_HOME/target/site/reference/html/index.html. It’s still in progress, but the content that’s there is good.

Static Content Alongside Jersey Services

If you’ve worked with Jersey, you’re likely familiar with the embedded Grizzly server. Though I haven’t seen it mentioned often, Grizzly can indeed serve static content too. The following snippet is all that’s necessary to fire up an embedded web server. These examples were written with Grizzly 1.9.18-m and Jersey 1.4.

1
2
3
4
GrizzlyWebServer server = new GrizzlyWebServer(8080, "/var/www");

server.start();
...

While I’ve found some evidence that you should be able to just add a Jersey ServletAdapter to the Grizzly server, that doesn’t appear to work with current versions. Fortunately there are still a couple options that do work. If you want to serve both your static pages and Jersey services from the same context path, you can do something like the following. The key differences from a regular Jersey adapter are to specify the location to the static content in the ServletAdapter constructor, and you need to setHandleStaticResources to true.

1
2
3
4
5
6
7
8
9
10
11
12
13
GrizzlyWebServer server = new GrizzlyWebServer(8080);

ServletAdapter jerseyAdapter = new ServletAdapter("/var/www");
jerseyAdapter.addInitParameter("com.sun.jersey.config.property.packages",
                             "com.yourdomain");
jerseyAdapter.setContextPath("/");
jerseyAdapter.setServletInstance(new ServletContainer());
jerseyAdapter.setHandleStaticResources(true);

server.addGrizzlyAdapter(jerseyAdapter, new String[]{"/"});

server.start();
...

If you want to have different context paths for your static content and services, you can create two adapters like below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GrizzlyWebServer server = new GrizzlyWebServer(8080);

ServletAdapter staticContentAdapter = new ServletAdapter("/var/www");
staticContentAdapter.setContextPath("/");
staticContentAdapter.setHandleStaticResources(true);

ServletAdapter jerseyAdapter = new ServletAdapter();
jerseyAdapter.addInitParameter("com.sun.jersey.config.property.packages",
                             "com.yourdomain");
jerseyAdapter.setContextPath("/ws");
jerseyAdapter.setServletInstance(new ServletContainer());

server.addGrizzlyAdapter(staticContentAdapter, new String[]{"/"});
server.addGrizzlyAdapter(jerseyAdapter, new String[]{"/ws"});

server.start();
...

One limitation to note is that this approach expects the files to be on the file system, which may not be what you’re looking for if you’re trying to create an executable jar type utility.

GData Objective-C Client

I’ve recently been working on an iPhone application that integrates with Blogger, and as such I have gotten some experience with the GData Objective-C Client Library. The issues below were all encountered while working with the GDataServiceGoogleBlogger service, but they should apply to the other GData services too.

Authorization Scope

The GData APIs support OAuth for authorization. One of the first things you need to do when initializing a GData service is to decide which authorization scope you’re going to use. The various service implementations have a [Service authorizationScope] method that you can use, but it may not always be the best option.

The default authorization scope for Blogger is https://www.blogger.com/feeds/, but the Blogger API does not consistently return links that use https for the scheme. You have a few options to deal with this issue. The simplest is to just use a scope of http://www.blogger.com/feeds/. Another option is to request an authorization scope for both http and https by specifying them together, separated by a space. The downside of this approach is that Blogger will be listed twice when the user is redirected to Google to authorize your application, which I think looks weird for the user. Finally you can correct the scheme portion of URLs before submitting requests with them, but that won’t work for all API calls, such as for deleting entries.

Also note that some links included in responses may not match the authorization scope at all, for example the http://your-blog.blogspot.com/feeds/posts/default feed URL. The OAuth Playground is an excellent tool for experimenting with the various GData APIs.

Update: The latest version of GDataServiceGoogleBlogger has been updated to make http://www.blogger.com/feeds/ the base service URL until the Blogger https issue is resolved, so things just work now.

Retain Feeds

Something which may not be immediately obvious is that you must retain a feed if you’re going to retain any of its entries. Failing to do so can result in EXC_BAC_ACCESS errors with a stack trace similar to the following.

1
2
3
4
5
#0  0x002c646c in xmlStrdup
#1   0x0027580f in xmlCopyPropList
#2   0x00089ec5 in -[GDataXMLNode XMLNodeCopy] at GDataXMLNode.m:879
#3   0x00089c0c in -[GDataXMLNode copyWithZone:] at GDataXMLNode.m:843
...

The issue here is that a feed’s entries refer back to the feed, but they do not retain it. This can be particularly frustrating to debug, as none of the usual NSZombie tricks work, presumably because the failure is occurring down in libxml2 code.

GDataHTTPFetcher Logging

The GData APIs have a very nice logging feature that you can enable with the following code. You can include it pretty much anywhere in your project.

1
[GDataHTTPFetcher setIsLoggingEnabled:YES]

There is currently a linker bug that requires you to add -ObjC and either the -all_load or -force_load options to the Other Linker Flags section of your build target. Once you’ve done that, you can find the logs deep within your home directory. Mine showed up in ~/Library/Application Support/iPhone Simulator/4.1/Applications/<some UUID>/GDataHTTPDebugLogs.

Simple Backup

I finished putting together my personal website backup script, and it can be found on my Github page.

Simple Backup is a bash script that will backup a single file system directory and a mysql database. It is intended to be used for personal web sites, hosting smaller amounts of data. You can run the script out of cron on the server that hosts your website, and it is recommended that you also run a sync process on your personal computer to ensure you have a copy of your backup files that is not on the server.

The default options will remove backup files after they are 30 days old. File system backups are done via tar, with a full backup once a week and incremental backups for all other days. The database backup dumps all tables the configured account has access to. You should set up a read only mysql user just for backups.

This script is a result of my personal desire to back up my blog, and it is based on my post about backing up your personal website. I didn’t see much else out there, so hopefully this is useful for others as well. I’m happy to fix any bugs that may be encountered, but I can’t guarantee any kind of timeline. I accept absolutely no responsibility for the integrity of backups created with this script, and I highly recommend doing a test recovery if you choose to use it.

I would be happy to hear feedback from anyone who decides to give it a try.

Moving and Copying Lines in Xcode

Being a heavy Eclipse user, I’ve grown accustomed to its keyboard shortcuts for moving and copying lines of text. Xcode doesn’t have a regular editing command for this, but it does offer similar functionality via user scripts. The Move Line Up and Move Line Down scripts are already there, just waiting for you to bind keys to them. Go to the script menu, which is to the left of the Help menu, and select the Edit User Scripts… menu item. Expand the disclosure triangle next to Text, and you can set keyboard shortcuts by double-clicking in the column to the right of the script names. Below is a screenshot of what the dialog will look like if you follow all of the instructions in this post.

edit-user-scripts.png

As you can see, I’ve also added Copy Line Up and Copy Line Down scripts. Xcode doesn’t come with scripts for these commands, but you can pretty easily modify the existing move line scripts to create them. I’ve included the scripts I came up with below. These are just the Apple provided scripts with the delete lines removed and the text selection offsets changed to highlight the copied text.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using terms from application "Xcode"
  tell first text document
      set {startLine, endLine} to selected paragraph range
      if startLine > 1 then
          set theText to (paragraphs startLine through endLine)
          set theText to (theText as string)
          make new paragraph at beginning of paragraph (startLine)
              with data theText
          set selected paragraph range to {startLine, endLine}
      else
          beep 1
      end if
  end tell
end using terms from
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using terms from application "Xcode"
  tell first text document
      set {startLine, endLine} to selected paragraph range
      if endLine < (count paragraphs) then
          set theText to (paragraphs startLine through endLine)
          set theText to (theText as string)
          make new paragraph at beginning of paragraph (endLine + 1)
              with data theText
          set theSize to (endLine - startLine)
          set selected paragraph range
              to {endLine + 1, endLine + theSize + 1}
      else
          beep 1
      end if
  end tell
end using terms from

You’ll need to save these scripts somewhere in your home directory. I put them in ~/Library/Application Support/Developer/Shared/Xcode, as that’s were other Xcode customization files go. You can then add the scripts by clicking on the plus in the bottom left corner of the Edit User Scripts window and selecting the Add Script File… menu item. You should also change the four drop down options on the right to be the same as the original move line scripts. Once you’ve added the scripts, you can bind keys to them like any of the other scripts.

If you’re going to make further modifications, you need to be sure you understand the difference between the “shell scripts” and “script files”. Shell scripts will have their contents displayed in the text view on the right side of the Edit User Scripts window. You can safely use the Duplicate Script option on them, because you have your own copy of the content in the XCUserScripts.plist file located in the previously mentioned directory. Script files on the other hand are stored in the main Xcode directory, so you need to manually copy and add them before making any modifications, just like I did for this example.

Finally I’ve noticed that the user scripts don’t block further input while they execute. If you’re trying to move multiple lines of text around, you can actually hit keys faster than the scripts finish executing, which may result in the group of lines being de-selected and getting jumbled up. Because of this, the user scripts are best suited for single lines or short range copies.

Reading Two-Up PDFs

Some Two-Up PDFs, such as the new Pragmatic Guide Series, are intended to be read with specific pages on the left or right side of the screen. Unfortunately these types of documents don’t always display the way the author intended in every PDF reader. However most PDF readers do provide features to get the desired presentation.

Preview

If you have a mac, you’re familiar with the Preview application. I prefer Preview to other options because of the way it maximizes to the full size of the document you’re reading, as opposed to maximizing to take up the whole screen. It doesn’t offer any features that let you directly control the page presentation of two-up PDFs, but you can insert blank pages into the document to force the correct display.

Preview - Insert Blank Page

While this approach seems a bit brute, it does get the job done. It also has the advantage that you only have to do it once for documents that would otherwise require you to toggle a setting on subsequent reads. You can undo (⌘Z) the insert operation if you don’t get the placement right the first time.

Adobe Reader

Everyone who’s used a computer has come across Adobe Reader. Reader offers a Show Cover Page During Two-Up option that you can toggle on or off to get the desired results. While this saves you from having to modify the document you’re reading, you have to keep toggling the setting if you move back and forth within the document.

Adobe Reader - Show Cover Page During Two-Up

GoodReader

I wrote about GoodReader in my Getting Files onto the iPad post. As further support of GoodReader’s goodness, it dedicates an entire menu to two-up PDF presentation. Simply select the appropriate option from the Pages Layout menu. The menu icon will be the icon for the current presentation mode, so be prepared to look for a different icon if you need to change the setting to something else. Both two-up settings only work in landscape mode.

GoodReader - Pages Layout

Backing Up Your Personal Website

Most website hosting companies do their own backups, but they often make no guarantees about actually recovering your data. If you put any amount of effort into a website, you really owe it to yourself to have a decent backup and recovery strategy. You don’t have to do anything too fancy or complicated, but at a minimum you should backup your home directory with tar and any databases that your website uses.

Recoverability

The first thing you need to do is decide what kind of recoverability you need. In enterprise environments, you’ll often hear terms like Recovery Point Objective and Recovery Time Objective. All that really means is how much data you’re willing to lose and how fast you need it to be recovered if something goes wrong. For a personal website, the volume of data is likely to be small enough that recovery time is a non-issue. I think the more relevant consideration is whether you want to be able to do point in time recovery. The main questions to ask yourself are listed below.

  • How often are you going to backup your data?
  • Where are you going to run backups?
  • How long are you going to keep backup files?
  • How are you going to know that your backups are working?

Do Nothing

While not much of a strategy, this is your default option. If you do nothing, you’ll have whatever level of recoverability your hosting service provides. The pro of this strategy is that it’s easy. You don’t have to do anything. The con is that you will most likely lose some amount of your work if there is ever a failure.

Manual Backups

My web host provides cPanel. While I think it’s an impressive piece of software, the backup features leave something to be desired. There are a few options for specifying what is backed up, after which you receive an email when the backup is completed, and you then have a few days to download the backup file from the server. While better than nothing, I personally don’t have the time or inclination to do a manual backup at some regular interval. The pro of this strategy is that you don’t have to write any scripts. The con is that you have to remember to do it, and it takes up your time.

Automated Server Side Backups

Ideally you want your backups to run automatically on the server. This can be as easy as setting up a cron job to execute a script once a day. One of the challenges when working with a server that you don’t actually own, is that you also have to find a way to get the backup files to your personal computer or some other reliable storage location. Having backups files sitting on the server when its file system gets corrupted isn’t going to do much to help you recover your website. Tools like FTP, SCP and rsync can help you copy backup files to your local system.

Automated Client Side Backups

If you can’t run backups on the server, you can always run them on your computer. One of the downsides with this approach is that your personal computer may not always be on or connected to the internet, making the possibility of a scheduled backup failing more likely. You’ll also probably have to pipe command output back through SSH, which not all people are familiar with. On the plus side, you don’t have to worry about copying files to your computer, as they’ll just be there once your backup completes.

Hybrid Backup Strategy

Another option is to run server side backups and have a local job automatically pull down new backup files. This option offers the reliability of server side backups, with the risk that something could happen to the server between the time that your backup completes and the local process downloads the files.

Notification

Once you’ve figured out how to automate your backup process, you should also make sure that you’re notified if something doesn’t work. This is the part that I think many people get wrong. You’ll often see scripts that attempt to build the notification in as part of the backup process. While that seems like a good idea at first, the problem is that if anything goes wrong with the backup script, the notification may never get sent. Recovery time is not when you want to discover a new bug with your backup code.

My preferred way to handle this is to have scripts do something to indicate success, and then I have a separate process that checks for success notifications and reports if any are missing. In a work environment this could be as simple as a web page with red/green icons for failure or success. The important thing is that you want something you look at regularly, where the lack of a recent success message is going to really stand out to you. On your personal computer, you could use something like growlnotify if you’re on a mac.

Offsite Copy

Depending on how paranoid you are, you may want to keep an offsite copy.  You could burn a CD/DVD once a month and drop it off at a friend’s house, or maybe mail it to a PO box. There are also an ever increasing number of “cloud” solutions that let you store files out on the internet. You could use something like Dropbox, although it’s not a pure backup product. I’m currently trying out Mozy, which lets you encrypt backups with your own key. I haven’t used it enough to recommend it yet, but it does seem to address the confidentiality concern that many people have about backing up their files with somebody else.

Do a Recovery

Last but not least, you should really do a test recovery. The last thing you want is to put all this thought and effort into backing up your files, only to find out after a problem that your backups are useless. Most likely all you need is an Apache server and a MySQL database, both of which are free and extremely easy to find good documentation for.

Conclusion

Backing up your personal website is something you should strongly consider. If you’re familiar with shell scripting and standard UNIX tools, you should be able to put something simple together in a few hours. The basic process is to decide what your recoverability requirements are, choose where to run your backups and create some kind of notification mechanism. I’ve been working on a simple backup script for my own site, and I plan on making it publicly available in the near future.

Eclipse Generated Equality

Most users of Eclipse are familiar with its code generation features. One of the more common of these is “Generate hashCode() and equals()…” from the right-click –> Source (⌘-Opt-S) menu. It roughly follows the recipe on page 33 of Joshua Bloch’s Effective Java, and it is effective in most situations. One case where it doesn’t work is when the class is proxied. Hibernate is just one example of a framework that generates proxies. If the Person class below were proxied, the default Eclipse implementation of equals() would break. In a Hibernate application, this can lead to anything from unnecessary deletes and inserts to some very frustrating bugs.

1
2
3
4
5
6
7
8
9
public class Person {

  private String name;

  public String getName() {
      return name;
  }
  
}

The first potential problem with the default Eclipse equals() is object type. Proxy classes are generally subclasses, but they will never be the same class as the object they’re proxying. As shown here, the default Eclipse behavior is to check for type compatibility with getClass(). If one of our Person objects were a proxy, its class would not be Person, and this equals() method would return false. Fortunately Eclipse offers the option to use instanceof for the type check, so all you have to do to solve this problem is click the “Use ‘instanceof’ to compare types” checkbox.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public boolean equals(Object obj) {
  if (this == obj)
      return true;
  if (obj == null)
      return false;
  if (getClass() != obj.getClass())
      return false;
  DefaultPerson other = (DefaultPerson) obj;
  if (name == null) {
      if (other.name != null)
          return false;
  } else if (!name.equals(other.name))
      return false;
  return true;
}

Let’s take a minute to talk about what proxies look like internally. The proxies generated by both cglib and Javassist will contain the same fields as the target class, and an actual instance of the target class will be held internally. Any method calls on the proxy are then delegated to this internal instance. Below is conceptually what a proxy looks like.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PersonProxy extends Person {

  private String name; // <-- always null!
  
  // the real Person
  private Person person;
  
  public String getName() {
      // do some magic to get the real Person instance
      return person.getName();
  }
  
}

With this in mind, the next problem is the nullness of proxy fields. As shown above, if a Person proxy were passed into the equals() method below, its name field would be null, and the method would return false. The solution to this problem is to use a getter instead of field access.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public boolean equals(Object obj) {
  if (this == obj)
      return true;
  if (obj == null)
      return false;
  if (!(obj instanceof Person))
      return false;
  Person other = (Person) obj;
  if (name == null) {
      if (other.name != null)
          return false;
  } else if (!name.equals(other.name))
      //                        ^^^^--- This will always be null!
      return false;
  return true;
}

The best way I’ve found to convert an equals() method to use getters is with the “Encapsulate Field…” refactoring. To do this, click on any of the occurrences of name, then right-click on it (⌘-Opt-T) and select “Encapsulate Field…” from the Refactoring menu. Make sure “use setter and getter” is selected, and then click the “Preview” button. If the class only has one field, you can then select both the hashCode() and equals() methods from the “Refactored Source” pane and copy them to the clipboard. Click the “Cancel” button and paste the refactored versions over those currently in the class. If there are multiple fields, you’ll have to encapsulate them all, then undo all of the changes after copying the final method implementations. This approach is less error prone than using find and replace, and it lets you continue to use field access in methods other than hashCode() and equals().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public boolean equals(Object obj) {
  if (this == obj)
      return true;
  if (obj == null)
      return false;
  if (!(obj instanceof Person))
      return false;
  Person other = (Person) obj;
  if (getName() == null) {
      if (other.getName() != null)
          return false;
  } else if (!getName().equals(other.getName()))
      return false;
  return true;
}

You should now have an equals() method similar to the one above. Because this version uses getters, the calls will always be delegated to the real Person object, and you shouldn’t get any false equality failures. You can certainly still have other issues with your equals() implementation, but these two steps should address the main problems you encounter when proxies enter the picture.

Liquibase and the modifySql Tag

Liquibase is a tool that helps you manage database changes. It can can be integrated into your build process to automatically update your database, and it also has a command line interface that operational staff can use to generate SQL scripts for production deployment. One of its nice features is database abstraction. You define your changesets in XML, and Liquibase generates compatible SQL for you. As with any tool that tries to provide this level of abstraction over a large number of vendors, it doesn’t work 100% with all DBMS. Below is a simple changeset.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
  http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">

  <changeSet author="marc" id="1">
      <createTable tableName="test">
          <column name="id" type="INT" />
      </createTable>
  </changeSet>

  <changeSet author="marc" id="2">
      <createIndex indexName="idx1" tableName="test">
          <column name="id" />
      </createIndex>
  </changeSet>

</databaseChangeLog>

Writing these by hand isn’t that bad, especially if you’re using an XML editor that does autocomplete based on the document’s XSD, such as Eclipse WTP. Some of the database compatibility issues you may run into include things like unsupported constraint options, lack of support for DDL in transactions, metadata issues, statement formatting and identifier delimitation. These are not issues with Liquibase, just things that can be incompatible between different DBMS.

If you find yourself faced with one of these incompatibilities, you have a few options. You can open a bug report and wait until it’s fixed. Better yet, you can fix it yourself and contribute back to the project. But if neither of those options will work for you, the modifySql tag may get the job done. This tag lets you modify the SQL that’s generated, so you can do anything from a simple append or character replacement to regular expressions. The example I’m going to cover is how to do a regular expression replacement. Regular expressions in Java are bad enough, but they can be a little confusing in Liquibase because you have to format them for XML. Below is a Java example of what we’re trying to accomplish.

1
2
3
4
5
6
7
8
9
@Test
public void pattern_replaces_quotes_with_square_brackets() {
  String expression = "\"(\\w+)\"";
  Pattern p = Pattern.compile(expression);
  Matcher m = p.matcher("create index \"idx1\" on \"test\" (\"id\")");
  assertThat(
      m.replaceAll("[$1]"), is("create index [idx1] on [test] ([id])"));
  System.out.println(expression.replaceAll("\"", "&quot;"));
}

This pattern will find all instances of one or more word characters ([a-zA-Z_0-9]) within quotes and replace those quotes with square brackets. The $1 contains whatever the first thing in parentheses matched, \w+ in this case. We can’t just do a simple character replacement of double quotes because we have to account for left and right brackets. Note that if you have dashes (–) in your object names, the \w pattern won’t match them. Check out the Javadoc for Pattern if you need to brush up on your regular expression syntax. The println at the end of the test will print out the expression formatted for inclusion in XML, so you can just copy and paste the output into your changelog file. If you follow these steps, you’ll end up with a modifySql tag like the one below.

1
2
3
4
5
6
7
8
  <changeSet author="marc" id="2">
      <createIndex indexName="idx1" tableName="test">
          <column name="id" />
      </createIndex>
      <modifySql>
          <regExpReplace replace="&quot;(\w+)&quot;" with="[$1]"/>
      </modifySql>
  </changeSet>

I would highly recommend creating a unit test like I did to ensure that you have the correct regular expression. You’ll save yourself a lot of time, as doing your verification through Liquibase would require repeatedly executing the change, checking it, then rolling back if necessary to try again. If you’re not interested in adding more XML to your build process, but you’d like a capable tool to manage your database changes, Liquibase is supposed to support SQL changelogs in its 2.0 release.

Getting Started with Xcode

I finally spent some quality time with Xcode this past weekend. While I haven’t done any real work with it yet, I think it should be a serviceable IDE. The current version of Xcode at the time of this writing is 3.2.3, and it can be downloaded for free from Apple. You do have to register an account if you don’t have one yet, but you don’t have to pay anything until you want to join the iPhone Developer Program. You can’t actually install anything on a mobile device until you subscribe, which mainly gives you a one year certificate that you use to sign your applications.

Debug Messages

The preferred way to print debug messages is with NSLog. One thing to be aware of is that NSLog messages in Unit tests will not go to the debugger console in Xcode. They will however go to the system console, available via Applications –> Utilities –> Console.app. Personally I find that Unit tests largely obviate the need for debug statements, but they do come in handy sometimes. I also stumbled across a Stackoverflow question that describes what looks like a good technique for using breakpoints to generate debug output. Assuming those breakpoints aren’t in test code. More on that later.

Target Membership

File Listing

If you get linker errors complaining that the class you’re trying to test isn’t found, you most likely just need to add that class to your test target. Some of the information you come across may say not to do this, but it’s most likely referring to Application tests and not Logic/Unit tests. The easiest way to see if a given file is part of the current target is to look for a check mark in the far right column of the file listing as shown above. You can also check the “Compile Sources” folder under the build target itself, or you can examine the target memberships of a specific file with Get Info –> Targets –> Target Memberships.

Testing

Xcode comes with the OCUnit testing framework, which has been around for quite a while. It provides all the standard equality assertions and setup/teardown stuff that you would expect from an xUnit testing framework. Here’s a very simple example of testing a property. I don’t see a need to maintain separate header files for Unit tests. The ordering for assertions is actual valueexpected valuedescription, and the description is required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <SenTestingKit/SenTestingKit.h>
#import "Person.h"

@interface PersonTest : SenTestCase {   
}
@end

@implementation PersonTest

- (void) testName {
  Person *person = [[Person alloc] init];
  person.name = @"marc";
  STAssertEqualObjects(person.name, @"marc", @"");
}

@end

Apple’s documentation talks about Logic tests and Application tests. One of the key distinctions is that Logic/Unit tests directly include the code they’re testing, while Application tests are injected into the running application via a special target. Any project you set up is going to have at least four targets and two executables for various testing and debugging use.

Build Results

You can bring up the Build Results window with ⌘ – Shift – B, which will give you something like the screenshot below if you have a failing test. With the results window set to “By Step”, you have to click on the little horizontal lines button to see what tests failed, and even then you have to visually filter though all of the test output. Fortunately there is a better way.

By Step

If you click on “By Issue”, you’re presented with a much nicer view that has a line for each failing test.

By Issue

If you click on the test failure message, it’ll bring up the test file and highlight the failing assertion line as shown below. You can see that I just commented out the property setting line in my test to get it to fail for this example. From what I’ve read, Xcode used to display message bubbles in the main editor, but I haven’t been able to figure out if that behavior is still possible.

Failed Assertion

Uncomment the assignment statement, ⌘ - B, and we’re all green again.

Green Bar

Debugging

Debugging is an area that could use some work. Breakpoints in Unit tests just plain don’t work out of the box. Several kind people have written up guides explaining how to get them to work, and I’ve listed some of the better ones below. The core of the solution is creating a separate debug executable that you set some flags and environment variables on. Unfortunately the settings are different depending on if you’re targeting a Mac or iPhone. The options also sometimes change, rendering some posts you my find invalid. Note that breakpoints in application code do work, it’s just the ones in test code that require manual configuration. I plan to explore this area more in the future, but for now I’m more interested in getting some applications working.

Testing and Debugging Resources