/* * 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 fgAllPaths; private static List fgAllDirectories; private static List 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(); fgAllDirectories = new ArrayList(); fgAllFiles = new ArrayList(); fgAllPaths.add("main:/"); fgAllDirectories.add("main:/"); Set visited = new HashSet(); AVMNodeDescriptor root = fService.getRepositoryRoot(-1, "main"); RecursiveRefresh(root, visited); } private void RecursiveRefresh(AVMNodeDescriptor dir, Set visited) { try { String baseName = dir.getPath().endsWith("/") ? dir.getPath() : dir.getPath() + "/"; Map 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 allPaths = new ArrayList(); List allDirectories = new ArrayList(); List allFiles = new ArrayList(); 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())); } }