heroku java
DESCRIPTION
Heroku上でJavaアプリケーションを動かすためのチュートリアル。Spring Rooを使ったSpring MVCのサンプルもあり。データストアとして、PostgreSQL、Database.comにも対応TRANSCRIPT
Kazuyuki Kawamura 日本Springユーザグループ
www.heroku.com
今日はJavaユーザ会のCCC
Cloud Application Platform
herokuの新しいスタック サポートする言語とフレームワーク
Ruby Rails 3 Sinatra
Node.js express
Clojure Ring
Java Spring Play!
Python Django
Scala
スタックのソフトウェアバージョン OS: Ubuntu Server 10.04 Ruby: MRI 1.9.2 Bundler: 1.1pre Node.js: 0.4.7 NPM: 1.0.6 JVM: OpenJDK 6
前提条件 (ローカルマシン) JDKとMavenがインストールされている gitがインストールされている
http://code.google.com/p/git-osx-installer/downloads/list?can=3
herokuのコマンドラインクライアントがインストールされている (バージョン 2.1.0以上) gem install heroku
アプリケーションを書く pom.xmlに依存するライブラリを定義 ローカルでテスト herokuへデプロイ
public class HelloWorld extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("Hello from Java!\n"); }
public static void main(String[] args) throws Exception{ Server server = new Server(Integer.valueOf(System.getenv("PORT"))); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); context.addServlet(new ServletHolder(new HelloWorld()),"/*"); server.start(); server.join(); } }
<?xml version="1.0" encoding="UTF-‐8"?> <project xmlns="http://maven.apache.org/
POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-‐instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-‐v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>foo</groupId> <version>1.0-‐SNAPSHOT</version> <name>helloheroku</name> <artifactId>helloheroku</artifactId> <packaging>jar</packaging>
<properties> <java.version>1.6</java.version> <jetty.version> 7.4.4.v20110707 </jetty.version> </properties>
<dependencies>
<!-‐-‐ Jetty -‐-‐> <dependency> <groupId> org.eclipse.jetty </groupId> <artifactId> jetty-‐webapp </artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.mortbay.jetty</groupId> <artifactId> jsp-‐2.1-‐glassfish </artifactId> <version>2.1.v20100127</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId> org.apache.maven.plugins</groupId> <artifactId>maven-‐compiler-‐plugin </artifactId> <version>2.3.2</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>appassembler-‐maven-‐plugin </artifactId> <version>1.1.1</version> <configuration> <assembleDirectory>target </assembleDirectory> <generateRepository>false </generateRepository>
<programs> <program> <mainClass> HelloWorld </mainClass> <name>webapp</name> </program> <programs> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>assemble</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
プロセスモデル Procfileとherokuのプロセス管理コマンド
Procfile heroku上のwebまたはworker dynoで実行されるコマンドを定義
ローカルで実行 mvn install
export REPO=$HOME/.m2/repository foreman start
web: sh target/bin/webapp
12:20:54 web.1 | 2011-10-17 12:20:54.047:INFO::jetty-7.4.4.v20110707 12:20:54 web.1 | 2011-10-17 12:20:54.153:INFO::started o.e.j.s.ServletContextHandler{/,null} 12:20:54 web.1 | 2011-10-17 12:20:54.192:INFO::Started [email protected]:5000 STARTING
prereq: gem install foreman
herokuへデプロイ .gitignoreを作成
gitにアプリケーションを格納 git init
git add .
git commit –m “init”
target
herokuへデプロイ cedar stack上にアプリケーションを作成 heroku create --stack cedar Enter your Heroku credentials. Email: [email protected] Password: Creating quiet-lightning-477... done, stack is cedar http://quiet-lightning-477.herokuapp.com/ | [email protected]:quiet-lightning-477.git Git remote heroku added
herokuへデプロイ コードのデプロイ git push heroku master Counting objects: 6, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (6/6), 629 bytes, done. Total 6 (delta 0), reused 0 (delta 0) -----> Heroku receiving push -----> Node.js app detected -----> Vendoring node 0.4.7 -----> Installing dependencies with npm 1.0.8 [email protected] ./node_modules/express ├── [email protected] ├── [email protected] └── [email protected] Dependencies installed -----> Discovering process types Procfile declares types -> web -----> Compiled slug size is 3.2MB -----> Launching... done, v4 http://quiet-lightning-477.herokuapp.com deployed to Heroku
To [email protected]:quiet-lightning-477.git * [new branch] master -> master
heroku プロセス管理コマンド プロセスの実行数を変更
heroku ps:scale web=1
プロセスのステータスを確認 heroku ps
ログを確認 heroku logs
Process State Command ------------ ------------------ ------------------------------ web.1 idle for 158h sh target/bin/webapp
Scaling web processes... done, now running 1
2011-07-18T05:33:23+00:00 app[web.1]: Listening on 13725 2011-07-18T05:33:42+00:00 heroku[web.1]: State changed from starting to up
サンプルプログラム Rooを利用して作成 DBにデータを格納するSpring MVCを利用したWebアプリケーション
Pizza Shop http://www.springsource.org/roo/guide?w=beginning
作成したサンプルをHerokuで実行するように変更
project --topLevelPackage com.sample.pizzashop Mavenを利用してビルド AspectJを利用している
persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
JPAの初期設定 persistence.xmlやSpringの設定などを追加
作成するエンティティ Topping – トッピング Base – 生地 Pizza – ピザ PizzaOrder – ピザの注文
entity --class ~.domain.Topping --testAutomatically field string --fieldName name --notNull --sizeMin 2
entity --class ~.domain.Base --testAutomaticallyfield string --fieldName name --notNull --sizeMin 2
entity --class ~.domain.Pizza –testAutomatically field string --fieldName name --notNull --sizeMin 2
field number --fieldName price --type java.lang.Floatfield set --fieldName toppings --type ~.domain.Topping field reference --fieldName base --type ~.domain.Base
entity --class ~.domain.PizzaOrder --testAutomaticallyfield
string --fieldName name --notNull --sizeMin 2 field string --fieldName address --sizeMax 30 field number --fieldName total --type java.lang.Float field date --fieldName deliveryDate --type java.util.Date field set --fieldName pizzas --type ~.domain.Pizza
web mvc setup web mvc all --package ~.web
perform tests テストの実行
perform eclipse Eclipse関連のファイルの作成
ひとまず実行 mvn jetty:run http://localhost:8080/pizzashop へアクセス
herokuへデプロイ .gitignoreを作成
gitにアプリケーションを格納 git init
git add .
git commit –m “init”
herokuにデプロイ git push heroku master
target
Herokuが提供するデータベースはPostgreSQL Shared Databaseは5MBまで無償で利用可能 有料オプションもあり
DATABASE_URL環境変数にDBへの接続情報が格納されている
データベース関連の設定 DBとしてPostgreSQLを利用するように設定
以下をRooで実行 persistence setup --provider HIBERNATE --database POSTGRES
Spring設定ファイル(src/main/resources/META-INF/spring/applicationContext.xml)の編集 <bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"> <property name="driverClassName" value="org.postgresql.Driver"/> <property name="url" value="#{systemEnvironment['DATABASE_URL'].replaceAll( 'postgres://(.*):(.*)@(.*)', 'jdbc:postgresql://$3?user=$1&password=$2') }"/> </bean> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"> <property name="persistenceUnitName" value="persistenceUnit"/> <property name="dataSource" ref="dataSource"/> </bean>
データベース関連の設定 persistence.xml(src/main/resources/META-INF/
persistence.xml)の編集 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <persistence …⋯> <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL"> <provider>org.datanucleus.jpa.PersistenceProviderImpl</provider> <properties> <property name="datanucleus.autoCreateSchema" value="false"/> <property name="datanucleus.storeManagerType" value="rdbms"/> <property name="datanucleus.ConnectionURL” value="jdbc:postgresql://localhost:5432"/> <property name="datanucleus.ConnectionUserName" value=""/> <property name="datanucleus.ConnectionPassword" value=""/> <property name="datanucleus.autoCreateTables" value="true"/> …⋯ </properties> </persistence-unit> </persistence>
起動用のjettyの追加 pom.xmlに以下を追加 <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.5.1.v20110908</version> <destFileName>jetty-runner.jar</destFileName> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin>
サービス起動用の設定 Procfileを作成
ローカルで起動 環境変数DATABASE_URLを設定
export DATABASE_URL=postgres://demo:demo@localhost/demo サービスを起動
foreman start → http://locahost:5000/
web: java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war
herokuへデプロイ gitにアプリケーションを格納 git add .
git commit –m “Support PostgreSQL”
herokuにデプロイ git push heroku master
www.database.com native cloud database service
RESTやSOAPのAPIを通してデータにアクセス OAuth、SAMLを用いて認証
http://forcedotcom.github.com/java-sdk/ Javaアプリケーションからdatabase.comにアクセスするためのモジュール API Connector JPA Provider (datanucleusベース) Oauthによる認証/認可 Spring Securityプラグイン 自動コード生成モジュール (既存のオブジェクトからEntityを自動生成)
データベース関連の設定 JPAプロバイダとしてDatanucleusを利用するように設定 (databaseは何でも良い) 以下をRooで実行 persistence setup --provider DATANUCLEUS_2 --database POSTGRES
pom.xmlを変更 database.com SDKをdependencyに追加
datanucleusのバージョンの変更
<dependency> <groupId>com.force.sdk</groupId> <artifactId>force-jpa</artifactId> <version>22.0.7-BETA</version> </dependency>
<dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-core</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-jpa</artifactId> <version>2.1.7</version> </dependency>
プラグインのdependencyも要変更
persistence.xml(src/main/resources/META-INF/persistence.xml)の変更 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" …⋯> <persistence-unit name="forceDatabase"> <provider>com.force.sdk.jpa.PersistenceProviderImpl</provider> <properties> <property name="datanucleus.autoCreateSchema" value="true" /> <property name="datanucleus.storeManagerType" value="force" /> <property name="datanucleus.validateTables" value="false" /> <property name="datanucleus.validateConstraints" value="false" /> <property name="datanucleus.Optimistic" value="false" /> <property name="datanucleus.datastoreTransactionDelayOperations" value="true"/> <property name="datanucleus.jpa.addClassTransformer" value="false" /> <property name="datanucleus.cache.level2.type" value="none" /> <property name="datanucleus.detachAllOnCommit" value="true" /> <property name="datanucleus.copyOnAttach" value="false" /> </properties> </persistence-unit> </persistence>
Spring設定ファイル(src/main/resources/META-INF/spring/applicationContext.xml)の変更
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="forceDatabase" /> </bean>
<bean id="forceService" class="com.force.sdk.connector.ForceServiceConnector"> <property name="connectionName" value="forceDatabase"/> </bean>
Entityの変更 Database.comの制限
IDはStringでなければいけない
VersionフィールドはlastModifiedDateにマッピング
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private String Base.id;
@Version @Column(name = "lastModifiedDate") @Temporal(TemporalType.TIMESTAMP) private Calendar Base.version;
Base_Roo_Entity.aj
Base_Roo_Entity.aj
Entityの変更 Database.comの制限
ManyToManyは未サポート 要件に合わせて、OneToMany、ManyToOneに変更する 中間テーブル用のEntityを作成する
上記変更に合わせ、以下も修正 テストクラス Webのコントローラクラス
環境変数の設定 database.comのコネクタは、FORCE_<コネクション名>_URL環境変数で指定された接続先に接続 コネクション名は、JPAであればpersistence-unitの名前
export FORCE_FORCEDATABASE_URL="force://[email protected]&password=passwd"
ローカルで実行 mvn jetty:run
環境変数の設定 heroku config:add FORCE_FORCEDATABASE_URL="force://[email protected]&password=passwd"
デプロイ git add .
git commit -m "Support Database.com"
git push heroku master
ログを確認 heroku logs
Webブラウザでアプリケーションにアクセス heroku open
addons.heroku.com
Java on heroku heroku上でJavaアプリケーションを動かすことができる
gitを利用してデプロイ Mavenを利用してビルド Java EEではない
データソース herokuで提供されるDBはPostgreSQL database.comなど外部のツールとも連携可能 herokuのAdd-onによって様々なデータソースが利用可能