=== リンク ===
-・http://mercurial.selenic.com/
-・http://mercurial.selenic.com/wiki/.hgignore
-・http://www.selenic.com/mercurial/hgignore.5.html
-・http://mercurial.selenic.com/wiki/EolExtension
+・https://www.mercurial-scm.org/
+・https://www.mercurial-scm.org/wiki/.hgignore
+・https://www.selenic.com/mercurial/hgignore.5.html
+・https://www.mercurial-scm.org/wiki/EolExtension
・http://tortoisehg.bitbucket.org/
<dependency>
<groupId>jp.sourceforge.jindolf</groupId>
<artifactId>jinparser</artifactId>
- <version>1.408.4</version>
+ <version>1.408.5-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
--- /dev/null
+/*
+ * XML-dump task
+ *
+ * License : The MIT License
+ * Copyright(c) 2016 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.Callable;
+
+/**
+ * XML出力タスク。
+ */
+public class DumpXmlTask implements Callable<Void> {
+
+ private static final String ERR_IO = "出力エラーが発生しました";
+ private static final String ERR_ABORT = "XML出力が中断されました";
+
+
+ private final VillageData villageData;
+ private final Writer writer;
+
+
+ /**
+ * コンストラクタ。
+ * @param villageData 村情報
+ * @param writer 出力先
+ */
+ public DumpXmlTask(VillageData villageData, Writer writer){
+ super();
+ this.villageData = villageData;
+ this.writer = writer;
+ return;
+ }
+
+
+ /**
+ * 例外に応じたエラー説明を返す。
+ * @param cause 例外
+ * @return エラー説明
+ */
+ public static String getErrDescription(Throwable cause){
+ String desc;
+ if(cause instanceof IOException) desc = ERR_IO;
+ else desc = ERR_ABORT;
+ return desc;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ * @return null
+ * @throws IOException 出力エラー
+ */
+ @Override
+ public Void call() throws IOException{
+ try{
+ XmlUtils.dumpVillageData(this.writer, this.villageData);
+ }finally{
+ this.writer.close();
+ }
+
+ return null;
+ }
+
+}
package jp.sourceforge.jindolf.archiver;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.io.Reader;
import java.io.Writer;
import java.text.MessageFormat;
import java.util.List;
import java.util.Properties;
+import javax.xml.validation.Validator;
import jp.sourceforge.jindolf.corelib.LandDef;
import jp.sourceforge.jindolf.parser.DecodeException;
import jp.sourceforge.jindolf.parser.HtmlParseException;
+import org.xml.sax.SAXException;
/**
* メインエントリ。
writer = getStdOutWriter();
}
- writer = ValidateTask.wrapValidator(writer);
+ SnifWriter snifWriter = new SnifWriter(writer);
+ Reader reader = snifWriter.getSnifReader();
+ writer = new BufferedWriter(snifWriter);
+ reader = new BufferedReader(reader);
+
+ Validator validator;
try{
- dump(writer, landDef, vid);
+ validator = XmlUtils.createValidator();
+ }catch(SAXException e){
+ abortWithException(e, "処理を続行できません。");
+ return;
+ }
+
+ VillageData villageData;
+ try{
+ villageData = load(landDef, vid);
}catch(IOException e){
abortWithException(e);
+ return;
}catch(DecodeException e){
abortWithException(e);
+ return;
}catch(HtmlParseException e){
abortWithException(e);
+ return;
+ }
+
+ ValidateTask valTask = new ValidateTask(reader, validator);
+ DumpXmlTask dumpTask = new DumpXmlTask(villageData, writer);
+
+ ProdCons taskman = new ProdCons(dumpTask, valTask);
+ try{
+ taskman.submit();
+ }catch(InterruptedException e){
+ abortWithException(e);
}
+ if(taskman.hasError()){
+ Throwable cause = taskman.getCause();
+ String desc = taskman.getErrDescription();
+ abortWithException(cause, desc);
+ assert false;
+ }
+
+ return;
+ }
+
+ /**
+ * 例外によるアプリ終了。
+ * @param e 例外
+ */
+ private static void abortWithException(Throwable e){
+ abortWithException(e, "処理を続行できません。");
+ exit(1);
return;
}
/**
* 例外によるアプリ終了。
* @param e 例外
+ * @param desc 詳細テキスト
*/
- private static void abortWithException(Exception e){
+ private static void abortWithException(Throwable e, String desc){
e.printStackTrace(System.err);
- errprintln("処理を続行できません。");
+ errprintln(desc);
exit(1);
return;
}
/**
* 主処理。人狼サーバからXHTMLを読み込み。XMLで出力。
- * @param writer 出力先
* @param landDef 国情報
* @param vid 村番号
+ * @return 村情報
* @throws IOException 入出力エラー
* @throws DecodeException デコードエラー
* @throws HtmlParseException パースエラー
*/
- public static void dump(Writer writer, LandDef landDef, int vid)
+ public static VillageData load(LandDef landDef, int vid)
throws IOException, DecodeException, HtmlParseException{
List<PeriodResource> resourceList =
HttpAccess.loadResourceList(landDef, vid);
- VillageData village = new VillageData(resourceList);
+ VillageData villageData = new VillageData(resourceList);
- Builder.fillVillageData(village);
- XmlUtils.dumpVillageData(writer, village);
+ Builder.fillVillageData(villageData);
- return;
+ return villageData;
}
/**
+++ /dev/null
-/*
- * Multiplex Writer
- *
- * License : The MIT License
- * Copyright(c) 2008 olyutorskii
- */
-
-package jp.sourceforge.jindolf.archiver;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Writerのマルチプレクサ。
- */
-public class MultiPlexer extends Writer{
-
- private final List<Writer> childs = new LinkedList<Writer>();
-
- /**
- * コンストラクタ。
- */
- public MultiPlexer(){
- this(null);
- return;
- }
-
- /**
- * コンストラクタ。
- * @param writer 初期Writer
- */
- public MultiPlexer(Writer writer){
- super();
-
- if(writer != null){
- this.childs.add(writer);
- }
-
- return;
- }
-
- /**
- * Writerを追加する。
- * @param writer 追加するWriter。nullなら無視。
- */
- public void addWriter(Writer writer){
- if(writer == null) return;
- this.childs.add(writer);
- return;
- }
-
- /**
- * 出力するWriterの一覧を得る。
- * @return Writer一覧。
- */
- public List<Writer> getWriterList(){
- return Collections.unmodifiableList(this.childs);
- }
-
- /**
- * {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void close() throws IOException{
- for(Writer writer : this.childs){
- writer.close();
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void flush() throws IOException{
- for(Writer writer : this.childs){
- writer.flush();
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param cbuf {@inheritDoc}
- * @param off {@inheritDoc}
- * @param len {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void write(char[] cbuf, int off, int len) throws IOException{
- for(Writer writer : this.childs){
- writer.write(cbuf, off, len);
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param csq {@inheritDoc}
- * @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public Writer append(CharSequence csq) throws IOException{
- for(Writer writer : this.childs){
- writer.append(csq);
- }
- return this;
- }
-
- /**
- * {@inheritDoc}
- * @param csq {@inheritDoc}
- * @param start {@inheritDoc}
- * @param end {@inheritDoc}
- * @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public Writer append(CharSequence csq, int start, int end)
- throws IOException{
- for(Writer writer : this.childs){
- writer.append(csq, start, end);
- }
- return this;
- }
-
- /**
- * {@inheritDoc}
- * @param c {@inheritDoc}
- * @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public Writer append(char c) throws IOException{
- for(Writer writer : this.childs){
- writer.append(c);
- }
- return this;
- }
-
- /**
- * {@inheritDoc}
- * @param c {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void write(int c) throws IOException{
- for(Writer writer : this.childs){
- writer.write(c);
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param cbuf {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void write(char[] cbuf) throws IOException{
- for(Writer writer : this.childs){
- writer.write(cbuf);
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param str {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void write(String str) throws IOException{
- for(Writer writer : this.childs){
- writer.write(str);
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param str {@inheritDoc}
- * @param off {@inheritDoc}
- * @param len {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- public void write(String str, int off, int len) throws IOException{
- for(Writer writer : this.childs){
- writer.write(str, off, len);
- }
- return;
- }
-
-}
/**
* オプション情報。
- * <p>
- * ヘルプ指定がある場合、他の情報は無効。
+ *
+ * <p>ヘルプ指定がある場合、他の情報は無効。
* ヘルプ指定が無くエラー情報がある場合、他の情報は無効。
*/
public final class OptInfo{
return true;
}
- /**
- * エラーの有無を返す。
- * @return エラー情報があればtrue
- */
- public boolean hasError(){
- if(this.isHelp) return false;
- if(this.errMsg == null) return false;
-
- return true;
- }
-
}
--- /dev/null
+/*
+ * producer-consumer tasks manager
+ *
+ * License : The MIT License
+ * Copyright(c) 2016 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * Producer-Consumer モデルでのタスク管理。
+ * 生産者タスクと消費者タスク間での異常系のリカバリを適切に行う。
+ */
+public class ProdCons{
+
+ private static final int NUM_THREADS = 2;
+
+
+ private final CompletionService<Void> service;
+
+ private final DumpXmlTask dumpTask;
+ private final ValidateTask validateTask;
+
+ private Future<Void> dumpFuture;
+ private Future<Void> validFuture;
+
+ private boolean endValidator1st;
+
+ private Throwable cause = null;
+ private Callable<Void> causeTask = null;
+
+
+ /**
+ * コンストラクタ。
+ * @param dumpTask XML出力タスク
+ * @param validateTask XML検証タスク
+ */
+ public ProdCons(DumpXmlTask dumpTask, ValidateTask validateTask){
+ super();
+ this.service = createService();
+ this.dumpTask = dumpTask;
+ this.validateTask = validateTask;
+ return;
+ }
+
+
+ /**
+ * 2スレッド専用サービスを生成する。
+ * @return サービス
+ */
+ private static CompletionService<Void> createService(){
+ Executor executor = Executors.newFixedThreadPool(NUM_THREADS);
+ CompletionService<Void> service =
+ new ExecutorCompletionService<>(executor);
+ return service;
+ }
+
+ /**
+ * 終了タスクの失敗原因を得る。
+ * @param future タスク
+ * @return 失敗原因
+ * @throws IllegalStateException 終了していない。
+ */
+ private static Throwable getReason(Future<?> future)
+ throws IllegalStateException{
+ if(! future.isDone()) throw new IllegalStateException();
+
+ Throwable result = null;
+
+ try{
+ Object dummyNull = future.get();
+ assert dummyNull == null;
+ }catch(InterruptedException e){
+ assert false;
+ throw new IllegalStateException(e);
+ }catch(ExecutionException e){
+ result = e.getCause();
+ }
+
+ return result;
+ }
+
+
+ /**
+ * タスク投入。
+ * @throws InterruptedException 割り込まれた。※ありえない
+ */
+ public void submit() throws InterruptedException{
+ // consumer first
+ this.validFuture = this.service.submit(this.validateTask);
+ this.dumpFuture = this.service.submit(this.dumpTask);
+
+ waitTask1st();
+ waitTask2nd();
+
+ return;
+ }
+
+ /**
+ * 最初のタスク完了を待つ。
+ * @throws InterruptedException 割り込まれた
+ */
+ private void waitTask1st() throws InterruptedException{
+ Future<Void> future1st;
+ future1st = this.service.take();
+
+ if(future1st == this.validFuture) this.endValidator1st = true;
+ else this.endValidator1st = false;
+
+ this.cause = getReason(future1st);
+ if(this.cause != null){
+ if(this.endValidator1st) this.causeTask = this.validateTask;
+ else this.causeTask = this.dumpTask;
+ }
+
+ return;
+ }
+
+ /**
+ * 最後のタスク完了を待つ。
+ * 最初のタスクで既に異常系が進行している場合、
+ * 最後のタスクはキャンセルされる。
+ * @throws InterruptedException 割り込まれた
+ */
+ private void waitTask2nd() throws InterruptedException{
+ if(this.cause != null){
+ cancel2ndTask();
+ return;
+ }
+
+ Future<?> future2nd = this.service.take();
+
+ this.cause = getReason(future2nd);
+ if(this.cause != null){
+ if(this.endValidator1st) this.causeTask = this.dumpTask;
+ else this.causeTask = this.validateTask;
+ }
+
+ return;
+ }
+
+ /**
+ * 最後のタスクをキャンセルする。
+ * 最初のタスクの異常系は確定しているため、
+ * 最後のタスクの異常系は無視される。
+ * @throws InterruptedException 割り込まれた
+ */
+ private void cancel2ndTask() throws InterruptedException{
+ Future<Void> future2nd;
+ if(this.endValidator1st) future2nd = this.dumpFuture;
+ else future2nd = this.validFuture;
+
+ boolean useInterrupt = true;
+ future2nd.cancel(useInterrupt);
+
+ Future<Void> future = this.service.take();
+ assert future == future2nd;
+
+ // ignore error with cancel
+
+ return;
+ }
+
+ /**
+ * 異常系が発生しているか判定する。
+ * @return 異常系ならtrue
+ */
+ public boolean hasError(){
+ boolean result;
+ result = this.cause != null;
+ return result;
+ }
+
+ /**
+ * 異常系を引き起こした例外を返す。
+ * @return 例外。無ければnull
+ */
+ public Throwable getCause(){
+ return this.cause;
+ }
+
+ /**
+ * エラー説明文を返す。
+ * @return 説明文。無ければnull
+ */
+ public String getErrDescription(){
+ String desc;
+
+ if(this.causeTask == this.validateTask){
+ desc = ValidateTask.getErrDescription(this.cause);
+ }else if(this.causeTask == this.dumpTask){
+ desc = DumpXmlTask.getErrDescription(this.cause);
+ }else{
+ desc = null;
+ }
+
+ return desc;
+ }
+
+}
--- /dev/null
+/*
+ * Snif Writer
+ *
+ * License : The MIT License
+ * Copyright(c) 2016 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.PipedReader;
+import java.io.PipedWriter;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * 別スレッドから盗聴可能なWriter。
+ */
+public class SnifWriter extends Writer{
+
+ private final Writer fout;
+
+ private final Writer pout;
+ private final Reader reader;
+
+
+ /**
+ * コンストラクタ。
+ * @param writer 移譲先Writer
+ */
+ public SnifWriter(Writer writer){
+ super();
+
+ if(writer == null) throw new NullPointerException();
+ this.fout = writer;
+
+ PipedWriter pipeOut = new PipedWriter();
+ PipedReader pipeIn = new PipedReader();
+
+ try{
+ pipeOut.connect(pipeIn);
+ }catch(IOException e){
+ // ありえない
+ assert false;
+ }
+
+ this.pout = pipeOut;
+ this.reader = pipeIn;
+
+ return;
+ }
+
+
+ /**
+ * 傍受用Readerを返す。
+ * @return Reader
+ */
+ public Reader getSnifReader(){
+ return this.reader;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public void close() throws IOException{
+ this.fout.close();
+ this.pout.close();
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public void flush() throws IOException{
+ this.fout.flush();
+ this.pout.flush();
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @param cbuf {@inheritDoc}
+ * @param off {@inheritDoc}
+ * @param len {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException{
+ this.fout.write(cbuf, off, len);
+ this.pout.write(cbuf, off, len);
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @param str {@inheritDoc}
+ * @param off {@inheritDoc}
+ * @param len {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public void write(String str, int off, int len) throws IOException {
+ this.fout.write(str, off, len);
+ this.pout.write(str, off, len);
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @param c {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ public void write(int c) throws IOException {
+ this.fout.write(c);
+ this.pout.write(c);
+ return;
+ }
+
+}
package jp.sourceforge.jindolf.archiver;
import java.io.IOException;
-import java.io.PipedReader;
-import java.io.PipedWriter;
import java.io.Reader;
-import java.io.Writer;
+import java.util.concurrent.Callable;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Validator;
/**
* XML検証タスク。
*/
-public class ValidateTask implements Runnable{
+public class ValidateTask implements Callable<Void> {
- private final Validator validator;
+ private static final String ERR_IO = "XML入力エラーが発生しました";
+ private static final String ERR_XSD = "XML検証が失敗しました";
+ private static final String ERR_ABORT = "XML検証が中断されました";
+
+
+ private final Reader reader;
private final Source source;
+ private final Validator validator;
+
/**
* コンストラクタ。
* @param reader 文字入力
- * @throws SAXException 内部エラー
+ * @param valid バリデータ
*/
- protected ValidateTask(Reader reader) throws SAXException{
+ public ValidateTask(Reader reader, Validator valid){
super();
- this.validator = XmlUtils.createValidator();
- this.source = new StreamSource(reader);
+ this.reader = reader;
+ this.source = new StreamSource(this.reader);
+ this.validator = valid;
return;
}
+
/**
- * 検証タスク。
- * {@inheritDoc}
+ * 例外に応じたエラー説明を返す。
+ * @param cause 例外
+ * @return エラー説明
*/
- @Override
- public void run(){
- boolean done = false;
- try{
- this.validator.validate(this.source);
- done = true;
- }catch(IOException e){
- e.printStackTrace(System.err);
- System.err.println("XML検証に失敗しました。");
- }catch(SAXException e){
- e.printStackTrace(System.err);
- System.err.println("XML検証に失敗しました。");
- }finally{
- if( ! done ) System.exit(1);
- }
-
- return;
+ public static String getErrDescription(Throwable cause){
+ String desc;
+ if (cause instanceof IOException) desc = ERR_IO;
+ else if(cause instanceof SAXException) desc = ERR_XSD;
+ else desc = ERR_ABORT;
+ return desc;
}
+
/**
- * 文字出力を横取りしバックグラウンドで検証を行うWriterを生成する。
- * @param writer 元出力
- * @return 新しい出力
+ * XML検証タスク。
+ * @return null
+ * @throws IOException 入力エラー
+ * @throws SAXException 検証エラー
*/
- public static Writer wrapValidator(Writer writer){
- PipedReader reader = new PipedReader();
- Writer pipeWriter;
- try{
- pipeWriter = new PipedWriter(reader);
- }catch(IOException e){
- e.printStackTrace(System.err);
- System.err.println("処理を続行できません。");
- System.exit(1);
- return null;
- }
-
- MultiPlexer mtplx = new MultiPlexer();
- mtplx.addWriter(writer);
- mtplx.addWriter(pipeWriter);
-
- Runnable task;
+ @Override
+ public Void call() throws IOException, SAXException{
try{
- task = new ValidateTask(reader);
- }catch(SAXException e){
- e.printStackTrace(System.err);
- System.err.println("処理を続行できません。");
- System.exit(1);
- return null;
+ this.validator.validate(this.source);
+ }finally{
+ this.reader.close();
}
- Thread th = new Thread(task);
- th.setDaemon(false);
- th.start();
- return mtplx;
+ return null;
}
}