View Javadoc
1   /*
2    * Copyright 2012-2021 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.app.web.admin.scheduler;
17  
18  import java.text.MessageFormat;
19  import java.util.Base64;
20  
21  import javax.annotation.Resource;
22  
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.es.config.exentity.ScheduledJob;
30  import org.codelibs.fess.helper.ProcessHelper;
31  import org.codelibs.fess.helper.SystemHelper;
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  /**
43   * @author shinsuke
44   * @author Keiichi Watanabe
45   */
46  public class AdminSchedulerAction extends FessAdminAction {
47  
48      public static final String ROLE = "admin-scheduler";
49  
50      // ===================================================================================
51      //                                                                           Attribute
52      //                                                                           =========
53      @Resource
54      private ScheduledJobService scheduledJobService;
55      @Resource
56      private SchedulerPager schedulerPager;
57      @Resource
58      protected ProcessHelper processHelper;
59  
60      // ===================================================================================
61      //                                                                               Hook
62      //                                                                              ======
63      @Override
64      protected void setupHtmlData(final ActionRuntime runtime) {
65          super.setupHtmlData(runtime);
66          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNameScheduler()));
67      }
68  
69      @Override
70      protected String getActionRole() {
71          return ROLE;
72      }
73  
74      // ===================================================================================
75      //                                                                      Search Execute
76      //                                                                      ==============
77      @Execute
78      @Secured({ ROLE, ROLE + VIEW })
79      public HtmlResponse index(final SearchForm form) {
80          return asListHtml();
81      }
82  
83      @Execute
84      @Secured({ ROLE, ROLE + VIEW })
85      public HtmlResponse list(final OptionalThing<Integer> pageNumber, final SearchForm form) {
86          pageNumber.ifPresent(num -> {
87              schedulerPager.setCurrentPageNumber(pageNumber.get());
88          }).orElse(() -> {
89              schedulerPager.setCurrentPageNumber(0);
90          });
91          return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
92              searchPaging(data, form);
93          });
94      }
95  
96      @Execute
97      @Secured({ ROLE, ROLE + VIEW })
98      public HtmlResponse search(final SearchForm form) {
99          copyBeanToBean(form, schedulerPager, op -> op.exclude(Constants.PAGER_CONVERSION_RULE));
100         return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
101             searchPaging(data, form);
102         });
103     }
104 
105     @Execute
106     @Secured({ ROLE, ROLE + VIEW })
107     public HtmlResponse reset(final SearchForm form) {
108         schedulerPager.clear();
109         return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
110             searchPaging(data, form);
111         });
112     }
113 
114     protected void searchPaging(final RenderData data, final SearchForm form) {
115         RenderDataUtil.register(data, "scheduledJobItems", scheduledJobService.getScheduledJobList(schedulerPager)); // page navi
116 
117         // restore from pager
118         copyBeanToBean(schedulerPager, form, op -> op.include("id"));
119     }
120 
121     // ===================================================================================
122     //                                                                        Edit Execute
123     //                                                                        ============
124     // -----------------------------------------------------
125     //                                            Entry Page
126     //                                            ----------
127 
128     @Execute
129     @Secured({ ROLE })
130     public HtmlResponse createnewjob(final String type, final String id, final String name) {
131         saveToken();
132         return asHtml(path_AdminScheduler_AdminSchedulerEditJsp).useForm(CreateForm.class, op -> {
133             op.setup(scheduledJobForm -> {
134                 scheduledJobForm.initialize();
135                 scheduledJobForm.crudMode = CrudMode.CREATE;
136                 scheduledJobForm.jobLogging = Constants.ON;
137                 scheduledJobForm.crawler = Constants.ON;
138                 scheduledJobForm.available = Constants.ON;
139                 scheduledJobForm.cronExpression = null;
140                 final String decodedName = new String(Base64.getUrlDecoder().decode(name), Constants.CHARSET_UTF_8);
141                 scheduledJobForm.name = MessageFormat.format(fessConfig.getJobTemplateTitle(type), decodedName);
142                 final String[] ids = new String[] { "", "", "" };
143                 if (Constants.WEB_CRAWLER_TYPE.equals(type)) {
144                     ids[0] = "\"" + id + "\"";
145                 } else if (Constants.FILE_CRAWLER_TYPE.equals(type)) {
146                     ids[1] = "\"" + id + "\"";
147                 } else if (Constants.DATA_CRAWLER_TYPE.equals(type)) {
148                     ids[2] = "\"" + id + "\"";
149                 }
150                 scheduledJobForm.scriptData =
151                         MessageFormat.format(fessConfig.getJobTemplateScript(), ids[0], ids[1], ids[2], id.replace('-', '_'));
152             });
153         });
154     }
155 
156     @Execute
157     @Secured({ ROLE })
158     public HtmlResponse createnew() {
159         saveToken();
160         return asHtml(path_AdminScheduler_AdminSchedulerEditJsp).useForm(CreateForm.class, op -> {
161             op.setup(form -> {
162                 form.initialize();
163                 form.crudMode = CrudMode.CREATE;
164             });
165         });
166     }
167 
168     @Execute
169     @Secured({ ROLE })
170     public HtmlResponse edit(final EditForm form) {
171         validate(form, messages -> {}, this::asListHtml);
172         final String id = form.id;
173         scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
174             loadScheduledJob(form, entity);
175         }).orElse(() -> {
176             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
177         });
178         saveToken();
179         if (form.crudMode.intValue() == CrudMode.EDIT) {
180             // back
181             form.crudMode = CrudMode.DETAILS;
182             return asDetailsHtml(id);
183         }
184         form.crudMode = CrudMode.EDIT;
185         return asEditHtml();
186     }
187 
188     // -----------------------------------------------------
189     //                                               Details
190     //                                               -------
191     @Execute
192     @Secured({ ROLE, ROLE + VIEW })
193     public HtmlResponse details(final int crudMode, final String id) {
194         verifyCrudMode(crudMode, CrudMode.DETAILS);
195         saveToken();
196         return asHtml(path_AdminScheduler_AdminSchedulerDetailsJsp).renderWith(data -> {
197             RenderDataUtil.register(data, "systemJobId", fessConfig.isSystemJobId(id));
198         }).useForm(EditForm.class, op -> {
199             op.setup(form -> {
200                 scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
201                     loadScheduledJob(form, entity);
202                     form.crudMode = crudMode;
203                     LaRequestUtil.getOptionalRequest().ifPresent(request -> {
204                         request.setAttribute("running", entity.isRunning());
205                         request.setAttribute("enabled", entity.isEnabled());
206                     });
207                 }).orElse(() -> {
208                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), this::asListHtml);
209                 });
210             });
211         });
212     }
213 
214     // -----------------------------------------------------
215     //                                         Actually Crud
216     //                                         -------------
217     @Execute
218     @Secured({ ROLE })
219     public HtmlResponse create(final CreateForm form) {
220         verifyCrudMode(form.crudMode, CrudMode.CREATE);
221         validate(form, messages -> {}, this::asEditHtml);
222         verifyToken(this::asEditHtml);
223         getScheduledJob(form).ifPresent(entity -> {
224             try {
225                 scheduledJobService.store(entity);
226                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
227             } catch (final Exception e) {
228                 throwValidationError(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)),
229                         this::asEditHtml);
230             }
231         }).orElse(() -> {
232             throwValidationError(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL), this::asEditHtml);
233         });
234         return redirect(getClass());
235     }
236 
237     @Execute
238     @Secured({ ROLE })
239     public HtmlResponse update(final EditForm form) {
240         verifyCrudMode(form.crudMode, CrudMode.EDIT);
241         validate(form, messages -> {}, this::asEditHtml);
242         verifyToken(this::asEditHtml);
243         getScheduledJob(form).ifPresent(entity -> {
244             try {
245                 scheduledJobService.store(entity);
246                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
247             } catch (final Exception e) {
248                 throwValidationError(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)),
249                         this::asEditHtml);
250             }
251         }).orElse(() -> {
252             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asEditHtml);
253         });
254         return redirect(getClass());
255     }
256 
257     @Execute
258     @Secured({ ROLE })
259     public HtmlResponse delete(final EditForm form) {
260         verifyCrudMode(form.crudMode, CrudMode.DETAILS);
261         final String id = form.id;
262         validate(form, messages -> {}, () -> asDetailsHtml(id));
263         verifyToken(() -> asDetailsHtml(id));
264         scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
265             try {
266                 scheduledJobService.delete(entity);
267                 saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
268             } catch (final Exception e) {
269                 throwValidationError(messages -> messages.addErrorsCrudFailedToDeleteCrudTable(GLOBAL, buildThrowableMessage(e)),
270                         this::asEditHtml);
271             }
272         }).orElse(() -> {
273             throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id), () -> asDetailsHtml(id));
274         });
275         return redirect(getClass());
276     }
277 
278     @Execute
279     @Secured({ ROLE })
280     public HtmlResponse start(final EditForm form) {
281         verifyCrudMode(form.crudMode, CrudMode.DETAILS);
282         final String id = form.id;
283         validate(form, messages -> {}, () -> asDetailsHtml(id));
284         verifyToken(() -> asDetailsHtml(id));
285         scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
286             if (!entity.isEnabled() || entity.isRunning()) {
287                 throwValidationError(messages -> {
288                     messages.addErrorsFailedToStartJob(GLOBAL, entity.getName());
289                 }, () -> asDetailsHtml(id));
290             }
291             try {
292                 entity.start();
293                 saveInfo(messages -> messages.addSuccessJobStarted(GLOBAL, entity.getName()));
294             } catch (final Exception e) {
295                 throwValidationError(messages -> {
296                     messages.addErrorsFailedToStartJob(GLOBAL, entity.getName());
297                 }, () -> asDetailsHtml(id));
298             }
299         }).orElse(() -> {
300             throwValidationError(messages -> {
301                 messages.addErrorsFailedToStartJob(GLOBAL, id);
302             }, () -> asDetailsHtml(id));
303         });
304         return redirect(getClass());
305     }
306 
307     @Execute
308     @Secured({ ROLE })
309     public HtmlResponse stop(final EditForm form) {
310         verifyCrudMode(form.crudMode, CrudMode.DETAILS);
311         final String id = form.id;
312         validate(form, messages -> {}, () -> asDetailsHtml(id));
313         verifyToken(() -> asDetailsHtml(id));
314         scheduledJobService.getScheduledJob(id).ifPresent(entity -> {
315             try {
316                 entity.stop();
317                 saveInfo(messages -> messages.addSuccessJobStopped(GLOBAL, entity.getName()));
318             } catch (final Exception e) {
319                 throwValidationError(messages -> {
320                     messages.addErrorsFailedToStopJob(GLOBAL, entity.getName());
321                 }, () -> asDetailsHtml(id));
322             }
323         }).orElse(() -> {
324             throwValidationError(messages -> {
325                 messages.addErrorsFailedToStartJob(GLOBAL, id);
326             }, () -> asDetailsHtml(id));
327         });
328         return redirect(getClass());
329     }
330 
331     // ===================================================================================
332     //                                                                        Assist Logic
333     //                                                                        ============
334     protected void loadScheduledJob(final EditForm form, final ScheduledJob entity) {
335         copyBeanToBean(entity, form, op -> op.exclude("crudMode").excludeNull());
336         form.jobLogging = entity.isLoggingEnabled() ? Constants.ON : null;
337         form.crawler = entity.isCrawlerJob() ? Constants.ON : null;
338         form.available = entity.isEnabled() ? Constants.ON : null;
339     }
340 
341     private static OptionalEntity<ScheduledJob> getEntity(final CreateForm form, final String username, final long currentTime) {
342         switch (form.crudMode) {
343         case CrudMode.CREATE:
344             return OptionalEntity.of(new ScheduledJob()).map(entity -> {
345                 entity.setCreatedBy(username);
346                 entity.setCreatedTime(currentTime);
347                 return entity;
348             });
349         case CrudMode.EDIT:
350             if (form instanceof EditForm) {
351                 return ComponentUtil.getComponent(ScheduledJobService.class).getScheduledJob(((EditForm) form).id);
352             }
353             break;
354         default:
355             break;
356         }
357         return OptionalEntity.empty();
358     }
359 
360     public static OptionalEntity<ScheduledJob> getScheduledJob(final CreateForm form) {
361         final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
362         final String username = systemHelper.getUsername();
363         final long currentTime = systemHelper.getCurrentTimeAsLong();
364         return getEntity(form, username, currentTime).map(entity -> {
365             entity.setUpdatedBy(username);
366             entity.setUpdatedTime(currentTime);
367             copyBeanToBean(form, entity, op -> op.exclude(Constants.COMMON_CONVERSION_RULE));
368             entity.setJobLogging(isCheckboxEnabled(form.jobLogging) ? Constants.T : Constants.F);
369             entity.setCrawler(isCheckboxEnabled(form.crawler) ? Constants.T : Constants.F);
370             entity.setAvailable(isCheckboxEnabled(form.available) ? Constants.T : Constants.F);
371             return entity;
372         });
373     }
374 
375     // ===================================================================================
376     //                                                                        Small Helper
377     //                                                                        ============
378     protected void verifyCrudMode(final int crudMode, final int expectedMode) {
379         if (crudMode != expectedMode) {
380             throwValidationError(messages -> {
381                 messages.addErrorsCrudInvalidMode(GLOBAL, String.valueOf(expectedMode), String.valueOf(crudMode));
382             }, this::asListHtml);
383         }
384     }
385 
386     // ===================================================================================
387     //                                                                              JSP
388     //                                                                           =========
389 
390     private HtmlResponse asListHtml() {
391         return asHtml(path_AdminScheduler_AdminSchedulerJsp).renderWith(data -> {
392             RenderDataUtil.register(data, "scheduledJobItems", scheduledJobService.getScheduledJobList(schedulerPager)); // page navi
393         }).useForm(SearchForm.class, setup -> {
394             setup.setup(form -> {
395                 copyBeanToBean(schedulerPager, form, op -> op.include("id"));
396             });
397         });
398     }
399 
400     private HtmlResponse asEditHtml() {
401         return asHtml(path_AdminScheduler_AdminSchedulerEditJsp);
402     }
403 
404     private HtmlResponse asDetailsHtml(final String id) {
405         return asHtml(path_AdminScheduler_AdminSchedulerDetailsJsp).renderWith(data -> {
406             RenderDataUtil.register(data, "systemJobId", fessConfig.isSystemJobId(id));
407         });
408     }
409 }