Tuesday, June 15, 2010

ehcache relies on slf4j

I couldn't get my unit tests to run from my Ant task after switching to ehcache for Hibernate. Every test would fail with the following error:
java.lang.NoClassDefFoundError: Could not initialize class net.sf.ehcache.hibernate.EhCacheProvider
I followed the instructions in this blog entry to pretty print the classpath and confirm that ehcache.jar was indeed on the Junit test classpath. The application runs fine inside the app server, so what gives?

I solved the mystery by modifying my test so that it only ran one line of code:
System.out.println(Class.forName("net.sf.ehcache.hibernate.EhCacheProvider").newInstance());
This gave me a more useful error message:
java.lang.NoClassDefFoundError: Could not initialize class org.slf4j.LoggerFactory
Aha! I didn't have jboss-home/common/lib/sfl4j-api.jar on my classpath. I added it and one other slf4j jar to my classpath and my problem was solved.

There goes two hours due to a misleading error message.

Thursday, June 10, 2010

XPlanner+ installation

How to install a private instance of XPlanner+ on JBoss 5.1.0.  These instructions assume that you have already downloaded and unzipped JBoss AS 5.1.0.
  • Download the WAR file from the XPlanner+ web site.
  • Expand the WAR file into a new folder within <jboss_home>/server/default/deploy, and then rename the folder xplanner-plus.war
  • Delete <jboss_home>/server/default/deploy/xplanner-plus.war/WEB-INF/lib/xml-apis-1.0.b2.jar because this causes a deployment failure with the following error:
    java.lang.ClassCastException: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl  cannot be cast to javax.xml.parsers.DocumentBuilderFactory
  • Start JBoss using <jboss_home>/bin/run -b 0.0.0.0 so that the application is available from other machines. The default behavior for JBoss is to only serve pages to localhost.
The current version of XPlanner+ creats an in-memory HSQLDB instance that is fast and requires no configuration.  It also persists the data on a clean server shutdown by updating <jboss_home>/bin/hsqldb/xplanner.script.  If the server is abruptly shutdown then it reconstructs the data from <jboss_home>/bin/hsqldb/xplanner.log.  It's a simple and effective scheme and it makes moving the data to another relational database relatively easy because xplanner.script contains both the DML and the DDL to recreate the entire database. One disadvantage is that you cannot connect to the database from an external JDBC tool.

Thursday, March 4, 2010

iPhone with a prepaid plan

I liked the iPhone the first time I saw it, but I never could rationalize spending $80/month for service because I spend less than $8/month with a T-mobile prepaid plan.  Then I read a comment on an article about Google's Nexus One that said you can use a T-mobile SIM card in the iPhone because they both use GSM.  So I purchased a used iPhone 2G (I don't need 3G because I don't have a cellular data plan), unlocked the phone, and inserted my T-mobile SIM.  It works!  I have been using it since mid-January and I love it.  I don't miss the cellular data plan because there are lots of free Wi-Fi hotspots, and I don't really want to be "plugged in" all the time.

A few weeks later my mom asked me for a cell phone recommendation, and she has an AT&T GoPhone prepaid plan.  I thought a locked iPhone might work because it's AT&T, but I had to help unlock hers also.

I had to cobble together instructions and downloads from several sources, so I collected them in this blog entry in case I need to do the same thing again.

I tried unlocking the phone with version 3.1.2 software but BootNeuter didn't work, and I found that others had the same problem.  The most concise summary of the approach is provided in a comment from SOLUTION!!!, but there were no links.  So here are the instructions for Windows, step-by-step, with links:
  1. Buy a used 2G iPhone.  It doesn't matter what version of software it has.  If you buy one that is already unlocked then you can skip to the last step.
  2. Create a new folder for the following downloads:
    1. Download the 3.0 IPSW software for iPhone (not 3G) from here.
    2. Download RedSn0w 0.7.2 for Windows and unzip it.
    3. Download BL-39.bin and BL-46.bin
  3. Perform a DFU restore as follows:
    1. Plug the iPhone into your computer
    2. Launch iTunes
    3. Press and hold both the power button and the home button for 10 seconds, then release the power button only and continue to hold the home button until your computer says something about recognizing a DFU device.
    4. In iTunes, click the restore button while holding down the shift key.  
    5. You should see a dialog that allows you to select the IPSW file you downloaded.
    6. Wait for the iPhone to be restored to 3.0.
  4. Run RedSn0w and follow the instructions.  Select your downloaded IPSW and bin files when prompted.  Make sure you check the box to unlock the phone.
  5. Insert your SIM card by pushing the end of a paperclip into the paperclip-sized hole at the top of your iPhone, adjacent to the headphone jack.
Enjoy using your iPhone with a prepaid plan.  I like T-mobile because you can buy 1000 minutes for $100 and they don't expire for a year.  And if you're like me and you don't even use that many minutes in a year then you can add $10 just before they expire and you get another year.  That's $55 per year, or about $4.50 a month, including all taxes and fees.

System.nanoTime() does not always return a value greater than the previous call

 
-----Original Message-----
From: IncidentDaemon@sun.com [mailto:IncidentDaemon@sun.com]
Sent: Wednesday, October 14, 2009 1:52 PM
To: John Amos
Subject: Your Report (Review ID: 1630000) - System.nanoTime() does not always return a value greater than the previous call

************************************************
Dear Java Developer,

Thank you for your interest in improving the quality of Java Technology.

Your report has been assigned an internal review ID of 1630000, which is NOT visible on the Sun Developer Network (SDN).

Please be aware that the large volume of reports we receive sometimes prevents us from responding individually to each message.

If the information is determined to be a new Bug or RFE, or a duplicate of a known Bug or RFE, you will receive a followup email containing a seven digit bug number.  You may search for, view, or vote for this bug in the Bug Database at http://bugs.sun.com/.

If you just reported an issue that could have a major impact on your project and require a timely response, please consider purchasing one of the support offerings described at http://developers.sun.com/services/.

The Sun Developer Network (http://developers.sun.com) is a free service that Sun offers. To join, visit http://developers.sun.com/global/join_sdn.html.

For a limited time, SDN members can obtain fully licensed Java IDEs for web and enterprise development.  More information is at http://developers.sun.com/prodtech/javatools/free/.

Thank you for using our bug submit page.

Regards,
Java Developer Bug Report Review Team



---------------------------------------------------------------


Date Created: Wed Oct 14 14:52:15 MDT 2009
Type:        rfe
Customer Name:   John Amos
Customer Email:  ***@****.***
SDN ID:      
status:      Waiting
Category:    java
Subcategory: classes_lang
Company:     ******
release:     5.0
hardware:    x86
OSversion:   windows_2003
priority:    4
Synopsis:    System.nanoTime() does not always return a value greater than the previous call
Description:
 A DESCRIPTION OF THE REQUEST :
On some systems with VMWare 2.5.x, the difference between two calls to System.nanoTime() is negative (t1 - t0 < 0).  AbstractQueuedSynchronizer assumes this will never happen.



JUSTIFICATION :
While it is valid to claim that this a defect in VMWare and/or Windows and/or the hardware, it is also irrelevant.  System.nanoTime() should be written defensively, so that it can never return a value that is less than the value from the previous call.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
System.nanoTime() should never return a value that is less than the value from the previous call.  If that isn't possible on the host platform, then this method should write an error message to the console.  The current behavior of failing silently is not acceptable.
ACTUAL -
Runnables that are in the queue of a ScheduledThreadPoolExecutor never run because AbstractQueuedSynchronizer.awaitNanos() is in an infinite loop.  I confirmed using a debugger that the expression (now - lastTime) in this method evaluates to a negative number.

---------- BEGIN SOURCE ----------
now - lastTime evaluates to a negative number in the following method from AbstractQueuedSynchronizer, which is used by ScheduledThreadPoolExecutor.

public final long awaitNanos(long nanosTimeout) throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            long lastTime = System.nanoTime();
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                if (nanosTimeout <= 0L) {
                    transferAfterCancelledWait(node);
                    break;
                }
                LockSupport.parkNanos(nanosTimeout);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
                long now = System.nanoTime();
                nanosTimeout -= now - lastTime;
                lastTime = now;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
            return nanosTimeout - (System.nanoTime() - lastTime);
}


---------- END SOURCE ----------
workaround: 
comments:   

Friday, August 7, 2009

VRBO Web Scraper using Scala and TagSoup

When we go on vacation my wife and I like to stay in houses with kitchens and bedrooms with doors we can close. We have two young daughters, and it's not fun for anyone in the family to sleep together in one room, especially when one of the girls is screaming "THAT'S TOO LOUD!" at the other one.

There are lots of properties on
Vacation Rentals by Owner, but VRBO doesn't have an advanced search engine that allows you to specify your travel dates and number of bedrooms like HomeAway. It's tedious to click through the VRBO listings individually and check the availability calendar for each.

So I created a web scraper in Scala for VRBO that builds a database of property descriptions and availability dates. I recently finished reading
Programming in Scala, and I wanted to build something in Scala. Plus Scala has a seductively simple XML syntax.

My first attempt to use Scala's built-in XML parser failed because the calendar HTML could not be parsed as XML. I found a blog,
Processing real world HTML as if it were XML in scala with a JAR that I imported into my Scala project along with this JAR from TagSoup

To use the scraper, supply the URL of a VRBO page and a folder name on your hard drive for the tab-delimited results. The scraper collects property data in one file and calendar data in another. To load the tab-delimited text files into Oracle, I used this
DDL and these (1, 2) control files. And then I could query using SQL.

It works great, and the Scala code is compact and readable (at least to me):


import xml._
import java.io._
import de.hars.scalaxml._

object VrboParser {
def main(args:Array[String]) {
val url = args(0)
var path = args(1) + File.separator + url.substring(url.lastIndexOf('/') + 1)
val ele = new TagSoupFactoryAdapter load url
val props = (ele \\ "li") filter (attribute(_, "class").matches("property-(alt-)?row clearfix"))
val propSb = new StringBuilder
val writer = createWriter(path + "-cal.txt")
for (prop <- props) {
val propInfo = propertyInfo(prop)
propSb append propInfo._2
writer.write(loadCalendar(propInfo._1))
}
writer.close
writeResults(path + "-prop.txt", propSb.toString)
}

def attribute(node: Node, label: String) =
node.attribute(label) match {
case Some(res) => res.first.toString
case None => ""
}

def loadCalendar(propertyId: String) = {
val sb = new StringBuilder
val calURL = "http://www.homeawayconnect.com/calendar.aspx?propertyid=" + propertyId + "&cid=5"
val calNode = new TagSoupFactoryAdapter load calURL
val cals = (calNode \\ "table") filter (attribute(_, "id") startsWith "calMonthAvail2009")
for (month <- cals) sb append calInfo(month, propertyId)
sb.toString
}

def calInfo(month: Node, propertyId: String) = {
val sb = new StringBuilder
val id = attribute(month, "id")
val yearMonth = id.substring(id.length - 6)
val dates = (month \\ "td") filter (attribute(_, "class") matches "AC.DV")
for (date <- dates) sb.append(propertyId + "\t" + yearMonth + zeroPad(date) + "\t" + attribute(date, "class") + "\r\n")
sb.toString
}

def zeroPad(date: Node) = (if (date.text.length < 2) "0" else "") + date.text

def propertyInfo(prop: Node) = {
val titles = (prop \\ "a") filter (attribute(_, "class") == "property-title")
val items = (titles.first \\ "span").first.text.substring(2).split("\\[")
val propertyId = items(1).substring(1, items(1).length - 1)
val location = items(0).trim()
val details = (prop \\ "li") filter (attribute(_, "class") == "property-details")
(propertyId, propertyId + "\t" + location + "\t" + details.first.text + "\r\n")
}

def createWriter(path: String) = new BufferedWriter(new FileWriter(new File(path)))

def writeResults(path: String, result: String) {
val writer = createWriter(path)
writer.write(result)
writer.close
}
}

Thursday, July 30, 2009

XML with Scala and Java

The web application I build at work stores user permissions in XML. Each component of the application can be assigned a set of permissions like view, create, update and delete. Our documentation includes an Excel spreadsheet version of the same data.

The spreadsheet looks like this:

And the corresponding XML looks like this:

I discovered that my co-workers were updating this spreadsheet manually whenever the XML changed and at the end of each software release. So I wrote a simple utility in Java to convert the XML into the spreadsheet format. I used SAX to process the XML and wrote the results to a tab-delimited text file that could be copied and pasted into a spreadsheet. You can see the Java code here.

Then I finished reading Programming in Scala and wanted to write some Scala code. So I translated my Java utility into Scala, and you can see the Scala code here. I couldn't find any examples that used the scala.xml.pull package, so I had to figure it out using the API documentation. One segment of the Scala code is below:

def getPermissions(file: File, results: ListBuffer[String]) {
val er = new XMLEventReader()
er.initialize(io.Source.fromFile(file))
val sb = new StringBuilder
var atEnd: Boolean = false
while(!atEnd) {
var next = er.next
next match {
case EvElemStart(_, "Resource", _, _) => {
sb.append(getAttributeValue(next, "resourceName", "", "\t"))
sb.append(getAttributeValue(next, "description", "", "\t"))
}
case EvElemStart(_, "Permission", _, _) => {
if (!sb.isEmpty) {
sb.append(getAttributeValue(next, "permissionName", if (sb.endsWith("\t")) "" else ", ", ""))
}
}
case EvElemEnd(_, "Resource") => {
results += sb.toString
sb.clear
}
case EvElemEnd(_, "Application") => {
atEnd = true
er.stop
}
case _ =>
}
}
}

I learned the following about Scala's pull parser:
  • If you don't call XMLEventReader.stop when you are finished parsing a file, then the thread stays alive and your application never exits.
  • XMLEventReader.hasNext always returns true (in version 2.7.5.final), so I couldn't use it for the while() loop above. Instead, I had to create the atEnd Boolean variable and look for the ending XML element.
  • It's ten times slower than using SAX in Java.
On this last point, both versions write timing information to the console.

Scala:

parsing app1.xml took 422 milliseconds.
parsing app2.xml took 156 milliseconds.
parsing app3.xml took 68 milliseconds.
parsing app4.xml took 203 milliseconds.
writeResults took 8 milliseconds.
Completed in 888 milliseconds.

Java:

parsing app1.xml took 68 milliseconds.
parsing app2.xml took 14 milliseconds.
parsing app3.xml took 5 milliseconds.
parsing app4.xml took 14 milliseconds.
writeResults took 5 milliseconds.
Completed in 127 milliseconds.