Commit 5d7c4150 authored by shengnan hu's avatar shengnan hu
Browse files

init

parents
Pipeline #4715 failed with stage
in 30 seconds
#!/bin/sh
# jps.sh version 1.0.2
# there might be multiple java processes, e.g. log-agent
JPS_CMDS=($(ps aux | grep java | grep -v 'grep java' | awk '{print $11}' | sed -n 's/java$/jps/p'))
# find the first executable jps command
JPS_CMD=""
for jps in ${JPS_CMDS[@]}; do
if [ -x $jps ]; then
JPS_CMD=$jps
break
fi
done
if [ "$JPS_CMD" == "" ]; then
echo "No Java Process Found on this Machine."
exit 1
else
result=`$JPS_CMD -lmv | grep -v jps`
if [ "$result" == "" ]; then
ps aux | grep -E '^admin.*java.*' | grep -v grep | awk 'BEGIN{ORS=""}{print $2" ";for(j=NF;j>=12;j--){if(match($j, /^\-[a-zA-Z0-9]/)) {break;} } for(i=j+1;i<=NF;i++) {print $i" "} for(i=12;i<=j;i++) {print $i" "} print "\n" }'
else
echo "$result"
fi
fi
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-boot</artifactId>
<name>arthas-boot</name>
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>cli</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>arthas-boot</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.taobao.arthas.boot.Bootstrap</mainClass>
</manifest>
<manifestEntries>
<Created-By>core engine team, middleware group, alibaba inc.</Created-By>
<Specification-Title>${project.name}</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Implementation-Title>${project.name}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package com.taobao.arthas.boot;
import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_ERROR;
import static com.taobao.arthas.boot.ProcessUtils.STATUS_EXEC_TIMEOUT;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.JavaVersionUtils;
import com.taobao.arthas.common.SocketUtils;
import com.taobao.arthas.common.UsageRender;
import com.taobao.middleware.cli.CLI;
import com.taobao.middleware.cli.CommandLine;
import com.taobao.middleware.cli.UsageMessageFormatter;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.CLIConfigurator;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
/**
* @author hengyunabc 2018-10-26
*
*/
@Name("arthas-boot")
@Summary("Bootstrap Arthas")
@Description("EXAMPLES:\n" + " java -jar arthas-boot.jar <pid>\n" + " java -jar arthas-boot.jar --target-ip 0.0.0.0\n"
+ " java -jar arthas-boot.jar --telnet-port 9999 --http-port -1\n"
+ " java -jar arthas-boot.jar --username admin --password <password>\n"
+ " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' --app-name demoapp\n"
+ " java -jar arthas-boot.jar --tunnel-server 'ws://192.168.10.11:7777/ws' --agent-id bvDOe8XbTM2pQWjF4cfw\n"
+ " java -jar arthas-boot.jar --stat-url 'http://192.168.10.11:8080/api/stat'\n"
+ " java -jar arthas-boot.jar -c 'sysprop; thread' <pid>\n"
+ " java -jar arthas-boot.jar -f batch.as <pid>\n"
+ " java -jar arthas-boot.jar --use-version 3.6.4\n"
+ " java -jar arthas-boot.jar --versions\n"
+ " java -jar arthas-boot.jar --select math-game\n"
+ " java -jar arthas-boot.jar --session-timeout 3600\n" + " java -jar arthas-boot.jar --attach-only\n"
+ " java -jar arthas-boot.jar --disabled-commands stop,dump\n"
+ " java -jar arthas-boot.jar --repo-mirror aliyun --use-http\n" + "WIKI:\n"
+ " https://arthas.aliyun.com/doc\n")
public class Bootstrap {
private static final int DEFAULT_TELNET_PORT = 3658;
private static final int DEFAULT_HTTP_PORT = 8563;
private static final String DEFAULT_TARGET_IP = "127.0.0.1";
private static File ARTHAS_LIB_DIR;
private boolean help = false;
private long pid = -1;
private String targetIp;
private Integer telnetPort;
private Integer httpPort;
/**
* @see com.taobao.arthas.core.config.Configure#DEFAULT_SESSION_TIMEOUT_SECONDS
*/
private Long sessionTimeout;
private Integer height = null;
private Integer width = null;
private boolean verbose = false;
/**
* <pre>
* The directory contains arthas-core.jar/arthas-client.jar/arthas-spy.jar.
* 1. When use-version is not empty, try to find arthas home under ~/.arthas/lib
* 2. Try set the directory where arthas-boot.jar is located to arthas home
* 3. Try to download from remote repo
* </pre>
*/
private String arthasHome;
/**
* under ~/.arthas/lib
*/
private String useVersion;
/**
* list local and remote versions
*/
private boolean versions;
/**
* download from remo repository. if timezone is +0800, default value is 'aliyun', else is 'center'.
*/
private String repoMirror;
/**
* enforce use http to download arthas. default use https
*/
private boolean useHttp = false;
private boolean attachOnly = false;
private String command;
private String batchFile;
private String tunnelServer;
private String agentId;
private String appName;
private String username;
private String password;
private String statUrl;
private String select;
private String disabledCommands;
static {
String arthasLibDirEnv = System.getenv("ARTHAS_LIB_DIR");
if (arthasLibDirEnv != null) {
ARTHAS_LIB_DIR = new File(arthasLibDirEnv);
} else {
ARTHAS_LIB_DIR = new File(
System.getProperty("user.home") + File.separator + ".arthas" + File.separator + "lib");
}
try {
ARTHAS_LIB_DIR.mkdirs();
} catch (Throwable t) {
//ignore
}
if (!ARTHAS_LIB_DIR.exists()) {
// try to set a temp directory
ARTHAS_LIB_DIR = new File(System.getProperty("java.io.tmpdir") + File.separator + ".arthas" + File.separator + "lib");
try {
ARTHAS_LIB_DIR.mkdirs();
} catch (Throwable e) {
// ignore
}
}
if (!ARTHAS_LIB_DIR.exists()) {
System.err.println("Can not find directory to save arthas lib. please try to set user home by -Duser.home=");
}
}
@Argument(argName = "pid", index = 0, required = false)
@Description("Target pid")
public void setPid(long pid) {
this.pid = pid;
}
@Option(shortName = "h", longName = "help", flag = true)
@Description("Print usage")
public void setHelp(boolean help) {
this.help = help;
}
@Option(longName = "target-ip")
@Description("The target jvm listen ip, default 127.0.0.1")
public void setTargetIp(String targetIp) {
this.targetIp = targetIp;
}
@Option(longName = "telnet-port")
@Description("The target jvm listen telnet port, default 3658")
public void setTelnetPort(int telnetPort) {
this.telnetPort = telnetPort;
}
@Option(longName = "http-port")
@Description("The target jvm listen http port, default 8563")
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
@Option(longName = "session-timeout")
@Description("The session timeout seconds, default 1800 (30min)")
public void setSessionTimeout(Long sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
@Option(longName = "arthas-home")
@Description("The arthas home")
public void setArthasHome(String arthasHome) {
this.arthasHome = arthasHome;
}
@Option(longName = "use-version")
@Description("Use special version arthas")
public void setUseVersion(String useVersion) {
this.useVersion = useVersion;
}
@Option(longName = "repo-mirror")
@Description("Use special remote repository mirror, value is center/aliyun or http repo url.")
public void setRepoMirror(String repoMirror) {
this.repoMirror = repoMirror;
}
@Option(longName = "versions", flag = true)
@Description("List local and remote arthas versions")
public void setVersions(boolean versions) {
this.versions = versions;
}
@Option(longName = "use-http", flag = true)
@Description("Enforce use http to download, default use https")
public void setuseHttp(boolean useHttp) {
this.useHttp = useHttp;
}
@Option(longName = "attach-only", flag = true)
@Description("Attach target process only, do not connect")
public void setAttachOnly(boolean attachOnly) {
this.attachOnly = attachOnly;
}
@Option(shortName = "c", longName = "command")
@Description("Command to execute, multiple commands separated by ;")
public void setCommand(String command) {
this.command = command;
}
@Option(shortName = "f", longName = "batch-file")
@Description("The batch file to execute")
public void setBatchFile(String batchFile) {
this.batchFile = batchFile;
}
@Option(longName = "height")
@Description("arthas-client terminal height")
public void setHeight(int height) {
this.height = height;
}
@Option(longName = "width")
@Description("arthas-client terminal width")
public void setWidth(int width) {
this.width = width;
}
@Option(shortName = "v", longName = "verbose", flag = true)
@Description("Verbose, print debug info.")
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
@Option(longName = "tunnel-server")
@Description("The tunnel server url")
public void setTunnelServer(String tunnelServer) {
this.tunnelServer = tunnelServer;
}
@Option(longName = "agent-id")
@Description("The agent id register to tunnel server")
public void setAgentId(String agentId) {
this.agentId = agentId;
}
@Option(longName = "app-name")
@Description("The app name")
public void setAppName(String appName) {
this.appName = appName;
}
@Option(longName = "username")
@Description("The username")
public void setUsername(String username) {
this.username = username;
}
@Option(longName = "password")
@Description("The password")
public void setPassword(String password) {
this.password = password;
}
@Option(longName = "stat-url")
@Description("The report stat url")
public void setStatUrl(String statUrl) {
this.statUrl = statUrl;
}
@Option(longName = "select")
@Description("select target process by classname or JARfilename")
public void setSelect(String select) {
this.select = select;
}
@Option(longName = "disabled-commands")
@Description("disable some commands ")
public void setDisabledCommands(String disabledCommands) {
this.disabledCommands = disabledCommands;
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException,
ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Package bootstrapPackage = Bootstrap.class.getPackage();
if (bootstrapPackage != null) {
String arthasBootVersion = bootstrapPackage.getImplementationVersion();
if (arthasBootVersion != null) {
AnsiLog.info("arthas-boot version: " + arthasBootVersion);
}
}
Bootstrap bootstrap = new Bootstrap();
CLI cli = CLIConfigurator.define(Bootstrap.class);
CommandLine commandLine = cli.parse(Arrays.asList(args));
try {
CLIConfigurator.inject(commandLine, bootstrap);
} catch (Throwable e) {
e.printStackTrace();
System.out.println(usage(cli));
System.exit(1);
}
if (bootstrap.isVerbose()) {
AnsiLog.level(Level.ALL);
}
if (bootstrap.isHelp()) {
System.out.println(usage(cli));
System.exit(0);
}
if (bootstrap.getRepoMirror() == null || bootstrap.getRepoMirror().trim().isEmpty()) {
bootstrap.setRepoMirror("center");
// if timezone is +0800, default repo mirror is aliyun
if (TimeUnit.MILLISECONDS.toHours(TimeZone.getDefault().getOffset(System.currentTimeMillis())) == 8) {
bootstrap.setRepoMirror("aliyun");
}
}
AnsiLog.debug("Repo mirror:" + bootstrap.getRepoMirror());
if (bootstrap.isVersions()) {
System.out.println(UsageRender.render(listVersions()));
System.exit(0);
}
if (JavaVersionUtils.isJava6() || JavaVersionUtils.isJava7()) {
bootstrap.setuseHttp(true);
AnsiLog.debug("Java version is {}, only support http, set useHttp to true.",
JavaVersionUtils.javaVersionStr());
}
// check telnet/http port
long telnetPortPid = -1;
long httpPortPid = -1;
if (bootstrap.getTelnetPortOrDefault() > 0) {
telnetPortPid = SocketUtils.findTcpListenProcess(bootstrap.getTelnetPortOrDefault());
if (telnetPortPid > 0) {
AnsiLog.info("Process {} already using port {}", telnetPortPid, bootstrap.getTelnetPortOrDefault());
}
}
if (bootstrap.getHttpPortOrDefault() > 0) {
httpPortPid = SocketUtils.findTcpListenProcess(bootstrap.getHttpPortOrDefault());
if (httpPortPid > 0) {
AnsiLog.info("Process {} already using port {}", httpPortPid, bootstrap.getHttpPortOrDefault());
}
}
long pid = bootstrap.getPid();
// select pid
if (pid < 0) {
try {
pid = ProcessUtils.select(bootstrap.isVerbose(), telnetPortPid, bootstrap.getSelect());
} catch (InputMismatchException e) {
System.out.println("Please input an integer to select pid.");
System.exit(1);
}
if (pid < 0) {
System.out.println("Please select an available pid.");
System.exit(1);
}
}
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
if (httpPortPid > 0 && pid != httpPortPid) {
AnsiLog.error("Target process {} is not the process using port {}, you will connect to an unexpected process.",
pid, bootstrap.getHttpPortOrDefault());
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
httpPortPid);
AnsiLog.error("2. Or try to use different http port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port 9999", httpPortPid);
System.exit(1);
}
// find arthas home
File arthasHomeDir = null;
if (bootstrap.getArthasHome() != null) {
verifyArthasHome(bootstrap.getArthasHome());
arthasHomeDir = new File(bootstrap.getArthasHome());
}
if (arthasHomeDir == null && bootstrap.getUseVersion() != null) {
// try to find from ~/.arthas/lib
File specialVersionDir = new File(System.getProperty("user.home"), ".arthas" + File.separator + "lib"
+ File.separator + bootstrap.getUseVersion() + File.separator + "arthas");
if (!specialVersionDir.exists()) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootstrap.getRepoMirror(), bootstrap.isuseHttp(),
bootstrap.getUseVersion(), ARTHAS_LIB_DIR.getAbsolutePath());
}
verifyArthasHome(specialVersionDir.getAbsolutePath());
arthasHomeDir = specialVersionDir;
}
// Try set the directory where arthas-boot.jar is located to arhtas home
if (arthasHomeDir == null) {
CodeSource codeSource = Bootstrap.class.getProtectionDomain().getCodeSource();
if (codeSource != null) {
try {
// https://stackoverflow.com/a/17870390
File bootJarPath = new File(codeSource.getLocation().toURI().getSchemeSpecificPart());
verifyArthasHome(bootJarPath.getParent());
arthasHomeDir = bootJarPath.getParentFile();
} catch (Throwable e) {
// ignore
}
}
}
// try to download from remote server
if (arthasHomeDir == null) {
boolean checkFile = ARTHAS_LIB_DIR.exists() || ARTHAS_LIB_DIR.mkdirs();
if(!checkFile){
AnsiLog.error("cannot create directory {}: maybe permission denied", ARTHAS_LIB_DIR.getAbsolutePath());
System.exit(1);
}
/**
* <pre>
* 1. get local latest version
* 2. get remote latest version
* 3. compare two version
* </pre>
*/
List<String> versionList = listNames(ARTHAS_LIB_DIR);
Collections.sort(versionList);
String localLastestVersion = null;
if (!versionList.isEmpty()) {
localLastestVersion = versionList.get(versionList.size() - 1);
}
String remoteLastestVersion = DownloadUtils.readLatestReleaseVersion();
boolean needDownload = false;
if (localLastestVersion == null) {
if (remoteLastestVersion == null) {
// exit
AnsiLog.error("Can not find Arthas under local: {} and remote repo mirror: {}", ARTHAS_LIB_DIR,
bootstrap.getRepoMirror());
AnsiLog.error(
"Unable to download arthas from remote server, please download the full package according to wiki: https://github.com/alibaba/arthas");
System.exit(1);
} else {
needDownload = true;
}
} else {
if (remoteLastestVersion != null) {
if (localLastestVersion.compareTo(remoteLastestVersion) < 0) {
AnsiLog.info("local lastest version: {}, remote lastest version: {}, try to download from remote.",
localLastestVersion, remoteLastestVersion);
needDownload = true;
}
}
}
if (needDownload) {
// try to download arthas from remote server.
DownloadUtils.downArthasPackaging(bootstrap.getRepoMirror(), bootstrap.isuseHttp(),
remoteLastestVersion, ARTHAS_LIB_DIR.getAbsolutePath());
localLastestVersion = remoteLastestVersion;
}
// get the latest version
arthasHomeDir = new File(ARTHAS_LIB_DIR, localLastestVersion + File.separator + "arthas");
}
verifyArthasHome(arthasHomeDir.getAbsolutePath());
AnsiLog.info("arthas home: " + arthasHomeDir);
if (telnetPortPid > 0 && pid == telnetPortPid) {
AnsiLog.info("The target process already listen port {}, skip attach.", bootstrap.getTelnetPortOrDefault());
} else {
//double check telnet port and pid before attach
telnetPortPid = findProcessByTelnetClient(arthasHomeDir.getAbsolutePath(), bootstrap.getTelnetPortOrDefault());
checkTelnetPortPid(bootstrap, telnetPortPid, pid);
// start arthas-core.jar
List<String> attachArgs = new ArrayList<String>();
attachArgs.add("-jar");
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
attachArgs.add("-pid");
attachArgs.add("" + pid);
if (bootstrap.getTargetIp() != null) {
attachArgs.add("-target-ip");
attachArgs.add(bootstrap.getTargetIp());
}
if (bootstrap.getTelnetPort() != null) {
attachArgs.add("-telnet-port");
attachArgs.add("" + bootstrap.getTelnetPort());
}
if (bootstrap.getHttpPort() != null) {
attachArgs.add("-http-port");
attachArgs.add("" + bootstrap.getHttpPort());
}
attachArgs.add("-core");
attachArgs.add(new File(arthasHomeDir, "arthas-core.jar").getAbsolutePath());
attachArgs.add("-agent");
attachArgs.add(new File(arthasHomeDir, "arthas-agent.jar").getAbsolutePath());
if (bootstrap.getSessionTimeout() != null) {
attachArgs.add("-session-timeout");
attachArgs.add("" + bootstrap.getSessionTimeout());
}
if (bootstrap.getAppName() != null) {
attachArgs.add("-app-name");
attachArgs.add(bootstrap.getAppName());
}
if (bootstrap.getUsername() != null) {
attachArgs.add("-username");
attachArgs.add(bootstrap.getUsername());
}
if (bootstrap.getPassword() != null) {
attachArgs.add("-password");
attachArgs.add(bootstrap.getPassword());
}
if (bootstrap.getTunnelServer() != null) {
attachArgs.add("-tunnel-server");
attachArgs.add(bootstrap.getTunnelServer());
}
if (bootstrap.getAgentId() != null) {
attachArgs.add("-agent-id");
attachArgs.add(bootstrap.getAgentId());
}
if (bootstrap.getStatUrl() != null) {
attachArgs.add("-stat-url");
attachArgs.add(bootstrap.getStatUrl());
}
if (bootstrap.getDisabledCommands() != null){
attachArgs.add("-disabled-commands");
attachArgs.add(bootstrap.getDisabledCommands());
}
AnsiLog.info("Try to attach process " + pid);
AnsiLog.debug("Start arthas-core.jar args: " + attachArgs);
ProcessUtils.startArthasCore(pid, attachArgs);
AnsiLog.info("Attach process {} success.", pid);
}
if (bootstrap.isAttachOnly()) {
System.exit(0);
}
// start java telnet client
// find arthas-client.jar
URLClassLoader classLoader = new URLClassLoader(
new URL[] { new File(arthasHomeDir, "arthas-client.jar").toURI().toURL() });
Class<?> telnetConsoleClas = classLoader.loadClass("com.taobao.arthas.client.TelnetConsole");
Method mainMethod = telnetConsoleClas.getMethod("main", String[].class);
List<String> telnetArgs = new ArrayList<String>();
if (bootstrap.getCommand() != null) {
telnetArgs.add("-c");
telnetArgs.add(bootstrap.getCommand());
}
if (bootstrap.getBatchFile() != null) {
telnetArgs.add("-f");
telnetArgs.add(bootstrap.getBatchFile());
}
if (bootstrap.getHeight() != null) {
telnetArgs.add("--height");
telnetArgs.add("" + bootstrap.getHeight());
}
if (bootstrap.getWidth() != null) {
telnetArgs.add("--width");
telnetArgs.add("" + bootstrap.getWidth());
}
// telnet port ,ip
telnetArgs.add(bootstrap.getTargetIpOrDefault());
telnetArgs.add("" + bootstrap.getTelnetPortOrDefault());
AnsiLog.info("arthas-client connect {} {}", bootstrap.getTargetIpOrDefault(), bootstrap.getTelnetPortOrDefault());
AnsiLog.debug("Start arthas-client.jar args: " + telnetArgs);
// fix https://github.com/alibaba/arthas/issues/833
Thread.currentThread().setContextClassLoader(classLoader);
mainMethod.invoke(null, new Object[] { telnetArgs.toArray(new String[0]) });
}
private static void checkTelnetPortPid(Bootstrap bootstrap, long telnetPortPid, long targetPid) {
if (telnetPortPid > 0 && targetPid != telnetPortPid) {
AnsiLog.error("The telnet port {} is used by process {} instead of target process {}, you will connect to an unexpected process.",
bootstrap.getTelnetPortOrDefault(), telnetPortPid, targetPid);
AnsiLog.error("1. Try to restart arthas-boot, select process {}, shutdown it first with running the 'stop' command.",
telnetPortPid);
AnsiLog.error("2. Or try to stop the existing arthas instance: java -jar arthas-client.jar 127.0.0.1 {} -c \"stop\"", bootstrap.getTelnetPortOrDefault());
AnsiLog.error("3. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1");
System.exit(1);
}
}
private static long findProcessByTelnetClient(String arthasHomeDir, int telnetPort) {
// start java telnet client
List<String> telnetArgs = new ArrayList<String>();
telnetArgs.add("-c");
telnetArgs.add("session");
telnetArgs.add("--execution-timeout");
telnetArgs.add("2000");
// telnet port ,ip
telnetArgs.add("127.0.0.1");
telnetArgs.add("" + telnetPort);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
String error = null;
int status = ProcessUtils.startArthasClient(arthasHomeDir, telnetArgs, out);
if (status == STATUS_EXEC_TIMEOUT) {
error = "detection timeout";
} else if (status == STATUS_EXEC_ERROR) {
error = "detection error";
AnsiLog.error("process status: {}", status);
AnsiLog.error("process output: {}", out.toString());
} else {
// ignore connect error
}
if (error != null) {
AnsiLog.error("The telnet port {} is used, but process {}, you will connect to an unexpected process.", telnetPort, error);
AnsiLog.error("Try to use a different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1");
System.exit(1);
}
//parse output, find java pid
String output = out.toString("UTF-8");
String javaPidLine = null;
Scanner scanner = new Scanner(output);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.contains("JAVA_PID")) {
javaPidLine = line;
break;
}
}
if (javaPidLine != null) {
// JAVA_PID 10473
try {
String[] strs = javaPidLine.split("JAVA_PID");
if (strs.length > 1) {
return Long.parseLong(strs[strs.length - 1].trim());
}
} catch (NumberFormatException e) {
// ignore
}
}
} catch (Throwable ex) {
AnsiLog.error("Detection telnet port error");
AnsiLog.error(ex);
}
return -1;
}
private static String listVersions() {
StringBuilder result = new StringBuilder(1024);
List<String> versionList = listNames(ARTHAS_LIB_DIR);
Collections.sort(versionList);
result.append("Local versions:\n");
for (String version : versionList) {
result.append(" ").append(version).append('\n');
}
result.append("Remote versions:\n");
List<String> remoteVersions = DownloadUtils.readRemoteVersions();
if (remoteVersions != null) {
Collections.reverse(remoteVersions);
for (String version : remoteVersions) {
result.append(" " + version).append('\n');
}
} else {
result.append(" unknown\n");
}
return result.toString();
}
private static List<String> listNames(File dir) {
List<String> names = new ArrayList<String>();
if (!dir.exists()) {
return names;
}
File[] files = dir.listFiles();
if (files == null) {
return names;
}
for (File file : files) {
String name = file.getName();
if (name.startsWith(".") || file.isFile()) {
continue;
}
names.add(name);
}
return names;
}
private static void verifyArthasHome(String arthasHome) {
File home = new File(arthasHome);
if (home.isDirectory()) {
String[] fileList = { "arthas-core.jar", "arthas-agent.jar", "arthas-spy.jar" };
for (String fileName : fileList) {
if (!new File(home, fileName).exists()) {
throw new IllegalArgumentException(
fileName + " do not exist, arthas home: " + home.getAbsolutePath());
}
}
return;
}
throw new IllegalArgumentException("illegal arthas home: " + home.getAbsolutePath());
}
private static String usage(CLI cli) {
StringBuilder usageStringBuilder = new StringBuilder();
UsageMessageFormatter usageMessageFormatter = new UsageMessageFormatter();
usageMessageFormatter.setOptionComparator(null);
cli.usage(usageStringBuilder, usageMessageFormatter);
return UsageRender.render(usageStringBuilder.toString());
}
public String getArthasHome() {
return arthasHome;
}
public String getUseVersion() {
return useVersion;
}
public String getRepoMirror() {
return repoMirror;
}
public boolean isuseHttp() {
return useHttp;
}
public String getTargetIp() {
return targetIp;
}
public String getTargetIpOrDefault() {
if (this.targetIp == null) {
return DEFAULT_TARGET_IP;
} else {
return this.targetIp;
}
}
public Integer getTelnetPort() {
return telnetPort;
}
public int getTelnetPortOrDefault() {
if (this.telnetPort == null) {
return DEFAULT_TELNET_PORT;
} else {
return this.telnetPort;
}
}
public Integer getHttpPort() {
return httpPort;
}
public int getHttpPortOrDefault() {
if (this.httpPort == null) {
return DEFAULT_HTTP_PORT;
} else {
return this.httpPort;
}
}
public String getCommand() {
return command;
}
public String getBatchFile() {
return batchFile;
}
public boolean isAttachOnly() {
return attachOnly;
}
public long getPid() {
return pid;
}
public boolean isHelp() {
return help;
}
public Long getSessionTimeout() {
return sessionTimeout;
}
public boolean isVerbose() {
return verbose;
}
public boolean isVersions() {
return versions;
}
public Integer getHeight() {
return height;
}
public Integer getWidth() {
return width;
}
public String getTunnelServer() {
return tunnelServer;
}
public String getAgentId() {
return agentId;
}
public String getAppName() {
return appName;
}
public String getStatUrl() {
return statUrl;
}
public String getSelect() {
return select;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getDisabledCommands() {
return disabledCommands;
}
}
package com.taobao.arthas.boot;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.IOUtils;
/**
*
* @author hengyunabc 2018-11-06
*
*/
public class DownloadUtils {
private static final String ARTHAS_VERSIONS_URL = "https://arthas.aliyun.com/api/versions";
private static final String ARTHAS_LATEST_VERSIONS_URL = "https://arthas.aliyun.com/api/latest_version";
private static final String ARTHAS_DOWNLOAD_URL = "https://arthas.aliyun.com/download/${VERSION}?mirror=${REPO}";
private static final int CONNECTION_TIMEOUT = 3000;
public static String readLatestReleaseVersion() {
InputStream inputStream = null;
try {
URLConnection connection = openURLConnection(ARTHAS_LATEST_VERSIONS_URL);
inputStream = connection.getInputStream();
return IOUtils.toString(inputStream).trim();
} catch (Throwable t) {
AnsiLog.error("Can not read arthas version from: " + ARTHAS_LATEST_VERSIONS_URL);
AnsiLog.debug(t);
} finally {
IOUtils.close(inputStream);
}
return null;
}
public static List<String> readRemoteVersions() {
InputStream inputStream = null;
try {
URLConnection connection = openURLConnection(ARTHAS_VERSIONS_URL);
inputStream = connection.getInputStream();
String versionsStr = IOUtils.toString(inputStream);
String[] versions = versionsStr.split("\r\n");
ArrayList<String> result = new ArrayList<String>();
for (String version : versions) {
result.add(version.trim());
}
return result;
} catch (Throwable t) {
AnsiLog.error("Can not read arthas versions from: " + ARTHAS_VERSIONS_URL);
AnsiLog.debug(t);
} finally {
IOUtils.close(inputStream);
}
return null;
}
private static String getRepoUrl(String repoUrl, boolean http) {
if (repoUrl.endsWith("/")) {
repoUrl = repoUrl.substring(0, repoUrl.length() - 1);
}
if (http && repoUrl.startsWith("https")) {
repoUrl = "http" + repoUrl.substring("https".length());
}
return repoUrl;
}
public static void downArthasPackaging(String repoMirror, boolean http, String arthasVersion, String savePath)
throws IOException {
String repoUrl = getRepoUrl(ARTHAS_DOWNLOAD_URL, http);
File unzipDir = new File(savePath, arthasVersion + File.separator + "arthas");
File tempFile = File.createTempFile("arthas", "arthas");
AnsiLog.debug("Arthas download temp file: " + tempFile.getAbsolutePath());
String remoteDownloadUrl = repoUrl.replace("${REPO}", repoMirror).replace("${VERSION}", arthasVersion);
AnsiLog.info("Start download arthas from remote server: " + remoteDownloadUrl);
saveUrl(tempFile.getAbsolutePath(), remoteDownloadUrl, true);
AnsiLog.info("Download arthas success.");
IOUtils.unzip(tempFile.getAbsolutePath(), unzipDir.getAbsolutePath());
}
private static void saveUrl(final String filename, final String urlString, boolean printProgress)
throws IOException {
BufferedInputStream in = null;
FileOutputStream fout = null;
try {
URLConnection connection = openURLConnection(urlString);
in = new BufferedInputStream(connection.getInputStream());
List<String> values = connection.getHeaderFields().get("Content-Length");
int fileSize = 0;
if (values != null && !values.isEmpty()) {
String contentLength = values.get(0);
if (contentLength != null) {
// parse the length into an integer...
fileSize = Integer.parseInt(contentLength);
}
}
fout = new FileOutputStream(filename);
final byte[] data = new byte[1024 * 1024];
int totalCount = 0;
int count;
long lastPrintTime = System.currentTimeMillis();
while ((count = in.read(data, 0, 1024 * 1024)) != -1) {
totalCount += count;
if (printProgress) {
long now = System.currentTimeMillis();
if (now - lastPrintTime > 1000) {
AnsiLog.info("File size: {}, downloaded size: {}, downloading ...", formatFileSize(fileSize),
formatFileSize(totalCount));
lastPrintTime = now;
}
}
fout.write(data, 0, count);
}
} catch (javax.net.ssl.SSLException e) {
AnsiLog.error("TLS connect error, please try to add --use-http argument.");
AnsiLog.error("URL: " + urlString);
AnsiLog.error(e);
} finally {
IOUtils.close(in);
IOUtils.close(fout);
}
}
/**
* support redirect
*
* @param url
* @return
* @throws MalformedURLException
* @throws IOException
*/
private static URLConnection openURLConnection(String url) throws MalformedURLException, IOException {
URLConnection connection = new URL(url).openConnection();
if (connection instanceof HttpURLConnection) {
connection.setConnectTimeout(CONNECTION_TIMEOUT);
// normally, 3xx is redirect
int status = ((HttpURLConnection) connection).getResponseCode();
if (status != HttpURLConnection.HTTP_OK) {
if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER) {
String newUrl = connection.getHeaderField("Location");
AnsiLog.debug("Try to open url: {}, redirect to: {}", url, newUrl);
return openURLConnection(newUrl);
}
}
}
return connection;
}
private static String formatFileSize(long size) {
String hrSize;
double b = size;
double k = size / 1024.0;
double m = ((size / 1024.0) / 1024.0);
double g = (((size / 1024.0) / 1024.0) / 1024.0);
double t = ((((size / 1024.0) / 1024.0) / 1024.0) / 1024.0);
DecimalFormat dec = new DecimalFormat("0.00");
if (t > 1) {
hrSize = dec.format(t).concat(" TB");
} else if (g > 1) {
hrSize = dec.format(g).concat(" GB");
} else if (m > 1) {
hrSize = dec.format(m).concat(" MB");
} else if (k > 1) {
hrSize = dec.format(k).concat(" KB");
} else {
hrSize = dec.format(b).concat(" Bytes");
}
return hrSize;
}
}
package com.taobao.arthas.boot;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.InputMismatchException;
import com.taobao.arthas.common.AnsiLog;
import com.taobao.arthas.common.ExecutingCommand;
import com.taobao.arthas.common.IOUtils;
import com.taobao.arthas.common.JavaVersionUtils;
import com.taobao.arthas.common.PidUtils;
/**
*
* @author hengyunabc 2018-11-06
*
*/
public class ProcessUtils {
private static String FOUND_JAVA_HOME = null;
//status code from com.taobao.arthas.client.TelnetConsole
/**
* Process success
*/
public static final int STATUS_OK = 0;
/**
* Generic error
*/
public static final int STATUS_ERROR = 1;
/**
* Execute commands timeout
*/
public static final int STATUS_EXEC_TIMEOUT = 100;
/**
* Execute commands error
*/
public static final int STATUS_EXEC_ERROR = 101;
@SuppressWarnings("resource")
public static long select(boolean v, long telnetPortPid, String select) throws InputMismatchException {
Map<Long, String> processMap = listProcessByJps(v);
// Put the port that is already listening at the first
if (telnetPortPid > 0 && processMap.containsKey(telnetPortPid)) {
String telnetPortProcess = processMap.get(telnetPortPid);
processMap.remove(telnetPortPid);
Map<Long, String> newProcessMap = new LinkedHashMap<Long, String>();
newProcessMap.put(telnetPortPid, telnetPortProcess);
newProcessMap.putAll(processMap);
processMap = newProcessMap;
}
if (processMap.isEmpty()) {
AnsiLog.info("Can not find java process. Try to run `jps` command lists the instrumented Java HotSpot VMs on the target system.");
return -1;
}
// select target process by the '--select' option when match only one process
if (select != null && !select.trim().isEmpty()) {
int matchedSelectCount = 0;
Long matchedPid = null;
for (Entry<Long, String> entry : processMap.entrySet()) {
if (entry.getValue().contains(select)) {
matchedSelectCount++;
matchedPid = entry.getKey();
}
}
if (matchedSelectCount == 1) {
return matchedPid;
}
}
AnsiLog.info("Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.");
// print list
int count = 1;
for (String process : processMap.values()) {
if (count == 1) {
System.out.println("* [" + count + "]: " + process);
} else {
System.out.println(" [" + count + "]: " + process);
}
count++;
}
// read choice
String line = new Scanner(System.in).nextLine();
if (line.trim().isEmpty()) {
// get the first process id
return processMap.keySet().iterator().next();
}
int choice = new Scanner(line).nextInt();
if (choice <= 0 || choice > processMap.size()) {
return -1;
}
Iterator<Long> idIter = processMap.keySet().iterator();
for (int i = 1; i <= choice; ++i) {
if (i == choice) {
return idIter.next();
}
idIter.next();
}
return -1;
}
private static Map<Long, String> listProcessByJps(boolean v) {
Map<Long, String> result = new LinkedHashMap<Long, String>();
String jps = "jps";
File jpsFile = findJps();
if (jpsFile != null) {
jps = jpsFile.getAbsolutePath();
}
AnsiLog.debug("Try use jps to lis java process, jps: " + jps);
String[] command = null;
if (v) {
command = new String[] { jps, "-v", "-l" };
} else {
command = new String[] { jps, "-l" };
}
List<String> lines = ExecutingCommand.runNative(command);
AnsiLog.debug("jps result: " + lines);
long currentPid = Long.parseLong(PidUtils.currentPid());
for (String line : lines) {
String[] strings = line.trim().split("\\s+");
if (strings.length < 1) {
continue;
}
try {
long pid = Long.parseLong(strings[0]);
if (pid == currentPid) {
continue;
}
if (strings.length >= 2 && isJpsProcess(strings[1])) { // skip jps
continue;
}
result.put(pid, line);
} catch (Throwable e) {
// https://github.com/alibaba/arthas/issues/970
// ignore
}
}
return result;
}
/**
* <pre>
* 1. Try to find java home from System Property java.home
* 2. If jdk > 8, FOUND_JAVA_HOME set to java.home
* 3. If jdk <= 8, try to find tools.jar under java.home
* 4. If tools.jar do not exists under java.home, try to find System env JAVA_HOME
* 5. If jdk <= 8 and tools.jar do not exists under JAVA_HOME, throw IllegalArgumentException
* </pre>
*
* @return
*/
public static String findJavaHome() {
if (FOUND_JAVA_HOME != null) {
return FOUND_JAVA_HOME;
}
String javaHome = System.getProperty("java.home");
if (JavaVersionUtils.isLessThanJava9()) {
File toolsJar = new File(javaHome, "lib/tools.jar");
if (!toolsJar.exists()) {
toolsJar = new File(javaHome, "../lib/tools.jar");
}
if (!toolsJar.exists()) {
// maybe jre
toolsJar = new File(javaHome, "../../lib/tools.jar");
}
if (toolsJar.exists()) {
FOUND_JAVA_HOME = javaHome;
return FOUND_JAVA_HOME;
}
if (!toolsJar.exists()) {
AnsiLog.debug("Can not find tools.jar under java.home: " + javaHome);
String javaHomeEnv = System.getenv("JAVA_HOME");
if (javaHomeEnv != null && !javaHomeEnv.isEmpty()) {
AnsiLog.debug("Try to find tools.jar in System Env JAVA_HOME: " + javaHomeEnv);
// $JAVA_HOME/lib/tools.jar
toolsJar = new File(javaHomeEnv, "lib/tools.jar");
if (!toolsJar.exists()) {
// maybe jre
toolsJar = new File(javaHomeEnv, "../lib/tools.jar");
}
}
if (toolsJar.exists()) {
AnsiLog.info("Found java home from System Env JAVA_HOME: " + javaHomeEnv);
FOUND_JAVA_HOME = javaHomeEnv;
return FOUND_JAVA_HOME;
}
throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome
+ ", please try to start arthas-boot with full path java. Such as /opt/jdk/bin/java -jar arthas-boot.jar");
}
} else {
FOUND_JAVA_HOME = javaHome;
}
return FOUND_JAVA_HOME;
}
public static void startArthasCore(long targetPid, List<String> attachArgs) {
// find java/java.exe, then try to find tools.jar
String javaHome = findJavaHome();
// find java/java.exe
File javaPath = findJava(javaHome);
if (javaPath == null) {
throw new IllegalArgumentException(
"Can not find java/java.exe executable file under java home: " + javaHome);
}
File toolsJar = findToolsJar(javaHome);
if (JavaVersionUtils.isLessThanJava9()) {
if (toolsJar == null || !toolsJar.exists()) {
throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome);
}
}
List<String> command = new ArrayList<String>();
command.add(javaPath.getAbsolutePath());
if (toolsJar != null && toolsJar.exists()) {
command.add("-Xbootclasspath/a:" + toolsJar.getAbsolutePath());
}
command.addAll(attachArgs);
// "${JAVA_HOME}"/bin/java \
// ${opts} \
// -jar "${arthas_lib_dir}/arthas-core.jar" \
// -pid ${TARGET_PID} \
// -target-ip ${TARGET_IP} \
// -telnet-port ${TELNET_PORT} \
// -http-port ${HTTP_PORT} \
// -core "${arthas_lib_dir}/arthas-core.jar" \
// -agent "${arthas_lib_dir}/arthas-agent.jar"
ProcessBuilder pb = new ProcessBuilder(command);
try {
final Process proc = pb.start();
Thread redirectStdout = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = proc.getInputStream();
try {
IOUtils.copy(inputStream, System.out);
} catch (IOException e) {
IOUtils.close(inputStream);
}
}
});
Thread redirectStderr = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = proc.getErrorStream();
try {
IOUtils.copy(inputStream, System.err);
} catch (IOException e) {
IOUtils.close(inputStream);
}
}
});
redirectStdout.start();
redirectStderr.start();
redirectStdout.join();
redirectStderr.join();
int exitValue = proc.exitValue();
if (exitValue != 0) {
AnsiLog.error("attach fail, targetPid: " + targetPid);
System.exit(1);
}
} catch (Throwable e) {
// ignore
}
}
public static int startArthasClient(String arthasHomeDir, List<String> telnetArgs, OutputStream out) throws Throwable {
// start java telnet client
// find arthas-client.jar
URLClassLoader classLoader = new URLClassLoader(
new URL[]{new File(arthasHomeDir, "arthas-client.jar").toURI().toURL()});
Class<?> telnetConsoleClass = classLoader.loadClass("com.taobao.arthas.client.TelnetConsole");
Method processMethod = telnetConsoleClass.getMethod("process", String[].class);
//redirect System.out/System.err
PrintStream originSysOut = System.out;
PrintStream originSysErr = System.err;
PrintStream newOut = new PrintStream(out);
PrintStream newErr = new PrintStream(out);
// call TelnetConsole.process()
// fix https://github.com/alibaba/arthas/issues/833
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
System.setOut(newOut);
System.setErr(newErr);
Thread.currentThread().setContextClassLoader(classLoader);
return (Integer) processMethod.invoke(null, new Object[]{telnetArgs.toArray(new String[0])});
} catch (Throwable e) {
//java.lang.reflect.InvocationTargetException : java.net.ConnectException
e = e.getCause();
if (e instanceof IOException || e instanceof InterruptedException) {
// ignore connection error and interrupted error
return STATUS_ERROR;
} else {
// process error
AnsiLog.error("process error: {}", e.toString());
AnsiLog.error(e);
return STATUS_EXEC_ERROR;
}
} finally {
Thread.currentThread().setContextClassLoader(tccl);
//reset System.out/System.err
System.setOut(originSysOut);
System.setErr(originSysErr);
//flush output
newOut.flush();
newErr.flush();
}
}
private static File findJava(String javaHome) {
String[] paths = { "bin/java", "bin/java.exe", "../bin/java", "../bin/java.exe" };
List<File> javaList = new ArrayList<File>();
for (String path : paths) {
File javaFile = new File(javaHome, path);
if (javaFile.exists()) {
AnsiLog.debug("Found java: " + javaFile.getAbsolutePath());
javaList.add(javaFile);
}
}
if (javaList.isEmpty()) {
AnsiLog.debug("Can not find java/java.exe under current java home: " + javaHome);
return null;
}
// find the shortest path, jre path longer than jdk path
if (javaList.size() > 1) {
Collections.sort(javaList, new Comparator<File>() {
@Override
public int compare(File file1, File file2) {
try {
return file1.getCanonicalPath().length() - file2.getCanonicalPath().length();
} catch (IOException e) {
// ignore
}
return -1;
}
});
}
return javaList.get(0);
}
private static File findToolsJar(String javaHome) {
if (JavaVersionUtils.isGreaterThanJava8()) {
return null;
}
File toolsJar = new File(javaHome, "lib/tools.jar");
if (!toolsJar.exists()) {
toolsJar = new File(javaHome, "../lib/tools.jar");
}
if (!toolsJar.exists()) {
// maybe jre
toolsJar = new File(javaHome, "../../lib/tools.jar");
}
if (!toolsJar.exists()) {
throw new IllegalArgumentException("Can not find tools.jar under java home: " + javaHome);
}
AnsiLog.debug("Found tools.jar: " + toolsJar.getAbsolutePath());
return toolsJar;
}
private static File findJps() {
// Try to find jps under java.home and System env JAVA_HOME
String javaHome = System.getProperty("java.home");
String[] paths = { "bin/jps", "bin/jps.exe", "../bin/jps", "../bin/jps.exe" };
List<File> jpsList = new ArrayList<File>();
for (String path : paths) {
File jpsFile = new File(javaHome, path);
if (jpsFile.exists()) {
AnsiLog.debug("Found jps: " + jpsFile.getAbsolutePath());
jpsList.add(jpsFile);
}
}
if (jpsList.isEmpty()) {
AnsiLog.debug("Can not find jps under :" + javaHome);
String javaHomeEnv = System.getenv("JAVA_HOME");
AnsiLog.debug("Try to find jps under env JAVA_HOME :" + javaHomeEnv);
for (String path : paths) {
File jpsFile = new File(javaHomeEnv, path);
if (jpsFile.exists()) {
AnsiLog.debug("Found jps: " + jpsFile.getAbsolutePath());
jpsList.add(jpsFile);
}
}
}
if (jpsList.isEmpty()) {
AnsiLog.debug("Can not find jps under current java home: " + javaHome);
return null;
}
// find the shortest path, jre path longer than jdk path
if (jpsList.size() > 1) {
Collections.sort(jpsList, new Comparator<File>() {
@Override
public int compare(File file1, File file2) {
try {
return file1.getCanonicalPath().length() - file2.getCanonicalPath().length();
} catch (IOException e) {
// ignore
}
return -1;
}
});
}
return jpsList.get(0);
}
private static boolean isJpsProcess(String mainClassName) {
return "sun.tools.jps.Jps".equals(mainClassName) || "jdk.jcmd/sun.tools.jps.Jps".equals(mainClassName);
}
}
package com.taobao.arthas.boot;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class DownloadUtilsTest {
@Rule
public TemporaryFolder rootFolder = new TemporaryFolder();
@Test
public void testReadReleaseVersion() {
String releaseVersion = DownloadUtils.readLatestReleaseVersion();
Assert.assertNotNull(releaseVersion);
Assert.assertNotEquals("releaseVersion is empty", "", releaseVersion.trim());
System.err.println(releaseVersion);
}
@Test
public void testReadAllVersions() {
List<String> versions = DownloadUtils.readRemoteVersions();
Assert.assertEquals("", true, versions.contains("3.1.7"));
}
@Test
public void testAliyunDownload() throws IOException {
// fix travis-ci failed problem
if (TimeUnit.MILLISECONDS.toHours(TimeZone.getDefault().getOffset(System.currentTimeMillis())) == 8) {
String version = "3.3.7";
File folder = rootFolder.newFolder();
System.err.println(folder.getAbsolutePath());
DownloadUtils.downArthasPackaging("aliyun", false, version, folder.getAbsolutePath());
File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh");
Assert.assertTrue(as.exists());
}
}
@Test
public void testCenterDownload() throws IOException {
String version = "3.1.7";
File folder = rootFolder.newFolder();
System.err.println(folder.getAbsolutePath());
DownloadUtils.downArthasPackaging("center", false, version, folder.getAbsolutePath());
File as = new File(folder, version + File.separator + "arthas" + File.separator + "as.sh");
Assert.assertTrue(as.exists());
}
}
#Generated by Git-Commit-Id-Plugin
git.build.version=3.6.4
git.closest.tag.commit.count=
git.closest.tag.name=
git.commit.id=35f73a652cf4efc30cc57df74dbab7d5c1de757a
git.commit.id.abbrev=35f73a6
git.commit.id.describe=35f73a6
git.commit.id.describe-short=35f73a6
git.commit.message.full=ci-set-3
git.commit.message.short=ci-set-3
git.commit.user.email=shengnan.hu@ustchcs.com
git.commit.user.name=shengnan.hu
git.dirty=false
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>arthas-all</artifactId>
<groupId>com.taobao.arthas</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>arthas-client</artifactId>
<name>arthas-client</name>
<build>
<finalName>arthas-client</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.taobao.arthas.client.TelnetConsole</mainClass>
</manifest>
<manifestEntries>
<Created-By>core engine team, middleware group, alibaba inc.</Created-By>
<Specification-Title>${project.name}</Specification-Title>
<Specification-Version>${project.version}</Specification-Version>
<Implementation-Title>${project.name}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>cli</artifactId>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
</dependency>
</dependencies>
</project>
package com.taobao.arthas.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Writer;
/***
* This is a utility class providing a reader/writer capability required by the
* weatherTelnet, rexec, rshell, and rlogin example programs. The only point of
* the class is to hold the static method readWrite which spawns a reader thread
* and a writer thread. The reader thread reads from a local input source
* (presumably stdin) and writes the data to a remote output destination. The
* writer thread reads from a remote input source and writes to a local output
* destination. The threads terminate when the remote input source closes.
***/
public final class IOUtil {
public static final void readWrite(final InputStream remoteInput, final OutputStream remoteOutput,
final InputStream localInput, final Writer localOutput) {
Thread reader, writer;
reader = new Thread() {
@Override
public void run() {
int ch;
try {
while (!interrupted() && (ch = localInput.read()) != -1) {
remoteOutput.write(ch);
remoteOutput.flush();
}
} catch (IOException e) {
// e.printStackTrace();
}
}
};
writer = new Thread() {
@Override
public void run() {
try {
InputStreamReader reader = new InputStreamReader(remoteInput);
while (true) {
int singleChar = reader.read();
if (singleChar == -1) {
break;
}
localOutput.write(singleChar);
localOutput.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
writer.setPriority(Thread.currentThread().getPriority() + 1);
writer.start();
reader.setDaemon(true);
reader.start();
try {
writer.join();
reader.interrupt();
} catch (InterruptedException e) {
// Ignored
}
}
}
\ No newline at end of file
package com.taobao.arthas.client;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.apache.commons.net.telnet.InvalidTelnetOptionException;
import org.apache.commons.net.telnet.TelnetClient;
import org.apache.commons.net.telnet.TelnetOptionHandler;
import org.apache.commons.net.telnet.WindowSizeOptionHandler;
import com.taobao.arthas.common.OSUtils;
import com.taobao.arthas.common.UsageRender;
import com.taobao.middleware.cli.CLI;
import com.taobao.middleware.cli.CommandLine;
import com.taobao.middleware.cli.UsageMessageFormatter;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.CLIConfigurator;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Option;
import com.taobao.middleware.cli.annotations.Summary;
import jline.Terminal;
import jline.TerminalSupport;
import jline.UnixTerminal;
import jline.console.ConsoleReader;
import jline.console.KeyMap;
/**
* @author ralf0131 2016-12-29 11:55.
* @author hengyunabc 2018-11-01
*/
@Name("arthas-client")
@Summary("Arthas Telnet Client")
@Description("EXAMPLES:\n" + " java -jar arthas-client.jar 127.0.0.1 3658\n"
+ " java -jar arthas-client.jar -c 'dashboard -n 1' \n"
+ " java -jar arthas-client.jar -f batch.as 127.0.0.1\n")
public class TelnetConsole {
private static final String PROMPT = "[arthas@"; // [arthas@49603]$
private static final int DEFAULT_CONNECTION_TIMEOUT = 5000; // 5000 ms
private static final byte CTRL_C = 0x03;
// ------- Status codes ------- //
/**
* Process success
*/
public static final int STATUS_OK = 0;
/**
* Generic error
*/
public static final int STATUS_ERROR = 1;
/**
* Execute commands timeout
*/
public static final int STATUS_EXEC_TIMEOUT = 100;
/**
* Execute commands error
*/
public static final int STATUS_EXEC_ERROR = 101;
private boolean help = false;
private String targetIp = "127.0.0.1";
private int port = 3658;
private String command;
private String batchFile;
private int executionTimeout = -1;
private Integer width = null;
private Integer height = null;
@Argument(argName = "target-ip", index = 0, required = false)
@Description("Target ip")
public void setTargetIp(String targetIp) {
this.targetIp = targetIp;
}
@Argument(argName = "port", index = 1, required = false)
@Description("The remote server port")
public void setPort(int port) {
this.port = port;
}
@Option(longName = "help", flag = true)
@Description("Print usage")
public void setHelp(boolean help) {
this.help = help;
}
@Option(shortName = "c", longName = "command")
@Description("Command to execute, multiple commands separated by ;")
public void setCommand(String command) {
this.command = command;
}
@Option(shortName = "f", longName = "batch-file")
@Description("The batch file to execute")
public void setBatchFile(String batchFile) {
this.batchFile = batchFile;
}
@Option(shortName = "t", longName = "execution-timeout")
@Description("The timeout (ms) of execute commands or batch file ")
public void setExecutionTimeout(int executionTimeout) {
this.executionTimeout = executionTimeout;
}
@Option(shortName = "w", longName = "width")
@Description("The terminal width")
public void setWidth(int width) {
this.width = width;
}
@Option(shortName = "h", longName = "height")
@Description("The terminal height")
public void setheight(int height) {
this.height = height;
}
public TelnetConsole() {
}
private static List<String> readLines(File batchFile) {
List<String> list = new ArrayList<String>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(batchFile));
String line = br.readLine();
while (line != null) {
list.add(line);
line = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
// ignore
}
}
}
return list;
}
public static void main(String[] args) throws Exception {
try {
int status = process(args, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(STATUS_OK);
}
});
System.exit(status);
} catch (Throwable e) {
e.printStackTrace();
CLI cli = CLIConfigurator.define(TelnetConsole.class);
System.out.println(usage(cli));
System.exit(STATUS_ERROR);
}
}
/**
* 提供给arthas-boot使用的主处理函数
*
* @param args
* @return status code
* @throws IOException
* @throws InterruptedException
*/
public static int process(String[] args) throws IOException, InterruptedException {
return process(args, null);
}
/**
* arthas client 主函数
* 注意: process()函数提供给arthas-boot使用,内部不能调用System.exit()结束进程的方法
*
* @param args
* @param eotEventCallback Ctrl+D signals an End of Transmission (EOT) event
* @return status code
* @throws IOException
*/
public static int process(String[] args, ActionListener eotEventCallback) throws IOException {
// support mingw/cygw jline color
if (OSUtils.isCygwinOrMinGW()) {
System.setProperty("jline.terminal", System.getProperty("jline.terminal", "jline.UnixTerminal"));
}
TelnetConsole telnetConsole = new TelnetConsole();
CLI cli = CLIConfigurator.define(TelnetConsole.class);
CommandLine commandLine = cli.parse(Arrays.asList(args));
CLIConfigurator.inject(commandLine, telnetConsole);
if (telnetConsole.isHelp()) {
System.out.println(usage(cli));
return STATUS_ERROR;
}
// Try to read cmds
List<String> cmds = new ArrayList<String>();
if (telnetConsole.getCommand() != null) {
for (String c : telnetConsole.getCommand().split(";")) {
cmds.add(c.trim());
}
} else if (telnetConsole.getBatchFile() != null) {
File file = new File(telnetConsole.getBatchFile());
if (!file.exists()) {
throw new IllegalArgumentException("batch file do not exist: " + telnetConsole.getBatchFile());
} else {
cmds.addAll(readLines(file));
}
}
final ConsoleReader consoleReader = new ConsoleReader(System.in, System.out);
consoleReader.setHandleUserInterrupt(true);
Terminal terminal = consoleReader.getTerminal();
// support catch ctrl+c event
terminal.disableInterruptCharacter();
if (terminal instanceof UnixTerminal) {
((UnixTerminal) terminal).disableLitteralNextCharacter();
}
try {
int width = TerminalSupport.DEFAULT_WIDTH;
int height = TerminalSupport.DEFAULT_HEIGHT;
if (!cmds.isEmpty()) {
// batch mode
if (telnetConsole.getWidth() != null) {
width = telnetConsole.getWidth();
}
if (telnetConsole.getheight() != null) {
height = telnetConsole.getheight();
}
} else {
// normal telnet client, get current terminal size
if (telnetConsole.getWidth() != null) {
width = telnetConsole.getWidth();
} else {
width = terminal.getWidth();
// hack for windows dos
if (OSUtils.isWindows()) {
width--;
}
}
if (telnetConsole.getheight() != null) {
height = telnetConsole.getheight();
} else {
height = terminal.getHeight();
}
}
final TelnetClient telnet = new TelnetClient();
telnet.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT);
// send init terminal size
TelnetOptionHandler sizeOpt = new WindowSizeOptionHandler(width, height, true, true, false, false);
try {
telnet.addOptionHandler(sizeOpt);
} catch (InvalidTelnetOptionException e) {
// ignore
}
// ctrl + c event callback
consoleReader.getKeys().bind(Character.toString((char) CTRL_C), new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
consoleReader.getCursorBuffer().clear(); // clear current line
telnet.getOutputStream().write(CTRL_C);
telnet.getOutputStream().flush();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
// ctrl + d event call back
consoleReader.getKeys().bind(Character.toString(KeyMap.CTRL_D), eotEventCallback);
try {
telnet.connect(telnetConsole.getTargetIp(), telnetConsole.getPort());
} catch (IOException e) {
System.out.println("Connect to telnet server error: " + telnetConsole.getTargetIp() + " "
+ telnetConsole.getPort());
throw e;
}
if (cmds.isEmpty()) {
IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), consoleReader.getInput(),
consoleReader.getOutput());
} else {
try {
return batchModeRun(telnet, cmds, telnetConsole.getExecutionTimeout());
} catch (Throwable e) {
System.out.println("Execute commands error: " + e.getMessage());
e.printStackTrace();
return STATUS_EXEC_ERROR;
} finally {
try {
telnet.disconnect();
} catch (IOException e) {
//ignore ex
}
}
}
return STATUS_OK;
} finally {
//reset terminal setting, fix https://github.com/alibaba/arthas/issues/1412
try {
terminal.restore();
} catch (Throwable e) {
System.out.println("Restore terminal settings failure: "+e.getMessage());
e.printStackTrace();
}
}
}
private static int batchModeRun(TelnetClient telnet, List<String> commands, final int executionTimeout)
throws IOException, InterruptedException {
if (commands.size() == 0) {
return STATUS_OK;
}
long startTime = System.currentTimeMillis();
final InputStream inputStream = telnet.getInputStream();
final OutputStream outputStream = telnet.getOutputStream();
final BlockingQueue<String> receviedPromptQueue = new LinkedBlockingQueue<String>(1);
Thread printResultThread = new Thread(new Runnable() {
@Override
public void run() {
try {
StringBuilder line = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
int b = -1;
while (true) {
b = in.read();
if (b == -1) {
break;
}
line.appendCodePoint(b);
// 检查到有 [arthas@ 时,意味着可以执行下一个命令了
int index = line.indexOf(PROMPT);
if (index > 0) {
line.delete(0, index + PROMPT.length());
receviedPromptQueue.put("");
}
System.out.print(Character.toChars(b));
}
} catch (Exception e) {
// ignore
}
}
});
printResultThread.start();
// send commands to arthas server
for (String command : commands) {
if (command.trim().isEmpty()) {
continue;
}
// try poll prompt and check timeout
while (receviedPromptQueue.poll(100, TimeUnit.MILLISECONDS) == null) {
if (executionTimeout > 0) {
long now = System.currentTimeMillis();
if (now - startTime > executionTimeout) {
return STATUS_EXEC_TIMEOUT;
}
}
}
// send command to server
outputStream.write((command + " | plaintext\n").getBytes());
outputStream.flush();
}
// 读到最后一个命令执行后的 prompt ,可以直接发 quit命令了。
receviedPromptQueue.take();
outputStream.write("quit\n".getBytes());
outputStream.flush();
System.out.println();
return STATUS_OK;
}
private static String usage(CLI cli) {
StringBuilder usageStringBuilder = new StringBuilder();
UsageMessageFormatter usageMessageFormatter = new UsageMessageFormatter();
usageMessageFormatter.setOptionComparator(null);
cli.usage(usageStringBuilder, usageMessageFormatter);
return UsageRender.render(usageStringBuilder.toString());
}
public String getTargetIp() {
return targetIp;
}
public int getPort() {
return port;
}
public String getCommand() {
return command;
}
public String getBatchFile() {
return batchFile;
}
public int getExecutionTimeout() {
return executionTimeout;
}
public Integer getWidth() {
return width;
}
public Integer getheight() {
return height;
}
public boolean isHelp() {
return help;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.Charset;
/***
* The DatagramSocketClient provides the basic operations that are required
* of client objects accessing datagram sockets. It is meant to be
* subclassed to avoid having to rewrite the same code over and over again
* to open a socket, close a socket, set timeouts, etc. Of special note
* is the {@link #setDatagramSocketFactory setDatagramSocketFactory }
* method, which allows you to control the type of DatagramSocket the
* DatagramSocketClient creates for network communications. This is
* especially useful for adding things like proxy support as well as better
* support for applets. For
* example, you could create a
* {@link org.apache.commons.net.DatagramSocketFactory}
* that
* requests browser security capabilities before creating a socket.
* All classes derived from DatagramSocketClient should use the
* {@link #_socketFactory_ _socketFactory_ } member variable to
* create DatagramSocket instances rather than instantiating
* them by directly invoking a constructor. By honoring this contract
* you guarantee that a user will always be able to provide his own
* Socket implementations by substituting his own SocketFactory.
*
*
* @see DatagramSocketFactory
***/
public abstract class DatagramSocketClient
{
/***
* The default DatagramSocketFactory shared by all DatagramSocketClient
* instances.
***/
private static final DatagramSocketFactory __DEFAULT_SOCKET_FACTORY =
new DefaultDatagramSocketFactory();
/**
* Charset to use for byte IO.
*/
private Charset charset = Charset.defaultCharset();
/*** The timeout to use after opening a socket. ***/
protected int _timeout_;
/*** The datagram socket used for the connection. ***/
protected DatagramSocket _socket_;
/***
* A status variable indicating if the client's socket is currently open.
***/
protected boolean _isOpen_;
/*** The datagram socket's DatagramSocketFactory. ***/
protected DatagramSocketFactory _socketFactory_;
/***
* Default constructor for DatagramSocketClient. Initializes
* _socket_ to null, _timeout_ to 0, and _isOpen_ to false.
***/
public DatagramSocketClient()
{
_socket_ = null;
_timeout_ = 0;
_isOpen_ = false;
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
}
/***
* Opens a DatagramSocket on the local host at the first available port.
* Also sets the timeout on the socket to the default timeout set
* by {@link #setDefaultTimeout setDefaultTimeout() }.
* <p>
* _isOpen_ is set to true after calling this method and _socket_
* is set to the newly opened socket.
*
* @exception SocketException If the socket could not be opened or the
* timeout could not be set.
***/
public void open() throws SocketException
{
_socket_ = _socketFactory_.createDatagramSocket();
_socket_.setSoTimeout(_timeout_);
_isOpen_ = true;
}
/***
* Opens a DatagramSocket on the local host at a specified port.
* Also sets the timeout on the socket to the default timeout set
* by {@link #setDefaultTimeout setDefaultTimeout() }.
* <p>
* _isOpen_ is set to true after calling this method and _socket_
* is set to the newly opened socket.
*
* @param port The port to use for the socket.
* @exception SocketException If the socket could not be opened or the
* timeout could not be set.
***/
public void open(int port) throws SocketException
{
_socket_ = _socketFactory_.createDatagramSocket(port);
_socket_.setSoTimeout(_timeout_);
_isOpen_ = true;
}
/***
* Opens a DatagramSocket at the specified address on the local host
* at a specified port.
* Also sets the timeout on the socket to the default timeout set
* by {@link #setDefaultTimeout setDefaultTimeout() }.
* <p>
* _isOpen_ is set to true after calling this method and _socket_
* is set to the newly opened socket.
*
* @param port The port to use for the socket.
* @param laddr The local address to use.
* @exception SocketException If the socket could not be opened or the
* timeout could not be set.
***/
public void open(int port, InetAddress laddr) throws SocketException
{
_socket_ = _socketFactory_.createDatagramSocket(port, laddr);
_socket_.setSoTimeout(_timeout_);
_isOpen_ = true;
}
/***
* Closes the DatagramSocket used for the connection.
* You should call this method after you've finished using the class
* instance and also before you call {@link #open open() }
* again. _isOpen_ is set to false and _socket_ is set to null.
* If you call this method when the client socket is not open,
* a NullPointerException is thrown.
***/
public void close()
{
if (_socket_ != null) {
_socket_.close();
}
_socket_ = null;
_isOpen_ = false;
}
/***
* Returns true if the client has a currently open socket.
*
* @return True if the client has a curerntly open socket, false otherwise.
***/
public boolean isOpen()
{
return _isOpen_;
}
/***
* Set the default timeout in milliseconds to use when opening a socket.
* After a call to open, the timeout for the socket is set using this value.
* This method should be used prior to a call to {@link #open open()}
* and should not be confused with {@link #setSoTimeout setSoTimeout()}
* which operates on the currently open socket. _timeout_ contains
* the new timeout value.
*
* @param timeout The timeout in milliseconds to use for the datagram socket
* connection.
***/
public void setDefaultTimeout(int timeout)
{
_timeout_ = timeout;
}
/***
* Returns the default timeout in milliseconds that is used when
* opening a socket.
*
* @return The default timeout in milliseconds that is used when
* opening a socket.
***/
public int getDefaultTimeout()
{
return _timeout_;
}
/***
* Set the timeout in milliseconds of a currently open connection.
* Only call this method after a connection has been opened
* by {@link #open open()}.
*
* @param timeout The timeout in milliseconds to use for the currently
* open datagram socket connection.
* @throws SocketException if an error setting the timeout
***/
public void setSoTimeout(int timeout) throws SocketException
{
_socket_.setSoTimeout(timeout);
}
/***
* Returns the timeout in milliseconds of the currently opened socket.
* If you call this method when the client socket is not open,
* a NullPointerException is thrown.
*
* @return The timeout in milliseconds of the currently opened socket.
* @throws SocketException if an error getting the timeout
***/
public int getSoTimeout() throws SocketException
{
return _socket_.getSoTimeout();
}
/***
* Returns the port number of the open socket on the local host used
* for the connection. If you call this method when the client socket
* is not open, a NullPointerException is thrown.
*
* @return The port number of the open socket on the local host used
* for the connection.
***/
public int getLocalPort()
{
return _socket_.getLocalPort();
}
/***
* Returns the local address to which the client's socket is bound.
* If you call this method when the client socket is not open, a
* NullPointerException is thrown.
*
* @return The local address to which the client's socket is bound.
***/
public InetAddress getLocalAddress()
{
return _socket_.getLocalAddress();
}
/***
* Sets the DatagramSocketFactory used by the DatagramSocketClient
* to open DatagramSockets. If the factory value is null, then a default
* factory is used (only do this to reset the factory after having
* previously altered it).
*
* @param factory The new DatagramSocketFactory the DatagramSocketClient
* should use.
***/
public void setDatagramSocketFactory(DatagramSocketFactory factory)
{
if (factory == null) {
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
} else {
_socketFactory_ = factory;
}
}
/**
* Gets the charset name.
*
* @return the charset name.
* @since 3.3
* TODO Will be deprecated once the code requires Java 1.6 as a mininmum
*/
public String getCharsetName() {
return charset.name();
}
/**
* Gets the charset.
*
* @return the charset.
* @since 3.3
*/
public Charset getCharset() {
return charset;
}
/**
* Sets the charset.
*
* @param charset the charset.
* @since 3.3
*/
public void setCharset(Charset charset) {
this.charset = charset;
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.commons.net;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/***
* The DatagramSocketFactory interface provides a means for the
* programmer to control the creation of datagram sockets and
* provide his own DatagramSocket implementations for use by all
* classes derived from
* {@link org.apache.commons.net.DatagramSocketClient}
* .
* This allows you to provide your own DatagramSocket implementations and
* to perform security checks or browser capability requests before
* creating a DatagramSocket.
*
*
***/
public interface DatagramSocketFactory
{
/***
* Creates a DatagramSocket on the local host at the first available port.
* @return the socket
*
* @exception SocketException If the socket could not be created.
***/
public DatagramSocket createDatagramSocket() throws SocketException;
/***
* Creates a DatagramSocket on the local host at a specified port.
*
* @param port The port to use for the socket.
* @return the socket
* @exception SocketException If the socket could not be created.
***/
public DatagramSocket createDatagramSocket(int port) throws SocketException;
/***
* Creates a DatagramSocket at the specified address on the local host
* at a specified port.
*
* @param port The port to use for the socket.
* @param laddr The local address to use.
* @return the socket
* @exception SocketException If the socket could not be created.
***/
public DatagramSocket createDatagramSocket(int port, InetAddress laddr)
throws SocketException;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment