指定方法の例:


$ java -classpath "/home/hoge/java/lib/*" info.nilab.Hogetter

大切なこと:

  • *.jar ではなくて * を指定
  • ダブルクォーテーションで囲む
ただし、シェルに食われないような工夫が必要です。次のように書けば大丈夫のようです。

>java -cp ".\*" Foo

OR

>java -cp .\*; Foo // Windows の場合
> java -cp ./*: Foo // Unix 系の場合

Java in the Box Annex: 今日の忘れ物

失敗例: こんなエラー

ドット(.)とスラッシュ(/)が入れ替わってたり。


$ java -classpath ./*.jar info.nilab.Hogetter
Exception in thread "main" java.lang.NoClassDefFoundError: //cmu_time_awb/jar
Caused by: java.lang.ClassNotFoundException: ..cmu_time_awb.jar
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
Could not find the main class: ./cmu_time_awb.jar.  Program will exit.
 
$ java -classpath ./*.jar info.nilab.Hogetter
Exception in thread "main" java.lang.NoClassDefFoundError: //commons-beanutils-1/7/0/jar
Caused by: java.lang.ClassNotFoundException: ..commons-beanutils-1.7.0.jar
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
Could not find the main class: ./commons-beanutils-1.7.0.jar.  Program will exit.

参考資料:

- JDK 6 ドキュメント
- いつのまにか oracle.com ドメインにドキュメントが ⇒ Java SE 6 Documentation

クラスパスワイルドカードについて

ベース名のワイルドカード文字 * を含むクラスパスエントリは、ディレクトリ内の拡張子 .jar または .JAR を持つすべてのファイルのリストを指定したものと同等であるとみなされます。たとえば、クラスパスエントリ foo/* は foo というディレクトリ内のすべての JAR ファイルを指定します。* だけから成るクラスパスエントリは、カレントディレクトリ内のすべての JAR ファイルのリストに展開されます。ファイルは、隠されている (つまり、名前が「.」で始まる) かどうかに関係なく考慮されます。

* を含むクラスパスエントリは、クラスファイルには一致しません。単一のディレクトリ foo 内でクラスファイルと JAR ファイルの両方に一致するには、foo:foo/* または foo/*:foo を使用します。選択された順序により、foo 内のクラスおよびリソースが foo 内の JAR ファイルの前に (またはその逆で) ロードされるかどうかが決まります。

サブディレクトリは再帰的に検索されません。たとえば、foo/* では foo 内の JAR ファイルだけを検索し、foo/bar、foo/baz などは検索しません。

ディレクトリ内の JAR ファイルが展開されたクラスパスで列挙される順序は指定されず、プラットフォームにより、あるいは同じマシン上でも時間によって異なる場合があります。うまく構成されたアプリケーションは、特定の順序に依存しないようにすべきです。特定の順序が必要な場合は、クラスパスで JAR ファイルを明示的に列挙できます。

ワイルドカードの展開は早期に行われ、クラスのロード処理自体の間ではなく、プログラムの main メソッドの呼び出しの前に行われます。ワイルドカードを含む入力クラスパスの各要素は、指定したディレクトリ内の JAR ファイルを列挙して生成された要素のシーケンス (空の場合もある) によって置き換えられます。たとえば、ディレクトリ foo に a.jar、b.jar、および c.jar が含まれる場合、クラスパス foo/* は foo/a.jar:foo/b.jar:foo/c.jar に展開され、文字列はシステムプロパティー java.class.path の値になります。

CLASSPATH 環境変数は、-classpath (または -cp) コマンド行オプションと異なって扱われることはありません。つまり、すべての場合ワイルドカードが優先されます。ただし、クラスパスワイルドカードは Class-Path JAR マニフェストのヘッダーでは優先されません。

クラスパスの設定
Understanding class path wildcards

Class path entries can contain the basename wildcard character *, which is considered equivalent to specifying a list of all the files in the directory with the extension .jar or .JAR. For example, the class path entry foo/* specifies all JAR files in the directory named foo. A classpath entry consisting simply of * expands to a list of all the jar files in the current directory. Files will be considered regardless of whether or not they are hidden (that is, have names beginning with '.').

A class path entry that contains * will not match class files. To match both classes and JAR files in a single directory foo, use either foo:foo/* or foo/*:foo. The order chosen determines whether the classes and resources in foo are loaded before JAR files in foo, or vice versa.

Subdirectories are not searched recursively. For example, foo/* looks for JAR files only in foo, not in foo/bar, foo/baz, etc.

The order in which the JAR files in a directory are enumerated in the expanded class path is not specified and may vary from platform to platform and even from moment to moment on the same machine. A well-constructed application should not depend upon any particular order. If a specific order is required then the JAR files can be enumerated explicitly in the class path.

Expansion of wildcards is done early, prior to the invocation of a program's main method, rather than late, during the class-loading process itself. Each element of the input class path containing a wildcard is replaced by the (possibly empty) sequence of elements generated by enumerating the JAR files in the named directory. For example, if the directory foo contains a.jar, b.jar, and c.jar, then the class path foo/* is expanded into foo/a.jar:foo/b.jar:foo/c.jar, and that string would be the value of the system property java.class.path.

The CLASSPATH environment variable is not treated any differently from the -classpath (or -cp) command-line option. That is, wildcards are honored in all these cases. However, class path wildcards are not honored in the Class-Path jar-manifest header.

Setting the class path
Mustang addresses this annoying problem with the introduction of class-path wildcards. The basic idea is very simple: Instead of listing individual jar files in a directory, as in the example above, you can instead just use an asterisk to stand for all of the jar files that can be found in that directory:

java -classpath $LIB/'*' com.xyzzy.app.Main

Note that you must quote the asterisk in order to prevent it from being interpreted by your shell and expanded into something that’s guaranteed not to work if there’s more than one jar file in the directory. When designing this feature we considered various other syntaxes that wouldn’t require quoting. In the end, however, we decided to go with the asterisk since it’s already familiar to Ant users and we figured that anyone clever enough to use class-path wildcards would also be clever enough to understand shell quotation.

Mark Reinhold’s Blog - Class-Path Wildcards in Mustang
以下は、Java SE 6 以前でワイルドカードを使用して「libフォルダ以下にあるすべてのjarファイル」をクラスパスに追加するためのバッチファイルの例です。antのバッチ(lcp.bat)を参考に作成してみました。

for文+ワイルドカードで*.jarファイルをクラスパスに追加(Java SE 6 以前) - てんぷらメモ@はてな

今回のテスト環境:


$ uname -m -r -s -v
Linux 2.6.26-2-amd64 #1 SMP Tue Jan 25 05:59:43 UTC 2011 x86_64
 
$ java -version
java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Java HotSpot(TM) 64-Bit Server VM (build 17.1-b03, mixed mode)

tags: java

Posted by NI-Lab. (@nilab)