2018-05-02

Java usage tracking or "How to figure out what Java is used for in your environment"

UPDATE 2018-10-17: The release notes of Java 8u191/192 indicate that the location of the usagetracker.properties file has been changed. The guide below has been updated accordingly.

Oracle announced the end of Java 8 and that they will no longer provide free updates for Java 8 for enterprises after January 2019 here and here. And while Mozilla, Google and even Microsoft already removed Java support from their respective flagship browsers Oracle also announced that they will drop the browser plugin completely in upcoming releases.

Java 9 and later versions are already only available in 64bit and with the total removal of the plugin support approaching there are only two options left if you still need to support Java plugins in your environment: either run an outdated version of Java ... or figure out which vendors/developers to kick so that they switch their applications to either Java Web Start or get rid of Java completely.

While the first option is not really something I even want to consider the second option is certainly more challenging.
With over 3500 users and workstations spread over dozens of departments in this company figuring out who might be using Java applets by simply asking them is never going to work. People just do not know what their software is actually using in the background. So I was looking for a way to audit the usage of Java on the clients.

After asking on both the /r/sysadmin and the /r/java sub-reddits I first started looking into the "Java Advanced Management Console (AMC)" but quickly decided that it was too much of a clusterfuck and setting it up would require way too much time and effort. The instructions provided by Oracle on how to set AMC up being severely outdated played part in that. The next suggestion was more helpful, utilizing the same feature the AMC is leveraging, the "Java Usage Tracker (JUT)". (Be aware that if you want to use the JUT in production you will need a commercial license.)

You can enable the JUT by either placing a file called "usagetracker.properties" into the "%ProgramFiles(86)%\Java\conf\" folder if you want to track the usage of ALL Java versions present on the system (both installed and bundled with some random software) or alternatively you can create the "usagetracker.properties" file directly in the "<JRE directory>/conf/management/" directory ("<JRE directory>/lib/management/" for JRE releases prior to 9) if you want to track just a single Java version.

The "usagetracker.properties" file looks like this:

# UsageTracker template properties file.
# Copy to <JRE directory>/conf/management/usagetracker.properties
# (or <JRE directory>/lib/management/usagetracker.properties for
# JRE releases prior to 9) and edit, uncommenting required settings, to enable.

# Settings for logging to a file:
# Use forward slashes (/) because backslash is an escape character in a
# properties file.
# com.oracle.usagetracker.logToFile = ${user.home}/.java_usagetracker

# Settings for logging to a UDP socket:
# com.oracle.usagetracker.logToUDP = hostname.domain:32139
 
# (Optional) Specify a file size limit in bytes:
# com.oracle.usagetracker.logFileMaxSize = 10000000

# If the record should include additional Java properties,
# this can be a comma-separated list:
# com.oracle.usagetracker.additionalProperties =

# Additional options:
# com.oracle.usagetracker.verbose = true
# com.oracle.usagetracker.track.last.usage = false
com.oracle.usagetracker.separator = ,
com.oracle.usagetracker.quote = "
com.oracle.usagetracker.innerquote = '


You can have it either log to a file or directly to a UDP socket (like the AMC mentioned above). For simplicity I decided to just log to a file and then parse all the log files with a powershell script. However, because I wanted to audit the Java usage on all clients and really did not want to collect the log files from each client regularly I wanted the log files to be written directly to a server share. And I wanted the log file name to contain the machine names for easier filtering.

Seeing how the "usagetracker.properties" file already had the "${user.home}" variable in it I was hopeful to find a variable that would give me the machine name. But after looking into the list of available system properties and reading up on the subject it quickly became clear that "${user.name}" was the only variable that would actually get expanded in the "usagetracker.properties" file. So I needed to find another way to get the file containing the correct filename populated to the clients.

Since I already have a powershell startup script in place I just (ab)used that for this purpose using the following small script:

$java_usagefile = "${env:ProgramFiles(x86)}\Java\conf\usagetracker.properties"
@"
com.oracle.usagetracker.logToFile = //<server>/REPORTING/JavaUsage/$env:COMPUTERNAME.txt
com.oracle.usagetracker.separator = ,
com.oracle.usagetracker.quote = `"
com.oracle.usagetracker.innerquote = '
"@ | Out-File -FilePath $java_usagefile -Encoding ascii

Once a machine reboots or starts while connected to the network the "usagetracker.properties" file will get created in the "%ProgramFiles(86)%\Java\conf\" folder and I will be able to check which Java applets are being used across the entire company.

NOTE: The "%ProgramFiles(86)%\Java\conf\" path is for 32bit Java version installed on 64bit machines. If you have a 64bit Java version installed on a 64bit machine or a 32bit Java version installed on a 32bit machine the path is "%ProgramFiles%\Java\conf\" and the script above needs to be adjusted accordingly (simply change ${env:ProgramFiles(x86)}" to "$env:ProgramFiles").

NOTE 2: For Java versions prior to 8u191/192 the location for the "usagetracker.properties" file was not "%ProgramFiles(86)%\Java\conf\" (respectively "%ProgramFiles%\Java\conf\") but "%ProgramData%\Oracle\Java". Make sure to adjust your path when updating.


The output of the JUT is quite spammy/complex though (an example can be found in the documentation). Even a Java update or accessing the Java control panel will trigger it. So you will have to write an appropriate parser that filters out all the unwanted lines. A quick script that will give you all the Java applet and Web Start calls could look something like this:


foreach ($file In (Get-ChildItem \\<server\REPORTING\JavaUsage).FullName) {
   Select-String -Path $file -Pattern '^\"(VM\ start)\"' -NotMatch | % { $_.Line + "`n" }
}

A slightly more advanced script that gives me the information I need could look something like this:

$output = @()
foreach ($file In (Get-ChildItem \\<server>\REPORTING\JavaUsage).FullName) {
    $output += Import-Csv -Path $file -Verbose -Debug -Header "Type", "DateTime", "Computer", "Command"
}

$list = New-Object System.Collections.ArrayList
foreach ($item in ($output | Where-Object {$_.Type -eq "plugin2"} | Select-Object computer, command)) {
    $temp = New-Object System.Object
    $computer = $item.Computer -Replace "\/(\d{1,3}\.){3}\d{1,3}$", ""
    $temp | Add-Member -MemberType NoteProperty -Name "Computer" -Value $Computer
    $temp | Add-Member -MemberType NoteProperty -Name "Description" -Value (Get-ADComputer $computer -Properties Description | Select-Object Description).Description
    ($item.Command -match "^(http.*)\:\ .*") | Out-Null
    $temp | Add-Member -MemberType NoteProperty -Name "Website" -Value $matches[1]
    $list.Add($temp) | Out-Null
}

$list | Group-Object computer, description, website | Sort-Object count -Descending | ft count, name -AutoSize


Oh, and make sure that the log folder is writeable by all users, because otherwise you will only get system calls to Java logged. That totally did not happen to me. Really.

[1]: https://twitter.com/BeingSysAdmin/status/992427609857413120

[2]: https://www.reddit.com/r/SysAdminBlogs/comments/8h05a5/java_usage_tracking_or_how_to_figure_out_what/

[3]: https://www.reddit.com/r/sysadmin/comments/9ow7jc/oracle_changed_central_file_system_location_for/

[4]: https://twitter.com/BeingSysAdmin/status/1052463865240477696

3 comments:

  1. Thanks for the write up, really helpful

    ReplyDelete
  2. Thank you! Excelent informantion. Just a question. The final script to filters out all the unwanted lines is not working:
    Import-Csv : Cannot bind argument to parameter 'Path' because it is null

    Has the script worked for someone? I am looking for a way to centralice all the information in one single file. Has someone got it?

    Thank you,

    ReplyDelete
  3. Remember that to use the usage tracker you need to have a Commercial License from Oracle in the first place.

    ReplyDelete