Heroku Embedded Tomcat + Scala2.9.1

Posted 2012年1月2日 by
Tagged As: | Categories: Scala | Heroku Embedded Tomcat + Scala2.9.1 はコメントを受け付けていません

Embedded Tomcat7 をランチするScala Object のソースコードです。

Heroku/src/main/scala/launch/Main.scala ファイルです。

このMainオブジェクトは先に書いたMain.javaをScalaコードに書き換えたものです。

package launch

import java.util._
import java.net.URL
import java.net.MalformedURLException
import java.net.URLClassLoader
import java.io.File
import java.io.IOException
import java.io.Writer
import javax.servlet.Servlet
import javax.servlet.ServletException
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

import org.apache.catalina._
import org.apache.catalina.connector.Connector
import org.apache.catalina.core.StandardHost
import org.apache.catalina.core.StandardServer
import org.apache.catalina.core.AprLifecycleListener
import org.apache.catalina.startup.Embedded
import org.apache.catalina.startup.Tomcat
import org.apache.coyote.http11.AbstractHttp11JsseProtocol
import org.apache.tomcat.util.IntrospectionUtils

object Main {

	def main(args: Array[String]): Unit = {
		val webappDirLocation = "src/main/webapp/"
		val tomcat = new Tomcat
		//The port that we should run on can be set into an environment variable
		//Look for that variable and default to 8080 if it isn't there.
		var webPort = System.getenv("PORT")
		if(webPort == null || webPort.isEmpty()) {
			webPort = "8080"
		}
		tomcat.setPort(Integer.valueOf(webPort))
		var appBase = new File(webappDirLocation).getAbsolutePath
		val contextPath = "/"
		// Add AprLifecycleListener
		val server = tomcat.getServer
		var listener = new AprLifecycleListener
		server.addLifecycleListener(listener)

		val configFile = new File("META-INF/context.xml")
		val context = tomcat.addWebapp(contextPath, appBase)
		context.setConfigFile(configFile.toURI().toURL())
		println("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath())
		tomcat.enableNaming
		tomcat.start
		tomcat.getServer().await
	}
}


このコードの特徴は、

META-INF/context.xml を読み込んでいることです。
これでTomcat7のJDBCコネクション・プールが使えるようになります。

最初のアクセスはDBセッションを作るので遅いですが、それ以降はセッションがプールから得られるので早いです。

META-INF/context.xml ファイルの内容は、




    


ここまで来るには、いろいろと障害がある訳です。
ローカルの環境で動いたからといっても、Herokuで動くとは限らない。

その障害の1つ。

Main.javaの環境があるとところでこのScalaオブジェクトがあると、HerokuへファイルをアップロードするとすでにMain.javaがあるからとエラーとなってしまいました。

これを回避するには、ローカル環境にある.gitフォルダをいったんすべて削除してから、
Deploy to Herokuしました。

Commit your changes to Git:


$ git init
$ git add .
$ git commit -m "Ready to deploy"


新しいインスタンスを作ります。
Create the app on the Cedar stack:

$ heroku create --stack cedar


インストールが成功すると以下のようにメッセージされます。
Deploy your code:


$ git push heroku master
............

       [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ embedde
dTomcatSeedo ---
       [INFO] Installing /tmp/build_22ibt93uw91ys/target/embeddedTomcatSeedo.jar
 to /app/tmp/repo.git/.cache/.m2/repository/com/heroku/seedo/embeddedTomcatSeedo
/1.0-SNAPSHOT/embeddedTomcatSeedo-1.0-SNAPSHOT.jar
       [INFO] Installing /tmp/build_22ibt93uw91ys/pom.xml to /app/tmp/repo.git/.
cache/.m2/repository/com/heroku/seedo/embeddedTomcatSeedo/1.0-SNAPSHOT/embeddedT
omcatSeedo-1.0-SNAPSHOT.pom
       [INFO] ------------------------------------------------------------------
------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------
------
       [INFO] Total time: 1:33.414s
       [INFO] Finished at: Mon Jan 02 14:03:43 UTC 2012
       [INFO] Final Memory: 13M/490M
       [INFO] ------------------------------------------------------------------
------
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 17.8MB
-----> Launching... done, v5
       http://radiant-mountain-7703.herokuapp.com deployed to Heroku

To git@heroku.com:radiant-mountain-7703.git
 * [new branch]      master -> master

ewave@WYOMING ~/workspace/heroku (master)
$


[INFO] BUILD SUCCESS とくれば成功!

Heroku Embedded Tomcat + Seedo DB framework(Scala) + Oracle11g & MySQL5.5

Posted by
Tagged As: | Categories: Scala | Heroku Embedded Tomcat + Seedo DB framework(Scala) + Oracle11g & MySQL5.5 はコメントを受け付けていません

明けましておめでとうございます。

Heroku Embedded Tomcat Scala の設定が完成しました。

基本は、次のpom.xml ファイルです。

これでScalaがHerokuで動きます。それもLiftでもPlay! でもなく。
まったく独立して、それもOracle11gやMySQLを使って。

<?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>com.heroku.seedo</groupId>
  <artifactId>embeddedTomcatSeedo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>embeddedTomcatSeedo Maven Webapp</name>
  <properties>
    <scala.version>2.9.1</scala.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <repositories>
    <repository>
      <id>maven.apache.org</id>
      <name>Maven Apache</name>
      <url>http://maven.apache.org</url>
    </repository>
    <repository>
      <id>seedo.co.jp</id>
      <name>Seedo Repository</name>
      <url>http://www.ewavesolutions.com/seedorepo/</url>
    </repository>
    <repository>
      <id>scala-tools.releases</id>
      <name>Scala-Tools Dependencies Repository for Releases</name>
      <url>http://scala-tools.org/repo-releases</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>scala-tools.releases</id>
      <name>Scala-Tools Plugins Repository for Releases</name>
      <url>http://scala-tools.org/repo-releases</url>
    </pluginRepository>
  </pluginRepositories>
  <dependencies>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-logging-juli</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jasper</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jasper-el</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-jsp-api</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat</groupId>
      <artifactId>tomcat-dbcp</artifactId>
      <version>7.0.23</version>
    </dependency>
    <dependency>
      <groupId>oracle</groupId>
      <artifactId>ojdbc6</artifactId>
      <version>11</version>
    </dependency>
    <dependency>
      <groupId>oracle</groupId>
      <artifactId>orai18n</artifactId>
      <version>11</version>
    </dependency>
    <dependency>
      <groupId>oracle</groupId>
      <artifactId>mysql_connectory_java</artifactId>
      <version>5.1.17</version>
    </dependency>
    <dependency>
      <groupId>seedo</groupId>
      <artifactId>seedo</artifactId>
      <version>1</version>
    </dependency>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-compiler</artifactId>
      <version>${scala.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.scala-tools.testing</groupId>
      <artifactId>specs_2.9.1</artifactId>
      <version>1.6.9</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <resources>
      <resource>
        <directory>src/main/webapp</directory>
      </resource>
    </resources>
    <finalName>embeddedTomcatSeedo</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.1</version>
        <configuration>
          <encoding>UTF-8</encoding>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.scala-tools</groupId>
        <artifactId>maven-scala-plugin</artifactId>
        <version>2.9.1</version>
        <configuration>
          <encoding>UTF-8</encoding>
          <charset>${project.build.sourceEncoding}</charset>
          <jvmArgs>
            <jvmArg>-Xmx1024m</jvmArg>
            <jvmArg>-DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties</jvmArg>
          </jvmArgs>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.5</version>
        <executions>
          <execution>
            <id>default-copy-resources</id>
            <phase>process-resources</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <overwrite>true</overwrite>
              <outputDirectory>${project.build.directory}</outputDirectory>
              <resources>
                <resource>
                  <directory>${project.basedir}/src</directory>
                  <includes>
                    <include>packageLinkDefs.properties</include>
                  </includes>
                  <filtering>true</filtering>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>appassembler-maven-plugin</artifactId>
        <version>1.1.1</version>
        <configuration>
          <assembleDirectory>target</assembleDirectory>
          <programs>
            <program>
              <mainClass>launch.Main</mainClass>
              <name>webapp</name>
            </program>
          </programs>
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>assemble</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

以下のURLで実際にアクセスしてください。

http://radiant-mountain-7703.herokuapp.com/

DBをアクセスして一覧にするページは次のURLです。

http://radiant-mountain-7703.herokuapp.com/db

Heroku Embedded Tomcat + Seedo DB framework(Java) + MySQL

Posted 2011年12月27日 by
Tagged As: | Categories: Scala | Heroku Embedded Tomcat + Seedo DB framework(Java) + MySQL はコメントを受け付けていません

HEROKU JAVA + MySQL

HEROKUとSeedo DB フレームワーク(Java)によるMySQLへの対応は、env.propertiesファイルを以下のように書き換えます。
ZZZはIPアドレスを書きます。

driver = com.mysql.jdbc.Driver
jdbc = jdbc:mysql://ZZZ.ZZZ.ZZZ.ZZZ:3306/seedo?useUnicode=true&characterEncoding=UTF-8
user = scott
password = tiger

さきのOracleDB同様にHEROKUからインターネットを経由して、MySQLに直接JDBCによってアクセスします。
env.propertiesファイルのDB接続定義を書き換えるだけでOKです。

pom.xmlファイルには以下のように、MySQLのJDBCドライバ用Jarファイルをダウンロードできるようにdependencyタグを追加します。

    
      oracle
      mysql_connectory_java
      5.1.17
    

データベースは、クラウドではなく自分か管理しているDBサーバーを使うというシステム構成です。セキュリティ上は好ましくはないです。MySQLはアクセスするIPアドレスを特定できるので最低でもこの設定は必要でしょう。
これらのコードは実験的なものです。

Servlet 3.0

Servletは、TOMCAT7がサポートしている
Servlet 3.0の主要なテーマの1つであるEoDです。
Servlet 3.0では、EoDとして「Annotation based configuration」を新しく定義しました。アノテーションを利用することでServletの開発が簡単になります。

package servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.*;
import seedo.*;
import seedo.database.*;

@WebServlet(
	name = "DbServlet", 
	urlPatterns = {"/db"}
)
public class DbServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
//		ServletOutputStream out = resp.getOutputStream();
	    PrintWriter out = resp.getWriter();
    	resp.setContentType("text/html; charset=Shift_JIS");

		out.println("<html><body>");
        out.println("hello heroku db");
		db(out);
		out.println("</body></html>");
		out.flush();
		out.close();
    }
	private void db(PrintWriter out) {
		SqlPlus sqlplus = null;
		try {
			sqlplus = new SqlPlus();
			List<Object []> result1 = sqlplus.select("*").from("EMP").orderBy("ename").executeQuery();
			out.println("<TABLE border='1'>");
			for(int i = 0;i < result1.size();i++){
				Object [] record = result1.get(i);
				out.println("<TR><TH>No."+ i + "</TH>");
				for(int j = 0;j < record.length;j++){
					out.println("<TD>" + (record[j] != null?record[j].toString():"") + "</TD>");
				}
				out.println("</TR>");
			}
			out.println("</TABLE>");
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			sqlplus.close();
		}
	} 
}

Servlet2.5では、次のように作成したサーブレットをweb.xmlにマッピングしなければなりませんでした。


  DbServlet
  servlet.DbServlet

 

  DbServlet
  /db

「Annotation based configuration」を利用すると、「@WebServlet」というアノテーションを付加するだけでOKです。

HEROKUのGet Startingにある、HelloServlet の@WebServletコード部分を少しだけ変更してみました。

package servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(
        name = "HelloServlet", 
        urlPatterns = {"/hello"}
    )
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        ServletOutputStream out = resp.getOutputStream();
        out.write("hello heroku".getBytes());
        out.flush();
        out.close();
    }
}

Test
HEROKUにアップロードする前にローカルPCの上で、

target\bin\webapp.batファイルを実行して、ブラウザから以下の2つのURLをアクセスすることができます。

DbServlet は、以下のURLです。

http://127.0.0.1:8080/db

HelloServlet は、以下のURLです。

http://127.0.0.1:8080/hello

Heroku Embedded Tomcat + Seedo DB framework(Java) + Oracle11g

Posted 2011年12月26日 by
Tagged As: | Categories: Scala | Heroku Embedded Tomcat + Seedo DB framework(Java) + Oracle11g はコメントを受け付けていません

HEROKUからOracle11gにアクセスして動くWebアプリケーション

Herokuで動くEmbedded Tomcat7から、インターネット上にあるOracle11gにアクセスするJava Webアプリケーションです。まだScalaではありません。
このコードは動作テスト用に作ったものだということを予めお断りしておきます。また、実際のシステム構成としては望ましいものではありません。

OracleDBへのアクセスは、OracleのJDBCドライバとSeedoDBフレームワークを使っています。このJarファイルは以下のPOM.XMLファイルにあるリポジトリからダウンロードして、に依存関係のあるJarファイルを定義しています。


<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>com.heroku.seedo</groupId>
<artifactId>embeddedTomcatSeedo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>embeddedTomcatSeedo Maven Webapp</name>
<repositories>
<repository>
<id>maven.apache.org</id>
<name>Maven Apache</name>
<url>http://maven.apache.org</url>
</repository>
<repository>
<id>seedo.co.jp</id>
<name>Seedo Repository</name>
<url>http://www.ewavesolutions.com/seedorepo/</url>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>7.0.22</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<version>7.0.22</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>7.0.22</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>7.0.22</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>7.0.22</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>7.0.22</version>
</dependency>
<dependency>
<groupId>oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11</version>
</dependency>
<dependency>
<groupId>oracle</groupId>
<artifactId>orai18n</artifactId>
<version>11</version>
</dependency>
<dependency>
<groupId>seedo</groupId>
<artifactId>seedo</artifactId>
<version>1</version>
</dependency>
</dependencies>
<build>
<finalName>embeddedTomcatSeedo</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.1.1</version>
<configuration>
<assembleDirectory>target</assembleDirectory>
<programs>
<program>
<mainClass>launch.Main</mainClass>
<name>webapp</name>
</program>
</programs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

JSPファイルは、sqlplus1.jsp として、

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page language="java" %>
<%@ page import = "java.util.Date" %>
<%@ page import = "java.util.List" %>
<%@ page import = "java.io.*" %>
<%@ page import = "javax.servlet.*" %>
<%@ page import = "javax.servlet.http.*" %>
<%@ page import = "seedo.*" %>
<%@ page import = "seedo.database.*" %>
<jsp:useBean id="date" class="java.util.Date" />
<%
	SqlPlus sqlplus = new SqlPlus();
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META content="text/html; charset=UTF-8" http-equiv="Content-type">
<TITLE>SeeDo SqlPlus</TITLE>
</HEAD>
<BODY>
<%= date.toString() %><BR/>
<HR/>
<%= sqlplus.getTableList().toString() %>
<HR/>
総数=<%= sqlplus.execute("select * from emp").getResultSize() %>
<TABLE border="1">
<%
try{
	List<Object []> result1 = sqlplus.getResult();
	for(int i = 0;i < result1.size();i++){
		Object [] record = result1.get(i);
		out.print("<TR><TH>No."+ i + "</TH>");
		for(int j = 0;j < record.length;j++){
			out.print("<TD>" + (record[j] != null ? record[j].toString():"") + "</TD>");
		}
		out.print("</TR>");
	}

%>
</TABLE>
<HR/>
<%
} catch (Exception ex) {
	ex.printStackTrace();
} finally {
	sqlplus.close();
}
%>
</BODY>
</HTML>

ブラウザの画面は以下の通りです。
EMP表の内容が一覧表示されます。

SeedoDBフレームワーク用のDB接続定義情報は、
env.propertiesファイルに記述します。このファイルは、HEROKUフォルダの直下に置きます。
DBフレームワークでは、OracleDBとMySQLとが使えます。
#はコメントです。
———————————– driver = oracle.jdbc.driver.OracleDriver
#driver = com.mysql.jdbc.Driver

#jdbc = jdbc:mysql://XXX.XXX.XXX.XXX:3306/ABCDEFG?useUnicode=true&characterEncoding=UTF-8
jdbc = jdbc:oracle:thin:@YYY.YYY.YYY.YYY:1521:ORCL
user = scott
password = tiger

———————————–

IPアドレスは、XXXやYYYの部分です。
MySQLのDB名は、ABCDEFGとなっていますし、OracleSIDはORCLと定義しているので、ここも実際のDB名やSIDを書きます。

開発環境側のフォルダ構成は以下のようになります。

ここでミソなのが、依存するJarファイルをインターネット上に置いて、ダウンロードできるようにすることです。
POM.XMLのなかで以下のように定義すると、seedo-1.jarがダウンロードされます。



seedo
seedo
1

リポジトリ上のJarファイルは、以下のURLでアクセスできるようにしなければなりません。

http://www.ewavesolutions.com/seedorepo/seedo/seedo/1/seedo-1.jar

HEROKU のサポート

Posted 2011年12月21日 by
Tagged As: | Categories: Java | No Comments

Herokuへのディプロイまでは成功したのに、生成されたHEROKUサイトへのアクセスがうまくいかない。
その理由は、Procfileファイルがなかったこと。

このことがわからなくて、HEROKUのサポートチケットを使って、うまくいかないことを問い合わせました。
回答は翌日届き、事なきを得ました。
HEROKUサイトへのアクセスができたわけです。
こんない早く回答されたことに正直驚きました。

HEROKU用の環境は、FEDORAのようなLINUXで構築すると、HEROKUの Getting Started にあるように操作できます。しかし、WindowsでHEROKU用の環境を作る場合、少し工夫が必要になります。

Windowsのコマンド環境には、

コマンドプロンプトと呼ばれるDOS窓
Gitのコマンドライン
Cygwinターミナル

Gitはインストールするときに、UNIXコマンドが使えるようにするかが訊ねられます。
Cygwinは、DOS窓とは違いますが、DOSコマンドが実行できます。

target/bin/フォルダに下に生成される、
webapp ファイルと、webapp.bat ファイル。

webapp.batをDOS窓で実行すると、問題なくJavaアプリが動作します。
webapp はシェルスクリプトなので、Cygwinターミナルで

sh webapp

とコマンドを実行するとうまくいきますが、
Gitコマンドラインではクラスがないと、起動できません。

DOS窓も同様で、

C:\Users\XXX\workspace\HerokuJava\target\bin>sh webapp
エラー: メイン・クラスHelloWorldが見つからなかったかロードできませんでした

ruby環境も、foremanコマンドがうまく実行できない。

foreman start

パスの問題なんだけど。

HerokuにEmbedded TomcatヘJava Web Applicationを作る。その2

Posted by
Tagged As: | Categories: Java | No Comments

HEROKUへのデプロイ

Gitをコミットします。

$ git init
$ git add .
$ git commit -m "Ready to deploy"

Cedar stackにアプリを生成します。

$ heroku create --stack cedar
Creating cold-river-5491... done, stack is cedar
http://cold-river-5491.herokuapp.com/ | git@heroku.com:cold-river-5491.git
Git remote heroku added

コードをディプロイします。

$ git push heroku master
The authenticity of host 'heroku.com (50.19.85.154)' can't be established.
RSA key fingerprint is aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'heroku.com,50.19.85.154' (RSA) to the list of known
hosts.
Counting objects: 89, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (71/71), done.
Writing objects: 100% (89/89), 4.71 MiB | 103 KiB/s, done.
Total 89 (delta 16), reused 0 (delta 0)

-----> Heroku receiving push
-----> Java app detected
-----> Installing Maven 3.0.3..... done
-----> Installing settings.xml..... done
-----> executing /app/tmp/repo.git/.cache/.maven/bin/mvn -B -Duser.home=/tmp/bui
ld_2v7e7w2fkh8ke -Dmaven.repo.local=/app/tmp/repo.git/.cache/.m2/repository -s /
app/tmp/repo.git/.cache/.m2/settings.xml -DskipTests=true clean install
       [INFO] Scanning for projects...
       [INFO]

       [INFO] ------------------------------------------------------------------
------
       [INFO] Building embeddedTomcatSample Maven Webapp 1.0-SNAPSHOT
       [INFO] ------------------------------------------------------------------
------
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tom
cat-embed-core/7.0.22/tomcat-embed-core-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tomc
at-embed-core/7.0.22/tomcat-embed-core-7.0.22.pom (2 KB at 5.0 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tom
cat-embed-logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tomc
at-embed-logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.pom (2 KB at 10.0
KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tom
cat-embed-jasper/7.0.22/tomcat-embed-jasper-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tomc
at-embed-jasper/7.0.22/tomcat-embed-jasper-7.0.22.pom (2 KB at 8.5 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ja
sper/7.0.22/tomcat-jasper-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jas
per/7.0.22/tomcat-jasper-7.0.22.pom (3 KB at 20.8 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-se
rvlet-api/7.0.22/tomcat-servlet-api-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ser
vlet-api/7.0.22/tomcat-servlet-api-7.0.22.pom (2 KB at 7.1 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ju
li/7.0.22/tomcat-juli-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jul
i/7.0.22/tomcat-juli-7.0.22.pom (2 KB at 6.5 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-js
p-api/7.0.22/tomcat-jsp-api-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jsp
-api/7.0.22/tomcat-jsp-api-7.0.22.pom (2 KB at 12.2 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-el
-api/7.0.22/tomcat-el-api-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-el-
api/7.0.22/tomcat-el-api-7.0.22.pom (2 KB at 9.2 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/eclipse/jdt/core/compil
er/ecj/3.7/ecj-3.7.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/eclipse/jdt/core/compile
r/ecj/3.7/ecj-3.7.pom (2 KB at 7.6 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ja
sper-el/7.0.22/tomcat-jasper-el-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jas
per-el/7.0.22/tomcat-jasper-el-7.0.22.pom (2 KB at 16.4 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ap
i/7.0.22/tomcat-api-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-api
/7.0.22/tomcat-api-7.0.22.pom (2 KB at 4.5 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ut
il/7.0.22/tomcat-util-7.0.22.pom
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-uti
l/7.0.22/tomcat-util-7.0.22.pom (2 KB at 5.4 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tom
cat-embed-core/7.0.22/tomcat-embed-core-7.0.22.jar
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tom
cat-embed-logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.jar
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tom
cat-embed-jasper/7.0.22/tomcat-embed-jasper-7.0.22.jar
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ja
sper/7.0.22/tomcat-jasper-7.0.22.jar
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-se
rvlet-api/7.0.22/tomcat-servlet-api-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ser
vlet-api/7.0.22/tomcat-servlet-api-7.0.22.jar (173 KB at 719.6 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ju
li/7.0.22/tomcat-juli-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tomc
at-embed-logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.jar (38 KB at 135.
2 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-el
-api/7.0.22/tomcat-el-api-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jas
per/7.0.22/tomcat-jasper-7.0.22.jar (577 KB at 2028.9 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/eclipse/jdt/core/compil
er/ecj/3.7/ecj-3.7.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tomc
at-embed-jasper/7.0.22/tomcat-embed-jasper-7.0.22.jar (768 KB at 2459.8 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ap
i/7.0.22/tomcat-api-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jul
i/7.0.22/tomcat-juli-7.0.22.jar (38 KB at 260.1 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ut
il/7.0.22/tomcat-util-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-el-
api/7.0.22/tomcat-el-api-7.0.22.jar (43 KB at 305.4 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-ja
sper-el/7.0.22/tomcat-jasper-el-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-api
/7.0.22/tomcat-api-7.0.22.jar (7 KB at 46.9 KB/sec)
       Downloading: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-js
p-api/7.0.22/tomcat-jsp-api-7.0.22.jar
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-uti
l/7.0.22/tomcat-util-7.0.22.jar (23 KB at 180.1 KB/sec)
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/embed/tomc
at-embed-core/7.0.22/tomcat-embed-core-7.0.22.jar (2379 KB at 4600.9 KB/sec)
       Downloaded: http://s3pository.heroku.com/jvm/org/eclipse/jdt/core/compile
r/ecj/3.7/ecj-3.7.jar (1671 KB at 5405.4 KB/sec)
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jsp
-api/7.0.22/tomcat-jsp-api-7.0.22.jar (87 KB at 515.5 KB/sec)
       Downloaded: http://s3pository.heroku.com/jvm/org/apache/tomcat/tomcat-jas
per-el/7.0.22/tomcat-jasper-el-7.0.22.jar (120 KB at 474.3 KB/sec)
       [INFO]
       [INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ embeddedTomca
tSample ---
       [INFO] Deleting /tmp/build_2v7e7w2fkh8ke/target
       [INFO]
       [INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ e
mbeddedTomcatSample ---
       [INFO] Using 'UTF-8' encoding to copy filtered resources.
       [INFO] skip non existing resourceDirectory /tmp/build_2v7e7w2fkh8ke/src/m
ain/resources
       [INFO]
       [INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ embedd
edTomcatSample ---
       [INFO] Compiling 2 source files to /tmp/build_2v7e7w2fkh8ke/target/classe
s
       [INFO]
       [INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResour
ces) @ embeddedTomcatSample ---
       [INFO] Using 'UTF-8' encoding to copy filtered resources.
       [INFO] skip non existing resourceDirectory /tmp/build_2v7e7w2fkh8ke/src/t
est/resources
       [INFO]
       [INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile)
@ embeddedTomcatSample ---
       [INFO] No sources to compile
       [INFO]
       [INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ embeddedTomc
atSample ---
       [INFO] Tests are skipped.
       [INFO]
       [INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ embeddedTomcatSampl
e ---
       [INFO] Building jar: /tmp/build_2v7e7w2fkh8ke/target/embeddedTomcatSample
.jar
       [INFO]
       [INFO] --- appassembler-maven-plugin:1.1.1:assemble (default) @ embeddedT
omcatSample ---
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/embed/tomcat-embed-core/7.0.22/tomcat-embed-core-7.0.22.jar to /tmp/build_2v7
e7w2fkh8ke/target/repo/org/apache/tomcat/embed/tomcat-embed-core/7.0.22/tomcat-e
mbed-core-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/embed/tomcat-embed-logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.jar t
o /tmp/build_2v7e7w2fkh8ke/target/repo/org/apache/tomcat/embed/tomcat-embed-logg
ing-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/embed/tomcat-embed-jasper/7.0.22/tomcat-embed-jasper-7.0.22.jar to /tmp/build
_2v7e7w2fkh8ke/target/repo/org/apache/tomcat/embed/tomcat-embed-jasper/7.0.22/to
mcat-embed-jasper-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-jasper/7.0.22/tomcat-jasper-7.0.22.jar to /tmp/build_2v7e7w2fkh8ke/tar
get/repo/org/apache/tomcat/tomcat-jasper/7.0.22/tomcat-jasper-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-servlet-api/7.0.22/tomcat-servlet-api-7.0.22.jar to /tmp/build_2v7e7w2
fkh8ke/target/repo/org/apache/tomcat/tomcat-servlet-api/7.0.22/tomcat-servlet-ap
i-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-juli/7.0.22/tomcat-juli-7.0.22.jar to /tmp/build_2v7e7w2fkh8ke/target/
repo/org/apache/tomcat/tomcat-juli/7.0.22/tomcat-juli-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-el-api/7.0.22/tomcat-el-api-7.0.22.jar to /tmp/build_2v7e7w2fkh8ke/tar
get/repo/org/apache/tomcat/tomcat-el-api/7.0.22/tomcat-el-api-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/eclipse/jdt
/core/compiler/ecj/3.7/ecj-3.7.jar to /tmp/build_2v7e7w2fkh8ke/target/repo/org/e
clipse/jdt/core/compiler/ecj/3.7/ecj-3.7.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-api/7.0.22/tomcat-api-7.0.22.jar to /tmp/build_2v7e7w2fkh8ke/target/re
po/org/apache/tomcat/tomcat-api/7.0.22/tomcat-api-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-util/7.0.22/tomcat-util-7.0.22.jar to /tmp/build_2v7e7w2fkh8ke/target/
repo/org/apache/tomcat/tomcat-util/7.0.22/tomcat-util-7.0.22.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-jasper-el/7.0.22/tomcat-jasper-el-7.0.22.jar to /tmp/build_2v7e7w2fkh8
ke/target/repo/org/apache/tomcat/tomcat-jasper-el/7.0.22/tomcat-jasper-el-7.0.22
.jar
       [INFO] Installing /app/tmp/repo.git/.cache/.m2/repository/org/apache/tomc
at/tomcat-jsp-api/7.0.22/tomcat-jsp-api-7.0.22.jar to /tmp/build_2v7e7w2fkh8ke/t
arget/repo/org/apache/tomcat/tomcat-jsp-api/7.0.22/tomcat-jsp-api-7.0.22.jar
       [INFO] Installing /tmp/build_2v7e7w2fkh8ke/target/embeddedTomcatSample.ja
r to /tmp/build_2v7e7w2fkh8ke/target/repo/com/heroku/sample/embeddedTomcatSample
/1.0-SNAPSHOT/embeddedTomcatSample-1.0-SNAPSHOT.jar
       [INFO]
       [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ embedde
dTomcatSample ---
       [INFO] Installing /tmp/build_2v7e7w2fkh8ke/target/embeddedTomcatSample.ja
r to /app/tmp/repo.git/.cache/.m2/repository/com/heroku/sample/embeddedTomcatSam
ple/1.0-SNAPSHOT/embeddedTomcatSample-1.0-SNAPSHOT.jar
       [INFO] Installing /tmp/build_2v7e7w2fkh8ke/pom.xml to /app/tmp/repo.git/.
cache/.m2/repository/com/heroku/sample/embeddedTomcatSample/1.0-SNAPSHOT/embedde
dTomcatSample-1.0-SNAPSHOT.pom
       [INFO] ------------------------------------------------------------------
------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------
------
       [INFO] Total time: 6.654s
       [INFO] Finished at: Tue Dec 20 16:20:46 UTC 2011
       [INFO] Final Memory: 10M/490M
       [INFO] ------------------------------------------------------------------
------
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 5.4MB
-----> Launching... done, v5
       http://cold-river-5491.herokuapp.com deployed to Heroku

To git@heroku.com:cold-river-5491.git
 * [new branch]      master -> master

BUILD SUCCESSと表示されれば成功です。

HEROKUのサイトをブラウザでアクセスします。

$ heroku open

以下のURLでHEROKUのアプリがアクセスできます。

http://cold-river-5491.herokuapp.com/

http://cold-river-5491.herokuapp.com/hello

当分、アクセスできると思います。

HerokuにEmbedded TomcatヘJava Web Applicationを作る

Posted 2011年12月17日 by
Tagged As: | Categories: Java | No Comments

HEROKUが正式にScala言語をサポートしたということで、その前段としてJavaアプリケーションを作ってみました。

詳細は、HEROKUの開発者向けページの以下のURLに説明があります。

http://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat

まずは、前提となる環境を作ります。

1. JVMをインストールします。
  現在のJava環境は、Java Platform, Standard Edition のJava SE 7 Update 1をインストールしています。

以下のURLからインストール用のJDKをダウンロードします。
http://www.oracle.com/technetwork/jp/java/javase/downloads/index.html

2. Maven環境は、次のようになっています。

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

Maven環境は、

C:> mvn -version
Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900)
Maven home: C:\apache-maven-3.0.3\bin\..
Java version: 1.7.0_01, vendor: Oracle Corporation
Java home: C:\Program Files\Java\jdk1.7.0_01\jre
Default locale: ja_JP, platform encoding: MS932
OS name: "windows 7", version: "6.1", arch: "amd64", family: "windows"

以下のURLからダウンロードできます。
http://maven.apache.org/download.html
http://www.apache.org/dyn/closer.cgi/maven/binaries/apache-maven-3.0.3-bin.zip

Maven 2.2.1でもOKです。新しいもの好きなので3.0.3を使っています。

3. Git
使っているバージョンは以下のものですが、この文章を書いているときの最新はv1.7.7.5です。

Welcome to Git (version 1.7.6-preview20110708)

Gitは以下のURLからダウンロードできます。
http://git-scm.com/

これらをPCにインストールします。

4. pom.xml
Mavenを使うには、pom.xmlを定義します。
これは、HEROKUには、


4.0.0
com.heroku.sample
embeddedTomcatSample
1.0-SNAPSHOT
embeddedTomcatSample Maven Webapp
http://maven.apache.org


org.apache.tomcat.embed
tomcat-embed-core
7.0.22


org.apache.tomcat.embed
tomcat-embed-logging-juli
7.0.22


org.apache.tomcat.embed
tomcat-embed-jasper
7.0.22


org.apache.tomcat
tomcat-jasper
7.0.22


org.apache.tomcat
tomcat-jasper-el
7.0.22


org.apache.tomcat
tomcat-jsp-api
7.0.22



embeddedTomcatSample org.codehaus.mojo
appassembler-maven-plugin
1.1.1

target launch.Main
webapp


package
assemble



このファイルを、Eclipseからも使いたいので、
C:\User\XXX\workspace\Heroku\ディレクトリに置きます。

5. MVN
mvn をpom.xml ファイルがある場所で実行します。


$ mvn package

mvn を実行すると以下のような感じファイルがダウンロードされ、環境が構築されます。

[INFO] ------------------------------------------------------------------------
[INFO] Building embeddedTomcatSample Maven Webapp 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-surefi
re-plugin/2.7.2/maven-surefire-plugin-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-surefir
e-plugin/2.7.2/maven-surefire-plugin-2.7.2.pom (10 KB at 8.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire/2.
7.2/surefire-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire/2.7
.2/surefire-2.7.2.pom (11 KB at 23.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-surefi
re-plugin/2.7.2/maven-surefire-plugin-2.7.2.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/plugins/maven-surefir
e-plugin/2.7.2/maven-surefire-plugin-2.7.2.jar (29 KB at 33.1 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler-maven-
plugin/1.1.1/appassembler-maven-plugin-1.1.1.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler-maven-p
lugin/1.1.1/appassembler-maven-plugin-1.1.1.pom (8 KB at 18.6 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler/appass
embler/1.1.1/appassembler-1.1.1.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler/appasse
mbler/1.1.1/appassembler-1.1.1.pom (6 KB at 9.8 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/mojo-parent/27/mojo
-parent-27.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/mojo-parent/27/mojo-
parent-27.pom (27 KB at 56.4 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/codehaus-parent/3/codeha
us-parent-3.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/codehaus-parent/3/codehau
s-parent-3.pom (5 KB at 8.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler-maven-
plugin/1.1.1/appassembler-maven-plugin-1.1.1.jar
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler-maven-p
lugin/1.1.1/appassembler-maven-plugin-1.1.1.jar (762 KB at 408.6 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-
core/7.0.22/tomcat-embed-core-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-c
ore/7.0.22/tomcat-embed-core-7.0.22.pom (2 KB at 3.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-
logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-l
ogging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.pom (2 KB at 2.1 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-
jasper/7.0.22/tomcat-embed-jasper-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-j
asper/7.0.22/tomcat-embed-jasper-7.0.22.pom (2 KB at 2.2 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper/7.0.2
2/tomcat-jasper-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper/7.0.22
/tomcat-jasper-7.0.22.pom (3 KB at 4.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-servlet-api/
7.0.22/tomcat-servlet-api-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-servlet-api/7
.0.22/tomcat-servlet-api-7.0.22.pom (2 KB at 2.0 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-juli/7.0.22/
tomcat-juli-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-juli/7.0.22/t
omcat-juli-7.0.22.pom (2 KB at 2.4 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jsp-api/7.0.
22/tomcat-jsp-api-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jsp-api/7.0.2
2/tomcat-jsp-api-7.0.22.pom (2 KB at 3.1 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-el-api/7.0.2
2/tomcat-el-api-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-el-api/7.0.22
/tomcat-el-api-7.0.22.pom (2 KB at 2.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/eclipse/jdt/core/compiler/ecj/3.7
/ecj-3.7.pom
Downloaded: http://repo1.maven.org/maven2/org/eclipse/jdt/core/compiler/ecj/3.7/
ecj-3.7.pom (2 KB at 2.6 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper-el/7.
0.22/tomcat-jasper-el-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper-el/7.0
.22/tomcat-jasper-el-7.0.22.pom (2 KB at 2.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-api/7.0.22/t
omcat-api-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-api/7.0.22/to
mcat-api-7.0.22.pom (2 KB at 2.7 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-util/7.0.22/
tomcat-util-7.0.22.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-util/7.0.22/t
omcat-util-7.0.22.pom (2 KB at 2.1 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-
core/7.0.22/tomcat-embed-core-7.0.22.jar
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-
jasper/7.0.22/tomcat-embed-jasper-7.0.22.jar
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-servlet-api/
7.0.22/tomcat-servlet-api-7.0.22.jar
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper/7.0.2
2/tomcat-jasper-7.0.22.jar
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-
logging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-c
ore/7.0.22/tomcat-embed-core-7.0.22.jar (2379 KB at 1313.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-juli/7.0.22/
tomcat-juli-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-servlet-api/7
.0.22/tomcat-servlet-api-7.0.22.jar (173 KB at 74.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-el-api/7.0.2
2/tomcat-el-api-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-l
ogging-juli/7.0.22/tomcat-embed-logging-juli-7.0.22.jar (38 KB at 14.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/eclipse/jdt/core/compiler/ecj/3.7
/ecj-3.7.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-juli/7.0.22/t
omcat-juli-7.0.22.jar (38 KB at 36.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-api/7.0.22/t
omcat-api-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper/7.0.22
/tomcat-jasper-7.0.22.jar (577 KB at 150.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-util/7.0.22/
tomcat-util-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-el-api/7.0.22
/tomcat-el-api-7.0.22.jar (43 KB at 23.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper-el/7.
0.22/tomcat-jasper-el-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/embed/tomcat-embed-j
asper/7.0.22/tomcat-embed-jasper-7.0.22.jar (768 KB at 178.0 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jsp-api/7.0.
22/tomcat-jsp-api-7.0.22.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-api/7.0.22/to
mcat-api-7.0.22.jar (7 KB at 3.7 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-util/7.0.22/t
omcat-util-7.0.22.jar (23 KB at 12.9 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jsp-api/7.0.2
2/tomcat-jsp-api-7.0.22.jar (87 KB at 49.8 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/apache/tomcat/tomcat-jasper-el/7.0
.22/tomcat-jasper-el-7.0.22.jar (120 KB at 54.6 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/eclipse/jdt/core/compiler/ecj/3.7/
ecj-3.7.jar (1671 KB at 422.9 KB/sec)
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ embedded
TomcatSample ---
[WARNING] Using platform encoding (MS932 actually) to copy filtered resources, i
.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\ewave\workspace\HEROKU\src\m
ain\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ embeddedTomca
tSample ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @
embeddedTomcatSample ---
[WARNING] Using platform encoding (MS932 actually) to copy filtered resources, i
.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\ewave\workspace\HEROKU\src\t
est\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ embed
dedTomcatSample ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ embeddedTomcatSampl
e ---
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-bo
oter/2.7.2/surefire-booter-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-boo
ter/2.7.2/surefire-booter-2.7.2.pom (2 KB at 3.8 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-ap
i/2.7.2/surefire-api-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-api
/2.7.2/surefire-api-2.7.2.pom (2 KB at 4.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/maven-suref
ire-common/2.7.2/maven-surefire-common-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/maven-surefi
re-common/2.7.2/maven-surefire-common-2.7.2.pom (4 KB at 6.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-common-
artifact-filters/1.3/maven-common-artifact-filters-1.3.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-common-a
rtifact-filters/1.3/maven-common-artifact-filters-1.3.pom (4 KB at 5.4 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-bo
oter/2.7.2/surefire-booter-2.7.2.jar
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-ap
i/2.7.2/surefire-api-2.7.2.jar
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/maven-suref
ire-common/2.7.2/maven-surefire-common-2.7.2.jar
Downloading: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-common-
artifact-filters/1.3/maven-common-artifact-filters-1.3.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-boo
ter/2.7.2/surefire-booter-2.7.2.jar (32 KB at 26.9 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/maven-surefi
re-common/2.7.2/maven-surefire-common-2.7.2.jar (60 KB at 38.5 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/shared/maven-common-a
rtifact-filters/1.3/maven-common-artifact-filters-1.3.jar (31 KB at 16.8 KB/sec)

Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-api
/2.7.2/surefire-api-2.7.2.jar (119 KB at 57.5 KB/sec)
[INFO] No tests to run.
[INFO] Surefire report directory: C:\Users\ewave\workspace\HEROKU\target\surefir
e-reports
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-ju
nit3/2.7.2/surefire-junit3-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-jun
it3/2.7.2/surefire-junit3-2.7.2.pom (2 KB at 3.8 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-pr
oviders/2.7.2/surefire-providers-2.7.2.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-pro
viders/2.7.2/surefire-providers-2.7.2.pom (2 KB at 3.9 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-ju
nit3/2.7.2/surefire-junit3-2.7.2.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/maven/surefire/surefire-jun
it3/2.7.2/surefire-junit3-2.7.2.jar (26 KB at 54.2 KB/sec)

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
There are no tests to run.

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ embeddedTomcatSample ---
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: C:\Users\ewave\workspace\HEROKU\target\embeddedTomcatSample
.jar
[INFO]
[INFO] --- appassembler-maven-plugin:1.1.1:assemble (default) @ embeddedTomcatSa
mple ---
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler/appass
embler-model/1.1.1/appassembler-model-1.1.1.pom
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler/appasse
mbler-model/1.1.1/appassembler-model-1.1.1.pom (4 KB at 7.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/net/java/dev/stax-utils/stax-utils/20
060502/stax-utils-20060502.pom
Downloaded: http://repo1.maven.org/maven2/net/java/dev/stax-utils/stax-utils/200
60502/stax-utils-20060502.pom (565 B at 1.3 KB/sec)
Downloading: http://repo1.maven.org/maven2/stax/stax/1.1.1-dev/stax-1.1.1-dev.po
m
Downloaded: http://repo1.maven.org/maven2/stax/stax/1.1.1-dev/stax-1.1.1-dev.pom
 (3 KB at 7.1 KB/sec)
Downloading: http://repo1.maven.org/maven2/junit/junit/3.8.2/junit-3.8.2.pom
Downloaded: http://repo1.maven.org/maven2/junit/junit/3.8.2/junit-3.8.2.pom (747
 B at 1.5 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler/appass
embler-model/1.1.1/appassembler-model-1.1.1.jar
Downloading: http://repo1.maven.org/maven2/net/java/dev/stax-utils/stax-utils/20
060502/stax-utils-20060502.jar
Downloading: http://repo1.maven.org/maven2/stax/stax/1.1.1-dev/stax-1.1.1-dev.ja
r
Downloaded: http://repo1.maven.org/maven2/org/codehaus/mojo/appassembler/appasse
mbler-model/1.1.1/appassembler-model-1.1.1.jar (24 KB at 21.4 KB/sec)
Downloaded: http://repo1.maven.org/maven2/stax/stax/1.1.1-dev/stax-1.1.1-dev.jar
 (166 KB at 69.4 KB/sec)
Downloaded: http://repo1.maven.org/maven2/net/java/dev/stax-utils/stax-utils/200
60502/stax-utils-20060502.jar (126 KB at 48.0 KB/sec)
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\embed\tomcat-e
mbed-core\7.0.22\tomcat-embed-core-7.0.22.jar to C:\Users\ewave\workspace\HEROKU
\target\repo\org\apache\tomcat\embed\tomcat-embed-core\7.0.22\tomcat-embed-core-
7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\embed\tomcat-e
mbed-logging-juli\7.0.22\tomcat-embed-logging-juli-7.0.22.jar to C:\Users\ewave\
workspace\HEROKU\target\repo\org\apache\tomcat\embed\tomcat-embed-logging-juli\7
.0.22\tomcat-embed-logging-juli-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\embed\tomcat-e
mbed-jasper\7.0.22\tomcat-embed-jasper-7.0.22.jar to C:\Users\ewave\workspace\HE
ROKU\target\repo\org\apache\tomcat\embed\tomcat-embed-jasper\7.0.22\tomcat-embed
-jasper-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-jasper\
7.0.22\tomcat-jasper-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\repo\o
rg\apache\tomcat\tomcat-jasper\7.0.22\tomcat-jasper-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-servlet
-api\7.0.22\tomcat-servlet-api-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\tar
get\repo\org\apache\tomcat\tomcat-servlet-api\7.0.22\tomcat-servlet-api-7.0.22.j
ar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-juli\7.
0.22\tomcat-juli-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\repo\org\a
pache\tomcat\tomcat-juli\7.0.22\tomcat-juli-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-el-api\
7.0.22\tomcat-el-api-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\repo\o
rg\apache\tomcat\tomcat-el-api\7.0.22\tomcat-el-api-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\eclipse\jdt\core\compiler\ec
j\3.7\ecj-3.7.jar to C:\Users\ewave\workspace\HEROKU\target\repo\org\eclipse\jdt
\core\compiler\ecj\3.7\ecj-3.7.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-api\7.0
.22\tomcat-api-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\repo\org\apa
che\tomcat\tomcat-api\7.0.22\tomcat-api-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-util\7.
0.22\tomcat-util-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\repo\org\a
pache\tomcat\tomcat-util\7.0.22\tomcat-util-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-jasper-
el\7.0.22\tomcat-jasper-el-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\
repo\org\apache\tomcat\tomcat-jasper-el\7.0.22\tomcat-jasper-el-7.0.22.jar
[INFO] Installing C:\Users\ewave\.m2\repository\org\apache\tomcat\tomcat-jsp-api
\7.0.22\tomcat-jsp-api-7.0.22.jar to C:\Users\ewave\workspace\HEROKU\target\repo
\org\apache\tomcat\tomcat-jsp-api\7.0.22\tomcat-jsp-api-7.0.22.jar
[INFO] Installing C:\Users\ewave\workspace\HEROKU\target\embeddedTomcatSample.ja
r to C:\Users\ewave\workspace\HEROKU\target\repo\com\heroku\sample\embeddedTomca
tSample\1.0-SNAPSHOT\embeddedTomcatSample-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 38.329s
[INFO] Finished at: Sat Dec 17 15:54:28 JST 2011
[INFO] Final Memory: 6M/15M
[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS と表示されれば成功。

6. Launcher Class
TOMCATを起動させるためのJavaコードは以下の通りです。
このファイルを、C:\User\XXX\workspace\Heroku\src\main\java\launchディレクトリに置きます。


package launch;

import java.io.File;
import org.apache.catalina.startup.Tomcat;

public class Main {

public static void main(String[] args) throws Exception {

String webappDirLocation = “src/main/webapp/”;
Tomcat tomcat = new Tomcat();

//The port that we should run on can be set into an environment variable
//Look for that variable and default to 8080 if it isn’t there.
String webPort = System.getenv(“PORT”);
if(webPort == null || webPort.isEmpty()) {
webPort = “8080”;
}

tomcat.setPort(Integer.valueOf(webPort));

tomcat.addWebapp(“/”, new File(webappDirLocation).getAbsolutePath());
System.out.println(“configuring app with basedir: ” + new File(“./” + webappDirLocation).getAbsolutePath());

tomcat.start();
tomcat.getServer().await();
}
}

7.Servlet
Servletのコードは以下のとおり。
C:\User\XXX\workspace\Heroku\src\main\java\servlet\ディレクトリに置きます。


package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(
name = “MyServlet”,
urlPatterns = {“/hello”}
)
public class HelloServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
ServletOutputStream out = resp.getOutputStream();
out.write(“hello heroku”.getBytes());
out.flush();
out.close();
}

}

8. JSP
JSPファイルを作ります。
C:\Users\XXX\workspace\Heroku\target\bin\src\main\webapp\ディレクトリにindex.jspファイルを置きます。


<html>
<body>
<h2>Hello Heroku!</h2>
</body>
</html>

9. MVNコマンド
JAVAコードをコンパイルなどするために再びMVNを実行します。


$ mvn package

10.TOMCATの起動
TOMCATを起動するためにバッチファイルを実行します。


$ target/bin/webapp.bat

ブラウザでServletをアクセスするには以下のURLを。

http://localhost:8080/hello

JSPをアクセスするには以下のURLです。

http://localhost:8080

Hello Heroku! とブラウザ画面に表示されます。

Servletでは、

hello heroku

とブラウザ画面に表示されればOKです。

11.Procfile

Procfileファイルを作成します。
このファイルの中身は、

web: sh target/bin/webapp

このファイルによってHEROKUでアプリがHEROKUにデプロイされます。

次は、このTOMCATをHEROKUへデプロイする手順です。

htmlSrv class

Posted 2011年10月24日 by
Tagged As: | Categories: Framework, Scala | No Comments

WEBフレームワークの中心的クラスのhtmlSrvを拡張しています。

主な拡張点は、

  1. Cookieによるセッション管理機能
  2. 認証機能
  3. ページ割り込み機能

リダイレクションとフォワードによって、別ページを割り込ませます。
この機能によって、ログイン画面や、成人ですかという画面を割り込ませることができます。
設定は、TOMCATのWEB.XMLによって設定情報を定義するので、
この定義情報を取り出すためのコードがかなりのボリュームを持っています。

それと、HTMLパーサの呼び出しに引数を1つ追加し、PrintWriterを渡しています。
従来のコードでは、いったん1ページ分のデータをStringに格納してから
Outputしていましたが、読み込み次第、出力することができます。
大きなページでは反応がよくなります。

まだ、拡張し続けているの参考まで。

package ews.servlet
import org.apache.log4j.Logger
import java.io._
import java.net.{URLDecoder,URLEncoder}
import javax.servlet._
import javax.servlet.http._
import ews.servlet.sp._
import ews.servlet.session._

/**
 * The dynamic page is made by HttpServlet.
 */
class htmlSrv extends HttpServlet {
	lazy val logger = Logger.getLogger(classOf[ewsSrv])
	var context : ServletContext = null
	var DocumentRoot : String = null	// DocumentRoot

	var loginUrl : String = null			// Login画面のURL
	var checkUrl : String = null			// Cookie設定画面のURL
	var registrationUrl : String = null		// 登録画面のURL
	var registrationPath : String = null	// 登録が必要なコンテンツが格納されるディレクトリ (このバージョンでは1つしか指定できない)
	var authPath : String = null			// 認証が必要なコンテンツが格納されるディレクトリ (このバージョンでは1つしか指定できない)
	var checkCookie : String = null			// チェック画面を表示させる場合にセットされるCookieの名称
	var registrationCookie : String = null	// 登録画面を表示させる場合にセットされるCookieの名称

	var message = "Not Found :The requested URL was not found on this server."	// Not Found
	var characode = "utf-8"
	override def init(config : ServletConfig) : Unit = {
		super.init(config)
		context = config.getServletContext
		DocumentRoot = config.getInitParameter("html")	// WEB-INF/web.xml
		loginUrl = config.getInitParameter("login")	// WEB-INF/web.xml
		if(loginUrl == null)
			loginUrl = "/toro/login"
		checkUrl = config.getInitParameter("check")	// WEB-INF/web.xml
		if(checkUrl != null && checkUrl.length == 0)
			checkUrl = null
		registrationUrl = config.getInitParameter("registration")	// WEB-INF/web.xml
		if(registrationUrl != null && registrationUrl.length == 0)
			registrationUrl = null
		registrationPath = config.getInitParameter("registrationpath")	// WEB-INF/web.xml
		if(registrationPath != null && registrationPath.length == 0)
			registrationPath = null
		authPath = config.getInitParameter("authpath")	// WEB-INF/web.xml
		if(authPath != null && authPath.length == 0)
			authPath = null
		checkCookie = config.getInitParameter("checkcookie")	// WEB-INF/web.xml
		if(checkCookie != null && checkCookie.length == 0)
			checkCookie = null
		registrationCookie = config.getInitParameter("registrationcookie")	// WEB-INF/web.xml
		if(registrationCookie != null && registrationCookie.length == 0)
			registrationCookie = null
	}
	override def doGet(request : HttpServletRequest, response : HttpServletResponse) : Unit = {
		gen(request, response)
	}
	override def doPost(request : HttpServletRequest, response : HttpServletResponse) : Unit = {
		gen(request, response)
	}
	// generate HTML
	def gen(request : HttpServletRequest, response : HttpServletResponse) : Unit = {
		logger.debug("htmlSrv ################# start")
		var htmlFile = ""
		var parsersw = if(request.getParameter("parsersw") !=null){request.getParameter("parsersw").toInt}else{0}	// 強制的にHTMLパーサを切り替える
		request.setCharacterEncoding(characode)
   		val url = request.getRequestURL
		val uri = request.getRequestURI
		var spth:String = request.getServletPath
		// URLからパラメータ情報を取り出す
		spth = spth.replace("/","")
		val ur = uri.split("/")
		var fg = false
		ur.foreach({v =>
			if(fg){
				htmlFile += "/" + v
			}
			if(v.equals(spth))
				fg = true
		})
		val contentFilePath = DocumentRoot + htmlFile // コンテンツのファイルフルパス
		var ct = ContentTypeDef.changeFromFileNameToContentType(htmlFile)
		val sw = ContentTypeDef.getCtype(ct) match {
		  case "text" => {
   			if(htmlFile.lastIndexOf(".html") > 0 || htmlFile.lastIndexOf(".htm") > 0){
   				val session = new Session(request,response)	// Cookie
   				if(checkUrl != null				// URLが定義されているか?
   				    && checkCookie != null		// クッキーが定義されているか?
   				    && checkUrl.lastIndexOf(htmlFile) == -1		// チェック用のURLの場合はフォワードさせない
   				    && session.getAttribute(checkCookie) == null){// 特定のクッキーが値の有無によって制御したい場合
   					println("Cookie oc none checkUrl="+checkUrl + " htmlFile=" + htmlFile + " " + checkUrl.lastIndexOf(htmlFile))
// for test
   					session.cookie.setCookie(checkCookie,java.lang.Math.random.toString ,-1)
   					val rd = context.getRequestDispatcher(checkUrl)
   					rd.forward(request, response)
   					return
   				} else {
   					println("checkUrl is null & Cookie oc="+session.getAttribute(checkCookie))
   				}
   				if(registrationCookie != null		// クッキーが定義されているか?
   				    && registrationUrl != null		// URLが定義されているか?
   				    && registrationUrl.lastIndexOf(htmlFile) == -1		// 登録用のURLの場合はフォワードさせない
   				    && session.getAttribute(registrationCookie) == null // 特定のクッキーが値の有無によって制御したい場合
   					&& registrationPath != null
   					&& registrationPath.equals(getPath(htmlFile))){	// 指定されたパスが登録が必要なディレクトリか?
   					println("registrationCookie & htmlFile="+htmlFile)
   					val rd = context.getRequestDispatcher(registrationUrl)
   					rd.forward(request, response)
   					return
   				}
   				if((authPath != null && authPath.equals(getPath(htmlFile))) || htaccess(htmlFile)){	// 指定されたパスが認証が必要なディレクトリか?
   					if(!session.isLogin){	// 認証がされているか?
   						println("htmlSrv ############ Longin画面を表示 -> loginUrl=" + loginUrl+"?url=" + URLEncoder.encode(uri.toString))
   						logger.debug("htmlSrv ############ Longin画面を表示 -> loginUrl=" + loginUrl+"?url=" + URLEncoder.encode(uri.toString))
   						response.sendRedirect(loginUrl+"?uri=" + URLEncoder.encode(uri.toString))
   						val out = response.getWriter
   						out.close
   						return
   					}
   				}
   			}
		    if(ct.equals("text/html")){parsersw}else{11}
		  }
		  case "image" => 10
		  case "application" => if(ct.equals("application/xml")){2}else{10}
		  case "audio" => 10
		  case "video" => 10
		  case _ => 10
		}
		logger.debug("htmlSrv ############ "+ct+" ##### htmlFile="+htmlFile)
		println("htmlSrv ############ "+ct+" ##### htmlFile="+htmlFile)
		if(sw == 0){
			ct += "; charset=" + characode
			response.setContentType(ct)
			val sp = new Htmlparse6Sp(request, response, contentFilePath)// Dynamic page. use the org.htmlparser.sax._
			val r = sp.io
		  println("htmlSrv sw=0 Htmlparse6Sp r="+r+ " " + contentFilePath)
			if(!r){
        		System.err.println("Not Found the "+htmlFile)
				error(404,"Not Found "+htmlFile,response)
			}
		} else if(sw == 11){
			ct += "; charset=" + characode
			response.setContentType(ct)
			val sp = new TextSp(request, response, contentFilePath)
			sp.characode = characode
			val r = sp.io
			if(!r){
        		System.err.println("Not Found the "+htmlFile)
				error(404,"Not Found "+htmlFile,response)
			}
		} else if(sw < 10){	// HTMLパーサ評価用
			val ssp = sw match {
			  // It reads a file, and the content is output as it is.
			  case 1 => new StraightSp(request, response, contentFilePath)
			  // The HTML file is read by the Scala XML loader. The character string is converted and XML is output.
			  case 2 => new XmlSp(request, response, contentFilePath)
			  // The analytical result of HTML parser is converted in the character string and it outputs it.
			  case 3 => new Htmlparse2Sp(request, response, contentFilePath)// javax.swing.text.html.parser._
			  case 4 => new HtmlparseSp(request, response, contentFilePath)	// nu.validator.htmlparser._ for TEST
			  case 5 => new ValidatorHtmlparserSp(request, response, contentFilePath)	// nu.validator.htmlparser for TEST
			  case 6 => new Htmlparse3Sp(request, response, contentFilePath)// org.cyberneko.html.parsers._ for TEST
			  case 7 => new Htmlparse4Sp(request, response, contentFilePath)// org.htmlparser.sax._ for TEST
			  case 8 => new Ssp(request, response, contentFilePath)			// Dynamic page. use the nu.validator.htmlparser
			  case 9 => new Htmlparse5Sp(request, response, contentFilePath)// Dynamic page. use the org.htmlparser.sax._
			}
	   		val re = if(ssp.html != null){
	   			response.setStatus(200)
	   			ssp.toString
	   		} else {
				error(404,"Not Found "+htmlFile,response)
				return
	   		}
	   		ct += "; charset=" + characode
			response.setContentType(ct)
			val out = response.getWriter
			out.println(re)	// output html data
			out.flush
			out.close
		} else {
			var stream:FileInputStream = null
			try {
				val binaryFile = new java.io.File(contentFilePath)
				if(binaryFile.exists){
					response.setContentType(ct)
					response.setStatus(200)
					stream = new FileInputStream(binaryFile)
					val buf:Array[Byte] = new Array(1024)
					var out = response.getOutputStream
					while(stream.available != 0){
						val i = stream.read(buf,0,1024)
						out.write(buf,0,i)
					}
				} else {
	        		System.err.println("Not Found the "+htmlFile)
					error(404,"Not Found "+htmlFile,response)
				}
			} catch {
	        	case e:IOException => {
	        		System.err.println("IOException "+e.getMessage)
	        		error(500,"Internal Server Error",response)
	        	}
	        	case e:Exception => {
	        		System.err.println("Exception "+e.getMessage)
	        		error(500,"Internal Server Error",response)
	        	}
			} finally {
				if(stream != null)
				  stream.close
			}
		}
//		logger.debug(" #### End")
	}
	// for error
	def error(status:Int,message:String, response : HttpServletResponse) :Unit = {
		response.setContentType("text/html")
		response.setStatus(status,message)
		var out = response.getOutputStream
		out.close
	}
	// htaccessファイルがHTMLファイルが置かれているディレクトにあるかを調べる
	// このファイルがあればそのディレクトにあるコンテンツへのアクセスは認証が必要となる。
	var htaccess = "htaccess.txt"
	def htaccess(fileName:String) : Boolean = {
		val point = fileName.lastIndexOf("/")
	    if (point == -1) {
	    	return false
	    }
        val htaccessPath = fileName.substring(0,point+1)
        val path = DocumentRoot + htaccessPath + htaccess
		try {
			val htmlFile = new java.io.File(path)
	        return htmlFile.exists
	    } catch {
	    	case e:IOException => {
	    		System.err.println("IOException "+e.getMessage)
	        }
	    }
		return false
	}
	// ファイル名からパスを取り出す
	def getPath(fileName:String) : String = {
		val point = fileName.lastIndexOf("/")
	    if (point == -1) {
	    	return null
	    }
        val htaccessPath = fileName.substring(0,point+1)
        return DocumentRoot + htaccessPath
	}
}

Webフレームワークへの追加機能

Posted by
Tagged As: | Categories: Scala | No Comments

一か月以上、投稿していないので最近の出来事を箇条書きにします。

1.Programming in Scala 2nd Edition が恵比須の本屋に平積みになっていたので購入。
  初版もページをぱらぱとめくっただけで結局読んでいない。
  2nd Editionはまだページさえ開いていない。いったいいつ読むんだ?

2.長い間投稿していない理由

      

  • Javaでプログラムを書くのが忙しい。仕事しているということ。
  •   

  • 興味が散漫。世の中には面白いものがいっぱいある。iPhone4sに機種変したり、アンドロイドタブレットを手に入れたり、
  •   

  • 新機能を追加しているが、完成していないので記事にできない。

新機能
 新機能は、次の機能を実装しています。
盛りだくさんすぎて消化しきれていません。

   新機能1:セッション管理
    クッキーを使ったセッション管理機能を追加した。
    トラッキングクッキーとセッションクッキーの2つのクッキーを使い分けています。
    セッション管理はセッションクッキーを使って実装しています。
    トラッキングクッキーは実際のサイトでは普通に使われているので利用できるようにしました。

   新機能2:認証機能
    セッション管理の機能を使ってログイン認証する機能です。
    Apacheサーバのように認証が必要なページがあるディレクトリをWEB.XMLか、
    htaccess.txtファイルをそのディレクトに置くと、このページをアクセスすると
    ログイン画面が割り込みます。
    IP・パスワードや認証状態はMySQL(Oracle)で管理

   新機能3:フォーム生成
    Bean情報からWEBフォームを自動生成
    WEBフォームからDBに自動的にデータ登録・更新・削除
    この機能は実装する予定はなかったのですが、実際にWEBアプリを開発し始めると欲しくなったので開発しました。
    INPUTタグのname属性とテーブルのカラム名の関連リストを作るとDB操作のコードを書く必要がありません。
    関連リストがないときは、カラム名として処理することもできます。

   新機能4:セキュリティ対策機能
    セキュリティ対策は、WEBフレームに必須な機能です。
    現在、有名なフレームでも以下の説明するセキュリティ対策の機能を持っていないものがほとんどです。

    IPAから10種類のWEBアプリの脆弱性について解説されています。
    WEBフレームで対策することが困難なものもありますが、これらの脆弱性への対策する機能を実装しています。
    10種類の脆弱性については以下の通りです。

    1.SQLインジェクション
      この機能は、WEBフレームワークではなく、SEEDOフレームワークに実装されています。
      「基本はプリペアードステートメント(java.sql.PreparedStatement)を使ってSQL文は書きましょう」なのですが、
      もっと突っ込んだ対策として
       WHERE(“ename=?”,name)と書かずに、Stringの連結で WHERE(“ename='”+name+”‘”) と記述すると警告するというものです。

    2.クロスサイト・スクリプティング
    3.CSRF (クロスサイト・リクエスト・フォージェリ)
      これらについは、予め設定した画面遷移を踏まないと、フォーム処理ができない仕組みを標準で持たせています。
      セッション管理機能とフォーム自動生成機能で実装しています。

    4・パス名パラメータの未チェック/ディレクトリ・トラバーサル
      相対パスでのアクセスができないようにプロテクトします。

    5.OS コマンド・インジェクション
      Scalaなので直接OSコマンドは実行できません。
      RubyやPHPのようなスクリプト言語は注意しないといけませんね。

    6.セッション管理の不備
      Cookieを使ったセッション管理はSSLを使わない限り安全性の高いセッション管理を実装することは難しいです。
      現在、実装しているセッション管理では、セッションを横取りされないためにページ遷移するごとに
      乱数発生し、Cookieにこの乱数を格納してSubmitされたリクエストと乱数が整合しているかをチェックすることで
      安全性を高めようというアプローチです。

    7.HTTP ヘッダ・インジェクション
      WEBフレーム独自のヘッダ情報で管理します。

    8.HTTPS の不適切な利用
      これはWEBフレーム側の問題ではないです。

    9.サービス運用妨害 (DoS)
      通信容量を超えるアクセスへの対応は困難ですが、
      正規のアクセスではないと区別することができれば、DoS攻撃のアクセスをフィルタリングすることが可能です。

   10.メール不正中継
      これはWEBフレームワークで対策することは難しいです。

   上記、セキュリティ対策機能を構築しているのですが、これをすべて満たしてから正規にリリースしようと考えております。
   となると、いつまでたってもリリースできなくなってしまいますので、
   対策したものとそうでないものを明記してリリースしてはどうかとも考えております。

   しかし、このページへの反響が私には届いていないので、、、

   賛同してくれて開発に協力してくださる方を募集します。
   

ActiveRecordのようにDB操作 複数レコードの扱い

Posted 2011年9月11日 by
Tagged As: | Categories: Scala | No Comments

BeanTalkクラスを前回紹介しました。
ActiveRecordに近づけるために、find関数で複数レコードを扱えるように機能拡張しました。



クラス名をBeansTalkと複数形にしました。
bean stalkと単語の区切りを変えると、豆の木(茎)になり「ジャックと豆の木」という意味になることに気が付きました。



ActiveRecordについていろいろなことを書きましたが、正直、Ruby on railsのActiveRecordでコードを書いたことがないので、
頓珍漢なことを書いていてもActiveRecordフリークの方々ご容赦ください。



とわいえ、ActiveRecordfind_by_sqlの逆を行くわけで、長年SQL文を書いて生活の糧を得ていた者としては真逆の行為です。

しかし、簡単なWEBアプリケーションを書くなら、これで十分という気持ちです。

複数レコードを扱えるようにしたので、N:Nの関係を表現できます。



BlogやSNSを実装するならこれで十分かな?


package seedo.database
import scala.collection.mutable.{HashMap,LinkedHashMap,HashSet,ArrayBuffer}
import scala.reflect._
import scala.collection.mutable.Map
import seedo._

class BeansTalk[T] (implicit m : ClassManifest[T]) {
	var beanObj = m.erasure.newInstance().asInstanceOf[T]	// TのclassのBean を生成
	var joinEntity = Map.empty[String,Array[AnyRef]]	// joinしているテーブルのレコード

	val beanInfo = new FromBean(beanObj.asInstanceOf[AnyRef])	// Beanのアノテーション情報からテーブル情報を読み取る
	var praimaryKey : String = null	// プライマリキー
	if(beanInfo.praimaryKey.length == 1)
		praimaryKey = beanInfo.praimaryKey(0)
	var beanObjs:Array[T] = null	// 複数レコードが検索されたときに格納
	var joinEntities:Array[Map[String,Array[AnyRef]]] = null	// 複数レコードのときにJOINした表データを格納
	var column:String = "*"		// SELECTするときに読み込むカラムを指定
	var key:String = null		// Order by句 のソートキー
	var condition:String = null	// Where句の条件
	var values:Array[Any] = null	// 条件に指定する値
	// SELECT文の構成要素を初期化
	def clean:Unit = {
		column = "*"
		key = null
		condition = null
		values = null
	}
	var db:Db = null	// Dbクラス
	var tran = true		// トランザクション制御する。外部からDbクラスのオブジェクトが渡されたときはトランザクションモード(false)
	def bean : T = beanObj
	def entity : T = beanObj
	def beans : Array[T] = beanObjs
	var joins = Map.empty[String,List[List[String]]]	// join先のBean名とjoinの条件
	/**
	 * Joinしているテーブルのレコードを返す
	 */
	def getjoinEntity(joinBeanName:String) :Option[Array[AnyRef]] = {
	  	joinEntity.get(joinBeanName)
	}
	// join has_one / has_many
	def addJoin(beanName:String,on:List[String]*) : Unit = {
	  joins.put(beanName,on.toList)
	}
	def removeJoin(beanName:String) : Unit = {
	  joins.remove(beanName)
	  joinEntity.remove(beanName)
	}
	def removeAllJoin() : Unit = {
	  joins.clear
	  joinEntity.clear
	}
	def setDb(dbx:Db):Unit = {
		db = dbx
		tran = false
	} 
	// SELECT One record. praimaryKey is some columns.
	def getEntity(pk:Any*) : T = {
	  if(tran)
		  db = new Db
	  if(column==null)
		column = "*"
	  var sql:DL = db
	  val result = db.selectBean(beanObj.asInstanceOf[AnyRef],pk:_*)
	  if(tran){
		  db.close
		  db = null
	  }
	  joinsRecords(beanObj, joinEntity)
	  clean
	  return result.asInstanceOf[T]
	}
	// set bean object.
	def setEntity(e:T) : Unit = {
		beanObj = e
	}
	// select One record
	def find : T = {
	  if(tran)
		  db = new Db
	  if(column==null)
		  column = "*"
	  val sql:DL = db
	  val result = db.selectBean(beanObj.asInstanceOf[AnyRef])
	  if(tran){
		  db.close
		  db = null
	  }
	  joinsRecords(beanObj, joinEntity)
	  clean
	  return result.asInstanceOf[T]
	}
	// gen 'in' sentence.
	private def qu(no:Int):String = {
		var str = ""
		for(i <- 1 to no){
			if(i != 1){
			  str += ","
			}
			str += "?"
		}
		str
	}
	// select join records.
	private def joinsRecords(beanO:T,joinE:Map[String,Array[AnyRef]]) :Unit = {
	  joinE.clear
	  joins.foreach(j => {
		  val clazz = beanO.getClass
		  val k = j._2
		  var whereStr = ""
		  val whereVal = new ArrayBuffer[Any]
		  var columnStr = "*"
		  var n = 0
		  k.foreach(i => {
			  if(i.length > 2){
				  val methodName = "get" + i(0)	// Master側のJOINカラム BENAからそのカラムの値を取り出す
				  val v = clazz.getMethod(methodName).invoke(beanO)
				  whereVal += v
				  if(n != 0){
					  whereStr += " and "
				  }
				  whereStr += i(2) + i(1) + "?"	// Detail側のJOINカラム 抽出条件を作る。i(2)は、JOIN側のカラム名、i(1)は比較演算子「'=','<>','<','>','<=','>='」
				  
				  if(i.length > 3){
					columnStr = i(3)	// Detail側のSELECTしたときに取るカラム
				  }
			  }else{
				  throw new Exception("Join sentence should be 'Master.column name' + '=' + 'Detail.column name'. <- " + i.toString)
			  }
			  n += 1
		  })
		  val dl = db.select(columnStr)
		  if(whereVal.length > 0){
			  dl.whereParam(whereStr, whereVal.toArray)
		  }
		  val result = db.executeQueryBean(j._1)
		  joinE.put(j._1,result.toArray)
	  })	  
	}
	// SELECT some records. praimaryKey is one column.
	def find(ids:Any*) : Array[T] = {
	  joinEntities = null
	  if(tran)
		  db = new Db
	  if(column==null)
		  column = "*"
	  var sql:DL = db.select(column).from(beanInfo.table)
	  if(ids != null) {
		  sql.where(praimaryKey + " in (" + qu(ids.length) + ")",ids:_*)
	  }
	  if(key != null) {
		  sql.orderBy(key)
	  }
	  val beanObjsBuf = db.executeQuery(beanObj.asInstanceOf[AnyRef]).asInstanceOf[ArrayBuffer[T]]
	  if(beanObjsBuf.length == 1){	// 検索結果が1の場合は、自身のbeanObjを入れ替える
		  beanObj = beanObjsBuf(0)
		  joinsRecords(beanObj, joinEntity)
	  } else {
		  joinEntities = new Array[Map[String,Array[AnyRef]]](beanObjsBuf.length)
		  var i = 0
		  beanObjsBuf.foreach(v => {
			  val je = Map.empty[String,Array[AnyRef]]
			  joinsRecords(v, je)
			  joinEntities(i) = je
			  i += 1
		  })
	  }
	  if(tran){
		  db.close
		  db = null
	  }
	  clean
	  beanObjs = beanObjsBuf.toArray 
	  return beanObjs
	}
	// SELECT WHERE句で条件をレコードを絞り込む
	def findAll : Array[T] = {
	  if(tran)
		  db = new Db
	  var sql:DL = db.select(column).from(beanInfo.table)
	  if(condition != null){
		  sql.whereParam(condition,values)
	  }
	  if(key != null) {
		  sql.orderBy(key)
	  }
	  val beanObjsBuf = db.executeQuery(beanObj.asInstanceOf[AnyRef]).asInstanceOf[ArrayBuffer[T]]
	  if(tran){
		  db.close
		  db = null
	  }
	  clean
	  beanObjs = beanObjsBuf.toArray 
	  return beanObjs
	}
	// UPDATE one record
	def update : Int = {
		if(tran)
			db = new Db
		try {
			return db.updateBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	def save : Int = {
		update
	}
	// DELETE one record
	def delete : Int = {
		if(tran)
			db = new Db
		try {
			return db.deleteBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	// DELET some records. praimaryKey is one column.
	def delete(ids:Any*) : Int = {
		if(tran)
			db = new Db
		try {
			var sql:DL = db.deleteFrom(beanInfo.table)
			if(ids != null) {
				sql.where(praimaryKey + " in (" + qu(ids.length) + ")",ids:_*)
			}
			return db.execute.getResultSize
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
	  }
	}
	// INSERT one record
	def create : Int = {
		if(tran)
			db = new Db
		try {
			return db.insertBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	// SQL SELECT sentence
	def column(col:String) :BeansTalk[T] = {
		this.column = col
		this
	}
	def where(condition:String,values:Any*) :BeansTalk[T] = {
		this.condition = condition
		this.values = values.toArray
		this
	}
	def conditions(condition:String,values:Any*) :BeansTalk[T] = {
		this.condition = condition
		this.values = values.toArray
		this
	}
	def orderBy(k:String) :BeansTalk[T] = {
		this.key = k
		this
	}
	def commit : BeansTalk[T] = {
		if(!tran)
			db.commit
		this
	}
	def rollback : BeansTalk[T] = {
		if(!tran)
			db.rollback
		this
	}
	def dump : Unit = {
		println(beanObj.toString)
		if(joinEntity != null) {
			joinEntity.foreach(w => {
				println(" " + w._1)
				w._2.foreach(x =>{
					println("  " + x)
				})
			})
		}
	}
	def dumps : Unit = {
		var i = 0
		if(beanObjs == null)
			return
		beanObjs.foreach(r =>{
			println(r.toString)
			if(joinEntities != null) {
				joinEntities(i).foreach(w =>{
					println(" " + w._1)
					w._2.foreach(x =>{
						println("  " + x)
					})
				})
			}
			i += 1
		})
	}
}




テストコードです



find関数に従業員IDを3つの引数でを渡します。

@Test def test1 {
	println("--- TSET 1 ---")
	beanobj.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO"))
	val r = beanobj.find(7788,7698,7934)
	beanobj.dumps
}




実行結果


--- TSET 1 ---
7698,BLAKE,MANAGER,7839,1981-05-01 00:00:00.0,2850,,30
 scott.bean.DEPT
  30,SALES,CHICAGO
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
 scott.bean.DEPT
  20,RESEARCH,DALLAS
7934,MILLER,CLERK,7782,1982-01-23 00:00:00.0,1300,,10
 scott.bean.DEPT
  10,ACCOUNTING,NEW YORK
--- End TEST ---




テストコードその2



テスト2-1では、
where関数で検索する条件としてENAMEがSで始まる人をSELECTして、
orderBy関数によってENAMEの順序でソートします。

テスト2-2では、
where関数により給与が1500以上の条件で、
column関数でSELECTするカラムを”empno,sal,ename”と指定します。
orderBy関数によって給与順にソート。

@Test def test2 {
	println("--- TSET 2-1 ---")
	val r1 = beanobj.where("ename like ?","S%").orderBy("ename").findAll
	r1.foreach(v => println(v.toString))
	println("--- TSET 2-2 ---")
	val r2 = beanobj.column("empno,sal,ename").where("sal > ?",1500).orderBy("sal").findAll
	r2.foreach(v => println(v.toString))
}




実行結果


--- TSET 2-1 ---
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
7369,SMITH,CLERK,7902,1980-12-17 00:00:00.0,800,,20


--- TSET 2-2 ---
7499,ALLEN,1600
7782,CLARK,2450
7698,BLAKE,2850
7566,JONES,2975
7788,SCOTT,3000
7902,FORD,3000
7839,KING,5000
--- End TEST ---




BeansTalkクラスは、seedo-1.2.4.jarに格納しました。
動かしてみたい方は、
seedo-1.2.4.jarはこのリンクからダウンロードできます。

BEANを使うために以下の2つのJARもダウンロードしてください。
scott.jar と jpa.jar

MySQLの環境は事前に作ってください。

ActiveRecordのようにDB操作

Posted by
Tagged As: | Categories: Scala | No Comments

ActiveRecordのようにDB操作ということで、前回コードを書いてみましたが、EMP表に依存したクラスになってしまいました。
新しいコードはテーブルに依存しない汎用のBeanTalkクラスを書いてみました。

このクラスはかなり複雑な内容になってます。
implicitという表記を使っています。
リフレクションするときの定番の書き方です。
もっと綺麗な表記もあるのでしょうがこれ以上わかりにくくしたくないのでこのままにします。


package seedo.database
import scala.collection.mutable.{HashMap,LinkedHashMap,HashSet,ArrayBuffer}
import scala.reflect._
import scala.collection.mutable.Map
import seedo._

class BeanTalk [T] (implicit m : ClassManifest[T]) {
	var beanObj = m.erasure.newInstance().asInstanceOf[T]	// TのclassのBean を生成
	var joinEntity = Map.empty[String,Array[AnyRef]]	// joinしているテーブルのレコード
	var column:String = "*"		// SELECTするときに読み込むカラムを指定
	// SELECT文の構成要素を初期化
	def clean:Unit = {
		column = "*"
	}
	var db:Db = null	// Dbクラス
	var tran = true		// トランザクション制御する。外部からDbクラスのオブジェクトが渡されたときはトランザクションモード(false)
	def bean : T = beanObj
	def entity : T = beanObj
	var joins = Map.empty[String,List[List[String]]]	// join先のBean名とjoinの条件
	/**
	 * Joinしているテーブルのレコードを返す
	 */
	def getjoinEntity(joinBeanName:String) :Option[Array[AnyRef]] = {
	  	joinEntity.get(joinBeanName)
	}
	// join has_one / has_many
	def addJoin(beanName:String,on:List[String]*) : Unit = {
	  joins.put(beanName,on.toList)
	}
	def removeJoin(beanName:String) : Unit = {
	  joins.remove(beanName)
	  joinEntity.remove(beanName)
	}
	def removeAllJoin() : Unit = {
	  joins.clear
	  joinEntity.clear
	}
	def setDb(dbx:Db):Unit = {
		db = dbx
		tran = false
	} 
	// SELECT One record. praimaryKey is some columns.
	def getEntity(pk:Any*) : T = {
	  if(tran)
		  db = new Db
	  if(column==null)
	    column = "*"
	  var sql:DL = db
	  val result = db.selectBean(beanObj.asInstanceOf[AnyRef],pk:_*)
	  if(tran){
		  db.close
		  db = null
	  }
	  joinsRecords(beanObj, joinEntity)
	  clean
	  return result.asInstanceOf[T]
	}
	// set bean object.
	def setEntity(e:T) : Unit = {
		beanObj = e
	}
	// select One record
	def find : T = {
	  if(tran)
		  db = new Db
	  if(column==null)
		  column = "*"
	  val sql:DL = db
	  val result = db.selectBean(beanObj.asInstanceOf[AnyRef])
	  if(tran){
		  db.close
		  db = null
	  }
	  joinsRecords(beanObj, joinEntity)
	  clean
	  return result.asInstanceOf[T]
	}
	// select join records.
	private def joinsRecords(beanO:T,joinE:Map[String,Array[AnyRef]]) :Unit = {
	  joinE.clear
	  joins.foreach(j => {
		  val clazz = beanO.getClass
		  val k = j._2
		  var whereStr = ""
		  val whereVal = new ArrayBuffer[Any]
		  var columnStr = "*"
		  var n = 0
		  k.foreach(i => {
			  if(i.length > 2){
				  val methodName = "get" + i(0)	// Master側のJOINカラム BENAからそのカラムの値を取り出す
				  val v = clazz.getMethod(methodName).invoke(beanO)
				  whereVal += v
				  if(n != 0){
					  whereStr += " and "
				  }
				  whereStr += i(2) + i(1) + "?"	// Detail側のJOINカラム 抽出条件を作る。i(2)は、JOIN側のカラム名、i(1)は比較演算子「'=','<>','<','>','<=','>='」
				  
				  if(i.length > 3){
				    columnStr = i(3)	// Detail側のSELECTしたときに取るカラム
				  }
			  }else{
				  throw new Exception("Join sentence should be 'Master.column name' + '=' + 'Detail.column name'. <- " + i.toString)
			  }
			  n += 1
		  })
		  val dl = db.select(columnStr)
		  if(whereVal.length > 0){
			  dl.whereParam(whereStr, whereVal.toArray)
		  }
		  val result = db.executeQueryBean(j._1)
		  joinE.put(j._1,result.toArray)
	  })	  
	}
	// UPDATE one record
	def update : Int = {
		if(tran)
			db = new Db
		try {
			return db.updateBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	def save : Int = {
		update
	}
	// DELETE one record
	def delete : Int = {
		if(tran)
			db = new Db
		try {
			return db.deleteBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	// INSERT one record
	def create : Int = {
		if(tran)
			db = new Db
		try {
			return db.insertBean(beanObj.asInstanceOf[AnyRef])
		} catch {
			case e:Exception => {
				db.rollback
				throw e
			}
		} finally {
			if(tran){
				db.commit
				db.close
				db = null
			}
			clean
		}
	}
	// SQL SELECT sentence
	def column(col:String) :BeanTalk[T] = {
		this.column = col
		this
	}
	def commit : BeanTalk[T] = {
		if(!tran)
			db.commit
		this
	}
	def rollback : BeanTalk[T] = {
		if(!tran)
			db.rollback
		this
	}
	def dump : Unit = {
		println(beanObj.toString)
		if(joinEntity != null) {
			joinEntity.foreach(w => {
				println(" " + w._1)
				w._2.foreach(x =>{
					println("  " + x)
				})
			})
		}
	}
}



ActiveRecordでは、1つのクラスが 基本的にDBの1テーブルに対応し、クラスの属性(attribute)は、テーブルの各カラムに対応します。
ActiveRecordのDB操作についての私の見方は、MS-ACCESSのような操作イメージです。
数値型のプライマリーキー・カラムが1つあり、このキーによってレコードを特定します。
UPDATE操作やDELETE操作は、ACCESSの画面で操作するためテーブルのレコードデータをエクセルのシートのような表に読み込んでから操作します。人がPCでマウスで操作しますから当然です。

ACCESSはデータベースシステム自身であり、VBAでそのデータを操作するのであればこれでよいのですが、クライアント・サーバモデルでデータ操作するときに、オブジェクトとしてレコードデータをfindしてからUPDATEやDELETEというのはロジックのわかりやすさという点ではいいのですが、処理としては非効率です。

非手続き型言語のSQLと、RubyやJava、Scalaのような手続き型言語の折り合いの悪さは、リレーショナルデータベースとプログラミング言語の中のインピーダンスミスマッチとして議論され続けています。


話が違う方向に行きそうなので、話を戻すと、
BeanTalkクラスの戦略は、DBの1テーブルは、Beanクラスが対応します。
BeanTalkクラスでは、var beanobj = new BeansTalk[scott.bean.EMP]によってBeanをラッピングして、DB操作を共通化しますがSQL文をプログラマに見せません。

BeanTalkクラスが生成されると、内部にBeanオブジェクトが1つ生成されます。




Dbクラス、Beanを介してのSELECTなどのSQL操作は、SeedoフレームワークにあるDB操作のためのJarにあります。




早速、このクラスのテストコードを見ましょう。

@Beforeinitialize関数beanobj = new BeanTalk[scott.bean.EMP]によってEMP表のBeanTalkオブジェクトを生成します。
DEPT表についても、beanobjDept = new BeanTalk[scott.bean.DEPT]でBeanTalkオブジェクトを生成し、JDBCドライバによってデータベース接続を行うdb = new Db(driver, dsn, user, password)を生成し、各BeanTalkオブジェクトにセットします。


このテストコードは、junitなので明示的にDbクラスを生成していますが、TOMCATでBeanTalkクラスを使う場合は、TOMCATのコネクションプールからDB接続を入手しますので、driver,dsn,user,passwordは明示する必要はありません。

package test
import org.scalatest.junit.{JUnitSuite,ShouldMatchersForJUnit}
import org.junit.{Test,Before,After}
import seedo.database._

class BeanTalkTest extends JUnitSuite with ShouldMatchersForJUnit{
	val driver = "com.mysql.jdbc.Driver"
	val dsn = "jdbc:mysql://127.0.0.1:3306/seedo?useUnicode=true&characterEncoding=UTF-8"
	val user ="scott"
	val password ="tiger"
	var db:Db = null
	var beanobj:BeanTalk[scott.bean.EMP] = null
	var beanobjDept:BeanTalk[scott.bean.DEPT] = null
	@Before def initialize() {
		println("--- Start TSET ---")
		beanobj = new BeanTalk[scott.bean.EMP]
		println("getClass=" + beanobj.getClass)
		beanobjDept = new BeanTalk[scott.bean.DEPT]
		db = new Db(driver, dsn, user, password)
		beanobj.setDb(db)
		beanobjDept.setDb(db)
	}
	@Test def test1 {
		println("--- TSET 1 ---")
		beanobj.bean.setEMPNO(7788)
		val r = beanobj.find
		beanobj.dump
	}
	@Test def test2 {
		println("--- TSET 2 ---")
		try {
			beanobj.getEntity(7788)
			println("getEntity=" + beanobj.entity)
			beanobj.dump
			val entityCopy = beanobj.entity.clone
			
			println("--update op")
			beanobj.bean.setDEPTNO(10)
			beanobj.bean.setSAL(3200)
			beanobj.update
			println("--find")
			beanobj.find
			beanobj.dump

			println("---update op")
			beanobj.bean.setDEPTNO(20)
			beanobj.bean.setSAL(3000)
			beanobj.update
			println("---find")
			beanobj.find
			beanobj.dump
			
			println("---delete op")
			beanobj.delete
			println("---find")
			val b = beanobj.find
			if(b != null)
				beanobj.dump
			
			beanobj.setEntity(entityCopy)
			
			println("---create op")
			beanobj.create
			println("---find")
			beanobj.find
			beanobj.dump
		} catch {
		 case e:Exception => {
            println("Exception "+e.getMessage)
		 }
		}
	}
	@Test def test3 {
		println("--- TSET 3 ---")
		beanobj.bean.setEMPNO(7788)
		beanobj.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO","DEPTNO,DNAME,LOC"))
		beanobj.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"),List("JOB","=","JOB"))
		val r = beanobj.find
		println(r.toString)
		val s = beanobj.getjoinEntity("scott.bean.DEPT") match {
		  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
		  case None => Nil
		}
		val s3 = beanobj.getjoinEntity("scott.bean.EMP") match {
		  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
		  case None => Nil
		}
	}
	@Test def test4 {
		println("--- TSET 4 ---")
		beanobj.removeAllJoin
		beanobj.bean.setEMPNO(7782)
		val r3 = beanobj.find
		println(r3.toString)
		val s4 = beanobj.getjoinEntity("scott.bean.DEPT") match {
		  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
		  case None => Nil
		}
		val s5 = beanobj.getjoinEntity("scott.bean.EMP") match {
		  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
		  case None => Nil
		}
	}
	@Test def test5 {
		println("--- TSET 5 ---")
		beanobjDept.bean.setDEPTNO(10)
		beanobjDept.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"))
		beanobjDept.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO"))
		val r1 = beanobjDept.find
		println(r1.toString)
		val s1 = beanobjDept.getjoinEntity("scott.bean.EMP") match {
		  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
		  case None => Nil
		}
		val s2 = beanobjDept.getjoinEntity("scott.bean.DEPT") match {
		  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
		  case None => Nil
		}
	}
	@After def dest {
		db.close
		println("--- End TEST ---\n")
	}
}



JUNITの実行結果

最初のコードは、EMP表からEMPNOが7788の従業員をSELECTします。
BeanTalkの中でEMP表のBEANに対してEMPNOをSETします。
次にfind関数を呼び出せば、BEANのレコード内容が設定されます。
その内容は、dump関数によってプリントします。

beanobj.bean.setEMPNO(7788)
val r = beanobj.find
beanobj.dump




7788番のスコットさんがSELECTされています


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 1 ---
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
--- End TEST ---



次のコードは、SELECT,UPDATE,DELETE,INSERTの一連のDB操作です。

先ほどと同様に7788番のレコードをgetEntity関数でSELECTします。なぜfind関数ではないかは置いておいて、

beanobj.entity.clone関数によって7788のBEANの複製を作ります。


beanobj.getEntity(7788)
println("getEntity=" + beanobj.entity)
beanobj.dump
val entityCopy = beanobj.entity.clone




その結果が以下です。
dump関数によってスコットさんのレコードがプリントされています。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 2 ---
getEntity=7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20




次に、スコットさんの所属を20番から10番に、給料を3000から3200に更新します。BEANのSETTER関数で値を設定し、update関数を実行します。

更新した内容を再び、find関数でSELECTしてdump関数でプリントします。


println("--update op")
beanobj.bean.setDEPTNO(10)
beanobj.bean.setSAL(3200)
beanobj.update
println("--find")
beanobj.find
beanobj.dump




結果は、スコットさんの所属と給料が変更されています。


--update op
--find
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3200,,10




次に、再び所属と給料を設定してデータを元に戻します


println("---update op")
beanobj.bean.setDEPTNO(20)
beanobj.bean.setSAL(3000)
beanobj.update
println("---find")
beanobj.find
beanobj.dump




データは、元に戻っています。


---update op
---find
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20




次の操作は、
delete関数によってスコットさんのレコードを削除します。
find関数で削除された7788番のレコードを検索します。


println("---delete op")
beanobj.delete
println("---find")
val b = beanobj.find
if(b != null)
	beanobj.dump




結果は、レコードが削除されたのでプリントされません。

find関数は、レコードが検索できなかった場合はnullを返します。


---delete op
---find




beanobj.setEntity(entityCopy)関数によって先ほど作ったBeanクーロン(複製)をbeanobjにセットします。
create関数を呼び出し、EMP表にINSERTします。


beanobj.setEntity(entityCopy)
println("---create op")
beanobj.create
println("---find")
beanobj.find
beanobj.dump




再びスコットさんのレコードが検索できます。


---create op
---find
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
--- End TEST ---




この操作では、まったくSQL文は見えません。


マスター・ディテール



3つ目のテストは、マスター・ディテールの1:N関係です。



addJoin関数によってEMP表とDEPT表をSELECTします。
この処理は決して、EMP表とDEPT表をWHERE句でジョイン(結合)してSELECTしているわけではありません。



addJoin関数の
第一引数は、DEPT表用のBEANクラスの名称(scott.bean.DEPT)。
第二引数は、EMP表とDEPT表の結合関係をListによって表します。



関係は、
Listの
第一要素がEMP表のカラム
第二要素が関係式。以下の処理では’=’
第三要素がDEPT表のカラム
第四要素はDEPT表のBEANにSELECTして値を取ってくるカラムをString型で表記します。この第四要素は省略可能で、省略するとすべてのレコード値がBEANにセットされます。SQL文で書けば SELECT * FROM DEPT ということです。



関係式の意味をもう少し詳しく説明すると、
EMPのBEANには、スコットさんの従業員IDである7788となっていて、
所属は10です。この値は部門IDであり、DEPT表のプライマリーキーでもあります。
addJoin関数で結合関係を定義するということは、DEPT表をSELECTするSQL文は以下のようになります。



SELECT * FROM DEPT WHERE DEPTNO=10;



以下のSQL文が発行されるわけではありません。



SELECT * FROM EMP,DEPT WHERE EMP.DEPTNO=DEPT.DEPTNO;



以下のテストケースでは、さらにEMP表とも関係させています。
EMP表とEMP表の関係は、所属がスコットさんと同じで、かつ、同じ仕事をしている従業員をSELECTします。
スコットさんのJOBはANALYSTですね。

addJoin関数の第二引数以降はListの可変引数ですので、複数の関係条件を指定することができます。


beanobj.bean.setEMPNO(7788)
		beanobj.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO","DEPTNO,DNAME,LOC"))
		beanobj.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"),List("JOB","=","JOB"))
val r = beanobj.find
println(r.toString)
val s = beanobj.getjoinEntity("scott.bean.DEPT") match {
  case Some(v) => v.foreach(w => println(" DEPT " + w.toString))
  case None => Nil
}
val s3 = beanobj.getjoinEntity("scott.bean.EMP") match {
  case Some(v) => v.foreach(w => println(" EMP " + w.toString))
  case None => Nil
}




結果です



所属はRESEARCH部門。部門の所在地はDALLASです。
フォードさんが同じ所属で仕事がANALYSTです。スコットさんもSELECTされます。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 3 ---
7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
 DEPT 20,RESEARCH,DALLAS
 EMP 7788,SCOTT,ANALYST,7566,1987-04-19 00:00:00.0,3000,,20
 EMP 7902,FORD,ANALYST,7566,1981-12-03 00:00:00.0,3000,,20
--- End TEST ---



次は、JOIN条件を外します
removeAllJoin関数を呼び出すと、EMPとDEPTの1:Nと、EMPとEMPの1:Nの関係が削除されます。

beanobj.removeAllJoin
beanobj.bean.setEMPNO(7782)
val r3 = beanobj.find




結果は、7782番のクラークさんがSELECTされますが、
先ほどの関係はないので、DEPTとEMPのディテールはSELECTされません。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 4 ---
7782,CLARK,MANAGER,7839,1981-06-09 00:00:00.0,2450,,10
--- End TEST ---



次のテストケースでは、マスターがDEPT表、ディテールがEMP表です。
ここでは部門IDが10番に所属する従業員がSELECTされます。
データとしての意味はないですが、DEPT表とDEPT表も関係づけします。


beanobjDept.bean.setDEPTNO(10)
beanobjDept.addJoin("scott.bean.EMP",List("DEPTNO","=","DEPTNO"))
beanobjDept.addJoin("scott.bean.DEPT",List("DEPTNO","=","DEPTNO"))
val r1 = beanobjDept.find




ACCOUNTING部門はニューヨークにあり、3人が所属していることがわかります。


--- Start TSET ---
getClass=class seedo.database.BeanTalk
JDBC Driver:MySQL-AB JDBC Driver Version:mysql-connector-java-5.1.8 ( Revision: ${svn.Revision} )
Database:MySQL Version:5.5
--- TSET 5 ---
10,ACCOUNTING,NEW YORK
 EMP 7782,CLARK,MANAGER,7839,1981-06-09 00:00:00.0,2450,,10
 EMP 7839,KING,PRESIDENT,,1981-11-17 00:00:00.0,5000,,10
 EMP 7934,MILLER,CLERK,7782,1982-01-23 00:00:00.0,1300,,10
 DEPT 10,ACCOUNTING,NEW YORK
--- End TEST ---



DEPT BEAN クラス
最後に、DEPTクラスです。
このクラスは、SeedoフレームワークにあるBeanクラスの自動生成ツールによって作成されたものです。
MySQLやOracleにアクセスしてディクショナリ情報を参照してテーブルやビューのBeanコードを生成します。



package scott.bean

import scala.xml._
import java.math.BigDecimal
import java.sql.{Blob,Date,Timestamp}
import javax.persistence.{Column,Entity,Id,Table,Lob,UniqueConstraint,SequenceGenerator}

import seedo.Util

@Entity
@Table(name="DEPT")
class DEPT{
	// Type = TABLE
	// Index = 
	// Commect = -
	def PRIMARY_KEY :Array[String] = Array("DEPTNO")
	def timestamp_COLUMN :Array[String] = Array()
	def COLUMN :Array[String]= Array(
	"DEPTNO"		// NUMBER(2)
	,"DNAME"		// VARCHAR2(14)
	,"LOC"		// VARCHAR2(13)
	)

	def clear :Unit= {
		DEPTNO = null
		DNAME = null
		LOC = null
		for (i <- 0 to modifiedProperty.length - 1) {
			modifiedProperty(i) = false
		}
	}
	override def clone : DEPT = {
		var base = new DEPT
		if (DEPTNO != null) {
			base.DEPTNO = new BigDecimal(DEPTNO.toString)
		}
		if (DNAME != null) {
			base.DNAME = new String(DNAME)
		}
		if (LOC != null) {
			base.LOC = new String(LOC)
		}
		for (i <- 0 to modifiedProperty.length - 1) {
			base.modifiedProperty(i) = modifiedProperty(i)
		}
		base
	}
	def isModified  : Boolean = {
		modifiedProperty.foreach (i => {
			if (i) {
				return true
			}
		})
		return false
	}
	def isPrimaryKey(columnName:String) : Boolean = {
		if (columnName.equals("DEPTNO")) {return true}
		false
	}
	def isColumn(columnName:String) : Int= {
		return 0
	}
	/**
	 * Getter
	 * @return the DEPTNO
	 */
	def getDEPTNO:BigDecimal = DEPTNO
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO( Deptno:BigDecimal) : Unit = {
		this.DEPTNO = Deptno
		modifiedProperty(0) = true
	}
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO(Deptno:Int) :Unit = {
		this.DEPTNO = new BigDecimal(Deptno)
		modifiedProperty(0) = true
	}
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO(Deptno:Long) : Unit = {
		this.DEPTNO = new BigDecimal(Deptno)
		modifiedProperty(0) = true
	}
	/**
	 * Setter
	 * @param Deptno the DEPTNO to set
	 */
	def setDEPTNO(Deptno:Double) : Unit = {
		this.DEPTNO = new BigDecimal(String.valueOf(Deptno))
		modifiedProperty(0) = true
	}
	/**
	 * Getter
	 * @return the DNAME
	 */
	def getDNAME:String = DNAME
	/**
	 * Setter
	 * @param Dname the DNAME to set
	 */
	def setDNAME( Dname:String) : Unit = {
		this.DNAME = Dname
		modifiedProperty(1) = true
	}
	/**
	 * Getter
	 * @return the LOC
	 */
	def getLOC:String = LOC
	/**
	 * Setter
	 * @param Loc the LOC to set
	 */
	def setLOC( Loc:String) : Unit = {
		this.LOC = Loc
		modifiedProperty(2) = true
	}
	override def toString :String= {
		var buf = new StringBuffer
		if(modifiedProperty(0)){
			buf.append(if(DEPTNO!=null){DEPTNO}else{""})
		}
		if(modifiedProperty(1)){
			if(buf.length>0)buf.append(",")
			buf.append(if(DNAME!=null){DNAME}else{""})
		}
		if(modifiedProperty(2)){
			if(buf.length>0)buf.append(",")
			buf.append(if(LOC!=null){LOC}else{""})
		}
		buf.toString
	}
	def toXml :Node = {
<DEPT><DEPTNO>{DEPTNO}</DEPTNO><DNAME>{if(DNAME != null){seedo.Util.rpxml(DNAME)}else{""}}</DNAME><LOC>{if(LOC != null){seedo.Util.rpxml(LOC)}else{""}}</LOC></DEPT>	}
	def toXmlModified :Node = {
<DEPT>{if(modifiedProperty(0)){<DEPTNO>{DEPTNO}</DEPTNO>}}{if(modifiedProperty(1)){<DNAME>{if(DNAME != null){seedo.Util.rpxml(DNAME)}else{""}}</DNAME>}}{if(modifiedProperty(2)){<LOC>{if(LOC != null){seedo.Util.rpxml(LOC)}else{""}}</LOC>}}</DEPT>
	}
	// generate json format
	def toJson :String= {
		var buf = new StringBuffer
		buf.append("{")
		if(modifiedProperty(0)){
			buf.append("\"DEPTNO\":"+(if(DEPTNO!=null){DEPTNO}else{"null"}))
		}
		if(modifiedProperty(1)){
			if(buf.length>0)buf.append(",")
			buf.append("\"DNAME\":\""+(if(DNAME!=null){DNAME}else{"null"})+"\"")
		}
		if(modifiedProperty(2)){
			if(buf.length>0)buf.append(",")
			buf.append("\"LOC\":\""+(if(LOC!=null){LOC}else{"null"})+"\"")
		}
		buf.append("}")
		buf.toString
	}
	override def hashCode :Int = {
		return (DEPTNO).hashCode
	}
	@Id
	@Column(name="DEPTNO",precision=2,nullable=false,unique=true,columnDefinition="NUMBER(2)")
	@SequenceGenerator(name="PK_DEPT", sequenceName="PK_DEPT")
	var DEPTNO:BigDecimal = null
	@Column(name="DNAME",length=14,nullable=true,unique=false,columnDefinition="VARCHAR2(14)")
	var DNAME:String = null
	@Column(name="LOC",length=13,nullable=true,unique=false,columnDefinition="VARCHAR2(13)")
	var LOC:String = null
	var modifiedProperty:Array[Boolean] = Array(false,false,false,false)
}