Groovy

Finding Properties in JARs with Groovy

In previous blog posts I have looked at Searching JAR Files with Groovy to find entries (such as .class files) contained in the JAR and Viewing a JAR’s Manifest File with Groovy. In this post, I look at using Groovy to find a particular property in a properties file contained within a JAR. The script in this post searches JARs in a provided directory and its subdirectories for a properties file containing the specified property.

The following Groovy script leverages several advantages of Groovy to recursively search a specified directory and its subdirectories for JAR files containing properties files that contain the specified property. The script outputs matching JARs and their properties files entries that contain the specified property. The script also shows the value that each property is set to in each matched JAR/property file.

findPropertiesInJars.groovy

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env groovy
 
/**
 * findPropertiesInJars.groovy
 *
 * findPropertiesInJars.groovy -d <<root_directories>> -p <<properties_to_search_for>>
 *
 * Script that looks for provided properties (assumed to be in files with
 * .properties extension) in JAR files (assumed to have .jar extensions) in the
 * provided directory and all of its subdirectories.
 */
 
def cli = new CliBuilder(
   usage: 'findPropertiesInJars.groovy -d <root_directories> -p <property_names_to_search_for>',
   header: '\nAvailable options (use -h for help):\n',
   footer: '\nInformation provided via above options is used to generate printed string.\n')
import org.apache.commons.cli.Option
cli.with
{
   h(longOpt: 'help', 'Help', args: 0, required: false)
   d(longOpt: 'directories', 'Directories to be searched', args: Option.UNLIMITED_VALUES, valueSeparator: ',', required: true)
   p(longOpt: 'properties', 'Property names to search for in JARs', args: Option.UNLIMITED_VALUES, valueSeparator: ',', required: true)
}
def opt = cli.parse(args)
if (!opt) return
if (opt.h) cli.usage()
 
def directories = opt.ds
def propertiesToSearchFor = opt.ps
 
import java.util.zip.ZipFile
import java.util.zip.ZipException
 
def matches = new TreeMap<String, Set<String>>()
directories.each
{ directory ->
   def dir = new File(directory)
   propertiesToSearchFor.each
   { propertyToFind ->
      dir.eachFileRecurse
      { file->
         if (file.isFile() && file.name.endsWith('jar'))
         {
            try
            {
               zip = new ZipFile(file)
               entries = zip.entries()
               entries.each
               { entry->
                  def entryName = entry.name
                  if (entryName.contains('.properties'))
                  {
                     def fullEntryName = file.canonicalPath + '!/' + entryName
                     def properties = new Properties()
                     try
                     {
                        def url = new URL('jar:file:' + File.separator + fullEntryName)
                        def jarConnection = (JarURLConnection) url.openConnection()
                        properties.load(jarConnection.inputStream)
                     }
                     catch (Exception exception)
                     {
                        println 'Unable to load properties from ${fullEntryName} - ${exception}'
                     }
                     if (properties.get(propertyToFind) != null)
                     {
                        def pathPlusMatch = '${file.canonicalPath}\n\t\t${entryName}\n\t\t${propertyToFind}=${properties.get(propertyToFind)}'
                        if (matches.get(propertyToFind))
                        {
                           matches.get(propertyToFind).add(pathPlusMatch)
                        }
                        else
                        {
                           def containingJars = new TreeSet<String>()
                           containingJars.add(pathPlusMatch)
                           matches.put(propertyToFind, containingJars)
                        }
                     }
                  }
               }
            }
            catch (ZipException zipEx)
            {
               println 'Unable to open JAR file ${file.name}'
            }
         }
      }
   }
}
 
matches.each
{ propertyName, containingJarNames ->
   println '\nProperty '${propertyName}' Found:'
   containingJarNames.each
   { containingJarName ->
      println '\t${containingJarName}'
   }
}

When the above script is run against JARs, it lists JARs with properties files that have the named property and its assigned value. The screen snapshot shown next demonstrates running the script against the Apache Camel distribution on my machine to find all properties named ‘artifactId’ (Maven) in those numerous JAR files.

The above script takes advantage of several Groovy features. For example, Groovy’s ability to directly use Java APIs and libraries is evident throughout the script with use of classes such as ZipFile (for accessing JAR contents), Properties (for accessing contents of properties files), JarURLConnection (also for accessing properties files’ content), TreeSet (for easy sorting), and Apache Commons CLI (built into Groovy for command line support). Groovy’s closures and concise syntax lead to greater fluency and readability as well.

This script catches exceptions even though Groovy does not require any exception (whether checked or runtime) to be caught. The reason for this is that an uncaught exception would lead to the script terminating. By catching any encountered exception during opening each JAR file or loading from a property file, an exception in those cases will only prevent that particular JAR or property file from being loaded without stopping others from being processed.

This script makes a couple significant assumptions. The first assumption is that the JAR files to be searched have a .jar extension and that the contained properties files have .properties extensions. The script uses built-in CLI support’s nice feature of a single command-line flag allowing multiple directories and/or multiple property names to be searched for by separating the multiple values with commas.

There are times I want to know where a particular property is specified within my application and this script makes it easy to find where that particular property is specified in the JARs on the application’s classpath.
 

Reference: Finding Properties in JARs with Groovy from our JCG partner Dustin Marx at the Inspired by Actual Events blog.

Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button