com.axway.ats.agentapp.standalone.ContainerStarter Maven / Gradle / Ivy
/*
* Copyright 2017 Axway Software
*
* 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.axway.ats.agentapp.standalone;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;
import com.axway.ats.agentapp.standalone.exceptions.AgentException;
import com.axway.ats.agentapp.standalone.log.appenders.SizeRollingFileAppender;
import com.axway.ats.agentapp.standalone.utils.AtsVersionExtractor;
import com.axway.ats.agentapp.standalone.utils.CleaningThread;
import com.axway.ats.agentapp.standalone.utils.ThreadUtils;
public class ContainerStarter {
private static final Logger log = Logger.getLogger(ContainerStarter.class);
private static final String DEFAULT_AGENT_PORT_KEY = "ats.agent.default.port"; // NOTE: on change sync with ATSSystemProperties
private static final int DEFAULT_AGENT_PORT_VALUE = 8089; // NOTE: on change sync with ATSSystemProperties
/**
* Entry point for the premain java agent starting the ATS Agent
* @param agentArgs
* @param inst
* @throws IOException
*/
public static void premain( String agentArgs, Instrumentation inst ) throws IOException {
Server server = startServer();
startCleanerThread(server);
}
/**
* Entry point for a standalone application starting the ATS Agent
* @param args
* @throws IOException
*/
public static void main( String[] args ) throws IOException {
startServer();
writePidFile();
}
/**
* Method for starting the Jetty server with the ATS Agent webapp.
* @param port the port on which to start the server.
* @return the started server.
* @throws IOException
*/
private static Server startServer() throws IOException {
addAppender();
final int agentPort = getAgentDefaultPort();
log.info("Starting ATS agent at port: " + agentPort);
final String jettyHome = getJettyHome();
logSystemInformation(jettyHome);
// start the server
Connector connector = new SelectChannelConnector();
connector.setPort(agentPort);
Server server = new Server();
server.setConnectors(new Connector[]{ connector });
WebAppContext webApp = new WebAppContext();
webApp.setContextPath("/agentapp");
webApp.setWar(jettyHome + "/webapp/agentapp.war");
server.setHandler(webApp);
server.setStopAtShutdown(true);
setExtraClasspath(webApp, jettyHome);
try {
server.start();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
log.info("ATS agent started");
return server;
}
/**
* Method for starting the thread that will wait for all non daemon threads
* to finish except the threads of the server and stop the server and JVM.
* This method is needed only when the server is started from a javaagent
* @param server
*/
private static void startCleanerThread( Server server ) {
// Wait for the server to start.
// Make more attempts in short interval in order to stop this check
// as soon as possible.
int maxAttempts = 100;
while (!server.isRunning()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
log.error("Interrupted while waiting for the agent to start.", e);
}
if (maxAttempts == 0) {
throw new AgentException("Jetty server not running.");
}
--maxAttempts;
}
CleaningThread cleanerThread = new CleaningThread(server, ThreadUtils.getInstance()
.getAllThreadIDsExceptMain());
cleanerThread.start();
}
/**
* @return the Jetty home directory
*/
private static String getJettyHome() {
// find the current class in the agent jar.
String jettyHome = ContainerStarter.class.getClassLoader()
.getResource(ContainerStarter.class.getCanonicalName()
.replaceAll("\\.", "/")
+ ".class")
.toString();
// get rid of the 'jar:' prefix and the path after the jar's name
jettyHome = jettyHome.substring(4, jettyHome.lastIndexOf('!'));
// get rid of the jar name at the end
jettyHome = jettyHome.substring(0, jettyHome.lastIndexOf('/'));
// Directly read system property in order not to add dependency to common library
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("win");
if (isWindows) {
jettyHome = jettyHome.substring("file:/".length());
} else {
jettyHome = jettyHome.substring("file:".length());
}
try {
jettyHome = URLDecoder.decode(jettyHome, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unable to decode Jetty home path", e);
}
return jettyHome;
}
private static void setExtraClasspath( WebAppContext webApp, String jettyHome ) {
final String lineSeparator = System.getProperty("line.separator");
String jarFilesReference = getJarFilesReference(jettyHome + "/actions_dependencies");
webApp.setExtraClasspath(jarFilesReference);
log.debug("Additional libraries inserted into Jetty's classpath: " + lineSeparator
+ jarFilesReference.replaceAll(",;", lineSeparator));
}
/**
* Browse a folder for jar files (recursively)
*
* @param folder the folder to search into
* @return a list with all found jars
*/
private static String getJarFilesReference( String folder ) {
StringBuffer jarsReference = new StringBuffer();
try {
File[] files = new File(folder).listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
jarsReference.append(getJarFilesReference(file.getAbsolutePath()));
} else if (file.getName().endsWith(".jar")) {
jarsReference.append(folder);
jarsReference.append("/");
jarsReference.append(file.getName());
jarsReference.append(",;");
}
}
}
} catch (Exception e) {
log.warn("Error searching for jar files into '" + folder + "' folder");
}
return jarsReference.toString();
}
/**
* Read agent port to use - either the specified system property or the hardcoded default one
* @return ATS agent port number to use
*/
private static int getAgentDefaultPort() {
Integer defaultPort = null;
String portValueAsStr = null;
try {
portValueAsStr = System.getProperty(DEFAULT_AGENT_PORT_KEY);
defaultPort = Integer.parseInt(portValueAsStr);
} catch (NumberFormatException iae) {
System.err.println("System property with name '" + DEFAULT_AGENT_PORT_KEY
+ "' has a non integer value '" + portValueAsStr + "'");
}
if (defaultPort == null) {
defaultPort = DEFAULT_AGENT_PORT_VALUE;
}
return defaultPort;
}
private static void writePidFile() {
String pid = getCurrentProcessId();
if (pid == null) {
log.warn("Uable to get the current process ID, which means that we can't stop the agent later");
return;
}
String jettyHome = getJettyHome();
String logsFolderPath = new File(jettyHome).getParent();
String pidFilePath = logsFolderPath + "/logs/atsAgent_" + getAgentDefaultPort() + ".pid";
File pidFile = new File(pidFilePath);
if (pidFile.exists()) {
log.warn("PID file '" + pidFile.getAbsolutePath()
+ "' already exists. We will overwrite it now!");
}
FileWriter writer = null;
try {
writer = new FileWriter(pidFile, false);
writer.write(pid);
} catch (Exception e) {
log.warn("Error writing pid file");
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
log.warn("Error closing pid file");
}
}
}
}
/**
* This may fail on some JMV implementations
*
* @return the PID of this java process
*/
private static String getCurrentProcessId() {
// something like '@', at least in SUN / Oracle JVMs
final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
final int index = jvmName.indexOf('@');
if (index < 1) {
// part before '@' empty
log.warn("Cannot extract the system process ID of this agent instance");
return null;
}
try {
return Long.toString(Long.parseLong(jvmName.substring(0, index)));
} catch (NumberFormatException e) {
log.warn("Cannot extract the system process ID of this agent instance");
return null;
}
}
private static void addAppender() throws IOException {
Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy