動的ページ生成
class=”seedo:ews.HelloWorld:tb” 属性を実行する機能の実装です。
ExecClassクラスは、フルパスのクラス名をコンストラクタにして生成します。
Class.forName によってクラスを特定して、
newInstance.asInstanceOf[AnyRef] によってオブジェクトを生成します。
この処理の流れはJavaのリフレクションと同じ。
このコードだとクラスローダーを使って任意のクラス名の文字列からClassインスタンスを取得することはしていないので、実行したいクラスは予めクラスファイルかJarファイルをTOMCATに設定します。
最初のコードはシンプルにしたいのでクラスローダーの機能は省略。
package ews import java.lang.reflect.{Field,InvocationTargetException,Method,Type} import java.math.BigDecimal import java.util.{Calendar,Date} import java.sql.{CallableStatement, PreparedStatement, ResultSet,Timestamp} import javax.persistence.{Column,Entity,Id,Table,Lob,UniqueConstraint,SequenceGenerator} import seedo.Util /** * The function is executed specifying the class. */ class ExecClass(className:String ) { val myClass = Class.forName(className) // The class is gotten from the class name. val Obj = myClass.newInstance.asInstanceOf[AnyRef] // create Object val clazz:Class[_] = Obj.getClass // Class of MyClass /** * It executes it specifying the type of the argument of the function. */ def execStr(methodName:String,value:String) : AnyRef = { try { // The function with the Int in the argument is called. val v = value.toInt return exec(methodName,v) } catch { case e:Exception => {} } try { // The function with the Long in the argument is called. val v = value.toLong return exec(methodName,v) } catch { case e:Exception => {} } try { // The function with the Double in the argument is called. val v = value.toDouble return exec(methodName,v) } catch { case e:Exception => {} } return exec(methodName,value) } /** * The function with the argument is executed. */ def exec(methodName:String,value:Any) : AnyRef = { if(value == null){ val method:Method = clazz.getMethod(methodName) return method.invoke(Obj) } // The function is executed specifying the type of the argument of the function. if(value.isInstanceOf[Int]){// The function with the Int in the argument is called. val method = clazz.getMethod(methodName, classOf[Int]) val v = new Integer(value.asInstanceOf[Int]) return method.invoke(Obj, v) } else if(value.isInstanceOf[Long]){// The function with the Long in the argument is called. val method = clazz.getMethod(methodName, classOf[Long]) val v = new java.lang.Float(value.asInstanceOf[Long]) return method.invoke(Obj, v) } else if(value.isInstanceOf[Float]){// The function with the Float in the argument is called. val method = clazz.getMethod(methodName, classOf[Float]) val v = new java.lang.Float(value.asInstanceOf[Float]) return method.invoke(Obj, v) } else if(value.isInstanceOf[Double]){// The function with the Double in the argument is called. val method = clazz.getMethod(methodName, classOf[Double]) val v = new java.lang.Double(value.asInstanceOf[Double]) return method.invoke(Obj, v) } else if(value.isInstanceOf[Char]){// The function with the Char in the argument is called. val method = clazz.getMethod(methodName, classOf[Char]) val v = new Character(value.asInstanceOf[Char]) return method.invoke(Obj, v) } else { // other class var valClass:Class[_] = value.asInstanceOf[AnyRef].getClass val method = clazz.getMethod(methodName, valClass) return method.invoke(Obj, value.asInstanceOf[AnyRef]) } } /** * The function is executed. */ def exec(methodName:String) : AnyRef ={ if(methodName == null) return null try { val method:Method = clazz.getMethod(methodName) return method.invoke(Obj) } catch { case e:InvocationTargetException => { throw new Exception(e.getCause) } case e:Exception => { throw new Exception(e) } } } }
実行させたい関数を呼び出すためのexec関数は、
- 関数名だけで引数なしの場合は、def exec(methodName:String)
- 引数ありの場合は、def exec(methodName:String,value:Any)
引数なしのexec関数のコードには、例外処理のコードが追加されて、きちんと実装しています。
getMethodやinvokeで例外が発生した場合、
特に、InvocationTargetExceptionが発生したときは、
getCause()メソッドでターゲットのクラスからの例外を取得して、
throw new Exception(e.getCause)します。
def exec(methodName:String,value:Any)にも、
この例外処理を入れないといけません。このコードではとりあえず省略。
HTMLパーサしたときに、このクラスを使う場合は、def execStr(methodName:String,value:String) を使います。
execStr関数は、タグ属性で指定された引数文字列から、型、この場合、数字であれば
数値型の型変換します。
- Int 整数
- Long 整数
- Double 実数
型変換の処理は、toIntとかtoLongとかtoDoubleを呼び出して例外でなければ、その数値型で実行して、
例外が発生すれば、文字列として扱うためにString型で実行するという簡単なしかけです。
HelloWorldクラスのtb関数には、引数なし、Double引数、String引数の3つのtb関数を
定義しているので、もし、Intとして成立する数字文字列をタグ属性に書いてしまうと、
HelloWorldクラスには、そんな関数ないっていうことで例外が発生します。
次のLongもないので例外発生し、DoubleではOKとなります。
当然、タグ属性にtb関数以外の関数名が指定されても、そんな関数はないと例外発生となります。
ExecClassクラスの単体テスト用のコードを作ってテストします。
package ews import org.scalatest.junit.{JUnitSuite,ShouldMatchersForJUnit} import org.junit.{Test,Before} import scala.xml._ class ExecClassTest extends JUnitSuite with ShouldMatchersForJUnit{ var obj:ExecClass = null @Before def initialize() { obj = new ExecClass("ews.HelloWorld") } @Test def exec { println("Start ---------") var result:AnyRef = null try { result = obj.exec("tb") } catch { case e:Exception => { println("Exception "+e.getMessage) } } println(result) println(obj.execStr("tb","abcdefg")) println(obj.execStr("tb","3.14")) println(obj.execStr("tb","2001/1/1")) println(obj.execStr("tb","2011/8/20 12:9:59")) println(obj.execStr("tb","2")) println(obj.execStr("tb","99999999999999")) println("End ---------") } }
このコードを実行できるようにするには、JUNITとScalatest の2つのJarファイルをビルドパスに追加します。
私の環境では、以下のバージョンを使っていますが、最新のものでもテストできると思います。
- junit-4.9b3.jar
- scalatest-1.2.jar
テストを実行した結果です。
Start --------- HelloWorld Sun Aug 21 16:16:33 JST 2011 HelloWorld String [abcdefg] Sun Aug 21 16:16:33 JST 2011 HelloWorld Double [3.14] Sun Aug 21 16:16:33 JST 2011 HelloWorld String [2001/1/1] Sun Aug 21 16:16:33 JST 2011 HelloWorld String [2011/8/20 12:9:59] Sun Aug 21 16:16:33 JST 2011 HelloWorld Double [2.0] Sun Aug 21 16:16:33 JST 2011 HelloWorld Double [9.9999999999999E13] Sun Aug 21 16:16:33 JST 2011 End ---------
このtb関数で実行されたかは、HelloWorldの隣に型が出力され、型変換された結果が表示されます。
tb関数では、されに new Date で現在の時刻が表示されています。
いましがたテスト実行したので今日の日時です。
引数なしのtb関数呼び出しでは、例外発生したときに出力するようになっているので、
HelloWorldクラス側で、例外発生させるために、null 変数にtoStirng するような
コードを追加すればテストできます。
いよいよ次は、HTMLパーサによって解析したTAGからclass属性を探して、
TAG置換するコードとなります。