1 /* 2 * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * A JVM with JDP on should send multicast JDP packets regularly. 26 * Look at JdpOnTestCase.java and JdpOffTestCase.java 27 */ 28 29 30 import sun.management.jdp.JdpJmxPacket; 31 32 import java.io.IOException; 33 import java.io.UnsupportedEncodingException; 34 import java.net.DatagramPacket; 35 import java.net.MulticastSocket; 36 import java.net.SocketTimeoutException; 37 import java.util.Arrays; 38 import java.util.Map; 39 import java.util.logging.Level; 40 import java.util.logging.Logger; 41 42 public abstract class JdpTestCase { 43 final Logger log = Logger.getLogger("sun.management.jdp"); 44 final int MAGIC = 0xC0FFEE42; // Jdp magic number. 45 private static final int BUFFER_LENGTH = 64 * 1024; // max UDP size, except for IPv6 jumbograms. 46 private final int TIME_OUT_FACTOR = 10; // Socket times out after 10 times the jdp pause. 47 protected int timeOut; 48 private long startTime; 49 protected ClientConnection connection; 50 JdpTestCase(ClientConnection connection)51 public JdpTestCase(ClientConnection connection) { 52 this.connection = connection; 53 JdpTestUtil.enableConsoleLogging(log, Level.ALL); 54 } 55 run()56 public void run() throws Exception { 57 log.fine("Test started."); 58 log.fine("Listening for multicast packets at " + connection.address.getHostAddress() 59 + ":" + String.valueOf(connection.port)); 60 log.fine(initialLogMessage()); 61 log.fine("Pause in between packets is: " + connection.pauseInSeconds + " seconds."); 62 63 startTime = System.currentTimeMillis(); 64 timeOut = connection.pauseInSeconds * TIME_OUT_FACTOR; 65 log.fine("Timeout set to " + String.valueOf(timeOut) + " seconds."); 66 67 MulticastSocket socket = connection.connectWithTimeout(timeOut * 1000); 68 69 byte[] buffer = new byte[BUFFER_LENGTH]; 70 DatagramPacket datagram = new DatagramPacket(buffer, buffer.length); 71 72 do { 73 try { 74 socket.receive(datagram); 75 onReceived(extractUDPpayload(datagram)); 76 } catch (SocketTimeoutException e) { 77 onSocketTimeOut(e); 78 } 79 80 if (!shouldContinue()) { 81 break; 82 } 83 84 if (hasTestLivedLongEnough()) { 85 shutdown(); 86 } 87 88 } while (true); 89 log.fine("Test ended successfully."); 90 } 91 92 /** 93 * Subclasses: JdpOnTestCase and JdpOffTestCase have different messages. 94 */ initialLogMessage()95 protected abstract String initialLogMessage(); 96 97 98 /** 99 * Executed when the socket receives a UDP packet. 100 */ onReceived(byte[] packet)101 private void onReceived(byte[] packet) throws Exception { 102 if (isJDP(packet)) { 103 Map<String, String> payload = checkStructure(packet); 104 jdpPacketReceived(payload); 105 } else { 106 log.fine("Non JDP packet received, ignoring it."); 107 } 108 } 109 110 /** 111 * Determine whether the test should end. 112 * 113 * @return 114 */ shouldContinue()115 abstract protected boolean shouldContinue(); 116 117 /** 118 * This method is executed when the socket has not received any packet for timeOut seconds. 119 */ onSocketTimeOut(SocketTimeoutException e)120 abstract protected void onSocketTimeOut(SocketTimeoutException e) throws Exception; 121 122 /** 123 * This method is executed after a correct Jdp packet has been received. 124 * 125 * @param payload A dictionary containing the data if the received Jdp packet. 126 */ jdpPacketReceived(Map<String, String> payload)127 private void jdpPacketReceived(Map<String, String> payload) throws Exception { 128 final String instanceName = payload.get("INSTANCE_NAME"); 129 if (instanceName != null && instanceName.equals(connection.instanceName)) { 130 packetFromThisVMReceived(payload); 131 } else { 132 packetFromOtherVMReceived(payload); 133 } 134 } 135 136 /** 137 * This method is executed after a correct Jdp packet, coming from this VM has been received. 138 * 139 * @param payload A dictionary containing the data if the received Jdp packet. 140 */ packetFromThisVMReceived(Map<String, String> payload)141 protected abstract void packetFromThisVMReceived(Map<String, String> payload) throws Exception; 142 143 144 /** 145 * This method is executed after a correct Jdp packet, coming from another VM has been received. 146 * 147 * @param payload A dictionary containing the data if the received Jdp packet. 148 */ packetFromOtherVMReceived(Map<String, String> payload)149 protected void packetFromOtherVMReceived(Map<String, String> payload) { 150 final String jdpName = payload.get("INSTANCE_NAME"); 151 log.fine("Ignoring JDP packet sent by other VM, jdp.name=" + jdpName); 152 } 153 154 155 /** 156 * The test should stop if it has been 12 times the jdp.pause. 157 * jdp.pause is how many seconds in between packets. 158 * <p/> 159 * This timeout (12 times)is slightly longer than the socket timeout (10 times) on purpose. 160 * In the off test case, the socket should time out first. 161 * 162 * @return 163 */ hasTestLivedLongEnough()164 protected boolean hasTestLivedLongEnough() { 165 long now = System.currentTimeMillis(); 166 boolean haslivedLongEnough = (now - startTime) > (timeOut * 1.2 * 1000); 167 return haslivedLongEnough; 168 } 169 170 /** 171 * This exit condition arises when we receive UDP packets but they are not valid Jdp. 172 */ shutdown()173 protected void shutdown() throws Exception { 174 log.severe("Shutting down the test."); 175 throw new Exception("Not enough JDP packets received before timeout!"); 176 } 177 178 /** 179 * Assert that this Jdp packet contains the required two keys. 180 * <p/> 181 * We expect zero packet corruption and thus fail on the first corrupted packet. 182 * This might need revision. 183 */ checkStructure(byte[] packet)184 protected Map<String, String> checkStructure(byte[] packet) throws UnsupportedEncodingException { 185 Map<String, String> payload = JdpTestUtil.readPayload(packet); 186 assertTrue(payload.size() >= 2, "JDP should have minimun 2 entries."); 187 assertTrue(payload.get(JdpJmxPacket.UUID_KEY).length() > 0); 188 assertTrue(payload.get(JdpJmxPacket.JMX_SERVICE_URL_KEY).length() > 0); 189 return payload; 190 } 191 192 193 /** 194 * Check if packet has correct JDP magic number. 195 * 196 * @param packet 197 * @return 198 * @throws IOException 199 */ isJDP(byte[] packet)200 private boolean isJDP(byte[] packet) throws IOException { 201 int magic = JdpTestUtil.decode4ByteInt(packet, 0); 202 return (magic == MAGIC); 203 } 204 extractUDPpayload(DatagramPacket datagram)205 private byte[] extractUDPpayload(DatagramPacket datagram) { 206 byte[] data = Arrays.copyOf(datagram.getData(), datagram.getLength()); 207 return data; 208 } 209 210 /** 211 * Hack until I find a way to use TestNG's assertions. 212 */ assertTrue(boolean assertion, String message)213 private void assertTrue(boolean assertion, String message) { 214 if (assertion == false) { 215 log.severe(message); 216 assert (false); 217 } 218 } 219 assertTrue(boolean assertion)220 private void assertTrue(boolean assertion) { 221 assertTrue(assertion, ""); 222 } 223 224 } 225