简体   繁体   中英

How can I avoid needing to specify a fully qualified class name on the command line when delivering a single jar and using reflection?

I have a codebase that I want to produce several deliverable jar files, each of which is to be run from the command line and which accesses some functionality of the codebase. When run, some of the command-line parameters are other classes within the jar that are then created by reflection. It works, nicely, but I have to specify the very long fully qualified path to the class, even though the classes are within the jar file.

I am using ant to compile and then jar a series of directories. From a minimal sample build.xml file:

<property name="build" location="build"/>
<property name="jars" location="${build}/jars"/>
<property name="classes" location="${build}/classes"/>
<property name="src" location="src/dom/place/proj"/>

<target name="utility">
 <javac 
    includeantruntime="false" 
    srcdir="${src}/utility" destdir="${classes}" 
    classpathref="classpath" />
 <jar 
    destfile="${jars}/utility.jar" 
    basedir="${classes}" 
    includes="**/utility/**" />
</target>

<target name="tokenizers" depends="utility">
 <javac 
    includeantruntime="false" 
    srcdir="${src}/tokenizers" 
    destdir="${classes}" 
    classpathref="classpath" />
 <jar 
    destfile="${jars}/tokenizers.jar" 
    basedir="${classes}" 
    includes="**/tokenizers/**" />
</target>

Later, I am creating a single jar file that includes all needed classes and will run a command, for example:

<target name="tokenize-file-jar">
 <jar destfile="${dist}/TokenizeFile.jar">
  <zipgroupfileset dir="${jars}">
    <include name="utility.jar"/>
    <include name="tokenizers.jar"/>
  </zipgroupfileset>
  <manifest>
    <attribute name="Main-Class" value="dom.place.proj.tokenizers.TokenizeFile"/>
  </manifest>     
 </jar>    
</target>

This all works, and looking in the jar file, all the classes are there.

prompt > unzip -l TokenizeFile.jar 
Archive:  TokenizeFile.jar
Length     Date   Time    Name
--------    ----   ----    ----
        0  01-11-12 11:07   META-INF/
      286  01-11-12 11:07   META-INF/MANIFEST.MF
        0  01-11-12 10:16   dom/
        0  01-11-12 10:16   dom/place/
        0  01-11-12 10:16   dom/place/proj/
        0  01-11-12 10:16   dom/place/proj/tokenizers/
     1737  01-11-12 11:07   dom/place/proj/tokenizers/FileTokenizer.class
     1411  01-11-12 11:07   dom/place/proj/tokenizers/PorterTokenizer.class
     1754  01-11-12 11:07   dom/place/proj/tokenizers/TokenizeFile.class
      992  01-11-12 11:07   dom/place/proj/tokenizers/Tokenizer.class
        0  01-11-12 10:16   dom/place/proj/utility/
     1106  01-11-12 11:07   dom/place/proj/utility/BoundedExecutor.class
     3128  01-11-12 11:07   dom/place/proj/utility/Converter.class
     1107  01-11-12 11:07   dom/place/proj/utility/ExceptionHandler.class

Now, to run the code, I do:

java -jar TokenizeFile.jar test.txt dom.place.proj.tokenizers.FileTokenizer dom.place.proj.tokenizers.PorterTokenizer

and it works. Yay! But I would prefer my client not have to specify the fully qualified name for each class on the command line - this is a small example, and there may be many items. What can I change, in my code or in my build process, to allow this instead:

java -jar TokenizeFile.jar test.txt FileTokenizer PorterTokenizer

Thanks for your help. I am transitioning from C++/make and like ant and Java very much so far.

The short and simple answer is that this isn't possible; class names are only unambiguous given their package as well.

One option to make things simpler to callers is have your application prepend "dom.place.proj.tokenizers." to each class argument they provide - so your shorter, preferred example would behave identically to the first one.

However, with great simplicity comes diminished power; it would no longer be possible for a user to define their own custom class and pass it in, as the packages wouldn't match. (To some extent you could work around this by only prepending the default package if the class name doesn't contain a period ( . ), but the more special cases you add the harder this will be to understand...)


You mention that you're transitioning to Java. At the end of the day, I would expect to have classes specified by their full package-prefixed name when they appear in situations like this, and so the abbreviated version would likely be more confusing to someone who's used to Java.

My recommendation therefore is to leave things as they are now, and, basically, become more accustomed to the first version of the command line. Even if you were to solve the problem in this particular case, there are many other first-party and third-party Java apps and tools that require classes to be specified on the command-line - and they all require the fully-qualified class name too.

Apologies if this doesn't seem much like an answer, but I believe it is the best course of action in this situation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM