1 package rydeen;
2
3 import rydeen.spi.ProcessorService;
4
5 /**
6 * <p>
7 * 抽象処理器クラスです.{@link Processor <code>Processor</code>}にライフサイクルの概念が導入されています.
8 * </p><p>
9 * ライフサイクルは{@link Stage}クラスで表され,以下のように状態が遷移します.
10 * <pre> UNINITIALIZED
11 * ↓ {@link #init() <code>init</code>}
12 * INITIALIZED
13 * ↓ <em>{@link #prepare(Arguments) <code>prepare</code>}</em>
14 * ↓ {@link #execute(TargetSource, Destination) <code>execute</code>}
15 * PROCESSING
16 * ↓ <em>{@link #perform(TargetSource, Destination) <code>perform</code>}</em>
17 * PROCESSED
18 * ↓ {@link #finish() <code>finish</code>}
19 * ↓ <em>{@link #summarize() <code>summarize</code>}</em>
20 * FINISHED</pre>
21 * <p>
22 * 矢印の右に書かれている文字はメソッドを表しており,
23 * そのメソッドが呼び出されることで,ライフサイクルの状態が遷移します.
24 * 斜体で示されているメソッドが抽象メソッドであり,サブクラスでオーバーライドしなければならないメソッドです.
25 * その他のメソッドはfinal宣言されているため,オーバーライドできません.
26 * なお,状態は以降,ステージ,もしくはstageとして表記されます.
27 * </p><p>
28 * このオブジェクトが作成された直後のステージはUNINITIALIZEDです.
29 * initメソッドが呼び出されることにより,INITIALIZEDに移行します.
30 * なお,initメソッドからprepareメソッドを呼び出しますが,
31 * prepareメソッド呼び出しの直前にINITIALIZEDにステージは移行します.
32 * 続いて,executeメソッドが呼び出され,PROCESSINGステージに移行し,
33 * 自動的にperformメソッドが呼び出されます.
34 * そして,performメソッドの呼び出しが終了すると,PROCESSEDステージに移行します.
35 * 最後にfinishメソッドが呼び出され,finishメソッドからsummarizeメソッドが呼び出されます.
36 * summarizeメソッドの呼び出しが終了すると,FINISHEDステージに移行します.
37 * </p><p>
38 * この処理器には5つのステージが存在しますが,それぞれのステージで呼び出せるメソッドは限定されています.
39 * 呼び出し可能か否かを表したのが以下の表です.
40 * </p><p>
41 * 縦の列がステージを表しており,横の行が各メソッドを表しています.
42 * ○のセルがあるのは,その行にあるメソッドがその列のステージのときに呼び出せることを表しています.
43 * ×のセルの場合は,IllegalStateException が投げられます.
44 * </p>
45 * <table border="1">
46 * <thead>
47 * <th></th>
48 * <th></th>
49 * <th>UNINITIALIZED</th>
50 * <th>INITIALIZED</th>
51 * <th>PROCESSING</th>
52 * <th>PROCESSED</th>
53 * <th>FINISHED</th>
54 * </thead>
55 * <tbody>
56 * <tr>
57 * <td rowspan="6">情報取得</td>
58 * <td>{@link #getProcessorName <code>getProcessorName</code>}</td>
59 * <td>○</td>
60 * <td>○</td>
61 * <td>○</td>
62 * <td>○</td>
63 * <td>○</td>
64 * </tr>
65 * <tr>
66 * <td>{@link #getProvider <code>getProvider</code>}</td>
67 * <td>○</td>
68 * <td>○</td>
69 * <td>○</td>
70 * <td>○</td>
71 * <td>○</td>
72 * </tr>
73 * <tr>
74 * <td>{@link #getCurrentStage <code>getCurrentStage</code>}</td>
75 * <td>○</td>
76 * <td>○</td>
77 * <td>○</td>
78 * <td>○</td>
79 * <td>○</td>
80 * </tr>
81 * <tr>
82 * <td>{@link #getId <code>getId</code>}</td>
83 * <td>○</td>
84 * <td>○</td>
85 * <td>○</td>
86 * <td>○</td>
87 * <td>○</td>
88 * </tr>
89 * <tr>
90 * <td>{@link #getArguments <code>getArguments</code>}</td>
91 * <td>○</td>
92 * <td>×</td>
93 * <td>×</td>
94 * <td>×</td>
95 * <td>×</td>
96 * </tr>
97 * <tr>
98 * <td>{@link #getSummary <code>getSummary</code>}</td>
99 * <td>×</td>
100 * <td>○</td>
101 * <td>○</td>
102 * <td>○</td>
103 * <td>○</td>
104 * </tr>
105 * <tr>
106 * <td rowspan="2">情報設定</td>
107 * <td>{@link #setId <code>setId</code>}</td>
108 * <td>○</td>
109 * <td>×</td>
110 * <td>×</td>
111 * <td>×</td>
112 * <td>×</td>
113 * </tr>
114 * <tr>
115 * <td>{@link #putEntry <code>putEntry</code>}</td>
116 * <td>×</td>
117 * <td>○</td>
118 * <td>○</td>
119 * <td>○</td>
120 * <td>×</td>
121 * </tr>
122 * <tr>
123 * <td rowspan="3">処理メソッド</td>
124 * <td>{@link #init <code>init</code>}</td>
125 * <td>○</td>
126 * <td>×</td>
127 * <td>×</td>
128 * <td>×</td>
129 * <td>×</td>
130 * </tr>
131 * <tr>
132 * <td>{@link #execute <code>execute</code>}</td>
133 * <td>×</td>
134 * <td>○</td>
135 * <td>×</td>
136 * <td>×</td>
137 * <td>×</td>
138 * </tr>
139 * <tr>
140 * <td>{@link #finish <code>finish</code>}</td>
141 * <td>×</td>
142 * <td>×</td>
143 * <td>×</td>
144 * <td>○</td>
145 * <td>×</td>
146 * </tr>
147 * <tr>
148 * <td rowspan="3">オーバーライド</td>
149 * <td>{@link #prepare <code>prepare</code>}</td>
150 * <td>×</td>
151 * <td>○</td>
152 * <td>×</td>
153 * <td>×</td>
154 * <td>×</td>
155 * </tr>
156 * <tr>
157 * <td>{@link #perform <code>perform</code>}</td>
158 * <td>×</td>
159 * <td>×</td>
160 * <td>○</td>
161 * <td>×</td>
162 * <td>×</td>
163 * </tr>
164 * <tr>
165 * <td>{@link #summarize <code>summarize</code>}</td>
166 * <td>×</td>
167 * <td>×</td>
168 * <td>×</td>
169 * <td>○</td>
170 * <td>×</td>
171 * </tr>
172 * </tbody>
173 * </table>
174 *
175 * @author Haruaki Tamada
176 */
177 public abstract class AbstractProcessor implements Processor{
178 private Arguments arguments;
179 private Summary summary;
180 private ProcessorService provider;
181 private Stage stage = Stage.UNINITIALIZED;
182 private String id;
183
184 /**
185 * 指定されたサービスプロバイダをもとに処理器オブジェクトを作成します.
186 *
187 * @param provider サービスプロバイダ
188 */
189 protected AbstractProcessor(ProcessorService provider){
190 this.provider = provider;
191 this.arguments = provider.getDefaultArguments();
192 }
193
194 /**
195 * この処理器の名前を返します.
196 *
197 * @see ProcessorService#getProcessorName
198 */
199 @Override
200 public final String getProcessorName(){
201 return getProvider().getProcessorName();
202 }
203
204 /**
205 * 現在のステージを返します.
206 * @return 現在のステージ.
207 */
208 public final Stage getCurrentStage(){
209 return stage;
210 }
211
212 /**
213 * <p>
214 * Idを設定します.
215 * </p><p>
216 * Idは1セッションの間に,処理器を互いに区別するための文字列です.
217 * </p><p>
218 * このメソッドはステージがUNINITIALIZEDの時だけ呼び出せます.
219 * ステージがUNINITIALIZED以外のときには,IllegalStateException が投げられます.
220 * </p>
221 *
222 * @throws IllegalStateException 状態がUNINITIALIZED以外のときに,このメソッドが呼び出された場合.
223 */
224 public final void setId(String id){
225 if(stage != Stage.UNINITIALIZED){
226 throw new IllegalStateException("expects UNINITIALIZED, but " + stage);
227 }
228 this.id = id;
229 }
230
231 /**
232 * <p>
233 * 設定されたIdを返します.
234 * Idが設定されていない場合({@link #setId <code>setId</code>}
235 * が一度も呼び出されていない場合)はnullを返します.
236 * </p><p>
237 * このメソッドは例外を投げません.
238 * </p>
239 */
240 public final String getId(){
241 return id;
242 }
243
244 /**
245 * <p>
246 * 初期設定を行います.
247 * ユーザ独自の初期設定を行う場合は {@link #prepare(Arguments) <code>prepare</code>}
248 * メソッドをオーバーライドしてください.
249 * このメソッドはfinal宣言されていますので,オーバーライドできません.
250 * </p><p>
251 * このメソッド呼び出し前は{@link Stage#UNINITIALIZED <code>UNINITIALIZED</code>}であり,
252 * 呼び出し後は {@link Stage#INITIALIZED <code>INITIALIZED</code>}になります.
253 * そうでない場合は,<code>IllegalStateException</code>が投げられます.
254 * </p>
255 *
256 * @throws IllegalStateException 適切な状態でないときにこのメソッドが呼び出された場合.
257 */
258 @Override
259 public synchronized final void init() throws ProcessorException{
260 if(stage != Stage.UNINITIALIZED){
261 throw new IllegalStateException("expects UNINITIALIZED, but " + stage);
262 }
263 summary = new Summary(getId());
264 stage = Stage.INITIALIZED;
265 prepare(arguments);
266 }
267
268 /**
269 * <p>
270 * ユーザ定義の初期設定を行うメソッドです.引数に与えられた{@link Arguments <code>Arguments</code>}
271 * に従って処理を行うよう処理器を設定しなければいけません.
272 * </p><p>
273 * 初期設定時に例外が起こった場合は,ProcessorExceptionを投げるよう実装してください.
274 * なお,このメソッドが呼び出される直前にステージはINITIALIZEDに変更されています.
275 * </p>
276 * @param args 処理器に与えるパラメータ.
277 */
278 protected abstract void prepare(Arguments args) throws ProcessorException;
279
280 /**
281 * <p>
282 * 処理を行うためのメソッドです.
283 * </p><p>
284 * 具体的な処理内容は{@link #perform <code>perform</code>}メソッドを
285 * オーバーライドしてください.
286 * </p>
287 *
288 * @throws IllegalStateException 適切な状態でないときにこのメソッドが呼び出された場合.
289 */
290 @Override
291 public void execute(TargetSource source, Destination dest) throws ProcessorException{
292 if(stage != Stage.INITIALIZED){
293 throw new IllegalStateException("expects INITIALIZED, but " + stage);
294 }
295 stage = Stage.PROCESSING;
296 try{
297 perform(source, dest);
298 } catch(ProcessorException e){
299 throw e;
300 } finally{
301 stage = Stage.PROCESSED;
302 }
303 }
304
305 /**
306 * サブクラスでこのメソッドをオーバーライドして実際の処理を実装してください.
307 */
308 protected abstract void perform(TargetSource source, Destination dest) throws ProcessorException;
309
310 /**
311 * ユーザ定義の終了処理を行うメソッドです.この処理器で使用したリソースを適切に開放しなければいけません.
312 * 終了処理時に例外が起こった場合は,ProcessorExceptionを投げるよう実装してください.
313 */
314 protected abstract void summarize() throws ProcessorException;
315
316 /**
317 * <p>
318 * 終了処理を行います.
319 * </p><p>
320 * ユーザ独自の終了処理を行う場合は {@link #summarize() <code>summarize</code>}
321 * メソッドをオーバーライドしてください.
322 * </p><p>
323 * このメソッドはfinal宣言されていますので,オーバーライドできません.
324 * </p>
325 */
326 @Override
327 public synchronized void finish() throws ProcessorException{
328 if(stage != Stage.PROCESSED){
329 throw new IllegalStateException("expects PROCESSED, but " + stage);
330 }
331 summarize();
332 stage = Stage.FINISHED;
333 }
334
335 /**
336 * サービスプロバイダを返します.
337 * コンストラクタに与えられたサービスプロバイダをそのまま返します.
338 */
339 @Override
340 public ProcessorService getProvider(){
341 return provider;
342 }
343
344 /**
345 * <p>
346 * 処理器の設定項目を返します.
347 * ステージがUNINITIALIZEDの場合は,
348 * このメソッドで返されたオブジェクトの値を変更して,
349 * この処理器の設定を変えられます.
350 * </p><p>
351 * ステージがUNINITIALIZEDでない場合は,IllegalStateExceptionが投げられます.
352 * </p>
353 */
354 @Override
355 public Arguments getArguments(){
356 if(stage != Stage.UNINITIALIZED){
357 throw new IllegalStateException(
358 "current stage is " + stage + ". Initialization is already done."
359 );
360 }
361 return arguments;
362 }
363
364 /**
365 * <p>
366 * この処理器のサマリを返します.
367 * サマリとは処理内容を提示するためのオブジェクトです.
368 * </p><p>
369 * サマリに情報を追加するには{@link #putEntry <code>putEntry</code>}
370 * メソッドを使います.このメソッドに渡された項目がサマリオブジェクトに追加されます.
371 * </p><p>
372 * ステージがUNINITIALIZEDの場合は,IllegalStateExceptionが投げられます.
373 * </p>
374 * @see Summary
375 */
376 @Override
377 public Summary getSummary(){
378 if(stage == Stage.UNINITIALIZED){
379 throw new IllegalStateException(
380 "current stage is UNINITIALIZED. Summary is not constructed."
381 );
382 }
383 return summary;
384 }
385
386 /**
387 * <p>
388 * Summaryに出力するためのエントリを追加します.
389 * </p><p>
390 * 引数に与えられた{@link Arguments <code>Arguments</code>}にある全ての
391 * {@link Argument <code>Argument</code>}が{@link Summary <code>Summary</code>}
392 * に追加されます.Argumentのnameがkeyに割り当てられます.
393 * ただし,Argumentのvalueがnullの場合は追加されず,無視されます.
394 * </p><p>
395 * 現在の状態がFINISHEDのとき,もしくは,UNINITIALIZEDのときに,
396 * このメソッドが呼び出された場合,IllegalStateException が投げられます.
397 * </p>
398 *
399 * @param args
400 * @throws IllegalStateException 現在の状態がFINISHEDもしくはUNINITIALIZEDのとき.
401 * @see Summary
402 */
403 protected synchronized void putEntry(Arguments args){
404 for(Argument arg: args){
405 String value = arg.getValue();
406 if(value != null){
407 putEntry(arg.getName(), value);
408 }
409 }
410 }
411
412 /**
413 * <p>
414 * Summaryに出力するためのエントリを追加します.
415 * </p><p>
416 * 現在の状態がFINISHEDのとき,もしくは,UNINITIALIZEDのときに,
417 * このメソッドが呼び出された場合,IllegalStateException が投げられます.
418 * </p>
419 * @param key エントリのキー
420 * @param value エントリのキーに対応する値
421 * @throws IllegalStateException 現在の状態がFINISHEDもしくはUNINITIALIZEDのとき.
422 * @see Summary
423 */
424 protected synchronized void putEntry(String key, String value){
425 if(stage == Stage.FINISHED){
426 throw new IllegalStateException("current stage is FINISHED. Summary is already closed.");
427 }
428 if(stage == Stage.UNINITIALIZED){
429 throw new IllegalStateException("current stage is UNINITIALIZED. Summary is not constructed.");
430 }
431 summary.putEntry(key, value);
432 }
433 }