/* * Copyright 2017 ThoughtWorks, Inc. * * 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.thoughtworks.go.agent.bootstrapper; import com.thoughtworks.cruise.agent.common.launcher.AgentLauncher; import com.thoughtworks.go.agent.common.AgentBootstrapperArgs; import com.thoughtworks.go.agent.common.AgentCLI; import com.thoughtworks.go.agent.common.util.Downloader; import com.thoughtworks.go.agent.common.util.JarUtil; import com.thoughtworks.go.logging.LogConfigurator; import com.thoughtworks.go.util.SystemEnvironment; import com.thoughtworks.go.util.SystemUtil; import com.thoughtworks.go.util.validators.FileValidator; import com.thoughtworks.go.util.validators.Validation; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.NDC; import java.io.File; public class AgentBootstrapper { private static final int DEFAULT_WAIT_TIME_BEFORE_RELAUNCH_IN_MS = 10000; public static final String WAIT_TIME_BEFORE_RELAUNCH_IN_MS = "agent.bootstrapper.wait.time.before.relaunch.in.ms"; public static final String DEFAULT_LOG4J_CONFIGURATION_FILE = "agent-bootstrapper-log4j.properties"; int waitTimeBeforeRelaunch = SystemUtil.getIntProperty(WAIT_TIME_BEFORE_RELAUNCH_IN_MS, DEFAULT_WAIT_TIME_BEFORE_RELAUNCH_IN_MS); private static final Log LOG = LogFactory.getLog(AgentBootstrapper.class); private boolean loop; private AgentLauncherCreator launcherCreator; private Thread launcherThread; private Thread launcherCreatorShutdownHook; public AgentBootstrapper() { } public AgentBootstrapper(AgentLauncherCreator launcherCreator) { this.launcherCreator = launcherCreator; } public static void main(String[] argv) { AgentBootstrapperArgs args = new AgentCLI().parse(argv); LogConfigurator logConfigurator = new LogConfigurator(DEFAULT_LOG4J_CONFIGURATION_FILE); logConfigurator.initialize(); new AgentBootstrapper().go(true, args); } public void go(boolean shouldLoop, AgentBootstrapperArgs bootstrapperArgs) { loop = shouldLoop; launcherThread = Thread.currentThread(); validate(); cleanupTempFiles(); int returnValue = 0; DefaultAgentLaunchDescriptorImpl descriptor = new DefaultAgentLaunchDescriptorImpl(bootstrapperArgs, this); do { ClassLoader tccl = launcherThread.getContextClassLoader(); try { AgentLauncher launcher = getLauncher(); LOG.info("Attempting create and start launcher..."); setContextClassLoader(launcher.getClass().getClassLoader()); returnValue = launcher.launch(descriptor); resetContextClassLoader(tccl); LOG.info("Launcher returned with code " + returnValue + "(0x" + Integer.toHexString(returnValue).toUpperCase() + ")"); if (returnValue == AgentLauncher.IRRECOVERABLE_ERROR) { loop = false; } } catch (Exception e) { LOG.error("Error starting launcher", e); } finally { resetContextClassLoader(tccl); forceGCToPreventOOM(); destoryLauncherCreator(); } waitForRelaunchTime(); } while (loop); destoryLauncherCreator(); LOG.info("Agent Bootstrapper stopped"); jvmExit(returnValue); } private void cleanupTempFiles() { FileUtils.deleteQuietly(new File(JarUtil.EXPLODED_DEPENDENCIES_DIR_NAME)); FileUtils.deleteQuietly(new File(LauncherTempFileHandler.LAUNCHER_TMP_FILE_LIST)); FileUtils.deleteQuietly(new File(new SystemEnvironment().getConfigDir(), "trust.jks")); } void waitForRelaunchTime() { LOG.info(String.format("Waiting for %s ms before re-launch....", waitTimeBeforeRelaunch)); try { Thread.sleep(waitTimeBeforeRelaunch); } catch (InterruptedException e) { e.printStackTrace(); } } private void forceGCToPreventOOM() { System.gc(); } void jvmExit(int returnValue) { System.exit(returnValue); } private void destoryLauncherCreator() { LOG.info("Destroying launcher creator"); try { launcherCreator.destroy(); } catch (Exception e) { } launcherCreator = null; removeLauncherCreatorShutdownHook(); } private void removeLauncherCreatorShutdownHook() { try { Runtime.getRuntime().removeShutdownHook(launcherCreatorShutdownHook); } catch (Exception e) { } } private void setContextClassLoader(ClassLoader tccl) { Thread.currentThread().setContextClassLoader(tccl); } private void resetContextClassLoader(ClassLoader tccl) { Thread.currentThread().setContextClassLoader(tccl); } public void stopLooping() { loop = false; } void validate() { Validation validation = new Validation(); FileValidator.defaultFile(Downloader.AGENT_LAUNCHER, false).validate(validation); if (!validation.isSuccessful()) { validation.logErrors(LOG); throw new RuntimeException("Agent Bootstrapper initialization failed. Could not validate file: " + Downloader.AGENT_LAUNCHER); } } private AgentLauncher getLauncher() {//do not use across 2 invocations -jj try { initLauncherCreator(); return launcherCreator.createLauncher(); } catch (Exception e) { throw new RuntimeException("Failed to create an instance of agent launcher", e); } } private void initLauncherCreator() { launcherCreator = getLauncherCreator(); launcherCreatorShutdownHook = new Thread() { @Override public void run() { NDC.push("Agent-BootStrapper-ShutdownHook"); LOG.info("Interrupting Launcher and initiating shutdown..."); loop = false; launcherThread.interrupt(); destoryLauncherCreator(); NDC.pop(); } }; Runtime.getRuntime().addShutdownHook(launcherCreatorShutdownHook); } AgentLauncherCreator getLauncherCreator() { if (launcherCreator == null) { return new DefaultAgentLauncherCreatorImpl(); } return launcherCreator; } }