ITエンジニアの成長ブログ

ITエンジニアとして行う勉強の発信&日々の生活で体験した楽しいことをゆるく発信

Windows共有フォルダへのスムーズなアクセス

以下、ドメイン環境ではなくてワークグループ環境のお話です。

Windowsの共有フォルダにアクセスしたときに、以下のようなネットワーク資格情報の入力画面が表示されて入力しなくてはいけないときもあれば、以下の画面は表示されずに普通に共有フォルダへアクセスできたりすることもあり、仕組みをよく分かっていませんでした。

ネットワーク資格情報の入力

まず前提として、他のサーバーの共有フォルダにアクセスした場合は上記ネットワーク資格情報は表示されるのが仕様のようです。
ネットワーク資格情報が表示された場合は、共有フォルダを公開しているサーバー側で有効なユーザー情報を入力することで、晴れて共有フォルダにアクセスできます。

実は、このとき共有フォルダにアクセスする側とアクセスされる側の双方で同じユーザー名とパスワードを設定しておけば、上記のネットワーク資格情報の入力画面は表示されずにスムーズにアクセスができます。

これ知っていましたか?私は全く知らなかったので、良い情報を得たなと思いました。
基礎的な内容かもしれませんが、個人的なメモ程度に残しておきたいと思います。

今回は簡単ですが、この辺で。
最後までお読みいただきありがとうございました。

Mavenの推移的依存関係の優先順位について

Mavenの推移的依存関係の優先順位について気になったので調べました。

Maven公式サイトの以下ページで説明されています。
Introduction to the Dependency Mechanism – Maven

Mavenはdependencyタグに、プロジェクトで必要なライブラリを記述するだけで、その記述したライブラリが必要な依存したライブラリまですべてを自動的に取得してくれます。
そのように推移的に依存関係を解決してくれますが、例えばバージョンが異なる同じライブラリが同時に見つかった場合どれが優先されるのでしょうか。

結論としては、プロジェクトに最も近い定義が優先されます

Maven公式ページで説明されてる以下の図を引用します。

依存関係例1

上記例では、A => B => C => D (2.0) と A => E => D (1.0)という依存関係のツリーがあります。どちらでもDライブラリが依存関係になっていますが、バージョンが異なります。

このようなケースでは、結論で示した通り最も近い定義が優先されることになるので、D (1.0)が選択されることになります。バージョンが新しい方を優先しそうですが、そうではないので気を付ける必要があります。

また、上記ケースで本当はD (2.0)が使いたい場合を考えてみます。その場合はどうすれば良いでしょうか。その場合は、Aの依存関係に明示的に D (2.0)を追加することで使用するライブラリを強制することができます。

依存関係例2

あと気になる点としては、同じ階層で同一のライブラリが見つかった場合はどうなるでしょうか。
例えば、A => B => D (2.0) と A => E => D (1.0)というような依存関係があるケースです。この場合は、定義順で決定されます。dependencyタグが先に記述された方のライブラリが優先されます。

Aのpom.xmlに以下のように記述されていた場合は、 D (2.0)が優先されます。

<dependencies>

	<dependency>
		<groupId>com.example</groupId>
		<artifactId>B</artifactId>
		<version>1.0</version>
	</dependency>

	<dependency>
		<groupId>com.example</groupId>
		<artifactId>E</artifactId>
		<version>1.0</version>
	</dependency>

</dependencies>

定義順を変えて、Aのpom.xmlに以下のように記述されていた場合は、D (1.0)が優先されます。

<dependencies>

	<dependency>
		<groupId>com.example</groupId>
		<artifactId>E</artifactId>
		<version>1.0</version>
	</dependency>

	<dependency>
		<groupId>com.example</groupId>
		<artifactId>B</artifactId>
		<version>1.0</version>
	</dependency>

</dependencies>


以上、Mavenの推移的依存関係の優先順位について調べたことを簡単にまとめました。
今回はこの辺で。最後までお読みいただきありがとうございました。

Mavenのoptionalタグについて

恥ずかしながら、Mavenのdependencyタグの子要素にoptionalタグというものがあることを最近知ったのでメモ程度にまとめてみようと思います。

公式サイトで説明されているのは、以下のページ。
Introduction – Maven

実際にあるライブラリを元にこのoptionalタグについて確認していきたいと思います。
commons-loggingのバージョン1.1を例に説明したいと思います。このライブラリはとても古いので、今使うのはありえないですが依存関係がシンプルで説明しやすいのでこれをベースに説明します。

まずは、commons-loggingの1.1のpomを見てみます。以下のURLで公開されています。
https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1/commons-logging-1.1.pom

このライブラリのdependencysタグを抜粋すると以下の通りです。見てわかる通り、5つの依存が定義されています。

<dependencies>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
  </dependency>
  <dependency>
    <groupId>logkit</groupId>
    <artifactId>logkit</artifactId>
    <version>1.0.1</version>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>avalon-framework</groupId>
    <artifactId>avalon-framework</artifactId>
    <version>4.1.3</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.3</version>
  </dependency>
</dependencies>

仮にこのcommons-loggingの1.1を、自分のプロジェクトの依存関係モジュールに追加するとどうなるか。

  <dependencies>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1</version>
    </dependency>
  </dependencies>

追加したプロジェクトで、mvn dependency:treeを実行し依存関係を確認すると以下の通りです。

com.example:sample:jar:1.0-SNAPSHOT
\- commons-logging:commons-logging:jar:1.1:compile
   +- log4j:log4j:jar:1.2.12:compile
   +- logkit:logkit:jar:1.0.1:compile
   +- avalon-framework:avalon-framework:jar:4.1.3:compile
   \- javax.servlet:servlet-api:jar:2.3:compile

前段で確認した通り、commons-loggingの1.1のdependencyタグに定義されていたモジュールが推移的に解決されて依存関係として管理されています(junitはscopeがtestなので依存関係は推移的に解決されない)。

しかし、よくよく考えてみるとcommons-loggingといえば汎用ロギングライブラリであり、特定の実装には紐づく必要がないように作られているはずです。
commons-logging自体は、もちろん各種ロギングライブラリを使用するので依存関係としては必要ですが、commons-loggingを使用するプロジェクトはどれか特定のライブラリに紐づくはずで、すべてのライブラリに紐づく必要はありません。

実は、commons-loggingの1.1の次バージョンのcommons-loggingの1.1.1でこの状況が解決しています。事前の説明が長くなりましたが、ここでやっとoptionalタグが出てきます。

commons-loggingの1.1.1のpomは以下の通りです。
https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.pom

dependencysタグを抜粋すると以下の通りです。juit以外のdependencyタグにoptionalタグが追加されています。

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
    <optional>true</optional>
  </dependency>
  <dependency>
    <groupId>logkit</groupId>
    <artifactId>logkit</artifactId>
    <version>1.0.1</version>
    <optional>true</optional>
  </dependency>
  <dependency>
    <groupId>avalon-framework</groupId>
    <artifactId>avalon-framework</artifactId>
    <version>4.1.3</version>
    <optional>true</optional>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.3</version>
    <scope>provided</scope>
    <optional>true</optional>
  </dependency>
</dependencies>

commons-loggingの1.1の代わりに、commons-loggingの1.1.1を依存関係に追加し、mvn dependency:treeで確認すると以下の通りです。optionalが付与された依存関係がすべて表示されなくなりました。

com.example:sample:jar:1.0-SNAPSHOT
\- commons-logging:commons-logging:jar:1.1.1:compile

このように、optinalタグはそのoptionalタグを付与したモジュールが必要に応じて依存関係として追加されるように制御するための機能です。

今回の例では、例えばcommons-loggingのロギングにlog4jを使いたいといった場合には改めて依存関係をcommons-loggingを使用するユーザーが明示的に定義することになります。そうすることで、不要な依存関係が追加されなくなり、結果としてもモジュールのバージョン間の競合を防ぐことができます。

おわりに

いかがでしたでしょうか。

mavenはある程度適当に触っても使えてしまうところがありますが、このように細かい仕様も本来ならば理解しておきたいと思いました。

ちなみに、commons-loggingの1.1ではなぜoptionalタグなかったのかというと、簡単に言えば記述忘れたということらしいです。以下リリースノートにそのような記載がありました。
commons-logging/RELEASE-NOTES.txt at commons-logging-1.1.1 · apache/commons-logging · GitHub

今回はこの辺で。最後までお読みいただきありがとうございました。

JavaのSecurity Managerについて調べた

今更ながら、JavaのSecurity Managerについて調べたことをメモ程度にまとめます。

Security Managerとは?

簡単に言えば、Javaのコードを安全に実行する機能のこと。Security Managerを有効化した上で、例えばファイルの読み書きを事前に許可していない状態で、ファイル読み書きを処理するJavaコードを実行した場合、そのコード実行は失敗し例外がスローされます。

Security Managerの動作確認をする

説明だけだと実際のところ、よくわからないと思うので実際に動作確認したいと思います。
少し古いですが動作確認したJavaのバージョンは1.8です。

以下のコードでSecurity Managerの機能について確認します。コードの内容はファイルを作成し、"hello"という文字列を書き込むだけの簡単なものです。

import java.io.FileWriter;

public class TestSecurity {
    public static void main(String[] args) throws Exception {
        System.out.println("start");

        FileWriter fw = new FileWriter("test.txt");
        fw.write("hello");
        fw.close();

        System.out.println("end");
    }
}

まずは、上記コードを実行してみましょう。デフォルトではSecurity Managerは無効になっているので、特にエラーはなく実行が完了するはずです。

C:\work\test\security>java TestSecurity
start
end

ファイルも正常に作成されていると思います。

test.txt

それでは、次はSecurity Managerを有効化します。Security Managerはシステムプロパティで有効化できます。以下のように実行します。-Djava.security.managerが該当のシステムプロパティです。

C:\work\test\security>java -Djava.security.manager TestSecurity
start
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "test.txt" "write")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
        at java.security.AccessController.checkPermission(AccessController.java:884)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.SecurityManager.checkWrite(SecurityManager.java:979)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:200)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:101)
        at java.io.FileWriter.<init>(FileWriter.java:63)
        at TestSecurity.main(TestSecurity.java:7)

上記の通り、実行がエラーになりました。ファイル作成も失敗していることがわかるかと思います。
このように、ファイル書き込みの動作を制限しておけば、例えば悪意のある外部コードがあった場合、そのような意図しないコードの実行を防ぐことができます。

それでは、上記ではSecurity Managerの機能でファイル作成がエラーになってしまったので、問題なく動作するようにファイル作成を許可するようにしたいと思います。
許可する方法としては、どのような操作を許可するか設定するポリシーファイルを作成します。

例えば、allow.policyという名前でファイルを作成し、以下の内容を追記します。詳細は割愛しますが、test.txtファイルへの書き込みを許可した記述となっています。

grant {
    permission java.io.FilePermission "test.txt", "write";
};

上記ポリシーファイルを作成したら、以下のように実行します。

C:\work\test\security>java -Djava.security.manager -Djava.security.policy=allow.policy TestSecurity
start
end

実行時に -Djava.security.policy=allow.policyのようにシステムプロパティで、ポリシーファイルを指定します。そうすることで、権限を許可された操作を実行することができるようになります。上記の結果の通り、今度は正常に処理を行うことができました。

おわりに

いかがでしたでしょうか。

時々、セキュリティマネージャという単語を見かけることがあり実際のところ良くわかっていないで今まで過ごしていたのですが、今回概要だけでも知ることができてよかったと思いました。

簡単にSecurity Managerについて説明してきましたが、そもそもこの機能はJava 17 以降で非推奨になり、Java 24でデフォルトで無効化されました。そのため、新規に開発される案件などであれば使用することはないと思いますが、古いシステムの保守などでもしかしたら携わる機会もあるかもしれないので、頭の片隅に置いておきたいと思います。

今回はこの辺で。最後までお読みいただきありがとうございました。

Javaのtransient修飾子について

Javaのtransient修飾子について調べたことをメモ程度に残します。

transient修飾子とは?

Java言語のシリアライズ関連で使用される修飾子のことで、この修飾子を付与したフィールドはシリアライズ対象から外すことができます。

transient修飾子 | Javaコード入門

例えば以下のようなMyClassクラスがある場合、通常であればname、tmpのどちらのフィールドもシリアライズ対象となります。

package com.example;

import java.io.Serializable;

public class MyClass implements Serializable {
	private String name;
	private String tmp;
}

仮に、tmpは一時的な値を保持するフィールドであるためシリアライズしたくないといった場合は、transient修飾子を付与することでシリアライズ対象から外すことができます。

package com.example;

import java.io.Serializable;

public class MyClass implements Serializable {
	private String name;
	private transient String tmp;
}

実際にコードで確認する

実際にtransient修飾子を使ったコードで動作確認したいと思います。

Userというユーザーを表すクラスを作成し、このクラスのインスタンスをシリアライズすることを考えます。Userクラスは、フィールドとしてname(名前)とpassword(パスワード)を持っています。最初はtransient修飾子を使用しないで確認します。

import java.io.Serializable;

public class User implements Serializable {
    private String name;
    private String password;

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', password='" + password + "'}";
    }
}

上記のUserクラスをシリアライズ・デシリアライズするサンプルコードを以下に示します。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Main {
    public static void main(String[] args) throws Exception {
        User user = new User("taro", "pass123");

        // シリアライズ
        FileOutputStream fileOut = new FileOutputStream("user.ser");
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(user);
        out.close();
        fileOut.close();

        // デシリアライズ
        FileInputStream fileIn = new FileInputStream("user.ser");
        ObjectInputStream in = new ObjectInputStream(fileIn);
        User deserializedUser = (User) in.readObject();
        in.close();
        fileIn.close();

        System.out.println("Deserialized User: " + deserializedUser);
	}
}

上記の実行結果を見てみましょう。Userクラスのフィールドにはtransient修飾子を付与していないので、すべてのフィールドがシリアライズ・デシリアライズされています。

実行結果1

ここで、passwordフィールドについてはセキュリティの観点でシリアライズするものではないということで、transient修飾子を付与したいと思います。変更後のUserクラスは以下の通りです。

import java.io.Serializable;

public class User implements Serializable {
    private String name;
    private transient String password;

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', password='" + password + "'}";
    }
}

そうしたら、再度Mainクラスでシリアライズ・デシリアライズを実行してみましょう。passwordがnullとして表示されています。つまり、シリアライズ・デシリアライズの対象外となったことが確認できました。

実行結果2

おわりに

正直今まで業務でそもそも、シリアライズ・デシリアライズするコードを書く必要がなかったので、transient修飾子については見たことがあるけど良くわからないものでした。

しかし、OSSのソースコードリーディングしていた時に見かけてちゃんと調べてみようと思い、今回記事にしました。

今後も自分では使うことはないかもしれませんが、その意味について今回知ることができてよかったなと思います。

今回はこの辺で。最後までお読みいただきありがとうございました。

「Shift-JIS」ではなくて、「Shift_JIS」が正しい

タイトルの通りなのですが、「Shift-JIS」ではなくて、「Shift_JIS」が正しいらしいです。
ぱっと見、同じなのですが"Shift"と"JIS"の区切り文字の違いです。それぞれハイフン(-)とアンダーバー(_)ですね。

それで、「Shift_JIS」が正しいという根拠はIANAの正式な文書でそう言っているので、そうらしいです。
Character Sets

以下は、上記文書の「Shift_JIS」の掲載箇所の抜粋です。たしかに、ハイフン(-)ではなくてアンダーバー(_)ですね。

IANA文書「Shift_JIS」掲載箇所抜粋

まあ、ハイフンでも伝わるのでどうでもよいような気がしますが、変に突っ込まれたり、「こいつ分かってない」みたいに思われるのもあれなので、しっかりと「Shift_JIS」と書いた方が良さそうですね。

今回はこんな小ネタですが、ここまでにしておきます。
最後までお読みいただきありがとうございました。

「SJIS」が指すもの

先日、以下の記事を書いたのですが、そこで言及していない言葉でそういえば「SJIS」という言葉もあるよなと思い追加で調べました。
CP932、MS932、Shift_JIS、Windows31-Jについて - ITエンジニアの成長ブログ

色々な記事を見てみても、結構あいまいで正確な所は分からなかったのですが、「SJIS」 ≠ 「Shift_JIS」で、「SJIS」 = 「Windows-31J」が多くのケースでは正解のような感じですかね。

つまり、「SJIS」とした場合は「Shift_JIS」ではなくて、「Windows-31J」を指し示す言葉のようです。

例えば、私のお気に入りのエディタであるサクラエディタでは、テキストファイルを保存する際に文字コードセットの選択肢で「SJIS」が選択できます。

サクラエディタの「SJIS」

有名な「髙」(はしごだか)は、「Shift_JIS」にはなくて「Windows-31J」にはある文字ですが、サクラディタで文字コードセット「SJIS」で保存した場合にはこの文字を表示できます。

はしごだか

つまり、サクラエディタでは「SJIS」 ≠ 「Shift_JIS」であり、「SJIS」 = 「Windows-31J」であることが分かります(たぶん合っているかなと)。

以前の記事でも触れたように、「Shift_JIS」とした場合でも多くのケースでは「Windows-31J」を指していたりする場合もあるので実際のところは、正確に確認しなくてはならないものかなと思います。

今回は、「SJIS」 ≠ 「Shift_JIS」という結論を示しましたが、環境や実装によってはそうではないケースもあるもしれません。そんなことも念頭に置いておくと、文字コード周りで上手くいかない場合などに役立つかもしれません。

今回はこの辺で、最後までお読みいただきありがとうございました。