Apache POIを使って絵文字が含まれるエクセルを出力する
Javaでエクセルファイルを読み書きするにはApache POIを使うわけですが、😀のような絵文字を含むエクセルを作成して出力することが出来ません。
Java自体は絵文字を扱えるのですが、POIで出力すると「?」となってしまいます。
Windowsで日常的に使うツールで絵文字が扱えるものの中にエクセルがあるのですが、Javaプログラムで作成した絵文字を含むデータを出力できないのは痛い。
調べてみると他のサイトでこんなのを見つけました
ほどよくしっかり: POIで絵文字を出力するためのハック
根本原因はPOIが依存するライブラリのxmlbeansが絵文字を無効な文字と判断して「?」に変換してしまうからのようです。
xmlbeansは開発停止しているのですが、forkして改修をしている人がいました!
https://github.com/pjfanning/xmlbeans
mavenにあるので、依存ライブラリに加えれば良さそうです。
Scalaのsbtだと以下のように依存の設定をしました。
"org.apache.poi" % "poi" % "3.17", "org.apache.poi" % "poi-ooxml" % "3.17" exclude("org.apache.xmlbeans", "xmlbeans"), "com.github.pjfanning" % "xmlbeans" % "2.6.2",
この状態で絵文字を含むエクセルを出力すると、無事にエクセルで絵文字が表示されました!
ScalaでDBを使った小物ツールをサクッと作るには?JDBI!?(タイトル修正)
ScalaでORマッパーというとSlickやScalikeJDBC等色々あるが、ほんとにちょっとしたツールを作りたいだけならばもっと手軽にやりたいと思うはず。
ただJDBCを直に使うとなるとこれはこれで逆に使いづらい。
そんなあなたに「Commons DBUtils」…ではなく「JDBI」!
こいつはJDBCを使いやすくしてくれるラッパー…どころか、軽量なのにORマッパーとしての機能を十分に備えるすごいやつ!
「Commons DBUtils」でも良いのだけど、最近密かに凄くイイと思ってるJavaのフレームワーク「Dropwizard」に採用されているJDBIがイケてるのでそっちを覚えるといいことがあるかも!
という訳で使ってみた。
DBに接続する部分はhikariCPを使ってます。一番速いらしいコネクションプール実装。
http://jdbi.org/five_minute_intro/
公式にあるコードをちょっと改造しただけだが、DefaultMapperでmapするとMap[String, Any]のリストで返ってくるのでDBUtilsみたいな手抜きコードも書ける!
JDBIはJavaのライブラリだがイテレータを取得してScalaのイテレータに変換してしまえばあとはScalaで好き放題書けていい感じ。
公式のものを少しいじった程度だがORマッパー的な機能をScalaで使ってみたのが以下のコード。
DAO用のインターフェースはtraitで問題ない。が、
@RegisterMapper(Array(classOf[ApplicationMapper]))
とかちょっと不自然になってたり、
case class Application(@BeanProperty id: String, @BeanProperty name: String)
いちいち@BeanPropertyつけないといけなかったりするのがあれだけど他のORマッパーよりは手抜きできてると思いたい。
とりあえずScalaでもBeanのマッピングは普通(?)にできる!
簡単かつ強力なJDBIはScalaでも役に立つはず!
json4sでCase Classをシリアライズするときのキーをスネークケースにしたかった
「別にそのままキャメルケースのキーのJSON吐き出せばいいじゃん」ってなると思うけど、
既にスネークケースでJSON吐いてるシステムがあって、さらにscalaでもJSONを吐くシステムを作るときに
統一したいよ〜ていうので考えてみた。
なんか面倒そうなのでスネークケースのCase Class作ればいいじゃん。
なわけで、json4sのソース見た結果、例えばjackson使っている場合は
org.json4s.jackson.Serialization.writeメソッドの中身で
JsonMethods.mapper.writeValueAsString(Extraction.decompose(a)(formats))
ってやってて、JsonASTに変換して組み立てるのとmapperで文字列化するのを一気にやってるのね。
で、JsonASTで使えるキャメルケース⇔スネークケースの変換みたいな便利メソッドがあった!
class MonadicJValue(jv: JValue) { ・・・ /** * Underscore all the keys in this [[org.json4s.JsonAST.JValue]] */ def snakizeKeys = rewriteJsonAST(camelize = false) ・・・ }
ってことで、以下の様な感じで書けば出来るんじゃね?
うむ。
LogWatchでLogbackが出力したログをチェックするスクリプトをつくってみた
JavaアプリはLogbackを使ってログを出力している。出来ればこいつもLogWatchに一緒に監視してもらいたい。
perlで単純にログレベルの文字列を引っ掛けてやれば簡単にできそうなので、早速スクリプトを作ってみた。
で、どう作ればいいのかというと/etc/logwatch配下にスクリプトを書いておけばOK。(LogWatchのインストールの仕方にもよると思うが)
自分の環境では、スクリプトファイルと設定ファイルを以下のように配置した。
/etc/logwatch/scripts/services/myapp
/etc/logwatch/conf/logfiles/myapp.conf
/etc/logwatch/conf/services/myapp.conf
conf/servicesにあるmyapp.confが設定ファイル。
中身は、ログのタイトルとログファイル設定を別にしているため設定ファイルの指定をしている。
conf/logfiles/myapp.confはログファイルの設定。
設定内容は対象ログファイルの場所と、ローテーションした場合の過去ファイルの形式を指定。
さらに、*ExpandRepeatsを指定して日付が変わって一回のログチェックで監視したいログファイルが分割されてもいいようにする。で、その動作を指定した時にはLogwatchがログレコードの日時を認識できなきゃいけないわけだが、*applyeurodateを指定しつつLogbackの日時の出力形式を%d{yyyy-MM-dd HH:mm:ss SSS}とする(秒とミリ秒の間にスペースをスペースで分割)ことでeurodate形式で読み込めるようにしている。
scripts/servicesのmyappがperlのスクリプト。conf/servicesの○○.confの名前といっしょにするのがポイントらしい。
中身はすごい単純。。。
以下ソース。
/etc/logwatch/scripts/services/myapp
/etc/logwatch/conf/logfiles/myapp.conf
/etc/logwatch/conf/services/myapp.conf
の順
Spring Frameworkでアプリ作るときのちょっとしたメモ
ニコニコ動画からランキング取得して独自のRSSを作り出すWebAPIとTumblrへのポストを行うアプリをJavaで作ってみた。
その時の構成と、どのように設定して動かしたかをメモ書き程度に記載。
構成
java EE6な時代にspringを採用したのは、FreeMaker、MyBatis、AspectJと簡単に連携できるためである。
MyBatisは発行するSQLを完全に制御したいから使ったのだが、チューニングが必要な複雑なクエリが出来上がらない前提であればJPA(glassfishならeclipse link)で良かったかも。JOINさえしなければ、簡単なCRUDなら何も書かなくていいJPAが圧倒的有利だった。
FreeMakerはなんとなく使用。JSFはメモリ食いすぎ。
H2Databaseは軽量な上、組み込みモードだと圧倒的パフォーマンスらしいのでglassfish上で動作させて使用。glassfishの管理コンソールからデータソースを登録するだけでアプリから簡単に使用出来る。
AspectJは必須wプロキシなAOPなんて軟弱なものじゃ物足りないw
ログはslf4jでまとめてlogbackに出力。
quartzについてはEJBタイマーを使っても良かったのだが、全部springに任せる構成を簡単に実現できたため使用した。
MyBatisの設定は/WEB-INF/mybatis.xmlに記述。DBのアンダースコア区切りをJavaのキャメルケースに変換してマッピングする設定は個人的に必須なので下記の設定をしたxmlファイルを作成。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>
glassfish管理のトランザクションを使うための設定と、マッパーを勝手にスキャンする設定、上記のMyBatisの設定ファイルを読ませる設定のために、springのbean構成を追記。
<tx:jta-transaction-manager /> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.neco_labo.db" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"><value>jdbc/myh2db</value></property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="/WEB-INF/mybatis.xml" /> <property name="transactionFactory"> <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" /> </property> </bean>
なんか突っ込みどころありそうな書き方だけど、気にしない!
MyBatisはアノテーションを使ってSQLを記述。
@Paramでパラメータ名を教えてあげないとダメな部分があるのがなんだかなぁ。別にJPA使っても良かったのか。
package com.neco_labo.db; import java.util.List; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import com.neco_labo.web.db.PostedVideo; public interface PostedVideoMapper { @Select("SELECT * FROM posted_video") List<PostedVideo> getAll(); @Select("SELECT * FROM posted_video WHERE type = #{type} AND id = #{id}") PostedVideo select(@Param("type")final String type, @Param("id")final String id); @Select("SELECT * FROM posted_video WHERE type = #{type}") List<PostedVideo> selectByType(final String type); @Insert("INSERT INTO posted_video (type,id,title,tag,rate,pub_date,insert_date,update_date) values (#{type},#{id},#{title},#{tag},#{rate},#{pubDate},#{insertDate},#{updateDate})") void insert(PostedVideo record); @Update("UPDATE posted_video SET rate = #{rate},update_date = #{updateDate} WHERE type = #{type} AND id = #{id}") void updateRate(PostedVideo record); }
さらに、AspectJでクラスロード時にせっせとバイトコード書き換えてくれるように設定。
コンポーネントスキャンで@Aspectがついてるクラスを勝手にbean登録してくれる設定って素晴らしい!CDI(weld)もデフォルトでそれに近いけどバイトコード書き換えは出来ないし。
<aop:aspectj-autoproxy/> <context:component-scan base-package="com.neco_labo"> <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" /> </context:component-scan> <context:load-time-weaver/>
context:load-time-weaverを指定したらglassfishの起動時のパラメータに-javaagent:spring-instrument.jarを追加。glassfishの管理コンソールで簡単に設定可能!
もちろん
アスペクトの記述クラスは、@Aspectをつけるだけでおk。先ほどそう設定したからな。その他、@Pointcutと@Aroundなどすべてアノテーションだけで完結。
AspectJのウィービング設定は"クラスパスが通っている場所"にMETA-INFを作り、その配下にaop.xmlとして配置。ポイントはweaverにアスペクト自身の場所も含めないといけない(なんでだろ)ようだ。
<?xml version="1.0"?> <aspectj> <aspects> <aspect name="com.neco_labo.aspect.JapanLocaleAspect" /> <aspect name="com.neco_labo.aspect.CDATAAspect" /> </aspects> <weaver> <include within="com.example.io..*" /> <include within="com.neco_labo.aspect.*" /> </weaver> </aspectj>
view周りのUI層はこんなかんじで。
FreeMakerのテンプレートのサフィックスをhtmlにして、FreeMakerのタグを[]で囲うようにすることによってEclipseで扱いやすく!あと、文字コードをきっちり指定しないと文字化けするらしい。
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> <mvc:annotation-driven /> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="cache" value="true"/> <property name="prefix" value=""/> <property name="suffix" value=".html"/> <property name="contentType" value="text/html;charset=UTF-8"/> <property name="exposeSpringMacroHelpers" value="true"/> </bean> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/view/" /> <property name="defaultEncoding" value="UTF-8" /> <property name="freemarkerSettings"> <props> <prop key="tag_syntax">square_bracket</prop> </props> </property> </bean>
あとはquartzを動かす設定。
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="myJobTrigger" /> </list> </property> </bean> <bean id="myJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="myJob" /> <property name="cronExpression" value="0 0 */6 * * ?" /> </bean> <import resource="job-config.xml"/> </beans>
xmlの肥大化を防ぐため、バッチジョブ設定を外部ファイル化。で、何故そんな中途半端な外部化をしているかというと、テスト用のコンテキスト設定と本番用を分けているのだが、ジョブ設定は同じファイルを見てほしいため。でも、実行間隔は環境毎に違う値にしたいよ?とか適当なことを思いついたため。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <bean id="myJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="MyJobExecutable" /> <property name="targetMethod" value="execute" /> <property name="concurrent" value="false" /> </bean> </beans>
ニコニコ動画からデータを引っ張ってきたり、RSSの出力をするのにはApache Httpclient 4, jackson, rome, jdom, jsoupを使用。
Apache Httpclientは普通のWebブラウザとほぼ同じようにWebサイトにアクセスできるので便利。リダイレクト、クッキー、gzipなどが透過的に扱えるため採用。
レスポンスに応じて、json=>jackson, xml=>jdom, html=>jsoupでパースして処理を行う独自ラッパーを作成して使用している。jdomはW3Cの仕様に準拠していない代わりに速くて簡単。
romeは各種RSSフォーマットで出力するのに便利なライブラリ。だが、CDATAセクションに対応していなかったり、ロケールがUSでハードコードなため(怒)、AspectJで(以下略)
そんなこんなで今日も動いています。
http://api.neco-labo.com/rss/harurun0403/
CentOS6でGlassFishを動かしてみた。
タイトルの通り、CentOS6でGlassFishを動かしてみた。
サービス起動スクリプトも作ったよ♡
早速だが、他のブログの情報を参考にインストール。
アンサーファイル作ってやればいいのね。
wget "http://download.java.net/glassfish/3.1.2/release/glassfish-3.1.2-unix-ml.sh" chmod u+x glassfish-3.1.2-unix-ml.sh ./glassfish-3.1.2-unix-ml.sh -a answer -s
インストール後、glassfish実行ユーザーを作成。ココらへんは抜かり無い(?)
useradd -M -s /sbin/nologin glassfish chown glassfish:glassfish -R /usr/local/glassfish
さて、"asadmin create-service domain1"でもサービスを作成出来るのだが起動スクリプトを自作してみた。
これでglassfishユーザーで起動するようになるぞ!
こいつを/etc/init.dの配下に置けばいいのね。
service glassfish start
これでサービスとして簡単に管理できるよ。やったね!
「Ext2Fsd」と「robocopy」でExt3→NTFSのバックアップ
たった今、NASが壊れて中身のデータを別のHDDにバックアップしたくなりました!oh,No...
で、NASってLinuxベースで作られててフォーマットもLinuxのものを使ってます。多分100%。
NASのHDDを直接繋げられても、Windowsマシンだけではどうにもならないですね。
そこで「Ext2Fsd」を使います。このツールは、LinuxのファイルシステムもWindowsで普通に認識してマウントが出来る便利ツールです!
Ext3とかでも空いてるボリュームラベルにセットして普通に中身が見れます。
http://www.ext2fsd.com/
WindowsからLinuxのファイルシステムを扱える「Ext2Fsd」 | マイナビニュース
ただ、文字コードだけは要注意。
ちょっと英語が読めれば特に使い方に困らないですが、
default設定から文字コードを間違えた状態(中身がUTF-8なのにSJISで読むとか)で
ファイル名に日本語を含むファイルがあるディレクトリに移動すると自分の環境ではクラッシュしました…。
NASだとだいたいUTF-8だと思う(多分)ので、適切なエンコードを指定しましょう。
あと管理者権限で起動していないとうまく動かないのも注意点?
ファイルコピーは間違ってもエクスプローラとかでやると後悔すると思われるので止めましょうw
特別なツールをネットから落とすまでもなく、コマンドプロンプトからrobocopyコマンドで簡単に出来ます。直に打ち込むのは大変なので、バッチファイルを作ってそこから実行しましょう。
下記サイトを参考にするといいです。
Windowsスマートチューニング(157) Win 7編: Robocopyコマンドを使用したバッチファイルの作成と使用方法 | マイナビニュース
http://www.upken.jp/kb/dZGLAzWdYNuFHRHVOWWhttCMiXPmYF.html