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.core.provisioning.java.pushpull;
20  
21  import java.lang.reflect.ParameterizedType;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.List;
25  import java.util.Objects;
26  import java.util.Optional;
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.syncope.common.lib.to.Mapping;
29  import org.apache.syncope.common.lib.to.Provision;
30  import org.apache.syncope.common.lib.to.ProvisioningReport;
31  import org.apache.syncope.common.lib.types.AnyTypeKind;
32  import org.apache.syncope.common.lib.types.TraceLevel;
33  import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
34  import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
35  import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
36  import org.apache.syncope.core.persistence.api.entity.EntityFactory;
37  import org.apache.syncope.core.persistence.api.entity.ExternalResource;
38  import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
39  import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
40  import org.apache.syncope.core.provisioning.api.Connector;
41  import org.apache.syncope.core.provisioning.api.ConnectorManager;
42  import org.apache.syncope.core.provisioning.api.ProvisionSorter;
43  import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate;
44  import org.apache.syncope.core.provisioning.java.job.TaskJob;
45  import org.apache.syncope.core.spring.implementation.ImplementationManager;
46  import org.quartz.JobExecutionContext;
47  import org.quartz.JobExecutionException;
48  import org.springframework.beans.factory.annotation.Autowired;
49  
50  public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask<T>>
51          extends AbstractSchedTaskJobDelegate<T> {
52  
53      private static final String USER = "USER";
54  
55      private static final String GROUP = "GROUP";
56  
57      private static final String LINKED_ACCOUNT = "LINKED_ACCOUNT";
58  
59      /**
60       * ConnInstance loader.
61       */
62      @Autowired
63      protected ConnectorManager connectorManager;
64  
65      /**
66       * AnyTypeDAO DAO
67       */
68      @Autowired
69      protected AnyTypeDAO anyTypeDAO;
70  
71      /**
72       * Resource DAO.
73       */
74      @Autowired
75      protected ExternalResourceDAO resourceDAO;
76  
77      @Autowired
78      protected EntityFactory entityFactory;
79  
80      /**
81       * Policy DAO.
82       */
83      @Autowired
84      protected PolicyDAO policyDAO;
85  
86      protected Optional<ProvisionSorter> perContextProvisionSorter = Optional.empty();
87  
88      protected ProvisionSorter getProvisionSorter(final T task) {
89          if (task.getResource().getProvisionSorter() != null) {
90              try {
91                  return ImplementationManager.build(
92                          task.getResource().getProvisionSorter(),
93                          () -> perContextProvisionSorter.orElse(null),
94                          instance -> perContextProvisionSorter = Optional.of(instance));
95              } catch (Exception e) {
96                  LOG.error("While building {}", task.getResource().getProvisionSorter(), e);
97              }
98          }
99  
100         if (perContextProvisionSorter.isEmpty()) {
101             perContextProvisionSorter = Optional.of(new DefaultProvisionSorter());
102         }
103         return perContextProvisionSorter.get();
104     }
105 
106     /**
107      * Helper method to invoke logging per provisioning result, for the given trace level.
108      *
109      * @param results provisioning results
110      * @param level trace level
111      * @return report as string
112      */
113     protected String generate(final Collection<ProvisioningReport> results, final TraceLevel level) {
114         StringBuilder sb = new StringBuilder();
115 
116         results.stream().map(result -> {
117             if (level == TraceLevel.SUMMARY) {
118                 // No per entry log in this case.
119                 return null;
120             } else if (level == TraceLevel.FAILURES && result.getStatus() == ProvisioningReport.Status.FAILURE) {
121                 // only report failures
122                 return String.format("Failed %s (key/name): %s/%s with message: %s",
123                         result.getOperation(), result.getKey(), result.getName(), result.getMessage());
124             } else {
125                 // All
126                 return String.format("%s %s (key/name): %s/%s %s",
127                         result.getOperation(), result.getStatus(), result.getKey(), result.getName(),
128                         StringUtils.isBlank(result.getMessage())
129                         ? StringUtils.EMPTY
130                         : "with message: " + result.getMessage());
131             }
132         }).filter(Objects::nonNull).forEach(report -> sb.append(report).append('\n'));
133 
134         return sb.toString();
135     }
136 
137     /**
138      * Create a textual report of the provisioning operation, based on the trace level.
139      *
140      * @param provResults Provisioning results
141      * @param resource Provisioning resource
142      * @param dryRun dry run?
143      * @return report as string
144      */
145     protected String createReport(
146             final Collection<ProvisioningReport> provResults,
147             final ExternalResource resource,
148             final boolean dryRun) {
149 
150         TraceLevel traceLevel = resource.getProvisioningTraceLevel();
151         if (traceLevel == TraceLevel.NONE) {
152             return null;
153         }
154 
155         StringBuilder report = new StringBuilder();
156 
157         if (dryRun) {
158             report.append("==> Dry run only, no modifications were made <==\n\n");
159         }
160         if (interrupted) {
161             report.append("==> Execution was interrupted <==\n\n");
162         }
163 
164         List<ProvisioningReport> rSuccCreate = new ArrayList<>();
165         List<ProvisioningReport> rFailCreate = new ArrayList<>();
166         List<ProvisioningReport> rSuccUpdate = new ArrayList<>();
167         List<ProvisioningReport> rFailUpdate = new ArrayList<>();
168         List<ProvisioningReport> rSuccDelete = new ArrayList<>();
169         List<ProvisioningReport> rFailDelete = new ArrayList<>();
170         List<ProvisioningReport> rSuccNone = new ArrayList<>();
171         List<ProvisioningReport> rIgnore = new ArrayList<>();
172         List<ProvisioningReport> uSuccCreate = new ArrayList<>();
173         List<ProvisioningReport> uFailCreate = new ArrayList<>();
174         List<ProvisioningReport> uSuccUpdate = new ArrayList<>();
175         List<ProvisioningReport> uFailUpdate = new ArrayList<>();
176         List<ProvisioningReport> uSuccDelete = new ArrayList<>();
177         List<ProvisioningReport> uFailDelete = new ArrayList<>();
178         List<ProvisioningReport> uSuccNone = new ArrayList<>();
179         List<ProvisioningReport> uIgnore = new ArrayList<>();
180         List<ProvisioningReport> gSuccCreate = new ArrayList<>();
181         List<ProvisioningReport> gFailCreate = new ArrayList<>();
182         List<ProvisioningReport> gSuccUpdate = new ArrayList<>();
183         List<ProvisioningReport> gFailUpdate = new ArrayList<>();
184         List<ProvisioningReport> gSuccDelete = new ArrayList<>();
185         List<ProvisioningReport> gFailDelete = new ArrayList<>();
186         List<ProvisioningReport> gSuccNone = new ArrayList<>();
187         List<ProvisioningReport> gIgnore = new ArrayList<>();
188         List<ProvisioningReport> aSuccCreate = new ArrayList<>();
189         List<ProvisioningReport> aFailCreate = new ArrayList<>();
190         List<ProvisioningReport> aSuccUpdate = new ArrayList<>();
191         List<ProvisioningReport> aFailUpdate = new ArrayList<>();
192         List<ProvisioningReport> aSuccDelete = new ArrayList<>();
193         List<ProvisioningReport> aFailDelete = new ArrayList<>();
194         List<ProvisioningReport> aSuccNone = new ArrayList<>();
195         List<ProvisioningReport> aIgnore = new ArrayList<>();
196         List<ProvisioningReport> laSuccCreate = new ArrayList<>();
197         List<ProvisioningReport> laFailCreate = new ArrayList<>();
198         List<ProvisioningReport> laSuccUpdate = new ArrayList<>();
199         List<ProvisioningReport> laFailUpdate = new ArrayList<>();
200         List<ProvisioningReport> laSuccDelete = new ArrayList<>();
201         List<ProvisioningReport> laFailDelete = new ArrayList<>();
202         List<ProvisioningReport> laSuccNone = new ArrayList<>();
203         List<ProvisioningReport> laIgnore = new ArrayList<>();
204 
205         for (ProvisioningReport provResult : provResults) {
206             switch (provResult.getStatus()) {
207                 case SUCCESS:
208                     switch (provResult.getOperation()) {
209                         case CREATE:
210                             if (StringUtils.isBlank(provResult.getAnyType())) {
211                                 rSuccCreate.add(provResult);
212                             } else {
213                                 switch (provResult.getAnyType()) {
214                                     case USER:
215                                         uSuccCreate.add(provResult);
216                                         break;
217 
218                                     case LINKED_ACCOUNT:
219                                         laSuccCreate.add(provResult);
220                                         break;
221 
222                                     case GROUP:
223                                         gSuccCreate.add(provResult);
224                                         break;
225 
226                                     default:
227                                         aSuccCreate.add(provResult);
228                                 }
229                             }
230                             break;
231 
232                         case UPDATE:
233                             if (StringUtils.isBlank(provResult.getAnyType())) {
234                                 rSuccUpdate.add(provResult);
235                             } else {
236                                 switch (provResult.getAnyType()) {
237                                     case USER:
238                                         uSuccUpdate.add(provResult);
239                                         break;
240 
241                                     case LINKED_ACCOUNT:
242                                         laSuccUpdate.add(provResult);
243                                         break;
244 
245                                     case GROUP:
246                                         gSuccUpdate.add(provResult);
247                                         break;
248 
249                                     default:
250                                         aSuccUpdate.add(provResult);
251                                 }
252                             }
253                             break;
254 
255                         case DELETE:
256                             if (StringUtils.isBlank(provResult.getAnyType())) {
257                                 rSuccDelete.add(provResult);
258                             } else {
259                                 switch (provResult.getAnyType()) {
260                                     case USER:
261                                         uSuccDelete.add(provResult);
262                                         break;
263 
264                                     case LINKED_ACCOUNT:
265                                         laSuccDelete.add(provResult);
266                                         break;
267 
268                                     case GROUP:
269                                         gSuccDelete.add(provResult);
270                                         break;
271 
272                                     default:
273                                         aSuccDelete.add(provResult);
274                                 }
275                             }
276                             break;
277 
278                         case NONE:
279                             if (StringUtils.isBlank(provResult.getAnyType())) {
280                                 rSuccNone.add(provResult);
281                             } else {
282                                 switch (provResult.getAnyType()) {
283                                     case USER:
284                                         uSuccNone.add(provResult);
285                                         break;
286 
287                                     case LINKED_ACCOUNT:
288                                         laSuccNone.add(provResult);
289                                         break;
290 
291                                     case GROUP:
292                                         gSuccNone.add(provResult);
293                                         break;
294 
295                                     default:
296                                         aSuccNone.add(provResult);
297                                 }
298                             }
299                             break;
300 
301                         default:
302                     }
303                     break;
304 
305                 case FAILURE:
306                     switch (provResult.getOperation()) {
307                         case CREATE:
308                             if (StringUtils.isBlank(provResult.getAnyType())) {
309                                 rFailCreate.add(provResult);
310                             } else {
311                                 switch (provResult.getAnyType()) {
312                                     case USER:
313                                         uFailCreate.add(provResult);
314                                         break;
315 
316                                     case LINKED_ACCOUNT:
317                                         laFailCreate.add(provResult);
318                                         break;
319 
320                                     case GROUP:
321                                         gFailCreate.add(provResult);
322                                         break;
323 
324                                     default:
325                                         aFailCreate.add(provResult);
326                                 }
327                             }
328                             break;
329 
330                         case UPDATE:
331                             if (StringUtils.isBlank(provResult.getAnyType())) {
332                                 rFailUpdate.add(provResult);
333                             } else {
334                                 switch (provResult.getAnyType()) {
335                                     case USER:
336                                         uFailUpdate.add(provResult);
337                                         break;
338 
339                                     case LINKED_ACCOUNT:
340                                         laFailUpdate.add(provResult);
341                                         break;
342 
343                                     case GROUP:
344                                         gFailUpdate.add(provResult);
345                                         break;
346 
347                                     default:
348                                         aFailUpdate.add(provResult);
349                                 }
350                             }
351                             break;
352 
353                         case DELETE:
354                             if (StringUtils.isBlank(provResult.getAnyType())) {
355                                 rFailDelete.add(provResult);
356                             } else {
357                                 switch (provResult.getAnyType()) {
358                                     case USER:
359                                         uFailDelete.add(provResult);
360                                         break;
361 
362                                     case LINKED_ACCOUNT:
363                                         laFailDelete.add(provResult);
364                                         break;
365 
366                                     case GROUP:
367                                         gFailDelete.add(provResult);
368                                         break;
369 
370                                     default:
371                                         aFailDelete.add(provResult);
372                                 }
373                             }
374                             break;
375 
376                         default:
377                     }
378                     break;
379 
380                 case IGNORE:
381                     if (StringUtils.isBlank(provResult.getAnyType())) {
382                         rIgnore.add(provResult);
383                     } else {
384                         switch (provResult.getAnyType()) {
385                             case USER:
386                                 uIgnore.add(provResult);
387                                 break;
388 
389                             case LINKED_ACCOUNT:
390                                 laIgnore.add(provResult);
391                                 break;
392 
393                             case GROUP:
394                                 gIgnore.add(provResult);
395                                 break;
396 
397                             default:
398                                 aIgnore.add(provResult);
399                         }
400                     }
401                     break;
402 
403                 default:
404             }
405         }
406 
407         // Summary, also to be included for FAILURE and ALL, so create it anyway.
408         boolean includeUser = resource.getProvisionByAnyType(AnyTypeKind.USER.name()).isPresent();
409         boolean includeGroup = resource.getProvisionByAnyType(AnyTypeKind.GROUP.name()).isPresent();
410         boolean includeAnyObject = resource.getProvisions().stream().anyMatch(
411                 provision -> !provision.getAnyType().equals(AnyTypeKind.USER.name())
412                 && !provision.getAnyType().equals(AnyTypeKind.GROUP.name()));
413         boolean includeRealm = resource.getOrgUnit() != null;
414 
415         if (includeUser) {
416             report.append("Users ").
417                     append("[created/failures]: ").append(uSuccCreate.size()).append('/').append(uFailCreate.size()).
418                     append(' ').
419                     append("[updated/failures]: ").append(uSuccUpdate.size()).append('/').append(uFailUpdate.size()).
420                     append(' ').
421                     append("[deleted/failures]: ").append(uSuccDelete.size()).append('/').append(uFailDelete.size()).
422                     append(' ').
423                     append("[no operation/ignored]: ").append(uSuccNone.size()).append('/').append(uIgnore.size()).
424                     append('\n');
425 
426             report.append("Accounts ").
427                     append("[created/failures]: ").append(laSuccCreate.size()).append('/').append(laFailCreate.size()).
428                     append(' ').
429                     append("[updated/failures]: ").append(laSuccUpdate.size()).append('/').append(laFailUpdate.size()).
430                     append(' ').
431                     append("[deleted/failures]: ").append(laSuccDelete.size()).append('/').append(laFailDelete.size()).
432                     append(' ').
433                     append("[no operation/ignored]: ").append(laSuccNone.size()).append('/').append(laIgnore.size()).
434                     append('\n');
435         }
436         if (includeGroup) {
437             report.append("Groups ").
438                     append("[created/failures]: ").append(gSuccCreate.size()).append('/').append(gFailCreate.size()).
439                     append(' ').
440                     append("[updated/failures]: ").append(gSuccUpdate.size()).append('/').append(gFailUpdate.size()).
441                     append(' ').
442                     append("[deleted/failures]: ").append(gSuccDelete.size()).append('/').append(gFailDelete.size()).
443                     append(' ').
444                     append("[no operation/ignored]: ").append(gSuccNone.size()).append('/').append(gIgnore.size()).
445                     append('\n');
446         }
447         if (includeAnyObject) {
448             report.append("Any objects ").
449                     append("[created/failures]: ").append(aSuccCreate.size()).append('/').append(aFailCreate.size()).
450                     append(' ').
451                     append("[updated/failures]: ").append(aSuccUpdate.size()).append('/').append(aFailUpdate.size()).
452                     append(' ').
453                     append("[deleted/failures]: ").append(aSuccDelete.size()).append('/').append(aFailDelete.size()).
454                     append(' ').
455                     append("[no operation/ignored]: ").append(aSuccNone.size()).append('/').append(aIgnore.size());
456         }
457         if (includeRealm) {
458             report.append("Realms ").
459                     append("[created/failures]: ").append(rSuccCreate.size()).append('/').append(rFailCreate.size()).
460                     append(' ').
461                     append("[updated/failures]: ").append(rSuccUpdate.size()).append('/').append(rFailUpdate.size()).
462                     append(' ').
463                     append("[deleted/failures]: ").append(rSuccDelete.size()).append('/').append(rFailDelete.size()).
464                     append(' ').
465                     append("[no operation/ignored]: ").append(rSuccNone.size()).append('/').append(rIgnore.size());
466         }
467 
468         // Failures
469         if (traceLevel == TraceLevel.FAILURES || traceLevel == TraceLevel.ALL) {
470             if (includeUser) {
471                 if (!uFailCreate.isEmpty()) {
472                     report.append("\n\nUsers failed to create: ");
473                     report.append(generate(uFailCreate, traceLevel));
474                 }
475                 if (!uFailUpdate.isEmpty()) {
476                     report.append("\nUsers failed to update: ");
477                     report.append(generate(uFailUpdate, traceLevel));
478                 }
479                 if (!uFailDelete.isEmpty()) {
480                     report.append("\nUsers failed to delete: ");
481                     report.append(generate(uFailDelete, traceLevel));
482                 }
483 
484                 if (!laFailCreate.isEmpty()) {
485                     report.append("\n\nAccounts failed to create: ");
486                     report.append(generate(laFailCreate, traceLevel));
487                 }
488                 if (!laFailUpdate.isEmpty()) {
489                     report.append("\nAccounts failed to update: ");
490                     report.append(generate(laFailUpdate, traceLevel));
491                 }
492                 if (!laFailDelete.isEmpty()) {
493                     report.append("\nAccounts failed to delete: ");
494                     report.append(generate(laFailDelete, traceLevel));
495                 }
496             }
497 
498             if (includeGroup) {
499                 if (!gFailCreate.isEmpty()) {
500                     report.append("\n\nGroups failed to create: ");
501                     report.append(generate(gFailCreate, traceLevel));
502                 }
503                 if (!gFailUpdate.isEmpty()) {
504                     report.append("\nGroups failed to update: ");
505                     report.append(generate(gFailUpdate, traceLevel));
506                 }
507                 if (!gFailDelete.isEmpty()) {
508                     report.append("\nGroups failed to delete: ");
509                     report.append(generate(gFailDelete, traceLevel));
510                 }
511             }
512 
513             if (includeAnyObject && !aFailCreate.isEmpty()) {
514                 report.append("\nAny objects failed to create: ");
515                 report.append(generate(aFailCreate, traceLevel));
516             }
517             if (includeAnyObject && !aFailUpdate.isEmpty()) {
518                 report.append("\nAny objects failed to update: ");
519                 report.append(generate(aFailUpdate, traceLevel));
520             }
521             if (includeAnyObject && !aFailDelete.isEmpty()) {
522                 report.append("\nAny objects failed to delete: ");
523                 report.append(generate(aFailDelete, traceLevel));
524             }
525 
526             if (includeRealm) {
527                 if (!rFailCreate.isEmpty()) {
528                     report.append("\nRealms failed to create: ");
529                     report.append(generate(rFailCreate, traceLevel));
530                 }
531                 if (!rFailUpdate.isEmpty()) {
532                     report.append("\nRealms failed to update: ");
533                     report.append(generate(rFailUpdate, traceLevel));
534                 }
535                 if (!rFailDelete.isEmpty()) {
536                     report.append("\nRealms failed to delete: ");
537                     report.append(generate(rFailDelete, traceLevel));
538                 }
539             }
540         }
541 
542         // Succeeded, only if on 'ALL' level
543         if (traceLevel == TraceLevel.ALL) {
544             if (includeUser) {
545                 if (!uSuccCreate.isEmpty()) {
546                     report.append("\n\nUsers created:\n").
547                             append(generate(uSuccCreate, traceLevel));
548                 }
549                 if (!uSuccUpdate.isEmpty()) {
550                     report.append("\nUsers updated:\n").
551                             append(generate(uSuccUpdate, traceLevel));
552                 }
553                 if (!uSuccDelete.isEmpty()) {
554                     report.append("\nUsers deleted:\n").
555                             append(generate(uSuccDelete, traceLevel));
556                 }
557                 if (!uSuccNone.isEmpty()) {
558                     report.append("\nUsers no operation:\n").
559                             append(generate(uSuccNone, traceLevel));
560                 }
561                 if (!uIgnore.isEmpty()) {
562                     report.append("\nUsers ignored:\n").
563                             append(generate(uIgnore, traceLevel));
564                 }
565 
566                 if (!laSuccCreate.isEmpty()) {
567                     report.append("\n\nAccounts created:\n").
568                             append(generate(laSuccCreate, traceLevel));
569                 }
570                 if (!laSuccUpdate.isEmpty()) {
571                     report.append("\nAccounts updated:\n").
572                             append(generate(laSuccUpdate, traceLevel));
573                 }
574                 if (!laSuccDelete.isEmpty()) {
575                     report.append("\nAccounts deleted:\n").
576                             append(generate(laSuccDelete, traceLevel));
577                 }
578                 if (!laSuccNone.isEmpty()) {
579                     report.append("\nAccounts no operation:\n").
580                             append(generate(laSuccNone, traceLevel));
581                 }
582                 if (!laIgnore.isEmpty()) {
583                     report.append("\nAccounts ignored:\n").
584                             append(generate(laIgnore, traceLevel));
585                 }
586             }
587             if (includeGroup) {
588                 if (!gSuccCreate.isEmpty()) {
589                     report.append("\n\nGroups created:\n").
590                             append(generate(gSuccCreate, traceLevel));
591                 }
592                 if (!gSuccUpdate.isEmpty()) {
593                     report.append("\nGroups updated:\n").
594                             append(generate(gSuccUpdate, traceLevel));
595                 }
596                 if (!gSuccDelete.isEmpty()) {
597                     report.append("\nGroups deleted:\n").
598                             append(generate(gSuccDelete, traceLevel));
599                 }
600                 if (!gSuccNone.isEmpty()) {
601                     report.append("\nGroups no operation:\n").
602                             append(generate(gSuccNone, traceLevel));
603                 }
604                 if (!gIgnore.isEmpty()) {
605                     report.append("\nGroups ignored:\n").
606                             append(generate(gIgnore, traceLevel));
607                 }
608             }
609             if (includeAnyObject) {
610                 if (!aSuccCreate.isEmpty()) {
611                     report.append("\n\nAny objects created:\n").
612                             append(generate(aSuccCreate, traceLevel));
613                 }
614                 if (!aSuccUpdate.isEmpty()) {
615                     report.append("\nAny objects updated:\n").
616                             append(generate(aSuccUpdate, traceLevel));
617                 }
618                 if (!aSuccDelete.isEmpty()) {
619                     report.append("\nAny objects deleted:\n").
620                             append(generate(aSuccDelete, traceLevel));
621                 }
622                 if (!aSuccNone.isEmpty()) {
623                     report.append("\nAny objects no operation:\n").
624                             append(generate(aSuccNone, traceLevel));
625                 }
626                 if (!aIgnore.isEmpty()) {
627                     report.append("\nAny objects ignored:\n").
628                             append(generate(aIgnore, traceLevel));
629                 }
630             }
631             if (includeRealm) {
632                 if (!rSuccCreate.isEmpty()) {
633                     report.append("\n\nRealms created:\n").
634                             append(generate(rSuccCreate, traceLevel));
635                 }
636                 if (!rSuccUpdate.isEmpty()) {
637                     report.append("\nRealms updated:\n").
638                             append(generate(rSuccUpdate, traceLevel));
639                 }
640                 if (!rSuccDelete.isEmpty()) {
641                     report.append("\nRealms deleted:\n").
642                             append(generate(rSuccDelete, traceLevel));
643                 }
644                 if (!rSuccNone.isEmpty()) {
645                     report.append("\nRealms no operation:\n").
646                             append(generate(rSuccNone, traceLevel));
647                 }
648                 if (!rIgnore.isEmpty()) {
649                     report.append("\nRealms ignored:\n").
650                             append(generate(rIgnore, traceLevel));
651                 }
652             }
653         }
654 
655         return report.toString();
656     }
657 
658     protected Connector getConnector(final T provisioningTask) throws JobExecutionException {
659         Connector connector;
660         try {
661             connector = connectorManager.getConnector(provisioningTask.getResource());
662         } catch (Exception e) {
663             String msg = String.format("Connector instance bean for resource %s and connInstance %s not found",
664                     provisioningTask.getResource(), provisioningTask.getResource().getConnector());
665             throw new JobExecutionException(msg, e);
666         }
667 
668         return connector;
669     }
670 
671     @Override
672     protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context)
673             throws JobExecutionException {
674 
675         try {
676             Class<T> clazz = getTaskClassReference();
677             if (!clazz.isAssignableFrom(task.getClass())) {
678                 throw new JobExecutionException("Task " + task.getKey() + " isn't a ProvisioningTask");
679             }
680 
681             T provisioningTask = clazz.cast(task);
682 
683             Connector connector = getConnector(provisioningTask);
684 
685             boolean noMapping = true;
686             for (Provision provision : provisioningTask.getResource().getProvisions()) {
687                 Mapping mapping = provision.getMapping();
688                 if (mapping != null) {
689                     noMapping = false;
690                     if (mapping.getConnObjectKeyItem() == null) {
691                         throw new JobExecutionException("Invalid ConnObjectKey mapping for provision " + provision);
692                     }
693                 }
694             }
695             if (noMapping) {
696                 noMapping = provisioningTask.getResource().getOrgUnit() == null;
697             }
698             if (noMapping) {
699                 return "No provisions nor orgUnit available: aborting...";
700             }
701 
702             return doExecuteProvisioning(provisioningTask, connector, dryRun, executor, context);
703         } catch (Throwable t) {
704             LOG.error("While executing provisioning job {}", getClass().getName(), t);
705             throw t;
706         }
707     }
708 
709     protected abstract String doExecuteProvisioning(
710             T task, Connector connector, boolean dryRun, String executor, JobExecutionContext context)
711             throws JobExecutionException;
712 
713     @Override
714     protected boolean hasToBeRegistered(final TaskExec<?> execution) {
715         // True if either failed and failures have to be registered, or if ALL has to be registered.
716         return (TaskJob.Status.valueOf(execution.getStatus()) == TaskJob.Status.FAILURE
717                 && task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
718                 || task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal();
719     }
720 
721     @SuppressWarnings("unchecked")
722     protected Class<T> getTaskClassReference() {
723         return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
724     }
725 }