| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.syncope.core.logic.init; |
| |
| import java.lang.reflect.Modifier; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import org.apache.syncope.common.lib.policy.AccountRuleConf; |
| import org.apache.syncope.common.lib.policy.PasswordRuleConf; |
| import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; |
| import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf; |
| import org.apache.syncope.common.lib.report.ReportletConf; |
| import org.apache.syncope.common.lib.types.IdMImplementationType; |
| import org.apache.syncope.common.lib.types.IdRepoImplementationType; |
| import org.apache.syncope.common.lib.types.ImplementationTypesHolder; |
| import org.apache.syncope.core.logic.audit.AuditAppender; |
| import org.apache.syncope.core.logic.audit.JdbcAuditAppender; |
| import org.apache.syncope.core.persistence.api.ImplementationLookup; |
| import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator; |
| import org.apache.syncope.core.persistence.api.dao.AccountRule; |
| import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass; |
| import org.apache.syncope.core.persistence.api.dao.PasswordRule; |
| import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass; |
| import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule; |
| import org.apache.syncope.core.persistence.api.dao.PullCorrelationRuleConfClass; |
| import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule; |
| import org.apache.syncope.core.persistence.api.dao.PushCorrelationRuleConfClass; |
| import org.apache.syncope.core.persistence.api.dao.Reportlet; |
| import org.apache.syncope.core.persistence.api.dao.ReportletConfClass; |
| import org.apache.syncope.core.provisioning.api.LogicActions; |
| import org.apache.syncope.core.provisioning.api.ProvisionSorter; |
| import org.apache.syncope.core.provisioning.api.data.ItemTransformer; |
| import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate; |
| import org.apache.syncope.core.provisioning.api.notification.RecipientsProvider; |
| import org.apache.syncope.core.provisioning.api.propagation.PropagationActions; |
| import org.apache.syncope.core.provisioning.api.pushpull.PullActions; |
| import org.apache.syncope.core.provisioning.api.pushpull.PushActions; |
| import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder; |
| import org.apache.syncope.core.provisioning.java.data.JEXLItemTransformerImpl; |
| import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate; |
| import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; |
| import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; |
| import org.apache.syncope.core.spring.security.JWTSSOProvider; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; |
| import org.springframework.core.Ordered; |
| import org.springframework.core.type.filter.AssignableTypeFilter; |
| import org.springframework.util.ClassUtils; |
| |
| /** |
| * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage. |
| */ |
| public class ClassPathScanImplementationLookup implements ImplementationLookup { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(ImplementationLookup.class); |
| |
| private static final String DEFAULT_BASE_PACKAGE = "org.apache.syncope.core"; |
| |
| private Map<String, Set<String>> classNames; |
| |
| private Set<Class<?>> jwtSSOProviderClasses; |
| |
| private Map<Class<? extends ReportletConf>, Class<? extends Reportlet>> reportletClasses; |
| |
| private Map<Class<? extends AccountRuleConf>, Class<? extends AccountRule>> accountRuleClasses; |
| |
| private Map<Class<? extends PasswordRuleConf>, Class<? extends PasswordRule>> passwordRuleClasses; |
| |
| private Map<Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>> pullCRClasses; |
| |
| private Map<Class<? extends PushCorrelationRuleConf>, Class<? extends PushCorrelationRule>> pushCRClasses; |
| |
| private Set<Class<?>> auditAppenderClasses; |
| |
| @Override |
| public int getOrder() { |
| return Ordered.HIGHEST_PRECEDENCE + 1; |
| } |
| |
| /** |
| * This method can be overridden by subclasses to customize classpath scan. |
| * |
| * @return basePackage for classpath scanning |
| */ |
| protected static String getBasePackage() { |
| return DEFAULT_BASE_PACKAGE; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public void load() { |
| classNames = new HashMap<>(); |
| ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); |
| ImplementationTypesHolder.getInstance().getValues().forEach((typeName, typeInterface) -> { |
| classNames.put(typeName, new HashSet<>()); |
| try { |
| scanner.addIncludeFilter(new AssignableTypeFilter( |
| ClassUtils.resolveClassName(typeInterface, ClassUtils.getDefaultClassLoader()))); |
| } catch (IllegalArgumentException e) { |
| LOG.error("Could not find class, ignoring...", e); |
| } |
| }); |
| |
| jwtSSOProviderClasses = new HashSet<>(); |
| reportletClasses = new HashMap<>(); |
| accountRuleClasses = new HashMap<>(); |
| passwordRuleClasses = new HashMap<>(); |
| pullCRClasses = new HashMap<>(); |
| pushCRClasses = new HashMap<>(); |
| auditAppenderClasses = new HashSet<>(); |
| |
| scanner.findCandidateComponents(getBasePackage()).forEach(bd -> { |
| try { |
| Class<?> clazz = ClassUtils.resolveClassName( |
| Objects.requireNonNull(bd.getBeanClassName()), ClassUtils.getDefaultClassLoader()); |
| boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers()); |
| |
| if (JWTSSOProvider.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdRepoImplementationType.JWT_SSO_PROVIDER).add(clazz.getName()); |
| jwtSSOProviderClasses.add(clazz); |
| } |
| |
| if (Reportlet.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class); |
| if (annotation == null) { |
| LOG.warn("Found Reportlet {} without declared configuration", clazz.getName()); |
| } else { |
| classNames.get(IdRepoImplementationType.REPORTLET).add(clazz.getName()); |
| reportletClasses.put(annotation.value(), (Class<? extends Reportlet>) clazz); |
| } |
| } |
| |
| if (AccountRule.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class); |
| if (annotation == null) { |
| LOG.warn("Found account policy rule {} without declared configuration", clazz.getName()); |
| } else { |
| classNames.get(IdRepoImplementationType.ACCOUNT_RULE).add(clazz.getName()); |
| accountRuleClasses.put(annotation.value(), (Class<? extends AccountRule>) clazz); |
| } |
| } |
| |
| if (PasswordRule.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class); |
| if (annotation == null) { |
| LOG.warn("Found password policy rule {} without declared configuration", clazz.getName()); |
| } else { |
| classNames.get(IdRepoImplementationType.PASSWORD_RULE).add(clazz.getName()); |
| passwordRuleClasses.put(annotation.value(), (Class<? extends PasswordRule>) clazz); |
| } |
| } |
| |
| if (PullCorrelationRule.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| PullCorrelationRuleConfClass annotation = clazz.getAnnotation(PullCorrelationRuleConfClass.class); |
| if (annotation == null) { |
| LOG.warn("Found pull correlation rule {} without declared configuration", clazz.getName()); |
| } else { |
| classNames.get(IdMImplementationType.PULL_CORRELATION_RULE).add(clazz.getName()); |
| pullCRClasses.put(annotation.value(), (Class<? extends PullCorrelationRule>) clazz); |
| } |
| } |
| |
| if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| PushCorrelationRuleConfClass annotation = clazz.getAnnotation(PushCorrelationRuleConfClass.class); |
| if (annotation == null) { |
| LOG.warn("Found push correlation rule {} without declared configuration", clazz.getName()); |
| } else { |
| classNames.get(IdMImplementationType.PUSH_CORRELATION_RULE).add(clazz.getName()); |
| pushCRClasses.put(annotation.value(), (Class<? extends PushCorrelationRule>) clazz); |
| } |
| } |
| |
| if (ItemTransformer.class.isAssignableFrom(clazz) && !isAbstractClazz |
| && !clazz.equals(JEXLItemTransformerImpl.class)) { |
| |
| classNames.get(IdRepoImplementationType.ITEM_TRANSFORMER).add(clazz.getName()); |
| } |
| |
| if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbstractClazz |
| && !PullJobDelegate.class.isAssignableFrom(clazz) |
| && !PushJobDelegate.class.isAssignableFrom(clazz) |
| && !GroupMemberProvisionTaskJobDelegate.class.isAssignableFrom(clazz)) { |
| |
| classNames.get(IdRepoImplementationType.TASKJOB_DELEGATE).add(bd.getBeanClassName()); |
| } |
| |
| if (ReconFilterBuilder.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdMImplementationType.RECON_FILTER_BUILDER).add(bd.getBeanClassName()); |
| } |
| |
| if (LogicActions.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdRepoImplementationType.LOGIC_ACTIONS).add(bd.getBeanClassName()); |
| } |
| |
| if (PropagationActions.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdMImplementationType.PROPAGATION_ACTIONS).add(bd.getBeanClassName()); |
| } |
| |
| if (PullActions.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdMImplementationType.PULL_ACTIONS).add(bd.getBeanClassName()); |
| } |
| |
| if (PushActions.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdMImplementationType.PUSH_ACTIONS).add(bd.getBeanClassName()); |
| } |
| |
| if (PlainAttrValueValidator.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdRepoImplementationType.VALIDATOR).add(bd.getBeanClassName()); |
| } |
| |
| if (RecipientsProvider.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdRepoImplementationType.RECIPIENTS_PROVIDER).add(bd.getBeanClassName()); |
| } |
| |
| if (AuditAppender.class.isAssignableFrom(clazz) |
| && !JdbcAuditAppender.class.equals(clazz) && !isAbstractClazz) { |
| |
| classNames.get(IdRepoImplementationType.AUDIT_APPENDER).add(clazz.getName()); |
| auditAppenderClasses.add(clazz); |
| } |
| |
| if (ProvisionSorter.class.isAssignableFrom(clazz) && !isAbstractClazz) { |
| classNames.get(IdMImplementationType.PROVISION_SORTER).add(bd.getBeanClassName()); |
| } |
| } catch (Throwable t) { |
| LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t); |
| } |
| }); |
| |
| classNames = Collections.unmodifiableMap(classNames); |
| LOG.debug("Implementation classes found: {}", classNames); |
| |
| jwtSSOProviderClasses = Collections.unmodifiableSet(jwtSSOProviderClasses); |
| reportletClasses = Collections.unmodifiableMap(reportletClasses); |
| accountRuleClasses = Collections.unmodifiableMap(accountRuleClasses); |
| passwordRuleClasses = Collections.unmodifiableMap(passwordRuleClasses); |
| pullCRClasses = Collections.unmodifiableMap(pullCRClasses); |
| pushCRClasses = Collections.unmodifiableMap(pushCRClasses); |
| auditAppenderClasses = Collections.unmodifiableSet(auditAppenderClasses); |
| } |
| |
| @Override |
| public Set<String> getClassNames(final String type) { |
| return classNames.get(type); |
| } |
| |
| @Override |
| public Set<Class<?>> getJWTSSOProviderClasses() { |
| return jwtSSOProviderClasses; |
| } |
| |
| @Override |
| public Class<? extends Reportlet> getReportletClass( |
| final Class<? extends ReportletConf> reportletConfClass) { |
| |
| return reportletClasses.get(reportletConfClass); |
| } |
| |
| @Override |
| public Class<? extends AccountRule> getAccountRuleClass( |
| final Class<? extends AccountRuleConf> accountRuleConfClass) { |
| |
| return accountRuleClasses.get(accountRuleConfClass); |
| } |
| |
| @Override |
| public Class<? extends PasswordRule> getPasswordRuleClass( |
| final Class<? extends PasswordRuleConf> passwordRuleConfClass) { |
| |
| return passwordRuleClasses.get(passwordRuleConfClass); |
| } |
| |
| @Override |
| public Class<? extends PullCorrelationRule> getPullCorrelationRuleClass( |
| final Class<? extends PullCorrelationRuleConf> correlationRuleConfClass) { |
| |
| return pullCRClasses.get(correlationRuleConfClass); |
| } |
| |
| @Override |
| public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass( |
| final Class<? extends PushCorrelationRuleConf> correlationRuleConfClass) { |
| |
| return pushCRClasses.get(correlationRuleConfClass); |
| } |
| |
| @Override |
| public Set<Class<?>> getAuditAppenderClasses() { |
| return auditAppenderClasses; |
| } |
| } |