Quantcast

Tuesday, December 16, 2008

MySQL dates that work across multiple time-zones

Wouldn't it be nice if you could store date information in your database that would be correct regardless of your server and client time-zones (that may themselves change over time)?

One way to do this is to store all date information relative to the GMT time-zone regardless of the server and client time-zones. Whenever a client interacts with a date you simply convert it from GMT to the client's specific time-zone and back as needed.

Well, after 12 hours of investigation, I finally got this to work ;) Here's how:

  1. Configure the server to use the GMT time-zone by inserting default-time-zone=UTC (case sensitive) in my.cnf
  2. Set the useLegacyDatetimeCode MySQL driver parameter to false. This fixes this bug: http://bugs.mysql.com/bug.php?id=15604
  3. When reading/writing dates from/to the database always use UTC dates
That's it.

Wednesday, December 3, 2008

Tracking Java Versions using Google Analytics (part 2)

Update: I just posted (December 4th, 2008) a version that will allow you to differentiate between 1.6.0* the family versus 1.6.0 the version.

I'm experimenting with a new script for tracking Java versions. To use the new code simply add the following two lines after your Google Analytics script:


<script src="http://java.com/js/deployJava.js" type="text/javascript"></script>
<script src="http://jre-analytics.googlecode.com/svn/trunk/jre-analytics.js" type="text/javascript"></script>
I encourage you to sign up to the mailing list to follow future discussions on this topic: http://code.google.com/p/jre-analytics/

Benefits of new script
  1. Easier, auto-updating installation. Linking to my external script means that you get the benefit of future updates without any effort on your part.
  2. Does not conflict with other scripts. The old code uses _setVar() which records at most one variable across an entire user session. Different scripts compete for exclusive access to this variable. The new code uses _trackEvent() which allows multiple values to be recorded in separate namespaces, so different scripts do not interfere with each other's data collection.
  3. Records multiple JRE versions per user. The old script was supposed to do this but the limitations of _setVar() meant it only recorded the latest JRE version.

Details


About 48 hours after you install the script you should see new data under Content -> Event Tracking. Please note that the Event Tracking menu will not show up until you collect enough data.

Please let me know the statistics reported for your site. Post this at http://forums.java.net/jive/thread.jspa?messageID=317425

The new script will record two kinds of events:
  1. detected: indicates whether Java was detected on the user's machine.
  2. version: indicates the specific Java version detected on the user's machine.
This should give you a much better overview of what's really going on. I look forward to your feedback!

Warning

According to http://code.google.com/apis/analytics/docs/eventTrackerGuide.html both the old and new script will cause your page's "bounce rate" to incorrectly get reported as zero. This is a limitation of how _setVar() and _trackEvent() are implemented and there doesn't seem to be anything I can do about it. Sorry :(

Saturday, November 8, 2008

Tracking Java Versions using Google Analytics

Update: This code does not work as expected (it will only report the newest JRE version). Please see the newer version here.

Ever wonder what Java version your website visitors have installed? Here's how you can leverage Google Analytics to find out:

  1. Locate the Google Analytics script in your HTML page (you must be using the new "ga.js" version). Here is what mine looks like:
    <script type="text/javascript">
    var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
    document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
    </script>
    <script type="text/javascript">
    var pageTracker = _gat._getTracker("UA-XXXXX-X");
    pageTracker._initData();
    pageTracker._trackPageview();
    </script>

  2. Insert the following code after that script:
    <script src='http://java.com/js/deployJava.js' type='text/javascript'></script>
    <script type='text/javascript'>
    var jreVersions = deployJava.getJREs();
    if (jreVersions.length==0)
    pageTracker._setVar("Java: none");
    for (var i=0; i<jreVersions.length; ++i)
    pageTracker._setVar("Java: " + jreVersions[i]);
    </script>

  3. Please note, if you are using Blogger you will need to encode the '<' character as "&lt;". Open up Error Console in your browser to double-check that the script is not generating any errors.
  4. Wait 24 hours for Google to update the report.
  5. View the updated data at Google Analytics -> Visitors -> User Defined.
  6. There are two ways to drill-down based on user-defined values:
    1. Advanced Segments -> New -> Dimensions -> Visitors -> User Defined. For example:
      1. Track users without Java: Matches Exactly: "Java: none"
      2. Track users with Java: Starts With: "Java:" and Does Not Match Exactly: "Java: none"
      3. Track users with Java 1.6: Starts With: "Java: 1.6"
      4. You can then check how many Flash users also had Java installed. Or how many Windows users had Java 1.6 installed. Or the connection speed of Java users.
    2. Visitors -> User Defined -> [Pick One] -> Dimension. For example, you can now click on Java: 1.6.0 -> Dimension -> Browser to find out what browsers visitors with Java 1.6.0 were using.
  7. Publish your results here: http://forums.java.net/jive/thread.jspa?messageID=317425

Sunday, October 26, 2008

Integrating Google Guice into JUnit4 tests

You can integrate Guice into your JUnit 4 tests with three simple steps:

  1. Add the following class to your classpath:
    package com.google.inject.junit;
    
    import com.google.inject.Guice;
    import com.google.inject.Injector;
    import com.google.inject.Module;
    import java.util.List;
    import org.junit.runners.BlockJUnit4ClassRunner;
    import org.junit.runners.model.InitializationError;
    
    /**
     * Uses Guice to inject JUnit4 tests.
     *
     * @author Gili Tzabari
     */
    public class GuiceTestRunner extends BlockJUnit4ClassRunner
    {
      private final Injector injector;
    
      /**
       * Creates a new GuiceTestRunner.
       *
       * @param classToRun the test class to run
       * @param modules the Guice modules
       * @throws InitializationError if the test class is malformed
       */
      public GuiceTestRunner(final Class<?> classToRun, Module... modules) throws InitializationError
      {
        super(classToRun);
        this.injector = Guice.createInjector(modules);
      }
    
      @Override
      public Object createTest()
      {
        return injector.getInstance(getTestClass().getJavaClass());
      }
    
      @Override
      protected void validateZeroArgConstructor(List<Throwable> errors)
      {
        // Guice can inject constructors with parameters so we don't want this method to trigger an error
      }
    
      /**
       * Returns the Guice injector.
       *
       * @return the Guice injector
       */
      protected Injector getInjector()
      {
        return injector;
      }
    }

  2. Customize GuiceTestRunner for your specific project by subclassing it. For example:
    package myproject;
    
    import com.google.inject.junit.GuiceTestRunner;
    import com.wideplay.warp.persist.PersistenceService;
    import myproject.GuiceModule;
    import org.junit.runners.model.InitializationError;
    
    /**
     * JUnit4 runner customized for our Guice module.
     *
     * @author Gili Tzabari
     */
    public class GuiceIntegration extends GuiceTestRunner
    {
      /**
       * Creates a new GuiceIntegration.
       *
       * @param classToRun the test class to run
       * @throws InitializationError if the test class is malformed
       */
      public GuiceIntegration(Class classToRun) throws InitializationError
      {
        super(classToRun, new GuiceModule());
      }
    }

  3. Add @RunWith to all your JUnit test classes. For example:

    @RunWith(GuiceIntegration.class)
    public class MyTest
    {
      @Inject
      public MyTest(SomeDependency foo)
      {
        ...
      }
    }

That's it! Guice will now inject your test classes.

EDIT: I'm a convert :) I now use AtUnit to integrate Guice into JUnit.

Saturday, October 18, 2008

RESTful Dynamic Forms

One of the key characteristics of RESTful web services is (application) "statelessness". That is, all application state should be stored on the client-end and send over with every request.

Sometimes clients are asked to manipulate forms dynamically, adding or removing elements, before submitting it to the server. Typically the client uses "sessionId" to manipulate the page across multiple requests.

If you take a step back, however, you will realize that this application state doesn't belong on the server at all. The server doesn't really care about the intermediate page states, which is why it doesn't persist them to the database in the first place. It only cares about the initial page state (which it sends to the client) and the final page state (which it saves to the database). The server manipulates the page state on behalf of the client because it's easier to implement business logic in modern programming languages than it is to implement them in Javascript on the client. Ideally the client should download the page, manipulate it on its end, and submit the result to the server.

Monday, September 8, 2008

Fixing Visual Studio Run-Time Error R6034

THE PROBLEM

As of Visual Studio 2005, you are required to associate a manifest with any DLL having implicit dependencies. If you do not, LoadLibrary() will throw a runtime error R6034.


DEPENDENCIES

Implicit dependencies are any dynamic libraries that must be loaded before your EXE or DLL (henceforth known as "module") may load. For example, if you link against the Runtime DLLs (msvcr*.dll, msvcp*.dll) then your module is implicitly dependant on them. If you specify any libraries in "Linker -> Input" then your module is implicitly dependant on them. Your module's manifest must specify the names and versions of all these implicit dependencies.

Explicit dependencies, loaded by your explicit calls to LoadLibrary(), do not have to show up in the module's manifest.

HOW DOES THIS AFFECT JAVA?

When you invoke System.loadLibrary(), Java invokes LoadLibrary() under the hood. java.exe is linked statically against the runtime libraries, so it doesn't need a manifest, but any libraries it loads do need one. To reiterate, all JNI libraries must have a manifest for their implicit dependencies.

There are two kinds of manifests: internal or external. External manifest files sit alongside your library. Internal manifests are embedded as resources directly in your module. Manifests describe the location and version of assemblies (group of libraries). Private assemblies reside in the application directory (similar to a private JRE) while public assemblies are shared by multiple applications (similar to a public JRE).



PRIVATE ASSEMBLIES

Installing shared assemblies requires administrator privileges so I'll discuss private assemblies first. Here is how it works...

  1. Pick a directory under "C:\Program Files\Microsoft Visual Studio 9.0\VC\redist". In my case this is "x86\Microsoft.VC90.CRT" (32-bit, runtime libraries)
  2. Copy any implicit dependencies along with the manifest file into your application directory. In my case I copy msvcr90.dll, msvcp90.dll and Microsoft.VC90.CRT.manifest
  3. You're done

When your module invokes LoadLibrary("foo.dll") it will look for an embedded manifest in the DLL. If none is found, it will look for an external file "foo.dll.manifest". The manifest will detail any implicit dependencies, such as msvcr90.dll. Your module manifest contains an implicit dependency on the Microsoft CRT manifest file, so do not remove it. I haven't yet found a way to ship private assemblies without this manifest file, which seems to be the only way that Webstart applications can work (aside from static linking).



SHARED ASSEMBLIES

  1. Download shared assemblies for x86, x64 or IA64 onto the user's machine
  2. Run the installer interactively or silently
  3. You're done

If you want to get fancy, check whether the user already has a manifest installed before step 1 to avoid unnecessary downloads.



REFERENCES

C Run-Time Error R6034
Visual C++ Libraries as Shared Side-by-Side Assemblies
How to: Deploy using XCopy
Impossible to use VS2005 with Java?