OSDN Git Service

更新履歴
[coroid/inqubus.git] / frontend / src / yukihane / inqubus / manager / TaskManage.java
1 package yukihane.inqubus.manager;
2
3 import java.io.File;
4 import java.util.HashMap;
5 import java.util.Map;
6 import java.util.concurrent.ExecutorService;
7 import java.util.concurrent.Executors;
8 import java.util.concurrent.Future;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.apache.commons.lang.StringUtils;
12 import saccubus.worker.WorkerListener;
13 import saccubus.worker.impl.MessageReportable;
14 import saccubus.worker.impl.PercentageReportable;
15 import saccubus.worker.impl.convert.Convert;
16 import saccubus.worker.impl.convert.ConvertProgress;
17 import saccubus.worker.impl.convert.ConvertResult;
18 import saccubus.worker.impl.download.Download;
19 import saccubus.worker.impl.download.DownloadProgress;
20 import saccubus.worker.impl.download.DownloadResult;
21 import saccubus.worker.profile.ConvertProfile;
22 import saccubus.worker.profile.DownloadProfile;
23
24 /**
25  * GUIからのタスク(ダウンロード/変換)処理要求を管理するクラスです.
26  * タスク用Executor Serviceへの投入や、キャンセル要求の処理を行います.
27  * @author yuki
28  */
29 public class TaskManage {
30
31     private static final Logger logger = LoggerFactory.getLogger(TaskManage.class);
32     private final ExecutorService downloadExecutorService;
33     private final ExecutorService convertExecutorService;
34     private final Map<Integer, ManageTarget<DownloadResult>> downloadTargets = new HashMap<>();
35     private final Map<Integer, ManageTarget<ConvertResult>> convertTargets = new HashMap<>();
36     private final TaskManageListener clientListener;
37     private final int waitDownload;
38
39     public TaskManage() {
40         this(1, 30, 1, null);
41     }
42
43     public TaskManage(int maxDownload, int maxConvert) {
44         this(maxDownload, 30, maxConvert, null);
45     }
46
47     /**
48      * タスク管理オブジェクトを構築します.
49      * @param maxDownload 最大同時ダウンロード数.
50      * @param waitDownload 前回ダウンロードからの最小待ち時間(秒).
51      * @param maxConvert 最大同時変換数.
52      * @param listener イベントリスナ. 通知不要の場合はnull.
53      */
54     public TaskManage(int maxDownload, int waitDownload, int maxConvert, TaskManageListener listener) {
55         downloadExecutorService = Executors.newFixedThreadPool(maxDownload);
56         convertExecutorService = Executors.newFixedThreadPool(maxConvert);
57         this.waitDownload = waitDownload;
58         this.clientListener = listener;
59     }
60
61     public synchronized boolean add(RequestProcess request) {
62         final DownloadProfile dp = request.getDownloadProfile();
63         final ConvertProfile cp = request.getConvertProfile();
64         if (dp != null && (dp.getVideoProfile().isDownload() || dp.getCommentProfile().isDownload())) {
65             // ダウンロードするものがあればまずダウンロード処理
66             final Download task = new Download(dp, request.getVideoId(),
67                     new DownloadListener(request.getRowId()), waitDownload);
68             final Future<DownloadResult> future = downloadExecutorService.submit(task);
69             downloadTargets.put(request.getRowId(), new ManageTarget<>(request, future));
70             return true;
71
72         } else if (cp != null && cp.isConvert()) {
73             final Convert task = new Convert(cp, dp.getVideoProfile().getLocalFile(), dp.getCommentProfile().
74                     getLocalFile(), new ConvertListener(request.getRowId()));
75             final Future<ConvertResult> future = convertExecutorService.submit(task);
76             convertTargets.put(request.getRowId(), new ManageTarget<>(request, future));
77             return true;
78         }
79         return false;
80     }
81
82     public synchronized boolean cancel(int rowId) {
83         // FIXME 実行前にキャンセルした場合にはcancelledイベントが飛ばないのでMapからリクエストを削除できない
84         final ManageTarget<DownloadResult> down = downloadTargets.get(rowId);
85         if (down != null) {
86             return down.getFuture().cancel(true);
87         }
88         final ManageTarget<ConvertResult> conv = convertTargets.get(rowId);
89         if (conv != null) {
90             return conv.getFuture().cancel(true);
91         }
92
93         return false;
94     }
95
96     private class DownloadListener extends TaskManageInnerListener<DownloadResult, DownloadProgress> {
97
98         private DownloadListener(int rowId) {
99             super(rowId);
100         }
101
102         @Override
103         public void done(DownloadResult result) {
104             super.done(result);
105             synchronized (TaskManage.this) {
106                 final ManageTarget<DownloadResult> mt = removeRequest(getRowId());
107                 final RequestProcess request = mt.getRequest();
108                 if (request.getConvertProfile().isConvert()) {
109                     final DownloadProfile dp = request.getDownloadProfile();
110                     final File video = (dp.getVideoProfile().isDownload()) ? result.getDownloadVideo() : dp.
111                             getVideoProfile().getLocalFile();
112                     final File comment = (dp.getCommentProfile().isDownload()) ? result.getDownloadComment() : dp.
113                             getCommentProfile().getLocalFile();
114                     final ConvertProfile cp = request.getConvertProfile();
115                     final Convert task = new Convert(cp, video, comment, new ConvertListener(getRowId()));
116                     final Future<ConvertResult> future = convertExecutorService.submit(task);
117                     convertTargets.put(request.getRowId(), new ManageTarget<>(request, future));
118                 }
119             }
120         }
121
122         @Override
123         protected TaskKind getKind() {
124             return TaskKind.DOWNLOAD;
125         }
126
127         @Override
128         protected ManageTarget<DownloadResult> removeRequest(int rowId) {
129             return downloadTargets.remove(rowId);
130         }
131     }
132
133     private class ConvertListener extends TaskManageInnerListener<ConvertResult, ConvertProgress> {
134
135         private ConvertListener(int rowId) {
136             super(rowId);
137         }
138
139         @Override
140         protected TaskKind getKind() {
141             return TaskKind.CONVERT;
142         }
143
144         @Override
145         public void done(ConvertResult result) {
146             super.done(result);
147             synchronized (TaskManage.this) {
148                 removeRequest(getRowId());
149             }
150         }
151
152         @Override
153         protected ManageTarget<ConvertResult> removeRequest(int rowId) {
154             return convertTargets.remove(rowId);
155         }
156     }
157
158     abstract class TaskManageInnerListener<T, V extends PercentageReportable & MessageReportable> implements WorkerListener<T, V> {
159
160         private final int rowId;
161
162         protected TaskManageInnerListener(int rowId) {
163             this.rowId = rowId;
164         }
165
166         protected int getRowId() {
167             return rowId;
168         }
169
170         private void notify(TaskStatus status) {
171             notify(status, -1.0, "");
172         }
173
174         private void notify(TaskStatus status, double percentage, String message) {
175             if (getListener() == null) {
176                 return;
177             }
178             getListener().process(rowId, getKind(), status, percentage, message);
179         }
180
181         private TaskManageListener getListener() {
182             return clientListener;
183         }
184
185         protected abstract TaskKind getKind();
186
187         @Override
188         public void process(V progress) {
189             logger.trace("process: {}", progress);
190             notify(TaskStatus.DOING, progress.getPercentage(), progress.getMessage());
191         }
192
193         @Override
194         public final void cancelled() {
195             logger.debug("cancelled: {}", toString());
196             synchronized (TaskManage.this) {
197                 removeRequest(rowId);
198             }
199             notify(TaskStatus.CANCELLED);
200         }
201
202         /**
203          * この処理をオーバライドしてキューからリクエストを削除する必要があります.
204          * @param result 処理結果.
205          */
206         @Override
207         public void done(T result) {
208             logger.debug("done: {}", result);
209             notify(TaskStatus.DONE);
210         }
211
212         @Override
213         public final void error(Throwable th) {
214             logger.debug("error", th);
215             synchronized (TaskManage.this) {
216                 removeRequest(rowId);
217             }
218
219             String message = th.getMessage();
220             if(StringUtils.isBlank(message)) {
221                 message = "予期しないエラーが発生しました";
222             }
223             notify(TaskStatus.ERROR, 0.0, message);
224         }
225
226         protected abstract ManageTarget<T> removeRequest(int rowId);
227     }
228
229     private static class ManageTarget<T> {
230
231         private final RequestProcess request;
232         private final Future<T> future;
233
234         ManageTarget(RequestProcess request, Future<T> future) {
235             this.request = request;
236             this.future = future;
237         }
238
239         Future<T> getFuture() {
240             return future;
241         }
242
243         RequestProcess getRequest() {
244             return request;
245         }
246     }
247 }