JettyでWebSocketを使う(IntelliJの設定等も説明)

NetBeansやらEclipseでやってる例は簡単に見つけることが出来ましたが、IntelliJでやってる記事が見当たらない感じだったので備忘録として**詳しく**書いておきます。
あと、ここではjarで出力して動かすまでの手順を説明をしていきます。

##環境
- MacOSX El Capitan
- IntelliJ IDEA 14.1.5 (Ultimate)
- Jetty 9.2.13

##WebSocketを使う前の準備
WebSocketを使うために、Jettyを動かす必要があるのでここではJettyを動かす環境の作成について説明します。
###プロジェクトの作成
まず、IntelliJを立ち上げます。
そしてプロジェクト新規作成。
以下のように"Web Application"にチェックを入れます。
![スクリーンショット 2015-10-18 21.17.28.png](https://qiita-image-store.s3.amazonaws.com/0/55941/258e003b-e372-6716-4c12-8e64e24eb969.png "スクリーンショット 2015-10-18 21.17.28.png")

プロジェクト名は適当に。
![スクリーンショット 2015-10-18 21.19.46.png](https://qiita-image-store.s3.amazonaws.com/0/55941/ecaa5ae7-5acc-136f-b8a4-193ad9da5db8.png "スクリーンショット 2015-10-18 21.19.46.png")

###ライブラリを追加
プロジェクトの生成が完了したら、ライブラリを入れるディレクトリを作成します。
![スクリーンショット 2015-10-18 21.20.39.png](https://qiita-image-store.s3.amazonaws.com/0/55941/6b9ec753-f649-adeb-aeab-3d35e9ec161e.png "スクリーンショット 2015-10-18 21.20.39.png")
ここでは"libs"とします。
![スクリーンショット 2015-10-18 21.21.41.png](https://qiita-image-store.s3.amazonaws.com/0/55941/62d57c44-5a3a-0b77-8e62-1394ec77558c.png "スクリーンショット 2015-10-18 21.21.41.png")
ディレクトリを作成したら、以下のページから2つのjarファイルをダウンロードします。
[jetty-all-9.2.13.v20150730.jar](http://mvnrepository.com/artifact/org.eclipse.jetty.aggregate/jetty-all/9.2.13.v20150730)
[javax.servlet-api-3.1.0.jar](http://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/3.1.0)
ダウンロードしたら、先ほど作ったライブラリを入れるディレクトリ(今回はlibs)にファイルを入れます。
すると以下のようになります。
![スクリーンショット 2015-10-18 21.27.44.png](https://qiita-image-store.s3.amazonaws.com/0/55941/f212d99c-e81f-afeb-4f2c-b4289210c9a9.png "スクリーンショット 2015-10-18 21.27.44.png")
そして、以下のアイコンから"Project Structure"を開きます。
![スクリーンショット 2015-10-18 21.30.42.png](https://qiita-image-store.s3.amazonaws.com/0/55941/6b6ff133-89bc-63f3-885c-4c15b324c707.png "スクリーンショット 2015-10-18 21.30.42.png")
開いたら、左のペインから"Modules"を選択し、"Dependencies"タブを開きます。
![スクリーンショット 2015-10-18 21.30.48.png](https://qiita-image-store.s3.amazonaws.com/0/55941/a3938053-6cf9-88e5-f07a-59fd1ed3d6dc.png "スクリーンショット 2015-10-18 21.30.48.png")
すると、下部「＋」ボタンがあるのでクリックし、"Jars or directories..."をクリックします。
![スクリーンショット 2015-10-18 21.30.56.png](https://qiita-image-store.s3.amazonaws.com/0/55941/c98e5373-0821-6721-eb5b-b4076cad23d6.png "スクリーンショット 2015-10-18 21.30.56.png")
そして、先ほど作成したライブラリディレクトリ(libs)を選択します。
![スクリーンショット 2015-10-18 21.34.46.png](https://qiita-image-store.s3.amazonaws.com/0/55941/12b72a92-203e-96d4-e1cd-98c88432f460.png "スクリーンショット 2015-10-18 21.34.46.png")
そうすると以下の画面になると思うので、「OK」を押して完了です。
![スクリーンショット 2015-10-18 21.34.56.png](https://qiita-image-store.s3.amazonaws.com/0/55941/017f1850-c909-3e27-e273-5299da9881a4.png "スクリーンショット 2015-10-18 21.34.56.png")
以上で、WebSocketを使う準備ができました。

##プログラムを書く
前項でJettyを動かす事が出来るようになったので、次はWebSocketを使う為にプログラムを記述していきます。

今回はjarとして動かすのでmain()を作ります。

```java:Main.java
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public class Main {

    public static void main(String[] args){
        Server server = new Server(8080);
        ResourceHandler resourceHandler = new ResourceHandler();

        WebSocketServletImpl wsservlet = new WebSocketServletImpl();
        ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        contextHandler.addServlet(new ServletHolder(wsservlet), "/");

        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[] { resourceHandler, contextHandler });
        server.setHandler(handlers);

        try {
            server.start();
            System.out.println("Server Start");
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
```

```java:WebSocketServletImpl.java

import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

public class WebSocketServletImpl extends WebSocketServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void configure(WebSocketServletFactory factory) {
        factory.register(WebSocketSample.class);
    }
}

```

WebSocketSample.javaは、@WebSocketアノテーションを忘れずに。

```java:WebSocketSample.java

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

@WebSocket //付け忘れに注意
public class WebSocketSample {
    private Session session;

    @OnWebSocketConnect
    public void onConnect(Session session) {
        this.session = session;
        WebSocketBroadcaster.getInstance().join(this);
    }

    @OnWebSocketMessage
    public void onText(String message) {
        WebSocketBroadcaster.getInstance().sendToAll(message);
    }

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
        WebSocketBroadcaster.getInstance().bye(this);
    }

    public Session getSession(){
        return this.session;
    }
}

```

```java:WebSocketBroadcaster.java
import java.util.ArrayList;
import java.util.List;

public class WebSocketBroadcaster {

    private static WebSocketBroadcaster INSTANCE = new WebSocketBroadcaster();
    private List<WebSocketSample> clients = new ArrayList<WebSocketSample>();

    private WebSocketBroadcaster(){}

    protected static WebSocketBroadcaster getInstance(){
        return INSTANCE;
    }
    
    //接続時に呼ばれる
    protected void join(WebSocketSample socket){
        clients.add(socket);
    }

	//切断時に呼ばれる
    protected void bye(WebSocketSample socket){
        clients.remove(socket);
    }

	//全ソケットにmessageを送信
    protected void sendToAll(String message){
        for(WebSocketSample member: clients){
            member.getSession().getRemote().sendStringByFuture(message);
        }
    }

}
```

```xml:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <servlet>
        <display-name>WebSocketSample</display-name>
        <servlet-name>WebSocketSample</servlet-name>
        <servlet-class>WebSocketServletImpl</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>WebSocketSample</servlet-name>
        <url-pattern>/WebSocketSample/*</url-pattern>
    </servlet-mapping>
</web-app>
```

記述したら以下のようになると思います。
![スクリーンショット 2015-10-18 23.39.01.png](https://qiita-image-store.s3.amazonaws.com/0/55941/5c4a9c80-8379-2371-1cce-beb9b4b3df20.png "スクリーンショット 2015-10-18 23.39.01.png")

####おまけ(?)
Mainにサーバを立ち上げる記述があります。

```
server.start();
```

サーバが立ち上がった後に、以下のコードを実行する事で接続中の全クライアントに対してメッセージを送ることが出来ます。

```
WebSocketBroadcaster.getInstance().sendToAll("Hello World!");
```

##実行
上部にある実行ボタンの横のプルダウンメニュの"Edit Configurations"を選択します。
(ここでは既にMainがありますが気にしないで下さい)
![スクリーンショット 2015-10-18 23.47.28.png](https://qiita-image-store.s3.amazonaws.com/0/55941/2bdb716f-7407-60d8-115e-9620ad04d433.png "スクリーンショット 2015-10-18 23.47.28.png")
下図のように「＋」をクリックして"Application"をクリックします。
![スクリーンショット 2015-10-18 23.49.56.png](https://qiita-image-store.s3.amazonaws.com/0/55941/9397e3d0-55fb-9692-c251-3c4ee3eb4ed0.png "スクリーンショット 2015-10-18 23.49.56.png")
名前とMainクラスを設定し、OKをクリックします。
![スクリーンショット 2015-10-18 23.50.54.png](https://qiita-image-store.s3.amazonaws.com/0/55941/47bff47e-4062-7130-d7a1-6daf706f6f85.png "スクリーンショット 2015-10-18 23.50.54.png")
そして、実行ボタンを押すとサーバが起動します。
![スクリーンショット 2015-10-18 23.51.56.png](https://qiita-image-store.s3.amazonaws.com/0/55941/9c496b74-af93-499b-c6cb-713033d03e40.png "スクリーンショット 2015-10-18 23.51.56.png")


###WebSocket接続
サーバを動かした状態で、Chromeの拡張を入れて実際に動作を確認してみます。
[Simple WebSocket Client](https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo)

URLに「ws://localhost:8080/WebSocketSample」を設定してOpenすると接続できます。
接続出来たら、Requestからメッセージを送ってそのまま戻ってくると成功です。
![スクリーンショット 2015-10-18 23.57.19.png](https://qiita-image-store.s3.amazonaws.com/0/55941/27135075-f89a-dc52-e29a-d573125a257d.png "スクリーンショット 2015-10-18 23.57.19.png")


##jarを生成
"Project Structure"を開き、左のペインから"Artifacts"をクリックして「＋」をクリックし、以下を参考に選択します。
![スクリーンショット 2015-10-18 23.58.07.png](https://qiita-image-store.s3.amazonaws.com/0/55941/ed6525a5-cb75-9a21-ec9f-5a8713535933.png "スクリーンショット 2015-10-18 23.58.07.png")

Main Classを設定します。(ここではModule名などがJettyWS2になってますが気にしないで下さい。)
![スクリーンショット 2015-10-18 23.58.15.png](https://qiita-image-store.s3.amazonaws.com/0/55941/6eba38a9-0563-3b95-535c-5e8344050325.png "スクリーンショット 2015-10-18 23.58.15.png")
設定が終わったら、Buildします。
![スクリーンショット 2015-10-19 0.01.05.png](https://qiita-image-store.s3.amazonaws.com/0/55941/4c9ea698-20b9-f9a1-8a1e-0c828086d1d3.png "スクリーンショット 2015-10-19 0.01.05.png")
あとは、jarが生成されるのでそれを叩けばサーバが動きます。