1/* 2 * Copyright (c) 2013, 2015, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.management.internal; 27 28import com.sun.management.DiagnosticCommandMBean; 29import java.lang.reflect.Constructor; 30import java.lang.reflect.InvocationTargetException; 31import java.security.Permission; 32import java.util.*; 33import javax.management.Attribute; 34import javax.management.AttributeList; 35import javax.management.AttributeNotFoundException; 36import javax.management.Descriptor; 37import javax.management.ImmutableDescriptor; 38import javax.management.InvalidAttributeValueException; 39import javax.management.ListenerNotFoundException; 40import javax.management.MBeanException; 41import javax.management.MBeanInfo; 42import javax.management.MBeanNotificationInfo; 43import javax.management.MBeanOperationInfo; 44import javax.management.MBeanParameterInfo; 45import javax.management.MalformedObjectNameException; 46import javax.management.Notification; 47import javax.management.NotificationFilter; 48import javax.management.NotificationListener; 49import javax.management.ObjectName; 50import javax.management.ReflectionException; 51import sun.management.ManagementFactoryHelper; 52import sun.management.NotificationEmitterSupport; 53import sun.management.VMManagement; 54 55/** 56 * Implementation class for the diagnostic commands subsystem. 57 * 58 * @since 1.8 59 */ 60public class DiagnosticCommandImpl extends NotificationEmitterSupport 61 implements DiagnosticCommandMBean { 62 63 private final VMManagement jvm; 64 private volatile Map<String, Wrapper> wrappers = null; 65 private static final String strClassName = "".getClass().getName(); 66 private static final String strArrayClassName = String[].class.getName(); 67 private final boolean isSupported; 68 private static DiagnosticCommandImpl diagCommandMBean = null; 69 70 static synchronized DiagnosticCommandMBean getDiagnosticCommandMBean() { 71 VMManagement jvm = ManagementFactoryHelper.getVMManagement(); 72 73 // Remote Diagnostic Commands may not be supported 74 if (diagCommandMBean == null && jvm.isRemoteDiagnosticCommandsSupported()) { 75 diagCommandMBean = new DiagnosticCommandImpl(jvm); 76 } 77 return diagCommandMBean; 78 } 79 80 @Override 81 public Object getAttribute(String attribute) throws AttributeNotFoundException, 82 MBeanException, ReflectionException { 83 throw new AttributeNotFoundException(attribute); 84 } 85 86 @Override 87 public void setAttribute(Attribute attribute) throws AttributeNotFoundException, 88 InvalidAttributeValueException, MBeanException, ReflectionException { 89 throw new AttributeNotFoundException(attribute.getName()); 90 } 91 92 @Override 93 public AttributeList getAttributes(String[] attributes) { 94 return new AttributeList(); 95 } 96 97 @Override 98 public AttributeList setAttributes(AttributeList attributes) { 99 return new AttributeList(); 100 } 101 102 private class Wrapper { 103 104 String name; 105 String cmd; 106 DiagnosticCommandInfo info; 107 Permission permission; 108 109 Wrapper(String name, String cmd, DiagnosticCommandInfo info) 110 throws InstantiationException { 111 this.name = name; 112 this.cmd = cmd; 113 this.info = info; 114 this.permission = null; 115 Exception cause = null; 116 if (info.getPermissionClass() != null) { 117 try { 118 Class<?> c = Class.forName(info.getPermissionClass()); 119 if (info.getPermissionAction() == null) { 120 try { 121 Constructor<?> constructor = c.getConstructor(String.class); 122 permission = (Permission) constructor.newInstance(info.getPermissionName()); 123 124 } catch (InstantiationException | IllegalAccessException 125 | IllegalArgumentException | InvocationTargetException 126 | NoSuchMethodException | SecurityException ex) { 127 cause = ex; 128 } 129 } 130 if (permission == null) { 131 try { 132 Constructor<?> constructor = c.getConstructor(String.class, String.class); 133 permission = (Permission) constructor.newInstance( 134 info.getPermissionName(), 135 info.getPermissionAction()); 136 } catch (InstantiationException | IllegalAccessException 137 | IllegalArgumentException | InvocationTargetException 138 | NoSuchMethodException | SecurityException ex) { 139 cause = ex; 140 } 141 } 142 } catch (ClassNotFoundException ex) { } 143 if (permission == null) { 144 InstantiationException iex = 145 new InstantiationException("Unable to instantiate required permission"); 146 iex.initCause(cause); 147 } 148 } 149 } 150 151 public String execute(String[] args) { 152 if (permission != null) { 153 SecurityManager sm = System.getSecurityManager(); 154 if (sm != null) { 155 sm.checkPermission(permission); 156 } 157 } 158 if(args == null) { 159 return executeDiagnosticCommand(cmd); 160 } else { 161 StringBuilder sb = new StringBuilder(); 162 sb.append(cmd); 163 for(int i=0; i<args.length; i++) { 164 if(args[i] == null) { 165 throw new IllegalArgumentException("Invalid null argument"); 166 } 167 sb.append(" "); 168 sb.append(args[i]); 169 } 170 return executeDiagnosticCommand(sb.toString()); 171 } 172 } 173 } 174 175 DiagnosticCommandImpl(VMManagement jvm) { 176 this.jvm = jvm; 177 isSupported = jvm.isRemoteDiagnosticCommandsSupported(); 178 } 179 180 private static class OperationInfoComparator implements Comparator<MBeanOperationInfo> { 181 @Override 182 public int compare(MBeanOperationInfo o1, MBeanOperationInfo o2) { 183 return o1.getName().compareTo(o2.getName()); 184 } 185 } 186 187 @Override 188 public MBeanInfo getMBeanInfo() { 189 SortedSet<MBeanOperationInfo> operations = new TreeSet<>(new OperationInfoComparator()); 190 Map<String, Wrapper> wrappersmap; 191 if (!isSupported) { 192 wrappersmap = Collections.emptyMap(); 193 } else { 194 try { 195 String[] command = getDiagnosticCommands(); 196 DiagnosticCommandInfo[] info = getDiagnosticCommandInfo(command); 197 MBeanParameterInfo stringArgInfo[] = new MBeanParameterInfo[]{ 198 new MBeanParameterInfo("arguments", strArrayClassName, 199 "Array of Diagnostic Commands Arguments and Options") 200 }; 201 wrappersmap = new HashMap<>(); 202 for (int i = 0; i < command.length; i++) { 203 String name = transform(command[i]); 204 try { 205 Wrapper w = new Wrapper(name, command[i], info[i]); 206 wrappersmap.put(name, w); 207 operations.add(new MBeanOperationInfo( 208 w.name, 209 w.info.getDescription(), 210 (w.info.getArgumentsInfo() == null 211 || w.info.getArgumentsInfo().isEmpty()) 212 ? null : stringArgInfo, 213 strClassName, 214 MBeanOperationInfo.ACTION_INFO, 215 commandDescriptor(w))); 216 } catch (InstantiationException ex) { 217 // If for some reasons the creation of a diagnostic command 218 // wrappers fails, the diagnostic command is just ignored 219 // and won't appear in the DynamicMBean 220 } 221 } 222 } catch (IllegalArgumentException | UnsupportedOperationException e) { 223 wrappersmap = Collections.emptyMap(); 224 } 225 } 226 wrappers = Collections.unmodifiableMap(wrappersmap); 227 HashMap<String, Object> map = new HashMap<>(); 228 map.put("immutableInfo", "false"); 229 map.put("interfaceClassName","com.sun.management.DiagnosticCommandMBean"); 230 map.put("mxbean", "false"); 231 Descriptor desc = new ImmutableDescriptor(map); 232 return new MBeanInfo( 233 this.getClass().getName(), 234 "Diagnostic Commands", 235 null, // attributes 236 null, // constructors 237 operations.toArray(new MBeanOperationInfo[operations.size()]), // operations 238 getNotificationInfo(), // notifications 239 desc); 240 } 241 242 @Override 243 public Object invoke(String actionName, Object[] params, String[] signature) 244 throws MBeanException, ReflectionException { 245 if (!isSupported) { 246 throw new UnsupportedOperationException(); 247 } 248 if (wrappers == null) { 249 getMBeanInfo(); 250 } 251 Wrapper w = wrappers.get(actionName); 252 if (w != null) { 253 if (w.info.getArgumentsInfo().isEmpty() 254 && (params == null || params.length == 0) 255 && (signature == null || signature.length == 0)) { 256 return w.execute(null); 257 } else if((params != null && params.length == 1) 258 && (signature != null && signature.length == 1 259 && signature[0] != null 260 && signature[0].compareTo(strArrayClassName) == 0)) { 261 return w.execute((String[]) params[0]); 262 } else { 263 throw new ReflectionException( 264 new NoSuchMethodException(actionName 265 + ": mismatched signature " 266 + (signature != null ? Arrays.toString(signature) : "[]") 267 + " or parameters")); 268 } 269 } else { 270 throw new ReflectionException( 271 new NoSuchMethodException("Method " + actionName 272 + " with signature " 273 + (signature != null ? Arrays.toString(signature) : "[]") 274 + " not found")); 275 } 276 } 277 278 private static String transform(String name) { 279 StringBuilder sb = new StringBuilder(); 280 boolean toLower = true; 281 boolean toUpper = false; 282 for (int i = 0; i < name.length(); i++) { 283 char c = name.charAt(i); 284 if (c == '.' || c == '_') { 285 toLower = false; 286 toUpper = true; 287 } else { 288 if (toUpper) { 289 toUpper = false; 290 sb.append(Character.toUpperCase(c)); 291 } else if(toLower) { 292 sb.append(Character.toLowerCase(c)); 293 } else { 294 sb.append(c); 295 } 296 } 297 } 298 return sb.toString(); 299 } 300 301 private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException { 302 HashMap<String, Object> map = new HashMap<>(); 303 map.put("dcmd.name", w.info.getName()); 304 map.put("dcmd.description", w.info.getDescription()); 305 map.put("dcmd.vmImpact", w.info.getImpact()); 306 map.put("dcmd.permissionClass", w.info.getPermissionClass()); 307 map.put("dcmd.permissionName", w.info.getPermissionName()); 308 map.put("dcmd.permissionAction", w.info.getPermissionAction()); 309 map.put("dcmd.enabled", w.info.isEnabled()); 310 StringBuilder sb = new StringBuilder(); 311 sb.append("help "); 312 sb.append(w.info.getName()); 313 map.put("dcmd.help", executeDiagnosticCommand(sb.toString())); 314 if (w.info.getArgumentsInfo() != null && !w.info.getArgumentsInfo().isEmpty()) { 315 HashMap<String, Object> allargmap = new HashMap<>(); 316 for (DiagnosticCommandArgumentInfo arginfo : w.info.getArgumentsInfo()) { 317 HashMap<String, Object> argmap = new HashMap<>(); 318 argmap.put("dcmd.arg.name", arginfo.getName()); 319 argmap.put("dcmd.arg.type", arginfo.getType()); 320 argmap.put("dcmd.arg.description", arginfo.getDescription()); 321 argmap.put("dcmd.arg.isMandatory", arginfo.isMandatory()); 322 argmap.put("dcmd.arg.isMultiple", arginfo.isMultiple()); 323 boolean isOption = arginfo.isOption(); 324 argmap.put("dcmd.arg.isOption", isOption); 325 if(!isOption) { 326 argmap.put("dcmd.arg.position", arginfo.getPosition()); 327 } else { 328 argmap.put("dcmd.arg.position", -1); 329 } 330 allargmap.put(arginfo.getName(), new ImmutableDescriptor(argmap)); 331 } 332 map.put("dcmd.arguments", new ImmutableDescriptor(allargmap)); 333 } 334 return new ImmutableDescriptor(map); 335 } 336 337 private final static String notifName = 338 "javax.management.Notification"; 339 340 private final static String[] diagFramNotifTypes = { 341 "jmx.mbean.info.changed" 342 }; 343 344 private MBeanNotificationInfo[] notifInfo = null; 345 346 @Override 347 public MBeanNotificationInfo[] getNotificationInfo() { 348 synchronized (this) { 349 if (notifInfo == null) { 350 notifInfo = new MBeanNotificationInfo[1]; 351 notifInfo[0] = 352 new MBeanNotificationInfo(diagFramNotifTypes, 353 notifName, 354 "Diagnostic Framework Notification"); 355 } 356 } 357 return notifInfo.clone(); 358 } 359 360 private static long seqNumber = 0; 361 private static long getNextSeqNumber() { 362 return ++seqNumber; 363 } 364 365 private void createDiagnosticFrameworkNotification() { 366 367 if (!hasListeners()) { 368 return; 369 } 370 ObjectName on = null; 371 try { 372 on = ObjectName.getInstance(PlatformMBeanProviderImpl.DIAGNOSTIC_COMMAND_MBEAN_NAME); 373 } catch (MalformedObjectNameException e) { } 374 Notification notif = new Notification("jmx.mbean.info.changed", 375 on, 376 getNextSeqNumber()); 377 notif.setUserData(getMBeanInfo()); 378 sendNotification(notif); 379 } 380 381 @Override 382 public synchronized void addNotificationListener(NotificationListener listener, 383 NotificationFilter filter, 384 Object handback) { 385 boolean before = hasListeners(); 386 super.addNotificationListener(listener, filter, handback); 387 boolean after = hasListeners(); 388 if (!before && after) { 389 setNotificationEnabled(true); 390 } 391 } 392 393 @Override 394 public synchronized void removeNotificationListener(NotificationListener listener) 395 throws ListenerNotFoundException { 396 boolean before = hasListeners(); 397 super.removeNotificationListener(listener); 398 boolean after = hasListeners(); 399 if (before && !after) { 400 setNotificationEnabled(false); 401 } 402 } 403 404 @Override 405 public synchronized void removeNotificationListener(NotificationListener listener, 406 NotificationFilter filter, 407 Object handback) 408 throws ListenerNotFoundException { 409 boolean before = hasListeners(); 410 super.removeNotificationListener(listener, filter, handback); 411 boolean after = hasListeners(); 412 if (before && !after) { 413 setNotificationEnabled(false); 414 } 415 } 416 417 private native void setNotificationEnabled(boolean enabled); 418 private native String[] getDiagnosticCommands(); 419 private native DiagnosticCommandInfo[] getDiagnosticCommandInfo(String[] commands); 420 private native String executeDiagnosticCommand(String command); 421 422} 423