/*
* 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.catalina.session;
import java.io.File;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.startup.ExpandWar;
import org.apache.tomcat.unittest.TesterContext;
/*
* Test for https://bz.apache.org/bugzilla/show_bug.cgi?id=69781
*
* The test currently fails almost instantly on markt's desktop with a 5s run time.
*
* This needs to be run manually. It will not run as part of the standard unit tests as it is named "Tester...". This
* could be changed once the bug has been fixed.
*/
public class TestFileStoreConcurrency {
private static final int TEST_RUN_TIME_MS = 5000;
private static final FileStore FILE_STORE = new FileStore();
private static final String STORE_DIR = "STORE_TMP";
private static final File STORE_FILE = new File(STORE_DIR);
private static final int SESSION_COUNT = 10;
private static final Session[] SESSIONS = new Session[SESSION_COUNT];
@BeforeClass
public static void setup() {
Context context = new TesterContext();
Manager manager = new TesterManager();
manager.setContext(context);
for (int i = 0; i < SESSION_COUNT; i++) {
SESSIONS[i] = new StandardSession(null);
SESSIONS[i].setManager(manager);
SESSIONS[i].setId(Integer.toString(i));
}
FILE_STORE.setDirectory(STORE_FILE.getAbsolutePath());
FILE_STORE.setManager(manager);
}
@AfterClass
public static void cleanUp() {
ExpandWar.delete(STORE_FILE);
}
@Test
public void testConcurrency() throws Exception {
SaveTask saveTask = new SaveTask();
Thread saveThread = new Thread(saveTask);
saveThread.start();
LoadTask loadTask = new LoadTask();
Thread loadThread = new Thread(loadTask);
loadThread.start();
RemoveTask removeTask = new RemoveTask();
Thread removeThread = new Thread(removeTask);
removeThread.start();
Thread.sleep(TEST_RUN_TIME_MS);
saveTask.stop();
loadTask.stop();
removeTask.stop();
saveThread.join();
loadThread.join();
removeThread.join();
Assert.assertFalse("Exception during save", saveTask.getFailed());
Assert.assertFalse("Exception during load", loadTask.getFailed());
Assert.assertFalse("Exception during remove", removeTask.getFailed());
System.out.println("Looped over sessions [" + saveTask.getLoopCount() + "] times calling save()");
System.out.println("Looped over sessions [" + loadTask.getLoopCount() + "] times calling load()");
System.out.println("Looped over sessions [" + removeTask.getLoopCount() + "] times calling remove()");
}
private static final class SaveTask extends TaskBase {
@Override
protected void doTask(Session session) throws Exception {
FILE_STORE.save(session);
}
@Override
protected String getTaskName() {
return "save";
}
}
private static final class LoadTask extends TaskBase {
@Override
protected void doTask(Session session) throws Exception {
FILE_STORE.load(session.getId());
}
@Override
protected String getTaskName() {
return "load";
}
}
private static final class RemoveTask extends TaskBase {
@Override
protected void doTask(Session session) throws Exception {
FILE_STORE.remove(session.getId());
}
@Override
protected String getTaskName() {
return "remove";
}
}
private abstract static class TaskBase implements Runnable {
private volatile boolean stop = false;
private volatile boolean failed = false;
private volatile int loopCount = 0;
@Override
public void run() {
while (!stop) {
for (Session session : SESSIONS) {
try {
doTask(session);
} catch (Exception e) {
System.out.println("Failed to " + getTaskName() + " session [" + session.getId() + "]");
e.printStackTrace(System.out);
stop = true;
failed = true;
}
}
loopCount++;
}
}
public void stop() {
stop = true;
}
public boolean getFailed() {
return failed;
}
public int getLoopCount() {
return loopCount;
}
protected abstract void doTask(Session session) throws Exception;
protected abstract String getTaskName();
}
}