/******************************************************************************* * Copyright (c) 2006,2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt *******************************************************************************/ package org.aspectj.testing.server; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; /** * @author Matthew Webster * @author Andy Clement */ public class TestServer implements Runnable { private static final boolean debug = Boolean.getBoolean("org.aspectj.testing.server.debug"); // AspectJ project root folder nameing pattern, case-insensitive (i.e. org.aspectj, AspectJ) protected static final String REGEX_PROJECT_ROOT_FOLDER = "(?i)(org[.])?aspectj"; private boolean exitOnError = true; private File workingDirectory; private ClassLoader rootLoader; private Map loaders = new HashMap<>(); private String mainClass = "UnknownClass"; private String mainLoader = "UnknownLoader"; public void initialize () throws IOException { createRootLoader(); loadConfiguration(); } private void loadConfiguration () throws IOException { File file = new File(workingDirectory,"server.properties"); Properties props = new Properties(); FileInputStream in = new FileInputStream(file); props.load(in); in.close(); Enumeration enu = props.propertyNames(); while (enu.hasMoreElements()) { String key = (String)enu.nextElement(); if (key.startsWith("loader.")) { createLoader(props.getProperty(key)); } else if (key.equals("main")) { StringTokenizer st = new StringTokenizer(props.getProperty(key),","); mainClass = st.nextToken(); mainLoader = st.nextToken(); } } } private void createLoader (String property) throws IOException { ClassLoader parent = rootLoader; StringTokenizer st = new StringTokenizer(property,","); String name = st.nextToken(); String classpath = st.nextToken(); if (debug) System.err.println("Creating loader "+name+" with classpath "+classpath); if (st.hasMoreTokens()) { String parentName = st.nextToken(); parent = loaders.get(parentName); if (parent == null) error("No such loader: " + parentName); } List urlList = new ArrayList<>(); st = new StringTokenizer(classpath,";"); while (st.hasMoreTokens()) { String fileName = st.nextToken(); File file = new File(workingDirectory,fileName).getCanonicalFile(); if (!file.exists()) error("Missing or invalid file: " + file.getPath()); URL url = file.toURI().toURL(); urlList.add(url); } URL[] urls = new URL[urlList.size()]; urlList.toArray(urls); ClassLoader loader = new URLClassLoader(urls, parent); if (debug) System.err.println("? TestServer.createLoader() loader=" + loader + ", name='" + name + "', urls=" + urlList + ", parent=" + parent); loaders.put(name,loader); } private void createRootLoader() throws IOException { List urlList = new ArrayList<>(); // Sandbox urlList.add(workingDirectory.getCanonicalFile().toURI().toURL()); File projectRootFolder = findProjectRootFolder(); urlList.add(new File(projectRootFolder,"runtime/target/classes").toURI().toURL()); // urlList.add(new File(projectRootFolder,"aspectjrt/target/classes").toURI().toURL()); // urlList.add(new File(projectRootFolder,"aspectj5rt/target/classes").toURI().toURL()); URL[] urls = new URL[urlList.size()]; urlList.toArray(urls); ClassLoader parent = getClass().getClassLoader().getParent(); rootLoader = new URLClassLoader(urls,parent); if (debug) System.err.println("? TestServer.createRootLoader() loader=" + rootLoader + ", urlList=" + urlList + ", parent=" + parent); } // Make protected in order to at least make this part of the code testable -> TestServerTest protected File findProjectRootFolder() throws IOException { // Find the AspectJ project root folder File currentFolder = new File(".").getCanonicalFile(); while (currentFolder != null && !currentFolder.getName().matches(REGEX_PROJECT_ROOT_FOLDER)) { currentFolder = currentFolder.getParentFile(); } if (currentFolder == null) { error( "Unable to locate project root folder matching regex '" + REGEX_PROJECT_ROOT_FOLDER + "' in " + new File(".").getCanonicalPath() ); } return currentFolder; } public void setExitOntError (boolean b) { exitOnError = b; } public void setWorkingDirectory (String name) { workingDirectory = new File(name); if (!workingDirectory.exists()) error("Missing or invalid working directory: " + workingDirectory.getPath()); } public static void main(String[] args) throws Exception { System.out.println("Starting ..."); TestServer server = new TestServer(); server.setWorkingDirectory(args[0]); server.initialize(); Thread thread = new Thread(server,"application"); thread.start(); thread.join(); System.out.println("Stopping ..."); } public void run() { System.out.println("Running " + mainClass); runClass(mainClass,loaders.get(mainLoader)); } private void runClass (String className, ClassLoader classLoader) { try { Class clazz = Class.forName(className,false,classLoader); invokeMain(clazz,new String[] {}); } catch (ClassNotFoundException ex) { ex.printStackTrace(); error(ex.toString()); } } public void invokeMain (Class clazz, String[] args) { Class[] paramTypes = new Class[1]; paramTypes[0] = args.getClass(); try { Method method = clazz.getDeclaredMethod("main",paramTypes); Object[] params = new Object[1]; params[0] = args; method.invoke(null,params); } catch (InvocationTargetException ex) { Throwable th = ex.getTargetException(); th.printStackTrace(); error(th.toString()); } catch (Throwable th) { th.printStackTrace(); error(th.toString()); } } private void error (String message) { System.out.println(message); // FIXME: // This is horrible: Why not just throw an exception? And if we exit, why with exit code 0? // It basically makes the tests useless because they might log errors without checking any conditions. if (exitOnError) System.exit(0); } }