/**
* @version $Id: AsyncController.java 1839 2014-04-16 02:33:51Z yukihiro-kinjyo $
*
* 2011/08/29 21:30:11
* @author imai
*
* Copyright 2011-2014 TIDAコンソーシアム All Rights Reserved.
*/
package com.tida_okinawa.corona.ui.controllers;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import com.tida_okinawa.corona.CoronaActivator;
import com.tida_okinawa.corona.correction.controller.IListener;
import com.tida_okinawa.corona.ui.PreferenceInitializer;
import com.tida_okinawa.corona.ui.UIActivator;
/**
* UI-処理のインタフェース
*
* 大量のデータを1件ずつ処理して、処理結果をUIに通知する
*
* @author imai
*
* @param <TS>
* 入力データ
* @param <TR>
* 処理結果
*
* TD は io, TL は view に実装
*/
abstract public class AsyncController<TS, TR> implements IRunnableWithProgress {
final String name;
IDataProvider<TS> provider;
List<IListener<TR>> listeners = new ArrayList<IListener<TR>>();
/**
* 処理スレッドの数
*/
final int nProcThread;
/**
* @param name
* 処理名
* @param nProcThread
* 処理スレッド数
*/
AsyncController(String name, int nProcThread) {
this.name = name;
this.nProcThread = nProcThread;
}
/**
* @param name
* 処理名
*/
AsyncController(String name) {
this.name = name;
if (UIActivator.getDefault() != null) {
this.nProcThread = UIActivator.getDefault().getPreferenceStore().getInt(PreferenceInitializer.PREF_NUM_THREADS);
} else {
this.nProcThread = Runtime.getRuntime().availableProcessors();
}
}
/**
*
* @param name
* タスク名
* @param nProcThread
* 処理スレッド数
* @param provider
* 問い合わせ情報など
*/
AsyncController(String name, int nProcThread, final IDataProvider<TS> provider) {
this(name, nProcThread);
setProvider(provider);
}
void setProvider(final IDataProvider<TS> provider) {
this.provider = provider;
}
void addListener(final IListener<TR> listener) {
this.listeners.add(listener);
}
/**
* 処理スレッドの数を取得
*
* @return 処理スレッドの数
*/
public int getThreadNum() {
return nProcThread;
}
int worked = 0;
/**
* 処理を実行する
*
* @param progressMonitor
* プログレスモニター
*/
@Override
public void run(final IProgressMonitor progressMonitor) throws InterruptedException, InvocationTargetException {
System.out.println("# create " + getThreadNum() + " threads."); //$NON-NLS-1$ //$NON-NLS-2$
final IProgressMonitor monitor;
if (progressMonitor == null) {
monitor = new NullProgressMonitor();
} else {
monitor = progressMonitor;
}
/* タスクの量 = 入力データの量 */
final int total = provider.total();
monitor.beginTask(name, total + 1);
start(new SubProgressMonitor(monitor, 1));
if (monitor.isCanceled()) {
throw new InterruptedException(""); //$NON-NLS-1$
}
monitor.setTaskName(name);
/* 処理スレッドを準備 */
Thread[] threads = new Thread[getThreadNum()];
/* 処理スレッドの起動 */
for (int i = 0; i < getThreadNum(); i++) {
threads[i] = new RunThread(monitor, total, i);
/* プライオリティを設定 */
threads[i].setPriority(Thread.MIN_PRIORITY);
threads[i].start();
}
/* 入力データの供給を開始 */
provider.run(monitor);
/* すべての処理を待つ */
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/* 終わりを通知 */
for (IListener<TR> listener : listeners) {
listener.end(monitor);
}
monitor.done();
end(monitor);
}
/**
* @return Controller内のスレッド番号を取得
*/
@SuppressWarnings("static-method")
protected final int getThreadId() {
@SuppressWarnings("unchecked")
RunThread t = (RunThread) Thread.currentThread();
return t.threadId;
}
private final class RunThread extends Thread {
private final IProgressMonitor monitor;
private final int total;
final int threadId;
/**
* 処理時間
*/
private int totalTime = 0;
/**
* 処理レコード数
*/
private int nRecs = 0;
RunThread(IProgressMonitor monitor, int total, int threadId) {
this.monitor = monitor;
this.total = total;
this.threadId = threadId;
}
@Override
public void run() {
TS data;
while ((data = next()) != null && !monitor.isCanceled()) {
try {
long t0 = System.currentTimeMillis();
TR result = exec(data);
long t1 = System.currentTimeMillis();
sendResult(result);
totalTime += (t1 - t0);
nRecs++;
} catch (Exception e) {
e.printStackTrace();
IStatus status = null;
if (CoronaActivator.isDebugMode()) {
status = new Status(IStatus.ERROR, UIActivator.PLUGIN_ID, "Uncaught exception occurred.", e); //$NON-NLS-1$
} else {
status = new Status(IStatus.WARNING, UIActivator.PLUGIN_ID, "Uncaught exception occurred.", e); //$NON-NLS-1$
}
CoronaActivator.log(status, false);
}
monitor.worked(1);
worked++;
monitor.subTask(worked + "/" + total); //$NON-NLS-1$
}
provider.endQueue();
}
private TS next() {
synchronized (provider) {
TS data = provider.next();
return data;
}
}
private void sendResult(TR result) {
for (IListener<TR> listener : listeners) {
synchronized (listener) {
listener.receiveResult(result);
}
}
}
}
/**
* 1データ分の処理
* スレッドセーフにすること
*
* @param data
* 入力データ
* @return 解析結果
*/
abstract TR exec(TS data);
/**
* 開始時の処理
*
* @param monitor
* プログレスモニター
* @throws InterruptedException
* 何らかの割り込みが発生
*/
abstract protected void start(IProgressMonitor monitor) throws InterruptedException;
/**
* 終了時の処理
*
* @param monitor
* 進捗ダイアログ
*/
abstract protected void end(IProgressMonitor monitor);
}