public class PreferredListGen extends Object
PreferredClassLoader
. The list is generated by
examining the dependencies of classes contained within a target JAR file and
zero or more additional supporting JAR files. Through various command-line
options, a set of "root" classes are identified as belonging to a public API.
These root classes provide the starting point for recursively computing a
dependency graph, finding all of the classes referenced in the public API of
the root classes, finding all of the classes referenced in turn by the public
API of those classes, and so on, until no new classes are found. The results
of the dependency analysis are combined with the preferred list information
in the additional supporting JAR files to compute a preferred list having the
smallest number of entries that describes the preferred state of the classes
and resources contained in all of the JAR files. The output of the tool is a
new version of the target JAR file containing the generated preferred list,
and/or a copy of the list printed to System.out
.
This tool implements the first guideline described in net.jini.loader.pref
. In many cases it is sufficient to specify
the roots via the -proxy
option. The -api
and
-impl
options are used to generate lists for JAR files
in which the roots are not completely defined by the
proxy classes, or for non-service JAR files. Since there is no definitive
set of rules for determining whether a class should be preferred,
the developer should verify the correctness of the generated list.
The following items are discussed below:
To run the tool on Microsoft Windows platforms:java -jar install_dir/lib/preferredlistgen.jar processing_options
java -jar install_dir\lib\preferredlistgen.jar processing_options
Note that the options for this tool can be specified in any order, and can be intermixed.
-cp
input_classpath
-jar
file
Class-Path
attribute, then these JAR files
will also be processed recursively. The default behavior is to replace the
original JAR file with a new file containing the generated preferred list. If
the original target JAR file contained a preferred list, that list is ignored
and is replaced by the newly generated list. This option may be specified
zero or more times. If multiple -jar
options are specified, the
first file specified is considered the target JAR file.
-proxy
classname
-api
name-expression
Bar
is a nested class of Foo,
then
Bar
would be expressed as Foo$Bar
. The most
specific name-expression is used to match an entry. By default,
any public class in the JAR file that matches name-expression will
be included in the set of roots for dependency analysis. If
name-expression is a class name, then that class will be included
in the set of roots irregardless of its access modifier. If the
-nonpublic
option is also present, then matching non-public
classes will also be included in the set of roots. The -api
option may be specified zero or more times.
As an example, presuming the class com.sun.jini.example.Foo
was included in the target JAR file, then the following would all cause
that class to be included in the public API:
and the last example would also apply to, for instance,-api com/sun/jini/example/Foo.class -api com.sun.jini.example.Foo -api com/sun/jini/example/* -api com/sun/jini/example/-
com.sun.jini.example.gui.FooPanel
.
-impl
name-expression
-api
option. If name-expression is a class name or a
class JAR entry name, that class will be considered preferred and will not be
selected by or included in the dependency analysis even if it was included in
the set of roots as a result of processing the -proxy
and
-api
options. This option may be specified zero or more times.
-nonpublic
-api
name-expressions to be included in the set of
roots for dependency analysis.
-nomerge
-impl
and -api
options may be used to initialize
the preferred state of the merged classes.
-default
false|true
false
case will not be generated (except when no
single entry is found, in which case a default preferred value of
false
is written). In the event of optimization ties, a default
value of false is used.
-noreplace
System.out
.
-print
System.out
, even if
the list is also placed in an updated JAR file.
-tell
classname
Using values from the -api
and -impl
options, a
graph is constructed that defines initial preferred values to be inherited by
the target JAR entries as they are loaded into the graph. If there were no
such options specified, all entries from the target JAR file loaded into the
graph initially will be marked as preferred. The classes and resources
identified by the first -jar
option (the target JAR file) are
then loaded into this graph and are assigned their initial preferred
values. The remaining JAR files that include preferred lists are loaded into
the graph and the entries assigned preferred values based on the preferred
list contained in the JAR file being loaded. If a non-target JAR file does
not contain a preferred list, the default behavior is to merge the classes
and resources in the file with those of the target JAR file (for purposes of
dependency analysis only), making them subject to the -api
and
-impl
options. The -nomerge
option can be used to
override the default behavior, causing all such classes to be assigned a
value of not preferred. The set of root classes is constructed by finding
all of the classes from the target JAR file that are marked as not preferred
in the graph, and by adding all of the public interfaces, or any public
superinterfaces of non-public interfaces implemented by the proxy classes
specified via the -proxy
option. Starting with the root classes,
dependent classes are identified by examining the compiled class file for the
class, finding all of the public and protected fields, methods, constructors,
interfaces, and super classes it references, and then in turn examining those
classes. Any dependent classes found that also exist in the graph will be
marked not preferred, unless that class was explicitly named by a
-impl
option. Any root class or dependent class named by a
-impl
option retains its original preferred value and no further
dependency analysis is performed for the class. The range of the dependency
analysis is restricted to the set of classes included in the graph.
The tool then processes the graph to find the smallest number of preferred list entries that describes the preferred state of all classes and resources in the graph. The resulting preferred list may be printed or included in a JAR file that replaces the original (first) JAR file.
com.sun.jini.reggie.RegistrarProxy
and
com.sun.jini.reggie.AdminProxy
are not identified on the command
line because they are parent classes of
com.sun.jini.reggie.ConstrainableRegistrarProxy
and
com.sun.jini.reggie.ConstrainableAdminProxy
.
java -jar install_dir/lib/preferredlistgen.jar \ -cp install_dir/lib/jsk-platform.jar \ -jar install_dir/lib-dl/reggie-dl.jar \ -jar install_dir/lib-dl/jsk-dl.jar \ -proxy com.sun.jini.reggie.ConstrainableRegistrarProxy \ -proxy com.sun.jini.reggie.ConstrainableAdminProxy
Modifier and Type | Class and Description |
---|---|
private class |
PreferredListGen.Graph
A representation of the graph of classes contained in the set of
JARs being analyzed.
|
private class |
PreferredListGen.PrefData
A representation of a class path, package path wildcard, or namespace
path wildcard preferred list entry.
|
Modifier and Type | Field and Description |
---|---|
private String |
classpath
the classpath specified by the -cp option
|
private boolean |
defaultToForce
the default preference value to force
|
private boolean |
doMerge
if true, load JARs without preferred lists directly into listGraph
|
private Boolean |
FALSE
Boolean equivalent of false
|
private boolean |
forceDefault
if true, use defaultToForce for default, otherwise optimize
|
private HashSet |
jarEntries
union of the entries in all JAR files for -api/-impl existence check
|
private HashSet |
jarFiles
loaded JAR names, to avoid infinite loops due to circular definitions
|
private Collection |
jarList
ordered list of JAR files names specified by the -jar options
|
private boolean |
keepNonPublicRoots
if true, non-public roots are allowed
|
private PreferredListGen.Graph |
listGraph
the ordered graph containing the JAR class info
|
private ClassLoader |
loader
the class loader for resolving class names
|
private boolean |
printResults
print the preferred list, only meaningful with the -jar option
|
private Collection |
proxies
the names of proxies supplied by the -proxy option
|
private boolean |
replaceJar
replace the first JAR with a copy containing the preferred list
|
private static boolean |
resinit
flag to indicate that initialization of resources has been attempted
|
private static ResourceBundle |
resources
I18N resource bundle
|
private Collection |
seen
remember classes processed to avoid redundant work or loops
|
private File |
targetJar
the first JAR in the set of files loaded via the -jar option
|
private Collection |
tells
the set of classes to report based on the -tell options
|
private Boolean |
TRUE
Boolean equivalent of true
|
Modifier | Constructor and Description |
---|---|
|
PreferredListGen()
Constructor for programmatic access.
|
private |
PreferredListGen(String[] args)
Create a preferred list generator and process the command line arguments.
|
Modifier and Type | Method and Description |
---|---|
void |
addApi(String apiName)
Initialize the dependency graph with a public API entry.
|
private void |
addIfPublic(Class intFace,
Collection roots)
Add to the given set of roots the interface named
intFace if
that interface is public. |
void |
addImpl(String implName)
Initialize the dependency graph with a private API entry.
|
void |
addJar(String jarName)
Add
jarName to the list of JAR files to process. |
void |
addProxy(String proxy)
Add
proxy to the set of proxies used to identify
roots. |
private void |
addProxyRoot(Class proxyClass,
Collection roots)
Add to the given set of roots the public interfaces implemented by
the
proxyClass and all of its superclasses. |
void |
addTell(String tellName)
Add
tellName to the tell list. |
private void |
buildJarFile(String preferredList)
Replace the first JAR file in the set of JARs with a new copy containing
the given preferred list.
|
void |
compute()
Load JAR files, initialize the dependency graph, and perform the
dependency analysis.
|
private PreferredListGen.Graph |
createGraph(File jarFile)
Create a
Graph initialized from the preferred list of
the given JAR file. |
private String |
fileToClass(String classID)
Convert the given class reference to a class name.
|
void |
generatePreferredList(PrintWriter writer)
Generate the preferred list from the dependency graph.
|
private Collection |
getRoots()
Generate the
Collection of roots to use for the dependency
analysis. |
private static String |
getString(String key)
Return the string according to resourceBundle format.
|
private static String |
getString(String key,
Object v1)
Return the string according to resourceBundle format.
|
private static String |
getString(String key,
Object v1,
Object v2)
Return the string according to resourceBundle format.
|
private static String |
getString(String key,
Object v1,
Object v2,
Object v3)
Get the strings from our resource localization bundle.
|
private boolean |
include(int modifiers)
Determine whether the given modifier imply inclusion in the public api.
|
private void |
inspectClass(String className)
Process all public and protected fields, methods, constructors,
interfaces, and superclasses declared by the given class.
|
private void |
loadJar(File jar)
Load the given JAR
File , adding every class or resource in
the file a graph. |
private void |
loadJars()
Load all of the JAR files named on the command line or referenced in
Class-Path manifest entries of those JAR files.
|
static void |
main(String[] args)
The command line interface to the tool.
|
private static String |
nextNonBlankLine(BufferedReader reader)
Returns next non-blank, non-comment line, or null if end of file has
been reached.
|
private boolean |
noPreferredList(File jar)
Return
true if the given JAR file does not contain a preferred list. |
private void |
populateGraph(PreferredListGen.Graph g,
File jar)
Load the given JAR
File , adding every class or resource in
the file to the given graph. |
private static void |
print(String key,
Object v1)
Print out string according to resourceBundle format.
|
private static void |
print(String key,
Object v1,
Object v2)
Print out string according to resourceBundle format.
|
private static void |
print(String key,
Object v1,
Object v2,
Object v3)
Print out string according to resourceBundle format.
|
private void |
process(String from,
Class c)
Inspect the given class for dependencies.
|
private void |
process(String from,
Class[] arr)
Inspect the given array of classes for dependencies.
|
private void |
process(String from,
Constructor c)
Inspect the given constructor for dependencies.
|
private void |
process(String from,
Field f)
Inspect the given field for dependencies.
|
private void |
process(String from,
Method m)
Inspect the given method for dependencies.
|
private void |
process(String from,
String clazz)
Mark the given class in the
Graph as 'not preferred'
unless overridden by the command line -impl argument. |
void |
setClasspath(String path)
Set the classpath of the classes to include in the analysis.
|
void |
setDefault(boolean def)
Set the default value to use for the preferred list.
|
void |
setKeepNonPublicRoots(boolean keepNonPublicRoots)
Set the flag controlling whether non-public classes should be retained
in the set of roots used for performing dependency analysis.
|
void |
setMerge(boolean doMerge)
Select the behavior for processing non-target JAR files which do not
contain preferred lists.
|
void |
setPrint(boolean printResults)
Set the flag controlling whether a preferred list is to be printed.
|
void |
setReplaceJar(boolean replaceJar)
Set the flag controlling whether a preferred list is to be placed
in the target JAR file.
|
private static void |
usage()
Print the usage message.
|
private final Collection seen
private Boolean TRUE
private Boolean FALSE
private final Collection proxies
private final Collection tells
private File targetJar
private boolean replaceJar
private boolean printResults
private final Collection jarList
private String classpath
private final PreferredListGen.Graph listGraph
private boolean forceDefault
private boolean defaultToForce
private ClassLoader loader
private static ResourceBundle resources
private static boolean resinit
private HashSet jarEntries
private HashSet jarFiles
private boolean keepNonPublicRoots
private boolean doMerge
private PreferredListGen(String[] args)
args
- the command line argumentspublic PreferredListGen()
set
and
add
methods must be called to supply the argument
values. Then compute
and generatePreferredList
must be called to perform the dependency analysis and to generate the
preferred list.private static String getString(String key, Object v1, Object v2, Object v3)
private static String getString(String key)
private static String getString(String key, Object v1)
private static String getString(String key, Object v1, Object v2)
private static void print(String key, Object v1)
private static void print(String key, Object v1, Object v2)
private static void print(String key, Object v1, Object v2, Object v3)
public void setPrint(boolean printResults)
PrintWriter
supplied in
the call to generatePreferredList
is non-null
.
The default value is false
.printResults
- if true
, print the preferred listpublic void setKeepNonPublicRoots(boolean keepNonPublicRoots)
keepNonPublicRoots
- if true
, non-public root classes
are retainedpublic void setMerge(boolean doMerge)
doMerge
is true
, the
classes contained in these JAR files are merged with the target JAR
file for purposes of dependency analysis. The -impl
and
-api
options may be used to initialize the preferred state
of the merged classes. If doMerge
is false
,
the classes in non-target JAR files which do not contain preferred lists are
initialized with a preferred state of 'not preferred'. The default behavior
corresponds to calling setMerge(true)
.doMerge
- if true
, perform the mergepublic void setReplaceJar(boolean replaceJar)
true
.replaceJar
- if true
, update the target JAR filepublic void addJar(String jarName)
jarName
to the list of JAR files to process.
The first call identifies the target JAR file. This method must
be called at least once.jarName
- the name of the JAR file to add to the set.public void addTell(String tellName)
tellName
to the tell list. If a class is identified
as not preferred through the dependency analysis, and if that class
name is in the tell list, then the source dependency causing the class to
be included is printed. This is for debugging purposes.tellName
- the name of the JAR file to add to the tell set.public void addImpl(String implName)
implName
identifies a class or a JAR entry, package, or
namespace that is to be considered private and therefore preferred. If
implName
ends with ".class", it represents a class whose
name is implName
without the ".class" suffix and with each
'/' character replaced with a '.'. Otherwise, if implName
ends with "/" or "/*", it represents a directory wildcard matching all
entries in the named directory. Otherwise, if implName
ends
with "/-", it represents a namespace wildcard that matches all entries in
the named directory and all of its subdirectories. Otherwise
implName
represents a non-class resource in the JAR
file. Alternatively, implName
may be expressed directly as a
class name. The most specific implName
is used to match an
entry found in the JAR files being analyzed. If implName
is
either of the class name forms, then that class is forced to be preferred
and is not included in the public API even it is found by the dependency
analysis.implName
- the identifier for the private API entryIllegalArgumentException
- if implName
does not match
any of the criteria above.public void addApi(String apiName)
apiName
identifies a class or a JAR entry, package, or
namespace that is to be considered public and therefore not
preferred. If apiName
ends with ".class", it represents a
class whose name is apiName
without the ".class" suffix and
with each '/' character replaced with a '.'. Otherwise, if
apiName
ends with "/" or "/*", it represents a directory
wildcard matching all entries in the named directory. Otherwise, if
apiName
ends with "/-", it represents a namespace wildcard
that matches all entries in the named directory and all of its
subdirectories. Otherwise apiName
represents a non-class
resource in the JAR file. Alternatively, apiName
may be
expressed directly as a class name. The most specific
apiName
is used to match an entry found in the JAR files
being analyzed. Any class in the JAR file that matches
apiName
will be included in the set of roots for dependency
analysis. This method may be called zero or more times.apiName
- the identifier for the public API entryIllegalArgumentException
- if apiName
does not match
any of the criteria above.public void setDefault(boolean def)
false
is used.def
- the default value to use for the listpublic void setClasspath(String path)
addJar
method.path
- the classpath for the classes to include in the analysispublic void addProxy(String proxy)
proxy
to the set of proxies used to identify
roots. This method may be called zero or more times.proxy
- the name of the proxy classprivate void loadJars() throws IOException
IOException
- if an error occurs reading the contents
of any of the JAR files.private void loadJar(File jar) throws IOException
File
, adding every class or resource in
the file a graph. The first time this method is called,
listGraph
is loaded directly and all non-preferred classes
found in the graph are marked as roots. On subsequent calls, a new
temporary graph is created and initialized based on the preferred list in
the JAR. After entries are loaded into the graph, it is merged into
listGraph
. The manifest is also read, and any JARs specified
by the manifest Class-Path
attribute are loaded
recursively. If the given JAR file does not exist or is a directory, an
error message is printed.jar
- the File
representing the JAR fileIOException
- if an error occured reading the contents
of the JAR or any of the JARs in the Class-Path.IllegalArgumentException
- if the JAR file does not exist
or is a directoryprivate boolean noPreferredList(File jar) throws IOException
true
if the given JAR file does not contain a preferred list.jar
- the file to examineIOException
- if an error occured reading the contents of the JARprivate void populateGraph(PreferredListGen.Graph g, File jar)
File
, adding every class or resource in
the file to the given graph. The manifest is also read, and any JARs
specified by the manifest Class-Path
attribute are loaded
recursively.g
- the graph to populatejar
- the File
representing the JAR fileprivate PreferredListGen.Graph createGraph(File jarFile) throws IOException
Graph
initialized from the preferred list of
the given JAR file.jarFile
- the JAR file from which an initialized graph is
to be derivedGraph
IOException
- if an error occurs reading jarFile
private static String nextNonBlankLine(BufferedReader reader) throws IOException
reader
- the input streamString
containing the next line,
or null
IOException
- if an error occurs reading the streamprivate String fileToClass(String classID)
classID
- the class identifier, which may be a class file
reference or a class nameprivate void process(String from, Class c)
from
- the dependent of the given class, or null if this
is the top-level classc
- the class to inspectprivate void process(String from, Class[] arr)
from
- the dependent of the given array, or null if this
is the top-level classarr
- the array of classes to inspectprivate boolean include(int modifiers)
true
if the modifiers include the public or protected
flags.modifiers
- the modifier valueprivate void process(String from, Constructor c)
from
- the dependent of the constructor, or null if this
is the top-level classc
- the constructor to inspectprivate void process(String from, Method m)
from
- the dependent of the method, or null if this
is the top-level classm
- the method to inspectprivate void process(String from, Field f)
from
- the dependent of the field, or null if this
is the top-level classf
- the field to inspectprivate void process(String from, String clazz)
Graph
as 'not preferred'
unless overridden by the command line -impl
argument.
If so marked, then the public and private methods, fields,
constructors, interfaces, and superclasses declared by the class are
also inspected recursively. Only classes classes contained within
listGraph
are processed.from
- the class referencing the given class, or null if this
is the top-level classclazz
- the class to add to the public apiprivate void inspectClass(String className)
className
- the name of the class to processpublic void compute() throws IOException
IOException
- if an error occurs constructing the class loader
or reading any of the JAR files.IllegalArgumentException
- in the following cases:
addJar
was never
called or addJar
was called with a file which
does not exist or is a directory
addProxy
method could not be found
private Collection getRoots()
Collection
of roots to use for the dependency
analysis. All classes from the first JAR with a preferred state of false
(which will only be true due to the -api option, or because the same
class in another JAR is marked not-preferred) are added to the set;
Also, all interfaces of classes in the set of -proxy
arguments are added to the set of roots.Collection
of rootsIllegalArgumentException
- if any of the proxies supplied via
the addProxy
method could not be found.private void addProxyRoot(Class proxyClass, Collection roots)
proxyClass
and all of its superclasses.
If any non-public interfaces are implemented, then any parent
interfaces which are public are added.proxyClass
- the proxy to inspectroots
- the set of root class namesprivate void addIfPublic(Class intFace, Collection roots)
intFace
if
that interface is public. Otherwise, add any superinterfaces which are
public.intFace
- the interface to addroots
- the set of rootsprivate static void usage()
public void generatePreferredList(PrintWriter writer) throws IOException
false/true
in order of precedence for 'optimization ties')
is generated. An explicit default entry is generated only for the
default true
case.
The preferred list is sorted such that more specific definitions precede less specific definitions; ties are broken with an alphabetic secondary sort.
The preferred list will be placed in the target JAR file unless
setReplaceJar(false)
was called. The preferred list will be
written to writer
if it is non-null
. If
writer
is null
and setPrint(true)
was called, the preferred list will be written to
System.out
.
writer
- the PrintWriter
to write the preferred list
to.IOException
- if an error occurs updating the target JAR file.private void buildJarFile(String preferredList) throws IOException
preferredList
- the new preferred listIOException
- if an error occurs updating the target JAR file.public static void main(String[] args)
args
- the command line argumentsCopyright 2007-2013, multiple authors.
Licensed under the Apache License, Version 2.0, see the NOTICE file for attributions.