diff --git a/dev/com.ibm.ws.concurrent.persistent/bnd.bnd b/dev/com.ibm.ws.concurrent.persistent/bnd.bnd index 73e4da78ef8d..99652f7fb2b3 100644 --- a/dev/com.ibm.ws.concurrent.persistent/bnd.bnd +++ b/dev/com.ibm.ws.concurrent.persistent/bnd.bnd @@ -1,5 +1,5 @@ #******************************************************************************* -# Copyright (c) 2017,2019 IBM Corporation and others. +# Copyright (c) 2017,2020 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -44,6 +44,7 @@ Service-Component:\ -dsannotations:\ com.ibm.ws.concurrent.persistent.internal.ApplicationTracker,\ com.ibm.ws.concurrent.persistent.internal.PersistentExecutorImpl,\ + com.ibm.ws.concurrent.persistent.internal.PersistentExecutorIntrospector,\ com.ibm.ws.concurrent.persistent.internal.PersistentExecutorMBeanImpl instrument.classesExcludes: com/ibm/ws/concurrent/persistent/resources/*.class diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/db/DatabaseTaskStore.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/db/DatabaseTaskStore.java index 575e8ebedf50..00ab70fbdb6a 100644 --- a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/db/DatabaseTaskStore.java +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/db/DatabaseTaskStore.java @@ -16,7 +16,6 @@ import java.sql.SQLIntegrityConstraintViolationException; import java.sql.SQLTimeoutException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -840,38 +839,6 @@ public Long getPartition(long taskId) throws Exception { return partitionId; } - /** {@inheritDoc} */ - @Override - public Long getPartitionWithState(long stateBits) throws Exception { - String select = "SELECT p.ID,p.EXECUTOR,p.HOSTNAME,p.ID,p.LSERVER,p.USERDIR,p.EXPIRY,p.STATES FROM Partition p WHERE p.STATES-p.STATES/:d*:d=:r AND p.EXPIRY>:t ORDER BY p.EXPIRY DESC"; - - final boolean trace = TraceComponent.isAnyTracingEnabled(); - if (trace && tc.isEntryEnabled()) - Tr.entry(this, tc, "getPartitionWithState", stateBits, select); - - long denominator = stateBits < 2 ? 2 : stateBits < 4 ? 4 : -1; - if (denominator == -1) - throw new IllegalArgumentException(Long.toString(stateBits)); // internal error: no states > 3 are currently defined - - Object[] partitionInfo; - EntityManager em = getPersistenceServiceUnit().createEntityManager(); - try { - TypedQuery query = em.createQuery(select.toString(), Object[].class); - query.setParameter("d", denominator); - query.setParameter("r", stateBits); - query.setParameter("t", System.currentTimeMillis()); - query.setMaxResults(1); - List results = query.getResultList(); - partitionInfo = results == null || results.isEmpty() ? null : results.get(0); - } finally { - em.close(); - } - - if (trace && tc.isEntryEnabled()) - Tr.exit(this, tc, "getPartitionWithState", partitionInfo == null ? null : Arrays.toString(partitionInfo)); - return partitionInfo == null ? null : (Long) partitionInfo[0]; - } - /** * Returns the persistence service unit, lazily initializing if necessary. * diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/ApplicationTracker.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/ApplicationTracker.java index bf3116d4e0fc..b8437b4d24e9 100644 --- a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/ApplicationTracker.java +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/ApplicationTracker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 IBM Corporation and others. + * Copyright (c) 2015,2020 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,6 +10,7 @@ *******************************************************************************/ package com.ibm.ws.concurrent.persistent.internal; +import java.io.PrintWriter; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -67,7 +68,7 @@ public class ApplicationTracker { /** * Declarative Services method for setting a started Application instance - * + * * @param ref reference to the service */ @Reference(service = Application.class, @@ -94,7 +95,7 @@ protected void addStartedApplication(ServiceReference ref) { /** * Declarative Services method for setting a starting Application instance - * + * * @param ref reference to the service */ @Reference(service = Application.class, @@ -143,9 +144,33 @@ void deferTask(Runnable task, String appName, PersistentExecutorImpl persistentE executor.submit(task); } + /** + * Dump internal state to the introspector. + * + * @param out writer for the introspector. + */ + void introspect(PrintWriter out) { + if (lock.readLock().tryLock()) + try { + for (Map.Entry> entry : deferredTasks.entrySet()) { + out.print("Deferred tasks for "); + out.print(entry.getKey()); + out.print(": "); + out.println(entry.getValue()); + } + for (Map.Entry entry : appStates.entrySet()) { + out.print(entry.getKey()); + out.print(" is "); + out.println(entry.getValue()); + } + } finally { + lock.readLock().unlock(); + } + } + /** * Returns true if the application with the specified name is started, otherwise false. - * + * * @return true if the application with the specified name is started, otherwise false. */ boolean isStarted(String appName) { @@ -159,7 +184,7 @@ boolean isStarted(String appName) { /** * Declarative Services method for unsetting a started Application instance - * + * * @param ref reference to the service */ protected void removeStartedApplication(ServiceReference ref) { @@ -174,7 +199,7 @@ protected void removeStartedApplication(ServiceReference ref) { /** * Declarative Services method for unsetting a starting Application instance - * + * * @param ref reference to the service */ protected void removeStartingApplication(ServiceReference ref) { @@ -182,7 +207,7 @@ protected void removeStartingApplication(ServiceReference ref) { /** * Declarative Services method for setting the Liberty executor. - * + * * @param svc the service */ @Reference(target = "(component.name=com.ibm.ws.threading)") @@ -192,7 +217,7 @@ protected void setExecutor(ExecutorService svc) { /** * Declarative Services method for unsetting the Liberty executor. - * + * * @param svc the service */ protected void unsetExecutor(ExecutorService svc) { diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/Config.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/Config.java index ed14a3a9e539..6bc4c6e1c646 100644 --- a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/Config.java +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/Config.java @@ -143,6 +143,8 @@ public String toString() { return new StringBuilder(300) .append("instance=") .append(Integer.toHexString(System.identityHashCode(this))) + .append(",id=") + .append(id) .append(",jndiName=") .append(jndiName) .append(",enableTaskExecution=") @@ -161,8 +163,6 @@ public String toString() { .append(retryLimit) .append(",xpathId=") .append(xpathId) - .append(",id=") - .append(id) .toString(); } diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorImpl.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorImpl.java index 1b5a5ff25b94..a56f23640c74 100644 --- a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorImpl.java +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorImpl.java @@ -16,6 +16,7 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.PrintWriter; import java.io.Serializable; import java.io.Writer; import java.net.InetAddress; @@ -39,6 +40,7 @@ import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -250,7 +252,7 @@ public String run() throws UnknownHostException { /** * Reference to the future for the next (or current) poll. */ - private final AtomicReference> pollingFutureRef = new AtomicReference>(); + private final AtomicReference> pollingFutureRef = new AtomicReference>(); /** * Indicates if we received a signal from the user to start polling. @@ -926,6 +928,49 @@ private final String getOwner() { return name; } + /** + * Dump internal state to the introspector. + * + * @param out writer for the introspector. + */ + void introspect(PrintWriter out) { + out.println(toString() + ' ' + name + (deactivated ? " is deactivated" : "")); + + out.print(" Partition "); + if (partitionIdLock.readLock().tryLock()) + try { + out.println(partitionId); + } finally { + partitionIdLock.readLock().unlock(); + } + else + out.println("lock temporarily unavailable"); + + out.println(" Config " + configRef.get()); + + out.println(" Accessed from " + applications); + + out.println(" Signalled to poll? " + pollingStartSignalReceived); + out.println(" PollingManager state " + readyForPollingTask.bits); + + ScheduledFuture pollFuture = pollingFutureRef.get(); + if (pollFuture != null) + out.println(" Next poll in " + pollFuture.getDelay(TimeUnit.MILLISECONDS) + "ms"); + + if (configUpdatePendingQueueLock.readLock().tryLock()) + try { + out.print(" Config updates (" + configUpdatesInProgress + ") in progress, which block tasks:"); + for (InvokerTask task : configUpdatePendingQueue) + out.print(' ' + task.taskId); + out.println(); + } finally { + configUpdatePendingQueueLock.readLock().unlock(); + } + + out.println(" In-memory list of known pending (or active) tasks: " + inMemoryTaskIds.keySet()); + out.println(); + } + /** {@inheritDoc} */ @Override public List> invokeAll(Collection> callables) throws InterruptedException { @@ -1924,7 +1969,7 @@ protected synchronized void unsetServerStarted(ServiceReference r * @param config snapshot of configuration. */ private void startPollingTask(Config config) { - Future future; + ScheduledFuture future; PollingTask pollingTask = new PollingTask(config); future = scheduledExecutor.schedule(pollingTask, config.initialPollDelay, TimeUnit.MILLISECONDS); @@ -1997,7 +2042,7 @@ public void failedCompletion(Future future, Throwable t) { /** * List of Tasks to execute after configuration updates have completed. */ - private final LinkedList configUpdatePendingQueue = new LinkedList(); + private final LinkedList configUpdatePendingQueue = new LinkedList(); /** * futureMonitor used to track the outcome of a configuration update @@ -2017,11 +2062,11 @@ protected void unsetFutureMonitor(FutureMonitor futureMonitor) { * If a configuration update is currently in progress add the targetRunnable to a * local queue to drive its run method after the configuration update(s) is complete. * - * @param targetRunnable Runnable eligible for execution. + * @param targetRunnable task eligible for execution. * @return return true if a configuration update is in progress, false if not. */ @Trivial - boolean deferExecutionForConfigUpdate(Runnable targetRunnable) { + boolean deferExecutionForConfigUpdate(InvokerTask targetRunnable) { boolean returnValue = false; configUpdatePendingQueueLock.readLock().lock(); @@ -2099,7 +2144,7 @@ int configUpdateCompleted(String notificationName) { @Override @Trivial public void run() { - Runnable r; + InvokerTask r; for (;;) { configUpdatePendingQueueLock.writeLock().lock(); @@ -2287,7 +2332,7 @@ public void run() { long delay = config.pollInterval - TimeUnit.NANOSECONDS.toMillis(duration); if (trace && tc.isDebugEnabled()) Tr.debug(PersistentExecutorImpl.this, tc, "Poll completed in " + duration + "ns. Next poll " + delay + "ms from now"); - Future future; + ScheduledFuture future; future = scheduledExecutor.schedule(this, delay, TimeUnit.MILLISECONDS); pollingFutureRef.getAndSet(future); diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorIntrospector.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorIntrospector.java new file mode 100644 index 000000000000..ffc39c56274e --- /dev/null +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PersistentExecutorIntrospector.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2020 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package com.ibm.ws.concurrent.persistent.internal; + +import java.io.PrintWriter; +import java.security.AccessController; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +import com.ibm.websphere.concurrent.persistent.PersistentExecutor; +import com.ibm.websphere.ras.annotation.Trivial; +import com.ibm.ws.kernel.service.util.SecureAction; +import com.ibm.wsspi.logging.Introspector; + +/** + * Introspector for persistent executors. + */ +@Component(configurationPolicy = ConfigurationPolicy.IGNORE) +public class PersistentExecutorIntrospector implements Introspector { + @Override + @Trivial + public String getIntrospectorName() { + return "PersistentExecutorIntrospector"; + } + + @Override + @Trivial + public String getIntrospectorDescription() { + return "Persistent timers/tasks diagnostics"; + } + + @Override + public void introspect(PrintWriter out) throws Exception { + SecureAction priv = AccessController.doPrivileged(SecureAction.get()); + BundleContext bundleContext = priv.getBundleContext(FrameworkUtil.getBundle(getClass())); + + for (ServiceReference ref : priv.getServiceReferences(bundleContext, PersistentExecutor.class, "(!(com.ibm.wsspi.resource.ResourceFactory=true))")) { + PersistentExecutorImpl executor = (PersistentExecutorImpl) priv.getService(bundleContext, ref); + if (executor == null) { + String displayId = (String) ref.getProperty("config.displayId"); + String name = displayId.contains("]/persistentExecutor[") ? displayId : (String) ref.getProperty("id"); + if (name == null) + name = (String) ref.getProperty("jndiName"); + out.println("PersistentExecutor " + name + " is not available"); + out.println("Properties: " + ref.getProperties()); + out.println(); + } else { + executor.introspect(out); + } + } + + ServiceReference appTrackerRef = bundleContext.getServiceReference(ApplicationTracker.class); + bundleContext.getService(appTrackerRef); + + ApplicationTracker appTracker = priv.getService(bundleContext, ApplicationTracker.class); + appTracker.introspect(out); + } +} diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PollingManager.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PollingManager.java index a7edfa812b96..be5359da9b99 100644 --- a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PollingManager.java +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/ws/concurrent/persistent/internal/PollingManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014 IBM Corporation and others. + * Copyright (c) 2014,2020 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -47,7 +47,7 @@ class PollingManager { /** * Combination of bits that represent if we are ready for polling. */ - private final AtomicInteger bits = new AtomicInteger(); + final AtomicInteger bits = new AtomicInteger(); /** * Add an event without checking if we are ready to start polling. diff --git a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/wsspi/concurrent/persistent/TaskStore.java b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/wsspi/concurrent/persistent/TaskStore.java index 99c2bc67dede..ff2f3731bb0b 100644 --- a/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/wsspi/concurrent/persistent/TaskStore.java +++ b/dev/com.ibm.ws.concurrent.persistent/src/com/ibm/wsspi/concurrent/persistent/TaskStore.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2019 IBM Corporation and others. + * Copyright (c) 2014, 2020 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -238,17 +238,6 @@ List> findTaskStatus(String pattern, Character escape, TaskState s */ Long getPartition(long taskId) throws Exception; - /** - * Returns the identifier of a partition whose STATE field's rightmost bits matches the specified value. - * This method assumes that the sign bit of the persisted STATES field is always positive (0), - * so as to be able to compute the remainder when dividing by the next highest power of 2. - * - * @param stateBits desired state bits to match. - * @return a matching partition identifier, otherwise null. - * @throws Exception if an error occurs accessing the persistent store. - */ - Long getPartitionWithState(long stateBits) throws Exception; - /** * Returns name/value pairs for all persisted properties that match the specified name pattern. * For example, to find property names that start with "MY_PROP_NAME_", diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/bnd.bnd b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/bnd.bnd index 1ee55ed63f14..55253bee6abb 100644 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/bnd.bnd +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/bnd.bnd @@ -1,5 +1,5 @@ #******************************************************************************* -# Copyright (c) 2019 IBM Corporation and others. +# Copyright (c) 2019,2020 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -22,4 +22,5 @@ fat.project: true com.ibm.websphere.javaee.concurrent.1.0;version=latest,\ com.ibm.websphere.javaee.servlet.3.1;version=latest,\ com.ibm.websphere.javaee.transaction.1.1;version=latest,\ + com.ibm.ws.kernel.boot.archive;version=latest,\ com.ibm.ws.concurrent.persistent;version=latest diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/build.gradle b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/build.gradle index 3327b0248393..03dd4ae5f78c 100644 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/build.gradle +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/build.gradle @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019 IBM Corporation and others. + * Copyright (c) 2019,2020 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -11,3 +11,7 @@ addRequiredLibraries.dependsOn addDerby + +dependencies { + requiredLibs project(':com.ibm.ws.kernel.boot.archive') +} \ No newline at end of file diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/FATSuite.java b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/FATSuite.java index a5f1fe3f2e6e..73cd9eca41e1 100755 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/FATSuite.java +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/FATSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2019 IBM Corporation and others. + * Copyright (c) 2014, 2020 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,10 +10,21 @@ *******************************************************************************/ package com.ibm.ws.concurrent.persistent.fat.errorpaths; +import static org.junit.Assert.assertEquals; + +import java.io.BufferedInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; +import com.ibm.websphere.simplicity.ProgramOutput; + import componenttest.topology.impl.LibertyServer; import componenttest.topology.impl.LibertyServerFactory; @@ -25,4 +36,53 @@ }) public class FATSuite { static LibertyServer server = LibertyServerFactory.getLibertyServer("com.ibm.ws.concurrent.persistent.fat.errorpaths"); + + /** + * Utility method to dump the server and collect the persistent executor introspector output. + * + * @return list of lines of the persistent executor introspector output. + */ + static List persistentExecutorIntrospectorDump() throws Exception { + ProgramOutput output = server.serverDump(); + assertEquals(0, output.getReturnCode()); + assertEquals("", output.getStderr()); + + // Parse standard output. Examples: + // Server com.ibm.ws.session.cache.fat.config.infinispan dump complete in /Users/user/lgit/open-liberty/dev/build.image/wlp/usr/servers/com.ibm.ws.session.cache.fat.config.infinispan/com.ibm.ws.session.cache.fat.config.infinispan.dump-18.04.11_14.30.55.zip. + // Server com.ibm.ws.session.cache.fat.config.infinispan dump complete in C:\\jazz-build-engines\\wasrtc-proxy.hursley.ibm.com\\EBC.PROD.WASRTC\\build\\dev\\image\\output\\wlp\\usr\\servers\\com.ibm.ws.session.cache.fat.config.infinispan\\com.ibm.ws.session.cache.fat.config.infinispan.dump-18.06.10_00.16.59.zip. + + String out = output.getStdout(); + int end = out.lastIndexOf('.'); + int begin = out.lastIndexOf(' ', end) + 1; + + String dumpFileName = out.substring(begin, end); + + System.out.println("Dump file name: " + dumpFileName); + + // Example of file within the zip: + // dump_18.04.11_14.30.55/introspections/PersistentExecutorIntrospector.txt + + end = dumpFileName.indexOf(".zip"); + String prefix = "com.ibm.ws.concurrent.persistent.fat.errorpaths.dump-"; + begin = dumpFileName.lastIndexOf(prefix, end) + prefix.length(); + + String introspectorFileName = "dump_" + dumpFileName.substring(begin, end) + "/introspections/PersistentExecutorIntrospector.txt"; + + System.out.println("Looking for intropspector entry: " + introspectorFileName); + + List lines = new ArrayList(); + try (ZipFile dumpFile = new ZipFile(dumpFileName)) { + ZipEntry entry = dumpFile.getEntry(introspectorFileName); + System.out.println("Found: " + entry); + try (BufferedInputStream in = new BufferedInputStream(dumpFile.getInputStream(entry))) { + for (Scanner scanner = new Scanner(in); scanner.hasNextLine();) { + String line = scanner.nextLine(); + System.out.println(line); + lines.add(line); + } + } + } + + return lines; + } } \ No newline at end of file diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTest.java b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTest.java index cb8aeaab695a..adefa19d470f 100755 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTest.java +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2019 IBM Corporation and others. + * Copyright (c) 2014, 2020 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -18,6 +18,8 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.util.Arrays; +import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -129,6 +131,116 @@ public void testGetWithTimeout() throws Exception { runInServlet("testGetWithTimeout"); } + /** + * Verify that pending/active task ids, plus other helpful information, appears in the server dump output. + */ + // TODO switch to full mode after we are further along + @Test + public void testIntrospectorWithFailOverDisabled() throws Exception { + // schedule some tasks that will remain active while the introspector output is recorded + StringBuilder response = runInServlet("testScheduleIntrospectableTasks"); + int i = response.indexOf("TASKS ARE "); + if (i < 0) + throw new Exception("Start of task list not found in output: " + response); + int end = response.indexOf(".", i); + if (end < 0) + throw new Exception("End of task list not found in output: " + response); + + String[] scheduledTasks = response.substring(i + "TASKS ARE ".length(), end).split(","); + + boolean successful = false; + try { + // Request a dump of the server + List output = FATSuite.persistentExecutorIntrospectorDump(); + + // Validate contents of dump + String found = null; + for (String line : output) + if (line.contains("concurrent/myScheduler")) { + found = line; + if (line.toLowerCase().contains("deactivated")) + throw new Exception("Persistent executor should still be active. " + output); + break; + } + if (found == null) + throw new Exception("Persistent executor JNDI name is absent from server dump output: " + output); + + found = null; + for (String line : output) { + int p = line.indexOf("Partition "); + if (p >= 0) { + found = line; + String s = line.substring(p + "Partition ".length()); + long l = Long.parseLong(s); + if (l < 1) + throw new Exception("Invalid partition in server dump output: " + output); + break; + } + } + if (found == null) + throw new Exception("Persistent executor partition is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("initialPollDelay=3600000ms")) { + found = line; + break; + } + if (found == null) + throw new Exception("Persistent executor config is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("Accessed from [persistenterrtest]")) { + found = line; + break; + } + if (found == null) + throw new Exception("Application that used persistent executor is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("persistenterrtest is STARTED")) { + found = line; + break; + } + if (found == null) + throw new Exception("State of application that used persistent executor is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("In-memory")) { + found = line; + int b1 = line.indexOf('['); + int b2 = line.indexOf(']'); + if (b1 < 0 || b2 < 0 || b2 < b1) + throw new Exception("In-memory task list not found in server dump output " + output); + String[] foundTasks = line.substring(b1 + 1, b2).split(", "); + List foundTasksList = Arrays.asList(foundTasks); + for (String taskId : scheduledTasks) + if (!foundTasksList.contains(taskId)) + throw new Exception("Scheduled tasks " + Arrays.toString(scheduledTasks) + " are not all found in server dump output: " + output); + break; + } + if (found == null) + throw new Exception("In-memory tasks are absent from server dump output " + output); + + successful = true; + } finally { + // Cancel tasks that we created during this test + try { + StringBuilder removalRequest = new StringBuilder("testRemoveTasks"); + for (String taskId : scheduledTasks) + removalRequest.append("&taskId=").append(taskId); + runInServlet(removalRequest.toString()); + } catch (Exception x) { + if (successful) + throw x; + // else allow the original failure to be raised + } + } + } + @Test public void testLongRunningTask() throws Exception { runInServlet("testLongRunningTask"); diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTestWithFailoverAndPollingEnabled.java b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTestWithFailoverAndPollingEnabled.java index c1f048cf15e6..55349b4403d6 100644 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTestWithFailoverAndPollingEnabled.java +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/fat/src/com/ibm/ws/concurrent/persistent/fat/errorpaths/PersistentExecutorErrorPathsTestWithFailoverAndPollingEnabled.java @@ -18,6 +18,7 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.util.Arrays; import java.util.List; import org.junit.AfterClass; @@ -191,6 +192,126 @@ public void testFailOnceAndSkipFirstRetryNoAutoPurgeFEWithPolling() throws Excep runInServlet("testFailOnceAndSkipFirstRetryNoAutoPurge"); } + /** + * Verify that pending/active task ids, plus other helpful information, appears in the server dump output. + */ + // TODO switch to full mode after we are further along + @Test + public void testIntrospectorWithFailOverEnabled() throws Exception { + // schedule some tasks that will remain active while the introspector output is recorded + StringBuilder response = runInServlet("testScheduleDistantIntrospectableTasks"); + int i = response.indexOf("TASKS ARE "); + if (i < 0) + throw new Exception("Start of task list not found in output: " + response); + int end = response.indexOf(".", i); + if (end < 0) + throw new Exception("End of task list not found in output: " + response); + + String[] scheduledDistantTasks = response.substring(i + "TASKS ARE ".length(), end).split(","); + + response = runInServlet("testScheduleFrequentIntrospectableTasks"); + i = response.indexOf("TASKS ARE "); + if (i < 0) + throw new Exception("Start of task list not found in output: " + response); + end = response.indexOf(".", i); + if (end < 0) + throw new Exception("End of task list not found in output: " + response); + + String[] scheduledFrequentTasks = response.substring(i + "TASKS ARE ".length(), end).split(","); + + boolean successful = false; + try { + // Request a dump of the server + List output = FATSuite.persistentExecutorIntrospectorDump(); + + // Validate contents of dump + String found = null; + for (String line : output) + if (line.contains("concurrent/myScheduler")) { + found = line; + if (line.toLowerCase().contains("deactivated")) + throw new Exception("Persistent executor should still be active. " + output); + break; + } + if (found == null) + throw new Exception("Persistent executor JNDI name is absent from server dump output: " + output); + + found = null; + for (String line : output) + if (line.contains("Partition 0")) { + found = line; + break; + } + if (found == null) + throw new Exception("Persistent executor partition must show as 0 in server dump output when fail over is enabled " + output); + + found = null; + for (String line : output) + if (line.contains("missedTaskThreshold=5s")) { + found = line; + break; + } + if (found == null) + throw new Exception("Persistent executor config is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("Accessed from [persistenterrtest]")) { + found = line; + break; + } + if (found == null) + throw new Exception("Application that used persistent executor is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("persistenterrtest is STARTED")) { + found = line; + break; + } + if (found == null) + throw new Exception("State of application that used persistent executor is absent from server dump output " + output); + + found = null; + for (String line : output) + if (line.contains("In-memory")) { + found = line; + int b1 = line.indexOf('['); + int b2 = line.indexOf(']'); + if (b1 < 0 || b2 < 0 || b2 < b1) + throw new Exception("In-memory task list not found in server dump output " + output); + String[] foundTasks = line.substring(b1 + 1, b2).split(", "); + List foundTasksList = Arrays.asList(foundTasks); + for (String taskId : scheduledFrequentTasks) + if (!foundTasksList.contains(taskId)) + throw new Exception("Scheduled tasks " + Arrays.toString(scheduledFrequentTasks) + " are not all found in server dump output: " + output); + for (String taskId : scheduledDistantTasks) + if (foundTasksList.contains(taskId)) + throw new Exception("Scheduled task " + taskId + " should not be found in server dump output " + + "because it is scheduled for the distant future and not claimed by this instance " + output); + break; + } + if (found == null) + throw new Exception("In-memory tasks are absent from server dump output " + output); + + successful = true; + } finally { + // Cancel tasks that we created during this test + try { + StringBuilder removalRequest = new StringBuilder("testRemoveTasks"); + for (String taskId : scheduledDistantTasks) + removalRequest.append("&taskId=").append(taskId); + for (String taskId : scheduledFrequentTasks) + removalRequest.append("&taskId=").append(taskId); + runInServlet(removalRequest.toString()); + } catch (Exception x) { + if (successful) + throw x; + // else allow the original failure to be raised + } + } + } + /** * testMissedTaskThresholdBelowMinimum - attempt to use a persistent executor where the missedTaskThreshold value is less than * the minimum allowed. Expect IllegalArgumentException with a translatable message. diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/publish/servers/com.ibm.ws.concurrent.persistent.fat.errorpaths/server.xml b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/publish/servers/com.ibm.ws.concurrent.persistent.fat.errorpaths/server.xml index 1fa44a220ffd..26c8f3321081 100755 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/publish/servers/com.ibm.ws.concurrent.persistent.fat.errorpaths/server.xml +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/publish/servers/com.ibm.ws.concurrent.persistent.fat.errorpaths/server.xml @@ -1,5 +1,5 @@ persistentExecutor-1.0 servlet-3.1 @@ -18,7 +19,7 @@ - + diff --git a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/test-applications/persistenterrtest/src/web/PersistentErrorTestServlet.java b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/test-applications/persistenterrtest/src/web/PersistentErrorTestServlet.java index 418fec629519..ad2093394c79 100755 --- a/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/test-applications/persistenterrtest/src/web/PersistentErrorTestServlet.java +++ b/dev/com.ibm.ws.concurrent.persistent_fat_errorpaths/test-applications/persistenterrtest/src/web/PersistentErrorTestServlet.java @@ -87,7 +87,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t try { out.println(getClass().getSimpleName() + " is starting " + test + "
"); System.out.println("-----> " + test + " starting"); - getClass().getMethod(test, PrintWriter.class).invoke(this, out); + getClass().getMethod(test, HttpServletRequest.class, PrintWriter.class).invoke(this, request, out); System.out.println("<----- " + test + " successful"); out.println(test + " " + SUCCESS_MESSAGE); } catch (Throwable x) { @@ -107,7 +107,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t /** * Schedule a task that fails all execution attempts, exceeding the task failure limit, then and auto purges. */ - public void testFailingTaskAndAutoPurge(PrintWriter out) throws Exception { + public void testFailingTaskAndAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedFailingTask.execProps.put(AutoPurge.PROPERTY_NAME, AutoPurge.ALWAYS.toString()); SharedFailingTask.failOn.add(1l); @@ -135,7 +135,7 @@ public void testFailingTaskAndAutoPurge(PrintWriter out) throws Exception { * Schedule a task that fails all execution attempts, exceeding the task failure limit, * and remains in the persistent store upon completion. */ - public void testFailingTaskNoAutoPurge(PrintWriter out) throws Exception { + public void testFailingTaskNoAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedFailingTask.execProps.put(ManagedTask.IDENTITY_NAME, "testFailingTaskNoAutoPurge"); SharedFailingTask.failOn.add(1l); @@ -181,7 +181,7 @@ public void testFailingTaskNoAutoPurge(PrintWriter out) throws Exception { * Schedule a task that fails an execution attempt and skips the retry. * Autopurges when the retry runs the next time after the skip. */ - public void testFailOnceAndSkipFirstRetryAndAutoPurge(PrintWriter out) throws Exception { + public void testFailOnceAndSkipFirstRetryAndAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedSkippingTrigger.clear(); SharedFailingTask task = new SharedFailingTask(); @@ -232,7 +232,7 @@ public void testFailOnceAndSkipFirstRetryAndAutoPurge(PrintWriter out) throws Ex /** * Schedule a task that fails an execution attempt and skips the retry. Do not autopurge. */ - public void testFailOnceAndSkipFirstRetryNoAutoPurge(PrintWriter out) throws Exception { + public void testFailOnceAndSkipFirstRetryNoAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedSkippingTrigger.clear(); SharedFailingTask task = new SharedFailingTask(); @@ -292,7 +292,7 @@ public void testFailOnceAndSkipFirstRetryNoAutoPurge(PrintWriter out) throws Exc /** * Attempt to invoke TaskStatus.get(timeout, unit). Expect UnsupportedOperationException. */ - public void testGetWithTimeout(PrintWriter out) throws Exception { + public void testGetWithTimeout(HttpServletRequest request, PrintWriter out) throws Exception { ScheduledFuture status = scheduler.schedule((Callable) new SharedCounterTask(), 14, TimeUnit.DAYS); try { Long result = status.get(14, TimeUnit.MICROSECONDS); @@ -304,7 +304,7 @@ public void testGetWithTimeout(PrintWriter out) throws Exception { /** * Attempt to schedule a task with the long running hint set to true. Verify it is rejected. */ - public void testLongRunningTask(PrintWriter out) throws Exception { + public void testLongRunningTask(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedFailingTask.execProps.put(ManagedTask.LONGRUNNING_HINT, Boolean.TRUE.toString()); try { @@ -324,7 +324,7 @@ public void testLongRunningTask(PrintWriter out) throws Exception { * testMissedTaskThresholdBelowMinimum - attempt to use a persistent executor where the missedTaskThreshold value is less than * the minimum allowed. The detailed error message that is logged is tested by the caller of this method. */ - public void testMissedTaskThresholdBelowMinimum(PrintWriter out) throws Exception { + public void testMissedTaskThresholdBelowMinimum(HttpServletRequest request, PrintWriter out) throws Exception { try { PersistentExecutor misconfiguredExecutor = InitialContext.doLookup("concurrent/belowMinMissedTaskThreshold"); throw new Exception("Should not be able to obtain misconfigured persistentExecutor where the missedTaskThreshold value is less than the minimum allowed " + misconfiguredExecutor); @@ -337,7 +337,7 @@ public void testMissedTaskThresholdBelowMinimum(PrintWriter out) throws Exceptio * testMissedTaskThresholdExceedsMaximum - attempt to use a persistent executor where the missedTaskThreshold value exceeds * the maximum allowed. The detailed error message that is logged is tested by the caller of this method. */ - public void testMissedTaskThresholdExceedsMaximum(PrintWriter out) throws Exception { + public void testMissedTaskThresholdExceedsMaximum(HttpServletRequest request, PrintWriter out) throws Exception { try { PersistentExecutor misconfiguredExecutor = InitialContext.doLookup("concurrent/exceedsMaxMissedTaskThreshold"); throw new Exception("Should not be able to obtain misconfigured persistentExecutor where missedTaskThreshold value exceeds the maximum allowed " + misconfiguredExecutor); @@ -349,7 +349,7 @@ public void testMissedTaskThresholdExceedsMaximum(PrintWriter out) throws Except /** * Schedule a task with a negative transaction timeout. Expect IllegalArgumentException. */ - public void testNegativeTransactionTimeout(PrintWriter out) throws Exception { + public void testNegativeTransactionTimeout(HttpServletRequest request, PrintWriter out) throws Exception { TenSecondTask task = new TenSecondTask(); task.getExecutionProperties().put(PersistentExecutor.TRANSACTION_TIMEOUT, "-2"); try { @@ -364,7 +364,7 @@ public void testNegativeTransactionTimeout(PrintWriter out) throws Exception { /** * Schedule a task that requires Classloader Context, but don't capture and propagate that type of context to the task. */ - public void testNoClassloaderContext(PrintWriter out) throws Exception { + public void testNoClassloaderContext(HttpServletRequest request, PrintWriter out) throws Exception { LoadClassTask task = new LoadClassTask(); System.out.println("Servlet thread class loader " + Thread.currentThread().getContextClassLoader()); @@ -405,7 +405,7 @@ public void testNoClassloaderContext(PrintWriter out) throws Exception { /** * Schedule a task that requires Java EE Metadata Context, but don't capture and propagate that type of context to the task. */ - public void testNoJavaEEMetadataContext(PrintWriter out) throws Exception { + public void testNoJavaEEMetadataContext(HttpServletRequest request, PrintWriter out) throws Exception { LookupTask task = new LookupTask(); // Validate that it works from the servlet thread @@ -443,7 +443,7 @@ public void testNoJavaEEMetadataContext(PrintWriter out) throws Exception { /** * Schedule a task with a transaction timeout value that isn't an integer. Expect IllegalArgumentException. */ - public void testNonIntegerTransactionTimeout(PrintWriter out) throws Exception { + public void testNonIntegerTransactionTimeout(HttpServletRequest request, PrintWriter out) throws Exception { TenSecondTask task = new TenSecondTask(); task.getExecutionProperties().put(PersistentExecutor.TRANSACTION_TIMEOUT, "1.5"); try { @@ -461,7 +461,7 @@ public void testNonIntegerTransactionTimeout(PrintWriter out) throws Exception { * Schedule a task that returns a non-serializable result. Expect the TaskStatus to be returned successfully, * but an error must occur when attempting to access the result. */ - public void testNonSerializableResult(PrintWriter out) throws Exception { + public void testNonSerializableResult(HttpServletRequest request, PrintWriter out) throws Exception { NonSerializableTaskAndResult.resultOverride = null; // just in case any other test forgets to clean it up TaskStatus status = scheduler.schedule(new NonSerializableTaskAndResult(), 15, TimeUnit.NANOSECONDS); @@ -488,7 +488,7 @@ public void testNonSerializableResult(PrintWriter out) throws Exception { /** * Attempt to create/find/remove null (and empty) property and value. */ - public void testNullProperty(PrintWriter out) throws Exception { + public void testNullProperty(HttpServletRequest request, PrintWriter out) throws Exception { try { boolean created = scheduler.createProperty(null, "value1"); throw new Exception("Should not be able to create a property with null name. Result: " + created); @@ -533,7 +533,7 @@ public void testNullProperty(PrintWriter out) throws Exception { /** * Attempt to submit null tasks. Verify the proper errors are raised. */ - public void testNullTasks(PrintWriter out) throws Exception { + public void testNullTasks(HttpServletRequest request, PrintWriter out) throws Exception { Trigger trigger = new NextTenthOfSecondTrigger(); try { @@ -600,7 +600,7 @@ public void testNullTasks(PrintWriter out) throws Exception { /** * Attempt to submit tasks with null Triggers. Verify the proper errors are raised. */ - public void testNullTriggers(PrintWriter out) throws Exception { + public void testNullTriggers(HttpServletRequest request, PrintWriter out) throws Exception { try { ScheduledFuture status = scheduler.schedule((Callable) new SharedCounterTask(), null); throw new Exception("schedule(callable, null trigger) should fail. Instead: " + status); @@ -621,7 +621,7 @@ public void testNullTriggers(PrintWriter out) throws Exception { /** * Attempt to submit tasks with null units. Verify the proper errors are raised. */ - public void testNullUnits(PrintWriter out) throws Exception { + public void testNullUnits(HttpServletRequest request, PrintWriter out) throws Exception { try { Future status = scheduler.schedule((Callable) new SharedCounterTask(), 6, null); throw new Exception("schedule(callable, null units) should fail. Instead: " + status); @@ -651,7 +651,7 @@ public void testNullUnits(PrintWriter out) throws Exception { * testPollIntervalBelowMinimum - attempt to use a persistent executor where the pollInterval value is less than * the minimum allowed. The detailed error message that is logged is tested by the caller of this method. */ - public void testPollIntervalBelowMinimum(PrintWriter out) throws Exception { + public void testPollIntervalBelowMinimum(HttpServletRequest request, PrintWriter out) throws Exception { try { PersistentExecutor misconfiguredExecutor = InitialContext.doLookup("concurrent/belowMinPollInterval"); throw new Exception("Should not be able to obtain misconfigured persistentExecutor where the pollInterval value is less than the minimum allowed. " + misconfiguredExecutor); @@ -664,7 +664,7 @@ public void testPollIntervalBelowMinimum(PrintWriter out) throws Exception { * testPollIntervalThresholdExceedsMaximum - attempt to use a persistent executor where the pollInterval value exceeds * the maximum allowed. The detailed error message that is logged is tested by the caller of this method. */ - public void testPollIntervalExceedsMaximum(PrintWriter out) throws Exception { + public void testPollIntervalExceedsMaximum(HttpServletRequest request, PrintWriter out) throws Exception { try { PersistentExecutor misconfiguredExecutor = InitialContext.doLookup("concurrent/exceedsMaxPollInterval"); throw new Exception("Should not be able to obtain misconfigured persistentExecutor where the pollInterval value exceeds the maximum allowed " + misconfiguredExecutor); @@ -676,7 +676,7 @@ public void testPollIntervalExceedsMaximum(PrintWriter out) throws Exception { /** * Attempt to schedule a task with a predetermined result that declares itself serializable but fails to serialize. */ - public void testPredeterminedResultFailsToSerialize(PrintWriter out) throws Exception { + public void testPredeterminedResultFailsToSerialize(HttpServletRequest request, PrintWriter out) throws Exception { try { TaskStatus status = scheduler.submit(new SharedCounterTask(), new ResultThatFailsSerialization()); throw new Exception("Task should not schedule when its predetermined result fails to serialize. " + status); @@ -691,7 +691,7 @@ public void testPredeterminedResultFailsToSerialize(PrintWriter out) throws Exce /** * Attempt to schedule a task with a predetermined result that is not serializable. */ - public void testPredeterminedResultIsNotSerializable(PrintWriter out) throws Exception { + public void testPredeterminedResultIsNotSerializable(HttpServletRequest request, PrintWriter out) throws Exception { try { TaskStatus status = scheduler.submit(new SharedCounterTask(), new Object()); throw new Exception("Task should not schedule when its predetermined result is not serializable. " + status); @@ -703,12 +703,22 @@ public void testPredeterminedResultIsNotSerializable(PrintWriter out) throws Exc } } + /** + * Removes tasks with the specified ids. + */ + public void testRemoveTasks(HttpServletRequest request, PrintWriter out) throws Exception { + for (String taskId : request.getParameterValues("taskId")) { + if (!scheduler.remove(Long.valueOf(taskId))) + throw new Exception("Did not find task " + taskId); + } + } + /** * Schedule a task that returns a result that fails to serialize. Expect the TaskStatus to be returned successfully, * but the error must be chained to the ExecutionException (including any chained exceptions) * when attempting to access the result. */ - public void testResultFailsToSerialize(PrintWriter out) throws Exception { + public void testResultFailsToSerialize(HttpServletRequest request, PrintWriter out) throws Exception { try { Throwable cause = new ServiceConfigurationError("chained cause 1").initCause(new SQLException("chained cause 2")); NonSerializableTaskAndResult.resultOverride = new ResultThatFailsSerialization("testResultFailsToSeralize", cause); @@ -759,7 +769,7 @@ public void testResultFailsToSerialize(PrintWriter out) throws Exception { * Expect the TaskStatus to be returned successfully, but a serializable copy of the error must be chained to * the ExecutionException (including copies of any chained exceptions) when attempting to access the result. */ - public void testResultSerializationFailureFailsToSerialize(PrintWriter out) throws Exception { + public void testResultSerializationFailureFailsToSerialize(HttpServletRequest request, PrintWriter out) throws Exception { try { Throwable cause = new IllegalStateException("chained cause"); NonSerializableTaskAndResult.resultOverride = new ResultThatFailsSerialization(new ThreadGroup("this is not serializable"), cause); @@ -798,7 +808,7 @@ public void testResultSerializationFailureFailsToSerialize(PrintWriter out) thro * Schedule a task that fails its first execution attempt, but runs successfully the next time, * and auto purges (by default) upon completion. */ - public void testRetryFailedTaskAndAutoPurge(PrintWriter out) throws Exception { + public void testRetryFailedTaskAndAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedFailingTask.failOn.add(1l); try { @@ -823,7 +833,7 @@ public void testRetryFailedTaskAndAutoPurge(PrintWriter out) throws Exception { * Schedule a task that fails its first execution attempt, but runs successfully the next time, * and remains in the persistent store upon completion. */ - public void testRetryFailedTaskNoAutoPurge(PrintWriter out) throws Exception { + public void testRetryFailedTaskNoAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedFailingTask.clear(); SharedFailingTask.execProps.put(AutoPurge.PROPERTY_NAME, AutoPurge.NEVER.toString()); SharedFailingTask.failOn.add(1l); @@ -849,7 +859,7 @@ public void testRetryFailedTaskNoAutoPurge(PrintWriter out) throws Exception { * testRetryIntervalBelowMissedTaskThreshold - attempt to use a persistent executor where the retryInterval value is less than * the missedTaskThreshold. The detailed error message that is logged is tested by the caller of this method. */ - public void testRetryIntervalBelowMissedTaskThreshold(PrintWriter out) throws Exception { + public void testRetryIntervalBelowMissedTaskThreshold(HttpServletRequest request, PrintWriter out) throws Exception { try { PersistentExecutor misconfiguredExecutor = InitialContext.doLookup("concurrent/retryIntervalBelowMissedTaskThreshold"); throw new Exception("Should not be able to obtain misconfigured persistentExecutor where the retryInterval value is less than the missedTaskThreshold. " + misconfiguredExecutor); @@ -858,11 +868,49 @@ public void testRetryIntervalBelowMissedTaskThreshold(PrintWriter out) throws Ex } } + /** + * Schedule some tasks that will remain pending for the distant future during a dump of the server. + * Report the task IDs so that the controlling test can look for them in the output. + */ + public void testScheduleDistantIntrospectableTasks(HttpServletRequest request, PrintWriter out) throws Exception { + TaskStatus statusA = scheduler.scheduleAtFixedRate(new SharedCounterTask(), 32, 32, TimeUnit.DAYS); + + TaskStatus statusB = scheduler.schedule((Runnable) new SharedCounterTask(), 32, TimeUnit.HOURS); + + out.println("TASKS ARE " + statusA.getTaskId() + "," + statusB.getTaskId() + "."); + } + + /** + * Schedule some tasks that will run frequently during a dump of the server. + * Report the task IDs so that the controlling test can look for them in the output. + */ + public void testScheduleFrequentIntrospectableTasks(HttpServletRequest request, PrintWriter out) throws Exception { + TaskStatus statusA = scheduler.scheduleWithFixedDelay(new SharedCounterTask(), 0, 300, TimeUnit.MILLISECONDS); + + TaskStatus statusB = scheduler.scheduleAtFixedRate(new SharedCounterTask(), 0, 333, TimeUnit.MILLISECONDS); + + out.println("TASKS ARE " + statusA.getTaskId() + "," + statusB.getTaskId() + "."); + } + + /** + * Schedule some tasks that will run or remain pending during a dump of the server. + * Report the task IDs so that the controlling test can look for them in the output. + */ + public void testScheduleIntrospectableTasks(HttpServletRequest request, PrintWriter out) throws Exception { + TaskStatus statusA = scheduler.scheduleWithFixedDelay(new SharedCounterTask(), 0, 250, TimeUnit.MILLISECONDS); + + TaskStatus statusB = scheduler.scheduleAtFixedRate(new SharedCounterTask(), 0, 26, TimeUnit.DAYS); + + TaskStatus statusC = scheduler.schedule((Runnable) new SharedCounterTask(), 27, TimeUnit.HOURS); + + out.println("TASKS ARE " + statusA.getTaskId() + "," + statusB.getTaskId() + "," + statusC.getTaskId() + "."); + } + /** * Shut down the database before a task executes. Connection errors will occur, * but the task should be retried and succeed on the next attempt. */ - public void testShutDownDerbyBeforeTaskExecution(PrintWriter out) throws Exception { + public void testShutDownDerbyBeforeTaskExecution(HttpServletRequest request, PrintWriter out) throws Exception { SharedCounterTask.counter.set(0); SharedCounterTask task = new SharedCounterTask(); TaskStatus status = scheduler.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS); @@ -888,7 +936,7 @@ public void testShutDownDerbyBeforeTaskExecution(PrintWriter out) throws Excepti * Shut down the database while a task is running. Connection errors will occur, * but the task should be retried and succeed on the next attempt. */ - public void testShutDownDerbyDuringTaskExecution(PrintWriter out) throws Exception { + public void testShutDownDerbyDuringTaskExecution(HttpServletRequest request, PrintWriter out) throws Exception { DerbyShutdownTask task = new DerbyShutdownTask(); // auto-purges upon successful completion DerbyShutdownTask.counter.set(0); @@ -913,7 +961,7 @@ public void testShutDownDerbyDuringTaskExecution(PrintWriter out) throws Excepti /** * Trigger.skipRun fails causing the first execution attempt to be skipped. The second attempt should succeed. */ - public void testSkipRunFailsOnFirstExecutionAttempt(PrintWriter out) throws Exception { + public void testSkipRunFailsOnFirstExecutionAttempt(HttpServletRequest request, PrintWriter out) throws Exception { SharedCounterTask.counter.set(0); SharedCounterTask task = new SharedCounterTask(); @@ -938,7 +986,7 @@ public void testSkipRunFailsOnFirstExecutionAttempt(PrintWriter out) throws Exce * The task entry should be autopurged because it was not failed or canceled. * In terms of autopurging, it should make no difference whether the first, last, or any other executions were skipped. */ - public void testSkipRunFailsOnLastExecutionAttempt(PrintWriter out) throws Exception { + public void testSkipRunFailsOnLastExecutionAttempt(HttpServletRequest request, PrintWriter out) throws Exception { SharedCounterTask.counter.set(0); SharedCounterTask task = new SharedCounterTask(); @@ -958,7 +1006,7 @@ public void testSkipRunFailsOnLastExecutionAttempt(PrintWriter out) throws Excep * Trigger.skipRun fails causing the last execution attempt to be skipped. The first attempt should succeed. * Verify that the task entry remains in the persistent store because we disabled autopurge. */ - public void testSkipRunFailsOnLastExecutionAttemptNoAutoPurge(PrintWriter out) throws Exception { + public void testSkipRunFailsOnLastExecutionAttemptNoAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { NonSerializableTaskAndResult task = new NonSerializableTaskAndResult(); ImmediateSkippingTrigger trigger = new ImmediateSkippingTrigger(2); @@ -985,7 +1033,7 @@ public void testSkipRunFailsOnLastExecutionAttemptNoAutoPurge(PrintWriter out) t * Trigger.skipRun fails causing the second and third execution attempts to be skipped. * The first and last attempts should succeed. */ - public void testSkipRunFailsOnMiddleExecutionAttempts(PrintWriter out) throws Exception { + public void testSkipRunFailsOnMiddleExecutionAttempts(HttpServletRequest request, PrintWriter out) throws Exception { SharedCounterTask.counter.set(0); SharedCounterTask task = new SharedCounterTask(); @@ -1011,7 +1059,7 @@ public void testSkipRunFailsOnMiddleExecutionAttempts(PrintWriter out) throws Ex * The task should be autopurged because it was not failed or canceled. * In terms of autopurging, it should make no difference whether the first, last, or any other executions were skipped. */ - public void testSkipRunFailsOnOnlyExecutionAttempt(PrintWriter out) throws Exception { + public void testSkipRunFailsOnOnlyExecutionAttempt(HttpServletRequest request, PrintWriter out) throws Exception { SharedCounterTask.counter.set(0); SharedCounterTask task = new SharedCounterTask(); @@ -1031,7 +1079,7 @@ public void testSkipRunFailsOnOnlyExecutionAttempt(PrintWriter out) throws Excep * Trigger.skipRun fails causing the only execution attempt to be skipped. * Verify that the task entry remains in the persistent store because we disabled autopurge. */ - public void testSkipRunFailsOnOnlyExecutionAttemptNoAutoPurge(PrintWriter out) throws Exception { + public void testSkipRunFailsOnOnlyExecutionAttemptNoAutoPurge(HttpServletRequest request, PrintWriter out) throws Exception { SharedCounterTask.counter.set(0); NonSerializableTaskAndResult task = new NonSerializableTaskAndResult(); @@ -1058,7 +1106,7 @@ public void testSkipRunFailsOnOnlyExecutionAttemptNoAutoPurge(PrintWriter out) t /** * Attempt to schedule a task that declares itself serializable but fails to serialize. */ - public void testTaskFailsToSerialize(PrintWriter out) throws Exception { + public void testTaskFailsToSerialize(HttpServletRequest request, PrintWriter out) throws Exception { try { TaskStatus status = scheduler.schedule(new TaskThatFailsSerialization(), 21, TimeUnit.DAYS); throw new Exception("Task should not schedule when it fails to serialize. " + status); @@ -1074,7 +1122,7 @@ public void testTaskFailsToSerialize(PrintWriter out) throws Exception { /** * Schedule a task that exceeds the transaction timeout when it runs. */ - public void testTransactionTimeout(PrintWriter out) throws Exception { + public void testTransactionTimeout(HttpServletRequest request, PrintWriter out) throws Exception { TenSecondTask task = new TenSecondTask(); task.getExecutionProperties().put(AutoPurge.PROPERTY_NAME, AutoPurge.NEVER.name()); task.getExecutionProperties().put(ManagedTask.IDENTITY_NAME, "testTransactionTimeout"); @@ -1096,7 +1144,7 @@ public void testTransactionTimeout(PrintWriter out) throws Exception { /** * Schedule a task that exceeds the transaction timeout of the suspended persistent executor transaction when it runs. */ - public void testTransactionTimeoutSuspendedTransaction(PrintWriter out) throws Exception { + public void testTransactionTimeoutSuspendedTransaction(HttpServletRequest request, PrintWriter out) throws Exception { TenSecondTask task = new TenSecondTask(); task.getExecutionProperties().put(AutoPurge.PROPERTY_NAME, AutoPurge.NEVER.name()); task.getExecutionProperties().put(ManagedTask.IDENTITY_NAME, "testTransactionTimeoutSuspendedTransaction"); @@ -1119,7 +1167,7 @@ public void testTransactionTimeoutSuspendedTransaction(PrintWriter out) throws E /** * When Trigger.getNextRunTime fails after a task runs, the execution must be rolled back and retried until the failure is reported. */ - public void testTriggerFailsGetNextRunTimeAfterTaskRuns(PrintWriter out) throws Exception { + public void testTriggerFailsGetNextRunTimeAfterTaskRuns(HttpServletRequest request, PrintWriter out) throws Exception { Callable task = new SharedCounterTask(); FailingTrigger trigger = new FailingTrigger(); trigger.allowFirstGetNextRunTime = true; @@ -1144,7 +1192,7 @@ public void testTriggerFailsGetNextRunTimeAfterTaskRuns(PrintWriter out) throws /** * Attempt to schedule a task where Trigger.getNextRunTime fails. */ - public void testTriggerFailsInitialGetNextRunTime(PrintWriter out) throws Exception { + public void testTriggerFailsInitialGetNextRunTime(HttpServletRequest request, PrintWriter out) throws Exception { try { TaskStatus status = scheduler.schedule((Callable) new SharedCounterTask(), new FailingTrigger()); throw new Exception("Task should not schedule when the Trigger fails getNextRunTime: " + status); @@ -1157,7 +1205,7 @@ public void testTriggerFailsInitialGetNextRunTime(PrintWriter out) throws Except /** * Attempt to schedule a task where the Trigger declares itself serializable but fails to serialize. */ - public void testTriggerFailsToSerialize(PrintWriter out) throws Exception { + public void testTriggerFailsToSerialize(HttpServletRequest request, PrintWriter out) throws Exception { try { TaskStatus status = scheduler.schedule((Runnable) new SharedCounterTask(), new TriggerThatFailsSerialization()); throw new Exception("Task should not schedule when the Trigger fails to serialize. " + status); @@ -1172,7 +1220,7 @@ public void testTriggerFailsToSerialize(PrintWriter out) throws Exception { /** * Attempt to schedule a task with a trigger that has no executions. Verify the proper error is raised. */ - public void testTriggerWithNoExecutions(PrintWriter out) throws Exception { + public void testTriggerWithNoExecutions(HttpServletRequest request, PrintWriter out) throws Exception { try { TaskStatus status = scheduler.schedule((Callable) new SharedCounterTask(), new NoExecutionsTrigger()); throw new Exception("Task (Callable) should not schedule when there are no executions: " + status); @@ -1193,7 +1241,7 @@ public void testTriggerWithNoExecutions(PrintWriter out) throws Exception { /** * Attempt to invoke unsupported methods. Verify the proper errors are raised. */ - public void testUnsupportedOperations(PrintWriter out) throws Exception { + public void testUnsupportedOperations(HttpServletRequest request, PrintWriter out) throws Exception { @SuppressWarnings("unchecked") Collection> tasks = Arrays.asList((Callable) new SharedCounterTask(), (Callable) new SharedCounterTask()); @@ -1256,7 +1304,7 @@ public void testUnsupportedOperations(PrintWriter out) throws Exception { /** * Attempt to submit repeating tasks with non-positive intervals. Verify the proper errors are raised. */ - public void testZeroOrNegativeIntervals(PrintWriter out) throws Exception { + public void testZeroOrNegativeIntervals(HttpServletRequest request, PrintWriter out) throws Exception { try { Future status = scheduler.scheduleAtFixedRate(new SharedCounterTask(), 10, 0, TimeUnit.HOURS); throw new Exception("scheduleAtFixedRate(negative interval) should fail. Instead: " + status);