/* * Copyright 2012 Jason Miller * * Licensed 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 jj; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * <p> * Entry point to start the JibbrJabbr container from the command line. * Behaves as the root classloader for the server. * Also contains some container-wide constants and util methods needed * to start up. * </p> * * <p> * The direct dependencies of this class are limited to things in the * bootstrapper project and JDK classes * </p> * * @author Jason Miller */ public final class JJ { // TODO MARKED FOR DEATH!! let main determine the running state public static final boolean isRunning; private static final String JJ_MAIN_CLASS = "jj.Main"; static { boolean result; try { // pretty sure this is right Class.forName(JJ_MAIN_CLASS); result = false; } catch (ClassNotFoundException cnfe) { result = true; } isRunning = result; } private static final Pattern JAR_URL_PARSER = Pattern.compile("^jar:([^!]+)!.+$"); /** * Get a resource path for a given Class, for instance * "/jj/JJ.class" for this class. Arrays are unwrapped * to their component type, and primitives and synthetic * classes are ignored. * @param clazz The class * @return The resource path */ public static String resourcePath(final Class<?> clazz) { if (clazz.isPrimitive() || clazz.isSynthetic()) return null; if (clazz.isArray()) return resourcePath(clazz.getComponentType()); return resourcePath(clazz.getName()); } /** * Get a resource path for a fully qualified class name, for * instance "/jj/JJ.class" for "jj.JJ" * @param className * @return */ public static String resourcePath(final String className) { return "/" + className.replace('.', '/') + ".class"; } /** * Basic utility to turn a Class instance into its URI for * resource lookup. * @param clazz The Class to look up * @return The URI representing that class, or null if the class cannot be * represented as a resource (for instance if it is generated) */ public static URI uri(final Class<?> clazz) { try { return clazz.getResource(resourcePath(clazz)).toURI(); } catch (Exception e) { return null; } } /** * Basic utility to get a URI to the jar that contains the * resource identified by the URI, or null if the resource * is not in a jar. * @param uri The resource URI to locate * @return The Path to the jar containing the identified resource, or null * if the resource is not in a jar. */ public static Path jarPath(URI uri) { try { Matcher m = JAR_URL_PARSER.matcher(uri.toString()); m.matches(); return Paths.get(new URI(m.group(1))); } catch (Exception e) { return null; } } /** * Basic utility to get a Path to the jar the contains the file that * defined the given Class. * @param clazz The Class to look up * @return A Path to the jar containing the class file, or null if the * jar cannot be found (for generated classes or class files not in * a jar, for instance.) */ public static Path jarForClass(Class<?> clazz) { return jarPath(uri(clazz)); } static final Path myJarPath = jarForClass(JJ.class); private static boolean initialized = false; public static void main(String[] args) throws Exception { new Thread(() -> { while (true) { try { if (System.in.read() == 3) { break; } } catch (Exception e) { break; } } System.exit(0); }).start(); try { final JJ jj = new JJ().init(args); jj.start(); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { jj.stop(); } catch (Exception e) { // kablizzle e.printStackTrace(); System.exit(-1); } } }); } catch (IllegalStateException ise) { ise.printStackTrace(); System.exit(-1); } } private Class<?> mainClass; private Object mainInstance; private JJ() {} private boolean processBootstrapArgs(String[] args) { // we install regardless if (args.length == 1 && "install".equals(args[0])) { System.out.println("⟾⟾⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾"); System.out.println("⟾⟾"); System.out.println("⟾⟾ JibbrJabbr installed to: "); System.out.println("⟾⟾ " + installer.basePath); System.out.println("⟾⟾"); System.out.println("⟾⟾⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾"); // we just stop here having performed the installation return true; } if (args.length == 1 && "env".equals(args[0])) { System.out.println("ENVIRONMENT:"); for (String key : System.getenv().keySet()) { System.out.printf("ENV[%s] = [%s]\n", key, System.getenv(key)); } System.out.println("SYSTEM PROPERTIES:"); for (Object keyObj : System.getProperties().keySet()) { String key = (String)keyObj; System.out.printf("%s = [%s]\n", key, System.getProperty(key)); } return true; } return false; } private BootstrapInstaller installer; public JJ init(String[] args) throws Exception { if (initialized) { throw new IllegalStateException("Already run once."); } initialized = true; Jars systemJars = null; if (myJarPath != null) { System.out.println("Checking installation"); installer = new BootstrapInstaller(myJarPath); if (processBootstrapArgs(args)) return this; systemJars = new Jars(installer.systemPath); mainClass = new BootstrapClassLoader(systemJars).loadClass(JJ_MAIN_CLASS); } else { System.out.println("⟾⟾⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾"); System.out.println("⟾⟾"); System.out.println("⟾⟾ WARNING - this operation is not fully implemented."); System.out.println("⟾⟾ For best results, you should package this project"); System.out.println("⟾⟾ and run from the resulting jar."); System.out.println("⟾⟾"); System.out.println("⟾⟾⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾ ⟾⟾"); return this; } makeMain(args, systemJars); return this; } private void makeMain(String[] args, Jars systemJars) throws Exception { // counts a ticker on the console while it starts try { mainInstance = mainClass.getConstructor(args.getClass()).newInstance((Object)args); mainClass.getMethod("systemJars", Jars.class).invoke(mainInstance, systemJars); } catch (InvocationTargetException ite) { messageInitError(ite.getCause()); } catch (Throwable t) { messageInitError(t); } } private void messageInitError(Throwable cause) { System.err.println("Couldn't initialize the server!"); cause.printStackTrace(); System.exit(-1); } public void start() throws Exception { if (mainInstance != null) { System.out.println("Starting up, one moment please..."); try { mainClass.getMethod("start").invoke(mainInstance); } catch (InvocationTargetException ite) { messageInitError(ite.getCause()); } } } public void stop() throws Exception { if (mainInstance != null) { mainClass.getMethod("stop").invoke(mainInstance); } } public void destroy() { if (mainInstance != null) { try { mainClass.getMethod("dispose").invoke(mainInstance); } catch (Exception e) { System.err.println("Trouble shutting down"); e.printStackTrace(); } } } }