/* * Copyright 2000-2017 JetBrains s.r.o. * * 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 com.intellij.rt.execution.application; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.lang.instrument.Instrumentation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.Socket; import java.util.Locale; /** * @author ven * @noinspection UseOfSystemOutOrSystemErr */ public class AppMainV2 { public static final String LAUNCHER_PORT_NUMBER = "idea.launcher.port"; public static final String LAUNCHER_BIN_PATH = "idea.launcher.bin.path"; private static final String JAVAFX_LAUNCHER = "com.sun.javafx.application.LauncherImpl"; private static final String LAUNCH_APPLICATION_METHOD_NAME = "launchApplication"; private static native void triggerControlBreak(); private static boolean loadHelper(String binPath) { String osName = System.getProperty("os.name").toLowerCase(Locale.US); if (osName.startsWith("windows")) { String arch = System.getProperty("os.arch").toLowerCase(Locale.US); File libFile = new File(binPath, arch.equals("amd64") ? "breakgen64.dll" : "breakgen.dll"); if (libFile.isFile()) { System.load(libFile.getAbsolutePath()); return true; } } return false; } private static void startMonitor(final int portNumber, final boolean helperLibLoaded) { Thread t = new Thread("Monitor Ctrl-Break") { public void run() { try { Socket client = new Socket("127.0.0.1", portNumber); try { BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "US-ASCII")); try { while (true) { String msg = reader.readLine(); if ("TERM".equals(msg)) { return; } else if ("BREAK".equals(msg)) { if (helperLibLoaded) { triggerControlBreak(); } } else if ("STOP".equals(msg)) { System.exit(1); } } } finally { reader.close(); } } finally { client.close(); } } catch (Exception ignored) { } } }; t.setDaemon(true); t.start(); } public static void main(String[] args) throws Throwable { try { boolean helperLibLoaded = loadHelper(System.getProperty(LAUNCHER_BIN_PATH)); int portNumber = Integer.parseInt(System.getProperty(LAUNCHER_PORT_NUMBER)); startMonitor(portNumber, helperLibLoaded); } catch (Throwable t) { System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')'); } String mainClass = args[0]; String[] params = new String[args.length - 1]; System.arraycopy(args, 1, params, 0, args.length - 1); Class appClass = Class.forName(mainClass); Method m; try { m = appClass.getMethod("main", new Class[]{params.getClass()}); } catch (NoSuchMethodException e) { if (!startJavaFXApplication(params, appClass)) { throw e; } return; } if (!Modifier.isStatic(m.getModifiers())) { System.err.println("main method should be static"); return; } if (!void.class.isAssignableFrom(m.getReturnType())) { System.err.println("main method must return a value of type void"); return; } try { m.setAccessible(true); m.invoke(null, new Object[]{params}); } catch (InvocationTargetException ite) { throw ite.getTargetException(); } } private static boolean startJavaFXApplication(String[] params, Class appClass) throws NoSuchMethodException { try { //check in launch method for application class in the stack trace leads to this hack here Class[] types = {appClass.getClass(), params.getClass()}; Method launchApplication = Class.forName(JAVAFX_LAUNCHER).getMethod(LAUNCH_APPLICATION_METHOD_NAME, types); launchApplication.invoke(null, new Object[] {appClass, params}); return true; } catch (Throwable e) { return false; } } public static class Agent { public static void premain(String args, Instrumentation i) { AppMainV2.premain(args); } } // todo[r.sh] inline some time after 2017.1.1 release public static void premain(String args) { try { int p = args.indexOf(':'); if (p < 0) throw new IllegalArgumentException("incorrect parameter: " + args); boolean helperLibLoaded = loadHelper(args.substring(p + 1)); int portNumber = Integer.parseInt(args.substring(0, p)); startMonitor(portNumber, helperLibLoaded); } catch (Throwable t) { System.err.println("Launcher failed - \"Dump Threads\" and \"Exit\" actions are unavailable (" + t.getMessage() + ')'); } } }