1 /* 2 * Copyright (c) 2013, 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 26 package sun.management; 27 28 import com.sun.management.DiagnosticCommandMBean; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.InvocationTargetException; 31 import java.security.Permission; 32 import java.util.*; 33 import javax.management.*; 34 35 /** 36 * Implementation class for the diagnostic commands subsystem. 37 * 38 * @since 8 39 */ 40 class DiagnosticCommandImpl extends NotificationEmitterSupport 41 implements DiagnosticCommandMBean { 42 43 private final VMManagement jvm; 44 private volatile Map<String, Wrapper> wrappers = null; 45 private static final String strClassName = "".getClass().getName(); 46 private static final String strArrayClassName = String[].class.getName(); 47 private final boolean isSupported; 48 49 @Override 50 public Object getAttribute(String attribute) throws AttributeNotFoundException, 51 MBeanException, ReflectionException { 52 throw new AttributeNotFoundException(attribute); 53 } 54 55 @Override 56 public void setAttribute(Attribute attribute) throws AttributeNotFoundException, 57 InvalidAttributeValueException, MBeanException, ReflectionException { 58 throw new AttributeNotFoundException(attribute.getName()); 59 } 60 61 @Override 62 public AttributeList getAttributes(String[] attributes) { 63 return new AttributeList(); 64 } 65 66 @Override 67 public AttributeList setAttributes(AttributeList attributes) { 68 return new AttributeList(); 69 } 70 71 private class Wrapper { 72 73 String name; 74 String cmd; 75 DiagnosticCommandInfo info; 76 Permission permission; 77 78 Wrapper(String name, String cmd, DiagnosticCommandInfo info) 79 throws InstantiationException { 80 this.name = name; 81 this.cmd = cmd; 82 this.info = info; 83 this.permission = null; 84 Exception cause = null; 85 if (info.getPermissionClass() != null) { 86 try { 87 Class c = Class.forName(info.getPermissionClass()); 88 if (info.getPermissionAction() == null) { 89 try { 90 Constructor constructor = c.getConstructor(String.class); 91 permission = (Permission) constructor.newInstance(info.getPermissionName()); 92 93 } catch (InstantiationException | IllegalAccessException 94 | IllegalArgumentException | InvocationTargetException 95 | NoSuchMethodException | SecurityException ex) { 96 cause = ex; 97 } 98 } 99 if (permission == null) { 100 try { 101 Constructor constructor = c.getConstructor(String.class, String.class); 102 permission = (Permission) constructor.newInstance( 103 info.getPermissionName(), 104 info.getPermissionAction()); 105 } catch (InstantiationException | IllegalAccessException 106 | IllegalArgumentException | InvocationTargetException 107 | NoSuchMethodException | SecurityException ex) { 108 cause = ex; 109 } 110 } 111 } catch (ClassNotFoundException ex) { } 112 if (permission == null) { 113 InstantiationException iex = 114 new InstantiationException("Unable to instantiate required permission"); 115 iex.initCause(cause); 116 } 117 } 118 } 119 120 public String execute(String[] args) { 121 if (permission != null) { 122 SecurityManager sm = System.getSecurityManager(); 123 if (sm != null) { 124 sm.checkPermission(permission); 125 } 126 } 127 if(args == null) { 128 return executeDiagnosticCommand(cmd); 129 } else { 130 StringBuilder sb = new StringBuilder(); 131 sb.append(cmd); 132 for(int i=0; i<args.length; i++) { 133 if(args[i] == null) { 134 throw new IllegalArgumentException("Invalid null argument"); 135 } 136 sb.append(" "); 137 sb.append(args[i]); 138 } 139 return executeDiagnosticCommand(sb.toString()); 140 } 141 } 142 } 143 144 DiagnosticCommandImpl(VMManagement jvm) { 145 this.jvm = jvm; 146 isSupported = jvm.isRemoteDiagnosticCommandsSupported(); 147 } 148 149 private static class OperationInfoComparator implements Comparator<MBeanOperationInfo> { 150 @Override 151 public int compare(MBeanOperationInfo o1, MBeanOperationInfo o2) { 152 return o1.getName().compareTo(o2.getName()); 153 } 154 } 155 156 @Override 157 public MBeanInfo getMBeanInfo() { 158 SortedSet<MBeanOperationInfo> operations = new TreeSet<>(new OperationInfoComparator()); 159 Map<String, Wrapper> wrappersmap; 160 if (!isSupported) { 161 wrappersmap = (Map<String, Wrapper>) Collections.EMPTY_MAP; 162 } else { 163 try { 164 String[] command = getDiagnosticCommands(); 165 DiagnosticCommandInfo[] info = getDiagnosticCommandInfo(command); 166 MBeanParameterInfo stringArgInfo[] = new MBeanParameterInfo[]{ 167 new MBeanParameterInfo("arguments", strArrayClassName, 168 "Array of Diagnostic Commands Arguments and Options") 169 }; 170 wrappersmap = new HashMap<>(); 171 for (int i = 0; i < command.length; i++) { 172 String name = transform(command[i]); 173 try { 174 Wrapper w = new Wrapper(name, command[i], info[i]); 175 wrappersmap.put(name, w); 176 operations.add(new MBeanOperationInfo( 177 w.name, 178 w.info.getDescription(), 179 (w.info.getArgumentsInfo() == null 180 || w.info.getArgumentsInfo().isEmpty()) 181 ? null : stringArgInfo, 182 strClassName, 183 MBeanOperationInfo.ACTION_INFO, 184 commandDescriptor(w))); 185 } catch (InstantiationException ex) { 186 // If for some reasons the creation of a diagnostic command 187 // wrappers fails, the diagnostic command is just ignored 188 // and won't appear in the DynamicMBean 189 } 190 } 191 } catch (IllegalArgumentException | UnsupportedOperationException e) { 192 wrappersmap = (Map<String, Wrapper>) Collections.EMPTY_MAP; 193 } 194 } 195 wrappers = Collections.unmodifiableMap(wrappersmap); 196 HashMap<String, Object> map = new HashMap<>(); 197 map.put("immutableInfo", "false"); 198 map.put("interfaceClassName","com.sun.management.DiagnosticCommandMBean"); 199 map.put("mxbean", "false"); 200 Descriptor desc = new ImmutableDescriptor(map); 201 return new MBeanInfo( 202 this.getClass().getName(), 203 "Diagnostic Commands", 204 null, // attributes 205 null, // constructors 206 operations.toArray(new MBeanOperationInfo[operations.size()]), // operations 207 getNotificationInfo(), // notifications 208 desc); 209 } 210 211 @Override 212 public Object invoke(String actionName, Object[] params, String[] signature) 213 throws MBeanException, ReflectionException { 214 if (!isSupported) { 215 throw new UnsupportedOperationException(); 216 } 217 if (wrappers == null) { 218 getMBeanInfo(); 219 } 220 Wrapper w = wrappers.get(actionName); 221 if (w != null) { 222 if (w.info.getArgumentsInfo().isEmpty() 223 && (params == null || params.length == 0) 224 && (signature == null || signature.length == 0)) { 225 return w.execute(null); 226 } else if((params != null && params.length == 1) 227 && (signature != null && signature.length == 1 228 && signature[0] != null 229 && signature[0].compareTo(strArrayClassName) == 0)) { 230 return w.execute((String[]) params[0]); 231 } 232 } 233 throw new ReflectionException(new NoSuchMethodException(actionName)); 234 } 235 236 private static String transform(String name) { 237 StringBuilder sb = new StringBuilder(); 238 boolean toLower = true; 239 boolean toUpper = false; 240 for (int i = 0; i < name.length(); i++) { 241 char c = name.charAt(i); 242 if (c == '.' || c == '_') { 243 toLower = false; 244 toUpper = true; 245 } else { 246 if (toUpper) { 247 toUpper = false; 248 sb.append(Character.toUpperCase(c)); 249 } else if(toLower) { 250 sb.append(Character.toLowerCase(c)); 251 } else { 252 sb.append(c); 253 } 254 } 255 } 256 return sb.toString(); 257 } 258 259 private Descriptor commandDescriptor(Wrapper w) throws IllegalArgumentException { 260 HashMap<String, Object> map = new HashMap<>(); 261 map.put("dcmd.name", w.info.getName()); 262 map.put("dcmd.description", w.info.getDescription()); 263 map.put("dcmd.vmImpact", w.info.getImpact()); 264 map.put("dcmd.permissionClass", w.info.getPermissionClass()); 265 map.put("dcmd.permissionName", w.info.getPermissionName()); 266 map.put("dcmd.permissionAction", w.info.getPermissionAction()); 267 map.put("dcmd.enabled", w.info.isEnabled()); 268 StringBuilder sb = new StringBuilder(); 269 sb.append("help "); 270 sb.append(w.info.getName()); 271 map.put("dcmd.help", executeDiagnosticCommand(sb.toString())); 272 if (w.info.getArgumentsInfo() != null && !w.info.getArgumentsInfo().isEmpty()) { 273 HashMap<String, Object> allargmap = new HashMap<>(); 274 for (DiagnosticCommandArgumentInfo arginfo : w.info.getArgumentsInfo()) { 275 HashMap<String, Object> argmap = new HashMap<>(); 276 argmap.put("dcmd.arg.name", arginfo.getName()); 277 argmap.put("dcmd.arg.type", arginfo.getType()); 278 argmap.put("dcmd.arg.description", arginfo.getDescription()); 279 argmap.put("dcmd.arg.isMandatory", arginfo.isMandatory()); 280 argmap.put("dcmd.arg.isMultiple", arginfo.isMultiple()); 281 boolean isOption = arginfo.isOption(); 282 argmap.put("dcmd.arg.isOption", isOption); 283 if(!isOption) { 284 argmap.put("dcmd.arg.position", arginfo.getPosition()); 285 } else { 286 argmap.put("dcmd.arg.position", -1); 287 } 288 allargmap.put(arginfo.getName(), new ImmutableDescriptor(argmap)); 289 } 290 map.put("dcmd.arguments", new ImmutableDescriptor(allargmap)); 291 } 292 return new ImmutableDescriptor(map); 293 } 294 295 private final static String notifName = 296 "javax.management.Notification"; 297 298 private final static String[] diagFramNotifTypes = { 299 "jmx.mbean.info.changed" 300 }; 301 302 private MBeanNotificationInfo[] notifInfo = null; 303 304 @Override 305 public MBeanNotificationInfo[] getNotificationInfo() { 306 synchronized (this) { 307 if (notifInfo == null) { 308 notifInfo = new MBeanNotificationInfo[1]; 309 notifInfo[0] = 310 new MBeanNotificationInfo(diagFramNotifTypes, 311 notifName, 312 "Diagnostic Framework Notification"); 313 } 314 } 315 return notifInfo.clone(); 316 } 317 318 private static long seqNumber = 0; 319 private static long getNextSeqNumber() { 320 return ++seqNumber; 321 } 322 323 private void createDiagnosticFrameworkNotification() { 324 325 if (!hasListeners()) { 326 return; 327 } 328 ObjectName on = null; 329 try { 330 on = ObjectName.getInstance(ManagementFactoryHelper.HOTSPOT_DIAGNOSTIC_COMMAND_MBEAN_NAME); 331 } catch (MalformedObjectNameException e) { } 332 Notification notif = new Notification("jmx.mbean.info.changed", 333 on, 334 getNextSeqNumber()); 335 notif.setUserData(getMBeanInfo()); 336 sendNotification(notif); 337 } 338 339 @Override 340 public synchronized void addNotificationListener(NotificationListener listener, 341 NotificationFilter filter, 342 Object handback) { 343 boolean before = hasListeners(); 344 super.addNotificationListener(listener, filter, handback); 345 boolean after = hasListeners(); 346 if (!before && after) { 347 setNotificationEnabled(true); 348 } 349 } 350 351 @Override 352 public synchronized void removeNotificationListener(NotificationListener listener) 353 throws ListenerNotFoundException { 354 boolean before = hasListeners(); 355 super.removeNotificationListener(listener); 356 boolean after = hasListeners(); 357 if (before && !after) { 358 setNotificationEnabled(false); 359 } 360 } 361 362 @Override 363 public synchronized void removeNotificationListener(NotificationListener listener, 364 NotificationFilter filter, 365 Object handback) 366 throws ListenerNotFoundException { 367 boolean before = hasListeners(); 368 super.removeNotificationListener(listener, filter, handback); 369 boolean after = hasListeners(); 370 if (before && !after) { 371 setNotificationEnabled(false); 372 } 373 } 374 375 private native void setNotificationEnabled(boolean enabled); 376 private native String[] getDiagnosticCommands(); 377 private native DiagnosticCommandInfo[] getDiagnosticCommandInfo(String[] commands); 378 private native String executeDiagnosticCommand(String command); 379 380 }