1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.app.web.admin.scheduler;
17
18 import java.text.MessageFormat;
19 import java.util.Base64;
20
21 import org.apache.logging.log4j.LogManager;
22 import org.apache.logging.log4j.Logger;
23 import org.codelibs.fess.Constants;
24 import org.codelibs.fess.annotation.Secured;
25 import org.codelibs.fess.app.pager.SchedulerPager;
26 import org.codelibs.fess.app.service.ScheduledJobService;
27 import org.codelibs.fess.app.web.CrudMode;
28 import org.codelibs.fess.app.web.base.FessAdminAction;
29 import org.codelibs.fess.helper.ProcessHelper;
30 import org.codelibs.fess.helper.SystemHelper;
31 import org.codelibs.fess.opensearch.config.exentity.ScheduledJob;
32 import org.codelibs.fess.util.ComponentUtil;
33 import org.codelibs.fess.util.RenderDataUtil;
34 import org.dbflute.optional.OptionalEntity;
35 import org.dbflute.optional.OptionalThing;
36 import org.lastaflute.web.Execute;
37 import org.lastaflute.web.response.HtmlResponse;
38 import org.lastaflute.web.response.render.RenderData;
39 import org.lastaflute.web.ruts.process.ActionRuntime;
40 import org.lastaflute.web.util.LaRequestUtil;
41
42 import jakarta.annotation.Resource;
43
44
45
46
47
48 public class AdminSchedulerAction extends FessAdminAction {
49
50
51
52
53 public AdminSchedulerAction() {
54 super();
55 }
56
57
58 public static final String ROLE = "admin-scheduler";
59
60 private static final Logger logger = LogManager.getLogger(AdminSchedulerAction.class);
61
62
63
64
65
66 @Resource
67 private ScheduledJobService scheduledJobService;
68
69 @Resource
70 private SchedulerPager schedulerPager;
71
72 @Resource
73 protected ProcessHelper processHelper;
74
75
76
77
78 @Override
79 protected void setupHtmlData(final ActionRuntime runtime) {
80 super.setupHtmlData(runtime);
81 runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameScheduler()));
82 }
83
84 @Override
85 protected String getActionRole() {
86 return ROLE;
87 }
88
89
90
91
92
93
94
95
96
97
98 @Execute
99 @Secured({ ROLE, ROLE + VIEW })
100 public HtmlResponse index(final SearchForm form) {
101 return asListHtml();
102 }
103
104
105
106
107
108
109
110
111 @Execute
112 @Secured({ ROLE, ROLE + VIEW })
113 public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
114 pageNumber.ifPresent(num -> {
115 schedulerPager.setCurrentPageNumber(pageNumber.get());
116 }).orElse(() -> {
117 schedulerPager.setCurrentPageNumber(0);
118 });
119 return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
120 searchPaging(data, form);
121 });
122 }
123
124
125
126
127
128
129
130 @Execute
131 @Secured({ ROLE, ROLE + VIEW })
132 public HtmlResponse search(final SearchForm form) {
133 copyBeanToBean(form, schedulerPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
134 return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
135 searchPaging(data, form);
136 });
137 }
138
139
140
141
142
143
144
145 @Execute
146 @Secured({ ROLE, ROLE + VIEW })
147 public HtmlResponse reset(final SearchForm form) {
148 schedulerPager.clear();
149 return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
150 searchPaging(data, form);
151 });
152 }
153
154
155
156
157
158
159
160 protected void searchPaging(final RenderData data, final SearchForm form) {
161 RenderDataUtil.register(data, "scheduledJobItems", scheduledJobService.getScheduledJobList(schedulerPager));
162
163
164 copyBeanToBean(schedulerPager, form, op -> op.include("id"));
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 @Execute
183 @Secured({ ROLE })
184 public HtmlResponse createnewjob(final String type, final String id, final String name) {
185 saveToken();
186 return asHtml(path_AdminScheduler_AdminSchedulerEditJsp).useForm(CreateForm.class, op -> {
187 op.setup(scheduledJobForm -> {
188 scheduledJobForm.initialize();
189 scheduledJobForm.crudMode = CrudMode.CREATE;
190 scheduledJobForm.jobLogging = Constants.ON;
191 scheduledJobForm.crawler = Constants.ON;
192 scheduledJobForm.available = Constants.ON;
193 scheduledJobForm.cronExpression = null;
194 final String decodedName = new String(Base64.getUrlDecoder().decode(name), Constants.CHARSET_UTF_8);
195 scheduledJobForm.name = MessageFormat.format(fessConfig.getJobTemplateTitle(type), decodedName);
196 final String[] ids = { "", "", "" };
197 if (Constants.WEB_CRAWLER_TYPE.equals(type)) {
198 ids[0] = "\"" + id + "\"";
199 } else if (Constants.FILE_CRAWLER_TYPE.equals(type)) {
200 ids[1] = "\"" + id + "\"";
201 } else if (Constants.DATA_CRAWLER_TYPE.equals(type)) {
202 ids[2] = "\"" + id + "\"";
203 }
204 scheduledJobForm.scriptData =
205 MessageFormat.format(fessConfig.getJobTemplateScript(), ids[0], ids[1], ids[2], id.replace('-', '_'));
206 });
207 });
208 }
209
210
211
212
213
214
215 @Execute
216 @Secured({ ROLE })
217 public HtmlResponse createnew() {
218 saveToken();
219 return asHtml(path_AdminScheduler_AdminSchedulerEditJsp).useForm(CreateForm.class, op -> {
220 op.setup(form -> {
221 form.initialize();
222 form.crudMode = CrudMode.CREATE;
223 });
224 });
225 }
226
227
228
229
230
231
232
233 @Execute
234 @Secured({ ROLE })
235 public HtmlResponse edit(final EditForm form) {
236 validate(form, messages -> {}, this::asListHtml);
237 final String id = form.id;
238 scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
239 loadScheduledJob(form, entity);
240 }).orElse(() -> {
241 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
242 });
243 saveToken();
244 if (form.crudMode.intValue() == CrudMode.EDIT) {
245
246 form.crudMode = CrudMode.DETAILS;
247 return asDetailsHtml(id);
248 }
249 form.crudMode = CrudMode.EDIT;
250 return asEditHtml();
251 }
252
253
254
255
256
257
258
259
260
261
262
263 @Execute
264 @Secured({ ROLE, ROLE + VIEW })
265 public HtmlResponse details(final int crudMode, final String id) {
266 verifyCrudMode(crudMode, CrudMode.DETAILS, this::asListHtml);
267 saveToken();
268 return asHtml(path_AdminScheduler_AdminSchedulerDetailsJsp).renderWith(data -> {
269 RenderDataUtil.register(data, "systemJobId", fessConfig.isSystemJobId(id));
270 }).useForm(EditForm.class, op -> {
271 op.setup(form -> {
272 scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
273 loadScheduledJob(form, entity);
274 form.crudMode = crudMode;
275 LaRequestUtil.getOptionalRequest().ifPresent(request -> {
276 request.setAttribute("running", entity.isRunning());
277 request.setAttribute("enabled", entity.isEnabled());
278 });
279 }).orElse(() -> {
280 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
281 });
282 });
283 });
284 }
285
286
287
288
289
290
291
292
293
294
295 @Execute
296 @Secured({ ROLE })
297 public HtmlResponse create(final CreateForm form) {
298 verifyCrudMode(form.crudMode, CrudMode.CREATE, this::asListHtml);
299 validate(form, messages -> {}, this::asEditHtml);
300 verifyToken(this::asEditHtml);
301 getScheduledJob(form).ifPresent(entity -> {
302 try {
303 scheduledJobService.store(entity);
304 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
305 } catch (final Exception e) {
306 logger.warn("Failed to process a request.", e);
307 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
308 this::asEditHtml);
309 }
310 }).orElse(() -> {
311 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
312 });
313 return redirect(getClass());
314 }
315
316
317
318
319
320
321
322 @Execute
323 @Secured({ ROLE })
324 public HtmlResponse update(final EditForm form) {
325 verifyCrudMode(form.crudMode, CrudMode.EDIT, this::asListHtml);
326 validate(form, messages -> {}, this::asEditHtml);
327 verifyToken(this::asEditHtml);
328 getScheduledJob(form).ifPresent(entity -> {
329 try {
330 scheduledJobService.store(entity);
331 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
332 } catch (final Exception e) {
333 logger.warn("Failed to process a request.", e);
334 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
335 this::asEditHtml);
336 }
337 }).orElse(() -> {
338 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asEditHtml);
339 });
340 return redirect(getClass());
341 }
342
343
344
345
346
347
348
349 @Execute
350 @Secured({ ROLE })
351 public HtmlResponse delete(final EditForm form) {
352 verifyCrudMode(form.crudMode, CrudMode.DETAILS, this::asListHtml);
353 final String id = form.id;
354 validate(form, messages -> {}, () -> asDetailsHtml(id));
355 verifyToken(() -> asDetailsHtml(id));
356 scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
357 try {
358 scheduledJobService.delete(entity);
359 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
360 } catch (final Exception e) {
361 logger.warn("Failed to process a request.", e);
362 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
363 this::asEditHtml);
364 }
365 }).orElse(() -> {
366 throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml(id));
367 });
368 return redirect(getClass());
369 }
370
371
372
373
374
375
376
377 @Execute
378 @Secured({ ROLE })
379 public HtmlResponse start(final EditForm form) {
380 verifyCrudMode(form.crudMode, CrudMode.DETAILS, this::asListHtml);
381 final String id = form.id;
382 validate(form, messages -> {}, () -> asDetailsHtml(id));
383 verifyToken(() -> asDetailsHtml(id));
384 scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
385 if (!entity.isEnabled() || entity.isRunning()) {
386 throwValidationError(messages -> {
387 messages.addErrorsFailedToStartJob(GLOBAL, entity.getName());
388 }, () -> asDetailsHtml(id));
389 }
390 try {
391 entity.start();
392 saveInfo(messages -> messages.addSuccessJobStarted(GLOBAL, entity.getName()));
393 } catch (final Exception e) {
394 logger.warn("Failed to process a request.", e);
395 throwValidationError(messages -> {
396 messages.addErrorsFailedToStartJob(GLOBAL, entity.getName());
397 }, () -> asDetailsHtml(id));
398 }
399 }).orElse(() -> {
400 throwValidationError(messages -> {
401 messages.addErrorsFailedToStartJob(GLOBAL, id);
402 }, () -> asDetailsHtml(id));
403 });
404 return redirect(getClass());
405 }
406
407
408
409
410
411
412
413 @Execute
414 @Secured({ ROLE })
415 public HtmlResponse stop(final EditForm form) {
416 verifyCrudMode(form.crudMode, CrudMode.DETAILS, this::asListHtml);
417 final String id = form.id;
418 validate(form, messages -> {}, () -> asDetailsHtml(id));
419 verifyToken(() -> asDetailsHtml(id));
420 scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
421 try {
422 entity.stop();
423 saveInfo(messages -> messages.addSuccessJobStopped(GLOBAL, entity.getName()));
424 } catch (final Exception e) {
425 logger.warn("Failed to process a request.", e);
426 throwValidationError(messages -> {
427 messages.addErrorsFailedToStopJob(GLOBAL, entity.getName());
428 }, () -> asDetailsHtml(id));
429 }
430 }).orElse(() -> {
431 throwValidationError(messages -> {
432 messages.addErrorsFailedToStartJob(GLOBAL, id);
433 }, () -> asDetailsHtml(id));
434 });
435 return redirect(getClass());
436 }
437
438
439
440
441
442
443
444
445
446
447 protected void loadScheduledJob(final EditForm form, final ScheduledJob entity) {
448 copyBeanToBean(entity, form, op -> op.exclude("crudMode").excludeNull());
449 form.jobLogging = entity.isLoggingEnabled() ? Constants.ON : null;
450 form.crawler = entity.isCrawlerJob() ? Constants.ON : null;
451 form.available = entity.isEnabled() ? Constants.ON : null;
452 }
453
454
455
456
457
458
459
460
461
462 private static OptionalEntity<ScheduledJob> getEntity(final CreateForm form, final String username, final long currentTime) {
463 switch (form.crudMode) {
464 case CrudMode.CREATE:
465 return OptionalEntity.of(new ScheduledJob()).map(entity -> {
466 entity.setCreatedBy(username);
467 entity.setCreatedTime(currentTime);
468 return entity;
469 });
470 case CrudMode.EDIT:
471 if (form instanceof EditForm) {
472 return ComponentUtil.getComponent(ScheduledJobService.class).getScheduledJob(((EditForm) form).id);
473 }
474 break;
475 default:
476 break;
477 }
478 return OptionalEntity.empty();
479 }
480
481
482
483
484
485
486
487 public static OptionalEntity<ScheduledJob> getScheduledJob(final CreateForm form) {
488 final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
489 final String username = systemHelper.getUsername();
490 final long currentTime = systemHelper.getCurrentTimeAsLong();
491 return getEntity(form, username, currentTime).map(entity -> {
492 entity.setUpdatedBy(username);
493 entity.setUpdatedTime(currentTime);
494 copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
495 entity.setJobLogging(isCheckboxEnabled(form.jobLogging) ? Constants.T : Constants.F);
496 entity.setCrawler(isCheckboxEnabled(form.crawler) ? Constants.T : Constants.F);
497 entity.setAvailable(isCheckboxEnabled(form.available) ? Constants.T : Constants.F);
498 return entity;
499 });
500 }
501
502
503
504
505
506
507
508 private HtmlResponse asListHtml() {
509 return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
510 RenderDataUtil.register(data, "scheduledJobItems", scheduledJobService.getScheduledJobList(schedulerPager));
511 }).useForm(SearchForm.class, setup -> {
512 setup.setup(form -> {
513 copyBeanToBean(schedulerPager, form, op -> op.include("id"));
514 });
515 });
516 }
517
518 private HtmlResponse asEditHtml() {
519 return asHtml(path_AdminScheduler_AdminSchedulerEditJsp);
520 }
521
522 private HtmlResponse asDetailsHtml(final String id) {
523 return asHtml(path_AdminScheduler_AdminSchedulerDetailsJsp).renderWith(data -> {
524 RenderDataUtil.register(data, "systemJobId", fessConfig.isSystemJobId(id));
525 });
526 }
527 }