You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2024/08/05 14:36:07 UTC
(jackrabbit) 01/01: JCR-5095 Improve exception handling in TokenBasedLoginTest
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch bugfix/improve-error-handing-in-tokenbasedlogintest
in repository https://gitbox.apache.org/repos/asf/jackrabbit.git
commit 12e9c981d51703f66db9dc80f302b612f0279cea
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Mon Aug 5 16:35:56 2024 +0200
JCR-5095 Improve exception handling in TokenBasedLoginTest
---
.../authentication/token/TokenBasedLoginTest.java | 268 ++++++++++-----------
1 file changed, 132 insertions(+), 136 deletions(-)
diff --git a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java
index 78d8194e3..4c973b869 100644
--- a/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java
+++ b/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/security/authentication/token/TokenBasedLoginTest.java
@@ -16,6 +16,23 @@
*/
package org.apache.jackrabbit.core.security.authentication.token;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.jcr.LoginException;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
@@ -26,18 +43,6 @@ import org.apache.jackrabbit.test.AbstractJCRTest;
import org.apache.jackrabbit.test.NotExecutableException;
import org.apache.jackrabbit.test.RepositoryStub;
-import javax.jcr.LoginException;
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
/**
* <code>TokenBasedLoginTest</code>...
*/
@@ -230,120 +235,77 @@ public class TokenBasedLoginTest extends AbstractJCRTest {
* Tests concurrent login on the Repository including token creation.
* Test copied and slightly adjusted from org.apache.jackrabbit.core.ConcurrentLoginTest
*/
- public void testConcurrentLogin() throws RepositoryException, NotExecutableException {
- final Exception[] exception = new Exception[1];
- List<Thread> testRunner = new ArrayList<Thread>();
- for (int i = 0; i < 10; i++) {
- testRunner.add(new Thread(new Runnable() {
- public void run() {
- for (int i = 0; i < 100; i++) {
+ public void testConcurrentLogin() throws Throwable {
+ assertParallelExecutionSucceeds(10, new JcrRunnable() {
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ SimpleCredentials sc = new SimpleCredentials(testuser.getID(), testuser.getID().toCharArray());
+ sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+
+ Session s = getHelper().getRepository().login(sc);
try {
- SimpleCredentials sc = new SimpleCredentials(testuser.getID(), testuser.getID().toCharArray());
- sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
-
- Session s = getHelper().getRepository().login(sc);
- try {
- Set<TokenCredentials> tcs = ((SessionImpl) s).getSubject().getPublicCredentials(TokenCredentials.class);
- assertFalse(tcs.isEmpty());
- } finally {
- s.logout();
- }
- } catch (Exception e) {
- exception[0] = e;
- break;
+ Set<TokenCredentials> tcs = ((SessionImpl) s).getSubject().getPublicCredentials(TokenCredentials.class);
+ assertFalse(tcs.isEmpty());
+ } finally {
+ s.logout();
}
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("Repository exception", e);
}
}
- }));
- }
-
- // start threads
- for (Object aTestRunner : testRunner) {
- ((Thread) aTestRunner).start();
- }
-
- // join threads
- for (Object aTestRunner : testRunner) {
- try {
- ((Thread) aTestRunner).join();
- } catch (InterruptedException e) {
- fail(e.toString());
}
- }
+ });
- if (exception[0] != null) {
- fail(exception[0].toString());
- }
}
/**
* Tests concurrent login of 3 different users on the Repository including
* token creation.
* Test copied and slightly adjusted from org.apache.jackrabbit.core.ConcurrentLoginTest
+ * @throws InterruptedException
*/
- public void testConcurrentLoginOfDifferentUsers() throws RepositoryException, NotExecutableException {
- final Exception[] exception = new Exception[1];
- List<Thread> testRunner = new ArrayList<Thread>();
- for (int i = 0; i < 10; i++) {
- testRunner.add(new Thread(new Runnable() {
- public void run() {
- for (int i = 0; i < 100; i++) {
+ public void testConcurrentLoginOfDifferentUsers() throws InterruptedException {
+ assertParallelExecutionSucceeds(10, new JcrRunnable() {
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ SimpleCredentials c;
+ double rand = 3 * Math.random();
+ int index = (int) Math.floor(rand);
+ switch (index) {
+ case 0:
+ c = new SimpleCredentials(testuser.getID(), testuser.getID().toCharArray());
+ break;
+ case 1:
+ c = new SimpleCredentials(getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_SUPERUSER_NAME), getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_SUPERUSER_PWD).toCharArray());
+ break;
+ default:
+ c = new SimpleCredentials(getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_READONLY_NAME), getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_READONLY_PWD).toCharArray());
+ break;
+ }
+ c.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+ Session s = getHelper().getRepository().login(c);
try {
- SimpleCredentials c;
- double rand = 3 * Math.random();
- int index = (int) Math.floor(rand);
- switch (index) {
- case 0:
- c = new SimpleCredentials(testuser.getID(), testuser.getID().toCharArray());
- break;
- case 1:
- c = new SimpleCredentials(getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_SUPERUSER_NAME), getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_SUPERUSER_PWD).toCharArray());
- break;
- default:
- c = new SimpleCredentials(getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_READONLY_NAME), getHelper().getProperty(RepositoryStub.PROP_PREFIX + "." + RepositoryStub.PROP_READONLY_PWD).toCharArray());
- break;
- }
- c.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
- Session s = getHelper().getRepository().login(c);
- try {
- Set<TokenCredentials> tcs = ((SessionImpl) s).getSubject().getPublicCredentials(TokenCredentials.class);
- assertFalse(tcs.isEmpty());
- } finally {
- s.logout();
- }
- } catch (Exception e) {
- exception[0] = e;
- break;
+ Set<TokenCredentials> tcs = ((SessionImpl) s).getSubject().getPublicCredentials(TokenCredentials.class);
+ assertFalse(tcs.isEmpty());
+ } finally {
+ s.logout();
}
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("Repository exception", e);
}
}
- }));
- }
-
- // start threads
- for (Object aTestRunner : testRunner) {
- ((Thread) aTestRunner).start();
- }
-
- // join threads
- for (Object aTestRunner : testRunner) {
- try {
- ((Thread) aTestRunner).join();
- } catch (InterruptedException e) {
- fail(e.toString());
}
- }
-
- if (exception[0] != null) {
- fail(exception[0].toString());
- }
+ });
}
/**
* Tests concurrent login on the Repository including token creation.
* Test copied and slightly adjusted from org.apache.jackrabbit.core.ConcurrentLoginTest
+ * @throws InterruptedException
*/
- public void testConcurrentLoginDifferentWorkspaces() throws RepositoryException, NotExecutableException {
+ public void testConcurrentLoginDifferentWorkspaces() throws RepositoryException, NotExecutableException, InterruptedException {
final String testID = testuser.getID();
// check if test is executable
@@ -367,53 +329,87 @@ public class TokenBasedLoginTest extends AbstractJCRTest {
}
}
- final Exception[] exception = new Exception[1];
+ assertParallelExecutionSucceeds(10, new JcrRunnable() {
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ double rand = wspNames.size() * Math.random();
+ int index = (int) Math.floor(rand);
+ String wspName = wspNames.get(index);
+
+ SimpleCredentials sc = new SimpleCredentials(testID, testID.toCharArray());
+ sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
+
+ Session s = getHelper().getRepository().login(sc, wspName);
+ try {
+ Set<TokenCredentials> tcs = ((SessionImpl) s).getSubject().getPublicCredentials(TokenCredentials.class);
+ assertFalse(tcs.isEmpty());
+ } finally {
+ s.logout();
+ }
+
+ } catch (RepositoryException e) {
+ throw new IllegalStateException("Repository exception", e);
+ }
+ }
+ }
+ });
+ }
+
+ static class UncheckedRepositoryException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public UncheckedRepositoryException(RepositoryException e) {
+ super(e);
+ }
+ }
+
+ static interface JcrRunnable {
+ public void run() throws RepositoryException;
+ }
+
+ private static void assertParallelExecutionSucceeds(int numThreads, JcrRunnable runnable) throws InterruptedException {
List<Thread> testRunner = new ArrayList<Thread>();
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < numThreads; i++) {
testRunner.add(new Thread(new Runnable() {
public void run() {
- for (int i = 0; i < 100; i++) {
- try {
- double rand = wspNames.size() * Math.random();
- int index = (int) Math.floor(rand);
- String wspName = wspNames.get(index);
-
- SimpleCredentials sc = new SimpleCredentials(testID, testID.toCharArray());
- sc.setAttribute(TokenBasedAuthentication.TOKEN_ATTRIBUTE, "");
-
- Session s = getHelper().getRepository().login(sc, wspName);
- try {
- Set<TokenCredentials> tcs = ((SessionImpl) s).getSubject().getPublicCredentials(TokenCredentials.class);
- assertFalse(tcs.isEmpty());
- } finally {
- s.logout();
- }
-
- } catch (Exception e) {
- exception[0] = e;
- break;
- }
+ try {
+ runnable.run();
+ } catch (RepositoryException e) {
+ throw new UncheckedRepositoryException(e);
}
}
}));
}
-
+ assertThreadExecutionSucceeds(testRunner);
+ }
+ /**
+ * Executes all given threads and waits for them to finish. Exception in any of the thread will make the calling thread die with an exception as well
+ * @param testRunner the threads to execute
+ * @throws InterruptedException
+ */
+ private static void assertThreadExecutionSucceeds(Collection<Thread> testRunner) throws InterruptedException {
+ Map<String, Throwable> threadExceptions = new ConcurrentHashMap<>();
// start threads
- for (Object aTestRunner : testRunner) {
- ((Thread) aTestRunner).start();
+ for (Thread aTestRunner : testRunner) {
+ aTestRunner.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+ public void uncaughtException(Thread t, Throwable e) {
+ threadExceptions.put(t.getName() + " (id " + t.getId() +")", e);
+ }
+ } );
+ aTestRunner.start();
}
// join threads
- for (Object aTestRunner : testRunner) {
- try {
- ((Thread) aTestRunner).join();
- } catch (InterruptedException e) {
- fail(e.toString());
- }
+ for (Thread aTestRunner : testRunner) {
+ aTestRunner.join();
}
- if (exception[0] != null) {
- fail(exception[0].toString());
+ // only rethrow one exception(put the other ones as suppressed)
+ IllegalStateException mainException = threadExceptions.entrySet().stream().findFirst().map(e -> new IllegalStateException("Thread " + e.getKey() + " failed", e.getValue())).orElse(null);
+ if (mainException != null) {
+ threadExceptions.entrySet().stream().skip(1).forEach(e -> mainException.addSuppressed(new IllegalStateException("Thread " + e.getKey() + " failed", e.getValue())));
+ throw mainException;
}
}
}
\ No newline at end of file