ラズパイにIoT開発環境を構築するという自前のミッションがあるのですが、急遽ラズパイにJava製のWebサーバーを起動しようということになりました。※そのように考えた次第です。。。
具体的に、Iotの実装を行うためにはそれなりの開発環境を構築する必要があります。※JavaME環境
しかし、自分が持っているラズパイは古いので、遅い。。。開発環境を構築して作業を行うのはちょいと面倒なわけです。
ラズパイでの開発環境構築が必要な理由は、デバイス(USB、GPIOなど)へのアクセスが必要になるため実機での開発環境が必要になります。
なので、今回は古いラズパイ(RPi2 model B)での実装を行うので、Windows上で開発したJavaサーバーをラズパイに移植して起動しようという考えです。
具体的な手順
- Widnows上で開発、テストを行う
- JARファイル(実行ファイル)を出力して起動できる確認をする
- 作成したコードをGithubにコミットする
- ラズパイでGithubからソースをチェックアウトしてビルド、JARファイルを作成
- ラズパイの起動時に実行するシェル・スクリプトで手順4の処理を行う
上記のような手順で作業を行うと結果としては、下のように動くであろうというところです。
- ラズパイに電源を入れる
- 起動スクリプトが動く
- 作成したJavaサーバーが起動する
問題は、作成したJavaサーバーに何を行わせるか?というところです。
今回作成する、JavaサーバーはHTTPプロトコル以外でも受信し処理を行うことができるものを作成します。
「なぜそれが可能か?」というところですが、これは低レベルな実装なためHTTPだろうが、バイナリだろうが関係なく受信、レスポンスの送信を行うことが可能になります。
その代わり、以下のような部分を実装する必要があります。
- 受信したリクエストがHTTPなのか、それ以外なのか?を判別
- HTTPリクエストならばHTTPでレスポンスを返し、それ以外はそれぞれのレスポンスを返す
- それぞれのプロトコル(HTTP, FTPなど)で受信したときにどのような処理を行うのか?
通常というか身近にある「ウェブサーバー」と呼ばれているものはHTTPリクエストを受け、それに対応するHTMLを読みこんで、そのテキストデータ(HTML)を返却、ブラウザで表示というような処理を行っていますが、
今回作成するJavaサーバーは低レベルなため、大体のことは大体できますが、以下の部分を実装する必要があります。
HTTPリクエストを受け、それに対応するHTMLを読みこんで、そのテキストデータ(HTML)を返却
この部分を実装できるようになると、自作のフレームワークを作成したり一人で多くの作業を行おうとするときに「前もって作成しておく道具」として使用することができます。このような「道具」を作成しておくとやらなければいけいない作業が減り、いろいろなことができるのではないでしょうか?
注意点
上記のJavaサーバーは、ServerSocketを使用して作成しますが、これの実装をする前に理解しておく必要がいくつかあります。
- サーバーとクライアントの関係に関する理解
- Webサーバー、アプリケーションサーバー(APサーバー)、DBサーバーの関係性に対する理解
- MVCモデルの概要に対する理解
まとめると次のようになります。
- リクエストの送信と受信を行うときに「サーバー」と「クライアント」の関係がある
- HTTP以外にもプロトコルがあり、ブラウザはHTTP、FTPなどのプロトコルを使用する
- HTTP以外のプロトコルで、Socket通信のようにバイトデータをそのまま送信することもある
- 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にアクセスしたときはどのようになるか?
下の画像は、次のコマンドをコマンドプロンプトでたたいたときの結果です。
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メッセージ
よく、「Httpヘッダー」とか「Httpボディ」とか言ったりしますがHttpメッセージの「ヘッダ」「ボディ」と言うのが正確な言い方です。
参考の処理内容
- ServerSocketをXXXポートで待機
- リクエストを受け付けたら、受け取ったメッセージ(Httpメッセージ)から相手のIPを取得
- IPを使用してHttpレスポンスを作成
- OuputStreamでリクエスト元に返却する(レスポンスを返却する)
と言うような流れで処理を作成しているようです。
レシピ
詳細に関しては、Java APIを参照してください。
こんなくらいです。
webサーバー
- リクエストを受けて(SocketServer#accept())
- リクエストの値からファイルを読み込む
- HTTPメッセージボディにHTMLを書く
- データ(DataOutputSream)を返却
シンプルにこんな手順で処理を行えば良いと思います。
低レベルなAPIは、全て作成する必要がありますが、逆に言えば全てが想い通りに作れると言うところが魅力的(笑)
こんな感じで考えております。このアプリにどんな機能を追加するかは作成者の思うままですね。。。
何やろうかな?
でわでわ。。。