3
2

More than 3 years have passed since last update.

無限の猿定理をWebアプリ化した

Posted at

はじめに

皆さんは『無限の猿定理』というものをご存知だろうか。

無限の猿定理(むげんのさるていり、英語: infinite monkey theorem)とは、十分長い時間をかけてランダムに文字列を作り続ければ、どんな文字列もほとんど確実にできあがるという定理である。比喩的に「猿がタイプライターの鍵盤をいつまでもランダムに叩きつづければ、ウィリアム・シェイクスピアの作品を打ち出す」などと表現されるため、この名がある。

Wikipediaより

要するに”無限”というものの凄さの例えである。
この定理に着想を得て、以前「頭文字を打つだけで文章ができていくアプリケーション」というものを作成した。
このプログラムはコンソール上だけでの動作だったので,Webアプリに移植して,公開することにした。
URLはこちら。

https://infinite-monkey-theorem.herokuapp.com/

本記事では、公開までに躓いたポイントの備忘録である。

環境
Windows10
VSCode
構成
Java11
Spring-Boot
PostgreSQL
Heroku

ローカル開発環境の準備

VSCodeに必要な拡張機能をインストールする

  • Java Extension Pack
  • Spring Boot Extension Pack
  • Lombok Annotations Support for VS Code

アプリの雛型を自動生成する

Ctrl+Shift+Pでコマンドパレットを開く
"Spring.Initializr: Create a Maven Project..."を選択
Spring Boot Version 2.5.3を選択
Project Language Javaを選択
Group Idを入力,今回はworks.izumi
Artifact Id を入力,今回はinfinite_monkey_theorem
packaging type Jarを入力
Java Version 11を選択
dependenciesは以下を選択

  • Spring Boot Dev Tools
  • Lombok
  • Spring Web
  • Thymeleaf
  • Spring Data JPA
  • PostgreSQL Driver

プロジェクトを格納するフォルダを選択して終了。

初期動作確認

動作確認のため以下のファイルを追加。

【躓きポイント】利用するDBを指定しないと実行に失敗するので注意

SPRING BOOT DASHBOARDから実行し,localhost:8080にアクセス。
『hello』と表示されたら,成功。

demo\src\main\java\com\example\demo\controller\AppController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AppController {

    @ResponseBody
    @RequestMapping("/")
    public String init(){
        return "hello";
    }

}
demo\src\main\resources\application.properties
# 接続情報
spring.datasource.url=jdbc:postgresql://localhost:5432/[データベース名]
spring.datasource.username=[ユーザ名]
spring.datasource.password=[パスワード]

DB作成と接続テスト

pgAdminでテーブルを作成。
image.png

さらに,Entity,Repository,serviceを追加し,controllerも追記.

AppEntity.java
package com.example.demo.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Data
@Table(name="test")
public class AppEntity implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int nounId;

    @Column(name="initial")
    private String initial;

    @Column(name="notation")
    private String notation;

}

AppRepository.java
package com.example.demo.repo;

import java.util.List;

import com.example.demo.model.AppEntity;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface NounRepository extends JpaRepository<AppEntity, Integer>{

    public List<AppEntity> findByInitial(String initial); 

}

AppService.java
package com.example.demo.service;

import java.util.List;

import com.example.demo.model.AppEntity;

import org.springframework.stereotype.Service;

@Service
public interface AppService {

    public List<AppEntity> test();

}

AppServiceImpl.java
package com.example.demo.service.impl;

import java.util.List;

import com.example.demo.model.AppEntity;
import com.example.demo.repo.NounRepository;
import com.example.demo.service.AppService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AppServiceImpl implements AppService {

    @Autowired
    NounRepository repo;

    @Override
    public List<AppEntity> test(){

        return repo.findAll();

    }

}

AppController.java
package com.example.demo.controller;

import com.example.demo.service.AppService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AppController {

    @Autowired
    AppService service;

    @ResponseBody
    @RequestMapping("/")
    public String init(){
        return "hello";
    }

    @ResponseBody
    @RequestMapping("test")
    public String test(){
        return service.test().toString();
    }

}

localhost:8080/testにアクセスし,テーブルの中身が表示されれば成功。

Herokuへデプロイ

こちらの記事が大変参考になる。
とりあえずHeroku + Spring Boot デプロイ

【躓きポイント】Procfileとsystem.propertiesをきちんと追加・設定しよう

これでおそらくデプロイは成功する。
しかしHerokuのURLにアクセスしようとすると、おそらく『Application error』になる。
その場合、Procfileで設定したjarファイルが生成されていない可能性が高い。
こちらの記事が大変参考になる。

Herokuにデプロイしたアプリの更新で「Application error」

【躓きポイント】Mavenビルドとjarファイルをgit addしよう

Sleep対策

Herokuの無料枠では30分アクセスがないとアプリケーションがスリープしてしまう。
スリープ中にアクセスするとSpring Bootの起動から始まるので表示にとても時間がかかる。
そこでNew Relicアドオンを用いて15分毎にアクセスを行うことで、スリープを防止した。
こちらの記事が大変参考になる。

Heroku Free DynoのSleepを防止する

その他忘れがちなこと

  • テストクラスに@SpringBootTestをつける
  • Ajax(jQuery)の雛型: contentType: "application/json" とすることで、サーバ側@RequestBodyでうまく受け取れるようになる
AjaxSample.js
$.ajax({
    type: "POST",
    url: "/hoge",
    contentType: "application/json",
    data: val,
})
.done(function (data) {
    // 通信成功時
    alert("通信成功");
})
.fail(function (data) {
    // 通信失敗時の処理
    alert("通信失敗");
})
.always(function (data) {
    // 常に実行する処理
    console.log(data);
});

おわりに

お盆休みの暇な時間を有効に使おうと思い、セルフサマーキャンプ的なノリで始めた本開発。
実装の中身は既にあったので、今回はクラウドサーバへのアップロードとUIをメインの課題として取り組んだ。
飽き性なのでこれくらいコンパクトに絞って本当に良かった。
今度はツール的なものの開発に取り組んでみようかな。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2