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     public class Profile extends WizardStep {
128 
129         private static final long serialVersionUID = -3043839139187792810L;
130 
131         private final IModel<List<String>> taskJobDelegates = SyncopeWebApplication.get().
132                 getImplementationInfoProvider().getTaskJobDelegates();
133 
134         private final IModel<List<String>> reconFilterBuilders = SyncopeWebApplication.get().
135                 getImplementationInfoProvider().getReconFilterBuilders();
136 
137         private final IModel<List<String>> pullActions = SyncopeWebApplication.get().
138                 getImplementationInfoProvider().getPullActions();
139 
140         private final IModel<List<String>> pushActions = SyncopeWebApplication.get().
141                 getImplementationInfoProvider().getPushActions();
142 
143         public Profile(final SchedTaskTO taskTO) {
144             AjaxTextFieldPanel name = new AjaxTextFieldPanel(
145                     Constants.NAME_FIELD_NAME, Constants.NAME_FIELD_NAME,
146                     new PropertyModel<>(taskTO, Constants.NAME_FIELD_NAME),
147                     false);
148             name.addRequiredLabel();
149             name.setEnabled(true);
150             add(name);
151 
152             AjaxTextFieldPanel description = new AjaxTextFieldPanel(
153                     Constants.DESCRIPTION_FIELD_NAME, Constants.DESCRIPTION_FIELD_NAME,
154                     new PropertyModel<>(taskTO, Constants.DESCRIPTION_FIELD_NAME), false);
155             description.setEnabled(true);
156             add(description);
157 
158             AjaxCheckBoxPanel active = new AjaxCheckBoxPanel(
159                     "active", "active", new PropertyModel<>(taskTO, "active"), false);
160             add(active);
161 
162             AjaxDropDownChoicePanel<String> jobDelegate = new AjaxDropDownChoicePanel<>(
163                     "jobDelegate", "jobDelegate", new PropertyModel<>(taskTO, "jobDelegate"), false);
164             jobDelegate.setChoices(taskJobDelegates.getObject());
165             jobDelegate.addRequiredLabel();
166             jobDelegate.setEnabled(taskTO.getKey() == null);
167             add(jobDelegate);
168 
169             AutoCompleteSettings settings = new AutoCompleteSettings();
170             settings.setShowCompleteListOnFocusGain(fullRealmsTree);
171             settings.setShowListOnEmptyInput(fullRealmsTree);
172 
173             // ------------------------------
174             // Only for macro tasks
175             // ------------------------------            
176             WebMarkupContainer macroTaskSpecifics = new WebMarkupContainer("macroTaskSpecifics");
177             add(macroTaskSpecifics.setRenderBodyOnly(true));
178 
179             AjaxSearchFieldPanel realm =
180                     new AjaxSearchFieldPanel("realm", "realm",
181                             new PropertyModel<>(taskTO, "realm"), settings) {
182 
183                 private static final long serialVersionUID = -6390474600233486704L;
184 
185                 @Override
186                 protected Iterator<String> getChoices(final String input) {
187                     return (RealmsUtils.checkInput(input)
188                             ? searchRealms(input)
189                             : List.<String>of()).iterator();
190                 }
191             };
192 
193             if (taskTO instanceof MacroTaskTO) {
194                 realm.addRequiredLabel();
195                 if (StringUtils.isBlank(MacroTaskTO.class.cast(taskTO).getRealm())) {
196                     // add a default destination realm if missing in the task
197                     realm.setModelObject(SyncopeConstants.ROOT_REALM);
198                 }
199             }
200             macroTaskSpecifics.add(realm);
201 
202             AjaxCheckBoxPanel continueOnError = new AjaxCheckBoxPanel(
203                     "continueOnError", "continueOnError", new PropertyModel<>(taskTO, "continueOnError"), false);
204             macroTaskSpecifics.add(continueOnError);
205 
206             AjaxCheckBoxPanel saveExecs = new AjaxCheckBoxPanel(
207                     "saveExecs", "saveExecs", new PropertyModel<>(taskTO, "saveExecs"), false);
208             macroTaskSpecifics.add(saveExecs);
209 
210             // ------------------------------
211             // Only for pull tasks
212             // ------------------------------            
213             WebMarkupContainer pullTaskSpecifics = new WebMarkupContainer("pullTaskSpecifics");
214             add(pullTaskSpecifics.setRenderBodyOnly(true));
215 
216             boolean isFiltered = false;
217             if (taskTO instanceof PullTaskTO) {
218                 isFiltered = PullTaskTO.class.cast(taskTO).getPullMode() == PullMode.FILTERED_RECONCILIATION;
219             } else {
220                 pullTaskSpecifics.setEnabled(false).setVisible(false);
221             }
222 
223             AjaxDropDownChoicePanel<PullMode> pullMode = new AjaxDropDownChoicePanel<>(
224                     "pullMode", "pullMode", new PropertyModel<>(taskTO, "pullMode"), false);
225             pullMode.setChoices(List.of(PullMode.values()));
226             if (taskTO instanceof PullTaskTO) {
227                 pullMode.addRequiredLabel();
228             }
229             pullMode.setNullValid(!(taskTO instanceof PullTaskTO));
230             pullTaskSpecifics.add(pullMode);
231 
232             AjaxDropDownChoicePanel<String> reconFilterBuilder = new AjaxDropDownChoicePanel<>(
233                     "reconFilterBuilder", "reconFilterBuilder",
234                     new PropertyModel<>(taskTO, "reconFilterBuilder"), false);
235             reconFilterBuilder.setChoices(reconFilterBuilders.getObject());
236             reconFilterBuilder.setEnabled(isFiltered);
237             reconFilterBuilder.setRequired(isFiltered);
238             pullTaskSpecifics.add(reconFilterBuilder);
239 
240             pullMode.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
241 
242                 private static final long serialVersionUID = -1107858522700306810L;
243 
244                 @Override
245                 protected void onUpdate(final AjaxRequestTarget target) {
246                     reconFilterBuilder.setEnabled(
247                             pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
248                     reconFilterBuilder.setRequired(
249                             pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
250                     target.add(reconFilterBuilder);
251                 }
252             });
253 
254             AjaxSearchFieldPanel destinationRealm =
255                     new AjaxSearchFieldPanel("destinationRealm", "destinationRealm",
256                             new PropertyModel<>(taskTO, "destinationRealm"), settings) {
257 
258                 private static final long serialVersionUID = -6390474600233486704L;
259 
260                 @Override
261                 protected Iterator<String> getChoices(final String input) {
262                     return (RealmsUtils.checkInput(input)
263                             ? searchRealms(input)
264                             : List.<String>of()).iterator();
265                 }
266             };
267 
268             if (taskTO instanceof PullTaskTO) {
269                 destinationRealm.addRequiredLabel();
270                 if (StringUtils.isBlank(PullTaskTO.class.cast(taskTO).getDestinationRealm())) {
271                     // add a default destination realm if missing in the task
272                     destinationRealm.setModelObject(SyncopeConstants.ROOT_REALM);
273                 }
274             }
275             pullTaskSpecifics.add(destinationRealm);
276 
277             AjaxCheckBoxPanel remediation = new AjaxCheckBoxPanel(
278                     "remediation", "remediation", new PropertyModel<>(taskTO, "remediation"), false);
279             pullTaskSpecifics.add(remediation);
280 
281             // ------------------------------
282             // Only for push tasks
283             // ------------------------------
284             WebMarkupContainer pushTaskSpecifics = new WebMarkupContainer("pushTaskSpecifics");
285             add(pushTaskSpecifics.setRenderBodyOnly(true));
286 
287             if (!(taskTO instanceof PushTaskTO)) {
288                 pushTaskSpecifics.setEnabled(false).setVisible(false);
289             }
290 
291             AjaxSearchFieldPanel sourceRealm = new AjaxSearchFieldPanel(
292                     "sourceRealm", "sourceRealm", new PropertyModel<>(taskTO, "sourceRealm"), settings) {
293 
294                 private static final long serialVersionUID = -6390474600233486704L;
295 
296                 @Override
297                 protected Iterator<String> getChoices(final String input) {
298                     return (RealmsUtils.checkInput(input)
299                             ? searchRealms(input)
300                             : List.<String>of()).iterator();
301                 }
302             };
303 
304             if (taskTO instanceof PushTaskTO) {
305                 sourceRealm.addRequiredLabel();
306             }
307             pushTaskSpecifics.add(sourceRealm);
308 
309             // ------------------------------
310             // For push and pull tasks
311             // ------------------------------
312             WebMarkupContainer provisioningTaskSpecifics = new WebMarkupContainer("provisioningTaskSpecifics");
313             add(provisioningTaskSpecifics.setOutputMarkupId(true));
314 
315             if (taskTO instanceof ProvisioningTaskTO) {
316                 jobDelegate.setEnabled(false).setVisible(false);
317                 macroTaskSpecifics.setEnabled(false).setVisible(false);
318             } else if (taskTO instanceof MacroTaskTO) {
319                 jobDelegate.setEnabled(false).setVisible(false);
320                 provisioningTaskSpecifics.setEnabled(false).setVisible(false);
321             } else {
322                 provisioningTaskSpecifics.setEnabled(false).setVisible(false);
323                 macroTaskSpecifics.setEnabled(false).setVisible(false);
324             }
325 
326             AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
327                     setAllowMoveAll(true).setAllowOrder(true).
328                     build("actions",
329                             new PropertyModel<>(taskTO, "actions"),
330                             new ListModel<>(taskTO instanceof PushTaskTO
331                                     ? pushActions.getObject() : pullActions.getObject()));
332             provisioningTaskSpecifics.add(actions.setOutputMarkupId(true));
333 
334             AjaxDropDownChoicePanel<MatchingRule> matchingRule = new AjaxDropDownChoicePanel<>(
335                     "matchingRule", "matchingRule", new PropertyModel<>(taskTO, "matchingRule"), false);
336             matchingRule.setChoices(List.of(MatchingRule.values()));
337             provisioningTaskSpecifics.add(matchingRule);
338 
339             AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
340                     "unmatchingRule", "unmatchingRule", new PropertyModel<>(taskTO, "unmatchingRule"),
341                     false);
342             unmatchingRule.setChoices(List.of(UnmatchingRule.values()));
343             provisioningTaskSpecifics.add(unmatchingRule);
344 
345             AjaxCheckBoxPanel performCreate = new AjaxCheckBoxPanel(
346                     "performCreate", "performCreate", new PropertyModel<>(taskTO, "performCreate"), false);
347             provisioningTaskSpecifics.add(performCreate);
348 
349             AjaxCheckBoxPanel performUpdate = new AjaxCheckBoxPanel(
350                     "performUpdate", "performUpdate", new PropertyModel<>(taskTO, "performUpdate"), false);
351             provisioningTaskSpecifics.add(performUpdate);
352 
353             AjaxCheckBoxPanel performDelete = new AjaxCheckBoxPanel(
354                     "performDelete", "performDelete", new PropertyModel<>(taskTO, "performDelete"), false);
355             provisioningTaskSpecifics.add(performDelete);
356 
357             AjaxCheckBoxPanel syncStatus = new AjaxCheckBoxPanel(
358                     "syncStatus", "syncStatus", new PropertyModel<>(taskTO, "syncStatus"), false);
359             provisioningTaskSpecifics.add(syncStatus);
360 
361             // Concurrent settings
362             PropertyModel<ThreadPoolSettings> concurrentSettingsModel =
363                     new PropertyModel<>(taskTO, "concurrentSettings");
364 
365             AjaxCheckBoxPanel enableConcurrentSettings = new AjaxCheckBoxPanel(
366                     "enableConcurrentSettings", "enableConcurrentSettings", new IModel<Boolean>() {
367 
368                 private static final long serialVersionUID = -7126718045816207110L;
369 
370                 @Override
371                 public Boolean getObject() {
372                     return concurrentSettingsModel.getObject() != null;
373                 }
374 
375                 @Override
376                 public void setObject(final Boolean object) {
377                     // nothing to do
378                 }
379             });
380             provisioningTaskSpecifics.add(enableConcurrentSettings.
381                     setVisible(taskTO instanceof ProvisioningTaskTO).setOutputMarkupId(true));
382 
383             FieldPanel<Integer> corePoolSize = new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
384                     "corePoolSize",
385                     "corePoolSize",
386                     Integer.class,
387                     new ConcurrentSettingsValueModel(concurrentSettingsModel, "corePoolSize")).setRequired(true);
388             corePoolSize.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
389             corePoolSize.setVisible(taskTO instanceof ProvisioningTaskTO
390                     ? concurrentSettingsModel.getObject() != null
391                     : false);
392             provisioningTaskSpecifics.add(corePoolSize);
393 
394             FieldPanel<Integer> maxPoolSize = new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
395                     "maxPoolSize",
396                     "maxPoolSize",
397                     Integer.class,
398                     new ConcurrentSettingsValueModel(concurrentSettingsModel, "maxPoolSize")).setRequired(true);
399             maxPoolSize.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
400             maxPoolSize.setVisible(taskTO instanceof ProvisioningTaskTO
401                     ? concurrentSettingsModel.getObject() != null
402                     : false);
403             provisioningTaskSpecifics.add(maxPoolSize);
404 
405             FieldPanel<Integer> queueCapacity = new AjaxSpinnerFieldPanel.Builder<Integer>().min(1).build(
406                     "queueCapacity",
407                     "queueCapacity",
408                     Integer.class,
409                     new ConcurrentSettingsValueModel(concurrentSettingsModel, "queueCapacity")).setRequired(true);
410             queueCapacity.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
411             queueCapacity.setVisible(taskTO instanceof ProvisioningTaskTO
412                     ? concurrentSettingsModel.getObject() != null
413                     : false);
414             provisioningTaskSpecifics.add(queueCapacity);
415 
416             enableConcurrentSettings.getField().add(
417                     new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
418 
419                 private static final long serialVersionUID = -1107858522700306810L;
420 
421                 @Override
422                 protected void onUpdate(final AjaxRequestTarget target) {
423                     if (concurrentSettingsModel.getObject() == null) {
424                         concurrentSettingsModel.setObject(new ThreadPoolSettings());
425                     } else {
426                         concurrentSettingsModel.setObject(null);
427                     }
428 
429                     corePoolSize.setVisible(concurrentSettingsModel.getObject() != null);
430                     maxPoolSize.setVisible(concurrentSettingsModel.getObject() != null);
431                     queueCapacity.setVisible(concurrentSettingsModel.getObject() != null);
432 
433                     target.add(provisioningTaskSpecifics);
434                 }
435             });
436         }
437     }
438 
439     protected static class ConcurrentSettingsValueModel implements IModel<Integer> {
440 
441         private static final long serialVersionUID = 8869612332790116116L;
442 
443         private final PropertyModel<ThreadPoolSettings> concurrentSettingsModel;
444 
445         private final String property;
446 
447         public ConcurrentSettingsValueModel(
448                 final PropertyModel<ThreadPoolSettings> concurrentSettingsModel,
449                 final String property) {
450 
451             this.concurrentSettingsModel = concurrentSettingsModel;
452             this.property = property;
453         }
454 
455         @Override
456         public Integer getObject() {
457             return Optional.ofNullable(concurrentSettingsModel.getObject()).
458                     map(s -> (Integer) PropertyAccessorFactory.forBeanPropertyAccess(s).getPropertyValue(property)).
459                     orElse(null);
460         }
461 
462         @Override
463         public void setObject(final Integer object) {
464             Optional.ofNullable(concurrentSettingsModel.getObject()).
465                     ifPresent(s -> PropertyAccessorFactory.forBeanPropertyAccess(s).setPropertyValue(property, object));
466         }
467     }
468 
469     public class Schedule extends WizardStep {
470 
471         private static final long serialVersionUID = -785981096328637758L;
472 
473         public Schedule(final SchedTaskTO taskTO) {
474             crontabPanel = new CrontabPanel(
475                     "schedule", new PropertyModel<>(taskTO, "cronExpression"), taskTO.getCronExpression());
476             add(crontabPanel);
477         }
478     }
479 }