20200123のJavaに関する記事は8件です。

ArrayListのnewとclear()では挙動が異なる

地味にハマってしまったので。

はじめに

Javaでリストを扱う時、よくArrayListをnewして使うと思います。

hoge.java
List<String> list = new ArrayList<>();

そのArrayListを使い回したいという時にGoogle先生に聞いてみると、

  1. newする
  2. ArrayList.clear()メソッドを使う

の2通りをオススメされると思います。そして、両者の違いについては特に触れられず、強いて言えば「実行速度が違うよ」と言っていくらかQiitaの記事がヒットすると思います。

しかし、実際のところは速度だけではなく、挙動にも異なる点があります。

newとclear()の違い

1次元リストから要素を2つずつ取り出して2次元リストを作る場合を想定します。

以下にサンプルコードを示します。
まずはnewした場合からです。

sample.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TestArraylist {
    public static void main(String args[]) {
        // 出力用の2次元リスト 
        List<ArrayList<String>> sampleLists = new ArrayList<>();
        // sampleListsに要素を追加するための一時的なリスト
        ArrayList<String> listA = new ArrayList<>();
        // 適当なサンプル
        List<String> listB = Arrays.asList("AA", "BB", "CC", "DD", "EE", "FF");

        // 要素数が2のリストを作ってsampleListsに追加する
        for (int i = 0; i < listB.size(); i++) {
            if (i % 2 == 0) {
                listA.add(listB.get(i));
                continue;
            }
            sampleLists.add(listA);
            listA.add(listB.get(i));
            // new前の確認
            System.out.println(sampleLists);

            // 一時的なリストをnewする
            listA = new ArrayList<>();
            // new後の確認
            System.out.println(sampleLists);
        }
    }
}

これを実行すると・・・

[[AA, BB]]
[[AA, BB]]
[[AA, BB], [CC, DD]]
[[AA, BB], [CC, DD]]
[[AA, BB], [CC, DD], [EE, FF]]
[[AA, BB], [CC, DD], [EE, FF]]

ちゃんと目的通りのリストが出来上がっていることがわかります。

一方、newの代わりにclearメソッドを使ったとします。上記サンプルのlistA = new ArrayList<>();をclearメソッドを用いたコードlistA.clear();に書き換えた時の出力は・・・

[[AA, BB]]
[[]]
[[CC, DD], [CC, DD]]
[[], []]
[[EE, FF], [EE, FF], [EE, FF]]
[[], [], []]

このように、確かに追加したはずの要素がclearメソッドを呼び出すたびに要素0のリストとなってしまい、次にclearメソッドを呼び出すまで新たに追加した要素がすべてのリストに追加されてしまいます

おわりに

ググってみると配列の要素を消すのはどちらを使ってもよいというページがたくさん出てくるので油断してしまいますが、実際は上記のような異なる挙動をしているので注意が必要です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

匿名クラス(Stream api 導入の為に)

目的

Stream apiを学ぶ上では匿名クラスの設計及び実装の考え方の理解が不可欠だと感じた.
メソッド参照,関数型インターフェース,ラムダ式,ジェネリクスを用いた設計(インスタンススコープ及びメソッドスコープ,PECS原則に基づいた設計)学習前の入口となる匿名内部クラスについて学ぶ.

入れ子クラス

ブロック({})内で宣言されたクラスを「入れ子クラス」(Nasted Class)と呼ぶ

image.png 入れ子クラスのイメージ図.

NastedClazzSample.java
public class NastedClazzSample {
    static class StaticClazz {}     //static member class

    class MemberClazz {}            //menber class  (inner class)

    void method() {
        class LocalClazz {}         //local class   (inner class)
    }
}

メンバー・クラスとして宣言された入れ子クラスにはアクセス修飾子を付けることが出来る

NastedClazzSample.java
public class NastedClazzSample {
    static protected class StaticClazz {} // OK

    private class MemberClazz {}          // OK

    void method() {
        public class LocalClazz {}        // NG
    }
}
Error.java
ローカル・クラス LocalClazz の修飾子が正しくありません。abstract または final だけが許可されています    NastedClassSample.java  /...(path)   11  Java 問題

何故か.
ローカル・クラスのスコープはローカル変数と同様

例ではmethod()を介してLocalClazzにアクセス
つまりmethod()のアクセッサーと同様になる為記述が出来ない仕様になっている

内部クラス

上記の通り,staticメンバー・クラス以外の入れ子クラス(非staticメンバー・クラス)を内部クラスと言う

内部クラスの特徴

  • ①staticメンバを所持できない
  • ②外部クラスで定義したインスタンス変数にアクセス可能
①staticメンバを所持できない

非staticなメンバはインスタンスと関連
ローカル変数はスタック領域で管理され,対象クラスがインスタンス化される時にヒープ領域に配置される

InnerClazzSample.java
public class InnerClazzSample {
    public class Inner {
        String foo;
    }

    public static void main(String... args) {
        Inner inner = new InnerClazzSample().new Inner();
        inner.foo = "bar";
        System.out.println(inner.foo);
    }
}
実行結果.java
bar

インスタンス化せずに使用した場合

InnerClazzSample.java
public class InnerClazzSample {
    public class Inner {
        String foo;
    }

    public static void main(String... args) {
        Inner.foo = "bar";
    }
}
Error.java
 static フィールド Inner.foo  static 参照できません   InnerClazzSample.java   /~(path)     9   Java 問題

これはどういうことか.

まず,JVMはアプリケーションに使用されるクラスを読み込みメモリ上に展開する
この時staticメンバは非staticメンバとは異なるメモリ領域(クラス領域)に配置される
JVM起動時ブートストラップ・クラス・ローダーが呼び出されるが,すべてのクラスを読み込むのではなくアプリケーションからのクラス参照があった時にロードされる
また,-Xbootclasspathを変更するとロードできるクラス・セットの範囲を変更できる

staticメンバーはクラスと関連している.
メモリに展開されるタイミングはJVMの実装に左右される.

②外部クラスで定義したインスタンス変数にアクセス可能
Outer.java
public class Outer {
    private int foo;

    public class Inner {
        public int bar;
        private void method() {
            this.foo = Outer.this.bar;   // OK
        }
    }

    private void method() {
        this.foo = bar;                  // NG
    }
}

また,非static内部クラスのインスタンス優先順位は

  • 呼び出し元ローカル変数
  • 内部クラスのインスタンス変数
  • 外部クラスのインスタンス変数
Outer.java
public class Outer {
    String foo = "outerclass-a";
    String bar = "outerclass-b";
    String baz = "outerclass-c";

    public class Inner {
        String foo = "innerclass-a";
        String bar = "innerclass-b";

        public void thread() {
            String foo = "thread-c";

            System.out.println(foo);
            System.out.println(bar);
            System.out.println(baz);

            System.out.println(this.foo);
            System.out.println(Outer.this.foo);
        }
    }

    public static void main(String... args) {
        new Outer().new Inner().thread();
    }
}
実行結果.java
thread-c
innerclass-b
outerclass-c
innerclass-a
outerclass-a

匿名クラス(無名クラス,匿名内部クラス)

入れ子クラスを利用する際,名前を持たないクラスとして利用することができる
そのような入れ子クラスを「匿名クラス」という

主に以下の目的の為に使用される

  • ① 処理の再利用性が低くその場でのみ必要となる場合
  • ② 処理コードが短いがカスタマイズが豊富にある場合
匿名クラス-構文1.text
new 型名() {
    抽象メソッドの実装
}

匿名クラス-構文2.text
型名 インスタンス名 = new 型名() {
    抽象メソッドの実装
}

まずは匿名クラスを利用せず,インターフェースの抽象メソッドの実装を行う.

Sample.java
interface FooInterface {                           //interface
    void method();                                 //abstract method
}

class BarClazz implements FooInterface {           //implemented class
    public void method() {
        System.out.println("Did Implementation");  //implemented abstract method
    }
}

public class Sample{                               //runner
    public static void main(String... args) {
        new BarClazz().method();                   //new instance
    }
}

匿名クラスを利用して抽象メソッドの実装を行うと

Sample.java

interface FooInterface {
    void method();
}
                                                            //nothing inplemented class
public class Sample {
    public static void main(String... args) {
        FooInterface foo = new FooInterface() {             //anonymous class
            public void method() {
                System.out.println("Did Implementation");   //implemented abstract method
            }
        };                                                  //semi-coron
        foo.method();                                       //instance.method()
    }
}
実行結果.java
Did Implementation

実用例を考えてみる

① 処理の再利用性が低くその場でのみ必要となる場合

顧客情報を管理するクラスを定義

ManagementClientInformation.java
public class ManagementClientInformation {
    public static class Client {
        private int id;
        private String name;

        Client(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return this.id;
        }

        public String getName() {
            return this.name;
        }
    }

    public static void main(String... args) {
        List<Client> clientList = new ArrayList<>();
        clientList.add(new Client(1, "matsuda"));
        clientList.add(new Client(2, "tanaka"));
        clientList.add(new Client(3, "neko"));
        clientList.stream()
                .forEach(x -> System.out.println(x.getId() + " " + x.getName()));
    }
}
実行結果.java
1 matsuda
2 tanaka
3 neko


匿名クラスの実用例

以下の事象について考えてみる

あなたはシステム・エンジニアです
保守・改修中に顧客からの要望がありました
ある画面について顧客idの逆順に並び替えてほしいとの要望が
尚,システム影響懸念・肥大化の為Clientクラスに手を加えることは禁止とする

⇒匿名クラスを使用

ManagementClientInformation.java
public class ManagementClientInformation {
    public static class Client {
        private int id;
        private String name;

        Client(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return this.id;
        }

        public String getName() {
            return this.name;
        }
    }

    public static void main(String... args) {
        List<Client> clientList = new ArrayList<>();
        clientList.add(new Client(1, "matsuda"));
        clientList.add(new Client(2, "tanaka"));
        clientList.add(new Client(3, "neko"));

        Collections.sort(clientList, new Comparator<Client>() {
            public int compare(Client c1, Client c2) {
                return Integer.compare(c2.getId(), c1.getId());
            }
        });

        clientList.stream()
                .forEach(x -> System.out.println(x.getId() + " " + x.getName()));
    }
}
実行結果.java
3 neko
2 tanaka
1 matsuda

終わりに

初めてStream apiに触れた時,内容が全く分かりませんでした.
もんもんしているうちにメソッド参照⇒ラムダ式⇒ジェネリクス設計と逆順に学習していくうちに,少しずつ理解が深まっていきました.
基礎的な部分をしっかりと理解した上で応用したいと思います.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

匿名クラス(stream api 導入を目指して)

目的

stream apiを学ぶ上では匿名クラスの設計及び実装の考え方の理解が不可欠だと感じた.
メソッド参照,関数型インターフェース,ラムダ式,ジェネリクスを用いた設計(インスタンススコープ及びメソッドスコープ,PECS原則に基づいた設計)学習前の入口となる匿名内部クラスについて学ぶ.

入れ子クラス

ブロック({})内で宣言されたクラスを「入れ子クラス」(Nasted Class)と呼ぶ

image.png 入れ子クラスのイメージ図.

NastedClazzSample.java
public class NastedClazzSample {
    static class StaticClazz {}     //static member class

    class MemberClazz {}            //menber class  (inner class)

    void method() {
        class LocalClazz {}         //local class   (inner class)
    }
}

メンバー・クラスとして宣言された入れ子クラスにはアクセス修飾子を付けることが出来る

NastedClazzSample.java
public class NastedClazzSample {
    static protected class StaticClazz {} // OK

    private class MemberClazz {}          // OK

    void method() {
        public class LocalClazz {}        // NG
    }
}
Error.java
ローカル・クラス LocalClazz の修飾子が正しくありません。abstract または final だけが許可されています    NastedClassSample.java  /...(path)   11  Java 問題

何故か.
ローカル・クラスのスコープはローカル変数と同様

例ではmethod()を介してLocalClazzにアクセス
つまりmethod()のアクセッサーと同様になる為記述が出来ない仕様になっている

内部クラス

上記の通り,staticメンバー・クラス以外の入れ子クラス(非staticメンバー・クラス)を内部クラスと言う

内部クラスの特徴

  • ①staticメンバを所持できない
  • ②外部クラスで定義したインスタンス変数にアクセス可能
①staticメンバを所持できない

非staticなメンバはインスタンスと関連
ローカル変数はスタック領域で管理され,対象クラスがインスタンス化される時にヒープ領域に配置される

InnerClazzSample.java
public class InnerClazzSample {
    public class Inner {
        String foo;
    }

    public static void main(String... args) {
        Inner inner = new InnerClazzSample().new Inner();
        inner.foo = "bar";
        System.out.println(inner.foo);
    }
}
実行結果.java
bar

インスタンス化せずに使用した場合

InnerClazzSample.java
public class InnerClazzSample {
    public class Inner {
        String foo;
    }

    public static void main(String... args) {
        Inner.foo = "bar";
    }
}
Error.java
 static フィールド Inner.foo  static 参照できません   InnerClazzSample.java   /~(path)     9   Java 問題

これはどういうことか.

まず,JVMはアプリケーションに使用されるクラスを読み込みメモリ上に展開する
この時staticメンバは非staticメンバとは異なるメモリ領域(クラス領域)に配置される
JVM起動時ブートストラップ・クラス・ローダーが呼び出されるが,すべてのクラスを読み込むのではなくアプリケーションからのクラス参照があった時にロードされる
また,-Xbootclasspathを変更するとロードできるクラス・セットの範囲を変更できる

staticメンバーはクラスと関連している.
メモリに展開されるタイミングはJVMの実装に左右される.

②外部クラスで定義したインスタンス変数にアクセス可能
Outer.java
public class Outer {
    private int foo;

    public class Inner {
        public int bar;
        private void method() {
            this.foo = Outer.this.bar;   // OK
        }
    }

    private void method() {
        this.foo = bar;                  // NG
    }
}

また,非static内部クラスのインスタンス優先順位は

  • 呼び出し元ローカル変数
  • 内部クラスのインスタンス変数
  • 外部クラスのインスタンス変数
Outer.java
public class Outer {
    String foo = "outerclass-a";
    String bar = "outerclass-b";
    String baz = "outerclass-c";

    public class Inner {
        String foo = "innerclass-a";
        String bar = "innerclass-b";

        public void thread() {
            String foo = "thread-c";

            System.out.println(foo);
            System.out.println(bar);
            System.out.println(baz);

            System.out.println(this.foo);
            System.out.println(Outer.this.foo);
        }
    }

    public static void main(String... args) {
        new Outer().new Inner().thread();
    }
}
実行結果.java
thread-c
innerclass-b
outerclass-c
innerclass-a
outerclass-a

匿名クラス(無名クラス,匿名内部クラス)

入れ子クラスを利用する際,名前を持たないクラスとして利用することができる
そのような入れ子クラスを「匿名クラス」という

主に以下の目的の為に使用される

  • ① 処理の再利用性が低くその場でのみ必要となる場合
  • ② 処理コードが短いがカスタマイズが豊富にある場合
匿名クラス-構文1.text
new 型名() {
    抽象メソッドの実装
}

匿名クラス-構文2.text
型名 インスタンス名 = new 型名() {
    抽象メソッドの実装
}

まずは匿名クラスを利用せず,インターフェースの抽象メソッドの実装を行う.

Sample.java
interface FooInterface {                           //interface
    void method();                                 //abstract method
}

class BarClazz implements FooInterface {           //implemented class
    public void method() {
        System.out.println("Did Implementation");  //implemented abstract method
    }
}

public class Sample{                               //runner
    public static void main(String... args) {
        new BarClazz().method();                   //new instance
    }
}

匿名クラスを利用して抽象メソッドの実装を行うと

Sample.java

interface FooInterface {
    void method();
}
                                                            //nothing inplemented class
public class Sample {
    public static void main(String... args) {
        FooInterface foo = new FooInterface() {             //anonymous class
            public void method() {
                System.out.println("Did Implementation");   //implemented abstract method
            }
        };                                                  //semi-coron
        foo.method();                                       //instance.method()
    }
}
実行結果.java
Did Implementation

匿名クラスの実用例

匿名クラスの実用例を考えてみる

① 処理の再利用性が低くその場でのみ必要となる場合

顧客情報を管理するクラスを定義

ManagementClientInformation.java
public class ManagementClientInformation {
    public static class Client {
        private int id;
        private String name;

        Client(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return this.id;
        }

        public String getName() {
            return this.name;
        }
    }

    public static void main(String... args) {
        List<Client> clientList = new ArrayList<>();
        clientList.add(new Client(1, "matsuda"));
        clientList.add(new Client(2, "tanaka"));
        clientList.add(new Client(3, "neko"));
        clientList.stream()
                .forEach(x -> System.out.println(x.getId() + " " + x.getName()));
    }
}
実行結果.java
1 matsuda
2 tanaka
3 neko


以下の事象について考えてみる

あなたはシステム・エンジニアです
保守・改修中に顧客からの要望がありました
ある画面について顧客idの逆順に並び替えてほしいとの要望が
尚,システム影響懸念・肥大化の為Clientクラスに手を加えることは禁止とする

⇒匿名クラスを使用

ManagementClientInformation.java
public class ManagementClientInformation {
    public static class Client {
        private int id;
        private String name;

        Client(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return this.id;
        }

        public String getName() {
            return this.name;
        }
    }

    public static void main(String... args) {
        List<Client> clientList = new ArrayList<>();
        clientList.add(new Client(1, "matsuda"));
        clientList.add(new Client(2, "tanaka"));
        clientList.add(new Client(3, "neko"));

        Collections.sort(clientList, new Comparator<Client>() {
            public int compare(Client c1, Client c2) {
                return Integer.compare(c2.getId(), c1.getId());
            }
        });

        clientList.stream()
                .forEach(x -> System.out.println(x.getId() + " " + x.getName()));
    }
}
実行結果.java
3 neko
2 tanaka
1 matsuda

終わりに

初めてStream apiに触れた時,内容が全く分かりませんでした.
もんもんしているうちにメソッド参照⇒ラムダ式⇒ジェネリクス設計と逆順に学習していくうちに,少しずつ理解が深まっていきました.
基礎的な部分をしっかりと理解した上で応用したいと思います.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android StudioでJavaのversionで躓いたのでまとめる

はじめに

ここ数ヶ月、実務インターンをするようになってから、インターン先が変わるたびに環境構築をしているのですが、適当にやっているせいでJDKのバージョンが原因なエラーに幾度となく躓いてきました。

毎回調べて、

ああ...JDKのversionが原因か。で、どうするんだっけ。と調べるのが不毛なため備忘録として記事にしました。

この記事は自分のユースケースにおける対処法なため、他の方には当てはまらないかもしれませんが、随時updateしていければと思います。

躓いた具体的なユースケース

まず、

% java -vesion

とターミナルで打つと、ご自身のJDKのバージョンがいくつか確認できるのでご参考までに。

How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

この時の自分のJDKは

java 12.0.2 2019-07-16

でした。

Java 9で「モジュール」の機能が追加された際に、標準ライブラリからいくつかのモジュールが非推奨となりました。

Java 9以降でJAXBを使用するには、外部JARが必要 #49

この機能追加により、 JAXB なるモジュールも非推奨となり、上記のエラーが出るようになったみたいです。Java 8以下では再現せず、Java 9以上の環境で再現するとのこと。

解決策

Java 9, 10であれば、実行時のオプションで

--add-modules java.xml.bind

を指定してあげれば一旦は凌げます。

毎回オプションを追加するのが面倒臭いとか、そもそもJava 10より上のJDK環境の場合は以下の強引な解決策を試してみてください。

強引な解決策

Android StudioのJDKを使用する

まず、知っておくべきことが

Android Studioは、Javaの実行環境そのものを提供してくれています。

そのため、Android Studioがapkを作る時に使っているJavaのPathのメモ の記事でも言及されているように、普段我々がGUIでAndroid StudioでRunした際には、 Android StudioのJava環境でapplication moduleをビルドし、apkを作成しています。

逆に、

Android Studioの下部のTerrminalや、MacやWindowsのアプリケーションとして開いているターミナルでは、OSの実行環境が使用されます。

したがって、GUIではちゃんとビルドできるのに、

% ./gradlew assemble

等のコマンドでビルドしようとすると失敗するのはそのせいです。

GUIでビルドができるなら、Android Studioの実行環境でTerminalも動かしてしまえば良いということで、

.bashrc.zshrc に環境変数のPathを通してあげればよさそうです。

% export PATH=$PATH:/Applications/"Android Studio.app"/Contents/jre/jdk/Contents/Home/bin
% export JAVA_HOME=/Applications/"Android Studio.app"/Contents/jre/jdk/Contents/Home

すると、Android Studioのバージョンにもよりますが、適切なJavaのバージョンでの実行環境でTerminalからも起動できるようになります。

ちなみにAndroid Studio 3.6-RC1では 1.8.0_212-release でした。

異なるversionのJDKをダウンロードする

異なるversionといえども上記のAndroid Studioの実行環境から、 1.8.0 系が良さそうです。Java 8ですね。

Oracle社HP からinstallします。

installの手順は沢山記事があるのでそちらを参考願います。

最近からサインインが必要になったためOracleのアカウントを作らないと行けなかったはずです。

また、表示名が Java SE Development Kit 8u241 みたいな形で、 8u*** となっているかもしれませんが、それが1.8.0 系です。

installができたら、自分がインストールしたJDKのバージョン一覧をみてみましょう。

% /usr/libexec/java_home -V

確認できたら、そのJDK versionを環境変数に設定します。

% export JAVA_HOME=`/usr/libexec/java_home -v {java_version}

{java_version} には先ほど確認したversionを入れましょう。

あとは無事設定できてるかを

% java -version

で確認して、ちゃんと設定できていたらOKです。

参考文献

https://qiita.com/mas0061/items/2fe9333f045800d00b5c

https://qiita.com/kkkkan/items/f9595107a2a9153d1c60

https://github.com/acroquest/javabook-support/issues/49

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[java] HttpURLConnection POST

使い方

以下の通りである。

        try {
            //コネクション
            URL url = new URL("URL");
            httpURLConnection_ = (HttpURLConnection) url.openConnection();
            httpURLConnection_.setDoOutput(true);  //POST可能にする
            httpURLConnection_.connect();

            //送信したいデータ
            String param = "param1=1";

            //リクエストボディに送信したいデータを書き込む
            OutputStream os = httpURLConnection_.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
            writer.write(param);
            writer.flush();
            writer.close();

            //クローズ処理
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

サーバ側で値を取得するときは以下のようにする。(サーバはHttpServletを継承したjavaファイル)

public class Server extends HttpServlet{
    public void doPost(HttpServletRequest request, 
                       HttpServletResponse response)
                       throws IOException, ServletException{
        request.getParameter("param1");
    }
}

感想

見かけは難しく見えたが、予想より簡単に実装できた。結構使うらしいので覚えておきたい。

参考URL

http://konkea.hatenablog.com/entry/2016/10/20/181856

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Java]Jerjey(Jax-rs)でmultipart/form-dataの受け取り

帰ったら書きます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Jerjey(Jax-rs)でmultipart/form-dataの受け取り

はじめに

[Android]端末からサーバへ画像のアップロードにて受け取る側がServletになっていました。本当はJerjeyで受け取りたかったのですが、勝手がわからず妥協していました。今回その方法がなんとなくわかったので紹介させていただきます。

環境

  • Eclipse 4.13.0
  • java 11
  • Jerjey 2.29

multipart/form-dataを対応させる

以下をダウンロードしてください。

この2つにパスを通します。

Applicationクラスを作成

このままでは、multipart/form-dataがまだ使えないので対応させたいプロジェクトのパッケージ内に
[ApplicationConfig.java]を作成し、以下をコピペしてください。

ApplicationConfig.java
package com.Sample.api;

import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;

public class ApplicationConfig extends ResourceConfig {
   public ApplicationConfig() {
     packages("pakage").register(MultiPartFeature.class);
   }
}

"pakage"には使用するパッケージ名を各自入れてください。
次に[Web.xml]を変更します。

Web.xml
<servlet>
  <servlet-name>jersey-app</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <!--ここの"pakage.ApplicationConfig"は上で作成したクラスのパスです。-->
    <param-value>pakage.ApplicationConfig</param-value>
  </init-param>

  <init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
 </servlet>

これでmultipart/form-data対応が完了しました。

実装

以下クラスを作成してください。

Upload.java
import static java.nio.file.StandardCopyOption.*;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;

import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;

@Path("/Upload")
public class Upload {
    @POST
    @Path("/File)
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public String upload(@FormDataParam(value = "file") InputStream fileStream,
                        @FormDataParam(value = "file") FormDataContentDisposition fileDisposition) {
         try {
              Files.copy(fileStream, Paths.get("保存先フォルダ",fileDisposition.getFileName()), REPLACE_EXISTING);
              return "END";
            } catch (IOException e) {
              throw new WebApplicationException(e, 500);
            }

    }
}

これでhttp://localhost/XX/api/Upload/File
(XXはパッケージ名)にファイルを送れば保存されるはずです。

参考

JAX-RSでファイルアップロード!
http://blog.yumix.net/entry/2012/12/17/002515

Jerseyの設定1(web.xmlとかApplicationクラスとか)
https://edgegram.hatenablog.jp/entry/2015/11/25/160433

java - JAX-RS HTTPマルチパートリクエスト
https://tutorialmore.com/questions-1757453.htm

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

spring-batchでreaderの値に応じてwriterを動的に切り替える

たとえば、readerが整数を読み込み、偶数・奇数で異なるwriterを呼び出す、という例を考える。

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.support.ClassifierCompositeItemWriter;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableBatchProcessing
public class App {
    @Bean
    public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
        return jobs
                .get("job")
                .start(step1(steps))
                .build();
    }

    ItemWriter<Integer> evenWriter() {
        return items -> items.forEach(i -> System.out.println("偶数" + i));
    }

    ItemWriter<Integer> oddWriter() {
        return items -> items.forEach(i -> System.out.println("奇数" + i));
    }

    public Step step1(StepBuilderFactory stepBuilderFactory) {
        ItemReader<Integer> reader = new ListItemReader<Integer>(IntStream.range(0, 100).boxed().collect(Collectors.toList()));

        ClassifierCompositeItemWriter<Integer> compositeWriter = new ClassifierCompositeItemWriter<Integer>();
        compositeWriter.setClassifier(number -> {
            if (number % 2 == 0) {
                return evenWriter();
            }
            return oddWriter();
        });

        return stepBuilderFactory
                .get("step1")
                .<Integer, Integer>chunk(4)
                .reader(reader)
                .writer(compositeWriter)
                .build();
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(App.class).run(args);
    }
}

ClassifierCompositeItemWriterは自身がwriterで、指定されたルーティングパターンを基に呼び出すwriterを切り替える。

これを実行すると以下のようになる。

偶数0
偶数2
奇数1
奇数3
...

chunk(4)なので、4件ごとに偶数・奇数writerがそれぞれ1度ずつ呼び出されている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む