Getting Started with Spring MVC Hibernate on Heroku/Cedar をためしてみてる
昨日に引き続きjavaでherokuと戯れてみる。
今回は Getting Started with Spring MVC Hibernate on Heroku | Heroku Dev Center をやってみる。
Prerequisites
java,maven.git,heroku client, foreman が必要とのこと。これらは昨日のエントリでいれたのでOK。
あと、ローカルでのテストのためにPostgresが必要とのこと。このあたりを参考にした。
$ sudo port install postgresql90 $ sudo port install postgresql90-server
9/12に9.1がリリースされてるが、MacPortsだとまだALPHA RELEASEっぽいので9.0にした。
データ領域を作成。
$ sudo mkdir -p /opt/local/var/db/postgresql90/defaultdb $ sudo chown postgres:postgres /opt/local/var/db/postgresql90/defaultdb $ sudo su postgres -c '/opt/local/lib/postgresql90/bin/initdb -D /opt/local/var/db/postgresql90/defaultdb'
で、起動。
$ sudo su postgres -c '/opt/local/lib/postgresql90/bin/postgres -D /opt/local/var/db/postgresql90/defaultdb'
Create a Spring MVC Hibernate app
Spring MVC Hivernate アプリは Spring Roo を使うととても簡単につくれるよ!
っぽいことが書かれている。SpringもHibernateもきちんとさわったことないので Option2 でいくことにする。
ちなみに Option1 はサンプルコードを git clone せよとのこと。
Option 2. Create the App Using Spring Roo
Spring Roo をインストールせよ。とのことなのでインストールしてみる。
Community Downloads | SpringSource.orgにLastest GA release 1.1.5.RELEASE というのがあるのでこれをDL。
このあたりをみると解凍して bin/roo.sh をPATHに追加すればいいっぽい。
今回は bin/roo.sh のシンボリックリンクを /usr/local/bin においてみた。
$ sudo ln -s ~/Library/Java/spring-roo-1.1.5.RELEASE/bin/roo.sh /usr/local/bin/roo
roo を実行するとこんなかんじ。rooのプロンプトが現れる。exitで抜けられる。
$ roo ____ ____ ____ / __ \/ __ \/ __ \ / /_/ / / / / / / / / _, _/ /_/ / /_/ / /_/ |_|\____/\____/ 1.1.5.RELEASE [rev d3a68c3] Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER. At this time you have not authorized Spring Roo to download an index of available add-ons. This will reduce Spring Roo features available to you. Please type 'download status' and press ENTER for further information. roo>
さて、roo がインストールできたところでアプリケーションを作成していく。
まず、今回のアプリは petclinic らしいのでアプリのディレクトリを作成する。
$ mkdir petclinic && cd petclinic
で、roo でアプリを生成せよとのこと。以下を実行。
$ roo script --file clinic.roo
大量のログとともにアプリのファイルを生成してるっぽい。
実行後のディレクトリ構造はこんな感じ。
$ tree -a . ├── log.roo ├── pom.xml └── src ├── main │   ├── java │   │   └── com │   │   └── springsource │   │   └── petclinic │   │   ├── domain │   │   │   ├── AbstractPerson.java │   │   │   ├── AbstractPerson_Roo_Configurable.aj │   │   │   ├── AbstractPerson_Roo_Entity.aj │   │   │   ├── AbstractPerson_Roo_JavaBean.aj │   │   │   ├── AbstractPerson_Roo_ToString.aj │   │   │   ├── Owner.java │   │   │   ├── Owner_Roo_Configurable.aj │   │   │   ├── Owner_Roo_Entity.aj │   │   │   ├── Owner_Roo_JavaBean.aj │   │   │   ├── Owner_Roo_ToString.aj │   │   │   ├── Pet.java │   │   │   ├── Pet_Roo_Configurable.aj │   │   │   ├── Pet_Roo_Entity.aj │   │   │   ├── Pet_Roo_Finder.aj │   │   │   ├── Pet_Roo_JavaBean.aj │   │   │   ├── Pet_Roo_ToString.aj │   │   │   ├── Vet.java │   │   │   ├── Vet_Roo_Configurable.aj │   │   │   ├── Vet_Roo_Entity.aj │   │   │   ├── Vet_Roo_JavaBean.aj │   │   │   ├── Vet_Roo_ToString.aj │   │   │   ├── Visit.java │   │   │   ├── Visit_Roo_Configurable.aj │   │   │   ├── Visit_Roo_Entity.aj │   │   │   ├── Visit_Roo_Finder.aj │   │   │   ├── Visit_Roo_JavaBean.aj │   │   │   └── Visit_Roo_ToString.aj │   │   ├── reference │   │   │   ├── PetType.java │   │   │   └── Specialty.java │   │   └── web │   │   ├── ApplicationConversionServiceFactoryBean.java │   │   ├── ApplicationConversionServiceFactoryBean_Roo_ConversionService.aj │   │   ├── OwnerController.java │   │   ├── OwnerController_Roo_Controller.aj │   │   ├── PetController.java │   │   ├── PetController_Roo_Controller.aj │   │   ├── PetController_Roo_Controller_Finder.aj │   │   ├── VetController.java │   │   ├── VetController_Roo_Controller.aj │   │   ├── VisitController.java │   │   ├── VisitController_Roo_Controller.aj │   │   └── VisitController_Roo_Controller_Finder.aj │   ├── resources │   │   ├── META-INF │   │   │   ├── persistence.xml │   │   │   └── spring │   │   │   ├── applicationContext.xml │   │   │   └── database.properties │   │   └── log4j.properties │   └── webapp │   ├── WEB-INF │   │   ├── classes │   │   │   ├── alt.properties │   │   │   └── standard.properties │   │   ├── i18n │   │   │   ├── application.properties │   │   │   ├── messages.properties │   │   │   ├── messages_de.properties │   │   │   └── messages_es.properties │   │   ├── layouts │   │   │   ├── default.jspx │   │   │   └── layouts.xml │   │   ├── spring │   │   │   └── webmvc-config.xml │   │   ├── tags │   │   │   ├── form │   │   │   │   ├── create.tagx │   │   │   │   ├── dependency.tagx │   │   │   │   ├── fields │   │   │   │   │   ├── checkbox.tagx │   │   │   │   │   ├── column.tagx │   │   │   │   │   ├── datetime.tagx │   │   │   │   │   ├── display.tagx │   │   │   │   │   ├── editor.tagx │   │   │   │   │   ├── input.tagx │   │   │   │   │   ├── reference.tagx │   │   │   │   │   ├── select.tagx │   │   │   │   │   ├── simple.tagx │   │   │   │   │   ├── table.tagx │   │   │   │   │   └── textarea.tagx │   │   │   │   ├── find.tagx │   │   │   │   ├── list.tagx │   │   │   │   ├── show.tagx │   │   │   │   └── update.tagx │   │   │   ├── menu │   │   │   │   ├── category.tagx │   │   │   │   ├── item.tagx │   │   │   │   └── menu.tagx │   │   │   └── util │   │   │   ├── language.tagx │   │   │   ├── load-scripts.tagx │   │   │   ├── pagination.tagx │   │   │   ├── panel.tagx │   │   │   ├── placeholder.tagx │   │   │   └── theme.tagx │   │   ├── views │   │   │   ├── dataAccessFailure.jspx │   │   │   ├── footer.jspx │   │   │   ├── header.jspx │   │   │   ├── index-template.jspx │   │   │   ├── index.jspx │   │   │   ├── menu.jspx │   │   │   ├── owners │   │   │   │   ├── create.jspx │   │   │   │   ├── list.jspx │   │   │   │   ├── show.jspx │   │   │   │   ├── update.jspx │   │   │   │   └── views.xml │   │   │   ├── pets │   │   │   │   ├── create.jspx │   │   │   │   ├── findPetsByNameAndWeight.jspx │   │   │   │   ├── findPetsByOwner.jspx │   │   │   │   ├── findPetsBySendRemindersAndWeightLessThan.jspx │   │   │   │   ├── findPetsByTypeAndNameLike.jspx │   │   │   │   ├── list.jspx │   │   │   │   ├── show.jspx │   │   │   │   ├── update.jspx │   │   │   │   └── views.xml │   │   │   ├── resourceNotFound.jspx │   │   │   ├── uncaughtException.jspx │   │   │   ├── vets │   │   │   │   ├── create.jspx │   │   │   │   ├── list.jspx │   │   │   │   ├── show.jspx │   │   │   │   ├── update.jspx │   │   │   │   └── views.xml │   │   │   ├── views.xml │   │   │   └── visits │   │   │   ├── create.jspx │   │   │   ├── findVisitsByDescriptionAndVisitDate.jspx │   │   │   ├── findVisitsByDescriptionLike.jspx │   │   │   ├── findVisitsByVisitDateBetween.jspx │   │   │   ├── list.jspx │   │   │   ├── show.jspx │   │   │   ├── update.jspx │   │   │   └── views.xml │   │   └── web.xml │   ├── images │   │   ├── add.png │   │   ├── banner-graphic.png │   │   ├── create.png │   │   ├── de.png │   │   ├── delete.png │   │   ├── en.png │   │   ├── es.png │   │   ├── favicon.ico │   │   ├── list.png │   │   ├── resultset_first.png │   │   ├── resultset_last.png │   │   ├── resultset_next.png │   │   ├── resultset_previous.png │   │   ├── show.png │   │   ├── springsource-logo.png │   │   └── update.png │   ├── selenium │   │   ├── test-owner.xhtml │   │   ├── test-pet.xhtml │   │   ├── test-suite.xhtml │   │   ├── test-vet.xhtml │   │   └── test-visit.xhtml │   └── styles │   ├── alt.css │   └── standard.css └── test ├── java │   └── com │   └── springsource │   └── petclinic │   └── domain │   ├── OwnerDataOnDemand.java │   ├── OwnerDataOnDemand_Roo_Configurable.aj │   ├── OwnerDataOnDemand_Roo_DataOnDemand.aj │   ├── OwnerIntegrationTest.java │   ├── OwnerIntegrationTest_Roo_Configurable.aj │   ├── OwnerIntegrationTest_Roo_IntegrationTest.aj │   ├── PetDataOnDemand.java │   ├── PetDataOnDemand_Roo_Configurable.aj │   ├── PetDataOnDemand_Roo_DataOnDemand.aj │   ├── PetIntegrationTest.java │   ├── PetIntegrationTest_Roo_Configurable.aj │   ├── PetIntegrationTest_Roo_IntegrationTest.aj │   ├── VetDataOnDemand.java │   ├── VetDataOnDemand_Roo_Configurable.aj │   ├── VetDataOnDemand_Roo_DataOnDemand.aj │   ├── VetIntegrationTest.java │   ├── VetIntegrationTest_Roo_Configurable.aj │   ├── VetIntegrationTest_Roo_IntegrationTest.aj │   ├── VisitDataOnDemand.java │   ├── VisitDataOnDemand_Roo_Configurable.aj │   ├── VisitDataOnDemand_Roo_DataOnDemand.aj │   ├── VisitIntegrationTest.java │   ├── VisitIntegrationTest_Roo_Configurable.aj │   └── VisitIntegrationTest_Roo_IntegrationTest.aj └── resources
かなりのファイル数があるな・・・でもpom.xmlがあったりmavenのディレクトリ構造と同じだったりするのでなんとなくわかるな。
デフォルトのDBがHypersonicのインメモリDBらしい。production環境と同じDB使うほうが絶対いいよといっているっぽいので postgres にスイッチする。
$ roo persistence setup --provider HIBERNATE --database POSTGRES ____ ____ ____ / __ \/ __ \/ __ \ / /_/ / / / / / / / / _, _/ /_/ / /_/ / /_/ |_|\____/\____/ 1.1.5.RELEASE [rev d3a68c3] Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER. Updated SRC_MAIN_RESOURCES/META-INF/spring/database.properties Please update your database details in src/main/resources/META-INF/spring/database.properties. Updated ROOT/pom.xml [removed dependency org.hsqldb:hsqldb:1.8.0.10; added dependency postgresql:postgresql:8.4-702.jdbc3] Updated SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml Updated SRC_MAIN_RESOURCES/META-INF/persistence.xml At this time you have not authorized Spring Roo to download an index of available add-ons. This will reduce Spring Roo features available to you. Please type 'download status' and press ENTER for further information.
ちょっと脱線だが、最後の3行はよく見かけるので言われるとおり download status を実行し、指示に従い了承っぽいことをした。どうやらリソースをダウンロードするには必要なようだ。
で、roo で生成したファイルを git にコミットする。
$ git init $ echo target > .gitignore $ git add . $ git commit -m "init"
Modify Database Configuration
roo で生成したファイルにはDBへの接続情報が書かれているが、ハードコードされているらしい。(ここらしい→ src/main/resources/META-INF/spring/database.properties)
ハードコードはイクナイので環境変数から読み込むようにしよう。っぽいことが書かれている。
環境変数名は"DATABASE_URL"の模様。で、この変数のフォーマットはこんなかんじ。
postgres://user:password@hostname/path
で、そのために src/main/resources/META-INF/spring/applicationContext.xml を編集する。以下の bean 定義を追加と、
<bean class="java.net.URI" id="dbUrl"> <constructor-arg value="${DATABASE_URL}"/> </bean>
以下のように id="dataSource" 要素内の property 定義を書き換える。
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"> <property name="driverClassName" value="${database.driverClassName}"/> <property name="url" value="#{ 'jdbc:postgresql://' + @dbUrl.getHost() + @dbUrl.getPath() }"/> <property name="username" value="#{ @dbUrl.getUserInfo().split(':')[0] }"/> <property name="password" value="#{ @dbUrl.getUserInfo().split(':')[1] }"/> ...
で、実行するために環境変数"DATABASE_URL"がいるので定義する。
export DATABASE_URL=postgres://scott:tiger@localhost/myapp
Add Jetty Runner
Jettyで実行するために、Jetty-runner のライブラリを target ディレクトリに置く必要があるっぽい。
それを実現するために、maven-dependency-plugin を使う模様。
以下を pom.xml 内の plugins セクションの最後に追加する。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals><goal>copy</goal></goals> <configuration> <artifactItems> <artifactItem> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-runner</artifactId> <version>7.4.5.v20110725</version> <destFileName>jetty-runner.jar</destFileName> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin>
Declare Process Types With Foreman/Procfile
昨日のエントリとほぼ同じ。Procfileを用意する。
が、違うところは jetty-runner で war ファイルを渡しているところか。これで webアプリを jetty 上で動かせるということか。
web: java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war
Run Your App Locally
Build Your App
では、ビルドしてみよう。
$ mvn package
んー、ビルドでエラーがでるな。どうやらテストでエラーな模様。Postgresの標準出力に以下のようなログが多数。
FATAL: role "scott" does not exist
Postgres のユーザとか一切作ってないからっぽい。postgres が起動している状態で以下を行った。
$ /opt/local/lib/postgres90/bin/psql -U postgres postgres=# create user scott; postgres=# \password scott Enter new password: Enter it again: postgres=# \q
もっかい動かしてみると以下のログに変わった。
FATAL: database "myapp" does not exist
どうやら、databaseをつくっていないからっぽい。で、以下を行った。
$ /opt/local/lib/postgresql90/bin/createdb -U postgres myapp
で、もっかい mvn package やったらテスト通った!ビルド成功!
Start Your App With Foreman/Procfile
起動してみる。
$ foreman start
で、 http://localhost:5000 へアクセス。おー、Rooの画面が表示された!
Test it
画面からデータも登録できたっぽい。いいかんじ。
Deploy to Heroku/Cedar
さぁ、herokuへデプロイ。
まず、これまでの変更をコミット。
$ git add . $ git commit -m "Ready to deploy"
herokuのCedarスタックも作成する。
$ heroku create --stack cedar
heroku へデプロイ!
$ git push heroku master
ビルドも成功!
$ heroku open
で、画面も確認できたしデータも登録できた!