Web フレームワークを作る!! 1日目で作ったhtmlSrv クラスをアップデートします。
Webフレームワークに、実際のサイトで使われている静的なHTMLファイルを読み込ませてみるといろいろと課題が見えてきました。
- IMGタグのJPGやGIFのイメージファイルのリクエスト
- 同じくCSSやJSファイルのリクエスト
- HTMLパーサによるSCRIPTタグの扱い
- 同じくコメントの扱い
1と2については、各種ファイルへのリクエストは、URLパスのエーリアスで処理しようかと考えたのですが、URLのパスをいちいち変更するのも面倒なので、これらのファイルを直接転送できるようにContentTypeでTEXTでないものはすべて転送で処理するように変更。
3と4については、nu.validator.htmlparserをHTMLパーサに採用しましたが、SCRIPTタグの中身をうまく返せない。こうなるとJavascriptが動かない。
HTMLパーサは、いろいろなソフトが使えるのでHTMLパーサを評価する。
評価してHTMLパーサは以下のとおり。
- nu.validator.htmlparser
- javax.swing.text.html.parser._
- org.cyberneko.html.parsers._
- org.htmlparser.sax._
細かい仕様がまったく違うので、評価コードを書くのは大変でした。
その1つが、inputSourceの引数がまったく異なる。
しかし評価結果は、この4つは共にWebフレームに必要な仕様を満たさない。
- nu.validator.htmlparserは先に述べたとおり。
何か出力を切り替えるスイッチがあるのではないかと思うのだがわからず。
ソースコード読めばいいだろうけど。
- javax.swing.text.html.parser._はJAVADOCを読むとSwingライブラリはスレッドセーフではないとある。
HTMLパーサとしては問題ないんですけど残念。
- org.cyberneko.html.parsers._は、DLタグのネストをうまく処理できない不具合がある。
最初、SAXParserをNEWするコードを書くと、コンストラクタがないとコンパイルエラー。
ネットで調べると2008年にScalaでDOMParserを使おうとして同じエラーに遭遇している人を見つけるが、対応コメントがない。
ソースを見るがSAXParserには確かにコンストラクタがある。
よくよくソースコードを見ると、
import org.apache.xerces.parsers.AbstractSAXParser;とあるではないですか。
ということは、xercesImpl.jarをLibに追加すれば問題解決。
- 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
}
}