Прочитал недавно один топик, немного поискал в интернете утилиту, которая бы выводила расшифровку флагов процессора, не нашел, а потому взял и написал свою. Используя LiftWeb.
Процессом написания я и хочу поделиться. Сразу предупреждаю, внутри много текста, но можно перейти непосредственно к практике, пропустив теоретическую часть.
Итак, нам понадобятся:
Для создания скелета приложения используем maven:
И проект можно уже запустить! mvn jetty:run и заходим на localhost:8080
Да, пока маловато функционала, и мы это исправим.
Проект представляет собой обычное J2EE web-приложение. В наличии имеется web.xml с единственным назначением — загружать LiftFilter, который и будет заниматься всей магией.
При инициализации фильтр вызывает метод bootstrap.liftweb.Boot.boot, который отвечает за настройку фреймворка. В частности, описывает, в каких пакетах искать снипеты (о них — дальше). Также в этом только что сгенеренном методе создается меню, но оно нам не нужно и его можно удалить.
Теперь о жизненном цикле запроса. Когда происходит обращение к какому-то URL, фреймворк находит соответствующий темплейт, рендерит его и отдает результат пользователю. Правила «нахождения» темплейта могут быть изменены в boot-методе, но это тема для отдельной статьи. Для начала нам хватит знания, что темплейты расположены в каталоге webapp и мапятся один к одному на URL запроса, то есть при запросе localhost:8080/index.html будет отрендерен темплейт index.html и так далее.
Каждый темплейт представляет собой xml-файл. Вот сгенеренный index.html для нашего проекта:
Тут используется важнейшая часть LiftWeb — сниппеты. Сниппет — это scala-метод, который принимает на вход html, каким-то образом его обрабатывает и выдает на выходе.
Например, сниппет lift:surround выполняет важнейшую функцию: маппинг темплейта на html. В данном примере происходит следующее:
Сниппет lift:helloWorld.howdy — это уже не стандартный сниппет, а сгенеренный. Когда LiftWeb встречает ссылку на сниппет, и если это не стандартный сниппет, то происходит поиск среди нашего scala-кода. Для этого в boot-методе и указывался пакет app.example.cpuflags. В данном случае создается новый экземпляр класса app.example.cpuflags.HelloWorld и у него вызывается метод howdy.
Вот этот метод:
Тут все просто: внутри in (который и есть поступающий на вход html) ищется нода b:time (которая есть в темплейте) и заменяется на текущую дату. Результат возвращается методом.
Меняем темплейт:
Создаем сниппет app.example.cpuflags.snippet.CpuInfo.scala:
cpuflags.properties я создал, используя информацию отсюда. Список не полный, но заморачиваться и искать дальше было лень.
Все, перезапускаем jetty:run и радуемся:
Процессом написания я и хочу поделиться. Сразу предупреждаю, внутри много текста, но можно перейти непосредственно к практике, пропустив теоретическую часть.
Итак, нам понадобятся:
- maven (я использовал версию 3)
- подключение к Интернет (мавен будет много чего качать)
- IDE либо текстовый редактор
Для создания скелета приложения используем maven:
mvn archetype:generate -DarchetypeGroupId=net.liftweb -DarchetypeArtifactId=lift-archetype-blank_2.8.1 -DarchetypeVersion=2.2-RC6 -DarchetypeRepository=http://scala-tools.org/repo-snapshots -DremoteRepositories=http://scala-tools.org/repo-snapshots -DgroupId=app.example -DartifactId=cpuflags -Dpackage=app.example.cpuflags
И проект можно уже запустить! mvn jetty:run и заходим на localhost:8080
Да, пока маловато функционала, и мы это исправим.
Но в начале немного теории
Проект представляет собой обычное J2EE web-приложение. В наличии имеется web.xml с единственным назначением — загружать LiftFilter, который и будет заниматься всей магией.
При инициализации фильтр вызывает метод bootstrap.liftweb.Boot.boot, который отвечает за настройку фреймворка. В частности, описывает, в каких пакетах искать снипеты (о них — дальше). Также в этом только что сгенеренном методе создается меню, но оно нам не нужно и его можно удалить.
Теперь о жизненном цикле запроса. Когда происходит обращение к какому-то URL, фреймворк находит соответствующий темплейт, рендерит его и отдает результат пользователю. Правила «нахождения» темплейта могут быть изменены в boot-методе, но это тема для отдельной статьи. Для начала нам хватит знания, что темплейты расположены в каталоге webapp и мапятся один к одному на URL запроса, то есть при запросе localhost:8080/index.html будет отрендерен темплейт index.html и так далее.
Каждый темплейт представляет собой xml-файл. Вот сгенеренный index.html для нашего проекта:
<lift:surround with="default" at="content">
<h2>Welcome to your project!</h2>
<p>
<lift:helloWorld.howdy>
<span>Welcome to cpuflags at <b:time/></span>
</lift:helloWorld.howdy>
</p>
</lift:surround>
Тут используется важнейшая часть LiftWeb — сниппеты. Сниппет — это scala-метод, который принимает на вход html, каким-то образом его обрабатывает и выдает на выходе.
Например, сниппет lift:surround выполняет важнейшую функцию: маппинг темплейта на html. В данном примере происходит следующее:
- в каталоге templates-hidden ищется файл с именем default.html (имя берется из атрибута with, плюс расширение html)
Вот этот файл:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <meta name="description" content="" /> <meta name="keywords" content="" /> <title>app.example:cpuflags:1.0-SNAPSHOT</title> <script id="jquery" src="/classpath/jquery.js" type="text/javascript"></script> </head> <body> <lift:bind name="content" /> <lift:Menu.builder /> <lift:msgs/> </body> </html>
- внутри этого файла ищется нода lift:bind с именем content (имя берется из атрибута at)
- содержимое lift:bind заменяется на содержимое lift:surround из темплейта
Сниппет lift:helloWorld.howdy — это уже не стандартный сниппет, а сгенеренный. Когда LiftWeb встречает ссылку на сниппет, и если это не стандартный сниппет, то происходит поиск среди нашего scala-кода. Для этого в boot-методе и указывался пакет app.example.cpuflags. В данном случае создается новый экземпляр класса app.example.cpuflags.HelloWorld и у него вызывается метод howdy.
Вот этот метод:
def howdy(in: NodeSeq): NodeSeq =
Helpers.bind("b", in, "time" -> (new _root_.java.util.Date).toString)
Тут все просто: внутри in (который и есть поступающий на вход html) ищется нода b:time (которая есть в темплейте) и заменяется на текущую дату. Результат возвращается методом.
Хватит теории, пора приступать к практике
Меняем темплейт:
<lift:surround with="default" at="content">
<h2>Enter your CPU flags</h2>
<form method="get">
<input type="text" name="flags"/>
<input type="submit"/>
</form>
<table cellpadding="10">
<lift:CpuInfo.flags>
<tr>
<td><flag:name/></td> <td><flag:description/></td>
</tr>
</lift:CpuInfo.flags>
</table>
</lift:surround>
Создаем сниппет app.example.cpuflags.snippet.CpuInfo.scala:
package app.example.cpuflags.snippet
import xml.NodeSeq
import net.liftweb.util.BindHelpers._
import net.liftweb.http.{S}
import java.util.Properties
class CpuInfo {
def flags(html: NodeSeq): NodeSeq = {
val a1 = CpuFlags.getInfo(S.param("flags").openOr(""))
a1.flatMap( p => bind("flag", html,
"name" -> p._1,
"description" -> p._2) )
}
}
object CpuFlags {
val propertyMap = {
val props = new Properties
props.load(this.getClass.getClassLoader.getResourceAsStream("cpuflags.properties"))
props
}
def getInfo(flags: String) = {
val s = flags.trim.toUpperCase
if (s.size>0)
s.split(" +").toList.map( (flag) => (flag, if (propertyMap.getProperty(flag)!=null) propertyMap.getProperty(flag) else "UNKNOWN FLAG") )
else
List()
}
}
cpuflags.properties я создал, используя информацию отсюда. Список не полный, но заморачиваться и искать дальше было лень.
Все, перезапускаем jetty:run и радуемся: