水曜日, 4月 29, 2009

[Scala] jettyが止まらない!?(netbeans)

ScalaのサポートはEclipseのpluginよりnetbeansのそれのほうが優れている(v6.5.1で見たとき)。
が、netbeansでlift開発をするとき、起動したjettyを落とすことができない、、と思っていたら、やはりissueとして登録されており、暫定対処もある様子なのでメモ。
http://wiki.netbeans.org/TaT_ShutdownJettyStartedByMavenUsingNB

via kwout

土曜日, 4月 25, 2009

[Scala] システムトレイで

システムトレイにアイコンを表示して、さらにメッセージボックスに日本語を出す実験。

import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;

import javax.imageio.ImageIO;

object Alarm2 {
var clockImage:Image = ImageIO.read(new File("clock.png"));
var alarmImage:Image = ImageIO.read(new File("alarm.png"));;
var icon:TrayIcon = new TrayIcon(clockImage);

var alarmTime:Date = new Date();

def main(args : Array[String]){
var tray:SystemTray = SystemTray.getSystemTray();

var menu:PopupMenu = new PopupMenu();
var item:MenuItem = new MenuItem("表示");
item.addActionListener(new ActionListener() {
def actionPerformed(event:ActionEvent):Unit = {
icon.displayMessage("Alarm", "メッセージ!", TrayIcon.MessageType.INFO);
println("message was displayed.");
}
});
menu.add(item);
item = new MenuItem("終了");
item.addActionListener(new ActionListener() {
def actionPerformed(event:ActionEvent):Unit = {
System.exit(0);
}
});
menu.add(item);

icon = new TrayIcon(clockImage, "アラーム", menu);
icon.setImage(alarmImage);

tray.add(icon);
}
}

じゃじゃ馬さんを参考に。ちなみに画像も使わせて頂きました(*)。

日曜日, 4月 19, 2009

[Scala] NB6.7とScala

v6.5ではmavenとの連携が悪い(環境変数を渡せない)ので、まだβではあるがv6.7へ移行。
当初Scalaプラグインが動かず困っていたが、暫定対応版(!?)を発見。

updates.xml.gzはダウンロードするものではなく、NetBeansのツール>プラグイン>設定>追加、で足して使う(使用可能なプラグインにScalaが出てくる)。

今のところv6.5でやれていたメソッド補完などは無事に動いている(Jettyが止まらない現象は(ある意味当然)v6.7にしても出続けているのでかっこ悪いがカスタムターゲットでjetty:stopする作戦をまま踏襲)。

土曜日, 4月 18, 2009

[Solr] GoSenを使う

Javaで形態素解析ライブラリはもうSenしかない状況、、だと思っていましたが、(ちょっと途中で投げ出されている感はあるものの)GoSenのほうが辞書作成もJavaだけでできる等、整備されていて良さそうです。
ただ、Senはトークナイザだけを提供しているので、Solrで使うにはLucene-jaというのを別途取ってきて、そこに入っている"アナライザ"経由で使わなくてはなりません。
つまり、(Lucene-jaの)アナライザが使うトークナイザを、SenからGoSenに変えればよい。。のですが、GoSenはSenから多少構成が変わっているため「ただjarを入れ替え」るだけでは動きません。

細かくはまた別に書こうと思いますが、hideakiさんのブログを参考に、
・Lucene-ja(のSenTokenizer.javaを)書き換え
・無いと不便なbuild.xmlを作成
としたlucene-ja-gosenセットを作りましたので、上げておきます(JDK6で作ったので5で使うならlib以下にあるgosen.jarとか作り直しかも)。

ちなみに、GoSenでもsen.xmlは必要ですが(厳密にはこれはLucene-ja側で使うファイルなので当たり前)、testdata/dictionary/dictionary.xmlをコピーして使えばOKです。

簡単に使い方を。

1.Lucene-ja-gosenをダウンロードして解凍(DIR-Aとする)
2.中のGoSen.tgzをさらに解凍(DIR-Bとする)
4.DIR-B/testdata/dictionaryへ移動してant(辞書作成)
3.sen/confとディレクトリを掘る
5.そのディレクトリへdictionaryで出来たファイルをコピー
 cp *.sen sen/conf
6.そのディレクトリへ以下リネームしてコピー
 mv dictionary.xml sen/conf/sen.xml
7.動かしたいSolrのWEB-INF/libへ移動して以下コピー(※追加参照)
 cp DIR-A/lucene-ja-gosen*.jar (Solrの)WEB-INF/lib
 cp DIR-B/lib/gosen*.jar (Solrの)WEB-INF/lib
8.ココを参考にSolrのschema.xmlへ組込み(senの場合と全く同じで)

/sen/confは起動時の"-Dsen.home"指定を使いたくないからココにしているだけなので変更は可能です。が、辞書とsen.xmlは同じ場所に置きましょう。

追加:
 手順の7は、Solr自体がbuildできる状態なら、build.xmlと同じディレクトリにあるlib以下にこれらjarを入れておけば、適当に(WEB-INF/libに)はいるようなwarを作ってくれます。

[Lift] JavaRebelを使う

Liftのアプリに対しても、コンテナをTomcatにすれば、今はやりの(?)JavaRebelを使ってホットデプロイを実現できるらしい。
http://wiki.liftweb.net/index.php?title=JavaRebel#Tomcat

via kwout

これで開発生産性(この単語は意味を持たなくなってきたけど)はあがりそうです、、、が、よく見るとJavaRebelって商用なんですね。残念。。。

補足:
日本の方が、thick4というのを開発中な様子。

金曜日, 4月 17, 2009

[Life] Liftのサンプル

ScalaのフレームワークなLiftと格闘中。GAE/Jで動くのはいつの日か。

なかなか説明を読んでも難しく(ゆろよろさんのが一番分かり良いと思う)、かつシンプルなサンプルが少ない(すぐDB連携とか見せちゃう)ので、シンプルなサンプルソースをmvn出来る形でまるっと置いておきます。
特にbootstrapのMenuはくせ者なので(ACL機能の代わりもしちゃう)、その部分、ソースにコメントしてあります。

ちなみに、Mavenでfscを使うには以下です(本家にありました)。
\apache-maven-2.0.9\bin\mvn scala:cc -Donce=true

木曜日, 4月 16, 2009

[Solr] Nutchでサイトクロール

Solrを使ってサイトクロールを行う方法が、公開されていたので、

やってみた。

Nutchはウェブスパイダーとしてのクロール機能を提供している。内部に分散ファイルシステムたるHadoopを持っていのだが、今回はこれは使わない方法(のようだ)。

- - - -

1. Nutchを<a href="http://hudson.zones.apache.org/hudson/job/Nutch-trunk/">ダウンロード</a>し、解凍する。

 tar xzf apache-nutch-1.0.tar.gz

2. Solrの設定を行う

Nutchの中にはschema.xmlなどSolrを連携して使うためのサンプルの設定ファイルが含まれています。

a. schema.xmlの配置
apache-nutch-1.0/conf to directory apache-solr-1.3.0/example/solr/conf

b. “content”フィールドの設定変更

<field name=”content” type=”text” stored=”true” indexed=”true”/>

c. クローラ用リクエストハンドラの作成

以下をsolrconfig.xmlに追記します。

<requestHandler name="/nutch" class="solr.SearchHandler" >
<lst name="defaults">
<str name="defType">dismax</str>
<str name="echoParams">explicit</str>
<float name="tie">0.01</float>
<str name="qf">
content^0.5 anchor^1.0 title^1.2
</str>
<str name="pf">
content^0.5 anchor^1.5 title^1.2 site^1.5
</str>
<str name="fl">
url
</str>
<str name="mm">
2&lt;-1 5&lt;-2 6&lt;90%
</str>
<int name="ps">100</int>
<bool hl="true"/>
<str name="q.alt">*:*</str>
<str name="hl.fl">title url content</str>
<str name="f.title.hl.fragsize">0</str>
<str name="f.title.hl.alternateField">title</str>
<str name="f.url.hl.fragsize">0</str>
<str name="f.url.hl.alternateField">url</str>
<str name="f.content.hl.fragmenter">regex</str>
</lst>
</requestHandler>

3. Solrを起動しておきます。

cd apache-solr-1.3.0/example
java -jar start.jar

4. Nutchの設定を行います。

a. apache-nutch-1.0/confにあるnutch-site.xmlの中身を以下のものにまるっと入れ替えます。
ここでは、クローラの名前や、有効化するプラグイン、ドメイン単位でみたときの最大同時接続数などを指定します。

<?xml version="1.0"?>
<configuration>
<property>
<name>http.agent.name</name>
<value>nutch-solr-integration</value>
</property>
<property>
<name>generate.max.per.host</name>
<value>100</value>
</property>
<property>
<name>plugin.includes</name>
<value>protocol-http|urlfilter-regex|parse-html|index-(basic|anchor)|query-(basic|site|url)|response-(json|xml)|summary-basic|scoring-opic|urlnormalizer-(pass|regex|basic)</value>
</property>
</configuration>

b. apache-nutch-1.0/confにあるregex-urlfilter.txtの中身を書き換えます。

-^(https|telnet|file|ftp|mailto):
 
# skip some suffixes
-\.(swf|SWF|doc|DOC|mp3|MP3|WMV|wmv|txt|TXT|rtf|RTF|avi|AVI|m3u|M3U|flv|FLV|WAV|wav|mp4|MP4|avi|AVI|rss|RSS|xml|XML|pdf|PDF|js|JS|gif|GIF|jpg|JPG|png|PNG|ico|ICO|css|sit|eps|wmf|zip|ppt|mpg|xls|gz|rpm|tgz|mov|MOV|exe|jpeg|JPEG|bmp|BMP)$
 
# skip URLs containing certain characters as probable queries, etc.
-[?*!@=]
 
# allow urls in foofactory.fi domain
+^http://([a-z0-9\-A-Z]*\.)*lucidimagination.com/
 
# deny anything else
-.

5. 初期URLリスト(クロール起点)ファイルを作成します。

mkdir urls
echo "http://www.lucidimagination.com/" > urls/seed.txt

6. 初期URLのページをクロールしてNutchのcrawldbに格納します。

bin/nutch inject crawl/crawldb urls

konpyuta:~/work/nutch tf0054$  bin/nutch inject crawl/crawldb urls
Injector: starting
Injector: crawlDb: crawl/crawldb
Injector: urlDir: urls
Injector: Converting injected urls to crawl db entries.
Injector: Merging injected urls into crawl db.
Injector: done
konpyuta:~/work/nutch tf0054$

7. 取得したページを解析してコンテンツを抜き出します。

bin/nutch generate crawl/crawldb crawl/segments

konpyuta:~/work/nutch tf0054$ bin/nutch generate crawl/crawldb crawl/segments
Generator: Selecting best-scoring urls due for fetch.
Generator: starting
Generator: segment: crawl/segments/20090416032246
Generator: filtering: true
Generator: jobtracker is 'local', generating exactly one partition.
Generator: Partitioning selected urls by host, for politeness.
Generator: done.
konpyuta:~/work/nutch tf0054$

上のコマンドで、crawl/segments以下に新たなクロール対象のディレクトリを作成します。 that at this point contains files that store the url(s) to be fetched. In the following commands we need the latest segment dir as parameter so we’ll store it in an environment variable:

export SEGMENT=crawl/segments/`ls -tr crawl/segments|tail -1`

クロールを開始します。

bin/nutch fetch $SEGMENT -noParsing

konpyuta:~/work/nutch tf0054$ bin/nutch fetch $SEGMENT -noParsing
Fetcher: Your 'http.agent.name' value should be listed first in 'http.robots.agents' property.
Fetcher: starting
Fetcher: segment: crawl/segments/20090416032246
Fetcher: threads: 10
QueueFeeder finished: total 1 records.
fetching http://www.lucidimagination.com/
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-finishing thread FetcherThread, activeThreads=1
-activeThreads=1, spinWaiting=0, fetchQueues.totalSize=0
-activeThreads=1, spinWaiting=0, fetchQueues.totalSize=0
-finishing thread FetcherThread, activeThreads=0
-activeThreads=0, spinWaiting=0, fetchQueues.totalSize=0
-activeThreads=0
Fetcher: done
konpyuta:~/work/nutch tf0054$

コンテンツをパースします。

bin/nutch parse $SEGMENT

konpyuta:~/work/nutch tf0054$ bin/nutch parse $SEGMENT
konpyuta:~/work/nutch tf0054$

Then I update the Nutch crawldb.
updatedb コマンドを使うと、先にとってきたページのパースと、もうパースされて見つかっているURLページの取得をパイプライン的に並行して行わせることができます。なお、Nutchは取得した各ページの状態を覚えているため、同じページを何度も取得しに行ってしまうことはありません。

bin/nutch updatedb crawl/crawldb $SEGMENT -filter -normalize

konpyuta:~/work/nutch tf0054$ bin/nutch updatedb crawl/crawldb $SEGMENT -f
ilter -normalize
CrawlDb update: starting
CrawlDb update: db: crawl/crawldb
CrawlDb update: segments: [crawl/segments/20090416032246]
CrawlDb update: additions allowed: true
CrawlDb update: URL normalizing: true
CrawlDb update: URL filtering: true
CrawlDb update: Merging segment data into db.
CrawlDb update: done
konpyuta:~/work/nutch tf0054$

これまでが、クロールの1サイクルとなります。

8. linkdbを作る

bin/nutch invertlinks crawl/linkdb -dir crawl/segments

konpyuta:~/work/nutch tf0054$ bin/nutch invertlinks crawl/linkdb -dir crawl/segments

LinkDb: starting
LinkDb: linkdb: crawl/linkdb
LinkDb: URL normalize: true
LinkDb: URL filter: true
LinkDb: adding segment: file:/Users/tf0054/work/nutch-2009-04-15_04-01-57/crawl/segments/20090416032246
LinkDb: done
konpyuta:~/work/nutch tf0054$

9. 全セグメントからSolrにコンテンツを送る

bin/nutch solrindex http://127.0.0.1:8983/solr/ crawl/crawldb crawl/linkdb crawl/segments/*

10. Solrにて検索可能となったか確認する。

http://127.0.0.1:8983/solr/nutch/?q=solr&amp;version=2.2&amp;start=0&amp;rows=10&amp;indent=on&amp;wt=json

- - - - -

水曜日, 4月 15, 2009

[Scala] スクリプト実行!

Javaで要求される杓子定規な、というか精緻な記述が省けるだけでもScalaを利用する価値はあるというものだが、さらにスクリプト実行(コンパイルして.classを作ってからじゃなく)できるとその価値はより高くなる。

以下、Windowsでスクリプト実行する場合(test.batなどとして保存されることを想定)。
::#!
@echo off
call c:\scala-2.7.4.final\bin\scala %0 %*
goto :eof
::!#

class HelloFunction {
def apply() = "hello"
def apply(name: String) = "hello " + name
}

// メソッド省略の例
val hello = new HelloFunction
println(hello()) // => "hello"
println(hello("Dean")) // => "hello Dean"

// コマンドライン引数処理の例
for(i <- 1 to (args.length)){
print(i + ",");
}

日曜日, 4月 12, 2009

[Java] SenよりもGoSen?

ちょっと前に見たSenは、しかしバグ付きだという噂だし、どうしたものかと思っていたら、GoSenというのもあるらしい。

PerlやJCLからの依存性を排除したとのことだが、しかし、これも本家のURLがNotFoundで、、どうもJavaでこの手のアプリは保守するのが面倒になるものなのか(CMeCabの中にあるような、辞書不要のタイプもある)。


書きながら調べていたら、早速(Senよか治ってるようだけど)バグ情報も。あとLucene対応の書換えについて。

土曜日, 4月 11, 2009

[Life] GAE/Javaとは

Javaのアカウントが来たので、Liftの稼働実験をしつつ、@ITの記事を読む。
昨日まで「負荷による拡張はgoogleさんがよしなにやってくれる」という言説の意味(どうやって水平分散に回せているのか)が、まったく分からなかったのだけど、やっと理解。
シングルスレッドだからですね。
ウェブからの各アクセスを一筆的に考えると、確かに途中で抜けられたりしなければ、筐体が分かれてても(2本の干渉が無いから)ざーっと勝手に流れて行って問題ないんですね。。なるほど。というか確かに。

追伸:
面白い潜入調査をしている方がいた。あと、mavenからの操作も出来るようになった。

木曜日, 4月 09, 2009

[Lift] derbyの中を調べる

Liftの例(todo)では、標準ではapacheのderbyが使われており、この中にテーブルなどが自動生成される。この、自動生成されたテーブル定義やらを見る方法を調べたのでメモ。

D:\work\Scala\todo>"d:\Documents and Settings\00962724\デスクトップ\db-derby-10.5.1.1-bin\bin\ij.bat"
ij バージョン 10.5
ij> connect 'jdbc:derby:d:\work\scala\todo\lift_example';
ij> describe users;
COLUMN_NAME |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL&
------------------------------------------------------------------------------
ID |BIGINT |0 |10 |19 |AUTOINCRE&|NULL |NO
FIRSTNAME |VARCHAR |NULL|NULL|32 |NULL |64 |YES
LASTNAME |VARCHAR |NULL|NULL|32 |NULL |64 |YES
EMAIL |VARCHAR |NULL|NULL|48 |NULL |96 |YES
LOCALE |VARCHAR |NULL|NULL|16 |NULL |32 |YES
TIMEZONE |VARCHAR |NULL|NULL|32 |NULL |64 |YES
PASSWORD_PW |VARCHAR |NULL|NULL|48 |NULL |96 |YES
PASSWORD_SLT |VARCHAR |NULL|NULL|20 |NULL |40 |YES
TEXTAREA |VARCHAR |NULL|NULL|2048 |NULL |4096 |YES
SUPERUSER |SMALLINT |0 |10 |5 |NULL |NULL |YES
VALIDATED |SMALLINT |0 |10 |5 |NULL |NULL |YES
UNIQUEID |VARCHAR |NULL|NULL|32 |NULL |64 |YES

12 行が選択されました
ij> describe todo;
COLUMN_NAME |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL&
------------------------------------------------------------------------------
PRIORITY |INTEGER |0 |10 |10 |NULL |NULL |YES
ID |BIGINT |0 |10 |19 |AUTOINCRE&|NULL |NO
DESC_C |VARCHAR |NULL|NULL|128 |NULL |256 |YES
DONE |SMALLINT |0 |10 |5 |NULL |NULL |YES
OWNER |BIGINT |0 |10 |19 |NULL |NULL |YES

5 行が選択されました
ij> show tables;
TABLE_SCHEM |TABLE_NAME |REMARKS
------------------------------------------------------------------------
SYS |SYSALIASES |
SYS |SYSCHECKS |
SYS |SYSCOLPERMS |
SYS |SYSCOLUMNS |
SYS |SYSCONGLOMERATES |
SYS |SYSCONSTRAINTS |
SYS |SYSDEPENDS |
SYS |SYSFILES |
SYS |SYSFOREIGNKEYS |
SYS |SYSKEYS |
SYS |SYSROLES |
SYS |SYSROUTINEPERMS |
SYS |SYSSCHEMAS |
SYS |SYSSTATEMENTS |
SYS |SYSSTATISTICS |
SYS |SYSTABLEPERMS |
SYS |SYSTABLES |
SYS |SYSTRIGGERS |
SYS |SYSVIEWS |
SYSIBM |SYSDUMMY1 |
APP |TODO |
APP |USERS |

22 行が選択されました
ij> exit
> ;
D:\work\Scala\todo>

木曜日, 4月 02, 2009

[Scala] 遅延評価の例

Scalaで遅延評価。引数の型指定部分で":"の後ろに"=>"を挟み込むことで指示できる(参考)。
#!/bin/bash
exec ~/work/scala/bin/scala "$0" "$@"
!#

def until(f: => Boolean)(p: => Unit) {
while (!f) p
}

var i = 0
until (i > 10) {
print(i + " ")
i += 1
}
println
この例で遅延評価を外す("until(f:Boolean)"とやる)と、
・最初に"i>10(でi=0)"がfalseと評価される
・その結果(false)がuntil関数に渡ってしまう
ため、(trueと渡って来たものを再評価できるわけもなく)無限ループに陥ってしまいます。

なお、"(p: => Unit)"もその意味では遅延評価の指定がなされている、つまり「関数とは要するに引数付き遅延評価式で」ということで、具体的にはココを"(p:Unit)"とすると、
・1回だけuntilに渡したブロック(printとか)を処理して
・戻値が無い(Unitな)whileが回り続ける(iは1になったが以後そのまま)
状態に陥ってしまう。なのでここでも遅延評価が活躍している(んだと思う)。