You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2019/03/25 13:44:04 UTC

[syncope] branch master updated: [SYNCOPE-1448] Relying on Spring's concurrency checks

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 50469a1  [SYNCOPE-1448] Relying on Spring's concurrency checks
50469a1 is described below

commit 50469a15b51b99e6b1e52a05626f96e0b9ce6cdd
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon Mar 18 08:03:40 2019 +0100

    [SYNCOPE-1448] Relying on Spring's concurrency checks
---
 .../syncope/core/spring/ImplementationManager.java | 105 ++++++++-------------
 .../core/spring/ImplementationManagerTest.java     | 104 ++++++++++++++++++++
 2 files changed, 144 insertions(+), 65 deletions(-)

diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/ImplementationManager.java b/core/spring/src/main/java/org/apache/syncope/core/spring/ImplementationManager.java
index b3e050a..f1481b3 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/ImplementationManager.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/ImplementationManager.java
@@ -57,24 +57,14 @@ public final class ImplementationManager {
 
             case JAVA:
             default:
-                Reportlet reportlet = null;
-
                 ReportletConf reportletConf = POJOHelper.deserialize(impl.getBody(), ReportletConf.class);
                 Class<? extends Reportlet> reportletClass = ApplicationContextProvider.getApplicationContext().
                         getBean(ImplementationLookup.class).getReportletClass(reportletConf.getClass());
-                if (reportletClass == null) {
+
+                Reportlet reportlet = buildJavaWithConf(reportletClass);
+                if (reportlet == null) {
                     LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
                 } else {
-                    // fetch (or create) reportlet
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
-                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(reportletClass.getName());
-                    } else {
-                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
-                                createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(reportletClass.getName(), reportlet);
-                    }
                     reportlet.setConf(reportletConf);
                 }
 
@@ -91,24 +81,14 @@ public final class ImplementationManager {
 
             case JAVA:
             default:
-                AccountRule rule = null;
-
                 AccountRuleConf ruleConf = POJOHelper.deserialize(impl.getBody(), AccountRuleConf.class);
                 Class<? extends AccountRule> ruleClass = ApplicationContextProvider.getApplicationContext().
                         getBean(ImplementationLookup.class).getAccountRuleClass(ruleConf.getClass());
-                if (ruleClass == null) {
+
+                AccountRule rule = buildJavaWithConf(ruleClass);
+                if (rule == null) {
                     LOG.warn("Could not find matching account rule for {}", impl.getClass());
                 } else {
-                    // fetch (or create) rule
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
-                        rule = (AccountRule) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(ruleClass.getName());
-                    } else {
-                        rule = (AccountRule) ApplicationContextProvider.getBeanFactory().
-                                createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(ruleClass.getName(), rule);
-                    }
                     rule.setConf(ruleConf);
                 }
 
@@ -125,24 +105,14 @@ public final class ImplementationManager {
 
             case JAVA:
             default:
-                PasswordRule rule = null;
-
                 PasswordRuleConf ruleConf = POJOHelper.deserialize(impl.getBody(), PasswordRuleConf.class);
                 Class<? extends PasswordRule> ruleClass = ApplicationContextProvider.getApplicationContext().
                         getBean(ImplementationLookup.class).getPasswordRuleClass(ruleConf.getClass());
-                if (ruleClass == null) {
+
+                PasswordRule rule = buildJavaWithConf(ruleClass);
+                if (rule == null) {
                     LOG.warn("Could not find matching password rule for {}", impl.getClass());
                 } else {
-                    // fetch (or create) rule
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
-                        rule = (PasswordRule) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(ruleClass.getName());
-                    } else {
-                        rule = (PasswordRule) ApplicationContextProvider.getBeanFactory().
-                                createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(ruleClass.getName(), rule);
-                    }
                     rule.setConf(ruleConf);
                 }
 
@@ -159,25 +129,15 @@ public final class ImplementationManager {
 
             case JAVA:
             default:
-                PullCorrelationRule rule = null;
-
                 PullCorrelationRuleConf ruleConf =
                         POJOHelper.deserialize(impl.getBody(), PullCorrelationRuleConf.class);
                 Class<? extends PullCorrelationRule> ruleClass = ApplicationContextProvider.getApplicationContext().
                         getBean(ImplementationLookup.class).getPullCorrelationRuleClass(ruleConf.getClass());
-                if (ruleClass == null) {
+
+                PullCorrelationRule rule = buildJavaWithConf(ruleClass);
+                if (rule == null) {
                     LOG.warn("Could not find matching pull correlation rule for {}", impl.getClass());
                 } else {
-                    // fetch (or create) rule
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
-                        rule = (PullCorrelationRule) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(ruleClass.getName());
-                    } else {
-                        rule = (PullCorrelationRule) ApplicationContextProvider.getBeanFactory().
-                                createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(ruleClass.getName(), rule);
-                    }
                     rule.setConf(ruleConf);
                 }
 
@@ -194,25 +154,15 @@ public final class ImplementationManager {
 
             case JAVA:
             default:
-                PushCorrelationRule rule = null;
-
                 PushCorrelationRuleConf ruleConf =
                         POJOHelper.deserialize(impl.getBody(), PushCorrelationRuleConf.class);
                 Class<? extends PushCorrelationRule> ruleClass = ApplicationContextProvider.getApplicationContext().
                         getBean(ImplementationLookup.class).getPushCorrelationRuleClass(ruleConf.getClass());
-                if (ruleClass == null) {
+
+                PushCorrelationRule rule = buildJavaWithConf(ruleClass);
+                if (rule == null) {
                     LOG.warn("Could not find matching push correlation rule for {}", impl.getClass());
                 } else {
-                    // fetch (or create) rule
-                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
-                        rule = (PushCorrelationRule) ApplicationContextProvider.getBeanFactory().
-                                getSingleton(ruleClass.getName());
-                    } else {
-                        rule = (PushCorrelationRule) ApplicationContextProvider.getBeanFactory().
-                                createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                        ApplicationContextProvider.getBeanFactory().
-                                registerSingleton(ruleClass.getName(), rule);
-                    }
                     rule.setConf(ruleConf);
                 }
 
@@ -265,6 +215,31 @@ public final class ImplementationManager {
                 createBean(clazz, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
     }
 
+    @SuppressWarnings("unchecked")
+    private static <T> T buildJavaWithConf(final Class<T> clazz) {
+        T bean = null;
+
+        if (clazz != null) {
+            if (ApplicationContextProvider.getBeanFactory().containsSingleton(clazz.getName())) {
+                bean = (T) ApplicationContextProvider.getBeanFactory().getSingleton(clazz.getName());
+            } else {
+                try {
+                    bean = (T) ApplicationContextProvider.getBeanFactory().
+                            createBean(clazz, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                    ApplicationContextProvider.getBeanFactory().registerSingleton(clazz.getName(), bean);
+                } catch (IllegalStateException e) {
+                    LOG.debug("While attempting to register {}", clazz.getName(), e);
+
+                    // if this exception was raised, it means another bean for same name is already registered,
+                    // revert to it
+                    bean = (T) ApplicationContextProvider.getBeanFactory().getSingleton(clazz.getName());
+                }
+            }
+        }
+
+        return bean;
+    }
+
     public static Class<?> purge(final String implementation) {
         return CLASS_CACHE.remove(implementation);
     }
diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
new file mode 100644
index 0000000..9c62381
--- /dev/null
+++ b/core/spring/src/test/java/org/apache/syncope/core/spring/ImplementationManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.spring;
+
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.security.TestImplementation;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
+
+@SpringJUnitConfig(locations = { "classpath:springTest.xml" })
+public class ImplementationManagerTest {
+
+    private DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() {
+        DefaultPasswordRuleConf baseDefaultPasswordRuleConf = new DefaultPasswordRuleConf();
+        baseDefaultPasswordRuleConf.setAlphanumericRequired(false);
+        baseDefaultPasswordRuleConf.setDigitRequired(false);
+        baseDefaultPasswordRuleConf.setLowercaseRequired(false);
+        baseDefaultPasswordRuleConf.setMaxLength(1000);
+        baseDefaultPasswordRuleConf.setMinLength(8);
+        baseDefaultPasswordRuleConf.setMustEndWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustEndWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustEndWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setMustStartWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustStartWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustStartWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntEndWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntEndWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustntEndWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntStartWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntStartWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustntStartWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setNonAlphanumericRequired(false);
+        baseDefaultPasswordRuleConf.setUppercaseRequired(false);
+        return baseDefaultPasswordRuleConf;
+    }
+
+    @Test
+    public void concurrentPasswordRuleBuilding() {
+        String body = POJOHelper.serialize(createBaseDefaultPasswordRuleConf());
+
+        assertTimeoutPreemptively(Duration.ofSeconds(30), () -> {
+            TestImplementation implementation = new TestImplementation();
+            implementation.setBody(body);
+            ReentrantLock lock = new ReentrantLock();
+            lock.lock();
+            AtomicInteger runningThreads = new AtomicInteger(0);
+            AtomicInteger errorCount = new AtomicInteger(0);
+            List<String> errorMessages = Collections.synchronizedList(new LinkedList<>());
+            for (int i = 0; i < 10; i++) {
+                runningThreads.incrementAndGet();
+                new Thread(() -> {
+                    try {
+                        while (lock.isLocked()) {
+                            Thread.yield();
+                        }
+                        try {
+                            ImplementationManager.buildPasswordRule(implementation).orElseThrow();
+                        } catch (Exception e) {
+                            errorMessages.add(e.getLocalizedMessage());
+                            errorCount.incrementAndGet();
+                        }
+                    } finally {
+                        runningThreads.decrementAndGet();
+                    }
+                }).start();
+            }
+            lock.unlock();
+            while (runningThreads.get() > 0) {
+                Thread.yield();
+            }
+
+            assertTrue(
+                    errorMessages.isEmpty(),
+                    errorMessages.stream().collect(Collectors.joining(System.lineSeparator())));
+        });
+    }
+}