611 lines
18 KiB
Java

/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.avm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
/**
* This is a Runnable which randomly performs operations on an AVM Repository.
* It's purpose is to act as a single thread in a multithreaded stress tester.
* @author britt
*/
class AVMTester implements Runnable
{
// Operation codes.
private static final int CREATE_FILE = 0;
private static final int CREATE_DIR = 1;
private static final int RENAME = 2;
private static final int CREATE_LAYERED_DIR = 3;
private static final int CREATE_LAYERED_FILE = 4;
private static final int REMOVE_NODE = 5;
private static final int MODIFY_FILE = 6;
private static final int READ_FILE = 7;
private static final int SNAPSHOT = 8;
private static List<String> fgAllPaths;
private static List<String> fgAllDirectories;
private static List<String> fgAllFiles;
private static boolean fgFrozen = false;
/**
* The operation table.
*/
private int [] fOpTable;
/**
* The number of operations to perform.
*/
private int fOpCount;
/**
* The AVMService instance.
*/
private AVMService fService;
/**
* The random number generators.
*/
private static Random fgRandom = new Random();
/**
* Names for nodes.
*/
private String[] fNames;
/**
* The id of this thread.
*/
private String fID;
/**
* Flag for whether this thread errored out.
*/
private boolean fError;
/**
* Flag for whether this thread should exit.
*/
private boolean fExit;
/**
* Initialize this with the relative frequencies of differents operations.
* @param createFile
* @param createDir
* @param rename
* @param createLayeredDir
* @param createLayeredFile
* @param removeNode
* @param modifyFile
* @param readFile
* @param snapshot
* @param opCount The number of operations to perform.
* @param service The instance of AVMService.
*/
public AVMTester(int createFile,
int createDir,
int rename,
int createLayeredDir,
int createLayeredFile,
int removeNode,
int modifyFile,
int readFile,
int snapshot,
int opCount,
AVMService service,
String id)
{
fError = false;
fExit = false;
fID = id;
fService = service;
fOpCount = opCount;
int count = createFile + createDir + rename + createLayeredDir +
createLayeredFile + removeNode + modifyFile + readFile +
snapshot;
fOpTable = new int[count];
int off = 0;
for (int i = 0; i < createFile; i++)
{
fOpTable[off + i] = CREATE_FILE;
}
off += createFile;
for (int i = 0; i < createDir; i++)
{
fOpTable[off + i] = CREATE_DIR;
}
off += createDir;
for (int i = 0; i < rename; i++)
{
fOpTable[off + i] = RENAME;
}
off += rename;
for (int i = 0; i < createLayeredDir; i++)
{
fOpTable[off + i] = CREATE_LAYERED_DIR;
}
off += createLayeredDir;
for (int i = 0; i < createLayeredFile; i++)
{
fOpTable[off + i] = CREATE_LAYERED_FILE;
}
off += createLayeredFile;
for (int i = 0; i < removeNode; i++)
{
fOpTable[off + i] = REMOVE_NODE;
}
off += removeNode;
for (int i = 0; i < modifyFile; i++)
{
fOpTable[off + i] = MODIFY_FILE;
}
off += modifyFile;
for (int i = 0; i < readFile; i++)
{
fOpTable[off + i] = READ_FILE;
}
off += readFile;
for (int i = 0; i < snapshot; i++)
{
fOpTable[off + i] = SNAPSHOT;
}
off += snapshot;
// Generate a bunch of names.
String [] letters = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z" };
fNames = new String[26 * 26];
for (int i = 0; i < 26; i++)
{
for (int j = 0; j < 26; j++)
{
fNames[i * 26 + j] = letters[i] + letters[j];
}
}
}
/**
* It's off.
*/
public void run()
{
try
{
long startTime = System.currentTimeMillis();
for (int i = 0; i < fOpCount; i++)
{
if (fgFrozen)
{
Thread.sleep(3600000);
}
if (fExit)
{
return;
}
System.out.print(fID + ":" + i + ":");
int which = fgRandom.nextInt(fOpTable.length);
switch (fOpTable[which])
{
case CREATE_FILE :
createFile();
break;
case CREATE_DIR :
createDirectory();
break;
case RENAME :
rename();
break;
case CREATE_LAYERED_DIR :
createLayeredDir();
break;
case CREATE_LAYERED_FILE :
createLayeredFile();
break;
case REMOVE_NODE :
removeNode();
break;
case MODIFY_FILE :
modifyFile();
break;
case READ_FILE :
readFile();
break;
case SNAPSHOT :
snapshot();
break;
}
}
System.out.println(fgAllPaths.size() + " fses in " + (System.currentTimeMillis() - startTime) +
"ms");
}
catch (Exception e)
{
e.printStackTrace(System.err);
fgFrozen = true;
fError = true;
}
}
private void createFile()
{
String name = "PF" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
try
{
System.out.println("create " + path + " " + name);
PrintStream out = new PrintStream(fService.createFile(path, name));
out.println(path + "/" + name);
out.close();
AddFile(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void createDirectory()
{
String name = "PD" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
try
{
System.out.println("mkdir " + path + " " + name);
fService.createDirectory(path, name);
AddDirectory(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void rename()
{
String name = fNames[fgRandom.nextInt(26 * 26)];
String path = RandomPath();
AVMNodeDescriptor desc = fService.lookup(-1, path);
if (path.equals("main:/"))
{
return;
}
int lastSlash = path.lastIndexOf('/');
String srcPath = path.substring(0, lastSlash);
if (srcPath.equals("main:"))
{
srcPath = srcPath + "/";
}
String srcName = path.substring(lastSlash + 1);
String dstPath = RandomDirectory();
try
{
System.out.println("rename " + srcPath + " " + srcName + " " + dstPath + " " + name);
fService.rename(srcPath, srcName, dstPath, name);
RemovePath(path);
if (desc.isDirectory())
{
AddDirectory(appendPath(dstPath, name));
}
else
{
AddFile(appendPath(dstPath, name));
}
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void createLayeredDir()
{
String name = "LD" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
String target = RandomDirectory();
try
{
System.out.println("mklayereddir " + path + " " + name + " " + target);
fService.createLayeredDirectory(target, path, name);
AddDirectory(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void createLayeredFile()
{
String name = "LF" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
String target = RandomFile();
try
{
System.out.println("createlayered " + path + " " + name + " " + target);
fService.createLayeredFile(target, path, name);
AddFile(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void removeNode()
{
String target = RandomPath();
int lastSlash = target.lastIndexOf('/');
String path = target.substring(0, lastSlash);
if (path.equals("main:"))
{
path = path + "/";
}
String name = target.substring(lastSlash + 1);
try
{
System.out.println("remove " + target);
fService.removeNode(path, name);
RemovePath(target);
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
}
private void modifyFile()
{
String path = RandomFile();
try
{
System.out.println("modify " + path);
PrintStream out =
new PrintStream(fService.getFileOutputStream(path));
out.println("I am " + path);
out.close();
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
}
private void readFile()
{
String path = RandomFile();
try
{
System.out.println("read " + path);
BufferedReader reader =
new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, path)));
String line = reader.readLine();
System.out.println(line);
reader.close();
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
catch (IOException e)
{
throw new AVMException("I/O Error.", e);
}
}
public void Refresh()
{
System.out.println("refresh");
fgAllPaths = new ArrayList<String>();
fgAllDirectories = new ArrayList<String>();
fgAllFiles = new ArrayList<String>();
fgAllPaths.add("main:/");
fgAllDirectories.add("main:/");
Set<Long> visited = new HashSet<Long>();
AVMNodeDescriptor root = fService.getRepositoryRoot(-1, "main");
RecursiveRefresh(root, visited);
}
private void RecursiveRefresh(AVMNodeDescriptor dir, Set<Long> visited)
{
try
{
String baseName = dir.getPath().endsWith("/") ? dir.getPath() : dir.getPath() + "/";
Map<String, AVMNodeDescriptor> listing = fService.getDirectoryListing(dir);
for (String name : listing.keySet())
{
String path = baseName + name;
AVMNodeDescriptor desc = listing.get(name);
switch (desc.getType())
{
case AVMNodeType.LAYERED_DIRECTORY :
case AVMNodeType.PLAIN_DIRECTORY :
{
if (visited.contains(desc.getId()))
{
continue;
}
visited.add(desc.getId());
fgAllPaths.add(path);
fgAllDirectories.add(path);
RecursiveRefresh(desc, visited);
break;
}
case AVMNodeType.LAYERED_FILE :
case AVMNodeType.PLAIN_FILE :
{
fgAllPaths.add(path);
fgAllFiles.add(path);
break;
}
}
}
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
}
private void snapshot()
{
System.out.println("snapshot");
fService.createSnapshot("main");
}
public boolean getError()
{
return fError;
}
public void setExit()
{
fExit = true;
}
private static synchronized void AddDirectory(String path)
{
fgAllDirectories.add(path);
fgAllPaths.add(path);
}
private static synchronized void AddFile(String path)
{
fgAllFiles.add(path);
fgAllPaths.add(path);
}
private static synchronized void RemovePath(String path)
{
List<String> allPaths = new ArrayList<String>();
List<String> allDirectories = new ArrayList<String>();
List<String> allFiles = new ArrayList<String>();
for (String p : fgAllPaths)
{
if (p.indexOf(path) != 0)
{
allPaths.add(p);
}
}
for (String p : fgAllDirectories)
{
if (p.indexOf(path) != 0)
{
allDirectories.add(p);
}
}
for (String p : fgAllFiles)
{
if (p.indexOf(path) != 0)
{
allFiles.add(p);
}
}
fgAllPaths = allPaths;
fgAllDirectories = allDirectories;
fgAllFiles = allFiles;
}
private String appendPath(String path, String name)
{
return path.endsWith("/") ? path + name : path + "/" + name;
}
private static synchronized String RandomDirectory()
{
return fgAllDirectories.get(fgRandom.nextInt(fgAllDirectories.size()));
}
private static synchronized String RandomFile()
{
return fgAllFiles.get(fgRandom.nextInt(fgAllFiles.size()));
}
private static synchronized String RandomPath()
{
return fgAllPaths.get(fgRandom.nextInt(fgAllPaths.size()));
}
}