View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.client.console.tasks;
20  
21  import java.io.Serializable;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Optional;
25  import java.util.stream.Collectors;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.syncope.client.console.SyncopeWebApplication;
28  import org.apache.syncope.client.console.commons.RealmsUtils;
29  import org.apache.syncope.client.console.rest.RealmRestClient;
30  import org.apache.syncope.client.console.rest.TaskRestClient;
31  import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchFieldPanel;
32  import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder;
33  import org.apache.syncope.client.ui.commons.Constants;
34  import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
35  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
36  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
37  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
38  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
39  import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
40  import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
41  import org.apache.syncope.common.lib.SyncopeConstants;
42  import org.apache.syncope.common.lib.to.MacroTaskTO;
43  import org.apache.syncope.common.lib.to.ProvisioningTaskTO;
44  import org.apache.syncope.common.lib.to.PullTaskTO;
45  import org.apache.syncope.common.lib.to.PushTaskTO;
46  import org.apache.syncope.common.lib.to.RealmTO;
47  import org.apache.syncope.common.lib.to.SchedTaskTO;
48  import org.apache.syncope.common.lib.types.MatchingRule;
49  import org.apache.syncope.common.lib.types.PullMode;
50  import org.apache.syncope.common.lib.types.TaskType;
51  import org.apache.syncope.common.lib.types.ThreadPoolSettings;
52  import org.apache.syncope.common.lib.types.UnmatchingRule;
53  import org.apache.wicket.PageReference;
54  import org.apache.wicket.ajax.AjaxRequestTarget;
55  import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings;
56  import org.apache.wicket.extensions.wizard.WizardModel;
57  import org.apache.wicket.extensions.wizard.WizardStep;
58  import org.apache.wicket.markup.html.WebMarkupContainer;
59  import org.apache.wicket.model.IModel;
60  import org.apache.wicket.model.PropertyModel;
61  import org.apache.wicket.model.util.ListModel;
62  import org.springframework.beans.PropertyAccessorFactory;
63  
64  public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizardBuilder<T> {
65  
66      private static final long serialVersionUID = 5945391813567245081L;
67  
68      protected final TaskType type;
69  
70      protected final RealmRestClient realmRestClient;
71  
72      protected final TaskRestClient taskRestClient;
73  
74      protected PushTaskWrapper wrapper;
75  
76      protected CrontabPanel crontabPanel;
77  
78      protected final boolean fullRealmsTree;
79  
80      public SchedTaskWizardBuilder(
81              final TaskType type,
82              final T taskTO,
83              final RealmRestClient realmRestClient,
84              final TaskRestClient taskRestClient,
85              final PageReference pageRef) {
86  
87          super(taskTO, pageRef);
88          this.type = type;
89          this.realmRestClient = realmRestClient;
90          this.taskRestClient = taskRestClient;
91          this.fullRealmsTree = SyncopeWebApplication.get().fullRealmsTree(realmRestClient);
92      }
93  
94      @Override
95      protected Serializable onApplyInternal(final SchedTaskTO modelObject) {
96          if (modelObject instanceof PushTaskTO && wrapper != null) {
97              wrapper.fillFilterConditions();
98          }
99  
100         modelObject.setCronExpression(crontabPanel.getCronExpression());
101         if (modelObject.getKey() == null) {
102             taskRestClient.create(type, modelObject);
103         } else {
104             taskRestClient.update(type, modelObject);
105         }
106         return modelObject;
107     }
108 
109     @Override
110     protected WizardModel buildModelSteps(final SchedTaskTO modelObject, final WizardModel wizardModel) {
111         wizardModel.add(new Profile(modelObject));
112         if (modelObject instanceof PushTaskTO) {
113             wrapper = new PushTaskWrapper(PushTaskTO.class.cast(modelObject));
114             wizardModel.add(new PushTaskFilters(wrapper, pageRef));
115         }
116         wizardModel.add(new Schedule(modelObject));
117         return wizardModel;
118     }
119 
120     protected List<String> searchRealms(final String realmQuery) {
121         return realmRestClient.search(fullRealmsTree
122                 ? RealmsUtils.buildRootQuery()
123                 : RealmsUtils.buildKeywordQuery(realmQuery)).
124                 getResult().stream().map(RealmTO::getFullPath).collect(Collectors.toList());
125     }
126 
127     protected class Profile extends WizardStep {
128 
129         private static final long serialVersionUID = -3043839139187792810L;
130 
131         protected final IModel<List<String>> taskJobDelegates = SyncopeWebApplication.get().
132                 getImplementationInfoProvider().getTaskJobDelegates();
133 
134         protected final IModel<List<String>> reconFilterBuilders = SyncopeWebApplication.get().
135                 getImplementationInfoProvider().getReconFilterBuilders();
136 
137         protected final IModel<List<String>> macroActions = SyncopeWebApplication.get().
138                 getImplementationInfoProvider().getMacroActions();
139 
140         protected final IModel<List<String>> pullActions = SyncopeWebApplication.get().
141                 getImplementationInfoProvider().getPullActions();
142 
143         protected final IModel<List<String>> pushActions = SyncopeWebApplication.get().
144                 getImplementationInfoProvider().getPushActions();
145 
146         protected Profile(final SchedTaskTO taskTO) {
147             AjaxTextFieldPanel name = new AjaxTextFieldPanel(
148                     Constants.NAME_FIELD_NAME, Constants.NAME_FIELD_NAME,
149                     new PropertyModel<>(taskTO, Constants.NAME_FIELD_NAME),
150                     false);
151             name.addRequiredLabel();
152             name.setEnabled(true);
153             add(name);
154 
155             AjaxTextFieldPanel description = new AjaxTextFieldPanel(
156                     Constants.DESCRIPTION_FIELD_NAME, Constants.DESCRIPTION_FIELD_NAME,
157                     new PropertyModel<>(taskTO, Constants.DESCRIPTION_FIELD_NAME), false);
158             description.setEnabled(true);
159             add(description);
160 
161             AjaxCheckBoxPanel active = new AjaxCheckBoxPanel(
162                     "active", "active", new PropertyModel<>(taskTO, "active"), false);
163             add(active);
164 
165             AjaxDropDownChoicePanel<String> jobDelegate = new AjaxDropDownChoicePanel<>(
166                     "jobDelegate", "jobDelegate", new PropertyModel<>(taskTO, "jobDelegate"), false);
167             jobDelegate.setChoices(taskJobDelegates.getObject());
168             jobDelegate.addRequiredLabel();
169             jobDelegate.setEnabled(taskTO.getKey() == null);
170             add(jobDelegate);
171 
172             AutoCompleteSettings settings = new AutoCompleteSettings();
173             settings.setShowCompleteListOnFocusGain(fullRealmsTree);
174             settings.setShowListOnEmptyInput(fullRealmsTree);
175 
176             // ------------------------------
177             // Only for macro tasks
178             // ------------------------------            
179             WebMarkupContainer macroTaskSpecifics = new WebMarkupContainer("macroTaskSpecifics");
180             add(macroTaskSpecifics.setRenderBodyOnly(true));
181 
182             AjaxSearchFieldPanel realm = new AjaxSearchFieldPanel(
183                     "realm", "realm", new PropertyModel<>(taskTO, "realm"), settings) {
184 
185                 private static final long serialVersionUID = -6390474600233486704L;
186 
187                 @Override
188                 protected Iterator<String> getChoices(final String input) {
189                     return (RealmsUtils.checkInput(input)
190                             ? searchRealms(input)
191                             : List.<String>of()).iterator();
192                 }
193             };
194             if (taskTO instanceof MacroTaskTO) {
195                 realm.addRequiredLabel();
196                 if (StringUtils.isBlank(MacroTaskTO.class.cast(taskTO).getRealm())) {
197                     // add a default destination realm if missing in the task
198                     realm.setModelObject(SyncopeConstants.ROOT_REALM);
199                 }
200             }
201             macroTaskSpecifics.add(realm);
202 
203             macroTaskSpecifics.add(new AjaxDropDownChoicePanel<>(
204                     "macroActions", "macroActions", new PropertyModel<>(taskTO, "macroActions"), false).
205                     setChoices(macroActions));
206 
207             AjaxCheckBoxPanel continueOnError = new AjaxCheckBoxPanel(
208                     "continueOnError", "continueOnError", new PropertyModel<>(taskTO, "continueOnError"), false);
209             macroTaskSpecifics.add(continueOnError);
210 
211             AjaxCheckBoxPanel saveExecs = new AjaxCheckBoxPanel(
212                     "saveExecs", "saveExecs", new PropertyModel<>(taskTO, "saveExecs"), false);
213             macroTaskSpecifics.add(saveExecs);
214 
215             // ------------------------------
216             // Only for pull tasks
217             // ------------------------------            
218             WebMarkupContainer pullTaskSpecifics = new WebMarkupContainer("pullTaskSpecifics");
219             add(pullTaskSpecifics.setRenderBodyOnly(true));
220 
221             boolean isFiltered = false;
222             if (taskTO instanceof PullTaskTO) {
223                 isFiltered = PullTaskTO.class.cast(taskTO).getPullMode() == PullMode.FILTERED_RECONCILIATION;
224             } else {
225                 pullTaskSpecifics.setEnabled(false).setVisible(false);
226             }
227 
228             AjaxDropDownChoicePanel<PullMode> pullMode = new AjaxDropDownChoicePanel<>(
229                     "pullMode", "pullMode", new PropertyModel<>(taskTO, "pullMode"), false);
230             pullMode.setChoices(List.of(PullMode.values()));
231             if (taskTO instanceof PullTaskTO) {
232                 pullMode.addRequiredLabel();
233             }
234             pullMode.setNullValid(!(taskTO instanceof PullTaskTO));
235             pullTaskSpecifics.add(pullMode);
236 
237             AjaxDropDownChoicePanel<String> reconFilterBuilder = new AjaxDropDownChoicePanel<>(
238                     "reconFilterBuilder", "reconFilterBuilder",
239                     new PropertyModel<>(taskTO, "reconFilterBuilder"), false);
240             reconFilterBuilder.setChoices(reconFilterBuilders.getObject());
241             reconFilterBuilder.setEnabled(isFiltered);
242             reconFilterBuilder.setRequired(isFiltered);
243             pullTaskSpecifics.add(reconFilterBuilder);
244 
245             pullMode.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
246 
247                 private static final long serialVersionUID = -1107858522700306810L;
248 
249                 @Override
250                 protected void onUpdate(final AjaxRequestTarget target) {
251                     reconFilterBuilder.setEnabled(pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
252                     reconFilterBuilder.setRequired(pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
253                     target.add(reconFilterBuilder);
254                 }
255             });
256 
257             AjaxSearchFieldPanel destinationRealm =
258                     new AjaxSearchFieldPanel("destinationRealm", "destinationRealm",
259                             new PropertyModel<>(taskTO, "destinationRealm"), settings) {
260 
261                 private static final long serialVersionUID = -6390474600233486704L;
262 
263                 @Override
264                 protected Iterator<String> getChoices(final String input) {
265                     return (RealmsUtils.checkInput(input)
266                             ? searchRealms(input)
267                             : List.<String>of()).iterator();
268                 }
269             };
270 
271             if (taskTO instanceof PullTaskTO) {
272                 destinationRealm.addRequiredLabel();
273                 if (StringUtils.isBlank(PullTaskTO.class.cast(taskTO).getDestinationRealm())) {
274                     // add a default destination realm if missing in the task
275                     destinationRealm.setModelObject(SyncopeConstants.ROOT_REALM);
276                 }
277             }
278             pullTaskSpecifics.add(destinationRealm);
279 
280             AjaxCheckBoxPanel remediation = new AjaxCheckBoxPanel(
281                     "remediation", "remediation", new PropertyModel<>(taskTO, "remediation"), false);
282             pullTaskSpecifics.add(remediation);
283 
284             // ------------------------------
285             // Only for push tasks
286             // ------------------------------
287             WebMarkupContainer pushTaskSpecifics = new WebMarkupContainer("pushTaskSpecifics");
288             add(pushTaskSpecifics.setRenderBodyOnly(true));
289 
290             if (!(taskTO instanceof PushTaskTO)) {
291                 pushTaskSpecifics.setEnabled(false).setVisible(false);
292             }
293 
294             AjaxSearchFieldPanel sourceRealm = new AjaxSearchFieldPanel(
295                     "sourceRealm", "sourceRealm", new PropertyModel<>(taskTO, "sourceRealm"), settings) {
296 
297                 private static final long serialVersionUID = -6390474600233486704L;
298 
299                 @Override
300                 protected Iterator<String> getChoices(final String input) {
301                     return (RealmsUtils.checkInput(input)
302                             ? searchRealms(input)
303                             : List.<String>of()).iterator();
304                 }
305             };
306 
307             if (taskTO instanceof PushTaskTO) {
308                 sourceRealm.addRequiredLabel();
309             }
310             pushTaskSpecifics.add(sourceRealm);
311 
312             // ------------------------------
313             // For push and pull tasks
314             // ------------------------------
315             WebMarkupContainer provisioningTaskSpecifics = new WebMarkupContainer("provisioningTaskSpecifics");
316             add(provisioningTaskSpecifics.setOutputMarkupId(true));
317 
318             if (taskTO instanceof ProvisioningTaskTO) {
319                 jobDelegate.setEnabled(false).setVisible(false);
320                 macroTaskSpecifics.setEnabled(false).setVisible(false);
321             } else if (taskTO instanceof MacroTaskTO) {
322                 jobDelegate.setEnabled(false).setVisible(false);
323                 provisioningTaskSpecifics.setEnabled(false).setVisible(false);
324             } else {
325                 provisioningTaskSpecifics.setEnabled(false).setVisible(false);
326                 macroTaskSpecifics.setEnabled(false).setVisible(false);
327             }
328 
329             AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
330                     setAllowMoveAll(true).setAllowOrder(true).
331                     build("actions",
332                             new PropertyModel<>(taskTO, "actions"),
333                             new ListModel<>(taskTO instanceof PushTaskTO
334                                     ? pushActions.getObject() : pullActions.getObject()));
335             provisioningTaskSpecifics.add(actions.setOutputMarkupId(true));
336 
337             AjaxDropDownChoicePanel<MatchingRule> matchingRule = new AjaxDropDownChoicePanel<>(
338                     "matchingRule", "matchingRule", new PropertyModel<>(taskTO, "matchingRule"), false);
339             matchingRule.setChoices(List.of(MatchingRule.values()));
340             provisioningTaskSpecifics.add(matchingRule);
341 
342             AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
343                     "unmatchingRule", "unmatchingRule", new PropertyModel<>(taskTO, "unmatchingRule"), false);
344             unmatchingRule.setChoices(List.of(UnmatchingRule.values()));
345             provisioningTaskSpecifics.add(unmatchingRule);
346 
347             AjaxCheckBoxPanel performCreate = new AjaxCheckBoxPanel(
348                     "performCreate", "performCreate", new PropertyModel<>(taskTO, "performCreate"), false);
349             provisioningTaskSpecifics.add(performCreate);
350 
351             AjaxCheckBoxPanel performUpdate = new AjaxCheckBoxPanel(
352                     "performUpdate", "performUpdate", new PropertyModel<>(taskTO, "performUpdate"), false);
353             provisioningTaskSpecifics.add(performUpdate);
354 
355             AjaxCheckBoxPanel performDelete = new AjaxCheckBoxPanel(
356                     "performDelete", "performDelete", new PropertyModel<>(taskTO, "performDelete"), false);
357             provisioningTaskSpecifics.add(performDelete);
358 
359             AjaxCheckBoxPanel syncStatus = new AjaxCheckBoxPanel(
360                     "syncStatus", "syncStatus", new PropertyModel<>(taskTO, "syncStatus"), false);
361             provisioningTaskSpecifics.add(syncStatus);
362 
363             // Concurrent settings
364             PropertyModel<ThreadPoolSettings> concurrentSettingsModel =
365                     new PropertyModel<>(taskTO, "concurrentSettings");
366 
367             AjaxCheckBoxPanel enableConcurrentSettings = new AjaxCheckBoxPanel(
368                     "enableConcurrentSettings", "enableConcurrentSettings", new IModel<Boolean>() {
369 
370                 private static final long serialVersionUID = -7126718045816207110L;
371 
372                 @Override
373                 public Boolean getObject() {
374                     return concurrentSettingsModel.getObject() != null;
375                 }
376 
377                 @Override
378                 public void setObject(final Boolean object) {
379                     // nothing to do
380                 }
381             });
382             provisioningTaskSpecifics.add(enableConcurrentSettings.
383                     setVisible(taskTO instanceof ProvisioningTaskTO).setOutputMarkupId(true));
384 
385             FieldPanel<Integer> corePoolSize = new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
386                     "corePoolSize",
387                     "corePoolSize",
388                     Integer.class,
389                     new ConcurrentSettingsValueModel(concurrentSettingsModel, "corePoolSize")).setRequired(true);
390             corePoolSize.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
391             corePoolSize.setVisible(taskTO instanceof ProvisioningTaskTO
392                     ? concurrentSettingsModel.getObject() != null
393                     : false);
394             provisioningTaskSpecifics.add(corePoolSize);
395 
396             FieldPanel<Integer> maxPoolSize = new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
397                     "maxPoolSize",
398                     "maxPoolSize",
399                     Integer.class,
400                     new ConcurrentSettingsValueModel(concurrentSettingsModel, "maxPoolSize")).setRequired(true);
401             maxPoolSize.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
402             maxPoolSize.setVisible(taskTO instanceof ProvisioningTaskTO
403                     ? concurrentSettingsModel.getObject() != null
404                     : false);
405             provisioningTaskSpecifics.add(maxPoolSize);
406 
407             FieldPanel<Integer> queueCapacity = new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
408                     "queueCapacity",
409                     "queueCapacity",
410                     Integer.class,
411                     new ConcurrentSettingsValueModel(concurrentSettingsModel, "queueCapacity")).setRequired(true);
412             queueCapacity.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
413             queueCapacity.setVisible(taskTO instanceof ProvisioningTaskTO
414                     ? concurrentSettingsModel.getObject() != null
415                     : false);
416             provisioningTaskSpecifics.add(queueCapacity);
417 
418             enableConcurrentSettings.getField().add(
419                     new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
420 
421                 private static final long serialVersionUID = -1107858522700306810L;
422 
423                 @Override
424                 protected void onUpdate(final AjaxRequestTarget target) {
425                     if (concurrentSettingsModel.getObject() == null) {
426                         concurrentSettingsModel.setObject(new ThreadPoolSettings());
427                     } else {
428                         concurrentSettingsModel.setObject(null);
429                     }
430 
431                     corePoolSize.setVisible(concurrentSettingsModel.getObject() != null);
432                     maxPoolSize.setVisible(concurrentSettingsModel.getObject() != null);
433                     queueCapacity.setVisible(concurrentSettingsModel.getObject() != null);
434 
435                     target.add(provisioningTaskSpecifics);
436                 }
437             });
438         }
439     }
440 
441     protected static class ConcurrentSettingsValueModel implements IModel<Integer> {
442 
443         private static final long serialVersionUID = 8869612332790116116L;
444 
445         private final PropertyModel<ThreadPoolSettings> concurrentSettingsModel;
446 
447         private final String property;
448 
449         public ConcurrentSettingsValueModel(
450                 final PropertyModel<ThreadPoolSettings> concurrentSettingsModel,
451                 final String property) {
452 
453             this.concurrentSettingsModel = concurrentSettingsModel;
454             this.property = property;
455         }
456 
457         @Override
458         public Integer getObject() {
459             return Optional.ofNullable(concurrentSettingsModel.getObject()).
460                     map(s -> (Integer) PropertyAccessorFactory.forBeanPropertyAccess(s).getPropertyValue(property)).
461                     orElse(null);
462         }
463 
464         @Override
465         public void setObject(final Integer object) {
466             Optional.ofNullable(concurrentSettingsModel.getObject()).
467                     ifPresent(s -> PropertyAccessorFactory.forBeanPropertyAccess(s).setPropertyValue(property, object));
468         }
469     }
470 
471     protected class Schedule extends WizardStep {
472 
473         private static final long serialVersionUID = -785981096328637758L;
474 
475         protected Schedule(final SchedTaskTO taskTO) {
476             crontabPanel = new CrontabPanel(
477                     "schedule", new PropertyModel<>(taskTO, "cronExpression"), taskTO.getCronExpression());
478             add(crontabPanel);
479         }
480     }
481 }