Tuesday, September 13, 2011

Interning String Literals In Java

A coworker and I were discussing strings, and some of the things we had heard about how the Java compiler stores and retrieves them from memory. We were particularly wondering if constants, local variables, literals, and runtime generated strings were handled differently. I wrote the following JUnit 4 test to show how all these cases are handled (Java JDK 1.6.0_27):

/**
* Test string interning affects on literal and runtime strings.
*/
public final class StringInternTest {

/**
* String intern test.
*/
@Test
public void stringIntern () {
/* literals */
assertEquals("date", "date");
assertTrue("date" == "date");
/* Compare to an external constant */
assertEquals("date", Constants.DATE);
assertTrue("date" == Constants.DATE);
/* Compare locally defined strings. */
final String myDateString = "date";
assertEquals("date", myDateString);
assertTrue("date" == myDateString);
final String myDateString2 = "date";
assertEquals(myDateString, myDateString2);
assertTrue(myDateString == myDateString2);
assertEquals(Constants.DATE, myDateString);
assertEquals(Constants.DATE, myDateString2);
assertTrue(Constants.DATE == myDateString);
assertTrue(Constants.DATE == myDateString2);
/* Create new strings at runtime. */
final String runtime1 = new String("date");
final String runtime2 = new String("date");
assertEquals("date", runtime1);
assertEquals("date", runtime2);
assertEquals(Constants.DATE, runtime1);
assertEquals(Constants.DATE, runtime2);
assertEquals(runtime1, runtime2);
assertFalse(runtime1 == runtime2); // !!!
assertFalse("date" == runtime1); // !!!
assertFalse("date" == runtime2); // !!!
assertFalse(Constants.DATE == runtime1); // !!!
assertFalse(Constants.DATE == runtime2); // !!!
/* Intern the runtime strings. */
final String interned1 = runtime1.intern();
final String interned2 = runtime2.intern();
assertTrue(interned1 == interned2);
assertTrue("date" == interned1);
assertTrue("date" == interned2);
assertTrue(Constants.DATE == interned1);
assertTrue(Constants.DATE == interned2);
}
}

If you set a break point in this test and examine the variables you will find that Constants.DATE, myDateString, myDateString2, interned1, and interned2 all have the same internal object ID. I learned online that all string literals are supposed to be interned when the application is compiled, which accounts for the variables having the same ID.

Strings stored in runtime1 and runtime2 are each given a unique object ID when instantiated at runtime, and thus return a false when == is tried with any of the interned strings.

Some have suggested that manually interning is better because using == is much faster (5x) than equals() (because comparing object IDs is faster than comparing string lengths || characters). However, others dismiss this as a minor gain at best, and I can certainly see that using == might lead to some hard-to-find bugs if a non-interned string is compared to either an interned or another non-interned string. I also learned that main argument strings (args[]) are not interned.

Interned strings are stored in the PermGen (Permanent Generation) memory, and it is possible to fill up that space with strings if one is not careful.

References:

Thursday, September 08, 2011

SOLUTION: "SQL driver not found org.apache.derby.jdbc.ClientDriver" When Using Oracle with Sonar and Maven

I am new to Sonar and Maven, and have been perplexed for several hours with the "SQL driver not found org.apache.derby.jdbc.ClientDriver" error I was getting when running mvn sonar:sonar from the Windows command line after starting the Sonar server.

What is not clear in the instructions is that you must modify both <sonar path>\conf\sonar.properties and <maven path>\conf\settings.xml to get this to work. The Sonar properties tell the Sonar server what database driver to use in the Sonar server, and the Maven settings file tells Maven what database driver to use when it is running Sonar.

Here are the changes I made to connect to my local Oracle XE instance:

sonar.properties

...

sonar.jdbc.username: sonar
sonar.jdbc.password: sonar

...

# Comment the following lines to deactivate the default embedded database.
#sonar.jdbc.url: jdbc:derby://localhost:1527/sonar;create=true
#sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver
#sonar.jdbc.validationQuery: values(1)

...

sonar.jdbc.url: jdbc:oracle:thin:@localhost:1521:xe
sonar.jdbc.driverClassName: oracle.jdbc.driver.OracleDriver
sonar.jdbc.validationQuery: select 1 from dual

settings.xml

<profiles>
  ...
  <profile>
    <id>sonar</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
      <sonar.jdbc.url>
      jdbc:oracle:thin:@localhost:1521:xe</sonar.jdbc.url>
      <sonar.jdbc.driver>
      oracle.jdbc.driver.OracleDriver</sonar.jdbc.driver>
      <sonar.jdbc.username>sonar</sonar.jdbc.username>
      <sonar.jdbc.password>sonar</sonar.jdbc.password>
      <sonar.host.url>http://localhost:9000</sonar.host.url>
    </properties>
  </profile>
</profiles>

Tuesday, September 06, 2011

SOLUTION: Modify PMD's ShortVariable Rule To Ignore ID Fields

Several people online have asked about the problem of not being able to add exceptions to PMD's ShortVariable rule. Most often, it has been the desire to get PMD to ignore the case where the variable name is 'id'. I was not able to find a solution online, but I worked with the XPath until I came up with a working solution.

  1. In Eclipse, go to Window » Preferences » PMD » Rules configuration » ShortVariable » Edit Rule...
  2. Change the XPath field to:
    //VariableDeclaratorId[(string-length(@Image) < 3) and (not (@Image='id'))]
    [not(ancestor::ForInit)]
    [not((ancestor::FormalParameter) and (ancestor::TryStatement))]
  3. Click Apply » Confirm rebuild » Click Ok » Confirm rebuild
  4. Restart Eclipse
  5. Right-click on the project » PMD » Check code with PMD
    1. You should then see the warning markers disappear on your id fields.