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