J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package sun.management.snmp; |
| 27 | |
| 28 | import com.sun.jmx.snmp.daemon.SnmpAdaptorServer; |
| 29 | import com.sun.jmx.snmp.InetAddressAcl; |
| 30 | import com.sun.jmx.snmp.IPAcl.SnmpAcl; |
| 31 | import sun.management.snmp.jvmmib.JVM_MANAGEMENT_MIB; |
| 32 | import sun.management.snmp.jvminstr.JVM_MANAGEMENT_MIB_IMPL; |
| 33 | import sun.management.snmp.jvminstr.NotificationTarget; |
| 34 | import sun.management.snmp.jvminstr.NotificationTargetImpl; |
| 35 | import sun.management.snmp.util.MibLogger; |
| 36 | import sun.management.snmp.util.JvmContextFactory; |
| 37 | |
| 38 | import sun.management.Agent; |
| 39 | import sun.management.AgentConfigurationError; |
| 40 | import static sun.management.AgentConfigurationError.*; |
| 41 | import sun.management.FileSystem; |
| 42 | |
| 43 | import java.util.List; |
| 44 | import java.util.ArrayList; |
| 45 | import java.util.Enumeration; |
| 46 | import java.util.Properties; |
| 47 | |
| 48 | import java.io.IOException; |
| 49 | import java.io.File; |
| 50 | import java.io.FileInputStream; |
| 51 | |
| 52 | import java.net.InetAddress; |
| 53 | import java.net.UnknownHostException; |
| 54 | |
| 55 | /** |
| 56 | * This class initializes and starts the SNMP Adaptor for JSR 163 SNMP |
| 57 | * Monitoring. |
| 58 | **/ |
| 59 | public final class AdaptorBootstrap { |
| 60 | |
| 61 | private static final MibLogger log = new MibLogger(AdaptorBootstrap.class); |
| 62 | |
| 63 | /** |
| 64 | * Default values for SNMP configuration properties. |
| 65 | **/ |
| 66 | public static interface DefaultValues { |
| 67 | public static final String PORT="161"; |
| 68 | public static final String CONFIG_FILE_NAME="management.properties"; |
| 69 | public static final String TRAP_PORT="162"; |
| 70 | public static final String USE_ACL="true"; |
| 71 | public static final String ACL_FILE_NAME="snmp.acl"; |
| 72 | public static final String BIND_ADDRESS="localhost"; |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Names of SNMP configuration properties. |
| 77 | **/ |
| 78 | public static interface PropertyNames { |
| 79 | public static final String PORT="com.sun.management.snmp.port"; |
| 80 | public static final String CONFIG_FILE_NAME= |
| 81 | "com.sun.management.config.file"; |
| 82 | public static final String TRAP_PORT= |
| 83 | "com.sun.management.snmp.trap"; |
| 84 | public static final String USE_ACL= |
| 85 | "com.sun.management.snmp.acl"; |
| 86 | public static final String ACL_FILE_NAME= |
| 87 | "com.sun.management.snmp.acl.file"; |
| 88 | public static final String BIND_ADDRESS= |
| 89 | "com.sun.management.snmp.interface"; |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * We keep a reference - so that we can possibly call |
| 94 | * terminate(). As of now, terminate() is only called by unit tests |
| 95 | * (makes it possible to run several testcases sequentially in the |
| 96 | * same JVM). |
| 97 | **/ |
| 98 | private SnmpAdaptorServer adaptor; |
| 99 | private JVM_MANAGEMENT_MIB_IMPL jvmmib; |
| 100 | |
| 101 | private AdaptorBootstrap(SnmpAdaptorServer snmpas, |
| 102 | JVM_MANAGEMENT_MIB_IMPL mib) { |
| 103 | jvmmib = mib; |
| 104 | adaptor = snmpas; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Compute the full path name for a default file. |
| 109 | * @param basename basename (with extension) of the default file. |
| 110 | * @return ${JRE}/lib/management/${basename} |
| 111 | **/ |
| 112 | private static String getDefaultFileName(String basename) { |
| 113 | final String fileSeparator = File.separator; |
| 114 | return System.getProperty("java.home") + fileSeparator + "lib" + |
| 115 | fileSeparator + "management" + fileSeparator + basename; |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * Retrieve the Trap Target List from the ACL file. |
| 120 | **/ |
| 121 | private static List<NotificationTarget> getTargetList(InetAddressAcl acl, |
| 122 | int defaultTrapPort) { |
| 123 | final ArrayList<NotificationTarget> result = |
| 124 | new ArrayList<NotificationTarget>(); |
| 125 | if (acl != null) { |
| 126 | if (log.isDebugOn()) |
| 127 | log.debug("getTargetList",Agent.getText("jmxremote.AdaptorBootstrap.getTargetList.processing")); |
| 128 | |
| 129 | final Enumeration td=acl.getTrapDestinations(); |
| 130 | for (; td.hasMoreElements() ;) { |
| 131 | final InetAddress targetAddr = (InetAddress)td.nextElement(); |
| 132 | final Enumeration tc = |
| 133 | acl.getTrapCommunities(targetAddr); |
| 134 | for (;tc.hasMoreElements() ;) { |
| 135 | final String community = (String) tc.nextElement(); |
| 136 | final NotificationTarget target = |
| 137 | new NotificationTargetImpl(targetAddr, |
| 138 | defaultTrapPort, |
| 139 | community); |
| 140 | if (log.isDebugOn()) |
| 141 | log.debug("getTargetList", |
| 142 | Agent.getText("jmxremote.AdaptorBootstrap.getTargetList.adding", |
| 143 | target.toString())); |
| 144 | result.add(target); |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | return result; |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * Initializes and starts the SNMP Adaptor Server. |
| 153 | * If the com.sun.management.snmp.port property is not defined, |
| 154 | * simply return. Otherwise, attempts to load the config file, and |
| 155 | * then calls {@link #initialize(java.lang.String, java.util.Properties)}. |
| 156 | * |
| 157 | **/ |
| 158 | public static synchronized AdaptorBootstrap initialize() { |
| 159 | |
| 160 | // Load a new properties |
| 161 | final Properties props = Agent.loadManagementProperties(); |
| 162 | if (props == null) return null; |
| 163 | |
| 164 | final String portStr = props.getProperty(PropertyNames.PORT); |
| 165 | |
| 166 | return initialize(portStr,props); |
| 167 | } |
| 168 | |
| 169 | /** |
| 170 | * Initializes and starts the SNMP Adaptor Server. |
| 171 | **/ |
| 172 | public static synchronized |
| 173 | AdaptorBootstrap initialize(String portStr, Properties props) { |
| 174 | |
| 175 | // Get port number |
| 176 | if (portStr.length()==0) portStr=DefaultValues.PORT; |
| 177 | final int port; |
| 178 | try { |
| 179 | port = Integer.parseInt(portStr); |
| 180 | } catch (NumberFormatException x) { |
| 181 | throw new AgentConfigurationError(INVALID_SNMP_PORT, x, portStr); |
| 182 | } |
| 183 | |
| 184 | if (port < 0) { |
| 185 | throw new AgentConfigurationError(INVALID_SNMP_PORT, portStr); |
| 186 | } |
| 187 | |
| 188 | // Get trap port number |
| 189 | final String trapPortStr = |
| 190 | props.getProperty(PropertyNames.TRAP_PORT, |
| 191 | DefaultValues.TRAP_PORT); |
| 192 | |
| 193 | final int trapPort; |
| 194 | try { |
| 195 | trapPort = Integer.parseInt(trapPortStr); |
| 196 | } catch (NumberFormatException x) { |
| 197 | throw new AgentConfigurationError(INVALID_SNMP_TRAP_PORT, x, trapPortStr); |
| 198 | } |
| 199 | |
| 200 | if (trapPort < 0) { |
| 201 | throw new AgentConfigurationError(INVALID_SNMP_TRAP_PORT, trapPortStr); |
| 202 | } |
| 203 | |
| 204 | // Get bind address |
| 205 | final String addrStr = |
| 206 | props.getProperty(PropertyNames.BIND_ADDRESS, |
| 207 | DefaultValues.BIND_ADDRESS); |
| 208 | |
| 209 | // Get ACL File |
| 210 | final String defaultAclFileName = |
| 211 | getDefaultFileName(DefaultValues.ACL_FILE_NAME); |
| 212 | final String aclFileName = |
| 213 | props.getProperty(PropertyNames.ACL_FILE_NAME, |
| 214 | defaultAclFileName); |
| 215 | final String useAclStr = |
| 216 | props.getProperty(PropertyNames.USE_ACL,DefaultValues.USE_ACL); |
| 217 | final boolean useAcl = |
| 218 | Boolean.valueOf(useAclStr).booleanValue(); |
| 219 | |
| 220 | if (useAcl) checkAclFile(aclFileName); |
| 221 | |
| 222 | AdaptorBootstrap adaptor = null; |
| 223 | try { |
| 224 | adaptor = getAdaptorBootstrap(port, trapPort, addrStr, |
| 225 | useAcl, aclFileName); |
| 226 | } catch (Exception e) { |
| 227 | throw new AgentConfigurationError(AGENT_EXCEPTION, e, e.getMessage()); |
| 228 | } |
| 229 | return adaptor; |
| 230 | } |
| 231 | |
| 232 | private static AdaptorBootstrap getAdaptorBootstrap |
| 233 | (int port, int trapPort, String bindAddress, boolean useAcl, |
| 234 | String aclFileName) { |
| 235 | |
| 236 | final InetAddress address; |
| 237 | try { |
| 238 | address = InetAddress.getByName(bindAddress); |
| 239 | } catch (UnknownHostException e) { |
| 240 | throw new AgentConfigurationError(UNKNOWN_SNMP_INTERFACE, e, bindAddress); |
| 241 | } |
| 242 | if (log.isDebugOn()) { |
| 243 | log.debug("initialize", |
| 244 | Agent.getText("jmxremote.AdaptorBootstrap.getTargetList.starting" + |
| 245 | "\n\t" + PropertyNames.PORT + "=" + port + |
| 246 | "\n\t" + PropertyNames.TRAP_PORT + "=" + trapPort + |
| 247 | "\n\t" + PropertyNames.BIND_ADDRESS + "=" + address + |
| 248 | (useAcl?("\n\t" + PropertyNames.ACL_FILE_NAME + "=" |
| 249 | + aclFileName):"\n\tNo ACL")+ |
| 250 | "")); |
| 251 | } |
| 252 | |
| 253 | final InetAddressAcl acl; |
| 254 | try { |
| 255 | acl = useAcl ? new SnmpAcl(System.getProperty("user.name"),aclFileName) |
| 256 | : null; |
| 257 | } catch (UnknownHostException e) { |
| 258 | throw new AgentConfigurationError(UNKNOWN_SNMP_INTERFACE, e, e.getMessage()); |
| 259 | } |
| 260 | |
| 261 | // Create adaptor |
| 262 | final SnmpAdaptorServer adaptor = |
| 263 | new SnmpAdaptorServer(acl, port, address); |
| 264 | adaptor.setUserDataFactory(new JvmContextFactory()); |
| 265 | adaptor.setTrapPort(trapPort); |
| 266 | |
| 267 | // Create MIB |
| 268 | // |
| 269 | final JVM_MANAGEMENT_MIB_IMPL mib = new JVM_MANAGEMENT_MIB_IMPL(); |
| 270 | try { |
| 271 | mib.init(); |
| 272 | } catch (IllegalAccessException x) { |
| 273 | throw new AgentConfigurationError(SNMP_MIB_INIT_FAILED, x, x.getMessage()); |
| 274 | } |
| 275 | |
| 276 | // Configure the trap destinations. |
| 277 | // |
| 278 | mib.addTargets(getTargetList(acl,trapPort)); |
| 279 | |
| 280 | |
| 281 | // Start Adaptor |
| 282 | // |
| 283 | try { |
| 284 | // Will wait until the adaptor starts or fails to start. |
| 285 | // If the adaptor fails to start, a CommunicationException or |
| 286 | // an InterruptedException is thrown. |
| 287 | // |
| 288 | adaptor.start(Long.MAX_VALUE); |
| 289 | } catch (Exception x) { |
| 290 | Throwable t=x; |
| 291 | if (x instanceof com.sun.jmx.snmp.daemon.CommunicationException) { |
| 292 | final Throwable next = t.getCause(); |
| 293 | if (next != null) t = next; |
| 294 | } |
| 295 | throw new AgentConfigurationError(SNMP_ADAPTOR_START_FAILED, t, |
| 296 | address + ":" + port, |
| 297 | "(" + t.getMessage() + ")"); |
| 298 | } |
| 299 | |
| 300 | // double check that adaptor is actually started (should always |
| 301 | // be active, so that exception should never be thrown from here) |
| 302 | // |
| 303 | if (!adaptor.isActive()) { |
| 304 | throw new AgentConfigurationError(SNMP_ADAPTOR_START_FAILED, |
| 305 | address + ":" + port); |
| 306 | } |
| 307 | |
| 308 | try { |
| 309 | // Add MIB to adaptor |
| 310 | // |
| 311 | adaptor.addMib(mib); |
| 312 | |
| 313 | // Add Adaptor to the MIB |
| 314 | // |
| 315 | mib.setSnmpAdaptor(adaptor); |
| 316 | } catch (RuntimeException x) { |
| 317 | new AdaptorBootstrap(adaptor,mib).terminate(); |
| 318 | throw x; |
| 319 | } |
| 320 | |
| 321 | log.debug("initialize", |
| 322 | Agent.getText("jmxremote.AdaptorBootstrap.getTargetList.initialize1")); |
| 323 | log.config("initialize", |
| 324 | Agent.getText("jmxremote.AdaptorBootstrap.getTargetList.initialize2", |
| 325 | address.toString(), java.lang.Integer.toString(adaptor.getPort()))); |
| 326 | return new AdaptorBootstrap(adaptor,mib); |
| 327 | } |
| 328 | |
| 329 | private static void checkAclFile(String aclFileName) { |
| 330 | if (aclFileName == null || aclFileName.length()==0) { |
| 331 | throw new AgentConfigurationError(SNMP_ACL_FILE_NOT_SET); |
| 332 | } |
| 333 | final File file = new File(aclFileName); |
| 334 | if (!file.exists()) { |
| 335 | throw new AgentConfigurationError(SNMP_ACL_FILE_NOT_FOUND, aclFileName); |
| 336 | } |
| 337 | if (!file.canRead()) { |
| 338 | throw new AgentConfigurationError(SNMP_ACL_FILE_NOT_READABLE, aclFileName); |
| 339 | } |
| 340 | |
| 341 | FileSystem fs = FileSystem.open(); |
| 342 | try { |
| 343 | if (fs.supportsFileSecurity(file)) { |
| 344 | if (!fs.isAccessUserOnly(file)) { |
| 345 | throw new AgentConfigurationError(SNMP_ACL_FILE_ACCESS_NOT_RESTRICTED, |
| 346 | aclFileName); |
| 347 | } |
| 348 | } |
| 349 | } catch (IOException e) { |
| 350 | throw new AgentConfigurationError(SNMP_ACL_FILE_READ_FAILED, aclFileName); |
| 351 | |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | |
| 356 | /** |
| 357 | * Get the port on which the adaptor is bound. |
| 358 | * Returns 0 if the adaptor is already terminated. |
| 359 | * |
| 360 | **/ |
| 361 | public synchronized int getPort() { |
| 362 | if (adaptor != null) return adaptor.getPort(); |
| 363 | return 0; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Stops the adaptor server. |
| 368 | **/ |
| 369 | public synchronized void terminate() { |
| 370 | if (adaptor == null) return; |
| 371 | |
| 372 | // Terminate the MIB (deregister NotificationListener from |
| 373 | // MemoryMBean) |
| 374 | // |
| 375 | try { |
| 376 | jvmmib.terminate(); |
| 377 | } catch (Exception x) { |
| 378 | // Must not prevent to stop... |
| 379 | // |
| 380 | log.debug("jmxremote.AdaptorBootstrap.getTargetList.terminate", |
| 381 | x.toString()); |
| 382 | } finally { |
| 383 | jvmmib=null; |
| 384 | } |
| 385 | |
| 386 | // Stop the adaptor |
| 387 | // |
| 388 | try { |
| 389 | adaptor.stop(); |
| 390 | } finally { |
| 391 | adaptor = null; |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | } |