eclipse+activatorでデバッグをWindows環境で行う

PlayFramework2.3から従来のplayコマンドではなくactivatorコマンドに変更となりました。それに伴いデバッグ方法も変わってしまったので、以下に書き留めておきます

環境

  • Windows8.1
  • eclipse 4.3
  • scala2.11
  • jdk1.7

設定

%UserProfile%\.activator\activatorconfig.txtにファイルを作成します。通常ですと、

c:\users\username\.activator\activatorconfig.txt

になるかと思います

このファイルに以下を記述します。addressはデバッグ用のポートなので開いているポートならばなんでも構いません。

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

実行

プロジェクトのフォルダにコマンドラインからアクセスし、activatorで起動します。8000番ポートでデバッグ用にポートが開いているのがわかります

Z:\test_project>activator
Listening for transport dt_socket at address: 8000
[info] Loading project definition from Z:\workspace\qrapp_api\project
[info] Set current project to qrapp_api (in build file:/Z:/workspace/qrapp_api/)
[qrapp_api] $ run
--- (Running the application from SBT, auto-reloading is enabled) ---
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/utsubo/.ivy2/cache/ch.qos.logback/lo
gback-classic/jars/logback-classic-1.1.1.jar!/org/slf4j/impl/StaticLoggerBinder.
class]
SLF4J: Found binding in [jar:file:/C:/Users/utsubo/.ivy2/cache/org.slf4j/slf4j-n
op/jars/slf4j-nop-1.6.4.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorSta
ticBinder]
[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
(Server started, use Ctrl+D to stop and go back to the console...)

次にEclipseからプロジェクトを右クリックし、DebugConfigurationからRemoteJavaApplicationをダブルクリックします。ポート番号8000を確認しScalaDebugger(SocketAttach)を選択。起動します。ブラウザを開いてアプリケーションにアクセスすれば、ブレークポイントでデバッグ可能となります。

設定はこの辺りに詳しく書いています

http://stackoverflow.com/questions/19473941/cant-debug-with-typesafe-activator

PlayFramework+EclipseでViews.htmlエラー

いつも忘れるのでメモ

PlayFrameworkをEclipseで開発しているとこのエラーが必ず出ます

object Application extends Controller{
def index()=Action{implicit request=>
Ok(views.html.index())			 // <=この行が赤いラインでエラー表示される
}
}

これの解決方法ですがプロジェクトのプロパティからJavaBuildPathを選択し、LibrariesタブからAddClassFolderで targetーscala-2.11ーclass_managedを追加

F5を押してリフレッシュすれば治ります

これが赤いままだとUnitテストでClassNotFoundエラーが出ます

Application.scala

package controllers

import play.api._
import play.api.mvc._
import java.io._
import org.apache.batik.dom.svg._
import org.apache.batik.transcoder._
import org.apache.batik.transcoder.image._
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.Document
import java.awt.Rectangle
import org.apache.fop.svg.PDFTranscoder

import play.api.libs.iteratee._
import play.api.data._
import play.api.data.Forms._

case class Image(
	filename:String,
	scale:Int,
	imagetype:String,
	svg:String
)

object Application extends Controller {

	val imageDataForm = Form(
			mapping(
					"filename"->text,
					"scale"->number,
					"type"->text,
					"svg"->text
			)(Image.apply)(Image.unapply)
	)
	
	def index()=Action{implicit request =>
		Ok(views.html.index())
	}

	def makeImage()=Action{implicit request =>
		val image=imageDataForm.bindFromRequest.get
		
		val is=new ByteArrayInputStream(image.svg.getBytes())
		import scala.concurrent.ExecutionContext.Implicits._
		Ok.chunked(Enumerator.outputStream { os =>
			val tin=new TranscoderInput(is)	
			val tot=new TranscoderOutput(os)	
			getTranscoder(image.imagetype, 0.7f) match{
				case Some(x) =>{
					println("transcode:x="+x)
					x.transcode(tin,tot)
				}
				
				case _ => None			
			}
			os.flush
			os.close	
			println("flush")
		} >>> Enumerator.eof).withHeaders(
				"Content-Type" -> (image.imagetype),
				"Content-Disposition"->("attachment; filename="+image.filename)
		)	 
	}


	def getParameter(request:play.api.mvc.Request[play.api.mvc.AnyContent],str:String) = {
		request.queryString.get(str).flatMap(_.headOption) match {
			case Some(x) => x
			case None => ""
		}
	}
	
	def getDocument(is:InputStream):Document ={
				val parser = XMLResourceDescriptor.getXMLParserClassName()
				val f = new SAXSVGDocumentFactory(parser)
				val doc = f.createDocument("http://www.w3.org/2000/svg",is)
				return doc
	}
 
	def	getTranscoder( transcoderType:String,	keyQuality:Float ) :Option[Transcoder]={
		println("getTranscoder:"+transcoderType)
				transcoderType match{
					case "image/jpeg" =>Option(getJPEGTranscoder(keyQuality))
					case "image/png" => Option(getPNGTranscoder())
					case "application/pdf" => Option(new PDFTranscoder())
					case	_ => None
				}
		}
	
	def	getJPEGTranscoder( keyQuality:Float):JPEGTranscoder= {
				val jpeg = new JPEGTranscoder()
				jpeg.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, keyQuality)
				return jpeg
		}
 
		def	getPNGTranscoder() :PNGTranscoder={
				return new PNGTranscoder();
		}
}

index.scala.html





こんな感じでいけるかと思います

batikをPlayFrameworkで使う

batikとはSVGからPDFやPNG等へ変換するすごーく便利なJavaのライブラリです。これをPlayframeworkでWebアプリとして構築してみました

準備

batickで使用するJarファイルをダウンロードしてきます。

http://mvnrepository.com/artifact/batik

この辺りから落としてくれば十分です

batik-anim.jar

batik-svgpp.jar

batik-awt-util.jar

batik-swing.jar

batik-bridge.jar

batik-transcoder.jar

batik-codec.jar

batik-ttf2svg.jar

batik-css.jar

batik-util.jar

batik-dom.jar

batik-xml.jar

batik-ext-1.6-1.jar

batik.jar

batik-ext.jar

batik-extension.jar

batik-gui-util.jar

batik-gvt.jar

batik-parser.jar

batik-rasterizer.jar

batik-script.jar

batik-slideshow.jar

batik-squiggle.jar

batik-svg-dom.jar

batik-svggen.jar

pdf-transcoder.jar

js.jar

w3c.jar

気がついたらこれだけダウンロードしていました。全部いるのでしょうか?

今回はPDFとJPEGとPNGをパラメタで分けて出力します

Windows7 HomePremiumでRemoteDeskTopServer

Windows7 HomePremiumでRDTを使おうとした際のメモ

  • サーバ側

Windows7 Home Premium SP1

  • クライアント側

iMac OS 10.9

まず、Windows7側にパッチを当てます。この辺りを参考にConcurrent_RDP_Patcher.zipをダウンロードし、パッチを当てるだけ。チェックボックスは2つとも外してももんだありません。

http://blog.livedoor.jp/rappazubon/archives/51943235.html

http://orebibou.com/2014/05/windows-7-home-premium%E3%81%A7%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97%E3%82%92%E6%9C%89%E5%8A%B9%E5%8C%96%E3%81%99%E3%82%8B/

次にはまったのがMac側。最初↓のMicrosoftRemoteDesktopを使用し接続しようとしてもエラー。

https://itunes.apple.com/jp/app/microsoft-remote-desktop/id715768417?mt=12

mac側からnmapでWindowsのポートを調べても特に問題なく3389ポートは開いている。

実はこのMac側のクライアントが悪かったようです。こちら↓のRemoteDesktopConnectionを使用すると問題なくつながります。

http://www.microsoft.com/ja-jp/download/details.aspx?id=18140

参考まで

eclipseでTomcatの設定

いつも忘れてしまうのでメモ

環境

MacOS10.9
tomcat7

brew install tomcat7

java

oracle jdkをインストールし、シンボリックリンク

$ which java

/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home//bin/java

eclipse kepler

インストール

EclipseのClassicをインストールするとサーバランタイムがインストールされずに、TOMCATアプリなど作成する際にデバッグに困ってしまいます。

サーバランタイムをインストールするには、予めBrewでTomcatをインストールした後に、ヘルプのinstall new softwareから

http://download.eclipse.org/releases/kepler

を選択し、いかの2つのプラグインをインストールします。

  • JST Server Adapters
  • JST Server Adapters Extensions

その後、環境設定ーServerーRuntimeEnvironmentsのaddでtomcatを追加するのですが、この際にディレクトリに以下を入力します

/usr/local/Cellar/tomcat7/7.0.55/libexec

これでサーバランタイムが設定可能となります。

この辺りの内容はこちらから引用しています

http://stackoverflow.com/questions/2000078/apache-tomcat-not-showing-in-eclipse-server-runtime-environments

http://stackoverflow.com/questions/17745834/unknown-version-of-tomcat-was-specified-with-tomcat-7-0-42

SQLiteにユーザ定義関数を追加しTOMCAT+DBCPで利用する

ユーザ定義関数の追加

SQLiteにはストアドプロシージャはなく、その代わりにユーザ定義関数を追加し、同様の処理を行うようにできます。


	
	public class TestFunc extends Function{
		@Override
		protected void xFunc() throws SQLException
		{
			try{
				String a = value_text(0);
				String b = value_text(1);
				result(Integer.parseInt(a)+Integer.parseInt(b));
			}catch(Exception e){
				throw new SQLException(e.getMessage());
			}
		}
	}

org.sqlite.Functionを継承したクラスを作成し、その中のxFuncをオーバラードしたメソッで実装する関数を定義しresultで戻します。

今回の場合には文字列で定義した数字を足しあわせて返すという関数を定義しました。

これを利用する際にはJDBCのコネクションに対しFunction.createでバインドしてやります


			InitialContext ic = new InitialContext();
			DataSource ds = (DataSource)ic.lookup("java:comp/env/test");
			Connection conn=ds.getConnection();
			conn.setAutoCommit(false);
			Function.create(conn,"my_func",new TestFunc());
	
			PreparedStatement stmt=conn.prepareStatement("select my_func(?,?)");
			stmt.setString(1,"1");
			stmt.setString(2,"3");
			ResultSet rs=stmt.executeQuery();
			while(rs.next()){
				out.println(rs.getString(1));
			}

バインドする際にSQL文内で呼ぶ関数名を同時に定義します。その後は通常のJDBCの処理と同じです。

DBCPで使う

DBCPで使うサンプルです

  • context.xml
				 	
  • TestServlet.java
public class TestServlet extends HttpServlet{
	private static final long serialVersionUID = 1L;
	
	@Override
	public void init(ServletConfig sc) throws ServletException {
		super.init(sc);
	}
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		doPost(req, res);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		res.setContentType("text/html;charset=utf-8");
		PrintWriter out=res.getWriter();
		
		//http://stackoverflow.com/questions/6489514/apache-commons-dbcp-connection-object-problem-thread-classcastexception-in-org
		//http://www.ksky.ne.jp/~snbhsmt/commons-dbcp.html
		//http://grokbase.com/t/tomcat/users/052spdat5s/tomcat-5-5-7-using-builtin-jdbc-connection-pool-cant-access-real-connection-accesstounderlyingconnectionallowed-true
		try{
			
			InitialContext ic = new InitialContext();

			BasicDataSource ds = (BasicDataSource)ic.lookup("java:comp/env/test");
								//ds.setAccessToUnderlyingConnectionAllowed(true);
								DelegatingConnection dcon=(DelegatingConnection)ds.getConnection();
			
								DelegatingStatement dstmt = (DelegatingStatement)dcon.createStatement();
								PoolableConnection pconn=(PoolableConnection)dstmt.getDelegate().getConnection();

								Connection conn=pconn.getInnermostDelegate();
			conn.setAutoCommit(false);
			Function.create(conn,"my_func",new TestFunc());
	
			PreparedStatement stmt=conn.prepareStatement("select my_func(?,?)");
			stmt.setString(1,"1");
			stmt.setString(2,"3");
			ResultSet rs=stmt.executeQuery();
			while(rs.next()){
				out.println(rs.getString(1));
			}
			dcon.close();// 140916 閉じ忘れるとTOMCAT起動時に止まる
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public class TestFunc extends Function{
		@Override
		protected void xFunc() throws SQLException
		{
			try{
				String a = value_text(0);
				String b = value_text(1);
				result(Integer.parseInt(a)+Integer.parseInt(b));
			}catch(Exception e){
				throw new SQLException(e.getMessage());
			}
		}
	}
}

ここでのポイントはDBCPのコネクションプールからJDBCのコネクションを取得する際に実際のコネクションを取得してやる必要があります。

			BasicDataSource ds = (BasicDataSource)ic.lookup("java:comp/env/test");
						//ds.setAccessToUnderlyingConnectionAllowed(true);
						DelegatingConnection dcon=(DelegatingConnection)ds.getConnection();
			
						DelegatingStatement dstmt = (DelegatingStatement)dcon.createStatement();
						PoolableConnection pconn=(PoolableConnection)dstmt.getDelegate().getConnection();

						Connection conn=pconn.getInnermostDelegate();

この部分です。詳細は↓あたりを参考にしてください。

http://stackoverflow.com/questions/6489514/apache-commons-dbcp-connection-object-problem-thread-classcastexception-in-org

http://www.ksky.ne.jp/~snbhsmt/commons-dbcp.html

http://grokbase.com/t/tomcat/users/052spdat5s/tomcat-5-5-7-using-builtin-jdbc-connection-pool-cant-access-real-connection-accesstounderlyingconnectionallowed-true

なおJDBCはsqlite-jdbc-3.7.2.jarではなくsqlite-jdbc-3.8.5-pre1.jarを使用してください。Function.create内のinstanceofでconnectionが実コネクションかどうか判定しているのですが、JDBCが古いほうだと何故かここでエラーになってしまいます。

こんな感じでSQLite+DBCPでユーザ定義関数が追加できます。参考になれば幸いです

20140916追記

DelegateingConnectionを取得した際にコネクションを閉じ忘れるとTOMCAT起動時にエラーというか無限ループに陥ります。注意

Yahooファイナンスの株価を取得してみる その2

前回、株価のヒストリカルデータを銘柄コード別にファイルに落とすところまでできました。このままでは使いづらいのでリレーショナルDBに入れることにします。

ただ、この動作しているマシンがRaspberryPIなので、データベースを入れるというような暴挙はできません。そこで、組み込みきらしくSQLiteを使うことにします。

  • schema
$ sqlite3 stock.db 
SQLite version 3.7.13 2012-07-17 17:46:21
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .schema
CREATE TABLE histDaily(
				date text,
				open real,
				high real,
				low	real,
				close real,
				volume real,
				adj_close real,
				code text
);
CREATE UNIQUE INDEX idx_histDaily on histDaily(code,date);
sqlite> 
  • hist_import.py
#!/bin/env python
# coding:utf-8
import sqlite3
import sys

def import_data(file):
	con=sqlite3.connect("stock.db",isolation_level=None)
	sql=u"insert into histDaily values(?,?,?,?,?,?,?,?)"
	for line in open(file,'r'):
		ay=line.split('\t')
		con.execute(sql,(ay[0],ay[1],ay[2],ay[3],ay[4],ay[5],ay[6],ay[7]))
	con.close()


if __name__ == '__main__':
	print sys.argv[1]
	import_data(sys.argv[1])
  • hist_imprt.sh
#!/bin/sh

codes=`ls data/*.txt|grep -v master`
for code in $codes;do
	echo $code
	python hist_import.py $code
done

これで動かしてみたのですがちょっとおそいです。というか丸一日動かして1000銘柄も取り込めていない。。。

後ほどチューニングすることにします

Yahooファイナンスの株価を取得してみる

RでYahooファイナンスを取得するモジュール、RFinanceYJを改造し使っていたのですが、あまりの遅さに閉口してしまいました。丸一日掛けても14年分の日次株価は1000銘柄程度しか撮れていません。やり方はこの辺りにまとめています。

http://d.hatena.ne.jp/anagotan/searchdiary?word=%2A%5BR%5D

RaspberryPiを手に入れたこともあり、pythonで書き直してみました。pythonは2.7です

master.py

マスターを取得します 

#!/bin/env python
# coding:utf-8

import urllib
import urllib2
from lxml import etree
import time


def get_stockmaster(hira):
	p=1
	ret="dummy"
	ss=""
	while(ret!=""):
		ret,plen=get_stockmaster_page(hira,p)
		#print ret
		if ret != "":
			ss=ss+ret
		p=p+1
	return ss

def get_stockmaster_page(hira,p):
	hira=hira.encode('utf-8')
	url="http://stocks.finance.yahoo.co.jp/stocks/qi/?%s"
	params={"js":hira,"p":p}
	data=urllib.urlencode(params)
	#print data
	res=urllib.urlopen(url % data)
	page=res.read()
	root=etree.fromstring(page,etree.HTMLParser())
	elem=root.xpath("//a[contains(@href,'/stocks/detail')]")
	i=0
	ret=""
	for ele in elem:
		if ele.text != None:
			if i%3==0:
				ret=ret+ele.text+"\t"
			if i%3==1:
				ret=ret+ele.text.encode('utf-8')+"\n"
		i=i+1
	time.sleep(1)
	return ret,len(elem)

def get_stockmasterall():
	hira=u"あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよわ"
	size=len(hira)
	ss=""
	for i in range(0,size):
		print hira[i:i+1]
		#print i
		ss=ss+get_stockmaster(hira[i:i+1])
	f=open("data/master.txt","w")
	f.write(ss)
	f.close()

if __name__ == '__main__':
	get_stockmasterall()

hist.py

時系列データを取得します 

#!/bin/env python
# coding:utf-8

import urllib
import urllib2
from lxml import etree
import time


def get_stockdata(code,sy,sm,sd,ey,em,ed,tm):
	p=1
	plen=53
	ss=""
	while(plen>=53):
		ret,plen=get_stockdata_page(code,sy,sm,sd,ey,em,ed,tm,p)
		ss=ss+ret
		p=p+1
	f=open("data/"+code+".txt","w")
	f.write(ss)
	f.close()

def get_stockdata_page(code,sy,sm,sd,ey,em,ed,tm,p):
	url="http://info.finance.yahoo.co.jp/history/?%s"
	params={"code":code,"sy":sy,"sm":sm,"sd":sd,"ey":ey,"em":em,"ed":ed,"tm":tm,"p":p}
	data=urllib.urlencode(params)
	#print data
	res=urllib.urlopen(url % data)
	page=res.read()
	root=etree.fromstring(page,etree.HTMLParser())
	elem=root.xpath("//table")
	
	ret=""
	plen=len(elem[1].xpath("//tr"))
	for tr in elem[1].xpath("//tr"):
		if len(tr.findall("td"))==7:
			str=""
			for td in tr.findall("td"):
				s=td.text.encode('utf-8').replace(',','').replace('年','-').replace('月','-').replace('日','')
				str=str+s+"\t"
			str=str+code
			ret=ret+str+"\n"
	time.sleep(1)
	return ret,plen

def get_stockdata_all(sy,sm,sd,ey,em,ed,tm):
	f=open("data/master.txt")
	lines=f.readlines()
	f.close()
	for line in lines:
		ay=line.split("\t")
		print ay[0]
		get_stockdata(ay[0],sy,sm,sd,ey,em,ed,tm)

if __name__ == '__main__':
	get_stockdata_all("2000","01","01","2014","08","27","d")

やっつけで作成しましたがとりあえずファイルに落とすところまでは確認できました。

ちなみに動作環境はRapberryPITypeB+のRASPBIANVersion:June 2014で動いています