Launching a Java JNLP file with an Old version of JRE

Back in February I wrote a post regarding using Oracle Deployment Ruleset to control the version of Java that executes a given jar file on a given website. Since that time, you may have discovered that applying this to a jnlp file is tricky at best. The problem here is that Internet Explorer downloads the jnlp file to your Temporary Internet files before executing it. As a result, the location it is launching from is not the online url, but the local jnlp file. It may be technically possible to find the certificate hash of each jar file referenced in the jnlp file and add these to you deploymentruleset.jar file but I have not tested this.

As with most Java applets, problems begin to occur when a version of JRE is installed that is higher than the version that the applet is designed for. At first what I tried to do is run a ProcMon on a computer with just the required version of Java installed.

This gave me an awful command something like this:

C:\Program Files (x86)\Java\jre6\bin\javaw.exe -Xbootclasspath/a:C:\\Program Files\\Java\\jre6\\lib\\javaws.jar;C:\\Program Files\\Java\\jre6\\lib\\deploy.jar;C:\\Program Files\\Java\\jre6\\lib\\plugin.jar" -classpath "C:\\Program Files\\Java\\jre6\\lib\\deploy.jar" "\\Program Files\\Java\\jre6\\lib\\security\\javaws.policy" -DtrustProxy=true -Xverify:remote "-Djnlpx.home=C:\\Program Files\\Java\\jre6\\bin" -Dsun.awt.warmup=true -Djnlpx.remove=true -Xmx2048M -Xms1024M -XX:PermSize=64M -XX:MaxPermSize=256M -Djnlp.connection.type=direct -Djnlpx.splashport=65025 "-Djnlpx.jvm=C:\\Program Files\\Java\\jre6\\bin\\javaw.exe" com.sun.javaws.Main "http://urltojnlp/jnlp.jnlp"

Then, I copied the commandline and ran it on a computer with both 1.8 Update 51 and 1.6 Update 26 installed. To my astonishment, although JRE 1.6 created the original command, it handed off to 1.8 and it attempted to run the jnlp file. Not surprisingly since the java app does not support 1.8 this crashed the jvm.

After much hullabaloo, I finally stumbled across this blog post by TimG: Running Web-Launched EAS with the Right Java Version. Tim has the only solution to this issue that I have been able to find.

For my purposes, I have modified Tim’s script slightly to allow for pragmatic selection of architecture version and to pass any extra java parameters (such as memory settings) to the applet.

Update Aug 18 2015: I found numerous bugs/issues with my previous version of the function and have updated the version below to address the following issues:
1. Pragmatic selection of architecture version was broken
2. Added support for extra Java parameters
3. Added notifications for new installations of Java not present in file.

Function Start-JNLP{
        [ValidateScript({$_ | select-string -pattern '^(\d{1}\.\d{1}.\d{1}_\d{2})$'})]
        [string]$ExtraJavaProps = $null
    Add-Type -AssemblyName System.Windows.Forms

    #Import Users Deployment Properties file
        $objDeploymentProperties = Get-Item (Join-Path $env:UserProfile -ChildPath 'AppData\LocalLow\Sun\Java\Deployment\') -ErrorAction SilentlyContinue
        $strDeploymentProperties = Get-Content $objDeploymentProperties
    }Catch [Exception]{

    #Get all Java versions enabled status
    $hashJavaEnabledStatus = @{}
    $arrAllJavaVers = $strDeploymentProperties | select-string -pattern 'deployment.javaws.jre.(\d+).product' | ForEach{$_.matches.Groups[1].Value}
    ForEach($intJava in $arrAllJavaVers){
        $hashJavaEnabledStatus.${intJava} = $strDeploymentProperties | Select-string -pattern "deployment.javaws.jre.${intJava}.enabled=(true|false)" | ForEach{$_.matches.Groups[1].value}

    #Find all Good Java Versions
    $arrGoodJavaVers = $strDeploymentProperties | select-string -pattern "deployment.javaws.jre.(\d+).product=${Version}" | ForEach{$_.matches.Groups[1].Value}

    #Choose correct processor architecture
    $intGoodJava = $arrGoodJavaVers | Where-object{$intVer=$_; $strDeploymentProperties | Select-String -pattern "deployment.javaws.jre.${intVer}.osarch=${Architecture}"  }

    if($intGoodJava.Count -eq 1){
        $strDeploymentProperties = $strDeploymentProperties -replace 'deployment.javaws.jre.(\d+).enabled=true', 'deployment.javaws.jre.$1.enabled=false'
        $strDeploymentProperties = $strDeploymentProperties -replace "deployment.javaws.jre.${intGoodJava}.enabled=false", "deployment.javaws.jre.${intGoodJava}.enabled=true"
        Set-Content -value $strDeploymentProperties -Path $objDeploymentProperties.FullName

        #Get Command
        $strCommand = ($strDeploymentProperties | Select-String -pattern "deployment.javaws.jre.${intGoodJava}.path=(.+)").matches.groups[1].value
        #Clean Command
        $strCommand = $strCommand.substring(0,1) + $strCommand.substring(2,$strCommand.length - 2)

        #Get root java folder
        $strLibPath = (Join-Path -Path ((Get-item $strCommand).Directory.Parent.FullName) -childpath 'lib').Replace('\','\\')
        $strJavaRoot = (Get-item $strCommand).Directory.FullName.Replace('\','\\')

        #Set Params
        $strParams = [string]'"-Xbootclasspath/a:' + $strLibPath + '\\javaws.jar;' + $strLibPath + '\\deploy.jar;' + $strLibPath + '\\plugin.jar" -classpath "' +  $strLibPath + '\\deploy.jar" "' + $strLibPath + '\\security\\javaws.policy" -DtrustProxy=true -Xverify:remote "-Djnlpx.home=' + $strJavaRoot + '" -Dsun.awt.warmup=true -Djnlpx.remove=true ' + $ExtraJavaProps + '"-Djnlpx.jvm=' + $strCommand + '" com.sun.javaws.Main "' + $jnlp + '"'
             #Run Command
            Start-Process $strCommand -ArgumentList $strParams
        }Catch [Exception]{
            write-output "Couldn't run ${strCommand} with arguments ${strParams}. Error" + $_.Exception

        Start-Sleep $WaitSeconds

        #Restore previous enabled flags
        ForEach($intJavaVer in $hashJavaEnabledStatus.keys){
            $boolstatus = $hashJavaEnabledStatus.Item("$intJavaver")
            $strDeploymentProperties = $strDeploymentProperties -replace "deployment.javaws.jre.${intJavaVer}.enabled=false", "deployment.javaws.jre.${intGoodJava}.enabled=${boolstatus}"
        #Save the file
        Set-Content -value $strDeploymentProperties -Path $objDeploymentProperties.FullName

        #Check if it is available in the registry
        $strWow = If($Architecture -match 'x86'){'Wow6432Node\'}else{''}
        $strJavaRegString = 'HKLM:\\Software\' + $strWow + "JavaSoft\Java RunTime Environment\${Version}"
        $regJavaPath = (Get-ItemProperty -Path $strJavaRegString -Name 'JavaHome' -ErrorAction SilentlyContinue).JavaHome
        If(test-path (Join-path $regJavaPath 'bin\javacpl.exe' -ErrorAction SilentlyContinue)){
            [System.Windows.Forms.MessageBox]::Show("Found required version of Java installed, but it isn't found in your file.  We will open the Java control panel to populate the file. You may safely close this after it opens and retry the application")
            Start-Process -Wait (Join-path $regJavaPath 'bin\javacpl.exe')
            [System.Windows.Forms.MessageBox]::Show('The required version of Java was not found')

Essentially what we are doing is pragmatically disabling all versions of JRE we don’t want to run. Then we execute our JNLP file and re-enable any previous version of JRE we disabled.

To call this function, we can simply run the following:

Start-JNLP -JNLP 'http://urltojnlp/file.jnlp' -Architecture amd64 -Version '1.6.0_26' -WaitSeconds 30 -ExtraJavaProps '-Xmx2048M -Xms1024M -XX:PermSize=64M -XX:MaxPermSize=256M -Djnlp.connection.type=direct -Djnlpx.splashport=65025'

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s