| /* |
| * Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.testing.tests.junit.sessionsxml; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.CountDownLatch; |
| |
| import org.eclipse.persistence.exceptions.ServerPlatformException; |
| import org.eclipse.persistence.logging.DefaultSessionLog; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.platform.server.ServerPlatformBase; |
| import org.eclipse.persistence.platform.server.ServerPlatformDetector; |
| import org.eclipse.persistence.platform.server.ServerPlatformUtils; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.sessions.ExternalTransactionController; |
| import org.eclipse.persistence.sessions.factories.SessionManager; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Tests the API from SessionManager. |
| */ |
| public class SessionManagerTest { |
| |
| private List<ServerPlatformDetector> detectors; |
| private SessionLog originalLogger; |
| |
| @Before |
| public void setUp() { |
| originalLogger = (SessionLog) getField(SessionManager.class, "LOG", null); |
| SessionLog log = new LogWrapper(); |
| log.setLevel(originalLogger.getLevel()); |
| setStaticField(SessionManager.class, "LOG", log); |
| } |
| |
| @After |
| public void tearDown() { |
| if (detectors != null) { |
| for (Iterator<ServerPlatformDetector> i = detectors.iterator(); i.hasNext();) { |
| ServerPlatformDetector detector = i.next(); |
| if (Detector.class.getName().equals(detector.getClass().getName())) { |
| i.remove(); |
| break; |
| } |
| } |
| setStaticField(ServerPlatformUtils.class, "SERVER_PLATFORM_CLS", null); |
| } |
| setStaticField(SessionManager.class, "LOG", originalLogger); |
| reinitManager(false, true); |
| } |
| |
| @Test |
| public void testConcurrency() { |
| reinitManager(true, true); |
| |
| SessionManager sm = SessionManager.getManager(); |
| Assert.assertEquals("test", getField(SessionManager.class, "context", sm)); |
| final Platform platform = (Platform) getField(SessionManager.class, "detectedPlatform", sm); |
| |
| CountDownLatch startSignal = new CountDownLatch(1); |
| CountDownLatch doneSignal = new CountDownLatch(1000); |
| |
| for (int i = 0; i < 1000; i++) { |
| // create and start testing threads |
| new Thread(new Worker(startSignal, doneSignal, platform)).start(); |
| } |
| //create and start context switching thread |
| ContextSwitcher cs = new ContextSwitcher(platform); |
| Thread contextSwitcher = new Thread(cs); |
| contextSwitcher.start(); |
| |
| // let all threads proceed |
| startSignal.countDown(); |
| try { |
| // wait for all to finish |
| doneSignal.await(); |
| } catch (InterruptedException e) { |
| //ignore |
| } |
| |
| //finish the context switching thread |
| cs.stop = true; |
| try { |
| contextSwitcher.join(); |
| } catch (InterruptedException e) { |
| //ignore |
| } |
| //check what we can |
| Set<String> contexts = cs.values; |
| ConcurrentMap<String, SessionManager> registeredManagers = (ConcurrentMap<String, SessionManager>) getField(SessionManager.class, "managers", null); |
| //cannot do this as there may not be any thread catching some particular value |
| //Assert.assertEquals(contexts.size() + 1, registeredManagers.size()); |
| for (String context: contexts) { |
| if ("test".equals(context)) { |
| continue; |
| } |
| platform.partitionId = context; |
| SessionManager.getManager().destroy(); |
| } |
| Assert.assertEquals(1, registeredManagers.size()); |
| } |
| |
| @Test |
| public void testAllManagers() { |
| Collection<SessionManager> allManagers = SessionManager.getAllManagers(); |
| Assert.assertEquals(1, allManagers.size()); |
| Assert.assertNull(getField(SessionManager.class, "context", allManagers.iterator().next())); |
| reinitManager(true, true); |
| allManagers = SessionManager.getAllManagers(); |
| Assert.assertEquals(1, allManagers.size()); |
| Assert.assertEquals("test", getField(SessionManager.class, "context", allManagers.iterator().next())); |
| } |
| |
| @Test |
| public void testCustomManager() { |
| SessionManager sm = new SM(); |
| SessionManager.setManager(sm); |
| SessionManager m1 = SessionManager.getManager(); |
| Assert.assertNotNull(m1); |
| Assert.assertNull(getField(SessionManager.class, "context", m1)); |
| Collection<SessionManager> allManagers = SessionManager.getAllManagers(); |
| Assert.assertEquals(1, allManagers.size()); |
| Assert.assertTrue(sm == allManagers.iterator().next()); |
| |
| reinitManager(true, true); |
| SessionManager.setManager(sm); |
| m1 = SessionManager.getManager(); |
| Assert.assertNotNull(m1); |
| Assert.assertEquals("test", getField(SessionManager.class, "context", m1)); |
| allManagers = SessionManager.getAllManagers(); |
| Assert.assertEquals(1, allManagers.size()); |
| Assert.assertTrue(sm == allManagers.iterator().next()); |
| sm.destroy(); |
| } |
| |
| @Test |
| public void testInvalidPlatform() { |
| // platform class name can be invalid/not found |
| reinitManager(true, false); |
| SessionManager sm = SessionManager.getManager(); |
| LogWrapper logger = (LogWrapper) getField(SessionManager.class, "LOG", null); |
| Assert.assertEquals(SessionLog.WARNING, logger.getLastLevel()); |
| Assert.assertEquals(SessionLog.CONNECTION, logger.getLastCategory()); |
| Throwable t = logger.getLastThrowable(); |
| Assert.assertTrue("invalid excpetion type: " + t, t instanceof ServerPlatformException); |
| Assert.assertTrue("invalid excpetion type: " + t.getCause(), t.getCause() instanceof ClassNotFoundException); |
| } |
| |
| @Test |
| public void testNPEFromPlatform() { |
| // platform impl can throw NPE which is being wrapped by other exceptions |
| Platform.forceNPE = true; |
| try { |
| reinitManager(true, true); |
| SessionManager sm = SessionManager.getManager(); |
| LogWrapper logger = (LogWrapper) getField(SessionManager.class, "LOG", null); |
| Assert.assertEquals(SessionLog.WARNING, logger.getLastLevel()); |
| Assert.assertEquals(SessionLog.CONNECTION, logger.getLastCategory()); |
| Throwable t = logger.getLastThrowable(); |
| Assert.assertTrue("invalid excpetion type: " + t, t instanceof ServerPlatformException); |
| } finally { |
| Platform.forceNPE = false; |
| } |
| } |
| |
| private void reinitManager(boolean addDetector, boolean validDetector) { |
| if (addDetector) { |
| detectors = (List<ServerPlatformDetector>) getField(ServerPlatformUtils.class, "PLATFORMS", null); |
| detectors.add(0, new Detector(validDetector)); |
| setStaticField(ServerPlatformUtils.class, "SERVER_PLATFORM_CLS", null); |
| } |
| Method m = null; |
| try { |
| m = SessionManager.class.getDeclaredMethod("init"); |
| m.setAccessible(true); |
| m.invoke(null); |
| } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { |
| throw new RuntimeException(e); |
| } finally { |
| if (m != null) { |
| try { |
| m.setAccessible(false); |
| } catch (Exception e) { |
| //ignore |
| } |
| } |
| } |
| } |
| |
| private Object getField(Class c, String field, Object o) { |
| Field f = null; |
| try { |
| f = c.getDeclaredField(field); |
| f.setAccessible(true); |
| return f.get(o); |
| } catch (IllegalAccessException | IllegalArgumentException | SecurityException | NoSuchFieldException ex) { |
| throw new RuntimeException(ex); |
| } finally { |
| if (f != null) { |
| f.setAccessible(false); |
| } |
| } |
| } |
| |
| private void setStaticField(Class c, String field, Object value) { |
| Field f = null; |
| try { |
| f = c.getDeclaredField(field); |
| setPrivateStaticFinalField(f, value); |
| } catch (IllegalArgumentException | SecurityException | NoSuchFieldException ex) { |
| throw new RuntimeException(ex); |
| } finally { |
| if (f != null) { |
| f.setAccessible(false); |
| } |
| } |
| } |
| |
| private void setPrivateStaticFinalField(Field f, Object value) { |
| Field modifiersField = null; |
| int orig = 0; |
| try { |
| f.setAccessible(true); |
| orig = f.getModifiers(); |
| if (Modifier.isFinal(orig)) { |
| // remove final modifier from field |
| modifiersField = Field.class.getDeclaredField("modifiers"); |
| modifiersField.setAccessible(true); |
| modifiersField.setInt(f, orig & ~Modifier.FINAL); |
| } |
| f.set(null, value); |
| } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { |
| throw new RuntimeException(ex); |
| } finally { |
| if (modifiersField != null) { |
| if (f != null) { |
| try { |
| modifiersField.setInt(f, orig); |
| } catch (IllegalArgumentException | IllegalAccessException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| modifiersField.setAccessible(false); |
| } |
| if (f != null) { |
| f.setAccessible(false); |
| } |
| } |
| } |
| |
| private static final class SM extends SessionManager { |
| public SM() { |
| //empty by intention to test the 'context' field initialization |
| //through the setManager API |
| } |
| } |
| |
| private static final class LogWrapper extends DefaultSessionLog { |
| private volatile Throwable lastThrowable; |
| private volatile String lastCategory; |
| private volatile int lastLevel; |
| |
| @Override |
| public void logThrowable(int level, String category, Throwable throwable) { |
| this.lastLevel = level; |
| this.lastCategory = category; |
| this.lastThrowable = throwable; |
| super.logThrowable(level, category, throwable); |
| } |
| |
| /** |
| * @return the throwable |
| */ |
| public Throwable getLastThrowable() { |
| return lastThrowable; |
| } |
| |
| /** |
| * @return the category |
| */ |
| public String getLastCategory() { |
| return lastCategory; |
| } |
| |
| /** |
| * @return the level |
| */ |
| public int getLastLevel() { |
| return lastLevel; |
| } |
| |
| } |
| |
| public static final class Platform extends ServerPlatformBase { |
| |
| private volatile String partitionId = "test"; |
| private static boolean forceNPE = false; |
| |
| public Platform(DatabaseSession newDatabaseSession) { |
| super(newDatabaseSession); |
| if (forceNPE) { |
| throw new NullPointerException(); |
| } |
| } |
| |
| @Override |
| public Class<? extends ExternalTransactionController> getExternalTransactionControllerClass() { |
| return null; |
| } |
| |
| @Override |
| public boolean usesPartitions() { |
| return true; |
| } |
| |
| @Override |
| public String getPartitionID() { |
| return partitionId; |
| } |
| } |
| |
| private static class Detector implements ServerPlatformDetector { |
| |
| private final boolean valid; |
| |
| Detector(boolean valid) { |
| this.valid = valid; |
| } |
| |
| @Override |
| public String checkPlatform() { |
| return valid ? Platform.class.getName() : "non-existing-class-name"; |
| } |
| } |
| |
| /** |
| * Randomly pick a number which is used as a context and changes it in random interval |
| */ |
| private static final class ContextSwitcher implements Runnable { |
| private final Random r = new Random(System.currentTimeMillis()); |
| private volatile boolean stop = false; |
| private final Platform p; |
| //contains names which were assigned to partitionId during test run |
| private final Set<String> values = new HashSet<>(); |
| |
| public ContextSwitcher(Platform p) { |
| this.p = p; |
| } |
| @Override |
| public void run() { |
| while (!stop) { |
| String id = String.valueOf(r.nextInt(10)); |
| p.partitionId = id; |
| values.add(id); |
| try { |
| Thread.sleep(r.nextInt(3)); |
| } catch (InterruptedException ie) { |
| |
| } |
| } |
| } |
| }; |
| |
| private static final class Worker implements Runnable { |
| private final CountDownLatch startSignal; |
| private final CountDownLatch doneSignal; |
| private final Platform p; |
| |
| Worker(CountDownLatch startSignal, CountDownLatch doneSignal, Platform p) { |
| this.startSignal = startSignal; |
| this.doneSignal = doneSignal; |
| this.p = p; |
| } |
| |
| @Override |
| public void run() { |
| try { |
| startSignal.await(); |
| //actual test |
| SessionManager sm = SessionManager.getManager(); |
| Assert.assertNotNull(sm); |
| } catch (InterruptedException ex) { |
| //ignore |
| } finally { |
| doneSignal.countDown(); |
| } |
| } |
| } |
| } |