Scala+DBMS+Web スカラ座の夜

2012年2月20日

動的ページのフレームワークの機能拡張

Filed under: Framework,Scala — admin @ 8:01 PM

久しぶりにパーサー部分に機能追加

いままでの動的動作は毎回クラスからオブジェクトをNEWして関数実行していました。
これを、NEWしたオブジェクトに対して関数を複数実行できるように機能拡張。

タグ属性にseedoを定義して、以下のように値を設定する。

クラス名:関数名:引数
クラス名:関数名

<div align="center"  class="xxx" seedo="myapp.HelloWorldTop1:tb:2500" id="asse111" />

スタートタグとエンドタグの間の値を関数実行した返り値と置換します。

一度、指定したクラス名はページが有効である間はオブジェクトが保持されているので、
クラス名が同じで別の関数を設定すれば、NEWしたオブジェクトの関数を実行することができます。

<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Hello world top</h2>
The current time is
<div class="seedo:myapp.HelloWorldDb1:tb">xxxxx</div>
<HR>
<div align="center" class="seedo:myapp.HelloWorldDb1:tb" />
<HR>
<div align="center"  class="xxx" seedo="myapp.HelloWorldTop1:tb:2500" id="asse111" />
<HR>
<table border="1">
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:2" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:3" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:1" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:0" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
</table>
<HR>
</BODY>
</HTML>

このHTMLをフレームワークで実行すると、

HTMLファイルのなかで使っているHelloWorldTop1 クラスの定義は以下のとおり。

package myapp
import java.util.{HashMap,Date}
import scala.collection.JavaConversions._
import scala.collection.immutable._
import scala.xml._
import seedo.database._

class HelloWorldTop1 extends TmpApp {
  var EMPNO:String = "EMPNO"
  var ENAME:String = "ENAME"
  var SAL:String = "SAL"
  var DEPTNO:String = "DEPTNO"
  var JOB:String = "JOB"
  var MGR:String = "MGR"

  var records:Array[Array[Any]] = null
  def rc(record:Array[Any]) : Unit ={
    EMPNO = if(record(0)!=null){record(0).toString}else{""}
    ENAME = if(record(1)!=null){record(1).toString}else{""}
    SAL = if(record(2)!=null){record(2).toString}else{""}
    DEPTNO = if(record(3)!=null){record(3).toString}else{""}
    JOB = if(record(4)!=null){record(4).toString}else{""}
    MGR = if(record(5)!=null){record(5).toString}else{""}
  }
  def recx(record:Array[Any]) :Node =  {
	var nodes  = Queue.empty[Node]
	record.foreach{(fx) => nodes += <TD>{fx}</TD>}
    return <TR>{nodes}</TR>
  }  
  // Function one argument
  def tb(sal:String) : String = {
    tb(sal.toInt)
  }
  def tb(sal:Int) : String = {
    val deptno = parameterMap.get("deptno") match {
      case v:Array[String] => v(0).toInt 
      case null => 10
    }
    var sql:Db = null 
    try {
		sql = new Db
		sql.select("EMPNO,ENAME,SAL,DEPTNO,JOB,MGR")
			.from("EMP")
			.where("SAL > ?",sal)
			.or("DEPTNO = ?",deptno)
			.orderBy("EMPNO")
		records = sql.executeQuery
		var nodes  = Queue.empty[Node]
		records.foreach{(rec) => {
			nodes += recx(rec)
			rc(rec) // record values 最後のレコードの値が残ることになる。
		  }
		}
		val xml = <table border="2">{nodes}</table>
	    return "HelloWorldDb3 " + new Date + xml.toString
	} catch {
      case e:Exception =>{
        throw e
      }
    } finally {
    	if(sql != null)sql.close
    }
  }
  def get(no:Int) : String = {
    if(records != null && no < records.length) {
    	rc(records(no))
    	return "G E T "+no
    } else {
    	return "Empty"
    }
  }
  def getId : String ={EMPNO}
  def getName : String ={ENAME}
  def getDept : String ={DEPTNO}
  def getJob : String ={JOB}
  def getSal : String ={SAL}
  def getMgr : String ={MGR}
}

フレームワークのコードは以下のとおり。

package ews.servlet.sp

import java.util.HashMap
import org.xml.sax._
import org.xml.sax.helpers.DefaultHandler
import scala.collection.JavaConversions._
import scala.collection.mutable.Stack
import ews.servlet.session._

/**
 * HTMLを直接PrintWriterへ出力する。
 */
class ParserCallback6(pMap:HashMap[String,Array[String]], out:java.io.PrintWriter, session : Session) extends DefaultHandler {
	var buf = new StringBuffer
	val stackChange = new Stack[ExecTag]	// For substitution
	var locator:Locator = null
	var createClasses = new HashMap[String,ExecClass]	// created class
	override def toString : String = buf.toString
	override def setDocumentLocator (locator:Locator) :Unit=  {
		this.locator = locator
	}
	override def startDocument ():Unit= {}
	override def endDocument():Unit= {}
	// タグスタート
	override def startElement ( uri:String,  name:String, qualifiedName:String , attrs:Attributes ):Unit={
		var tag = "<"
		if(name.startsWith("!")){
			tag += name
		} else {
			tag += name.toLowerCase	// tagを小文字で統一する
		}
		var fullFag = false	//
		var seedoFag = false // Page class object
		var end = false		// タグが / で閉じているか?
		for (idx <- 0 to attrs.getLength - 1){
			val attributeType = attrs.getType(idx)
			val attrLocalName = attrs.getLocalName(idx) 
		  	val attrName = attrs.getQName(idx)
			val attrValue = attrs.getValue(idx)
			if(attrLocalName.length > 0 && !"#text".equals(attrLocalName)){
				if("/".equals(attrLocalName) && attrValue.length == 0){
					end = true	// tagが閉じた
				} else if("!DOCTYPE".equalsIgnoreCase(name) && "html".equalsIgnoreCase(attrLocalName)){
					tag += " html"
				} else {
					if("class".equals(attrName) && attrValue.startsWith("seedo:")){
						val execCommand = attrValue.split(":")	// Each element is separated. 
						if(execCommand.length >= 3) {	// It doesn't substitute it if there are neither a class name nor a function name. 
							stackChange.push(new ExecTag(
							0
							,name
							,execCommand
							,new StringBuffer	// The tag to be substituted is maintained. 
							,false // class="seedo:...."
							))
							fullFag = true	// 動的タグとして置換状態にある
						} else {
							println("$$$$$$$$$$ execClass Warning " + attrName + "='" + attrValue + "'")
						}
					} else if("seedo".equals(attrName) && attrValue.length > 0){	// Servlet class
						val execCommand = attrValue.split(":")	// Each element is separated. 
						if(execCommand.length >= 1) {	// It doesn't substitute it if there are neither a class name nor a function name. 
							stackChange.push(new ExecTag(
							0
							,name
							,execCommand
							,new StringBuffer	// The tag to be substituted is maintained.
							,true // SEEDO attribute
							))
							seedoFag = true
						} else {
							println("$$$$$$$$$$ execClass Warning " + attrName + "='" + attrValue + "'")
						}
					} else {
						tag += (" " + attrLocalName)
						if(attrValue != null && !("selected".equalsIgnoreCase(attrLocalName) || "checked".equalsIgnoreCase(attrLocalName) || "disabled".equalsIgnoreCase(attrLocalName) )) {
							tag += ("=\"" + attrValue + "\"")
						}
					}
				}
			}
		}
		tag += ">"

		if(fullFag == true || seedoFag == true){	// 動的タグを記録する
			stackChange.top.setStartTagFull(tag)
			if(end){ // スラでタグが閉じている場合
				if(!stackChange.isEmpty){// Tag inside class="seedo" 
					stackChange.top.getBufChange.append(tag)	// 動的タグのオリジナルを設定
					tag += exec		// タグは、動的タグ生成側でタグ付けする。
					stackChange.pop	// The stack for substitution is liberated. 
				}
				out.print(tag)
			}
		} else {
			if(stackChange.isEmpty){
				out.print(tag)
			} else {	// 動的タグにネストするタグは置換対象となるため、このタグをバッファに蓄える
				stackChange.top.getBufChange.append(tag)
			}
		}
	}
	// タグエンド
	override def endElement (uri:String ,  name:String, qualifiedName:String):Unit={
		val endTag = "</" + name.toLowerCase +">"
		if(stackChange.isEmpty) {	// Tag to be substituted inside
			out.print(endTag)
		} else {
			stackChange.top.getBufChange.append(endTag)
	  		if(name.equalsIgnoreCase(stackChange.top.getStartTag)) {
	  			var str:String = null
				if(!stackChange.isEmpty){// Tag inside class="seedo" 
					str = stackChange.top.getStartTagFull + exec + endTag
					stackChange.pop	// The stack for substitution is liberated. 
				}
				if(stackChange.isEmpty){
					out.print(str)
				} else {// class="seedo" 動的タグがネストしている
					stackChange.top.getBufChange.append(str)
				}
	  		}
		}
	}
	// タグ以外
	override def characters(ch:Array[Char],start:Int,length:Int) : Unit = {
		var str:String = ""
		for(i <- start to length+start-1){
			str += ch(i)
		}
		if(stackChange.isEmpty){
			out.print(str)
		} else {// inside tag class="seedo"
			stackChange.top.getBufChange.append(str)
		}
	}
	// 動的タグを実行する
	def exec : String = {
		// Depth of the same stack as tag that became substitution beginning -> tag end
		var changeTag:String = ""
		val execCommand = stackChange.top.getExecCommand
		if(stackChange.top.getSeedo){ // SEEDO attribute
		  return create(execCommand)
		}
		val classname = execCommand(1)	// classnamesをNULLで関数呼び出ししてはいけない
		var method = ""
		try{
			var ec:ExecClass = new ExecClass(classname)		// Object is generated. 
			if(ec != null){
				ec.exec("setTag",stackChange.top.getBufChange.toString)	// TAG before it substitutes it is set. 
				ec.exec("setParameterMap",pMap)	// GetParameterMap method of ServletRequest to acquire parameter
				ec.exec("setSession",session)	// セッション情報を引き渡す
				if(execCommand.length == 3){	// Argument none
					method = execCommand(2)
					val result = ec.exec(method)
					if(result != null)
						changeTag = result.toString
				} else if(execCommand.length == 4){	// There is one argument. 
					method = execCommand(2)
					val value = execCommand(3)
					val result = ec.execStr(method,value)
					if(result != null)
					  changeTag = result.toString
				}
			}
		} catch {
			case e:NoSuchMethodException => {
				println("Exception "+e.getMessage + " NoSuchMethodException! :" + classname + " " + method)
			}
			case e:Exception => {
				println("Exception "+e.getMessage + " class or Method not find! :" + classname + " " + method)
			}
		}
		return changeTag
	}
	// Seedo 動的サブレットクラス・オブジェクトを生成する
	def create(classnames:Array) : String = {
		val classname = classnames(0)	// classnamesをNULLで関数呼び出ししてはいけない
		var method = ""
		var changeTag:String = ""
		var ec:ExecClass = null
		ec = createClasses.get(classname)
		try{
			if(ec == null){	// はじめてクラス名が現れた
				ec = new ExecClass(classname)		// Object is generated.
				if(ec != null){
					createClasses.put(classname, ec)		// 生成したクラス・オブジェクトを登録する
					ec.exec("setParameterMap",pMap)	// GetParameterMap method of ServletRequest to acquire parameter
					ec.exec("setSession",session)	// セッション情報を引き渡す
				}
			}
			if(ec != null){
				if(classnames.length == 2){	// Argument none
					method = classnames(1) 
					val result = ec.exec(method)
					if(result != null)
						changeTag = result.toString
				} else if(classnames.length == 3){	// There is one argument. 
					method = classnames(1) 
					val value = classnames(2)
					val result = ec.execStr(method,value)
					if(result != null)
						changeTag = result.toString
				}
			} else {
				println("Exception :Not create object = " + classname + " " + method)
			}
		} catch {
			case e:NoSuchMethodException => {
				println("Exception "+e.getMessage + " NoSuchMethodException! :" + classname + " " + method)
			}
			case e:Exception => {
				println("Exception "+e.getMessage + " class or Method not find! :" + classname + " " + method)
			}
		}
		return changeTag
	}
}

2011年10月24日

htmlSrv class

Filed under: Framework,Scala — admin @ 10:03 PM

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
	}
}

2011年8月31日

Web フレームワークを作る!! HTML Parserの差し替え

Filed under: Framework,Scala — admin @ 9:39 PM

HTMLパーサの差し替え

org.htmlparser.saxのバージョンは2.0をタグ拡張したバージョンにするため
以下のハンドラコードにを切り替える。

class Htmlparse5Spは、class Htmlparse4Spのハンドラを
生成しているところを、

val cb = new ParserCallback4 から val cb = new ParserCallback5 に書き換えればよい。

これで、オリジナルのHTMLファイルのScriptタグのなかにあるJavascriptのコードがなくならない。

このコードについて、このページを掲載してからいろいろとテストしたところ、
タグの再現性がないことを確認しました。

再現しない理由は、

  • HTML parserが解析しないタグが他にもあること。
  • タグの開始と終了が完全でないとエラーとなる。これは、ソースコードにあるstackでタグ関係をチェックしているために対が崩れるとエラーとしていることです。この場合このコードは不要です。

対応は、
HTML parserのオリジナルのコードにさらに定義されていないタグのクラス定義を追加し、
stackのタグ対チェックを外すことです。

これ以外にも、処理ロッジを変更しているので、新しいコードが完成次第リリースしますので、お試しください。

以下のコードはさらに差し替えになります。

package ews.servlet.sp
import java.util.HashMap
import org.xml.sax._
import org.xml.sax.helpers.DefaultHandler
import scala.collection.JavaConversions._
import scala.collection.mutable.Stack
import seedo._

class ParserCallback5(pMap:HashMap[String,Array[String]]) extends DefaultHandler {
	var buf = new StringBuffer
	val stack = new Stack[String]			// For indent inspection of tag
	val stackChange = new Stack[ExecTag]	// For substitution
	var locator:Locator = null

	override def toString :String = buf.toString
	override def setDocumentLocator (locator:Locator) :Unit=  {	this.locator = locator }
	override def startDocument ():Unit= {	System.out.println("Start document");}
	override def endDocument():Unit= {	System.out.println("End document");}
	override def startElement ( uri:String,  name:String, qualifiedName:String , attrs:Attributes ):Unit={
		var buflocal = new StringBuffer
		stack.push(name)
		buflocal.append("<"+name)
		var fullFag = false	//
		for (idx <- 0 to attrs.getLength - 1){
			val attributeType = attrs.getType(idx)
			val attrLocalName = attrs.getLocalName(idx)
		  	val attrName = attrs.getQName(idx)
			val attrValue = attrs.getValue(idx)
			if(attrLocalName.length > 0 && !"#text".equals(attrLocalName)){
				buflocal.append(" " + attrLocalName + "=\"" + attrValue + "\"")
				if("class".equals(attrName) && attrValue.startsWith("seedo:")){
					val execCommand = attrValue.split(":")	// Each element is separated.
					if(execCommand.length >= 3) {	// It doesn't substitute it if there are neither a class name nor a function name.
						stackChange.push(new ExecTag(
						stack.length
						,name
						,execCommand
						,new StringBuffer	// The tag to be substituted is maintained.
						))
						fullFag = true
					} else {
						println("$$$$$$$$$$ execClass Warning " + attrName + "='" + attrValue + "'")
					}
				}
			}
		}
		buflocal.append(">")
		if(!stackChange.isEmpty){
			stackChange.top.getBufChange.append(buflocal)
		} else {
			buf.append(buflocal)
		}
		if(fullFag == true){
			stackChange.top.setStartTagFull(buflocal.toString)
		}
	}
	override def endElement (uri:String ,  name:String, qualifiedName:String):Unit={
		val endTag = "</"+name+">"
		if(stack.top.equals(name)){	// It is the same as the tag that became a substitution beginning.
			if(!stackChange.isEmpty) {	// Tag to be substituted inside
			  if(stackChange.top.getStartTagDepth == stack.length && name.equals(stackChange.top.getStartTag) && !stackChange.isEmpty) {
				// Depth of the same stack as tag that became substitution beginning -> tag end
				stackChange.top.getBufChange.append(endTag)
				var changeTag:String = ""
				try{
					val execCommand = stackChange.top.getExecCommand
					var ec:ExecClass = new ExecClass(execCommand(1))		// Object is generated.
					if(ec != null){
						ec.exec("setTag",stackChange.top.getBufChange.toString)	// TAG before it substitutes it is set.
						ec.exec("setParameterMap",pMap)	// GetParameterMap method of ServletRequest to acquire parameter
						if(execCommand.length == 3){	// Argument none
							changeTag = ec.exec(execCommand(2)).toString
						}else if(execCommand.length == 4){	// There is one argument.
							val value = execCommand(3)
							changeTag = ec.execStr(execCommand(2),execCommand(3)).toString
						}
					}
				} catch {
					case e:NoSuchMethodException => {
						println("Exception "+e.getMessage + " NoSuchMethodException!")
					}
					case e:Exception => {
						println("Exception "+e.getMessage + " class or Method not find!")
					}
				}
				var str:String = null
				if(!stackChange.isEmpty){// Tag inside class="seedo"
					str = stackChange.top.getStartTagFull + changeTag + endTag
				}
				stackChange.pop	// The stack for substitution is liberated.
				if(!stackChange.isEmpty){// class="seedo"
					stackChange.top.getBufChange.append(str)
				} else {
					buf.append(str)	//
				}
			  } else {
				stackChange.top.getBufChange.append(endTag)
			  }
			} else {
				buf.append(endTag)
			}
			stack.pop
	//		println("-------------stack.length="+stack.length)
		} else {
			println("$$$$$$$$$$ The correspondence of tag is not taken. startTag(stack.top)=" + stack.top + " endTag=" + qualifiedName)
		}
	}
	override def characters(ch:Array[Char],start:Int,length:Int) : Unit = {
		var str:String = ""
		for(i <- start to length+start-1){
			str += ch(i)
		}
		if(!stackChange.isEmpty){// inside tag class="seedo"
			stackChange.top.getBufChange.append(str)
		} else {
			buf.append(str)
		}
	}
}

package がすべてewsだったので、機能ごとに階層を分ける。
servletから呼び出されるクラスは、package ews.servlet.spに置く。

class htmlSrvは、package ews.servletに置く。

packageの変更により、web.xmlの定義は以下のように修正。

HTMLファイルを置く場所も、テスト用にC:\test_site\htmlに変更する。
この設定を変更すればいろいろなサイトのコンテンツを評価することができます。

またews.servlet.htmlSrvを拡張してファイル転送できるようにしたので、イメージファイルやCSSファイル、JSファイルもこのディレクトリに置くことができます。

web.xml

	<servlet>
		<servlet-name>html</servlet-name>
		<servlet-class>ews.servlet.htmlSrv</servlet-class>
		<init-param>
			<param-name>html</param-name>
			<param-value>C:\test_site\html</param-value>
		</init-param>

		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>html</servlet-name>
		<url-pattern>/html/*</url-pattern>
	</servlet-mapping>

コードが落ち着いてきたらソースコード一式をダウンロードできるようにします。

2011年8月29日

HTML Parser 評価 org.cyberneko.html.parsers

Filed under: Framework,Scala — admin @ 7:44 PM

HTMLパーサ評価用のソースコード

org.cyberneko.html.parsersのバージョンは1.9.15を使用。
http://nekohtml.sourceforge.net/index.html

  1. HTMLファイルの読み込みは、InputStreamReaderが使えるので、コードセットを指定することができる。
    文字化けの原因を回避。
  2. SAXのハンドラはorg.xml.sax.helpers.DefaultHandlerの標準仕様
  3. オリジナルにあるコメントタグの内容は出力できない。
  4. エラーハンドラがあるので、エラーはここから出力できる。
  5. タグの出力は<BR>タグを<BR></BR>と出力するので修正。
    HRタグも同様
    IMGタグとINPUTタグのエンドタグは削除。
  6. ネストしたタグの解析処理に不具合があるようで、タグが不完全と判断して余分なタグを出力するため画面が乱れてしまう。

評価用のソースコードは以下のとおり。

package ews
import java.io._
import javax.servlet.http._
import org.xml.sax.helpers._
import org.xml.sax.{InputSource,ErrorHandler,Attributes,SAXException,SAXParseException}
import org.cyberneko.html.parsers._

class Htmlparse3Sp (request : HttpServletRequest, response : HttpServletResponse, htmlPath : String) extends TemplSp {
	var parsed:String = null	//
	override def toString :String = parsed
	// cyberneko 1.9.15
	def html : String = {
		val htmlFile = new java.io.File(htmlPath)
		if(htmlFile.exists && htmlPath.toLowerCase.endsWith(".html")){
			var input:InputStreamReader = null
			try {
				input = new InputStreamReader (new FileInputStream(htmlFile), "UTF-8")	// open HTML file
				val inputSource = new InputSource(input)
				val cb = new ParserCallback3
				val r = new SAXParser
				r.setContentHandler(cb)
				r.setErrorHandler(new ErrorHandler3)
				r.parse(inputSource)
				parsed = cb.toString.replaceAll("<br></br>","<br>")
				.replaceAll("<hr></hr>","<hr>")
				.replaceAll("</input>","")
				.replaceAll("</img>","")
				return parsed
			} catch {
			  case e:IOException => {
				  println("Exception "+e.getMessage)
			  }
			} finally {
				input.close
			}
		}
		null	// There is no file.
	}
}
class ErrorHandler3 extends ErrorHandler {
		def warning(e:SAXParseException ) :Unit= {
			println("ERROR warning")
			throw new SAXException(e)
		}
		def error(e:SAXParseException) :Unit= {
			println("ERROR error")
			throw new SAXException(e)
		}
		def fatalError(e:SAXParseException) :Unit= {
			println("ERROR fatalError")
			throw new SAXException(e)
		}
		// default constructor
		def ErrorTestHandler :Unit= {
		}
}

ハンドラのソースコード

package ews

import org.xml.sax._
import org.xml.sax.helpers.DefaultHandler

class ParserCallback3 extends DefaultHandler {
	var buf = new StringBuffer
	override def toString :String = buf.toString
	override def startDocument ():Unit= {/* System.out.println("Start document")*/}
	override def endDocument():Unit= {/* System.out.println("End document")*/}
	override def startElement ( uri:String,  name:String, qName:String , attrs:Attributes ):Unit={
		var buflocal = new StringBuffer
		buflocal.append("<"+qName.toLowerCase)
		for (idx <- 0 to attrs.getLength - 1){
			val attrName = attrs.getQName(idx)
			val attrValue = attrs.getValue(idx)
			buflocal.append(" " + attrName + "=\"" + attrValue + "\"")
		}
		buflocal.append(">")
		buf.append(buflocal)
	}
	override def endElement (uri:String ,  name:String, qName:String):Unit={
		buf.append("</" + qName.toLowerCase + ">")
	}
	override def characters(ch:Array[Char],start:Int,length:Int) : Unit = {
		var str:String = ""
		for(i <- start to length+start-1){
			str += ch(i)
		}
		buf.append(str)
	}
	override def unparsedEntityDecl(name:String,publicId:String,systemId:String,notationName:String) : Unit = {
		println("unparsedEntityDecl name="+name + " publicId=" + publicId + " systemId="+systemId + " notationName=" + notationName)
	}
	override def startPrefixMapping(prefix:String , uri:String ) : Unit = {
		println("startPrefixMapping prefix="+prefix + " uri=" + uri)
	}
	override def endPrefixMapping(prefix:String) : Unit = {
		println("endPrefixMapping prefix="+prefix)
	}
	override def processingInstruction(target:String, data:String) : Unit = {
		println("processingInstruction target="+target + " data="+data)
	}
	override def skippedEntity(name:String) : Unit = {
		println("skippedEntity name="+name)
	}
}

HTML Parser 評価 javax.swing.text.html.parser

Filed under: Framework,Java,Scala — admin @ 6:59 PM

HTML Parser 評価

HTMLパーサの主な用途は、クローラを書くときに使ったり、
HTML文書から文書だけを取り出すことでしょう。
あるいは、特定のタグの属性を見て何かするような目的もあります。

しかし、Web frameworkで使う目的は、タグの置換ですから、
オリジナルのHTMLにあって、パースした出力にはタグがなくなっている
ということはあってはなりません。

ここでのHTMLパーサの評価は、
オリジナルのHTMLファイルを読み込んで、
HTMLパーサによってパースした結果をHTMLファイルに戻す。
オリジナルと同じであることが評価されます。

すなわちオリジナルと出力したHTMLファイルの差異が評価ポイントとなります。

評価ポイント

評価ポイントは次のとおり。

  1. タグが未対応
  2. タグの欠落
  3. タグ要素の欠落
  4. タグが閉じていないケースの対応
  5. コメントの扱い
  6. Scriptコードの扱い
  7. XHTML化
  8. HTML5への対応
  9. 文字化け

HTMLパーサ評価用のソースコード

まずは、javax.swing.text.html.parserから。

  1. HTMLファイルの読み込みは、InputStreamReaderが使えるので、コードセットを指定することができる。
    文字化けの原因を回避。
  2. SAXのハンドラは独自。
    コメント用のハンドラがあるのでHTML内のコメントを出力できる。
    コメントを出力できないハンドラが多いなかこれは評価できる。
  3. タグへのパーサからの評価がタグ属性で出力される。
    最初にテストコードを動かしたときにオリジナルにない属性が出力されていたので
    出力しないようにコードを変更する。
  4. タグの出力は比較的素直。<BR>タグを<BR></BR>と出力しない。
package ews

import java.io._
import javax.servlet.http._
import javax.swing.text._
import javax.swing.text.html._
import javax.swing.text.html.parser._
/**
 * HtmlParse
 *
 * javax.swing.text.html should not be used.
 * Because it is not thread safe.
 */
class Htmlparse2Sp (request : HttpServletRequest, response : HttpServletResponse, htmlPath : String) extends TemplSp {
	var parsed:String = null	//
	override def toString :String = parsed
	// javax.swing.text.html.parser
	def html : String = {
		val htmlFile = new java.io.File(htmlPath)
		if(htmlFile.exists && htmlPath.toLowerCase.endsWith(".html")){
			var input:InputStreamReader = null
			try {
				input = new InputStreamReader (new FileInputStream(htmlFile), "UTF-8")	// open HTML file
				val cb = new ParserCallback2
				val pd = new ParserDelegator
				pd.parse(input, cb, true)
				parsed = cb.toString
				return parsed
			} catch {
			  case e:IOException => {
				  println("Exception "+e.getMessage)
			  }
			} finally {
				input.close
			}
		}
		null	// There is no file.
	}
}

ハンドラのソースコード

package ews
import javax.swing.text._
import javax.swing.text.html._

class ParserCallback2 extends HTMLEditorKit.ParserCallback {
	var buf = new StringBuffer
	override def toString :String = buf.toString

	override def handleStartTag(tag:HTML.Tag, attrs:MutableAttributeSet , pos:Int):Unit={
		var ok = true
		var buflocal = new StringBuffer
		buflocal.append("<"+tag.toString)
		val attrNames = attrs.getAttributeNames
		while (attrNames.hasMoreElements) {
			val attrName = attrNames.nextElement
			val attrValue = attrs.getAttribute(attrName)
			if(HTMLEditorKit.ParserCallback.IMPLIED.equals(attrName)){	// error
			  ok = false
			  println("ParserCallback2 [IMPLIED] tag=" + tag.toString + " attrName="+attrName + " attrValue="+attrValue)
			} else
			if(HTML.NULL_ATTRIBUTE_VALUE.equals(attrName)){
			  ok = false
			  println("ParserCallback2 [NULL_ATTRIBUTE_VALUE] tag=" + tag.toString + " attrName="+attrName + " attrValue="+attrValue)
			} else
			if(HTML.Attribute.ENDTAG.equals(attrName)){
			  ok = false
			  println("ParserCallback2 [ENDTAG] tag=" + tag.toString + " attrName="+attrName + " attrValue="+attrValue)
			}
			buflocal.append(" " + attrName + "=\"" + attrValue + "\"")
			if (attrName == StyleConstants.NameAttribute && tag == HTML.Tag.CONTENT) {

			}
		}
		buflocal.append(">")
		if(ok)
			buf.append(buflocal)
	}
	override def handleEndTag(tag:HTML.Tag, pos:Int):Unit={
		buf.append("</"+tag.toString+">\n")
	}
	override def handleSimpleTag(tag:HTML.Tag, attrs:MutableAttributeSet , pos:Int):Unit={
		handleStartTag(tag,attrs,pos)
	}
	override def handleText(ch:Array[Char],length:Int):Unit={
		buf.append(new String(ch))
	}
	override def handleComment(ch:Array[Char],length:Int):Unit={
		val str = new String(ch)
		if(str.trim.length > 0)
			buf.append("<!--\n"+str+"\n-->\n")
	}
	override def handleEndOfLineString(eol:String):Unit={
		buf.append(new String(eol))
	}
	def handleError(ln:Int, errorMsg:String):Unit={
		println("ERROR line number="+ln + " " + errorMsg)
	}
}

Scala Web framework !! update HttpServlet code

Filed under: Framework,Scala — admin @ 1:52 AM

Web フレームワークを作る!!  1日目で作ったhtmlSrv クラスをアップデートします。

 

Webフレームワークに、実際のサイトで使われている静的なHTMLファイルを読み込ませてみるといろいろと課題が見えてきました。

  1. IMGタグのJPGやGIFのイメージファイルのリクエスト
  2. 同じくCSSやJSファイルのリクエスト
  3. HTMLパーサによるSCRIPTタグの扱い
  4. 同じくコメントの扱い

 

1と2については、各種ファイルへのリクエストは、URLパスのエーリアスで処理しようかと考えたのですが、URLのパスをいちいち変更するのも面倒なので、これらのファイルを直接転送できるようにContentTypeでTEXTでないものはすべて転送で処理するように変更。

3と4については、nu.validator.htmlparserをHTMLパーサに採用しましたが、SCRIPTタグの中身をうまく返せない。こうなるとJavascriptが動かない。

 

HTMLパーサは、いろいろなソフトが使えるのでHTMLパーサを評価する。
評価してHTMLパーサは以下のとおり。

  1. nu.validator.htmlparser
  2. javax.swing.text.html.parser._
  3. org.cyberneko.html.parsers._
  4. org.htmlparser.sax._

 

細かい仕様がまったく違うので、評価コードを書くのは大変でした。
その1つが、inputSourceの引数がまったく異なる。

しかし評価結果は、この4つは共にWebフレームに必要な仕様を満たさない。

    1. nu.validator.htmlparserは先に述べたとおり。
      何か出力を切り替えるスイッチがあるのではないかと思うのだがわからず。
      ソースコード読めばいいだろうけど。

 

    1. javax.swing.text.html.parser._はJAVADOCを読むとSwingライブラリはスレッドセーフではないとある。
      HTMLパーサとしては問題ないんですけど残念。

 

    1. org.cyberneko.html.parsers._は、DLタグのネストをうまく処理できない不具合がある。
      最初、SAXParserをNEWするコードを書くと、コンストラクタがないとコンパイルエラー。
      ネットで調べると2008年にScalaでDOMParserを使おうとして同じエラーに遭遇している人を見つけるが、対応コメントがない。
      ソースを見るがSAXParserには確かにコンストラクタがある。

      よくよくソースコードを見ると、
      import org.apache.xerces.parsers.AbstractSAXParser;とあるではないですか。
      ということは、xercesImpl.jarをLibに追加すれば問題解決。

 

  1. org.htmlparser.sax._は、処理できないタグがある。当然HTML5のタグも処理できない。

    2006年で更新がストップしているので仕方ないことなんでしょう。ソースコードを読むと、タグごとにクラスが定義されている。
    ということは、対応していないタグのクラスを定義すれば。。。。

    SPANクラスを参考にして、未対応のタグとついでにHTML5で追加されたタグのクラスを定義。
    このクラスをNewしているPrototypicalNodeFactoryクラスにタグを追加するとOK。

この改良版org.htmlparser.sax._を使うことで、課題となっていた件を解決することができた。

改良版htmlSrvクラスのソースコード は以下のとおり。

いろいろなHTMLパーサを使って仕様を評価するために、

request.getParameter(“parsersw”)により、以下のURLのように
してHTMLパーサを切り替えることができる。

http://127.0.0.1:8080/toro/html/form/index.html?parsersw=7

package ews
import org.apache.log4j.Logger
import java.io._
import javax.servlet._
import javax.servlet.http._
/**
 * The dynamic page is made by HttpServlet.
 */
class htmlSrv extends HttpServlet {
	lazy val logger = Logger.getLogger(classOf[ewsSrv])
	var context : ServletContext = null
	var htmlPath : String = null
	var message = "Not Found :The requested URL was not found on this server."	// Not Found
	override def init(config : ServletConfig) : Unit = {
		super.init(config)
		context = config.getServletContext
		htmlPath = config.getInitParameter("html")	// WEB-INF/web.xml
	}
	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")
//		println("htmlSrv ################# start")
		var htmlFile = ""
		var parsersw = if(request.getParameter("parsersw") !=null){request.getParameter("parsersw").toInt}else{9}	// change HTML parser
		request.setCharacterEncoding("utf-8")
   		val url = request.getRequestURL
		val uri = request.getRequestURI
		var spth:String = request.getServletPath

		spth = spth.replace("/","")
		val ur = uri.split("/")
		var fg = false
		ur.foreach({v =>
			if(fg){
				htmlFile += "/" + v
			}
			if(v.equals(spth))
				fg = true
		})
		var ct = ContentTypeDef.changeFromFileNameToContentType(htmlFile)
		val sw = ContentTypeDef.getCtype(ct) match {
		  case "text" => if(ct.equals("text/html")){parsersw}else{1}
		  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)
		if(sw != 10){
			val ssp = sw match {
			  // It reads a file, and the content is output as it is.
			  case 1 => new StraightSp(request, response, htmlPath + htmlFile)
			  // 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, htmlPath + htmlFile)
			  // The analytical result of HTML parser is converted in the character string and it outputs it.
			  case 3 => new Htmlparse2Sp(request, response, htmlPath + htmlFile)// javax.swing.text.html.parser._
			  case 4 => new HtmlparseSp(request, response, htmlPath + htmlFile)	// nu.validator.htmlparser._ for TEST
			  case 5 => new ValidatorHtmlparserSp(request, response, htmlPath + htmlFile)	// nu.validator.htmlparser for TEST
			  case 6 => new Htmlparse3Sp(request, response, htmlPath + htmlFile)// org.cyberneko.html.parsers._ for TEST
			  case 7 => new Htmlparse4Sp(request, response, htmlPath + htmlFile)// org.htmlparser.sax._ for TEST
			  case 8 => new Ssp(request, response, htmlPath + htmlFile)			// Dynamic page. use the nu.validator.htmlparser
			  case 9 => new Htmlparse5Sp(request, response, htmlPath + htmlFile)// Dynamic page. use the org.htmlparser.sax._
			}
	   		val re = if(ssp.html != null){
	   				response.setStatus(200)
	   				ssp.toString
	   		  } else {
	   			  	response.setStatus(404,"Not Found")
	   			  	message
	   		  }
	   		ct += "; charset=utf-8"
			response.setContentType(ct)
			val out = response.getWriter
			out.println(re)	// output html data
			out.flush
			out.close
		} else {
			var out = response.getOutputStream
			var stream:FileInputStream = null
			try {
				val binaryFile = new java.io.File(htmlPath + htmlFile)
				if(binaryFile.exists){
					response.setContentType(ct)
					response.setStatus(200)
					stream = new FileInputStream(binaryFile)
					val buf:Array[Byte] = new Array(1024)
					while(stream.available != 0){
						val i = stream.read(buf,0,1024)
						out.write(buf,0,i)
					}
				} else {
					error(404,"Not Found",response)
					val ms = message.getBytes("UTF-8")
		  	   		out.write(ms,0,ms.length)
				}
			} catch {
	        	case e:IOException => {
	        		println("IOException "+e.getMessage)
	        		error(500,"Internal Server Error",response)
	        	}
	        	case e:Exception => {
	        		println("Exception "+e.getMessage)
	        		error(500,"Internal Server Error",response)
	        	}
			} finally {
				out.flush
				out.close
				stream.close
			}
		}
//		logger.debug(" #### End")
	}
	// for error
	def error(status:Int,message:String, response : HttpServletResponse) :Unit = {
		response.setContentType("text/html")
		response.setStatus(status,message)
	}
}

HTMLパーサの評価コードについては引き続き解説します。
お楽しみに。

ContentType

ファイルの拡張子からContentTypeを得るプログラムです。
TOMCATの中で持っているリストと同じ。

このリストも、ネットで調べても出てこない。
参考になれば。

気になるのが、JSファイルの扱い。

applicationかtextかということなんでしょうが、
application/javascriptが正解という見解もあるようですが、
TOMCATではtext/javascriptになってます。

 

package ews

/**
 * ContentType
 */
object ContentTypeDef {
	val ct = List(
	    ("html","text/html"),
	    ("json","application/json"),
        ("abs", "audio/x-mpeg"),
        ("ai", "application/postscript"),
        ("aif", "audio/x-aiff"),
        ("aifc", "audio/x-aiff"),
        ("aiff", "audio/x-aiff"),
        ("aim", "application/x-aim"),
        ("art", "image/x-jg"),
        ("asf", "video/x-ms-asf"),
        ("asx", "video/x-ms-asf"),
        ("au", "audio/basic"),
        ("avi", "video/x-msvideo"),
        ("avx", "video/x-rad-screenplay"),
        ("bcpio", "application/x-bcpio"),
        ("bin", "application/octet-stream"),
        ("bmp", "image/bmp"),
        ("body", "text/html"),
        ("cdf", "application/x-cdf"),
        ("cer", "application/x-x509-ca-cert"),
        ("class", "application/java"),
        ("cpio", "application/x-cpio"),
        ("csh", "application/x-csh"),
        ("css", "text/css"),
        ("dib", "image/bmp"),
        ("doc", "application/msword"),
        ("dtd", "application/xml-dtd"),
        ("dv", "video/x-dv"),
        ("dvi", "application/x-dvi"),
        ("eps", "application/postscript"),
        ("etx", "text/x-setext"),
        ("exe", "application/octet-stream"),
        ("gif", "image/gif"),
        ("gtar", "application/x-gtar"),
        ("gz", "application/x-gzip"),
        ("hdf", "application/x-hdf"),
        ("hqx", "application/mac-binhex40"),
        ("htc", "text/x-component"),
        ("htm", "text/html"),
//        ("html", "text/html"),
        ("hqx", "application/mac-binhex40"),
        ("ief", "image/ief"),
        ("jad", "text/vnd.sun.j2me.app-descriptor"),
        ("jar", "application/java-archive"),
        ("java", "text/plain"),
        ("jnlp", "application/x-java-jnlp-file"),
        ("jpe", "image/jpeg"),
        ("jpeg", "image/jpeg"),
        ("jpg", "image/jpeg"),
        ("js", "text/javascript"),
        ("jsf", "text/plain"),
        ("jspf", "text/plain"),
        ("kar", "audio/x-midi"),
        ("latex", "application/x-latex"),
        ("m3u", "audio/x-mpegurl"),
        ("mac", "image/x-macpaint"),
        ("man", "application/x-troff-man"),
        ("mathml", "application/mathml+xml"),
        ("me", "application/x-troff-me"),
        ("mid", "audio/x-midi"),
        ("midi", "audio/x-midi"),
        ("mif", "application/x-mif"),
        ("mov", "video/quicktime"),
        ("movie", "video/x-sgi-movie"),
        ("mp1", "audio/x-mpeg"),
        ("mp2", "audio/x-mpeg"),
        ("mp3", "audio/x-mpeg"),
        ("mp4", "video/mp4"),
        ("mpa", "audio/x-mpeg"),
        ("mpe", "video/mpeg"),
        ("mpeg", "video/mpeg"),
        ("mpega", "audio/x-mpeg"),
        ("mpg", "video/mpeg"),
        ("mpv2", "video/mpeg2"),
        ("ms", "application/x-wais-source"),
        ("nc", "application/x-netcdf"),
        ("oda", "application/oda"),
        ("odb", "application/vnd.oasis.opendocument.database"),
        ("odc", "application/vnd.oasis.opendocument.chart"),
        ("odf", "application/vnd.oasis.opendocument.formula"),
        ("odg", "application/vnd.oasis.opendocument.graphics"),
        ("odi", "application/vnd.oasis.opendocument.image"),
        ("odm", "application/vnd.oasis.opendocument.text-master"),
        ("odp", "application/vnd.oasis.opendocument.presentation"),
        ("ods", "application/vnd.oasis.opendocument.spreadsheet"),
        ("odt", "application/vnd.oasis.opendocument.text"),
        ("otg", "application/vnd.oasis.opendocument.graphics-template"),
        ("oth", "application/vnd.oasis.opendocument.text-web"),
        ("otp", "application/vnd.oasis.opendocument.presentation-template"),
        ("ots", "application/vnd.oasis.opendocument.spreadsheet-template "),
        ("ott", "application/vnd.oasis.opendocument.text-template"),
        ("ogx", "application/ogg"),
        ("ogv", "video/ogg"),
        ("oga", "audio/ogg"),
        ("ogg", "audio/ogg"),
        ("spx", "audio/ogg"),
        ("faca", "audio/flac"),
        ("anx", "application/annodex"),
        ("axa", "audio/annodex"),
        ("axv", "video/annodex"),
        ("xspf", "application/xspf+xml"),
        ("pbm", "image/x-portable-bitmap"),
        ("pct", "image/pict"),
        ("pdf", "application/pdf"),
        ("pgm", "image/x-portable-graymap"),
        ("pic", "image/pict"),
        ("pict", "image/pict"),
        ("pls", "audio/x-scpls"),
        ("png", "image/png"),
        ("pnm", "image/x-portable-anymap"),
        ("pnt", "image/x-macpaint"),
        ("ppm", "image/x-portable-pixmap"),
        ("ppt", "application/vnd.ms-powerpoint"),
        ("pps", "application/vnd.ms-powerpoint"),
        ("ps", "application/postscript"),
        ("psd", "image/x-photoshop"),
        ("qt", "video/quicktime"),
        ("qti", "image/x-quicktime"),
        ("qtif", "image/x-quicktime"),
        ("ras", "image/x-cmu-raster"),
        ("rdf", "application/rdf+xml"),
        ("rgb", "image/x-rgb"),
        ("rm", "application/vnd.rn-realmedia"),
        ("roff", "application/x-troff"),
        ("rtf", "application/rtf"),
        ("rtx", "text/richtext"),
        ("sh", "application/x-sh"),
        ("shar", "application/x-shar"),
        /*"shtml", "text/x-server-parsed-html",*/
        ("smf", "audio/x-midi"),
        ("sit", "application/x-stuffit"),
        ("snd", "audio/basic"),
        ("src", "application/x-wais-source"),
        ("sv4cpio", "application/x-sv4cpio"),
        ("sv4crc", "application/x-sv4crc"),
        ("svg", "image/svg+xml"),
        ("svgz", "image/svg+xml"),
        ("swf", "application/x-shockwave-flash"),
        ("t", "application/x-troff"),
        ("tar", "application/x-tar"),
        ("tcl", "application/x-tcl"),
        ("tex", "application/x-tex"),
        ("texi", "application/x-texinfo"),
        ("texinfo", "application/x-texinfo"),
        ("tif", "image/tiff"),
        ("tiff", "image/tiff"),
        ("tr", "application/x-troff"),
        ("tsv", "text/tab-separated-values"),
        ("txt", "text/plain"),
        ("ulw", "audio/basic"),
        ("ustar", "application/x-ustar"),
        ("vxml", "application/voicexml+xml"),
        ("xbm", "image/x-xbitmap"),
        ("xht", "application/xhtml+xml"),
        ("xhtml", "application/xhtml+xml"),
        ("xls", "application/vnd.ms-excel"),
        ("xml", "application/xml"),
        ("xpm", "image/x-xpixmap"),
        ("xsl", "application/xml"),
        ("xslt", "application/xslt+xml"),
        ("xul", "application/vnd.mozilla.xul+xml"),
        ("xwd", "image/x-xwindowdump"),
        ("vsd", "application/x-visio"),
        ("wav", "audio/x-wav"),
        ("wbmp", "image/vnd.wap.wbmp"),
        ("wml", "text/vnd.wap.wml"),
        ("wmlc", "application/vnd.wap.wmlc"),
        ("wmls", "text/vnd.wap.wmlscript"),
        ("wmlscriptc", "application/vnd.wap.wmlscriptc"),
        ("wmv", "video/x-ms-wmv"),
        ("wrl", "x-world/x-vrml"),
        ("wspolicy", "application/wspolicy+xml"),
        ("Z", "application/x-compress"),
        ("z", "application/x-compress"),
        ("zip", "application/zip")
    )
    def getCtype(ct:String) : String = {
	  if(ct == null)
	    return null
	  val t = ct.split("/")
	  return t(0)
	}
	def getSuffix(fileName:String):String= {
	    if (fileName == null)
	        return null
	    val point = fileName.lastIndexOf(".")
	    if (point != -1) {
	        return fileName.substring(point + 1).toLowerCase
	    }
	    return fileName.toLowerCase
	}  

	def changeFromFileNameToContentType(fname:String) : String = {
	  val sf = getSuffix(fname)
	  val rt = ct.find(_._1.equals(sf)) match {
	    case Some(f) => f._2
	    case None => null
	  }
	  return rt
	}
}

2011年8月25日

Scala Web framework !! DB select <- ParameterMap

Filed under: Framework,Scala — admin @ 8:08 PM

URLの引数によって値を取るケース

以下のURLのようにdeptno=30の値を HelloWorldDb3 クラスへ渡すコードです。

http://127.0.0.1:8080/toro/html/helloworlddb3.html?sw=4&deptno=30

parameterMap変数は、JavaのコンテナクラスのHashMap[String,Array[String]]です。

parameterMap.get(“deptno”) 関数によって、deptnoの引数の値を取り出します。
ScalaのMAPクラスではないので、case ではSome(f)ではなく、クラスマッチングで値を取り出し、
Arrayクラスなので配列の0番目の値を取り出します。
その値を toInt関数でStringから整数Intに変換します。

これをSELECT文の条件として使い、データベースに検索します。

import scala.collection.immutable._
import scala.xml._
import seedo.database._

class HelloWorldDb3 {
	var tag:String = null // Replaced original tag
	def setTag(tg:String): Unit = {tag = tg}
	var parameterMap:HashMap[String,Array[String]] = null // Parameter from request of servlet
	def setParameterMap(pm:HashMap[String,Array[String]]) : Unit =  {parameterMap = pm}

	def recx(record:Array[Any]) :Node =  {
			var nodes  = Queue.empty[Node]
			record.foreach{(fx) => nodes += <TD>{fx}</TD>}
			return <TR>{nodes}</TR>
		}
	// Function one argument
	def tb(sal:Int) : String = {
		val deptno = parameterMap.get("deptno") match {
		  case v:Array[String] => v(0).toInt
		  case null => 10
		}
		var sql:Db = null
		try {
			sql = new Db
			sql.select("EMPNO,ENAME,SAL,DEPTNO,JOB,MGR")
				.from("EMP")
				.where("SAL > ?",sal)
				.or("DEPTNO = ?",deptno)
				.orderBy("EMPNO")
			val re = sql.executeQuery
			var nodes  = Queue.empty[Node]
			re.foreach{(rec) => nodes += recx(rec)}
			val xml = <table border="2">{nodes}</table>
			return "HelloWorldDb3 " + new Date + xml.toString
		} catch {
		  case e:Exception =>{
			throw e
		  }
		} finally {
			if(sql != null)sql.close
		}
	}
}

結果

検索して結果は以下のとおり

deptno引数がなしの場合

http://127.0.0.1:8080/toro/html/helloworlddb3.html?sw=4

HelloWorldDb3 クラスのdeptno値は、引数がない場合は10になります。

deptno引数が30の場合

http://127.0.0.1:8080/toro/html/helloworlddb3.html?sw=4&deptno=30

SELECT文の条件にEMP表のDEPTカラムの値が30のレコードもOR条件で加わるので
レコード件数の数が増えています。

deptno引数が20の場合

http://127.0.0.1:8080/toro/html/helloworlddb3.html?sw=4&deptno=20

deptnoの引数の値によりSELECTされた結果が異なっていることがわかります。

helloworlddb3.htmlファイルの中身は、

<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Example6</h2>
The current time is
<div class="seedo:ews.HelloWorldDb3:tb:2500">emp</div>.
</BODY>
</HTML>

class属性の値が、
class=”seedo:ews.HelloWorldDb3:tb:2500″となり、
ews.HelloWorldDb3クラスを使用しています。

Scala Web framework !! DB select

Filed under: Framework,Scala — admin @ 2:38 AM

HelloWorldDb2 クラスに引数を渡して、引数の値をSELECT文の条件にします。

引数の渡し方には2通りあります。
最初の方式は、HTMLファイルのDIVタグのクラス属性に整数値の2500を記述します。

class=”seedo:ews.HelloWorldDb2:tb:2500″

これを、where(“SAL > ?”,sal)としてwhere関数の可変長引数として渡します。
セキュリティ上、SQLインジェクションされないように上記のように記述します。

このようにHTMLファイルの中の固定値で引数が書けます。

JDBCアクセスなので、try と catch で例外処理を捉えます。
finally でsql.close 関数を呼び出すことで、SQLコネクションをコネクションプールに返します。
close関数がないと、コネクションプールを消費し続け、最後にMAX値まで達するとSQLエラーの例外が発生します。

scalaだと、明示的にclose関数を記述しないでも内部でclose関数を呼ぶコードも書けます。
トライしてみてください。

package ews
import java.util.{HashMap,Date}
import scala.collection.JavaConversions._
import scala.collection.immutable._
import scala.xml._
import seedo.database._

class HelloWorldDb2 {
  var tag:String = null // Replaced original tag
  def setTag(tg:String): Unit = {tag = tg}
  var parameterMap:HashMap[String,Array[String]] = null // Parameter from request of servlet
  def setParameterMap(pm:HashMap[String,Array[String]]) : Unit =  {parameterMap = pm}

  def recx(record:Array[Any]) :Node =  {
            var nodes  = Queue.empty[Node]
            record.foreach{(fx) => nodes += <TD>{fx}</TD>}
            return <TR>{nodes}</TR>
        }
  // Function one argument
  def tb(sal:Int) : String = {
    val deptno = 30
    var sql:Db = null
    try {
		sql = new Db
		sql.select("EMPNO,ENAME,SAL,DEPTNO,JOB,MGR")
			.from("EMP")
			.where("SAL > ?",sal)
			.or("DEPTNO = ?",deptno)
			.orderBy("EMPNO")
		val re = sql.executeQuery
		var nodes  = Queue.empty[Node]
		re.foreach{(rec) => nodes += recx(rec)}
		val xml = <table border="1">{nodes}</table>
	    return "HelloWorldDb2 " + new Date + xml.toString
	} catch {
      case e:Exception =>{
        throw e
      }
    } finally {
    	if(sql != null)sql.close
    }
  }
}

HTLMファイルの中身です。

<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Example5</h2>
The current time is
<div class="seedo:ews.HelloWorldDb2:tb:2500">emp</div>.
</BODY>
</HTML>

結果

結果は以下のとおり

Scala Web framework !! DB test

Filed under: Framework,Scala — admin @ 1:20 AM

MySQL
Web framework を使ってMySQLにアクセスします。
使用するのはseedoフレームワークとの組み合わせ。
seedoフレームワークは、seedo1.2.2.jarを WEB-INF/libディレクトリのなかに入れる。

WEB-INF/classesディレクトリのなかにenv.properties ファイルを入れる。

DB接続の定義は、MySQLのJDBCドライバを使う。
connection-jndi を off にすることで、seedoフレームワーク独自のコネクションプールを使う。
TOMCATのコネクションプールを使う場合は、connection-jndiはon にして、
jndi-name = jdbc/mysql にする。
TOMCAT/conf/context.xml ファイルにDB接続定義を記述してもよい。

env.properties ファイル

driver = com.mysql.jdbc.Driver
#driver = oracle.jdbc.driver.OracleDriver

dsn = jdbc:mysql://127.0.0.1:3306/seedo?useUnicode=true&characterEncoding=UTF-8
#dsn = jdbc:oracle:thin:@127.0.0.1:1521:ORCL
user = scott
password = tiger

connection-jndi = off
jndi-name = jdbc/mysql
#jndi-name = jdbc/myoracle

EMP表に対してSELECT文を発行します。ENAMEフィールドでソートします。

MySQLにテスト用のDBを作る手順は、過去のBLOGにあるので参考に。

HelloWorldDb1.scala のソースコード

package ews
import java.util.{HashMap,Date}
import scala.collection.JavaConversions._
import scala.collection.immutable._
import scala.xml._
import seedo.database._

class HelloWorldDb1 {
  var tag:String = null // Replaced original tag
  def setTag(tg:String): Unit = {tag = tg}
  var parameterMap:HashMap[String,Array[String]] = null // Parameter from request of servlet
  def setParameterMap(pm:HashMap[String,Array[String]]) : Unit =  {parameterMap = pm}
  // One record -> TD tag
  def recx(record:Array[Any]) :Node =  {
            var nodes  = Queue.empty[Node]
            record.foreach{(fx) => nodes += <TD>{fx}</TD>}
            return <TR>{nodes}</TR>
        }
  // Function without argument
  def tb : String = {
	val sql = new Db
	val re = sql.executeQuery("select * from emp order by ename")
	sql.close
	var nodes  = Queue.empty[Node]
	re.foreach{(rec) => nodes += recx(rec)}
	val xml = <table>{nodes}</table>
    return "HelloWorldDb1 " + new Date + xml.toString
  }
}

HTMLファイルは、helloworlddb1.html

今回は、DIVタグ内の2つのSPANタグの中身は丸ごと、SELECT文の検索結果に置換されます。

<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Example4</h2>
The current time is
<div class="seedo:ews.HelloWorldDb1:tb">
	<div>The current time is <span class="seedo:ews.HelloWorld3:tb:999999999">now</span>.</div>
	<div>The current time is <span class="seedo:ews.HelloWorld3:tb:こんばんわ">now</span>.</div>
</div>.
</BODY>
</HTML>

結果

URLは、
http://127.0.0.1:8080/toro/html/helloworlddb1.html?sw=4

結果は見ての通り。

2011年8月23日

Web フレームワークを作る!! test case#2

Filed under: Framework,Scala — admin @ 2:07 AM

ExecClassクラスの改修

改修項目

  • String型からDate型への変換を追加
  • InvocationTargetExceptionの例外処理
package ews
import java.lang.reflect.{Field,InvocationTargetException,Method,Type}
import java.math.BigDecimal
import java.util.{Calendar,Date}
import java.text.SimpleDateFormat
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 Bean
	/**
	 * 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 => {}
	  }
	  val DATE_PATTERN = "yyyy/MM/dd"
      val sdf = new SimpleDateFormat(DATE_PATTERN)
      try {
    	val v = sdf.parse(value)
		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])
	   		try {
	   			return method.invoke(Obj, v)
	   		} catch {
	   			case e:InvocationTargetException => {
	   				throw new Exception(e.getCause)
	   			}
	   		}
	    } 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])
	   		try {
	   			return method.invoke(Obj, v)
	   		} catch {
	   			case e:InvocationTargetException => {
	   				throw new Exception(e.getCause)
	   			}
	   		}
	    } 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])
	   		try {
	   			return method.invoke(Obj, v)
	   		} catch {
	   			case e:InvocationTargetException => {
	   				throw new Exception(e.getCause)
	   			}
	   		}
	    } 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])
	   		try {
	   			return method.invoke(Obj, v)
	   		} catch {
	   			case e:InvocationTargetException => {
	   				throw new Exception(e.getCause)
	   			}
	   		}
	    } 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])
	   		try {
	   			return method.invoke(Obj, v)
	   		} catch {
	   			case e:InvocationTargetException => {
	   				throw new Exception(e.getCause)
	   			}
	   		}
	    } else {	// other class
	    	var valClass:Class[_] = value.asInstanceOf[AnyRef].getClass
	    	val method = clazz.getMethod(methodName, valClass)
	   		try {
	   			return method.invoke(Obj, value.asInstanceOf[AnyRef])
	   		} catch {
	   			case e:InvocationTargetException => {
	   				throw new Exception(e.getCause)
	   			}
	   		}
	    }
	}
	/**
	 * 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)
			}
		}
	}
}

改修したExecClassクラスをテストするためのHelloWorld3 クラス。

  • 引数なしtb関数に置換元を格納するtag変数からspanタグを取り出し、HelloWorld文字列に追加
  • 整数を引数に持つtb関数にパラメータの値を、HelloWorld文字列に追加
  • Date型を引数に持つtb関数を追加
package ews
import java.util.{HashMap,Date}
import scala.collection.JavaConversions._
import scala.xml._
/**
 *  A class that's instantiated early and run.  It allows the application
 * to modify Seedo's environment
 */
class HelloWorld3 {
  var tag:String = null // Replaced original tag
  def setTag(tg:String): Unit = {tag = tg}
  var parameterMap:HashMap[String,Array[String]] = null // Parameter from request of servlet
  def setParameterMap(pm:HashMap[String,Array[String]]) : Unit =  {parameterMap = pm}
  // Function without argument
  def tb : String = {
    var xml : Node = null
    var str : String = ""
    if(tag != null){
      xml = XML.loadString(tag)
      val l = xml \\ "span"
      l.foreach(v=> str += "" + v)
    }
    "HelloWorld " + new Date + str
  }
  def tb(no:Int) : String = {
    var str : String = " parameterMap: "
    if(parameterMap!=null){
    	println(parameterMap.size)
       parameterMap.foreach(v => {
         str += v._1 + "="
         v._2.foreach(w =>
           str += w + ","
         )
       })
    }
    "HelloWorld Int [" + no + "] " + new Date + str
  }
  def tb(no:Double) : String = {
    "HelloWorld Double [" + no + "] " + new Date
  }
  def tb(no:Date) : String = {
    "HelloWorld " + no.getClass + " [" + no + "] " + new Date
  }
  def tb(no:String) : String = {
    "HelloWorld " + no.getClass + " [" + no + "] " + new Date
  }
}

HTMLファイルは、helloworld3.html

<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Example3</h2>
The current time is
<div class="seedo:ews.HelloWorld3:tb">
	<div>The current time is <span class="seedo:ews.HelloWorld3:tb:999999999">now</span>.</div>
	<div>The current time is <span class="seedo:ews.HelloWorld3:tb:こんばんわ">now</span>.</div>
</div>.
</BODY>
</HTML>

結果

2011年8月22日

Web フレームワークを作る!! test case

Filed under: Framework,Scala — admin @ 12:41 AM

WebフレームワークのhtmlSrvクラスを少々改造

パラメータでどのパーサを使うかを選択できるようにします。

val sw = request.getParameter(“sw”)によってURLのパラメータを読み取ります。

 

	def gen(request : HttpServletRequest, response : HttpServletResponse) : Unit = {
		logger.debug("htmlSrv ################# start")
		println("htmlSrv ################# start")
		var htmlFile = ""
		request.setCharacterEncoding("utf-8")
		val sw = request.getParameter("sw")
   		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
		})
		logger.debug("uri="+uri+" htmlFile="+htmlFile)
		response.setContentType("text/html; charset=utf-8")
		var out = response.getWriter

		val ssp = sw match {
		  case null => new StraightSp(request, response, htmlPath + htmlFile)	// ファイルを読み込み、そのまま出力
		  case "1" => new XmlSp(request, response, htmlPath + htmlFile)			// HTMLファイルをXMLローダで読み込み、文字列変換して出力
		  case "2" => new Htmlparse2Sp(request, response, htmlPath + htmlFile)	// HTMLパーサーの解析結果を、文字列変換して出力
		  case "3" => new HtmlparseSp(request, response, htmlPath + htmlFile)	// HTMLパーサーの解析結果を、文字列変換して出力
		  case "4" => new Ssp(request, response, htmlPath + htmlFile)			// SEEDOによるクラス生成し、その出力結果を置換して出力
		  case _ => null
		}

   		val re = if(ssp.html != null){ssp.toString}else{message}
		out.println(re)	// output html data
		out.flush
		out.close
		logger.debug(" #### End")
		println(" #### End")
	}

 

テスト用のHTMLファイルにはtb関数の引数付きを追加します。
引数は、整数、実数、アスキー文字列、日本語文字列です。

<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Example2</h2>
<div>The current time is <span class="seedo:ews.HelloWorld:tb">now</span>.</div>
<div>The current time is <span class="seedo:ews.HelloWorld:tb:123">now</span>.</div>
<div>The current time is <span class="seedo:ews.HelloWorld:tb:999999999">now</span>.</div>
<div>The current time is <span class="seedo:ews.HelloWorld:tb:8888.8888">now</span>.</div>
<div>The current time is <span class="seedo:ews.HelloWorld:tb:Abcdefgh">now</span>.</div>
<div>The current time is <span class="seedo:ews.HelloWorld:tb:こんばんわ">now</span>.</div>
</BODY>
</HTML>

結果

URLは、以下のようになります。
TOMCATのプロジェクトはtoroです。

http://127.0.0.1:8080/toro/html/helloworld2.html?sw=4

tb関数が引数の型により呼び出されていることが確認できます。

Older Posts »

Powered by WordPress