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

【Java】Javaで作れるもの

プログラミング勉強日記

2020年11月20日
最近はずっとJavaを勉強しているが、Javaで作れるものを調べてみた。

Javaの特徴

  • プラットフォームに依存しない
  • 開発効率と保守性が高い
  • ライブラリが充実している

 Javaは動く環境を気にしない言語で、Javaでコードを書けばJVM(Java Virtual Machine:Java仮想マシン)が入っていればWindowsでもLinuxでもMac上でも動く。また、動作が早く、動作速度が要求されるような場合でも用いられる。

Javaで作れるもの

 Javaは「なんでもできる」言語であり、TwitterやEvemoteといったWebサービスはもちろん、三菱UFJ銀行でも採用されるような大規模金融システム、Androidアプリといった幅広くいろいろなものを作れる。具体的に紹介する。

1. Webサービス

 Javaはインターネットと相性が良く、私たちの想像できるWebサービスは大体Javaを使えば作れる。TwitterはJavaを利用している。(以前はRubyを使っていたが、動作が早いということでJavaに変えた)

2. Webサイト

 Webサービスと意味合い的には変わらない。いろいろなWebサイトのデザイン的な面ではなく裏側もJavaで作ることができる。ECサイトや旅行予約サイト、掲示板、SNSといったなんでも作ることができる。楽天の裏側もJavaが使われている。

3. デスクトップアプリ

 PCをインストールして動かすソフトウェアでもJavaが使われているOpenOfficeはJavaで作られているオープンソースソフトで、Eclipseも開発環境はJavaで作られている。
 Javaで作られているゲームあり、MINECRAFTもJavaで作られている。

4. 組込みシステム

 Javaは組込み系のために開発されたプログラムで、色々なマシンに使われている。ガラケーもJavaで動いていた。

5. Androidアプリ

 AndroidアプリのほとんどはJavaで書かれている。

6. 商用システム

 銀行のシステムといった商用システムはJavaで書かれている。みずほ銀行のシステム案件はJavaが多く用いられている。

参考文献

【初心者向け】これもJava? Javaで作れるものまとめてみた
Java入門からアプリ開発まで未経験者が知っておくべき基礎を総ざらい

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

【Java】this について

this

Java(というよりプログラミングにおいて)の this について学んだことのアウトプットです。

thisとは

「this」 とはクラスの中(メソッド内)でインスタンスを扱うための特殊な変数です。

なぜ、thisをつかうのか

結論からいうと理由は

  • 「ローカル変数とフィールド変数を明確に区別するため」

ローカル変数とフィールド変数は別々のものなので、同じ名前でも作ることが可能なため、同じ名前で使われたときにどっちなのかわからなくなってしまいます。そのために、
「フィールド変数にはthisをつける」 という書き方がよく使われます。

どこに、 thisをつかうのか

上にも書きましたが、使う場所は

  • フィールド変数名の頭です「例:this.フィールド名」

また、this は クラス内のメソッドの定義の中でのみ使用できます。
thisはメソッドが呼ばれた時に、そのメソッドを呼び出しているインスタンスに置き換えられます。

thisを使ってみる

ポケモン図鑑をプログラムしてみました。
Pokemon.java の中で フィールド変数 を定義し、それをクラス内で this をつけて使いまわしているのがわかるかと思います。

//【Pokemon.java】
public class Pokemon {
    // フィールド変数
    public int number;
    public String name;
    public String category;
    public String type;
    public String description;

    public Pokemon(int number, String name, String category, String type, String description) {
        this.number = number;
        this.name = name;
        this.category = category;
        this.type = type;
        this.description = description;
    }

}

こちらが呼び出し側のクラスです。

//【Main.java】
public class Main {

    public static void main(String[] args) {
        // 呼び出し(new した後で、引数へ与えられた値を元に Pokemon が生成されます)
        Pokemon pikachu = new Pokemon(025, "ピカチュウ", "ねずみポケモン", "でんき",
                "つくる でんきが きょうりょくな ピカチュウほど ほっぺの ふくろは やわらかく よく のびるぞ");

        // 使用時
        System.out.print("No." + pikachu.number);
        System.out.println(" " + pikachu.name);
        System.out.print(pikachu.category);
        System.out.println(" " + pikachu.type + "タイプ");
        System.out.println(pikachu.description);

    }
}

実行結果

No.21 ピカチュウ
ねずみポケモン でんきタイプ
つくる でんきが きょうりょくな ピカチュウほど ほっぺの ふくろは やわらかく よく のびるぞ

参考文献・記事

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

Google Photos APIsでフォトアルバム

私は普段Android端末を使っており、奥さんと旅行などに行った際よく写真を撮ります。
アカウントの同期を行っている方は、Android端末で撮った写真が自動でGoogleのPhotosサーバにバックアップされると思います。
Google Photos APIsが公開され、Google Photosサーバから直接画像を取得出来るようになったため、これを利用してフォトアルバムを作ろうと思ったのが本記事のきっかけです。

はじめに

本記事では、Google Photos APIsを用いフォトアルバムを表示するWebアプリ(Spring-boot)を作成します。
ソースは、Git にあげております。
下図の様に指定期間範囲内に撮った画像を、指定の間隔で再生するよう実装しております。
sample1.png

スコープ

・Google Photos APIsの有効化
・認証情報の取得
・フォトアルバム Webアプリ開発

Google Photos APIsの有効化

公式の案内 Enable the API に従えば、3step程で有効化できます。

認証情報の取得

こちらも公式の案内 Request an OAuth 2.0 client ID が分かりやすく、こちらを参照すれば簡単に出来ます。
サンプルのソースでは、下記設定としております。
[Name]: OAuth client
[Authorized JavaScript origins]: http://localhost:8081
[Authorized redirect URIs]: http://localhost:8081/Callback

設定が完了したら、作成したOAuth 2.0 Client IDsのダウンロードボタンから認証ファイルを取得します。
WebサーバがGoogle Photos APIsを使用する際に必要となります。

フォトアルバム Webアプリ開発

認証

PhotosLibraryClientFactory.java
public static PhotosLibraryClient Client;

// ユーザーに手動で認証してもらうURL
public static String AuthUrl;

// [本記事]-[認証情報の取得]でダウンロードした認証ファイルのパス
public String mCredentialsPath;

public PhotosLibraryClientFactory(String credentialsPath) {
    this.mCredentialsPath = credentialsPath;
}

public boolean init() throws IOException, GeneralSecurityException {
    if(Client != null) return true;

    Credentials credentials = getUserCredentials();
    if(credentials == null) return false;

    PhotosLibrarySettings settings = PhotosLibrarySettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credentials))
            .build();

    Client = PhotosLibraryClient.initialize(settings);
    return true;
}

private Credentials getUserCredentials() throws IOException, GeneralSecurityException {
    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(new FileInputStream(this.mCredentialsPath)));
    String clientId = clientSecrets.getDetails().getClientId();
    String clientSecret = clientSecrets.getDetails().getClientSecret();

    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        GoogleNetHttpTransport.newTrustedTransport(),
        JSON_FACTORY,
        clientSecrets,
        ImmutableList.of("https://www.googleapis.com/auth/photoslibrary.readonly")
    )
        .setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR))
        .setAccessType("offline")
        .build();

    LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(LOCAL_RECEIVER_PORT).build();

    Credential credential = flow.loadCredential("user");
    if (credential != null && (
            credential.getRefreshToken() != null ||
            credential.getExpiresInSeconds() == null ||
            credential.getExpiresInSeconds() > 60)) {
        // 認証成功した場合は認証情報を返す
        return UserCredentials.newBuilder()
                .setClientId(clientId)
                .setClientSecret(clientSecret)
                .setRefreshToken(credential.getRefreshToken())
                .build();
    }

    // 手動で認証が必要な場合は、認証URLを取得する
    String redirectUri = receiver.getRedirectUri();
    AuthorizationCodeRequestUrl authorizationUrl = flow.newAuthorizationUrl().setRedirectUri(redirectUri);
    AuthUrl = authorizationUrl.build();
    return null;
}

手動での認証が必要な場合は、AuthUrl に認証URLを設定しています。
こちらはUI側で一度開き、ユーザーに手作業で認証作業を行ってもらう必要があります。

画像のURL取得

PhotosLibraryClientFactory.java
public static java.util.Date StartDate;
public static java.util.Date EndDate;

public Iterable<MediaItem> getPhotos() throws IOException, GeneralSecurityException {
    if(!init()) return null;

    Builder builder = SearchMediaItemsRequest.newBuilder();
    if(StartDate != null && EndDate != null) {
        DateFilter dateFilter = DateFilter.newBuilder().addRanges(DateRange.newBuilder()
                        .setStartDate(CommonUtils.convertToGoogleDate(StartDate))
                        .setEndDate(CommonUtils.convertToGoogleDate(EndDate)).build()).build();
        Filters filter = Filters.newBuilder().setDateFilter(dateFilter).build();
        builder.setFilters(filter);
    }

    SearchMediaItemsRequest request = builder.build();
    SearchMediaItemsResponse response = PhotosLibraryClientFactory.Client.searchMediaItemsCallable().call(request);
    return response.getMediaItemsList();
}

StartDateやEndDateが指定されていた場合、指定期間に撮った写真を検索します。
指定されていない場合は検索条件なしになりますが「このアルバムの画像を取得」のような検索の仕方も可能ですので、お好みの検索ロジックを組むと良いと思います。

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

【Java】変更に強いコード - 値オブジェクト1(Value Object)

変更に強いコードを書くための考え方を学びます

変更に強いコードの5つのポイント

  • 1. 変数名は説明になるようにつけよう
    • 入力、処理、出力の役割ごとに変数名を使う(説明用の変数を導入)
  • 2. 目的ごとに変数を用意しよう
    • 計算した結果に対して変数を用意して、使いまわさない
  • 3. コードは意味のある段落に分けよう
    • 入力、処理、出力に分ける
  • 4. コードのまとまり(段落)をメソッドとして独立させよう
    • 異なるクラスでの重複コードはなくそう
  • 5. 狭い関心ごとに特化したクラス(ドメインオブジェクト)にしよう
    • 凝集度up = あるクラスはある目的にのみ存在する
    • ex: 送料クラスの関心事は送料のみ

小さなクラスでわかりやすく安全に

  • 5つのポイントをおさえ、特定の関心に特化した小さなクラスを作る
    • メソッドは短く、クラスは小さく
    • クラスの可読性up、変更が簡単になる
  • 継承で共通機能は親クラスへ
    • 派生クラスでは差分のspecificな部分のみ書くことで最低限のコードにする
  • 値を扱う専用のクラス(値オブジェクト)を作る

値オブジェクト(Value Object)とは?

  • データを使った業務ロジック:判断/加工/計算
  • このデータの種類ごとに、値を扱う専用のクラスを作るということ。
    • 専用の型を用意(クラスやインターフェース)
    • 専用の型を使うと不適切な値が混入するバグを防げる!
    • 変更時にも、クラスやインターフェース名を手掛かりに変更できる
    • 値オブジェクトは非常に重要

値オブジェクト実践

  • 以下のコードをリファクタする時。。?
int unitPrice = UNIT_PRICE;
int basePrice = unitPrice * quantity;
int basePriceWithTax = (int)(basePrice*TAX);
  • intは基本型なので使わないほうがいい
    • ここで値の種類ごとに専用の型を用意!=値オブジェクト
    • valuesクラスにもつint 型のvalueを保存
Project Root
└─src
    └─ main
        └─ java  
            └─ Main
        └─ values
            └─ Price
            └─ Quantity
            └─ Tax

Price / Quantity / Tax型を作る

  • 基本型ではなく専用型を用意し、intやfloatをPrice、Quantity、Taxに変更
  • getter(ここではgetValue())で値を返し、外から参照できるようにする
  • 処理中身はintなので計算中ではintになっているが、
  • 入力/処理/出力の処理のみintで、クラスの中に閉じ込め、返すのはPrice型
    • →変更をここだけに閉じ込めることができる?
Price.java
package values;

public class Price {
    private int value;
    public Price(int value){
        this.value = value;
    }
    public Price multiple(Quantity quantity){
        return new Price(this.value * quantity.getValue());
    }
    public Price getPriceWithTax(Tax tax){
        //tax.getValueはfloatなのでCast
        int priceWithTax = (int)(this.value * tax.getValue());
        //新しいオブジェクトを返す
        return new Price(priceWithTax);
    }

    public int getValue() {
        return value;
    }
}
Quantity.java
package values;

public class Quantity {
    private int value;

    public Quantity (int value){
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}


Tax.java
package values;

public class Tax {
    private float value;

    public Tax (float value){
        this.value = value;
    }
    public float getValue() {
        return value;
    }
}


Main.java
import values.Price;
import values.Quantity;
import values.Tax;

public class Main {

    private final static int UNIT_PRICE = 3000;
    private final static float TAX = 1.1f;

    public static void main(String[] args) {
       //コンストラクタで初期化
        var instance = new Main();
        var quantity = new Quantity(3);
        var totalCost = instance.getTotalCost(quantity);
        System.out.println(quantity.getValue());

        System.out.println("totalCost" + totalCost.getValue());
    }

    Price getTotalCost(Quantity quantity){
        Price unitPrice = new  Price(UNIT_PRICE);
        Price basePrice = unitPrice.multiple(quantity);
        System.out.println("basePrice: "+basePrice.getValue());
        Tax tax = new Tax(TAX);
        Price basePriceWithTax = basePrice.getPriceWithTax(tax);

/*リファクタ前
          int unitPrice = UNIT_PRICE;
          int basePrice = unitPrice * quantity;
          int basePriceWithTax = (int)(basePrice*TAX);
*/
        return basePriceWithTax;
    }

}

関心事を外に閉じ込められてない例

  • 上のMain.javaでの操作はこんな風に書くこともできる。が。。。。
  • 値オブジェクトを使いながら、実際の処理を外に書いてしまっている? (= Priceクラスを使う側で処理を書いている)
  • なので、せっかく値オブジェクトを使っているのに、関心事を外に閉じ込められてないということになります。。
//よくない例
Price unitPrice = new Price(UNIT_PRICE);
int basePrice = unitPrice.getValue() * quantity.getValue();
Tax tax = new Tax(TAX);
int basePriceWithTax = (int) (basePrice * tax.getValue());
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】初心者のためのConcurrentModificationExceptionについて【Java Silver対策】

はじめに

  • この記事は駆け出しエンジニアが書いています。間違いがありましたら遠慮なくご指摘いただけると幸いです。
  • この記事はJavaのArrayListにおけるConcurrentModificationExceptionについて超ざっくりと解説します

この記事の対象者について

  • Java Silver勉強中で、どういう場合に発生するのか確認したい方
  • Javaでシングルスレッドのコードを書いていたらConcurrentModificationExceptionが出たのでとりあえず解決したい方。

*詳しい解説はすべて省いて、初心者の方でも分かりやすいように書いています。

ConcurrentModificationExceptionについて

まだまだ駆け出しの筆者であるが、Java Se8 Silverの問題集をやっているとこんな問題が出た

*例

java.
        //配列を宣言し「ABCDE」を要素として配列に格納
        List<String> list = new ArrayList<String>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        list.add("E");

        //拡張for文でリストの中身を一つづつ取り出し、
        //「B」であればリストの中身の「B」を削除
        //それ以外はコンソールに表示させる
        for (String string : list) {
            if(string.equals("B")) {
                list.remove(string);
            } else {
                System.out.println(string);
            }
        }

これを実行すると例外が発生する

実行結果

A
Exception in thread "main" java.util.ConcurrentModificationException

Aだけ表示されていることからremoveメソッドで例外が発生しているのが分かると思う。

なぜ発生するのか

結論から言うと、拡張for文で『要素の取り出し中にremoveメソッドを実行したから』となる。
これはマルチスレッド環境下で片方が要素を取り出している最中にもう片方が同じリストから要素を削除していまうことがないようにするために投げられるの例外なのだが、今回のようにシングルスレッド環境下でも発生する。
初心者の方は遭遇する機会がほとんどないため、理解するに見合うだけのリターンが少ない。よく分からなければ、そういう例外があるんだと漠然と思っていただければ十分だと思う。

【Java Silver対策】どのタイミングなら発生しないのか

これも結論から言うと「listの要素の最後から2番目をremoveする場合は発生しない」となる

先述の問題に近い例題がある

java.
        List<String> list = new ArrayList<String>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        list.add("E");

        for (String string : list) {
            if(string.equals("D")) {
                list.remove(string);
            } else {
                System.out.println(string);
            }
        }

拡張for文の中のequalsメソッドの中身を"D"に変えただけのコード。この実行結果はというと

実行結果

A
B
C

このように例外は発生しない。(なぜ"E"が表示されないかは別の問題であるためこの記事では扱わない)

このようにlistの要素である「ABCDE」の中の最後から2番目である"D"をremoveする場合は例外は発生しない。
逆に言えば"A""B""C""E"は全て例外が発生してしまう。

これもそういうものだと覚えてしまおう。

ConcurrentModificationExceptionが発生しないようにするためには

実際にコードを書いているときにはあまり見ることはないだろうが、対応策として何例か考えたのでここに記述していく
通常は対応策1で十分だと思うが、どうしても要素を取り出しながら削除したいという方は「対応策3」「対応策4」を参考していただきたい。

対応策1-先に削除しておく-

java.
        List<String> list = new ArrayList<String>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        list.add("E");

        list.remove("B");
        for (String string : list) {
            System.out.println(string);
        }
実行結果

A
C
D
E

とても単純だが拡張for文の前で要素の削除を行う。
どうしても要素の中身を取り出しながら削除したい、という人以外はこれで大丈夫だと思う

*以下の対応策からListの要素の追加まで省略

対応策2 -通常のfor文を使う(非推奨)-

java.
        for (int i = 0; i < list.size(); i++) {
            if(list.get(i).equals("B")) {
                list.remove("B");
            } else {
                System.out.println(list.get(i));
            }
        }
実行結果

A
D
E

繰り返し処理の中で要素を削除したいという人は通常のfor文であれば例外は発生しない。
しかし、実行結果から分かる通り、要素の順番がずれるために実行結果は意図しないものになりがち。オススメはしない。

対応策3 Iterator(イテレータ)を使う

java.
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String string = iterator.next();
            if(string.equals("B")) {
                iterator.remove();
            } else {
                System.out.println(string);
            }
        }
実行結果

A
C
D
E

イテレータの詳細も省くがイテレータには「要素を取り出しながら削除される」ことを想定されているため、イテレータを使うことで例外を回避できる。
どうしても要素を取り出しながら削除したい人はこれで解決すると思う。

対応策4 -removeIfメソッドを使う

java.
        list.removeIf((String string) ->{
            if(string.equals("B")) {
                return true;
            } else {
                System.out.println(string);
                return false;
            }
        });
実行結果
A
C
D
E

先述した通り、要素を取り出しながら削除する場合にはイテレータを使うしかないが、removeIfメソッドもイテレータを取り出してくれる。
ラムダ式で条件を指定し、trueであれば要素を削除してくれるメソッドだ。
コード内でイテレータを呼び出すよりも幾分かスッキリしたコードになるためこちらのほうが使いやすいかもしれないが、ラムダ式(->)は初心者には分かりづらいため、使用するときはコメントを書いておくといいかもしれない。

備考ではあるが、Java Silverを受ける方はremoveIfメソッドは試験範囲のため、覚えておいても損はない。

最後に

ConcurrentModificationExceptionについて説明していきましたが、初心者の方はあまり見ることのないエラーのため、試験や、実際コードを書いている最中に見かけると戸惑ったかもしれません。
筆者もこのエラーについて調べた時に日本語で正しい解説をされているものが見つからなかったため、かなり戸惑いました。
ただ、あまり見かけることがないゆえに、中身を理解するよりもこの記事で「そういうものだ」と暗記してしまうのが楽かなと思います。
この記事が皆様のお役に立てたのなら幸いです。

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

JSP + Apache FOP で動的に PDF を作成して Web サイトに表示してみるサンプル

JSP サイトを管理している知り合いが「PDF を動的に作成して表示したい」と言っていたので、ちょっと試してみました。

Apache FOP

PDF を動的に生成するライブラリは多くあります。有料のものですと iText とか、複雑な帳票に向いていそうな SVF とか。

でも今回はあまり複雑でない PDF を作成するようですし、OSS での実装を検討してみました。コスト削減できますし、何かあったらソースコード読んで確認できるのは嬉しいものです。

まずチェックしたのが Apache PDFBox なのですが、あまりにプリミティブといいますか、そのまま作りこんでしまうとデザインの更新が難しくなりそうなので、パス。上にもうひとつコンテンツ作成レイヤが必要な印象です。

そして見つけたのが XSL ベースでコンテンツを作成する Apache FOP です。XHTML の知識でコンテンツ作成が可能!これに PDF 形式の出力機能がありました。

実際の Web 画面

以下のような入力画面で適当な文字列を入力して「Submit」すると
image.png
入力した日時や文字列を含んだ PDF を自動的に生成して表示します。
image.png

開発環境

今回利用した開発環境は素の Eclipse 2020-09 (4.17.0) の Java EE パースペクティブで、サーバーに Tomcat v8.5 を追加指定したものです。まあ QRCodeの時 と同じですね。

赤枠が今回のために追加、作成したファイルになります。
image.png
(1) ですが Apache FOP のダウンロードページ からダウンロードしたもの (私の場合は fop-2.5-bin.zip) からコピーします。fop.xconf ファイルもお忘れなく!

(2) と (3) の3つのファイルが今回の開発対象です。

実際のコード

入力ページ

入力ページは通常の html ページです。Bootstrap のテンプレート にフォーム要素を追加しただけの、非常にシンプルなものです。

QRCodeの時 のコードとほぼ同じ、タイトルと action を変えただけですね。手抜きですいません。。

WebContent/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <title>Simple Apache FOP sample with JSP</title>
  </head>
  <body>

<div class="container">
    <h2>Simple Apache FOP sample with JSP</h2>
    <form action="/test02/pdfServlet">
        <div class="form-group">
            <label for="i_url">Target URL</label>
            <input type="text" class="form-control" id="i_url" name="i_url">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
</div>

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </body>
</html>

Servlet ページ

さてこちらは、入力を受けて PDF を動的に生成している部分です。Apache FOP: Servlets にあるサンプルコードを、わりと大胆に流用しています。

pdfServlet.java
import java.io.*;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopConfParser;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FopFactoryBuilder;
import org.apache.fop.apps.MimeConstants;

@WebServlet("/pdfServlet")
public class pdfServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private TransformerFactory tFactory = TransformerFactory.newInstance();

    public pdfServlet() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            String i_url = request.getParameter("i_url");
            String xmlData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                    + "<?xml-stylesheet type=\"application/xml\"?>"
                    + "<users-data>"
                    + "<date>" + (new Date()).toString() + "</date>"
                    + "<body>" + (i_url == null ? "EMPTY" : i_url) + "</body>"
                    + "</users-data>";
            FopConfParser fopConfParser = new FopConfParser(new File(this.getServletContext().getRealPath("/WEB-INF/fop.xconf")));
            FopFactoryBuilder fopFactoryBuilder = fopConfParser.getFopFactoryBuilder();
            FopFactory fopFactory = fopFactoryBuilder.build();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
            Source xsl = new StreamSource(this.getServletContext().getRealPath("/WEB-INF/sample.xsl"));
            Transformer transformer = tFactory.newTransformer(xsl);
            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(new StreamSource(new StringReader(xmlData)), res);
            response.setContentType("application/pdf");
            response.setContentLength(out.size());
            response.getOutputStream().write(out.toByteArray());
            response.getOutputStream().flush();
        } catch (Exception ex) {
            throw new ServletException(ex);
        }
    }

}

コード内部でデータとなる xmlData 文字列を生成して、sample.xsl 変換設定をもとに、FOP でコンテンツ変換を実施して PDF 形式で出力しています。

変換設定

今回の sample.xsl は、このへん の一部をコピペして、エイヤ!と作成しました。なので生成された PDF の見た目は酷いものです… 実際に使用する時は ガイド に従ってしっかり作りましょう。

sample.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
    <xsl:output encoding="UTF-8" indent="yes" method="xml" standalone="no" omit-xml-declaration="no"/>
    <xsl:template match="users-data">
        <fo:root language="ja">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="A4-portrail" page-height="297mm" page-width="210mm" margin-top="5mm" margin-bottom="5mm" margin-left="5mm" margin-right="5mm">
                    <fo:region-body margin-top="25mm" margin-bottom="20mm"/>
                    <fo:region-before region-name="xsl-region-before" extent="25mm" display-align="before" precedence="true"/>
                </fo:simple-page-master>
            </fo:layout-master-set>
            <fo:page-sequence master-reference="A4-portrail" font-family="Meiryo,Hiragino">
                <fo:static-content flow-name="xsl-region-before">
                    <fo:table table-layout="fixed" width="100%" font-size="10pt" border-color="black" border-width="0.4mm" border-style="solid">
                        <fo:table-column column-width="proportional-column-width(60)"/>
                        <fo:table-column column-width="proportional-column-width(30)"/>
                        <fo:table-body>
                            <fo:table-row>
                                <fo:table-cell text-align="center" display-align="center">
                                    <fo:block font-size="150%">
                                        <fo:basic-link external-destination="https://qiita.com/yamachan360">PDF サンプル</fo:basic-link>
                                    </fo:block>
                                    <fo:block space-before="3mm"/>
                                </fo:table-cell>
                                <fo:table-cell text-align="right" display-align="center" padding-right="2mm">
                                    <fo:block>
                                        Date: <xsl:value-of select="date"/>
                                    </fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                        </fo:table-body>
                    </fo:table>
                </fo:static-content>
                <fo:flow flow-name="xsl-region-body" border-collapse="collapse" reference-orientation="0">
                    <fo:block><xsl:value-of select="body"/></fo:block>
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>
</xsl:stylesheet>

<xsl:value-of select="date"/> とか <xsl:value-of select="date"/> の部分にデータ用 XML の対応する要素が埋め込まれます。このあたりを深く知りたい場合は XSL や XSLT のワードでググってみましょう。

文字化けへの対応

今回のサンプルですが、最初は日本語が文字化けしていました。いろいろ試して、とりあえず以下の2点の修正で表示されました。

まずは fop.xconf ファイルです。配布されたこのファイルはデフォルト値なので、そのまま読み込んでも動作は一緒!…ではありませんでした。理由までは調べていないのでわかりませんが、この設定ファイルをちゃんと配置し、実行時に読み込むことで文字化けが解消できました。

これに加えて、変換ファイルで日本語のフォント指定が必要でした。とりあえず今回は、以下のような手抜き設定で逃げています。
image.png

ライセンス

この投稿に含まれる私の作成した全てのコードは Creative Commons Zero ライセンスとします。自由にお使いください。あ、sample.xsl はコピペ含むので、ちゃんと書き直したほうが良さそう。

Enjoy!

以上、Servlet を用いて Web サイトで、PDF の動的生成を試してみました。これをベースに、いろいろ機能を追加して遊んでみてください。

ではまた!

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

クラス・フィールド変数

クラス・フィールド変数

Javaの勉強で、クラス・フィールド変数 について学んだことをアウトプットしていきます。

クラス

クラスとは、「 データ 」と「 処理 」の集まりです。
Javaのプログラムは、クラスを複数組み合わせてプログラミングしていく。
Javaのプログラミングは クラスのプログラミング と、呼び出し側のプログラミングの2つの作業に分かれる。

クラスのプログラミング

クラスには、そのクラスが持つ特徴や情報などのデータと、そのクラスで行いたいタスク、TODO、ふるまいなどの処理を書きます。

呼び出し側のプログラミング

クラスが完成したらクラスのインスタンスを生成し、メソッドの呼び出しを記述する。

クラスのプログラミングの注意点は

  • 必ずpublic class クラスと記述すること。
  • { }の中にデータ処理を記述すること。

この囲まれたコードのまとまりをブロックという。

// ここからブロック範囲=============================
//クラス(最初の一文字は半角英大文字)
public class HelloWorld {
    // フィールド
    private String printHello;
    // コンストラクタ
    public HelloWorld() {
        printHello = "Hello World";
    }
    // メソッド
    public void print() {
        System.out.println(printHello);
    }
}
// ここまでブロック範囲=============================

【パスカルケース】Javaのクラス名の命名規則

クラス名の最初の一文字は半角英大文字とすること」とコード内にも書きましたが、つまりはパスカルケース(Pascal記法)で書く ということです。
パスカルケースのルールは

  • 先頭は大文字
  • 言葉の区切りは大文字
  • それ以外は小文字

となります。つまり 「JavaScript」, 「MyName」, 「AppleJuice」 みたいな書き方です。

クラス名の付け方に悩んだらcodec などのサービスがおすすめですのでつかってみてください。他にも良いサービスがあったらぜひ!

mainメソッド

コーディングしたクラスを動かすためには、自クラス又は他のクラス内に mainメソッド を記述しないといけません。
そのmainメソッドの中で「 new 」演算子を使用し、メモリ上にクラスが扱うデータ用の領域を新規に確保します。

クラスのインスタンス化

このようにして クラス という 「型」 から 「具体的な実体」 に生成されたものを インスタンス と呼びます。
そして、それらの一連の流れを インスタンス化 とよびます。

サンプルコード

まずこちらが、「データ」や「処理」を書いた クラス側のコード です。
最初に言った「クラスのプログラミング」に当たる部分です。

//クラス
public class HelloWorld {
    // フィールド
    private String printHello;

    // コンストラクタ
    public HelloWorld() {
        printHello = "Hello World";
    }

    // メソッド
    public void print() {
        System.out.println(printHello);
    }
}

下記がクラスの呼び出し側の「プログラムを実行するためのクラス」のコードです。
このままではまだ型の状態なので、こちらでプログラムを実行できる状態、つまり インスタンス化 します。

//プログラムを実行するためのクラス(mainメソッドを持つクラス)
public class Main {
    // mainメソッド
    public static void main(String[] args) {
        // HelloJavaクラスのインスタンスを生成して、HelloJavaクラス型の変数printHelloWorldに保持する
        HelloWorld printHelloWorld = new HelloWorld();
        // 生成したインスタンスより、HelloWorldクラスのprintメソッドを呼び出す
        printHelloWorld.print();
    }
}

実行結果
image.png

【フィールド】フィールド変数

フィールドまたは フィールド変数 とは、クラス内の 変数 のことです。
先程のコードのここの部分です。

//クラス
public class HelloWorld {
    // フィールド ここの部分
    private String printHello;
    // コンストラクタ
    public HelloWorld() {

フィールドの書き方

  • フィールドを宣言するとき、「 public 」「 private 」などの アクセス修飾子 を記述します。
  • アクセス修飾子を省略することが可能
  • 初期化を省略することが可能。(その場合は、デフォルト値が入ります。)

フィールドとして宣言した変数は、コンストラクタやメソッド内で変数を宣言することなく、そのクラス内であればどこからでも値を参照したり更新したりすることができます。

アクセス修飾子

修飾子とは

変数名、関数名の前に付与する単語 のこと。

アクセス修飾子とは

*「クラスや変数がどこからアクセス可能であるか」 *の公開範囲 を決定するための修飾子です。

アクセス修飾子の種類

こちらがアクセス修飾子種類と内容です。
以下の表では下に行くほどアクセスの条件が厳しくなっていきます。

アクセス修飾子 内容
public すべてのクラスからアクセスできる
protected 現在のクラスとサブクラスからアクセスできる
なし 現在のクラスと同じパッケージのクラスからアクセスできる
private 現在のクラスからだけアクセスできる

参考文献

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

【Java】クラス・フィールド変数

クラス・フィールド変数

Javaの勉強で、クラス・フィールド変数 について学んだことをアウトプットしていきます。

クラス

クラスとは、「 データ 」と「 処理 」の集まりです。
Javaのプログラムは、クラスを複数組み合わせてプログラミングしていく。
Javaのプログラミングは クラスのプログラミング と、呼び出し側のプログラミングの2つの作業に分かれる。

クラスのプログラミング

クラスには、そのクラスが持つ特徴や情報などのデータと、そのクラスで行いたいタスク、TODO、ふるまいなどの処理を書きます。

呼び出し側のプログラミング

クラスが完成したらクラスのインスタンスを生成し、メソッドの呼び出しを記述する。

クラスのプログラミングの注意点は

  • 必ずpublic class クラスと記述すること。
  • { }の中にデータ処理を記述すること。

この囲まれたコードのまとまりをブロックという。

// ここからブロック範囲=============================
//クラス(最初の一文字は半角英大文字)
public class HelloWorld {
    // フィールド
    private String printHello;
    // コンストラクタ
    public HelloWorld() {
        printHello = "Hello World";
    }
    // メソッド
    public void print() {
        System.out.println(printHello);
    }
}
// ここまでブロック範囲=============================

【パスカルケース】Javaのクラス名の命名規則

クラス名の最初の一文字は半角英大文字とすること」とコード内にも書きましたが、つまりはパスカルケース(Pascal記法)で書く ということです。
パスカルケースのルールは

  • 先頭は大文字
  • 言葉の区切りは大文字
  • それ以外は小文字

となります。つまり 「JavaScript」, 「MyName」, 「AppleJuice」 みたいな書き方です。

クラス名の付け方に悩んだらcodec などのサービスがおすすめですのでつかってみてください。他にも良いサービスがあったらぜひ!

mainメソッド

コーディングしたクラスを動かすためには、自クラス又は他のクラス内に mainメソッド を記述しないといけません。
そのmainメソッドの中で「 new 」演算子を使用し、メモリ上にクラスが扱うデータ用の領域を新規に確保します。

new演算子

new演算子は、Javaのクラスをインスタンス化するために利用されます。
new演算子に続けて、クラスのコンストラクタを呼び出して実行するコードを記述すると、クラスのインスタンスが生成されます。

new演算子の使い方は以下のとおりです。

new コンストラクタ;

コンストラクタはクラスと同じ名前を付ける決まりになっています。
なので、実際には以下のように書いてください。

new クラス名();

システム開発の現場では、インスタンスを生成することを「newする」とか言ったりするみたいですが。。

クラスのインスタンス化

このようにして クラス という 「型」 から 「具体的な実体」 に生成されたものを インスタンス と呼びます。
そして、それらの一連の流れを インスタンス化 とよびます。

サンプルコード

まずこちらが、「データ」や「処理」を書いた クラス側のコード です。
最初に言った「クラスのプログラミング」に当たる部分です。

//クラス
public class HelloWorld {
    // フィールド
    private String printHello;

    // コンストラクタ
    public HelloWorld() {
        printHello = "Hello World";
    }

    // メソッド
    public void print() {
        System.out.println(printHello);
    }
}

下記がクラスの呼び出し側の「プログラムを実行するためのクラス」のコードです。
このままではまだ型の状態なので、こちらでプログラムを実行できる状態、つまり インスタンス化 します。

//プログラムを実行するためのクラス(mainメソッドを持つクラス)
public class Main {
    // mainメソッド
    public static void main(String[] args) {
        // HelloJavaクラスのインスタンスを生成して、HelloJavaクラス型の変数printHelloWorldに保持する
        HelloWorld printHelloWorld = new HelloWorld();
        // 生成したインスタンスより、HelloWorldクラスのprintメソッドを呼び出す
        printHelloWorld.print();
    }
}

実行結果
image.png

【フィールド】フィールド変数

フィールドまたは フィールド変数 とは、クラス内の 変数 のことです。
先程のコードのここの部分です。

//クラス
public class HelloWorld {
    // フィールド ここの部分
    private String printHello;
    // コンストラクタ
    public HelloWorld() {

フィールドの書き方

  • フィールドを宣言するとき、「 public 」「 private 」などの アクセス修飾子 を記述します。
  • アクセス修飾子を省略することが可能
  • 初期化を省略することが可能。(その場合は、デフォルト値が入ります。)

フィールドとして宣言した変数は、コンストラクタやメソッド内で変数を宣言することなく、そのクラス内であればどこからでも値を参照したり更新したりすることができます。

アクセス修飾子

修飾子とは

変数名、関数名の前に付与する単語 のこと。

アクセス修飾子とは

*「クラスや変数がどこからアクセス可能であるか」 *の公開範囲 を決定するための修飾子です。

アクセス修飾子の種類

こちらがアクセス修飾子種類と内容です。
以下の表では下に行くほどアクセスの条件が厳しくなっていきます。

アクセス修飾子 内容
public すべてのクラスからアクセスできる
protected 現在のクラスとサブクラスからアクセスできる
なし 現在のクラスと同じパッケージのクラスからアクセスできる
private 現在のクラスからだけアクセスできる

参考記事・文献

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