Tapestry and testng.xml

You can use a testng.xml file to help control the tests and browser used by Selenium in Tapestry.  However, you have to tell Maven to look for the testng.xml file or it will ignore it.  You do this by putting the following in your pom.xml -> build -> plugins:

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.6</version>
				<configuration>
					<suiteXmlFiles>
						<suiteXmlFile>src/test/conf/testng.xml</suiteXmlFile>
					</suiteXmlFiles>
				</configuration>
			</plugin>

Sonar Installation Problems

I ran into a bunch of problems getting Sonar installed in Tomcat 6 as a war file.  It turns out I just didn’t understand the installation process.  To install Sonar as a war, you have to download it, unzip it, build the war using a script, and then deploy the war file.  During the period where it builds the war file, it links the war back to the directory you are building from.  If you move the unzipped directory, the war won’t work.

So unzip the directory somewhere you can keep it, make it owned by Tomcat6, set your configuration file, then create the war and deploy it.  You can make configuration changes by coming back to the directory you unzipped, making a change and then restarting Tomcat.

I was deleting or moving the unzipped directory before deploying the war.  The errors were odd though because it kept saying that it couldn’t bind to the address because the part was already in use.  When trying to use Derby, it kept saying that something was already running on that port.  The same thing happened with MySQL.  Leaving the unzipped directory in place fixed the problem.

Voice Mail Notifications Won’t Go Away

I have a Nexus S from Sprint and set it up with Google Voice.  Unfortunately when I was getting Google Voice set up, I tried calling my number and leaving a message from another phone.  This voice mail got put on Sprint’s voice mail system.  When I switched over to Google Voice, my Sprint voice mail was deleted, but it continued to send me notifications of the voice mail.

So if you are looking at switching to Google Voice, be sure to make the switch when someone isn’t likely to leave you a voice mail.  Sprint tried all kinds of things and eventually said I needed to reset my Nexus S back to factory settings.  They said that the problem was on the phone because Sprint wasn’t sending out any type of voice mail notifications to me. I wiped it, turned it back on and immediately got the little tape recorder notification from Sprint voice mail.

There are things I like about Sprint, but sometimes it seems like their whole system is a piece of alien technology and works most of the time, but they have not idea what is going on or how it actually works.

Finally I went into www.google.com/voice and disabled the Sprint/Google Voice integration.  The Sprint Voicemail notification went away within a few minutes.

I had to wait 15 minutes to reactivate Google Voice again, but once that was done everything seemed to work fine.

Lucene MoreLikeThis Example Code

I was recently working on a simple application where the user will enter famous quotations.  Obviously we want to avoid duplicates so I needed a way to check for quotations that were substantially similar before a new quote was added to the database.

The idea was to show the top 5 most similar quotes before letting the user save the new quotation to the db. I used Lucene for this which allowed me to punt on the more difficult task of figuring out if two quotes were similar or not. I left that up to Lucene and only had to worry about how to get my information in and out of Lucene in a usable manner.

Below is the interesting method that uses Lucene to build an index of all the quotes in the system and then returns the five quotes that are most similar to the new quote text.  Obviously creating a new index each time a quote is added isn’t particularly efficient, but makes it easier to demonstrate how it works and processor efficiency isn’t much of an issue with this particular task.

    public List<Quote> getSimilarQuotes() throws CorruptIndexException, IOException {

        String quoteText = quote.getText();
        logger.info("creating RAMDirectory");
        RAMDirectory idx = new RAMDirectory();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_31, new StandardAnalyzer(Version.LUCENE_31));
        IndexWriter writer = new IndexWriter(idx, indexWriterConfig);

        List<Quote> quotes =  session.createCriteria(Quote.class).list();

        //Create a Lucene document for each quote and add them to the
        //RAMDirectory Index.  We include the db id so we can retrive the
        //similar quotes before returning them to the client.
        for (Quote quote : quotes) {
            Document doc = new Document();
            doc.add(new Field("contents", quote.getText(),Field.Store.YES, Field.Index.ANALYZED));
            doc.add(new Field("id", quote.getId().toString() ,Field.Store.YES, Field.Index.ANALYZED));
            writer.addDocument(doc);
        }

        //We are done writing documents to the index at this point
        writer.close();

        //Open the index
        IndexReader ir = IndexReader.open(idx);
        logger.info("ir has " + ir.numDocs() + " docs in it");
        IndexSearcher is = new IndexSearcher(idx, true);

        MoreLikeThis mlt = new MoreLikeThis(ir);

 		//lower some settings to MoreLikeThis will work with very short
        //quotations
        mlt.setMinTermFreq(1);
        mlt.setMinDocFreq(1);

		//We need a Reader to create the Query so we'll create one
        //using the string quoteText.
        Reader reader = new StringReader(quoteText);

        //Create the query that we can then use to search the index
        Query query = mlt.like( reader);

        //Search the index using the query and get the top 5 results
        TopDocs topDocs = is.search(query,5);
        logger.info("found " + topDocs.totalHits + " topDocs");

        //Create an array to hold the quotes we are going to
        //pass back to the client
        List<Quote> foundQuotes = new ArrayList<Quote>();
        for ( ScoreDoc scoreDoc : topDocs.scoreDocs ) {
            //This retrieves the actual Document from the index using
            //the document number. (scoreDoc.doc is an int that is the
            //doc's id
            Document doc = is.doc( scoreDoc.doc );

            //Get the id that we previously stored in the document from
            //hibernate and parse it back to a long.
            String idField =  doc.get("id");
            long id = Long.parseLong(idField);

            //retrieve the quote from Hibernate so we can pass
            //back an Array of actual Quote objects.
            Quote thisQuote = (Quote)session.get(Quote.class, id);

            //Add the quote to the array we'll pass back to the client
            foundQuotes.add(thisQuote);
        }

        return foundQuotes;
    }

Nexus S 4G and Google Apps

I recently switched phones to the Nexus S 4G. (Take a look at my detailed Nexus S Review.) I’ve been pretty impressed with how well the Nexus S integrates with Google Apps.  I suppose this is expected because it is a Google phone, but experience tells me that just because a company makes a device doesn’t mean it will work well with their software and services.

Google apps has really gown into a nice business package. Here are a few things that impressed me when I was setting up the Nexus S 4G.

  • You can define security policies for Android devices so they can be managed centrally.  For example, you can make the Nexus S require a password or not.
  • You can remotely ring your Nexus S 4G at full volume using the web interface–regardless of what the volume is set on.
  • You can find the device on a map.
  • You can lock the Nexus S 4G remotely.  For example, if you lose it, you can lock it so it will require a pin to log back in.
  • The Nexus S 4G can be wiped remotely.  So if you lose your phone, you can delete everything on it by logging into Google Apps
  • It appears that most of the Nexus S 4G settings are backed up to the Google App servers, so if you do need to wipe your device, you can easily reinstall and set everything back up again once you get it back again.
  • Giving the Nexus S 4G your Google App account will let you connect to most Google services without needing to log back in again.
  • The Nexus S 4G appears to support multiple Google accounts so you can use your business account and a gmail account if you like.