Murayama blog.

プログラミング教育なブログ

AtomでXdebugを使ってステップ実行するには

Xdebugをインストールします。

https://xdebug.org/

Xdebugは実行環境やPHPのバージョンによってインストールの手順が異なるので、以下のURLからインストール手順を確認します。

https://xdebug.org/wizard.php

ローカルで php -i した出力結果を上記のページにコピペします。

私の環境(Mac)だと以下のようになりました。Windowsだともっと簡単で、dll落とすだけで済むっぽい。

手順に従ってインストールします。

Xdebugの設定

続いてphp.iniにXdebugの設定を追記します。

[xdebug]
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_connect_back=off
xdebug.remote_port=9000
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_autostart=true

以降ビルトインサーバの起動時には9000番ポートを使うようにします。

php -S localhost:9000

Atomの設定

Atomの拡張パッケージであるphp-debugをインストールします。

https://atom.io/packages/php-debug

インストールが完了すると、AtomのフッターにPHP Debugのアイコンが表示されます。

動作確認

適当なPHPプログラムを書きます。sample.php

<?php
$message = "Hello World";
for ($i=0; $i < 10; $i++) {
  echo $message;
}

上記の4行目(行番号の横)をクリックしてブレークポイントを設定します。またAtomのフッターからPHPDebugアイコンからデバッグ用のウィンドウを表示しておきます。デバッグ用のウィンドウを表示しておかないとブレークポイントは有効にならないので注意です。

PHPプログラムを実行します(ビルトインサーバで動かしてもOK)。

php sample.php

ブレークポイントでコードの実行が停止するので、デバッグ用のウィンドウでコード実行を制御できます。

知る、ということ

朝、出社前にこどもをバス停まで送っていくと、いつも同じ時刻に通りかかるおじいさんがいます。ニコニコとこどもの顔を覗きこんでは何も言わず、とぼとぼと歩いていくおじいさん。

最近私も歩くようにしています。会社帰りに1駅くらい歩くだけ。いつまで続くかな。

脳が壊れた、という本を読みました。

脳が壊れた (新潮新書)

脳が壊れた (新潮新書)

脳梗塞後のリハビリでの体験や、周囲との関わり方、脳梗塞によって今までできていたことができなくなったことで初めてきづいたことを著者は「僥倖」としてポジティブに捉えています。

ぎょうこうと読みます。思いがけない幸運という意味です。

実際に読んでみて「見えない障害」と言われる高次脳機能障害について、私は知らなさすぎでした。

症状の中に、感情失禁というものがあるそうです。感情を抑えることができず泣き出してしまったり、言葉に詰まったり。赤ちゃんを見ると嬉しい気持ちになってニヤニヤしてしまったり。

そういうことだったのか。知らなさすぎた。

もしかしたら、朝のおじいさんも大変な思いをしてリハビリしているのかも。なんだか切なくなってきた。

知らなかったことを知ることで、見え方は変わる。

いろんなことに気づかせてくれる本でした。読んでよかった。僥倖。

クリエィティブに仕事をしよう。

今日のお昼ごはんはいつもと違う"ヘルシーデリ"なるお弁当を買ってみました。

お店の明るい雰囲気の惹かれてふらふらと。

会社に戻ってお弁当を開けてみるとおしゃれなお弁当箱。

入ってるおかずはふつうのお弁当と大差ないんだけど、いつもよりちょっとだけ気分がいい。ヘルシーやし。おしゃれやし。

おかげさまで社内でも「何そのお弁当?」と人気者になれました。

お弁当も入れ物や呼び名ひとつで、その価値は変わるんだなと。

お弁当を売るならこだわるのはごはんやおかずだけじゃない。お弁当の容器ひとつ販売員さんの売り方ひとつが、口コミになってインターネット上に流れて、新しいお客さまを連れてきてくれるかもしれない。

若いころはクリエイティブな仕事とそうでない仕事があると思ってたけど、 年とって思うのは仕事の内容じゃなくてクリエイティブに仕事するかどうかだってこと。

クリエィティブに作られたお弁当を食べてそう思った。

クリエィティブに仕事をしよう。

Java 例外オブジェクトの生成について

ここでは簡単な計算機プログラムを作成して例外処理について考察します。

Part1

次の計算機クラス(Calculator)は0による除算時に例外をスローします。

計算機クラス

package example;

public class Calculator {

    public int divide(int x, int y){
        return x / y;
    }
}

引数 y が0のとき例外が発生します。

Mainクラス

package example;

public class Main {

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        // int result = calc.divide(9, 3);
        int result = calc.divide(9, 0);
        System.out.println(result);
    }
}

実行結果

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at example.Calculator.divide(Calculator.java:6)
    at example.Main.main(Main.java:8)

Arithmeticとは "算数" という意味です。


Part2

次に独自の例外クラス(CalculatorException)を作成してみます。

計算機例外クラス

package example;

public class CalculatorException extends Exception {
}

例外クラスを作成する場合はExceptionクラス(およびそのサブクラス)を継承します。Exceptionクラスを継承すると検査例外クラスになります。RuntimeExceptionを継承した場合は実行時例外クラスになります。

計算機クラス

package example;

public class Calculator {

    public int divide(int x, int y) throws CalculatorException{
        if (y == 0) {
            throw new CalculatorException();
        }
        return x / y;
    }
}

CalculatorExceptionは検査例外クラスであるため、例外処理(try-catch or throws)が必須になります。

Mainクラス

package example;

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        try {
            // int result = calc.divide(9, 3);
            int result = calc.divide(9, 0);
            System.out.println(result);
        } catch (CalculatorException e) {
            e.printStackTrace();
        }
    }
}

CalculatorクラスのdivideメソッドはCalculatorExceptionをスローします。CalculatorExceptionは検査例外クラスであるため、例外処理(try-catch or throws)が必須になります。

実行結果

example.CalculatorException
    at example.Calculator.divide(Calculator.java:7)
    at example.Main.main(Main.java:8)

Part1の実行結果と比較してみましょう。

e.printStackTrace();

変数eに対してprintStackTraceというメソッドを呼び出しているだけです。変数eはCalculatorException型の変数です。printStackTraceメソッドは例外情報(例外クラス名、メッセージ)とスタックトレース(メソッド呼び出しの履歴)を出力します。

例外クラス名: メッセージ
    スタックトレース
    ..
    スタックトレース

Part2.5

独自の例外クラス(CalculatorException)にはメッセージを含むこともできます。

計算機例外クラス

package example;

public class CalculatorException extends Exception {

    public CalculatorException(String message){
        super(message);
    }
}

スーパークラス(Exception)のコンストラクタにメッセージを設定すると、printStackTraceメソッドの中にメッセージを含むことができます。また設定したメッセージはgetMessageメソッドによって取得可能です。

計算機クラス

package example;

public class Calculator {

    public int divide(int x, int y) throws CalculatorException{
        if (y == 0) {
            throw new CalculatorException("/ by 0");
        }
        return x / y;
    }
}

Mainクラス

package example;

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        try {
            // int result = calc.divide(9, 3);
            int result = calc.divide(9, 0);
            System.out.println(result);
        } catch (CalculatorException e) {
            e.printStackTrace();
            // System.out.println(e.getMessage());
        }
    }
}

実行結果

example.CalculatorException: / by 0
    at example.Calculator.divide(Calculator.java:7)
    at example.Main.main(Main.java:8)

Exceptionクラスのコンストラクタにはメッセージ以外にも、原因を表す例外オブジェクトを設定することもできます。これを使うと原因を表す例外オブジェクトを別の例外オブジェクトでラップすることができます。原因を表す例外オブジェクトはgetCauseメソッドで取り出すことができます。


Part3

独自の例外クラスを作成せずに、JavaAPIに含まれる標準的な例外クラスを利用することもできます。

計算機クラス

package example;

public class Calculator {

    public int divide(int x, int y) {
        if (y == 0) {
            throw new IllegalArgumentException("y is zero");
        }
        return x / y;
    }
}

IllegalArgumentExceptionは不正な引数を表す例外クラスです。IllegalArgumentExceptionはRuntimeExceptionのサブクラスであるため実行時例外に分類されます。

Mainクラス

package example;

public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        try {
            // int result = calc.divide(9, 3);
            int result = calc.divide(9, 0);
            System.out.println(result);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            // System.out.println(e.getMessage());
        }
    }
}

catchする例外クラスをIllegalArgumentExceptionに変更しています。実行時例外の例外処理(try-catch or throws)は任意です。

実行結果

java.lang.IllegalArgumentException: y is zero
    at example.Calculator.divide(Calculator.java:7)
    at example.Main.main(Main.java:8)

参考:その他の標準的な例外クラス

http://murayama.hatenablog.com/entry/20091011/1255255351

会社組織で学ぶJavaの例外処理入門

Javaの例外処理についてサンプルプログラムを作ってみました。ここでは会社組織のおける部長(GeneralManager)、課長(SectionManager)、一般社員(Employee)クラスを作って組織的に仕事をする様子を実装してみます。

  • GeneralManager
    • 部長クラス。受け取った仕事を課長にお任せ(委譲)する。ただし、課長の対処しきれない仕事はどんな仕事でも片付ける。
  • SectionManager
    • 課長クラス。受け取った仕事を一般社員にお任せ(委譲)する。一般社員が対処しれない仕事に対処するが、それでも無理な仕事は部長にお返しする。
  • Employee
    • 一般社員クラス。受け取った仕事を処理する。無理な仕事は課長にお返しする。
  • Task
    • 仕事を表すクラス。仕事の名前と作業量を保持する。
  • TaskOverException
    • 対処しきれない仕事を表す例外クラス。
  • Main
    • maimメソッドを実装したクラス。

Javaプログラミング

まずは単純なTaskクラス、TaskOverExceptionクラスを作成し、次にEmployee、SectionManager、GeneralManagerクラスを作成します。そのあとMainクラスを作成して動作確認します。

Task

Taskクラスはタスク名と作業量を保持します。

package example;

public class Task {
    private String name;
    private int volume;

    public Task(String name, int volume) {
        this.name = name;
        this.volume = volume;
    }

    public String getName() {
        return name;
    }

    public int getVolume() {
        return volume;
    }
}

TaskOverException

TaskOverExceptionクラスは作業量超過を表す例外クラスです。

package example;

public class TaskOverException extends Exception {
}

Employee

Employeeクラスは作業量が5以下の仕事を処理します。作業量が多すぎる場合はTaskOverExceptionをスローします。

package example;

public class Employee {

    public void execute(Task task) throws TaskOverException {
        if (task.getVolume() <= 5) {
            System.out.println("Employee#execute " + task.getName());
        } else {
            throw new TaskOverException();
        }
    }
}

SectionManager

SectionManagerクラスは、Employeeクラスに処理を移譲します。SectionManagerはTaskOverExceptionを捕捉して処理します。このとき作業量が10以下の仕事を処理可能です。作業量が多すぎる場合はTaskOverExceptionをスローします。

package example;

public class SectionManager {
    private Employee employee;

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    public void execute(Task task) throws TaskOverException {
        try {
            employee.execute(task);
        } catch (TaskOverException e) {
            if (task.getVolume() <= 10) {
                System.out.println("SectionManager#execute " + task.getName());
            } else {
                throw e;
            }
        }
    }
}

GeneralManager

GeneralManagerクラスは、SectionManagerクラスに処理を移譲します。GeneralManagerはTaskOverExceptionを捕捉して処理します。

package example;

public class GeneralManager {
    private SectionManager sectionManager;

    public void setSectionManager(SectionManager sectionManager) {
        this.sectionManager = sectionManager;
    }

    public void execute(Task task) {
        try {
            sectionManager.execute(task);
        } catch (TaskOverException e) {
            System.out.println("GeneralManager#execute " + task.getName());
        }
    }
}

Main

Mainクラスにはmainメソッドを実装します。

package example;

public class Main {

    public static void main(String[] args) {
        GeneralManager generalManager = new GeneralManager();
        SectionManager sectionManager = new SectionManager();
        Employee employee = new Employee();

        generalManager.setSectionManager(sectionManager);
        sectionManager.setEmployee(employee);

        Task smallTask = new Task("SmallTask", 1);
        generalManager.execute(smallTask);

        Task mediumTask = new Task("MediumTask", 10);
        generalManager.execute(mediumTask);

        Task largeTask = new Task("LargeTask", 100);
        generalManager.execute(largeTask);
    }
}

実行結果

mainメソッドの実行結果は次のようになります。

Employee#execute SmallTask
SectionManager#execute MediumTask
GeneralManager#execute LargeTask

まとめ

自分で対処できない仕事は上司に報告するようにしましょう。

下手なアドバイス入門

プログラミング講師として受講者にアドバイスをする機会が多くあります。そこで気づいた失敗例をまとめました。

  1. 信頼関係が構築できていない
  2. 求められていない
  3. 前提が共有できていない
  4. 当たり前のことを言う
  5. 具体性がない
  6. えらそうに言う
  7. 自分ができていないことを言う

1. 信頼関係が構築できていない

アドバイスは両者の信頼関係の上で成立します。相手が貴方のことを信頼していないとアドバイスは無視されます。日頃からの信頼関係が構築できていないのなら、アドバイスするのは控えておきましょう。あなたが部下を指導したいなら、まずは信頼関係を構築するべきです。

2. 求められていない

アドバイスはタイミングが命です。求められていないときにアドバイスをしてはいけません。上司と部下の関係、先輩と後輩の関係など、アドバイスを交わす両者に上下関係がある場合は特に注意しておきましょう。部下はいつもアドバイスを求めているとはかぎりません。アドバイスってのは、相手が求めているときにそっと提供してあげるべきです。

3. 前提が共有できていない

相手の言い分を理解できていないのに、アドバイスしてはいけません。まずは相手のメッセージを理解しましょう。話してあげることよりも聞いてあげることを優先すべきです。

4. 当たり前のことを言う

アドバイスというのは相手に役にたつものでないといけません。相手が気づいていないことを気づかせるのがアドバイスの価値です。当たり前のアドバイスには価値はありません。

5. 具体性がない

行動に結びつかないアドバイスには価値はありません。アドバイスは抽象的にすればするほど「がんばれ」や「やるしかない」といった自明なものになります。悩んでいる人にとってはイラつかせるだけで、逆効果になるかもしれません。

6. えらそうに言う

アドバイスは「何を言うかよりどう言うか」です。アドバイスを交わす両者に上下関係がある場合は特に注意しておきましょう。

7. 自分ができていないことを言う

アドバイスは「どう言うかより誰が言うか」です。アドバイスをするときには、それを言うべき資格が自分にあるのか自問するようにします。

フレームワーク初学者のためのSpark Framework入門

最近、Javaのマイクロフレームワークがきてるようです。

http://postd.cc/java-micro-frameworks-the-new-trend-you-cant-ignore/

Javaフレームワーク事情

Javaに限らず、Webアプリケーション開発の世界ではフレームワークを使って開発することが一般的です。JavaにはSpringフレームワークや標準のJavaEEなど多くのフレームワークが存在しますが、これらのフレームワークは様々な要件を満たすため、多くの機能が含まれています。そのため、フレームワーク初学者にとって、フレームワークとはどういうものかを学習するという意味では全体像が掴みにくく、学習コストの高いものになっています。

昔はStrutsを勉強して、それからHibernateを勉強して、それからSpringやSeasar2を勉強する、という流れがありました。最近はいきなりSpringから入ることが多い?ようなので大変だろうなと思っています。Spring Bootもよさげですけどね。

マイクロフレームワーク

2016年現在、Webフレームワークの標準を作っているのはやはりRuby on Railsでしょう。Railsからインスパイヤされた(であろう)Webフレームワークは言語を問わず数多く存在します。これらは大変素晴らしいフレームワークですが、小さなWebアプリを開発する場合、たとえば簡易な掲示板を作成するようなシーンにおいては、やや大げさなものになります。

Railsが登場した後、Ruby界隈で、SinatraというWebアプリケーションフレームワークが注目されるようになります。SinatraRailsとは違って、シンプルで軽量なフレームワークです。簡単に言えば、HTTPリクエストと処理を関連づけるルーティング処理だけを実装したフレームワークです。またDSLで簡潔に処理を記述できるところも特徴の一つです。具体的には次のようなコードを実装します。

require 'sinatra'

get '/hi' do
  "Hello World!"
end

初めて見るとRubyのコードなの?という印象を受けるかもしれませんが、これはRubyの文法にしたがって記述されています。getというキーワードがありますが、これはHTTPのGETリクエストを処理することを意味しており、'/hi'の部分はURLを意味しています。つまり、GETリクエストで'/hi'というURLが要求された場合、あとのdo - endブロックが実行され、レスポンスとして"Hello World"が返却される仕組みになっています。

特定のドメイン(目的)に特化したコード表記法をDSLDomain Specific Language)と呼びます。Sinatraの場合はHTTPのルーティングに特化した表記法と考えることができます。こうすることで直感的で読みやすいコードを作ることができます。

Spark Frameworkとは

SparkはJavaで実装されたマイクロフレームワークです。Java8構文に従うことで少ない労力でWebアプリケーションを開発できます。

http://sparkjava.com/

ドキュメントが充実しているので、APIの使い方を一通り見ておくと良いでしょう。

http://sparkjava.com/documentation.html

チュートリアル1 - Hello World

ここではMavenでプロジェクトを作成し、Sparkフレームワークを使ったWebアプリケーションを作成します。また、Eclipseで開発できるようにプロジェクトをカスタマイズします。

まずはMavenでプロジェクトの雛形を作成します。グループIDはcom.example、アーティファクトIDはspark-appとしています。

$ mvn archetype:generate -DgroupId=com.example.app -DartifactId=spark-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

プロジェクトが作成されたら、cd spark-appを忘れないように実行しておきましょう。

次にpom.xmlを開いて、タグの中に以下の定義(Sparkフレームワーク)を追記します。

<dependency>
  <groupId>com.sparkjava</groupId>
  <artifactId>spark-core</artifactId>
  <version>2.5</version>
</dependency>

コンパイラーの設定を更新しておきましょう。pom.xmlに以下の設定を追加します。urlタグの後(dependenciesタグの前)に追記します。

 <properties>
        <encoding>UTF-8</encoding>
        <project.build.sourceEncoding>${encoding}</project.build.sourceEncoding>
        <project.reporting.outputEncoding>${encoding}</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <web.version>3.1</web.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

次にEclipseで作業できるように以下のmvnコマンドを実行します。

$ mvn eclipse:eclipse

これはeclipseプラグインeclipseゴールを指定しています。mvnコマンドにはフェーズやゴールといった引数を指定できます。mvn eclipse:eclipseによってEclipseプロジェクトとして必要な設定ファイルが作成できます。

mvn eclipse:cleanコマンドを実行すると設定ファイルを削除できます。

次にApp.javaを開いて、以下のように処理を実装します。import static spark.Spark.*; の指定を忘れないようにしてください。

package com.example.app;

import static spark.Spark.*;

import spark.Request;
import spark.Response;
import spark.Route;

public class App {
    public static void main(String[] args) {
        get("/hello", new Route() {
            @Override
            public Object handle(Request req, Response resp) throws Exception {
                return "Hello World";
            }
        });
    }
}

完成したApp.javaを実行します。サーバが起動するので以下のURLにアクセスします。

http://localhost:4567/hello

ブラウザに"Hello World"が表示されればOKです。

結果を確認したらApp.javaを忘れずに停止しておきましょう。Consoleビューの赤いボタンを押すとApp.javaは停止します。標準ではTomcatのようにオートリロードされないので、コードを修正する度にプログラムを再起動する必要があります。

チュートリアル2 - リクエストパラメータの表示

mainメソッドの中に2つ目のルーティング( “/echo")を追加してみましょう。ここではクエリパラメータではなく、URLの一部をリクエストパラメータと受け取るようにしています。

       get("/echo/:name", new Route() {
            @Override
            public Object handle(Request req, Response resp) throws Exception {
                return "echo:" + req.params("name");
            }
        });

完成したApp.javaを実行します。サーバが起動するので以下のURLにアクセスします。

http://localhost:4567/echo/Hello

ブラウザに"echo:Hello"が表示されればOKです。

チュートリアル3 - ビューテンプレートの使用

Thymeleafのインストー

Sparkは様々なビューテンプレートをサポートしています。ここではSpringでよく利用されるThymeleafを使ってみます。Thymeleafの詳細な情報については以下の公式サイトを参考にしてください。

SparkフレームワークからThymeleafを利用するためにMavenのpom.xmlに以下のライブラリを追記します。Sparkフレームワークの定義の下に追記すると良いでしょう。

<dependency>
  <groupId>com.sparkjava</groupId>
  <artifactId>spark-template-thymeleaf</artifactId>
  <version>2.3</version>
</dependency>

それからThymeleafのビューテンプレートを配置するディレクトリを作成しておきましょう。アプリケーションのルートフォルダで以下のコマンドを実行します。

$ mkdir -p src/main/resources/templates

Windowsの場合は mkdir src¥main¥resources¥templatesとしてください。

次にEclipseに更新を反映するために以下のmvnコマンドを実行します。

$ mvn eclipse:eclipse

その後Eclipse側でプロジェクトを選択して、Refreshを選択してください。thymeleafが反映されます。

Thymeleafを使うには

mainメソッドの中に3つ目のルーティング( “/books")を追加してみましょう。/booksではshopNameとbooksをモデルとして作成し、ビューにレンダリングします。

       get("/books", new TemplateViewRoute() {
            @Override
            public ModelAndView handle(Request req, Response resp) throws Exception {
                Map<String, Object> model = new HashMap<>();
                model.put("shopName", "MyBookStore");

                List<String> books = Arrays.asList("Java Book", "PHP Book", "Ruby Book");
                model.put("books", books);
                return new ModelAndView(model, "index");
            }
        }, new ThymeleafTemplateEngine());

getメソッドの第2引数にはこれまでのRouteインタフェースではなく、TemplateViewRouteインタフェースを指定している点に注意してください。TemplateViewRouteインタフェースは戻り値にModelAndViewオブジェクトを返却します。ModelAndViewにはバインドするモデルと、レンダリングするビューのプレフィックスを指定します。プレフィックスには"index"を指定しているので、src/main/resources/templatesディレクトリにあるindex.htmlが呼び出されるようになります。

ビューテンプレート(index.html)の作成

Thymeleafのビューテンプレートindex.htmlを作成します。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>BookStore</title>
</head>
<body>
    <h1 th:text="${shopName}">BookStore</h1>
    <ul>
        <li th:each="book : ${books}" th:text="${book}">hoge</li>
    </ul>
</body>
</html>

ThymeleafはXMLのバリデーションが働くのでタグの閉じ忘れに注意してください。

それではApp.javaを実行します。サーバが起動するので以下のURLにアクセスします。

http://localhost:4567/books/

ショップタイトルと本の一覧が表示されればOKです。

例外処理について

ビューファイル名を間違えたり、Thymeleafで正しくデータバインディング出来ない場合は例外が発生します。標準では例外のスタックトレースが表示されないので、ルーティング定義の後に、以下のように例外ハンドラを定義しておきましょう。

    exception(Exception.class, new ExceptionHandler() {
      @Override
      public void handle(Exception e, Request req, Response resp) {
        e.printStackTrace();
        resp.status(200);
        resp.body(e.getMessage());
      }
    });

これでルーティングの中で例外が発生しても、コンソールにスタックトレースが表示されるようになります。

App.java

ここまでのApp.javaプログラムを再掲しておきます。

package com.example.app;

import static spark.Spark.*;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import spark.ExceptionHandler;
import spark.ModelAndView;
import spark.Request;
import spark.Response;
import spark.Route;
import spark.TemplateViewRoute;
import spark.template.thymeleaf.ThymeleafTemplateEngine;

public class App {
    public static void main(String[] args) {

        get("/hello", new Route() {
            @Override
            public Object handle(Request req, Response resp) throws Exception {
                return "Hello World";
            }
        });

        get("/echo/:name", new Route() {
            @Override
            public Object handle(Request req, Response resp) throws Exception {
                return "echo:" + req.params("name");
            }
        });

        get("/books", new TemplateViewRoute() {
            @Override
            public ModelAndView handle(Request req, Response resp) throws Exception {
                Map<String, Object> model = new HashMap<>();
                model.put("shopName", "MyBookStore");

                List<String> books = Arrays.asList("Java Book", "PHP Book", "Ruby Book");
                model.put("books", books);
                return new ModelAndView(model, "index");
            }
        }, new ThymeleafTemplateEngine());

        exception(Exception.class, new ExceptionHandler() {
            @Override
            public void handle(Exception e, Request req, Response resp) {
                e.printStackTrace();
                resp.status(200);
                resp.body(e.getMessage());
            }
        });
    }
}

マイクロフレームワーク、シンプルでいいですね。

Java8で書く

もっとシンプルです。

package com.example.app;

import static spark.Spark.exception;
import static spark.Spark.get;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import spark.ModelAndView;
import spark.template.thymeleaf.ThymeleafTemplateEngine;

public class App {
    public static void main(String[] args) {

        get("/hello", (req, resp) -> "Hello World");

        get("/echo/:name", (req, resp) -> "echo:" + req.params("name"));

        get("/books", (req, resp) -> {
            Map<String, Object> model = new HashMap<>();
            model.put("shopName", "MyBookStore");
            List<String> books = Arrays.asList("Java Book", "PHP Book", "Ruby Book");
            model.put("books", books);
            return new ModelAndView(model, "index");
        } , new ThymeleafTemplateEngine());

        exception(Exception.class, (e, req, resp) -> {
            e.printStackTrace();
            resp.status(200);
            resp.body(e.getMessage());
        });
    }
}

Java8いいですね。