Scala+DBMS+Web スカラ座の夜

2011年8月29日

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

Comments

comments

Powered by Facebook Comments

コメントはまだありません »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

コメントを投稿するにはログインしてください。

Powered by WordPress