Java ミニゲーム ソース付き 〜テスト駆動開発:文言出力部品を作る〜

今回は、RPGゲームでよくある文字を1文字ずつ読めるスピードで出力する部品を作ります。

今まで作成したテキストRPGゲームに追加します。
現状は、こんな感じで動きます。

文字の出力は、一括で出力するのでゲームっぽくありません。どちらかというとログ出力っぽいです(笑)

文字出力部品を作る

現状は、System.out.println()を使用しています。これは引数にある文字列を標準出力に出力します。
標準出力 = System.outになります。

問題

どーやって1文字ずつ出力するか?
とりあえずは、テスト駆動開発にしますので、テストケースを作成します。

毎度おなじみJUnit

JUnitを使用してテストクラスを動かします。

作成手順

  1. Eclipseのプロジェクトに「test」フォルダを作成する
  2. テストするクラスと同じパッケージを作成する
  3. テストするクラスの後にTestをつけたクラスを作成する
  4. 〜Testクラスにテストケースを作成する
具体的に。。。

実装した時の動画を作成しました。

詳細は以下のようになります。
上の手順4以降の記載になります。

JUnitをビルドパスに追加


動画にもあるのですが、上のような画面でJUnitを追加します。細かい部分は動画にありますので参照ください。

テストケースを作る

今回のテストケースはコンソールにゆっくりと表示できれば良いので、動かすだけになります。
故に、コードは下のようになります。

public class PrintUtilsTest {
    @Test
    public void testPrint01() {
        String mes = "アイウエオカキクケコ。。。。";
        try {
            PrintUtils.print(mes);
        } catch (InterruptedException e) {
            fail(e.getMessage());
        }
    }
}

今回の作成するクラスは、PrintUtilsクラスです。テストクラスは上のコードにある通りです。

作成するクラスをガワだけ作ったら、あとは、テストケースで呼び出してやる処理を実装してやるだけです。

あとは、実装→起動→修正→確認を繰り返して終了です。

シンプルなもんです(笑)

JUnitを使用したときの再生リストへのリンクです。

JUnitで10進数を2進数に変換する処理を実装

テストファーストで、補数算出メソッドの実装

EclipseでJUnitの起動設定~テストクラスの作成1

EclipseでJUnitの起動設定~テストクラスの作成2

Java テスト駆動で引き算のメソッドを作る

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

オブジェクト指向関連ページ

  1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
  2. オブジェクト指向の概念2〜クラスとは〜

Java ミニゲーム ソース 〜じゃんけんゲーム in Console 〜

今回は、Javaで作成するミニゲームと題して下のゲームを作成したいと思います。

じゃんけんゲーム

毎度おなじみ「じゃんけんゲーム」シンプルに、楽しめるゲームとして最もポピュラーなものだと思います。
それをJava言語を使ってコンソールゲームにしました。
しかし、バグもあります。。。

Mainメソッド

メインメソッドは変更しません。前回のコードはこちらから見れます。※前回の記事です。

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String input = scan.next();

        FirstCls first = new FirstCls();
        if ("exe".equals(input)) {
            first.execute(scan);
        }
        boolean isNumber = first.isNumberString(input);
        if (isNumber) {
            System.out.println(input + "は数字です。");
        } else {
            System.out.println(input + "は数字ではありません。");
        }
    }
}

今回の実装はFirstCls#executeから始めます。。。

実際に作ってみた

ここで、Mainメソッド変更しません。とはじめに記載したのではじめが分かりづらいです。起動したら「exe」と入力する必要がありました(笑)
そして、実装したコード(FirstCls)は下のようなコードです。
※使用していないゴミコードは削除しています。全体はこちらのGithubで見ることができます。

public class FirstCls {
    /** システム(OS)で使用する改行コード */
    private static final String SEPARATOR = System.getProperty("line.separator");
    /** プレーヤの勝利フラグ */
    private static final Integer WIN = 1;
    /** プレーヤの敗北フラグ */
    private static final Integer LOOSE = 2;
    /** プレーヤの引き分けフラグ */
    private static final Integer DRAW = 3;

    public void execute(Scanner scan) {
        System.out.println("*** EXECUTEを起動します ***");
        // じゃんけんの手のマップ
        Map<String, String> map = createJankenTe();
        // じゃんけんの勝敗マップ
        Map<String, Integer> judgeMap = createJudgement();
        Random rnd = new Random();
        while(true) {
            System.out.print("じゃんけん。。。");
            String player = scan.next();
            int cpu = rnd.nextInt();
            System.out.println("あなた:" + map.get(player) + " CPU:" + map.get(String.valueOf(cpu)));
            if (judgeMap.get(player + cpu) == WIN) {
                System.out.println("You win!");
            } else if (judgeMap.get(player + cpu) == LOOSE) {
                System.out.println("You loose!");
            } else if (judgeMap.get(player + cpu) == DRAW) {
                System.out.println("Draw! one more time ...");
                continue;
            }
            System.out.println(SEPARATOR + "もう一度やる?" + SEPARATOR + "y: もう一度 n: やめる");

            String more = scan.next();
            if ("n".equals(more)) {
                break;
            } 
        }
    }

    /**
     * じゃんけんの手を設定したMapを返却する
     * @return Map
     */
    private Map<String, String> createJankenTe() {
        Map<String, String> map = new HashMap<>();
        map.put("1", "グー");
        map.put("2", "チョキ");
        map.put("3", "パー");
        return map;
    }

    /**
     * じゃんけんの勝敗パターンをMapに設定
     * @return Map
     */
    private Map<String, Integer> createJudgement() {
        Map<String, Integer> map = new HashMap<>();
        map.put("12", WIN);
        map.put("23", WIN);
        map.put("31", WIN);
        map.put("21", LOOSE);
        map.put("32", LOOSE);
        map.put("13", LOOSE);
        map.put("11", DRAW);
        map.put("22", DRAW);
        map.put("33", DRAW);
        return map;
    }
}

こんな感じです。

処理内容

Mainメソッドは。。。割愛します。
FirstCls#execute()では、ジャンケンで取りうる手(グー・チョキ・パー)をMapに設定しておき、1~3の入力に対応してそれぞれの手を表示、じゃんけんの判定にしようします。

勝敗の判定には、全てのパターンをMapに記憶してプレーヤーの手とCPUの手を連結→プレーヤー:グー、CPU: チョキであれば"12"で勝敗マップからは「1」が取得できます。

1は勝利判定のフラグです。
詳細は上のコードに書いてありますので。。。

近くに、これの解説動画も作成いたします。

問題

バグがあるのですが、それはどこでしょうか?
<ヒント>
動かしてみるとわかります。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

Java Basic Class 〜クラスを作ってクラスを理解する〜

久しぶりに、Javaの基本をやろうと思います。
Javaの基本といってもいろいろあるので、クラスの作り方をメインにやろうと思います。

早速作りましょう

とりあえずは、動かすのにメインメソッドが必要ですので、このメインメソッドを作成します。

作り方は、今までにやってきたので割愛します。

ここでのポイントは、MainメソッドのあるMainクラスを作成すると言うところです。

my.sample.Main

public class Main {
    public static void main(String[] args) {
        // 中身はまだない。。。
    }
}

このクラスから処理が始まります。

何をするか考える

設計の工程になります。。。

Step1

しかし、初めての人にもわかるよう、ハローワールドを実装します。

public class Main {
    public static void main(String[] args) {
        //  ハローワールド
        System.out.println("Hello World!");
    }
}
Step⒉

しかしこれでは、物足りない。。。
プログラム引数に、値を渡して、プログラム引数に値がある場合に表示すると言うものにしましょう

public class Main {
    public static void main(String[] args) {
        //  プログラム引数に値があるときは表示する
        if (args.length != 0) {
            System.out.println("プログラム引数:" + args[0]);
        }
        System.out.println("Hello World!");
    }
}
Step3

いやいや。。。プログラム引数が1つだけとは限らないので、そいつをどうにかしよう。

つまり、プログラム引数の数だけ表示しよう!

public class Main {
    public static void main(String[] args) {
        //  プログラム引数に値があるときは表示する
        if (args.length != 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("プログラム引数[" + i + "]: " + args[0]);
            }
        }
        System.out.println("Hello World!");
    }
}
Step4

次は、文字表示だけじゃ面白くないから計算をしよう。

public class Main {
    public static void main(String[] args) {
        //  プログラム引数に値があるときは表示する
        if (args.length != 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("プログラム引数[" + i + "]: " + args[0]);
            }
        }
        System.out.println("1 + 1 = " + (1 + 1));
    }
}

Step5

ここまできたら、クラスも作ってみよう。
しかし、今まで作成したコードは、書き換えてきたから、また呼び出したい時には、どうしたら良いだろうか?

クラスを使う

クラスを使うと、呼び出すメソッドを変えてやるだけで今までの作成したコードを実行できます。

Step1〜4までに作成したコードはどこかにいってしまいましたが、今後作成するプログラムは残せるように、クラスを作りながら進みます。

Step6: クラスを作る

とりあえず、今メインメソッドにある処理は邪魔なので、作成したクラスへ移動してしまいます。
新たに作成したクラスはFirstClsです、パッケージが違うのでインポートする必要があります。

そして、Mainクラスはこのようになりました。

public class Main {
    public static void main(String[] args) {
        FirstCls first = new FirstCls();
        first.printSomething(args);
    }
}

FirstCls

public class FirstCls {
    public void printSomething(String[] args) {
        //  プログラム引数に値があるときは表示する
        if (args.length != 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("プログラム引数[" + i + "]: " + args[0]);
            }
        }
        System.out.println("1 + 1 = " + (1 + 1));
    }
}

処理の結果は変わりません。

そして、Eclipseでのパッケージ構成は下のようになっています。

ここからです。

Step7: クラスを使うメリット

現状、作成したプログラムは、
なんとなく作成した、意味のない処理が動いている状態です。

これは、とりあえずFirstClsによけておき、入力を受け取るプログラムを追加したいともいます。
これは、全ての処理の始まりになりますので、Mainクラスに実装します。実際に動かした時の動画です。

Step8: 標準入力を受ける

今までに処理結果をコンソールに表示していました。
ここは、「標準出力」と言う場所で、System.outで表現される(メモリ)領域です。

とりあえずここに表示するSystem.out.printメソッドで今まで表示を行なっていました。

次は、逆に入力、出力に対して入力を受け付けるプログラムを書きます。
Mianクラスに実装します。

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String input = scan.next();

        FirstCls first = new FirstCls();
        first.printSomething(args);
    }
}

そして、取得した標準入力をコントロールするためのメソッドをFirstClsに作成します。

public class FirstCls {

    public void handleInput(String input) {
        System.out.println("入力値: " + input);
    }

    /** 使用しないので下のアノテーションをつける */
    @Deprecated
    public void printSomething(String[] args) {
        //  プログラム引数に値があるときは表示する
        if (args.length != 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("プログラム引数[" + i + "]: " + args[i]);
            }
        }
        System.out.println("1 + 1 = " + (1 + 1));
    }
}
Step9: まずはハローワールド

入力値をコンソールに表示するプログラムを作成しました。

上のような出力ができます。

作成したhandleInput()をそのままにしておき、次は引数が数字かどうか判定するメソッドを作成します。

public boolean isNumberString(String str) {
    // [0-9]は正規表現と言います。
    boolean isNumber = str.matches("[0-9]");
    return isNumber;
}

そして、これを初めに作ったメソッドから呼び出すようにします。

public void handleInput(String input) {
    System.out.println("入力値: " + input);
    if (isNumberString(input)) {
        System.out.println(input + "は、数字です");
    } else {
        System.out.println(input + "は、数字ではありません");
    }
}

こんな感じの実行結果が見れます。

作成したコードはこちらから参照(ダウンロード)できます
Mainクラス
FirstClsクラス

まとめ

このように、主軸になる処理を中心にして、処理を繋げられるように、処理を分断(機能レベル、処理レベル色々ありますが。。。)。

そして、組み合わせて実行!と言うように、なるべく修正、追加する作業を減らせるように作るとクールなプログラムと言えるでしょう。

あー自分も美しいプログラムが書けるようになりたい!

でわでわ。。。

関連ページ

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜


Java IoT 予備知識〜ラズパイにJava(Web)サーバーを立てる〜

ラズパイにIoT開発環境を構築するという自前のミッションがあるのですが、急遽ラズパイにJava製のWebサーバーを起動しようということになりました。※そのように考えた次第です。。。

具体的に、Iotの実装を行うためにはそれなりの開発環境を構築する必要があります。※JavaME環境

しかし、自分が持っているラズパイは古いので、遅い。。。開発環境を構築して作業を行うのはちょいと面倒なわけです。

ラズパイでの開発環境構築が必要な理由は、デバイス(USB、GPIOなど)へのアクセスが必要になるため実機での開発環境が必要になります。

なので、今回は古いラズパイ(RPi2 model B)での実装を行うので、Windows上で開発したJavaサーバーをラズパイに移植して起動しようという考えです。

具体的な手順

  1. Widnows上で開発、テストを行う
  2. JARファイル(実行ファイル)を出力して起動できる確認をする
  3. 作成したコードをGithubにコミットする
  4. ラズパイでGithubからソースをチェックアウトしてビルド、JARファイルを作成
  5. ラズパイの起動時に実行するシェル・スクリプトで手順4の処理を行う

上記のような手順で作業を行うと結果としては、下のように動くであろうというところです。

  1. ラズパイに電源を入れる
  2. 起動スクリプトが動く
  3. 作成したJavaサーバーが起動する

問題は、作成したJavaサーバーに何を行わせるか?というところです。

今回作成する、JavaサーバーはHTTPプロトコル以外でも受信し処理を行うことができるものを作成します。
「なぜそれが可能か?」というところですが、これは低レベルな実装なためHTTPだろうが、バイナリだろうが関係なく受信、レスポンスの送信を行うことが可能になります。

その代わり、以下のような部分を実装する必要があります。

  1. 受信したリクエストがHTTPなのか、それ以外なのか?を判別
  2. HTTPリクエストならばHTTPでレスポンスを返し、それ以外はそれぞれのレスポンスを返す
  3. それぞれのプロトコル(HTTP, FTPなど)で受信したときにどのような処理を行うのか?

通常というか身近にある「ウェブサーバー」と呼ばれているものはHTTPリクエストを受け、それに対応するHTMLを読みこんで、そのテキストデータ(HTML)を返却、ブラウザで表示というような処理を行っていますが、

今回作成するJavaサーバーは低レベルなため、大体のことは大体できますが、以下の部分を実装する必要があります。

HTTPリクエストを受け、それに対応するHTMLを読みこんで、そのテキストデータ(HTML)を返却

この部分を実装できるようになると、自作のフレームワークを作成したり一人で多くの作業を行おうとするときに「前もって作成しておく道具」として使用することができます。このような「道具」を作成しておくとやらなければいけいない作業が減り、いろいろなことができるのではないでしょうか?

注意点

上記のJavaサーバーは、ServerSocketを使用して作成しますが、これの実装をする前に理解しておく必要がいくつかあります。

  • サーバーとクライアントの関係に関する理解
  • Webサーバー、アプリケーションサーバー(APサーバー)、DBサーバーの関係性に対する理解
  • MVCモデルの概要に対する理解

まとめると次のようになります。

  1. リクエストの送信と受信を行うときに「サーバー」と「クライアント」の関係がある
  2. HTTP以外にもプロトコルがあり、ブラウザはHTTP、FTPなどのプロトコルを使用する
  3. HTTP以外のプロトコルで、Socket通信のようにバイトデータをそのまま送信することもある
  4. HTTP、それ以外、それぞれの通信方法で、それぞれに対応した処理を実装する必要がある

次はHttpServeltの実装について

ちょっと難しくなってしまったので、まずはJavaでのウェブサーバーを見ていきます。

JavaのWebサーバー

とりあえずはここのページを参考に作成しようと思います。

結論から言うと。。。

HttpServletクラスを拡張(extends)してリクエストを受け付ける常駐アプリを作成しようと言うことです。

とても、シンプルなJavaウェブサーバーです。Java出なくてもクライアントとサーバーの関連を学習したい人は一読作成して見ると、一発で理解できます。(理解できないと作成できません。)

このような、わりかし低レベルな実装は経験する機会があまりないので、Java学習者やウェブデザイナーなどウェブ・サーバーをよく使う人には一度やって見ると理解が早いです。

ちなみに、「通常実装するとき」というのは、SpringBoot(DispatcherServlet)、JavaEE(FacesServlet)のようなフレームワークを使用した実装のことを指しています。

今回作成するシンプルなJavaウェブサーバーというのは、一段レベルを下げて上記のようなサーブレットクラスの親クラス・インターフェース部分の実装を行うというところです。

ちなみに、FacesServletクラスはServletインターフェースを実装しています。なので、この部分に係る実装を行うということです。

具体的には、HttpServletクラスの子クラスを作成してこのクラスを拡張してやります。

大まかな仕組み

下のような図になります。

ふざけているのではありませんので。。。

  • クライアント(スマホやPC)からURLを指定してサーバーにアクセスします。
  • クライアントは、PCやスマホのことなので自分が持っている機械(デバイス)です。

そして、サーバーはどこかの会社とか、国の機関とかに置いてあるウェブサーバー(アプリ)を起動している機械(コンピュータ)にアクセスしてそのアプリが返却するHTMLを見ている状況です。一緒にJSなどもダウンロードされて画面(ブラウザ)に表示されます。

大まかに上のような処理を行う「ウェブサーバー」をJavaを使用して作成しようと言うことです。

サーバーはアプリのこと

サーバーはアプリケーションです。常駐して動き、何かしらのアクセスを待機しているアプリケーションのことを「サーバー」と呼びます。別な言い方をすると「常駐待機しているアプリケーション」ということになります。
具体的には、PCを動かすために起動する(画面を表示する)Xサーバーなんてものもありますし、電源を入れたらスマホの画面が開くのも常駐アプリなので「サーバー」の仲間に入ります。
全部がサーバーだと呼ぶのに不便なのでスマホアプリと言ったり、サーバーと言ったりして区別します。

ちなみに、サーバー(アプリケーション)を稼働させるためのコンピュータのことを「サーバー機」と呼びます、そして、基本情報などの説明書きには「サーバー」などと書かれていますが、「サーバー機」と「サーバー(アプリケーション)」とでは別物なので、注意してください。

Java版Webサーバー

通常は、TomcatとかApacheとかをダウンロードしてきてウェブサーバーとして稼働することが多いのですが、今回はこれをJavaで作成しようと思います、まずは調査から行きますが、TomcatはJavaでできているようです。

そして、よくある書籍などでは、下のようなコードで実装しています。

public class JspLlesson1 extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        res.setContentType("txtext/html; charset=UTF-8");
        PrintWriter write = res.getWriter();
        write.println("Hello World");
    }
}

HttpServletのポイント

このクラスはJavaDocにあるように、抽象クラスです。しかし、抽象メソッドが定義されていないので「必ずオーバーライド」をしなくてもよい形になっています。

意図としては、継承して使用してくださいということなのだと思います。※予想になります。。。

そして、「doGet()」を「doPost()」メソッドをオーバーライドすることでHTTPリクエストに対する処理を実装する事ができます。

上記の落書きのようなイメージにある、リクエストとレスポンスをハンドルする処理がこのクラス・メソッドに実装することになるというわけです。

具体的には、リクエストの種類が2つあり、それぞれ「GETリクエスト」「POSTリクエスト」になります。

GET(ゲット)リクエスト

ブラウザで、URLを叩いたときに「XXXX.html」のようなファイルを開くときに使用するリクエストです。
その名の通り、対象のファイル(HTMLファイル)をダウンロード、ブラウザで表示します。
もし、ブラウザではなくプログラムで対象のURLにアクセスしたときはどのようになるか?

下の画像は、次のコマンドをコマンドプロンプトでたたいたときの結果です。

curl https://zenryokuservice.com

POST(ポスト)リクエスト

これは、画面のキャプチャがとりずらく、イメージが取れませんでしたが、よくある「ログイン画面」を思い出してもらうとよいです。
これらの画面には、必ず「ログイン」などのような文言のある「ボタン」があると思います。

この「ボタン」はPOSTリクエストを送信するボタンになっていて、HTMLのみで書くとしたのようなHTMLコードになります。

HTMLソース
<form action="cgi-bin/abc.cgi" method="post">
<p>
名前:<input type="text" name="namae">
</p>
<p>
<input type="submit" value="送信する">
<input type="reset" value="入力内容をリセットする">
</p>
</form>

実際のブラウザ表示 ※このボタンを押下すると画面がリロードされるだけです。


HTMLソース

名前:



ちょっと補足を加えます。上記のHTMLは「formタグ」で囲っている内容をPOSTリクエストで送信しますという意味ですが、「formタグ」の属性(attribute)部分の「action」を見てみると「空」になっています。

このアクションが「空」になっているときは「このページにリクエストを送信しますよ」という意味になります。

同様に「post」の部分を見てみると「post」と書いてあります。これは「POSTリクエストを送信しますよ」という意味です。

そして、「inputタグ」にある「name」の部分はこの名前で、それぞれの値を送信するという意味になります。
つまりは、submitのボタンを押下したら、送信先へPOSTリクエストが送られるのですが、POSTリクエストはデータが一緒に送信されます、GETリクエストでも、データの送信ができますが、この場合はURLの後ろに「?変数名1=値,変数名2=値 ... 」のようにURLへ値を書く必要があります。

これでは、ログイン情報などの秘密にしたい情報が公開された状態でリクエストを送信することになるのでとても危険なのです。そのために「POSTリクエスト」を使用します。

JavaでのWebサーバー

このサイトを参考にしました。

結論から言うと、ServerSocketで受付(ポートを指定してリスン状態で待機)してHTTPメッセージを返却する。と言うものがWebサーバーの正体のようです。

シンプルなSocketサーバーは以前作成しました。

これを一度作ったことがあるならば(ServerSocketアプリのことです)わりかし簡単に作成することができると思います。

しかし、現時点では、HttpServletクラスを拡張した実装を行う方向で話を進めているのであくまでも下のようなコードをベースに考えてもらいたく思います。

public class JspLlesson1 extends HttpServlet {

    public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        res.setContentType("txtext/html; charset=UTF-8");
        PrintWriter write = res.getWriter();
        write.println("Hello World");
    }
}

このコードは、Tomcatを使用したときのコードです。Eclipseで実行したのですが、作成した上のクラスファイルを右クリックして「で起動」をクリックすると実行する事ができます。

これで、「サーバー上で利用できるリソースがありませんでした。」のようなメッセージが表示されたら下の手順で、実行することができるようになります。

Tomcatの準備

Tomcatランタイムの追加

Eclipseの上部にあるメニューからウィンドウ -> 設定を選択、下のイメージのように 検索部分に「サーバー」と入力 -> 赤枠を選択します。

そして、Java8を使用しているので、Tomcatの8.5を選択します。

ここで、Tomcatがインストール(ダウンロード)されていない場合は、Apacheのサイトからダウンロードしてきます。

ダウンロードした、Tomcatを展開して、Eclipseの設定でそのフォルダーを指定します。

最後に完了を押下します。

サーバープロジェクトの追加

1.パッケージエクスプローラーを右クリック、そのほかのプロジェクトをクリック
2.下のイメージのように、その他から次へをクリック

3.同様に、完了をクリックする※設定を確認する

4.プロジェクトファセットの設定を行います。

5.プロジェクトを選択して「Altボタンを押しながらEnterを押下」すると下のような画面が見れます。

6.プロジェクトファセットを選択、下のイメージのようにJava1.8を設定、動的ウェブモジュールを設定します。

7.設定を適用します。

8.web.xmlを生成します。

web.xmlの設定を行う

書き方は以下のようになります。

<servlet>
  <servlet-name>サーブレット名</servlet-name>
  <servlet-class>クラスファイル名</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>サーブレット名</servlet-name>
  <url-pattern>呼び出す時につけるURLパス名</url-pattern>
</servlet-mapping>

classesフォルダを作成する

最終的に下のように、フォルダ構成を作ります。

JavaServletを実行する

この状態で、下のように、プログラムを実行します。

以下のURLにアクセス

localhost:8080/プロジェクト名/

今回作成したプロジェクト名が「StudyJavaBasic」なので下のようなURLにアクセスすると作成したサーブレットが起動できます。

http://localhost:8080/StudyJava/

Httpメッセージ

よく、「Httpヘッダー」とか「Httpボディ」とか言ったりしますがHttpメッセージの「ヘッダ」「ボディ」と言うのが正確な言い方です。

参考の処理内容

  1. ServerSocketをXXXポートで待機
  2. リクエストを受け付けたら、受け取ったメッセージ(Httpメッセージ)から相手のIPを取得
  3. IPを使用してHttpレスポンスを作成
  4. OuputStreamでリクエスト元に返却する(レスポンスを返却する)

と言うような流れで処理を作成しているようです。

レシピ

詳細に関しては、Java APIを参照してください。

  1. ServerSocket
  2. BufferedReader(Reader)
  3. DataOutputSream(OutputSream)

こんなくらいです。

webサーバー

  1. リクエストを受けて(SocketServer#accept())
  2. リクエストの値からファイルを読み込む
  3. HTTPメッセージボディにHTMLを書く
  4. データ(DataOutputSream)を返却

シンプルにこんな手順で処理を行えば良いと思います。

低レベルなAPIは、全て作成する必要がありますが、逆に言えば全てが想い通りに作れると言うところが魅力的(笑)

こんな感じで考えております。このアプリにどんな機能を追加するかは作成者の思うままですね。。。

何やろうかな?

でわでわ。。。



Java Mid Basic 〜チェッカークラスを作る〜

オブジェクト指向の実装

オブジェクト指向の考え方では1クラス1機能です。前回作成した、数秘術→社会人基礎力の数値を算出するクラス内に入力チェック処理を作るとあの処理、この処理…と面倒なので、チェッカークラスを作成します。

そして、staticの使い方に関しても触れます。

チェッカークラスは、チェック処理を担当するクラスです。

コマンドクラスから呼び出す

コマンドクラスはインターフェースを使用してCommandIFを実装したクラスであればなんでも良い様に作成しました。

今度は、入力チェック用のクラスを作成します。ここで大切なのは「どの様に使うか?」です。

今回は、シンプルにチェッカーユーティリティクラスを作成します。クラス名は「CheckwrUtil」にします。

使い方は以下の様に静的呼び出しで行います。理由は、インスタンス化する意味がないからこの様な形で実装します。

CheckerUtil.isMandatory("文字列");

クラスの実装は下の様になります。

public class ChckerUtil {
    public static boolean  isMandatory(String inut) {
        // チェック処理
    }
}

そして、必須入力チェックなので、入力の有無を判定し、TrueかFalseを返却するように実装してやればオッケー。

具体的に

コマンドクラスでの実装サンプルです。

public class HelloCommand implements CommandIF {

 /* (non-Javadoc)
    * @see jp.zenryoku.sample.lv3.refactor.CommandIF#execute()
     */
    @Override
  public void execute() {
        // TODO Auto-generated method stub
     System.out.println("名前を入力してください");
      Scanner input = new Scanner(System.in);
      String inStr = input.nextLine();

      if (CheckerUtil.isMandatory(inStr)) {
         // 未入力時の処理
      } else {
         // 入力ありの時
      }
   }
}

executeメソッドで、入力時のチェック処理を行います。

とりあえずは、こんな感じの実装で使用することを想定して作りました。

ポイント

通常は、newしてから使用するメンバーメソッドを作るのですが、この処理には、インスタンスを作る必要がないので、staticをつけた静的メソッドに行くしています。

詳細

インスタンスを作る必要性に関して「通常」と言う言葉を使いましたが、何かしらの処理を行うのに、準備が必要な時、処理するデータを1クラスに1つ持たせたい場合などにはインスタンスが必要です。

例えば、DBにアクセスする時には、DBサーバーへのコネクションを取得、保持する必要があり、アクセス(データを取得する操作を複数人が行う想定の場合にはstatic(静的)クラスやメソッドは使えません。何故かと言うとstaticをつけると、メソッドの場合は、インスタンス1つにつき1つのクラスにしか影響しなかったのが全てのインスタンスに影響します。

具体的には

リストにデータを設定する場合、リストの中身のデータクラスがstaticだと、リストの中身が全部同じになります。

逆にstaticでない場合は、それぞれのクラスに別々のデータが設定されます。

## ポリモーフィズムのサンプル
CommandIF(インタフェース)を使用したポリモーフィズムの実行動画があります。