タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2014/04/26/VirtualBox 上の CentOS6 の GNOME で CapsLock がおかしい件が解決 | msakamoto-sf | 2014-04-26 19:48:27 |
日記/2014/04/26/業務系文書の自動生成についての予備調査(OIpenXML, OpenDocument, OpenOffice.org, LibreOffice) | msakamoto-sf | 2014-04-26 19:03:00 |
技術/OSSライセンス/勉強メモ | msakamoto-sf | 2014-04-26 18:18:24 |
日記/2014/04/20/牡蠣料理のメモ | msakamoto-sf | 2014-04-20 16:53:33 |
日記/2014/04/20/CheckstyleとかNetBeansとEclipseのフォーマットメモ | msakamoto-sf | 2014-04-20 14:57:34 |
日記/2014/04/14/flywayとApache DbUtilsの練習メモ | msakamoto-sf | 2014-04-14 03:09:36 |
日記/2014/04/13/ScheduledExecutorServiceの練習メモ | msakamoto-sf | 2014-04-13 14:23:40 |
日記/2014/04/07/Javaのconcurrentパッケージ勉強中・・・ | msakamoto-sf | 2014-04-07 00:08:11 |
Java/JSON Schema Validation | msakamoto-sf | 2014-02-24 01:43:41 |
日記/2014/02/18/JDKのCookie管理機能 | msakamoto-sf | 2014-02-18 23:46:59 |
技術/Linux/CentOS/VirtualBoxにインストールメモ(CentOS6, 2014-01版) でちらと書いた、CapsLockがおかしい件、色々調べたけどGNOMEの"Keyboard Layout Options"で"Caps Lock key behaviour"を "Default" から "Caps Lock toggles normal capitalization of alphabetic characters" にしたら直った。・・・何が悪かったのか・・・。
現象としてはこんな感じだったのです。
環境:
物理マシン : Win7 Pro SP1 日本語版 64bit版 キーボード : USB接続 FILCO Majestictouch テンキーレス ※特に物理マシン側でキーのmappingは変更してない VirtualBox : 4.3.6 VM : CentOS 6.5 x64版, GNOME
まず、runlevel=3のコマンドラインコンソールモードであればCapsLockについて全く問題はありませんでした。
runlevel=5で、GNOMEのX環境になると、以下の様な謎の挙動になりました。
また、GNOME上でも、CapsLockの切り替え後、1秒ほど待ってから入力を開始すればちゃんとCapsLockのON/OFFが反映されました。
取りこぼし(?)らしき現象が発生するのは、CapsLock切り替え後1秒未満の間に続けて入力を行う場合です。
一応、CapsLock切り替えで1秒以上待ってから入力を開始すれば問題は発生しないのですが、プログラミングをする上ではCapsLock切り替えでいちいち1秒以上またされるのはキツイです。
で、なかなか問題の切り分けが出来なかったのです。最初はVirtualBox側の問題かと思ったのですが、それらしき記事も見つからず、なによりrunlevel=3のCUIモードであれば全く問題は発生していなかったので、原因はX WindowsかGNOME側にアリそうでした。
試しにキーボードレイアウト設定でCapsLockのモード設定があったので、"Default"になってたのを、いくつか設定を切り替えてみたら、"Caps Lock toggles normal capitalization of alphabetic characters" で正常になった次第です。
他にもCapsLockの切り替えモードが色々用意されてるんですが、全部は試せておらず、とりあえずいくつか試してみて、通常使用時と同等で問題が解決できたのがたまたま上記の設定でした。
ちなみに、VMware Fusion でCapsLockの取りこぼしがある、というVMwareのKBが見つかったのですが、VirtualBoxですし、runlevel=3のCUIなら問題は発生しないので、違うようでした。
いわゆるワード文書やスプレッドシートを、ソフトウェアから自動生成するにはどんな方法があるかの調査メモです。
Javaから生成するのを念頭に置いてます。
そんなに突っ込んで調べてるわけではなくて、適当にググった結果の上位3-5くらいのURLを漁っただけの、浅い内容です。
Microsoft社提供のSDK:
JavaからOffice文書を生成するので有名なApachePOI関連:
OpenOffice.org と LibreOffice の両方で、マクロとして、あるいは外部プログラムからドキュメントを操作するためのAPIが公開されており、最近だとこちらを使ったほうが良いかもしれない。
どちらも、OpenOffice.org で開発されている "UNO" (Universal Netowrk Objects) という、RPCを複数言語でBINDしたようなアーキテクチャの上でAPIを公開しているため、JavaやPython、.NETなどから利用できるようになっている。
OpenOffice.orgでのAPIとUNO関連資料:
LibreOfficeでのAPIとUNO関連資料:
OpenOffice.orgはApacheライセンスですが、LibreOfficeはGPLv3という点に注意が必要かもしれません。
ちなみに、Webアプリなどから非同期でマルチスレッドで呼び出すような用途だとどうなるんだろう・・・と、"uno multithread"でぐぐったらこんなのが見つかりました。対応してるのか、してないのか、ちょっとまだ調べきれてないです。
OSSライセンスについて勉強したメモです。
最終的には日米間での著作権法の違いにまで話が及んでしまう「解釈」の問題になってしまう部分が多いので、特にGPLについてはそのソフトウェアの開発元に、「これこれこういう利用形態で使いたいんだけどライセンスどうなる?」と尋ねるのが一番確実なようです。
IPAによる調査資料:
OSC2009でのNECの人の発表資料:
OSSライセンスについて精力的に啓蒙活動を行われている可知豊氏のコンテンツ:
その他ライセンス毎の日本語訳やWikiページ、FAQ
よく見かけるライセンスについて:
※CDDL(Common Developement and Distribution License)は、GlassFish系のOSSで見かけた。
ヒントとなる単語:
「派生物 (Derived Work, Derivative Works)(CDDLでは「拡大制作物」"Larger Work")」・「リンク」・OOPでの継承やインターフェイスの実装
OSSを使って商用製品を開発する、あるいは、使っているOSSライブラリとは別のOSSライセンスを自分が作成したコードに適用する、等の場合に問題となるが、「派生物」の範囲となる。
なぜかというと、GPL系はもとより、MPL/EPL/CPL/CDDL系についても派生物の場合はそのソースコードを利用者が入手できるように、との記載があるためである。このため、自分達自身で作成したコードがどういった場合にOSSプログラムから見て派生物とみなされるのかは、死活問題となる。
例えば、少なくともGPLv2においては、GPLv2でライセンスされたライブラリに対して、動的・静的関わらず「リンク」しているプログラムにもGPLv2が適用されてしまうという強いコピーレフト性を持っている。非常に便利なGPLv2でライセンスされたOSSのライブラリがあるとして、これを商用製品の開発でリンクして用い、製品を独自のライセンスで提供したいケースでは、相当の注意が必要となる。
[BOLR2014]でも「2.1 伝播性のリスク」についてどこまでを派生物とみなすか、の説明がある。ただ面白いことに、その説明の中では特にライブラリのリンクについて Debian / RedHat / MySQL という比較的大きな開発組織のリーダやCTOで、それぞれ見解が異なっている。法的にも解釈の余地が残っている状態らしい。
他にも、GPLv2のFAQによると、2つのプログラムが結合され、事実上一つのプログラムの2つの部分となるなら、派生物とみなされるようだ。
http://ja.wikipedia.org/wiki/%E6%B4%BE%E7%94%9F%E7%89%A9
より確度の高い資料として以下がある。これはLGPLとJavaについて書かれたもので、結論としてはJavaの場合、ライブラリにアクセスするクライアント側のコードは、ライブラリの「派生物」とみなす、と書かれている。ただしLGPLの話なので、クライアント側のJavaコードについてはリバースエンジニアリングを許可するのであればLGPL以外のライセンスを適用できる、とも書かれている。
https://www.gnu.org/licenses/lgpl-java.html
一方で、EPL(Eclipse Public License)のFAQでは、以下の様な回答が記載されている。
For clarity, merely interfacing or interoperating with Eclipse plug-in APIs (without
modification) does not make an Eclipse plug-in a derivative work.
Eclipseは大部分がJavaで開発され、Eclipseプラグインはプラグイン用のAPIでEclipseと「リンク」していることになるが、単にインターフェイスを使っているだけであれば派生物とはみなされないようだ。
http://www.eclipse.org/legal/eplfaq.php#EXAMPLE
Javaについては以下も参照:
http://programmers.stackexchange.com/questions/224161/gpl-writing-an-exception-for-a-plugin-interface-under-section-7-of-gplv3
http://www.law.washington.edu/lta/swp/law/derivative.html
[BOLR2014]に戻ると、Javaのような動的リンクが行われるケースでは、標準インターフェイスを介しているかどうかが分かれ目のようである。
例えばServletAPIを使ったWebApplicationでは、確かに、デプロイされるコンテナがGPLで開発されたものであろうと、非GPLで開発されたものであろうと関係ない(コンテナに依存するような独自のAPIを利用するケースを除く)。
ASPサービスなど、ネットワーク経由で使われるソフトウェアの場合、利用者の手元にソフトウェアが「頒布」されるわけではない。
この場合、GPLについては利用者がソースコードを入手できるようにする必要はない。
これはGPLの抜け道として知られており、利用者へのソースコード公開を義務付けた、さらにコピーレフト性が強くなった
http://ja.wikipedia.org/wiki/Affero_General_Public_License
ASPサービスではGPLの抜け道を使って、GPLでライセンスされたライブラリを、独自開発のプログラムにリンクして実行し、ASPサービスとして利用してもらうことが可能だった。
しかし、クラウド利用で利用者に直接VMイメージをコピーして、利用者自身のクラウド環境で使用してもらうような場合、少なくともオブジェクトファイルのコピーが発生する、つまり「頒布」に相当すると考えられないだろうか?
そうなるとGPLの伝播性、コピーレフトの影響を考え無くてはならない、と思われる。
(調査中)
JavaScriptライブラリがGPLでライセンスされていた場合、そのライブラリを使ったJavaScriptもGPLになるのだろうか?
そのJavaScriptをscirptタグでロードするHTMLページをレンダリングするサーバサイドプログラムもGPLになるのだろうか?
観点1 : JavaScriptはブラウザ上にDLされて実行されるとすれば、「頒布」に相当するのか?
観点2 : JavaScriptライブラリをscriptタグでロードしているHTMLファイルは、そのJavaScriptライブラリにリンクしている派生物とみなされるのか?
観点3 : 上記HTMLファイルをサーバサイドで生成している場合、サーバサイドのプログラムも、そのJavaScriptライブラリが無ければ製品として成立しないようなケースではリンクしている派生物とみなされるのか?
参考:
2月ごろに殻付きの牡蠣をもらって、料理したのでその時のメモです。cookpadの参考URLなど。
牡蠣
マリネにしてみる:
牡蠣 適量 エキストラバージンオリーブオイル 適量 にんにく1~2かけ オリーブ(緑・黒)各適量 酢・塩・こしょう 各少々 ローズマリー 少々
片栗粉で洗ったあと炒め煮して、オイスターソースで風味漬けするバリエーション:
github : https://github.com/msakamoto-sf/javasnack/commit/dbf37b66465ab2dbf7228178c60dbc4bb334dc6c
Apache DbUtils:
http://commons.apache.org/proper/commons-dbutils/apidocs/
http://d.hatena.ne.jp/nodchip/20120211/1328952012
http://d.hatena.ne.jp/Kishi/20081212/1229076102
http://commons.apache.org/proper/commons-dbutils/examples.html
flywaydb:
http://flywaydb.org/
https://github.com/flyway/flyway
http://flywaydb.org/documentation/javadoc/
http://siguniang.wordpress.com/2013/11/10/db-schema-migration-made-easy-with-flyway/
http://blog.livedoor.jp/ryu22e/archives/65722084.html
練習用メモ。
AlarmListener:
package timer; public interface AlarmListener { public void onAlarm(); }
PeriodicAlarm:
package timer; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** http://www.02.246.ne.jp/~torutk/javahow2/timer.html#doc1_id120 http://d.hatena.ne.jp/AWAWA/20080423/1208955538 http://www.ibm.com/developerworks/jp/java/library/j-5things5.html */ public class PeriodicAlarm implements Runnable { ScheduledFuture future; ScheduledExecutorService executorService; final Set<AlarmListener> listeners = new HashSet<>(); final long initialDelay; final long period; final TimeUnit timeUnit; public PeriodicAlarm(long initialDelay, long period, TimeUnit timeUnit) { this.executorService = Executors.newScheduledThreadPool(1); this.initialDelay = initialDelay; this.period = period; this.timeUnit = timeUnit; } public void start() { this.future = this.executorService.scheduleAtFixedRate(this, this.initialDelay, this.period, this.timeUnit); } public void stop() { this.future.cancel(true); } public void shutdown() { this.stop(); this.executorService.shutdown(); } public void addAlarmListener(AlarmListener l) { synchronized (listeners) { listeners.add(l); } } public void removeAlarmListener(AlarmListener l) { synchronized (listeners) { listeners.remove(l); } } @Override public void run() { synchronized (listeners) { for (AlarmListener l : this.listeners) { l.onAlarm(); } } } }
PeriodicAlarmTest:
import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.concurrent.TimeUnit; import static org.testng.Assert.*; import org.testng.annotations.Test; import timer.AlarmListener; import timer.PeriodicAlarm; public class PeriodicAlarmTest { public PeriodicAlarmTest() { } class Countup implements AlarmListener { String name; int count = 0; final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss.SSS"); public Countup(String name) { this.name = name; } @Override public void onAlarm() { Calendar c = Calendar.getInstance(); String snow; synchronized (this.sdf) { snow = sdf.format(c.getTime()); } count++; System.out.println("[" + this.name + "]:now=" + snow + ", count=" + count); } } @Test public void hello() throws InterruptedException { PeriodicAlarm a = new PeriodicAlarm(0, 1, TimeUnit.SECONDS); a.start(); Countup c1 = new Countup("c1"); a.addAlarmListener(c1); Thread.sleep(2 * 1000); Countup c2 = new Countup("c2"); a.addAlarmListener(c2); Thread.sleep(2 * 1000); Countup c3 = new Countup("c3"); a.addAlarmListener(c3); Thread.sleep(2 * 1000); a.shutdown(); assertEquals(c1.count, 6); assertEquals(c2.count, 4); assertEquals(c3.count, 2); } }
日付変わってしまったけど。勉強中の練習プログラム。TestNG使ったテストケースで。
一応、ドメイン限定で、非同期ジョブ制御用のAPIに挑戦しようと思ってるので、使ってないメソッドとかも想像上のものとして残してたり。
HogeTest.java:
package hoge; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import org.testng.annotations.Test; /** * * @author FengJing */ public class HogeTest { public HogeTest() { } class JobMailBox<T> extends LinkedBlockingQueue<T> { protected boolean isShutdown = false; public void shutdown() { this.isShutdown = true; } @Override public boolean offer(T e) { if (this.isShutdown) { return false; } return super.offer(e); } @Override public boolean offer(T e, long timeout, TimeUnit unit) throws InterruptedException { if (this.isShutdown) { return false; } return super.offer(e, timeout, unit); } @Override public void put(T e) throws InterruptedException { if (this.isShutdown) { return; } super.put(e); } @Override public T poll(long timeout, TimeUnit unit) throws InterruptedException { return super.poll(timeout, unit); } @Override public T take() throws InterruptedException { return super.take(); } } enum JobEvent { STARTED, RUNNING, CANCELLED, TERMINATED; } class JobSignal { public final JobEvent event; public final String key; public final String message; public JobSignal(JobEvent event, String key, String message) { this.event = event; this.key = key; this.message = message; } } interface JobHandle<R, M> { public R main(BlockingQueue<M> mailbox) throws Exception; } class JobButler { protected ExecutorService executorService; protected Map<String, Future> jobs = new ConcurrentHashMap<String, Future>(); protected Map<String, BlockingQueue> mailboxes = new ConcurrentHashMap<String, BlockingQueue>(); protected final JobMailBox<JobSignal> butlerMailBox = new JobMailBox<>(); public JobButler(ExecutorService executorService) { this.executorService = executorService; } public JobMailBox<JobSignal> getButlersMailBox() { return this.butlerMailBox; } public <R, M> Future<R> submitNewJob(final String key, final JobHandle<R, M> job) throws InterruptedException { final BlockingQueue<M> mailbox = new ArrayBlockingQueue<M>(100); Callable<R> c = new Callable<R>() { public R call() throws Exception { butlerMailBox.put(new JobSignal(JobEvent.RUNNING, key, "running")); R result = job.main(mailbox); butlerMailBox.put(new JobSignal(JobEvent.TERMINATED, key, "terminated")); return result; } }; butlerMailBox.put(new JobSignal(JobEvent.STARTED, key, "started")); Future<R> f = this.executorService.submit(c); this.jobs.put(key, f); this.mailboxes.put(key, mailbox); return f; } public Future getFuture(String key) { return this.jobs.get(key); } public Future terminateJob(String key) { Future f = this.jobs.get(key); if (f.isCancelled() || f.isDone()) { return f; } f.cancel(true); return f; } public void shutdown() { this.executorService.shutdown(); } } class HogeJob implements JobHandle<String, String> { private final String name; public HogeJob(String name) { this.name = name; } public String main(BlockingQueue<String> mailbox) throws Exception { StringBuilder sb = new StringBuilder(1024); sb.append(this.name); sb.append(":"); for (int i = 0; i < 10; i++) { sb.append(i); } return sb.toString(); } } class CountJob implements JobHandle<Integer, String> { private int sum = 0; public Integer main(BlockingQueue<String> mailbox) throws Exception { for (int i = 0; i < 10; i++) { this.sum += i; } return new Integer(this.sum); } } @Test public void hoge2() throws InterruptedException, ExecutionException { JobButler jb = new JobButler(Executors.newCachedThreadPool()); Future<String> f1 = jb.submitNewJob("job1", new HogeJob("jon")); Future<String> f2 = jb.submitNewJob("job2", new HogeJob("bob")); Future<Integer> f3 = jb.submitNewJob("job3", new CountJob()); assertEquals(f1.get(), "jon:0123456789"); assertEquals(f2.get(), "bob:0123456789"); assertEquals(f3.get(), new Integer(45)); jb.shutdown(); } class SlowCountJob implements JobHandle<Integer, String> { private int sum = 0; private final String name; public SlowCountJob(String name) { this.name = name; } @Override public Integer main(BlockingQueue<String> mailbox) throws Exception { int i = 0; while (!"terminate".equals(mailbox.poll(1000, TimeUnit.MILLISECONDS))) { this.sum += i; i++; System.out.println(this.name + ":" + this.sum); } return new Integer(this.sum); } } @Test(expectedExceptions = CancellationException.class) public void hoge3() throws InterruptedException, ExecutionException, TimeoutException { JobButler jb = new JobButler(Executors.newCachedThreadPool()); Future<Integer> f1 = jb.submitNewJob("job1", new SlowCountJob("scj-1")); try { Thread.sleep(5000); } catch (InterruptedException ignore) { } // cancel flag was set, but not interrupted ... orphan thread!!! f1.cancel(false); assertTrue(f1.isCancelled()); assertTrue(f1.isDone()); try { f1.get(1, TimeUnit.SECONDS); } finally { jb.shutdown(); } } @Test(expectedExceptions = CancellationException.class) public void hoge4() throws InterruptedException, ExecutionException, TimeoutException { JobButler jb = new JobButler(Executors.newCachedThreadPool()); Future<Integer> f1 = jb.submitNewJob("job1", new SlowCountJob("scj-2")); try { Thread.sleep(5000); } catch (InterruptedException ignore) { } // cancel flag was set, and, inerrupted -> thread terminates. f1.cancel(true); assertTrue(f1.isCancelled()); assertTrue(f1.isDone()); try { f1.get(1, TimeUnit.SECONDS); } finally { jb.shutdown(); } } class AbnormalEndCountJob implements JobHandle<Integer, String> { private int sum = 0; private final String name; private final int stopper; public AbnormalEndCountJob(String name, int stopper) { this.name = name; this.stopper = stopper; } @Override public Integer main(BlockingQueue<String> mailbox) throws Exception { int i = 0; while (!"terminate".equals(mailbox.poll(1000, TimeUnit.MILLISECONDS))) { this.sum += i; i++; System.out.println(this.name + ":" + this.sum); if (i > stopper) { throw new IllegalStateException("stopper threashold over."); } } return new Integer(this.sum); } } @Test(expectedExceptions = IllegalStateException.class) public void hoge5() throws InterruptedException, ExecutionException, TimeoutException, Throwable { JobButler jb = new JobButler(Executors.newCachedThreadPool()); Future<Integer> f1 = jb.submitNewJob("job1", new AbnormalEndCountJob("abend-job-1", 3)); try { Thread.sleep(5000); } catch (InterruptedException ignore) { } // job is already done (stopper = 3 means "After 3 seconds, throw IllegalStateException") // so, cancel() does not effect any more. isCancelled() returns false. f1.cancel(true); assertFalse(f1.isCancelled()); assertTrue(f1.isDone()); try { f1.get(1, TimeUnit.SECONDS); } catch (ExecutionException expected) { Throwable t = expected.getCause(); assertEquals(t.getMessage(), "stopper threashold over."); throw t; } finally { jb.shutdown(); } } @Test public void hoge() throws InterruptedException, ExecutionException { ExecutorService es = Executors.newCachedThreadPool(); Callable<String> c = new Callable<String>() { public String call() throws Exception { StringBuilder sb = new StringBuilder(1024); for (int i = 0; i < 10; i++) { // Thread.sleep(1000); sb.append(i); } return sb.toString(); } }; Future<String> f = es.submit(c); assertEquals(f.get(), "0123456789"); } }
BoheTest.java:
package hoge; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import org.testng.annotations.Test; /** * * @author FengJing */ public class BoheTest { public BoheTest() { } class JobMailBox<T> extends LinkedBlockingQueue<T> { protected boolean isShutdown = false; public void shutdown() { this.isShutdown = true; } @Override public boolean offer(T e) { if (this.isShutdown) { return false; } return super.offer(e); } @Override public boolean offer(T e, long timeout, TimeUnit unit) throws InterruptedException { if (this.isShutdown) { return false; } return super.offer(e, timeout, unit); } @Override public void put(T e) throws InterruptedException { if (this.isShutdown) { return; } super.put(e); } @Override public T poll(long timeout, TimeUnit unit) throws InterruptedException { return super.poll(timeout, unit); } @Override public T take() throws InterruptedException { return super.take(); } } interface JobHandle<R, M> { public R main(BlockingQueue<M> mailbox) throws Exception; } class JobTask<V> extends FutureTask<V> { protected final String key; protected final JobButler butler; public JobTask(JobButler butler, String key, Callable<V> callable) { super(callable); this.key = key; this.butler = butler; } public JobTask(JobButler butler, String key, Runnable runnable, V result) { super(runnable, result); this.key = key; this.butler = butler; } @Override protected void done() { butler.notifyJobTermination(key); } } class JobGateway<R, M> { final Future<R> future; final JobMailBox<M> mailbox; public JobGateway(Future<R> f, JobMailBox<M> mb) { this.future = f; this.mailbox = mb; } } class JobButler { protected ExecutorService executorService; protected Map<String, JobGateway> jobs = new ConcurrentHashMap<>(); public JobButler(ExecutorService executorService) { this.executorService = executorService; } public synchronized <R, M> JobGateway<R, M> submitNewJob(final String key, final JobHandle<R, M> job) throws InterruptedException { final JobMailBox<M> mailbox = new JobMailBox<>(); JobTask<R> task = new JobTask<>(this, key, new Callable<R>() { @Override public R call() throws Exception { R result = job.main(mailbox); return result; } }); this.executorService.execute(task); JobGateway<R, M> jobgw = new JobGateway(task, mailbox); this.jobs.put(key, jobgw); return jobgw; } public synchronized JobGateway getJobGateway(String key) { return this.jobs.get(key); } synchronized void notifyJobTermination(String key) { if (!this.jobs.containsKey(key)) { return; } JobGateway jobgw = this.jobs.get(key); jobgw.mailbox.shutdown(); this.jobs.remove(key); } public void shutdown() { this.executorService.shutdown(); } } class HogeJob implements JobHandle<String, String> { private final String name; public HogeJob(String name) { this.name = name; } @Override public String main(BlockingQueue<String> mailbox) throws Exception { StringBuilder sb = new StringBuilder(1024); sb.append(this.name); sb.append(":"); for (int i = 0; i < 10; i++) { sb.append(i); } return sb.toString(); } } class CountJob implements JobHandle<Integer, String> { private int sum = 0; @Override public Integer main(BlockingQueue<String> mailbox) throws Exception { for (int i = 0; i < 10; i++) { this.sum += i; } return new Integer(this.sum); } } @Test public void hoge2() throws InterruptedException, ExecutionException { JobButler jb = new JobButler(Executors.newCachedThreadPool()); JobGateway<String, String> jgw1 = jb.submitNewJob("job1", new HogeJob("jon")); JobGateway<String, String> jgw2 = jb.submitNewJob("job2", new HogeJob("bob")); JobGateway<Integer, String> jgw3 = jb.submitNewJob("job3", new CountJob()); assertEquals(jgw1.future.get(), "jon:0123456789"); assertEquals(jgw2.future.get(), "bob:0123456789"); assertEquals(jgw3.future.get(), new Integer(45)); assertTrue(jgw1.future.isDone()); assertTrue(jgw2.future.isDone()); assertTrue(jgw3.future.isDone()); assertNull(jb.getJobGateway("job1")); assertNull(jb.getJobGateway("job2")); assertNull(jb.getJobGateway("job3")); jb.shutdown(); } class SlowCountJob implements JobHandle<Integer, String> { private int sum = 0; private final String name; public SlowCountJob(String name) { this.name = name; } @Override public Integer main(BlockingQueue<String> mailbox) throws Exception { int i = 0; while (!"terminate".equals(mailbox.poll(1000, TimeUnit.MILLISECONDS))) { this.sum += i; i++; System.out.println(this.name + ":" + this.sum); } return new Integer(this.sum); } } @Test(expectedExceptions = CancellationException.class) public void hoge3() throws InterruptedException, ExecutionException, TimeoutException { JobButler jb = new JobButler(Executors.newCachedThreadPool()); JobGateway<Integer, String> jgw = jb.submitNewJob("job1", new SlowCountJob("scj-1")); try { Thread.sleep(5000); } catch (InterruptedException ignore) { } // cancel flag was set, but not interrupted ... orphan thread!!! jgw.future.cancel(false); assertTrue(jgw.future.isCancelled()); assertTrue(jgw.future.isDone()); try { jgw.future.get(1, TimeUnit.SECONDS); } finally { // cancel(false) internally called done(). assertNull(jb.getJobGateway("job1")); jb.shutdown(); } } @Test(expectedExceptions = CancellationException.class) public void hoge4() throws InterruptedException, ExecutionException, TimeoutException { JobButler jb = new JobButler(Executors.newCachedThreadPool()); JobGateway<Integer, String> jgw = jb.submitNewJob("job1", new SlowCountJob("scj-2")); try { Thread.sleep(5000); } catch (InterruptedException ignore) { } // cancel flag was set, and, inerrupted -> thread terminates. jgw.future.cancel(true); assertTrue(jgw.future.isCancelled()); assertTrue(jgw.future.isDone()); try { jgw.future.get(1, TimeUnit.SECONDS); } finally { assertNull(jb.getJobGateway("job1")); jb.shutdown(); } } class AbnormalEndCountJob implements JobHandle<Integer, String> { private int sum = 0; private final String name; private final int stopper; public AbnormalEndCountJob(String name, int stopper) { this.name = name; this.stopper = stopper; } @Override public Integer main(BlockingQueue<String> mailbox) throws Exception { int i = 0; while (!"terminate".equals(mailbox.poll(1000, TimeUnit.MILLISECONDS))) { this.sum += i; i++; System.out.println(this.name + ":" + this.sum); if (i > stopper) { throw new IllegalStateException("stopper threashold over."); } } return new Integer(this.sum); } } @Test(expectedExceptions = IllegalStateException.class) public void hoge5() throws InterruptedException, ExecutionException, TimeoutException, Throwable { JobButler jb = new JobButler(Executors.newCachedThreadPool()); JobGateway<Integer, String> jgw = jb.submitNewJob("job1", new AbnormalEndCountJob("abend-job-1", 3)); try { Thread.sleep(5000); } catch (InterruptedException ignore) { } // job is already done (stopper = 3 means "After 3 seconds, throw IllegalStateException") // so, cancel() does not effect any more. isCancelled() returns false. jgw.future.cancel(true); assertFalse(jgw.future.isCancelled()); assertTrue(jgw.future.isDone()); try { jgw.future.get(1, TimeUnit.SECONDS); } catch (ExecutionException expected) { Throwable t = expected.getCause(); assertEquals(t.getMessage(), "stopper threashold over."); throw t; } finally { assertNull(jb.getJobGateway("job1")); jb.shutdown(); } } class TypicalMessageLoopJob implements JobHandle<String, String> { @Override public String main(BlockingQueue<String> mailbox) throws Exception { StringBuilder sb = new StringBuilder(1024); while (true) { String message = mailbox.poll(); if ("__END__".equals(message)) { break; } sb.append(message); } return sb.toString(); } } @Test public void hoge6() throws InterruptedException, ExecutionException, TimeoutException, Throwable { JobButler jb = new JobButler(Executors.newCachedThreadPool()); JobGateway<String, String> jgw = jb.submitNewJob("job1", new TypicalMessageLoopJob()); jgw.mailbox.put("hello, "); jgw.mailbox.put("bob. "); jgw.mailbox.put("Good "); jgw.mailbox.put("Morning!"); jgw.mailbox.put("__END__"); jgw.mailbox.put("(this message must be ignored."); assertEquals(jgw.future.get(), "hello, bob. Good Morning!"); assertFalse(jgw.future.isCancelled()); assertTrue(jgw.future.isDone()); try { Thread.sleep(1000); } catch (InterruptedException ignore) {} assertNull(jb.getJobGateway("job1")); jb.shutdown(); } }
BoheTest.javaの方が一応、洗練されてるつもり。
メモ書きです。
JSON Schemaを定義して、JSONの構造が正しいかチェックできる仕組みのようです。
java.net.CookieHandler, CookieManager, CookieStore, HttpCookieなど、JDK7になって必要なインターフェイス・クラスがひと通り公開された。JDK6以前は、ここまで自由に触れなかったっぽい。
https://blogs.oracle.com/CoreJavaTechTips/entry/cookie_handling_in_java_se
http://docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/accessingCookies.html
http://docs.oracle.com/javase/jp/7/api/java/net/CookieManager.html
JDK6時代のCookieManagerには謎の挙動やバグもあった。JDK7になって大体修正されたようだ。
http://tomott.cocolog-nifty.com/blog/2010/08/javecookie1-87d.html
http://0xc000013a.blog96.fc2.com/blog-entry-131.html
http://itpro.nikkeibp.co.jp/article/COLUMN/20070801/278817/
試しにJDK7のjava.net.HttpCookieのparse処理のコードとかを読んでみると、色々と歴史的な部分への細かい対処が詰め込まれていて、勉強になります、というか、自分で実装する気力が無くなりました。