8002048: Protocol to discovery of manageable Java processes on a network
Introduce a protocol to discover manageble Java instances across a network subnet, JDP Reviewed-by: sla, dfuchs
This commit is contained in:
parent
3f02516d3e
commit
eea117f3e5
jdk
@ -22,7 +22,6 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.management;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@ -31,49 +30,55 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
import static sun.management.AgentConfigurationError.*;
|
||||
import sun.management.jmxremote.ConnectorBootstrap;
|
||||
import sun.management.jdp.JdpController;
|
||||
import sun.management.jdp.JdpException;
|
||||
import sun.misc.VMSupport;
|
||||
|
||||
/**
|
||||
* This Agent is started by the VM when -Dcom.sun.management.snmp
|
||||
* or -Dcom.sun.management.jmxremote is set. This class will be
|
||||
* loaded by the system class loader. Also jmx framework could
|
||||
* be started by jcmd
|
||||
* This Agent is started by the VM when -Dcom.sun.management.snmp or
|
||||
* -Dcom.sun.management.jmxremote is set. This class will be loaded by the
|
||||
* system class loader. Also jmx framework could be started by jcmd
|
||||
*/
|
||||
public class Agent {
|
||||
// management properties
|
||||
|
||||
private static Properties mgmtProps;
|
||||
private static ResourceBundle messageRB;
|
||||
|
||||
private static final String CONFIG_FILE =
|
||||
"com.sun.management.config.file";
|
||||
"com.sun.management.config.file";
|
||||
private static final String SNMP_PORT =
|
||||
"com.sun.management.snmp.port";
|
||||
"com.sun.management.snmp.port";
|
||||
private static final String JMXREMOTE =
|
||||
"com.sun.management.jmxremote";
|
||||
"com.sun.management.jmxremote";
|
||||
private static final String JMXREMOTE_PORT =
|
||||
"com.sun.management.jmxremote.port";
|
||||
"com.sun.management.jmxremote.port";
|
||||
private static final String RMI_PORT =
|
||||
"com.sun.management.jmxremote.rmi.port";
|
||||
private static final String ENABLE_THREAD_CONTENTION_MONITORING =
|
||||
"com.sun.management.enableThreadContentionMonitoring";
|
||||
"com.sun.management.enableThreadContentionMonitoring";
|
||||
private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
|
||||
"com.sun.management.jmxremote.localConnectorAddress";
|
||||
|
||||
"com.sun.management.jmxremote.localConnectorAddress";
|
||||
private static final String SNMP_ADAPTOR_BOOTSTRAP_CLASS_NAME =
|
||||
"sun.management.snmp.AdaptorBootstrap";
|
||||
"sun.management.snmp.AdaptorBootstrap";
|
||||
|
||||
private static final String JDP_DEFAULT_ADDRESS = "239.255.255.225";
|
||||
private static final int JDP_DEFAULT_PORT = 7095;
|
||||
|
||||
// The only active agent allowed
|
||||
private static JMXConnectorServer jmxServer = null;
|
||||
@ -81,26 +86,25 @@ public class Agent {
|
||||
// Parse string com.sun.management.prop=xxx,com.sun.management.prop=yyyy
|
||||
// and return property set if args is null or empty
|
||||
// return empty property set
|
||||
private static Properties parseString(String args){
|
||||
private static Properties parseString(String args) {
|
||||
Properties argProps = new Properties();
|
||||
if (args != null) {
|
||||
for (String option : args.split(",")) {
|
||||
String s[] = option.split("=", 2);
|
||||
String name = s[0].trim();
|
||||
String value = (s.length > 1) ? s[1].trim() : "";
|
||||
for (String option : args.split(",")) {
|
||||
String s[] = option.split("=", 2);
|
||||
String name = s[0].trim();
|
||||
String value = (s.length > 1) ? s[1].trim() : "";
|
||||
|
||||
if (!name.startsWith("com.sun.management.")) {
|
||||
error(INVALID_OPTION, name);
|
||||
}
|
||||
if (!name.startsWith("com.sun.management.")) {
|
||||
error(INVALID_OPTION, name);
|
||||
}
|
||||
|
||||
argProps.setProperty(name, value);
|
||||
}
|
||||
argProps.setProperty(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return argProps;
|
||||
}
|
||||
|
||||
|
||||
// invoked by -javaagent or -Dcom.sun.management.agent.class
|
||||
public static void premain(String args) throws Exception {
|
||||
agentmain(args);
|
||||
@ -115,18 +119,18 @@ public class Agent {
|
||||
Properties arg_props = parseString(args);
|
||||
|
||||
// Read properties from the config file
|
||||
Properties config_props = new Properties();
|
||||
String fname = arg_props.getProperty(CONFIG_FILE);
|
||||
readConfiguration(fname, config_props);
|
||||
Properties config_props = new Properties();
|
||||
String fname = arg_props.getProperty(CONFIG_FILE);
|
||||
readConfiguration(fname, config_props);
|
||||
|
||||
// Arguments override config file
|
||||
config_props.putAll(arg_props);
|
||||
startAgent(config_props);
|
||||
// Arguments override config file
|
||||
config_props.putAll(arg_props);
|
||||
startAgent(config_props);
|
||||
}
|
||||
|
||||
// jcmd ManagementAgent.start_local entry point
|
||||
// Also called due to command-line via startAgent()
|
||||
private static synchronized void startLocalManagementAgent(){
|
||||
private static synchronized void startLocalManagementAgent() {
|
||||
Properties agentProps = VMSupport.getAgentProperties();
|
||||
|
||||
// start local connector if not started
|
||||
@ -156,7 +160,7 @@ public class Agent {
|
||||
throw new RuntimeException(getText(INVALID_STATE, "Agent already started"));
|
||||
}
|
||||
|
||||
Properties argProps = parseString(args);
|
||||
Properties argProps = parseString(args);
|
||||
Properties configProps = new Properties();
|
||||
|
||||
// Load the management properties from the config file
|
||||
@ -169,7 +173,7 @@ public class Agent {
|
||||
// management properties can be overridden by system properties
|
||||
// which take precedence
|
||||
Properties sysProps = System.getProperties();
|
||||
synchronized(sysProps){
|
||||
synchronized (sysProps) {
|
||||
configProps.putAll(sysProps);
|
||||
}
|
||||
|
||||
@ -190,21 +194,26 @@ public class Agent {
|
||||
// can specify this property inside config file, so enable optional
|
||||
// monitoring functionality if this property is set
|
||||
final String enableThreadContentionMonitoring =
|
||||
configProps.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
|
||||
configProps.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
|
||||
|
||||
if (enableThreadContentionMonitoring != null) {
|
||||
ManagementFactory.getThreadMXBean().
|
||||
setThreadContentionMonitoringEnabled(true);
|
||||
setThreadContentionMonitoringEnabled(true);
|
||||
}
|
||||
|
||||
String jmxremotePort = configProps.getProperty(JMXREMOTE_PORT);
|
||||
if (jmxremotePort != null) {
|
||||
jmxServer = ConnectorBootstrap.
|
||||
startRemoteConnectorServer(jmxremotePort, configProps);
|
||||
startRemoteConnectorServer(jmxremotePort, configProps);
|
||||
|
||||
startDiscoveryService(configProps);
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized void stopRemoteManagementAgent() throws Exception {
|
||||
|
||||
JdpController.stopDiscoveryService();
|
||||
|
||||
if (jmxServer != null) {
|
||||
ConnectorBootstrap.unexportRegistry();
|
||||
|
||||
@ -222,15 +231,15 @@ public class Agent {
|
||||
|
||||
// Enable optional monitoring functionality if requested
|
||||
final String enableThreadContentionMonitoring =
|
||||
props.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
|
||||
props.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
|
||||
if (enableThreadContentionMonitoring != null) {
|
||||
ManagementFactory.getThreadMXBean().
|
||||
setThreadContentionMonitoringEnabled(true);
|
||||
setThreadContentionMonitoringEnabled(true);
|
||||
}
|
||||
|
||||
try {
|
||||
if (snmpPort != null) {
|
||||
loadSnmpAgent(snmpPort, props);
|
||||
loadSnmpAgent(snmpPort, props);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -242,13 +251,14 @@ public class Agent {
|
||||
* of this "local" server is exported as a counter to the jstat
|
||||
* instrumentation buffer.
|
||||
*/
|
||||
if (jmxremote != null || jmxremotePort != null) {
|
||||
if (jmxremote != null || jmxremotePort != null) {
|
||||
if (jmxremotePort != null) {
|
||||
jmxServer = ConnectorBootstrap.
|
||||
startRemoteConnectorServer(jmxremotePort, props);
|
||||
jmxServer = ConnectorBootstrap.
|
||||
startRemoteConnectorServer(jmxremotePort, props);
|
||||
startDiscoveryService(props);
|
||||
}
|
||||
startLocalManagementAgent();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (AgentConfigurationError e) {
|
||||
error(e.getError(), e.getParams());
|
||||
@ -257,6 +267,73 @@ public class Agent {
|
||||
}
|
||||
}
|
||||
|
||||
private static void startDiscoveryService(Properties props)
|
||||
throws IOException {
|
||||
// Start discovery service if requested
|
||||
String discoveryPort = props.getProperty("com.sun.management.jdp.port");
|
||||
String discoveryAddress = props.getProperty("com.sun.management.jdp.address");
|
||||
String discoveryShouldStart = props.getProperty("com.sun.management.jmxremote.autodiscovery");
|
||||
|
||||
// Decide whether we should start autodicovery service.
|
||||
// To start autodiscovery following conditions should be met:
|
||||
// autodiscovery==true OR (autodicovery==null AND jdp.port != NULL)
|
||||
|
||||
boolean shouldStart = false;
|
||||
if (discoveryShouldStart == null){
|
||||
shouldStart = (discoveryPort != null);
|
||||
}
|
||||
else{
|
||||
try{
|
||||
shouldStart = Boolean.parseBoolean(discoveryShouldStart);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AgentConfigurationError("Couldn't parse autodiscovery argument");
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldStart) {
|
||||
// port and address are required arguments and have no default values
|
||||
InetAddress address;
|
||||
try {
|
||||
address = (discoveryAddress == null) ?
|
||||
InetAddress.getByName(JDP_DEFAULT_ADDRESS) : InetAddress.getByName(discoveryAddress);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new AgentConfigurationError("Unable to broadcast to requested address", e);
|
||||
}
|
||||
|
||||
int port = JDP_DEFAULT_PORT;
|
||||
if (discoveryPort != null) {
|
||||
try {
|
||||
port = Integer.parseInt(discoveryPort);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new AgentConfigurationError("Couldn't parse JDP port argument");
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuilding service URL to broadcast it
|
||||
String jmxremotePort = props.getProperty(JMXREMOTE_PORT);
|
||||
String rmiPort = props.getProperty(RMI_PORT);
|
||||
|
||||
JMXServiceURL url = jmxServer.getAddress();
|
||||
String hostname = url.getHost();
|
||||
|
||||
String jmxUrlStr = (rmiPort != null)
|
||||
? String.format(
|
||||
"service:jmx:rmi://%s:%s/jndi/rmi://%s:%s/jmxrmi",
|
||||
hostname, rmiPort, hostname, jmxremotePort)
|
||||
: String.format(
|
||||
"service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", hostname, jmxremotePort);
|
||||
|
||||
String instanceName = System.getProperty("com.sun.management.jdp.name");
|
||||
|
||||
try{
|
||||
JdpController.startDiscoveryService(address, port, instanceName, jmxUrlStr);
|
||||
}
|
||||
catch(JdpException e){
|
||||
throw new AgentConfigurationError("Couldn't start JDP service", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Properties loadManagementProperties() {
|
||||
Properties props = new Properties();
|
||||
|
||||
@ -268,22 +345,22 @@ public class Agent {
|
||||
// management properties can be overridden by system properties
|
||||
// which take precedence
|
||||
Properties sysProps = System.getProperties();
|
||||
synchronized(sysProps){
|
||||
synchronized (sysProps) {
|
||||
props.putAll(sysProps);
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized Properties getManagementProperties() {
|
||||
public static synchronized Properties getManagementProperties() {
|
||||
if (mgmtProps == null) {
|
||||
String configFile = System.getProperty(CONFIG_FILE);
|
||||
String snmpPort = System.getProperty(SNMP_PORT);
|
||||
String jmxremote = System.getProperty(JMXREMOTE);
|
||||
String jmxremotePort = System.getProperty(JMXREMOTE_PORT);
|
||||
|
||||
if (configFile == null && snmpPort == null &&
|
||||
jmxremote == null && jmxremotePort == null) {
|
||||
if (configFile == null && snmpPort == null
|
||||
&& jmxremote == null && jmxremotePort == null) {
|
||||
// return if out-of-the-management option is not specified
|
||||
return null;
|
||||
}
|
||||
@ -297,22 +374,23 @@ public class Agent {
|
||||
// invoke the following through reflection:
|
||||
// AdaptorBootstrap.initialize(snmpPort, props);
|
||||
final Class<?> adaptorClass =
|
||||
Class.forName(SNMP_ADAPTOR_BOOTSTRAP_CLASS_NAME,true,null);
|
||||
Class.forName(SNMP_ADAPTOR_BOOTSTRAP_CLASS_NAME, true, null);
|
||||
final Method initializeMethod =
|
||||
adaptorClass.getMethod("initialize",
|
||||
String.class, Properties.class);
|
||||
initializeMethod.invoke(null,snmpPort,props);
|
||||
String.class, Properties.class);
|
||||
initializeMethod.invoke(null, snmpPort, props);
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException x) {
|
||||
// snmp runtime doesn't exist - initialization fails
|
||||
throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT,x);
|
||||
throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT, x);
|
||||
} catch (InvocationTargetException x) {
|
||||
final Throwable cause = x.getCause();
|
||||
if (cause instanceof RuntimeException)
|
||||
if (cause instanceof RuntimeException) {
|
||||
throw (RuntimeException) cause;
|
||||
else if (cause instanceof Error)
|
||||
} else if (cause instanceof Error) {
|
||||
throw (Error) cause;
|
||||
}
|
||||
// should not happen...
|
||||
throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT,cause);
|
||||
throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,8 +431,8 @@ public class Agent {
|
||||
} catch (IOException e) {
|
||||
error(CONFIG_FILE_CLOSE_FAILED, fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void startAgent() throws Exception {
|
||||
@ -389,9 +467,9 @@ public class Agent {
|
||||
// invoke the premain(String args) method
|
||||
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
|
||||
Method premain = clz.getMethod("premain",
|
||||
new Class<?>[] { String.class });
|
||||
new Class<?>[]{String.class});
|
||||
premain.invoke(null, /* static */
|
||||
new Object[] { args });
|
||||
new Object[]{args});
|
||||
} catch (ClassNotFoundException ex) {
|
||||
error(AGENT_CLASS_NOT_FOUND, "\"" + cname + "\"");
|
||||
} catch (NoSuchMethodException ex) {
|
||||
@ -400,8 +478,8 @@ public class Agent {
|
||||
error(AGENT_CLASS_ACCESS_DENIED);
|
||||
} catch (Exception ex) {
|
||||
String msg = (ex.getCause() == null
|
||||
? ex.getMessage()
|
||||
: ex.getCause().getMessage());
|
||||
? ex.getMessage()
|
||||
: ex.getCause().getMessage());
|
||||
error(AGENT_CLASS_FAILED, msg);
|
||||
}
|
||||
}
|
||||
@ -425,7 +503,6 @@ public class Agent {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void error(String key, String message) {
|
||||
String keyText = getText(key);
|
||||
System.err.print(getText("agent.err.error") + ": " + keyText);
|
||||
@ -447,7 +524,7 @@ public class Agent {
|
||||
private static void initResource() {
|
||||
try {
|
||||
messageRB =
|
||||
ResourceBundle.getBundle("sun.management.resources.agent");
|
||||
ResourceBundle.getBundle("sun.management.resources.agent");
|
||||
} catch (MissingResourceException e) {
|
||||
throw new Error("Fatal: Resource for management agent is missing");
|
||||
}
|
||||
@ -470,10 +547,9 @@ public class Agent {
|
||||
}
|
||||
String format = messageRB.getString(key);
|
||||
if (format == null) {
|
||||
format = "missing resource key: key = \"" + key + "\", " +
|
||||
"arguments = \"{0}\", \"{1}\", \"{2}\"";
|
||||
format = "missing resource key: key = \"" + key + "\", "
|
||||
+ "arguments = \"{0}\", \"{1}\", \"{2}\"";
|
||||
}
|
||||
return MessageFormat.format(format, (Object[]) args);
|
||||
}
|
||||
|
||||
}
|
||||
|
124
jdk/src/share/classes/sun/management/jdp/JdpBroadcaster.java
Normal file
124
jdk/src/share/classes/sun/management/jdp/JdpBroadcaster.java
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.ProtocolFamily;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.UnsupportedAddressTypeException;
|
||||
|
||||
/**
|
||||
* JdpBroadcaster is responsible for sending pre-built JDP packet across a Net
|
||||
*
|
||||
* <p> Multicast group address, port number and ttl have to be chosen on upper
|
||||
* level and passed to broadcaster constructor. Also it's possible to specify
|
||||
* source address to broadcast from. </p>
|
||||
*
|
||||
* <p>JdpBradcaster doesn't perform any validation on a supplied {@code port} and {@code ttl} because
|
||||
* the allowed values depend on an operating system setup</p>
|
||||
*
|
||||
*/
|
||||
public final class JdpBroadcaster {
|
||||
|
||||
private final InetAddress addr;
|
||||
private final int port;
|
||||
private final DatagramChannel channel;
|
||||
|
||||
/**
|
||||
* Create a new broadcaster
|
||||
*
|
||||
* @param address - multicast group address
|
||||
* @param srcAddress - address of interface we should use to broadcast.
|
||||
* @param port - udp port to use
|
||||
* @param ttl - packet ttl
|
||||
* @throws IOException
|
||||
*/
|
||||
public JdpBroadcaster(InetAddress address, InetAddress srcAddress, int port, int ttl)
|
||||
throws IOException, JdpException {
|
||||
this.addr = address;
|
||||
this.port = port;
|
||||
|
||||
ProtocolFamily family = (address instanceof Inet6Address)
|
||||
? StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
|
||||
|
||||
channel = DatagramChannel.open(family);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.setOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl);
|
||||
|
||||
// with srcAddress equal to null, this constructor do exactly the same as
|
||||
// if srcAddress is not passed
|
||||
if (srcAddress != null) {
|
||||
// User requests particular interface to bind to
|
||||
NetworkInterface interf = NetworkInterface.getByInetAddress(srcAddress);
|
||||
try {
|
||||
channel.bind(new InetSocketAddress(srcAddress, 0));
|
||||
} catch (UnsupportedAddressTypeException ex) {
|
||||
throw new JdpException("Unable to bind to source address");
|
||||
}
|
||||
channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, interf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new broadcaster
|
||||
*
|
||||
* @param address - multicast group address
|
||||
* @param port - udp port to use
|
||||
* @param ttl - packet ttl
|
||||
* @throws IOException
|
||||
*/
|
||||
public JdpBroadcaster(InetAddress address, int port, int ttl)
|
||||
throws IOException, JdpException {
|
||||
this(address, null, port, ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast pre-built packet
|
||||
*
|
||||
* @param packet - instance of JdpPacket
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendPacket(JdpPacket packet)
|
||||
throws IOException {
|
||||
byte[] data = packet.getPacketData();
|
||||
// Unlike allocate/put wrap don't need a flip afterward
|
||||
ByteBuffer b = ByteBuffer.wrap(data);
|
||||
channel.send(b, new InetSocketAddress(addr, port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown broadcaster and close underlying socket channel
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void shutdown() throws IOException {
|
||||
channel.close();
|
||||
}
|
||||
}
|
196
jdk/src/share/classes/sun/management/jdp/JdpController.java
Normal file
196
jdk/src/share/classes/sun/management/jdp/JdpController.java
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* JdpController is responsible to create and manage a broadcast loop
|
||||
*
|
||||
* <p> Other part of code has no access to broadcast loop and have to use
|
||||
* provided static methods
|
||||
* {@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService}
|
||||
* and {@link #stopDiscoveryService() stopDiscoveryService}</p>
|
||||
* <p>{@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} could be called multiple
|
||||
* times as it stops the running service if it is necessary. Call to {@link #stopDiscoveryService() stopDiscoveryService}
|
||||
* ignored if service isn't run</p>
|
||||
*
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* <p> System properties below could be used to control broadcast loop behavior.
|
||||
* Property below have to be set explicitly in command line. It's not possible to
|
||||
* set it in management.config file. Careless changes of these properties could
|
||||
* lead to security or network issues.
|
||||
* <ul>
|
||||
* <li>com.sun.management.jdp.ttl - set ttl for broadcast packet</li>
|
||||
* <li>com.sun.management.jdp.pause - set broadcast interval in seconds</li>
|
||||
* <li>com.sun.management.jdp.source_addr - an address of interface to use for broadcast</li>
|
||||
* </ul>
|
||||
</p>
|
||||
* <p>null parameters values are filtered out on {@link JdpPacketWriter} level and
|
||||
* corresponding keys are not placed to packet.</p>
|
||||
*/
|
||||
public final class JdpController {
|
||||
|
||||
private static class JDPControllerRunner implements Runnable {
|
||||
|
||||
private final JdpJmxPacket packet;
|
||||
private final JdpBroadcaster bcast;
|
||||
private final int pause;
|
||||
private volatile boolean shutdown = false;
|
||||
|
||||
private JDPControllerRunner(JdpBroadcaster bcast, JdpJmxPacket packet, int pause) {
|
||||
this.bcast = bcast;
|
||||
this.packet = packet;
|
||||
this.pause = pause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!shutdown) {
|
||||
bcast.sendPacket(packet);
|
||||
try {
|
||||
Thread.sleep(this.pause);
|
||||
} catch (InterruptedException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
// pass;
|
||||
}
|
||||
|
||||
// It's not possible to re-use controller,
|
||||
// nevertheless reset shutdown variable
|
||||
try {
|
||||
stop();
|
||||
bcast.shutdown();
|
||||
} catch (IOException ex) {
|
||||
// pass - ignore IOException during shutdown
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
shutdown = true;
|
||||
}
|
||||
}
|
||||
private static JDPControllerRunner controller = null;
|
||||
|
||||
private JdpController(){
|
||||
// Don't allow to instantiate this class.
|
||||
}
|
||||
|
||||
// Utility to handle optional system properties
|
||||
// Parse an integer from string or return default if provided string is null
|
||||
private static int getInteger(String val, int dflt, String msg) throws JdpException {
|
||||
try {
|
||||
return (val == null) ? dflt : Integer.parseInt(val);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new JdpException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse an inet address from string or return default if provided string is null
|
||||
private static InetAddress getInetAddress(String val, InetAddress dflt, String msg) throws JdpException {
|
||||
try {
|
||||
return (val == null) ? dflt : InetAddress.getByName(val);
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new JdpException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts discovery service
|
||||
*
|
||||
* @param address - multicast group address
|
||||
* @param port - udp port to use
|
||||
* @param instanceName - name of running JVM instance
|
||||
* @param url - JMX service url
|
||||
* @throws IOException
|
||||
*/
|
||||
public static synchronized void startDiscoveryService(InetAddress address, int port, String instanceName, String url)
|
||||
throws IOException, JdpException {
|
||||
|
||||
// Limit packet to local subnet by default
|
||||
int ttl = getInteger(
|
||||
System.getProperty("com.sun.management.jdp.ttl"), 1,
|
||||
"Invalid jdp packet ttl");
|
||||
|
||||
// Broadcast once a 5 seconds by default
|
||||
int pause = getInteger(
|
||||
System.getProperty("com.sun.management.jdp.pause"), 5,
|
||||
"Invalid jdp pause");
|
||||
|
||||
// Converting seconds to milliseconds
|
||||
pause = pause * 1000;
|
||||
|
||||
// Allow OS to choose broadcast source
|
||||
InetAddress sourceAddress = getInetAddress(
|
||||
System.getProperty("com.sun.management.jdp.source_addr"), null,
|
||||
"Invalid source address provided");
|
||||
|
||||
// Generate session id
|
||||
UUID id = UUID.randomUUID();
|
||||
|
||||
JdpJmxPacket packet = new JdpJmxPacket(id, url);
|
||||
|
||||
// Don't broadcast whole command line for security reason.
|
||||
// Strip everything after first space
|
||||
String javaCommand = System.getProperty("sun.java.command");
|
||||
if (javaCommand != null) {
|
||||
String[] arr = javaCommand.split(" ", 2);
|
||||
packet.setMainClass(arr[0]);
|
||||
}
|
||||
|
||||
// Put optional explicit java instance name to packet, if user doesn't specify
|
||||
// it the key is skipped. PacketWriter is responsible to skip keys having null value.
|
||||
packet.setInstanceName(instanceName);
|
||||
|
||||
JdpBroadcaster bcast = new JdpBroadcaster(address, sourceAddress, port, ttl);
|
||||
|
||||
// Stop discovery service if it's already running
|
||||
stopDiscoveryService();
|
||||
|
||||
controller = new JDPControllerRunner(bcast, packet, pause);
|
||||
|
||||
Thread t = new Thread(controller, "JDP broadcaster");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop running discovery service,
|
||||
* it's safe to attempt to stop not started service
|
||||
*/
|
||||
public static synchronized void stopDiscoveryService() {
|
||||
if ( controller != null ){
|
||||
controller.stop();
|
||||
controller = null;
|
||||
}
|
||||
}
|
||||
}
|
40
jdk/src/share/classes/sun/management/jdp/JdpException.java
Normal file
40
jdk/src/share/classes/sun/management/jdp/JdpException.java
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
/**
|
||||
* An Exception thrown if a JDP implementation encounters a problem.
|
||||
*/
|
||||
public final class JdpException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Construct a new JDP exception with a meaningful message
|
||||
*
|
||||
* @param msg - message
|
||||
*/
|
||||
public JdpException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
/**
|
||||
* JdpGenericPacket responsible to provide fields
|
||||
* common for all Jdp packets
|
||||
*/
|
||||
public abstract class JdpGenericPacket implements JdpPacket {
|
||||
|
||||
/**
|
||||
* JDP protocol magic. Magic allows a reader to quickly select
|
||||
* JDP packets from a bunch of broadcast packets addressed to the same port
|
||||
* and broadcast group. Any packet intended to be parsed by JDP client
|
||||
* has to start from this magic.
|
||||
*/
|
||||
private static final int MAGIC = 0xC0FFEE42;
|
||||
|
||||
/**
|
||||
* Current version of protocol. Any implementation of this protocol has to
|
||||
* conform with the packet structure and the flow described in JEP-168
|
||||
*/
|
||||
private static final short PROTOCOL_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Default do-nothing constructor
|
||||
*/
|
||||
protected JdpGenericPacket(){
|
||||
// do nothing
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate protocol header magic field
|
||||
*
|
||||
* @param magic - value to validate
|
||||
* @throws JdpException
|
||||
*/
|
||||
public static void checkMagic(int magic)
|
||||
throws JdpException {
|
||||
if (magic != MAGIC) {
|
||||
throw new JdpException("Invalid JDP magic header: " + magic);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate protocol header version field
|
||||
*
|
||||
* @param version - value to validate
|
||||
* @throws JdpException
|
||||
*/
|
||||
public static void checkVersion(short version)
|
||||
throws JdpException {
|
||||
|
||||
if (version > PROTOCOL_VERSION) {
|
||||
throw new JdpException("Unsupported protocol version: " + version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return protocol magic
|
||||
*/
|
||||
public static int getMagic() {
|
||||
return MAGIC;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return current protocol version
|
||||
*/
|
||||
public static short getVersion() {
|
||||
return PROTOCOL_VERSION;
|
||||
}
|
||||
}
|
196
jdk/src/share/classes/sun/management/jdp/JdpJmxPacket.java
Normal file
196
jdk/src/share/classes/sun/management/jdp/JdpJmxPacket.java
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A packet to broadcasts JMX URL
|
||||
*
|
||||
* Fields:
|
||||
*
|
||||
* <ul>
|
||||
* <li>UUID - broadcast session ID, changed every time when we start/stop
|
||||
* discovery service</li>
|
||||
* <li>JMX_URL - URL to connect to JMX service</li>
|
||||
* <li>MAIN_CLASS - optional name of main class, filled from sun.java.command stripped for
|
||||
* security reason to first space</li>
|
||||
* <li>INSTANCE_NAME - optional custom name of particular instance as provided by customer</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class JdpJmxPacket
|
||||
extends JdpGenericPacket
|
||||
implements JdpPacket {
|
||||
|
||||
/**
|
||||
* Session ID
|
||||
*/
|
||||
public final static String UUID_KEY = "DISCOVERABLE_SESSION_UUID";
|
||||
/**
|
||||
* Name of main class
|
||||
*/
|
||||
public final static String MAIN_CLASS_KEY = "MAIN_CLASS";
|
||||
/**
|
||||
* JMX service URL
|
||||
*/
|
||||
public final static String JMX_SERVICE_URL_KEY = "JMX_SERVICE_URL";
|
||||
/**
|
||||
* Name of Java instance
|
||||
*/
|
||||
public final static String INSTANCE_NAME_KEY = "INSTANCE_NAME";
|
||||
|
||||
private UUID id;
|
||||
private String mainClass;
|
||||
private String jmxServiceUrl;
|
||||
private String instanceName;
|
||||
|
||||
/**
|
||||
* Create new instance from user provided data. Set mandatory fields
|
||||
*
|
||||
* @param id - java instance id
|
||||
* @param jmxServiceUrl - JMX service url
|
||||
*/
|
||||
public JdpJmxPacket(UUID id, String jmxServiceUrl) {
|
||||
this.id = id;
|
||||
this.jmxServiceUrl = jmxServiceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new instance from network data Parse packet and set fields.
|
||||
*
|
||||
* @param data - raw packet data as it came from a Net
|
||||
* @throws JdpException
|
||||
*/
|
||||
public JdpJmxPacket(byte[] data)
|
||||
throws JdpException {
|
||||
JdpPacketReader reader;
|
||||
|
||||
reader = new JdpPacketReader(data);
|
||||
Map<String, String> p = reader.getDiscoveryDataAsMap();
|
||||
|
||||
String sId = p.get(UUID_KEY);
|
||||
this.id = (sId == null) ? null : UUID.fromString(sId);
|
||||
this.jmxServiceUrl = p.get(JMX_SERVICE_URL_KEY);
|
||||
this.mainClass = p.get(MAIN_CLASS_KEY);
|
||||
this.instanceName = p.get(INSTANCE_NAME_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set main class field
|
||||
*
|
||||
* @param mainClass - main class of running app
|
||||
*/
|
||||
public void setMainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set instance name field
|
||||
*
|
||||
* @param instanceName - name of instance as provided by customer
|
||||
*/
|
||||
public void setInstanceName(String instanceName) {
|
||||
this.instanceName = instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return id of discovery session
|
||||
*/
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return main class field
|
||||
*/
|
||||
public String getMainClass() {
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return JMX service URL
|
||||
*/
|
||||
public String getJmxServiceUrl() {
|
||||
return jmxServiceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return instance name
|
||||
*/
|
||||
public String getInstanceName() {
|
||||
return instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return assembled packet ready to be sent across a Net
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public byte[] getPacketData() throws IOException {
|
||||
// Assemble packet from fields to byte array
|
||||
JdpPacketWriter writer;
|
||||
writer = new JdpPacketWriter();
|
||||
writer.addEntry(UUID_KEY, (id == null) ? null : id.toString());
|
||||
writer.addEntry(MAIN_CLASS_KEY, mainClass);
|
||||
writer.addEntry(JMX_SERVICE_URL_KEY, jmxServiceUrl);
|
||||
writer.addEntry(INSTANCE_NAME_KEY, instanceName);
|
||||
return writer.getPacketBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return packet hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 1;
|
||||
hash = hash * 31 + id.hashCode();
|
||||
hash = hash * 31 + jmxServiceUrl.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two packets
|
||||
*
|
||||
* @param o - packet to compare
|
||||
* @return either packet equals or not
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (o == null || ! (o instanceof JdpJmxPacket) ){
|
||||
return false;
|
||||
}
|
||||
|
||||
JdpJmxPacket p = (JdpJmxPacket) o;
|
||||
return Objects.equals(id, p.getId()) && Objects.equals(jmxServiceUrl, p.getJmxServiceUrl());
|
||||
}
|
||||
}
|
63
jdk/src/share/classes/sun/management/jdp/JdpPacket.java
Normal file
63
jdk/src/share/classes/sun/management/jdp/JdpPacket.java
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.management.jdp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Packet to broadcast
|
||||
*
|
||||
* <p>Each packet have to contain MAGIC and PROTOCOL_VERSION in order to be
|
||||
* recognized as a valid JDP packet.</p>
|
||||
*
|
||||
* <p>Default implementation build packet as a set of UTF-8 encoded Key/Value pairs
|
||||
* are stored as an ordered list of values, and are sent to the server
|
||||
* in that order.</p>
|
||||
*
|
||||
* <p>
|
||||
* Packet structure:
|
||||
*
|
||||
* 4 bytes JDP magic (0xC0FFE42)
|
||||
* 2 bytes JDP protocol version (01)
|
||||
*
|
||||
* 2 bytes size of key
|
||||
* x bytes key (UTF-8 encoded)
|
||||
* 2 bytes size of value
|
||||
* x bytes value (UTF-8 encoded)
|
||||
*
|
||||
* repeat as many times as necessary ...
|
||||
* </p>
|
||||
*/
|
||||
public interface JdpPacket {
|
||||
|
||||
/**
|
||||
* This method responsible to assemble packet and return a byte array
|
||||
* ready to be sent across a Net.
|
||||
*
|
||||
* @return assembled packet as an array of bytes
|
||||
* @throws IOException
|
||||
*/
|
||||
public byte[] getPacketData() throws IOException;
|
||||
|
||||
}
|
139
jdk/src/share/classes/sun/management/jdp/JdpPacketReader.java
Normal file
139
jdk/src/share/classes/sun/management/jdp/JdpPacketReader.java
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JdpPacketReader responsible for reading a packet <p>This class gets a byte
|
||||
* array as it came from a Net, validates it and breaks a part </p>
|
||||
*/
|
||||
public final class JdpPacketReader {
|
||||
|
||||
private final DataInputStream pkt;
|
||||
private Map<String, String> pmap = null;
|
||||
|
||||
/**
|
||||
* Create packet reader, extract and check magic and version
|
||||
*
|
||||
* @param packet - packet received from a Net
|
||||
* @throws JdpException
|
||||
*/
|
||||
public JdpPacketReader(byte[] packet)
|
||||
throws JdpException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(packet);
|
||||
pkt = new DataInputStream(bais);
|
||||
|
||||
try {
|
||||
int magic = pkt.readInt();
|
||||
JdpGenericPacket.checkMagic(magic);
|
||||
} catch (IOException e) {
|
||||
throw new JdpException("Invalid JDP packet received, bad magic");
|
||||
}
|
||||
|
||||
try {
|
||||
short version = pkt.readShort();
|
||||
JdpGenericPacket.checkVersion(version);
|
||||
} catch (IOException e) {
|
||||
throw new JdpException("Invalid JDP packet received, bad protocol version");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next entry from packet
|
||||
*
|
||||
* @return the entry
|
||||
* @throws EOFException
|
||||
* @throws JdpException
|
||||
*/
|
||||
public String getEntry()
|
||||
throws EOFException, JdpException {
|
||||
|
||||
try {
|
||||
short len = pkt.readShort();
|
||||
// Artificial setting the "len" field to Short.MAX_VALUE may cause a reader to allocate
|
||||
// to much memory. Prevent this possible DOS attack.
|
||||
if (len < 1 && len > pkt.available()) {
|
||||
throw new JdpException("Broken JDP packet. Invalid entry length field.");
|
||||
}
|
||||
|
||||
byte[] b = new byte[len];
|
||||
if (pkt.read(b) != len) {
|
||||
throw new JdpException("Broken JDP packet. Unable to read entry.");
|
||||
}
|
||||
return new String(b, "UTF-8");
|
||||
|
||||
} catch (EOFException e) {
|
||||
throw e;
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new JdpException("Broken JDP packet. Unable to decode entry.");
|
||||
} catch (IOException e) {
|
||||
throw new JdpException("Broken JDP packet. Unable to read entry.");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* return packet content as a key/value map
|
||||
*
|
||||
* @return map containing packet entries pair of entries treated as
|
||||
* key,value
|
||||
* @throws IOException
|
||||
* @throws JdpException
|
||||
*/
|
||||
public Map<String, String> getDiscoveryDataAsMap()
|
||||
throws JdpException {
|
||||
// return cached map if possible
|
||||
if (pmap != null) {
|
||||
return pmap;
|
||||
}
|
||||
|
||||
String key = null, value = null;
|
||||
|
||||
final Map<String, String> tmpMap = new HashMap<>();
|
||||
try {
|
||||
while (true) {
|
||||
key = getEntry();
|
||||
value = getEntry();
|
||||
tmpMap.put(key, value);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
// EOF reached on reading value, report broken packet
|
||||
// otherwise ignore it.
|
||||
if (value == null) {
|
||||
throw new JdpException("Broken JDP packet. Key without value." + key);
|
||||
}
|
||||
}
|
||||
|
||||
pmap = Collections.unmodifiableMap(tmpMap);
|
||||
return pmap;
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.management.jdp;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JdpPacketWriter responsible for writing a packet
|
||||
* <p>This class assembles a set of key/value pairs to valid JDP packet,
|
||||
* ready to be sent across a Net</p>
|
||||
*/
|
||||
public final class JdpPacketWriter {
|
||||
|
||||
private final ByteArrayOutputStream baos;
|
||||
private final DataOutputStream pkt;
|
||||
|
||||
/**
|
||||
* Create a JDP packet, add mandatory magic and version headers
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public JdpPacketWriter()
|
||||
throws IOException {
|
||||
baos = new ByteArrayOutputStream();
|
||||
pkt = new DataOutputStream(baos);
|
||||
|
||||
pkt.writeInt(JdpGenericPacket.getMagic());
|
||||
pkt.writeShort(JdpGenericPacket.getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Put string entry to packet
|
||||
*
|
||||
* @param entry - string to put (utf-8 encoded)
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addEntry(String entry)
|
||||
throws IOException {
|
||||
pkt.writeShort(entry.length());
|
||||
byte[] b = entry.getBytes("UTF-8");
|
||||
pkt.write(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put key/value pair to packet
|
||||
*
|
||||
* @param key - key to put (utf-8 encoded)
|
||||
* @param val - value to put (utf-8 encoded)
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addEntry(String key, String val)
|
||||
throws IOException {
|
||||
/* Silently skip key if value is null.
|
||||
* We don't need to distinguish between key missing
|
||||
* and key has no value cases
|
||||
*/
|
||||
if (val != null) {
|
||||
addEntry(key);
|
||||
addEntry(val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return assembled packet as a byte array
|
||||
*
|
||||
* @return packet bytes
|
||||
*/
|
||||
public byte[] getPacketBytes() {
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
55
jdk/src/share/classes/sun/management/jdp/package-info.java
Normal file
55
jdk/src/share/classes/sun/management/jdp/package-info.java
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Summary
|
||||
* -------
|
||||
*
|
||||
* Define a lightweight network protocol for discovering running and
|
||||
* manageable Java processes within a network subnet.
|
||||
*
|
||||
*
|
||||
* Description
|
||||
* -----------
|
||||
*
|
||||
* The protocol is lightweight multicast based, and works like a beacon,
|
||||
* broadcasting the JMXService URL needed to connect to the external JMX
|
||||
* agent if an application is started with appropriate parameters.
|
||||
*
|
||||
* The payload is structured like this:
|
||||
*
|
||||
* 4 bytes JDP magic (0xC0FFEE42)
|
||||
* 2 bytes JDP protocol version (1)
|
||||
* 2 bytes size of the next entry
|
||||
* x bytes next entry (UTF-8 encoded)
|
||||
* 2 bytes size of next entry
|
||||
* ... Rinse and repeat...
|
||||
*
|
||||
* The payload will be parsed as even entries being keys, odd entries being
|
||||
* values.
|
||||
*
|
||||
* The standard JDP packet contains four entries:
|
||||
*
|
||||
* - `DISCOVERABLE_SESSION_UUID` -- Unique id of the instance; this id changes every time
|
||||
* the discovery protocol starts and stops
|
||||
*
|
||||
* - `MAIN_CLASS` -- The value of the `sun.java.command` property
|
||||
*
|
||||
* - `JMX_SERVICE_URL` -- The URL to connect to the JMX agent
|
||||
*
|
||||
* - `INSTANCE_NAME` -- The user-provided name of the running instance
|
||||
*
|
||||
* The protocol sends packets to 239.255.255.225:7095 by default.
|
||||
*
|
||||
* The protocol uses system properties to control it's behaviour:
|
||||
* - `com.sun.management.jdp.port` -- override default port
|
||||
*
|
||||
* - `com.sun.management.jdp.address` -- override default address
|
||||
*
|
||||
* - `com.sun.management.jmxremote.autodiscovery` -- whether we should start autodiscovery or
|
||||
* not. Autodiscovery starts if and only if following conditions are met: (autodiscovery is
|
||||
* true OR (autodiscovery is not set AND jdp.port is set))
|
||||
*
|
||||
* - `com.sun.management.jdp.ttl` -- set ttl for broadcast packet, default is 1
|
||||
* - `com.sun.management.jdp.pause` -- set broadcast interval in seconds default is 5
|
||||
* - `com.sun.management.jdp.source_addr` -- an address of interface to use for broadcast
|
||||
*/
|
||||
|
||||
package sun.management.jdp;
|
160
jdk/test/sun/management/jdp/JdpClient.java
Normal file
160
jdk/test/sun/management/jdp/JdpClient.java
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.ProtocolFamily;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import sun.management.jdp.JdpException;
|
||||
import sun.management.jdp.JdpJmxPacket;
|
||||
|
||||
public class JdpClient {
|
||||
|
||||
private static class PacketListener implements Runnable {
|
||||
|
||||
private static final int BUFFER_LENGTH = 4096;
|
||||
private final DatagramChannel channel;
|
||||
private static int maxPacketCount = 1;
|
||||
private static int maxEmptyPacketCount = 10;
|
||||
|
||||
|
||||
PacketListener(DatagramChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public void run() {
|
||||
try {
|
||||
Selector sel;
|
||||
sel = Selector.open();
|
||||
channel.configureBlocking(false);
|
||||
channel.register(sel, SelectionKey.OP_READ);
|
||||
ByteBuffer buf = ByteBuffer.allocate(1024);
|
||||
|
||||
int count = 1;
|
||||
int emptyPacketsCount = 1;
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
|
||||
sel.selectedKeys().clear();
|
||||
buf.rewind();
|
||||
|
||||
sel.select(10 * 1000);
|
||||
channel.receive(buf);
|
||||
|
||||
if (buf.position() == 0 ){
|
||||
if (JdpDoSomething.getVerbose()){
|
||||
System.err.println("Empty packet received");
|
||||
}
|
||||
if (++emptyPacketsCount > maxEmptyPacketCount){
|
||||
throw new RuntimeException("Test failed, maxEmptyPacketCount reached");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.flip();
|
||||
byte[] dgramData = new byte[buf.remaining()];
|
||||
buf.get(dgramData);
|
||||
|
||||
try {
|
||||
JdpJmxPacket packet = new JdpJmxPacket(dgramData);
|
||||
JdpDoSomething.printJdpPacket(packet);
|
||||
if(++count > maxPacketCount){
|
||||
break;
|
||||
}
|
||||
} catch (JdpException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Test failed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
System.out.println("OK: Test passed");
|
||||
|
||||
} finally {
|
||||
sel.close();
|
||||
channel.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Test failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
String discoveryPort = System.getProperty("com.sun.management.jdp.port");
|
||||
String discoveryAddress = System.getProperty("com.sun.management.jdp.address");
|
||||
if (discoveryAddress == null || discoveryPort == null) {
|
||||
System.out.println("Test failed. address and port must be specified");
|
||||
return;
|
||||
}
|
||||
|
||||
int port = Integer.parseInt(discoveryPort);
|
||||
InetAddress address = InetAddress.getByName(discoveryAddress);
|
||||
|
||||
|
||||
ProtocolFamily family = (address instanceof Inet6Address)
|
||||
? StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
|
||||
|
||||
DatagramChannel channel;
|
||||
|
||||
channel = DatagramChannel.open(family);
|
||||
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
channel.bind(new InetSocketAddress(port));
|
||||
|
||||
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
|
||||
for (NetworkInterface interf : Collections.list(nets)) {
|
||||
if (interf.supportsMulticast()) {
|
||||
try {
|
||||
channel.join(address, interf);
|
||||
} catch (IOException e) {
|
||||
// Skip not configured interfaces
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PacketListener listener = new PacketListener(channel);
|
||||
new Thread(listener, "Jdp Client").start();
|
||||
|
||||
} catch (RuntimeException e){
|
||||
System.out.println("Test failed.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Test failed. unexpected error " + e);
|
||||
}
|
||||
}
|
||||
}
|
103
jdk/test/sun/management/jdp/JdpDoSomething.java
Normal file
103
jdk/test/sun/management/jdp/JdpDoSomething.java
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Objects;
|
||||
|
||||
import sun.management.jdp.JdpJmxPacket;
|
||||
import sun.management.jdp.JdpException;
|
||||
|
||||
public class JdpDoSomething {
|
||||
|
||||
private static final String lockFileName = "JdpDoSomething.lck";
|
||||
private static final boolean verbose=false;
|
||||
|
||||
public static boolean getVerbose(){
|
||||
return verbose;
|
||||
}
|
||||
|
||||
public static void printJdpPacket(JdpJmxPacket p) {
|
||||
if (getVerbose()) {
|
||||
try {
|
||||
RandomAccessFile f = new RandomAccessFile("out.dmp", "rw");
|
||||
f.write(p.getPacketData());
|
||||
f.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println("Can't write a dump file: " + e);
|
||||
}
|
||||
|
||||
System.out.println("Id: " + p.getId());
|
||||
System.out.println("Jmx: " + p.getJmxServiceUrl());
|
||||
System.out.println("Main: " + p.getMainClass());
|
||||
System.out.println("InstanceName: " + p.getInstanceName());
|
||||
|
||||
System.out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public static void compaireJdpPacketEx(JdpJmxPacket p1, JdpJmxPacket p2)
|
||||
throws JdpException {
|
||||
|
||||
if (!Objects.equals(p1, p1)) {
|
||||
throw new JdpException("Packet mismatch error");
|
||||
}
|
||||
|
||||
if (!Objects.equals(p1.getMainClass(), p2.getMainClass())) {
|
||||
throw new JdpException("Packet mismatch error (main class)");
|
||||
}
|
||||
|
||||
if (!Objects.equals(p1.getInstanceName(), p2.getInstanceName())) {
|
||||
throw new JdpException("Packet mismatch error (instance name)");
|
||||
}
|
||||
}
|
||||
|
||||
public static void doSomething() {
|
||||
try {
|
||||
File lockFile = new File(lockFileName);
|
||||
lockFile.createNewFile();
|
||||
|
||||
while (lockFile.exists()) {
|
||||
long datetime = lockFile.lastModified();
|
||||
long epoch = System.currentTimeMillis() / 1000;
|
||||
|
||||
// Don't allow test app to run more than an hour
|
||||
if (epoch - datetime > 3600) {
|
||||
System.err.println("Lock is too old. Aborting");
|
||||
return;
|
||||
}
|
||||
Thread.sleep(1);
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
System.err.println("Something bad happens:" + e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
System.err.println("main enter");
|
||||
doSomething();
|
||||
System.err.println("main exit");
|
||||
}
|
||||
}
|
323
jdk/test/sun/management/jdp/JdpTest.sh
Normal file
323
jdk/test/sun/management/jdp/JdpTest.sh
Normal file
@ -0,0 +1,323 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# version 2 for more details (a copy is included in the LICENSE file that
|
||||
# accompanied this code).
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License version
|
||||
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
|
||||
# @test
|
||||
# @bug 7169888
|
||||
# @build JdpUnitTest JdpClient JdpDoSomething
|
||||
# @run shell JdpTest.sh --jtreg --no-compile
|
||||
# @summary No word Failed expected in the test output
|
||||
|
||||
_verbose=no
|
||||
_jtreg=no
|
||||
_compile=yes
|
||||
|
||||
# temporary disable jcmd related tests
|
||||
# _testsuite="01,02,03,04,05"
|
||||
_testsuite="01,02,04"
|
||||
|
||||
_pwd=`pwd`
|
||||
|
||||
_testclasses=".classes"
|
||||
_testsrc="${_pwd}"
|
||||
_lockFileName="JdpDoSomething.lck"
|
||||
|
||||
_logname=".classes/output.txt"
|
||||
_last_pid=""
|
||||
|
||||
|
||||
_compile(){
|
||||
|
||||
if [ ! -e ${_testclasses} ]
|
||||
then
|
||||
mkdir -p ${_testclasses}
|
||||
fi
|
||||
|
||||
rm -f ${_testclasses}/*.class
|
||||
|
||||
# Compile testcase
|
||||
${TESTJAVA}/bin/javac -d ${_testclasses} JdpUnitTest.java \
|
||||
JdpDoSomething.java \
|
||||
JdpClient.java
|
||||
|
||||
|
||||
if [ ! -e ${_testclasses}/JdpDoSomething.class -o ! -e ${_testclasses}/JdpClient.class -o ! -e ${_testclasses}/JdpUnitTest.class ]
|
||||
then
|
||||
echo "ERROR: Can't compile"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
_app_start(){
|
||||
|
||||
testappname=$1
|
||||
shift
|
||||
|
||||
${TESTJAVA}/bin/java -server $* -cp ${_testclasses} ${testappname} >> ${_logname} 2>&1 &
|
||||
_last_pid=$!
|
||||
|
||||
npid=`_get_pid`
|
||||
if [ "${npid}" = "" ]
|
||||
then
|
||||
echo "ERROR: Test app not started"
|
||||
if [ "${_jtreg}" = "yes" ]
|
||||
then
|
||||
exit -1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
_get_pid(){
|
||||
${TESTJAVA}/bin/jps | sed -n "/Jdp/s/ .*//p"
|
||||
}
|
||||
|
||||
_app_stop(){
|
||||
rm ${_lockFileName}
|
||||
|
||||
# wait until VM is actually shuts down
|
||||
while true
|
||||
do
|
||||
npid=`_get_pid`
|
||||
if [ "${npid}" = "" ]
|
||||
then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
_testme(){
|
||||
${TESTJAVA}/bin/java \
|
||||
-cp ${_testclasses} \
|
||||
$* \
|
||||
-Dcom.sun.management.jdp.port=7095 \
|
||||
-Dcom.sun.management.jdp.address=239.255.255.225 \
|
||||
JdpClient
|
||||
|
||||
}
|
||||
|
||||
|
||||
_jcmd(){
|
||||
${TESTJAVA}/bin/jcmd JdpDoSomething $* > /dev/null 2>/dev/null
|
||||
}
|
||||
|
||||
|
||||
_echo(){
|
||||
echo "$*"
|
||||
echo "$*" >> ${_logname}
|
||||
}
|
||||
|
||||
# ============= TESTS ======================================
|
||||
|
||||
test_01(){
|
||||
|
||||
_echo "**** Test one ****"
|
||||
|
||||
_app_start JdpUnitTest \
|
||||
-Dcom.sun.management.jdp.port=7095 \
|
||||
-Dcom.sun.management.jdp.address=239.255.255.225 \
|
||||
-Dcom.sun.management.jdp.pause=5
|
||||
|
||||
res=`_testme`
|
||||
|
||||
case "${res}" in
|
||||
OK*)
|
||||
_echo "Passed"
|
||||
;;
|
||||
*)
|
||||
_echo "Failed!"
|
||||
;;
|
||||
esac
|
||||
|
||||
_app_stop
|
||||
}
|
||||
|
||||
test_02(){
|
||||
|
||||
_echo "**** Test two ****"
|
||||
|
||||
_app_start JdpDoSomething \
|
||||
-Dcom.sun.management.jdp.port=7095 \
|
||||
-Dcom.sun.management.jdp.address=239.255.255.225 \
|
||||
-Dcom.sun.management.jdp.pause=5 \
|
||||
-Dcom.sun.management.jmxremote.port=4545 \
|
||||
-Dcom.sun.management.jmxremote.authenticate=false \
|
||||
-Dcom.sun.management.jmxremote.ssl=false
|
||||
|
||||
res=`_testme`
|
||||
|
||||
case "${res}" in
|
||||
OK*)
|
||||
_echo "Passed"
|
||||
;;
|
||||
*)
|
||||
_echo "Failed!"
|
||||
;;
|
||||
esac
|
||||
|
||||
_app_stop
|
||||
}
|
||||
|
||||
test_03(){
|
||||
|
||||
_echo "**** Test three ****"
|
||||
|
||||
_app_start JdpDoSomething
|
||||
|
||||
_jcmd ManagementAgent.start\
|
||||
jdp.port=7095 \
|
||||
jdp.address=239.255.255.225 \
|
||||
jdp.pause=5 \
|
||||
jmxremote.port=4545 \
|
||||
jmxremote.authenticate=false \
|
||||
jmxremote.ssl=false
|
||||
|
||||
res=`_testme`
|
||||
|
||||
case "${res}" in
|
||||
OK*)
|
||||
_echo "Passed"
|
||||
;;
|
||||
*)
|
||||
_echo "Failed!"
|
||||
;;
|
||||
esac
|
||||
|
||||
_app_stop
|
||||
}
|
||||
|
||||
test_04(){
|
||||
|
||||
_echo "**** Test four ****"
|
||||
|
||||
_app_start JdpDoSomething \
|
||||
-Dcom.sun.management.jmxremote.autodiscovery=true \
|
||||
-Dcom.sun.management.jmxremote.port=4545 \
|
||||
-Dcom.sun.management.jmxremote.authenticate=false \
|
||||
-Dcom.sun.management.jmxremote.ssl=false
|
||||
|
||||
res=`_testme`
|
||||
|
||||
case "${res}" in
|
||||
OK*)
|
||||
_echo "Passed"
|
||||
;;
|
||||
*)
|
||||
_echo "Failed!"
|
||||
;;
|
||||
esac
|
||||
|
||||
_app_stop
|
||||
}
|
||||
|
||||
test_05(){
|
||||
|
||||
_echo "**** Test five ****"
|
||||
|
||||
_app_start JdpDoSomething
|
||||
|
||||
_jcmd ManagementAgent.start\
|
||||
jmxremote.autodiscovery=true \
|
||||
jmxremote.port=4545 \
|
||||
jmxremote.authenticate=false \
|
||||
jmxremote.ssl=false
|
||||
|
||||
|
||||
res=`_testme`
|
||||
|
||||
case "${res}" in
|
||||
OK*)
|
||||
_echo "Passed"
|
||||
;;
|
||||
*)
|
||||
_echo "Failed!"
|
||||
;;
|
||||
esac
|
||||
|
||||
_app_stop
|
||||
}
|
||||
|
||||
|
||||
# ============= MAIN =======================================
|
||||
|
||||
if [ "x${TESTJAVA}" = "x" ]
|
||||
then
|
||||
echo "TESTJAVA env have to be set"
|
||||
exit
|
||||
fi
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# reading parameters
|
||||
|
||||
for parm in "$@"
|
||||
do
|
||||
case $parm in
|
||||
--verbose) _verbose=yes ;;
|
||||
--jtreg) _jtreg=yes ;;
|
||||
--no-compile) _compile=no ;;
|
||||
--testsuite=*) _testsuite=`_echo $parm | sed "s,^--.*=\(.*\),\1,"` ;;
|
||||
*)
|
||||
echo "Undefined parameter $parm. Try --help for help"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ${_compile} = "yes" ]
|
||||
then
|
||||
_compile
|
||||
fi
|
||||
|
||||
if [ ${_jtreg} = "yes" ]
|
||||
then
|
||||
_testclasses=${TESTCLASSES}
|
||||
_testsrc=${TESTSRC}
|
||||
_logname="output.txt"
|
||||
fi
|
||||
|
||||
# Make sure _tesclasses is absolute path
|
||||
tt=`echo ${_testclasses} | sed -e 's,/,,'`
|
||||
if [ ${tt} = ${_testclasses} ]
|
||||
then
|
||||
_testclasses="${_pwd}/${_testclasses}"
|
||||
fi
|
||||
|
||||
_policyname="${_testclasses}/policy"
|
||||
|
||||
rm -f ${_logname}
|
||||
rm -f ${_policyname}
|
||||
|
||||
if [ -e ${_testsrc}/policy.tpl ]
|
||||
then
|
||||
|
||||
cat ${_testsrc}/policy.tpl | \
|
||||
sed -e "s,@_TESTCLASSES@,${_testclasses},g" -e "s,@TESTJAVA@,${TESTJAVA},g" \
|
||||
> ${_policyname}
|
||||
|
||||
fi
|
||||
|
||||
# Local mode tests
|
||||
for i in `echo ${_testsuite} | sed -e "s/,/ /g"`
|
||||
do
|
||||
test_${i}
|
||||
done
|
90
jdk/test/sun/management/jdp/JdpUnitTest.java
Normal file
90
jdk/test/sun/management/jdp/JdpUnitTest.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import sun.management.jdp.JdpController;
|
||||
import sun.management.jdp.JdpPacket;
|
||||
import sun.management.jdp.JdpJmxPacket;
|
||||
import sun.management.jdp.JdpException;
|
||||
|
||||
public class JdpUnitTest {
|
||||
|
||||
/**
|
||||
* This test tests that complete packet is build correctly
|
||||
*/
|
||||
public static void PacketBuilderTest()
|
||||
throws IOException, JdpException {
|
||||
|
||||
/* Complete packet test */
|
||||
{
|
||||
JdpJmxPacket p1 = new JdpJmxPacket(UUID.randomUUID(), "fake://unit-test");
|
||||
p1.setMainClass("FakeUnitTest");
|
||||
p1.setInstanceName("Fake");
|
||||
byte[] b = p1.getPacketData();
|
||||
|
||||
JdpJmxPacket p2 = new JdpJmxPacket(b);
|
||||
JdpDoSomething.printJdpPacket(p1);
|
||||
JdpDoSomething.compaireJdpPacketEx(p1, p2);
|
||||
}
|
||||
|
||||
/*Missed field packet test*/
|
||||
{
|
||||
JdpJmxPacket p1 = new JdpJmxPacket(UUID.randomUUID(), "fake://unit-test");
|
||||
p1.setMainClass("FakeUnitTest");
|
||||
p1.setInstanceName(null);
|
||||
byte[] b = p1.getPacketData();
|
||||
|
||||
JdpJmxPacket p2 = new JdpJmxPacket(b);
|
||||
JdpDoSomething.printJdpPacket(p1);
|
||||
JdpDoSomething.compaireJdpPacketEx(p1, p2);
|
||||
}
|
||||
|
||||
System.out.println("OK: Test passed");
|
||||
|
||||
}
|
||||
|
||||
public static void startFakeDiscoveryService()
|
||||
throws IOException, JdpException {
|
||||
|
||||
String discoveryPort = System.getProperty("com.sun.management.jdp.port");
|
||||
String discoveryAddress = System.getProperty("com.sun.management.jdp.address");
|
||||
InetAddress address = InetAddress.getByName(discoveryAddress);
|
||||
int port = Integer.parseInt(discoveryPort);
|
||||
JdpController.startDiscoveryService(address, port, "FakeDiscovery", "fake://unit-test");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
PacketBuilderTest();
|
||||
startFakeDiscoveryService();
|
||||
JdpDoSomething.doSomething();
|
||||
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Test failed. unexpected error " + e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user