<?xml version="1.0" encoding="utf-8"?>
<book fpi="-//Sun::SunSoft//DOCUMENT JVDMKTUTOR Version 1.0//en" role="unnumbered" label="pre-fcs" id="jvdmktutor" lang="en" userlevel="developer"><title lang="en">Java Dynamic Management Kit 4.0 Tutorial</title><bookinfo><bookbiblio><title lang="en">Java Dynamic Management Kit 4.0 Tutorial</title><authorgroup lang="en"><author lang="en"><firstname lang="en">Andrew</firstname><surname lang="en">Kass</surname></author></authorgroup><isbn lang="en"/><pubsnumber lang="en"><gentext type="text">Part No: </gentext>806-2986</pubsnumber><releaseinfo lang="en"/><pubdate lang="en">December 1999</pubdate><publisher lang="en"><publishername lang="en">Sun Microsystems, Inc.</publishername><address lang="en"><street lang="en">901 San Antonio Road</street><city lang="en">Palo Alto<gentext type="text">, </gentext></city><state lang="en">CA<gentext type="text"></gentext></state><postcode lang="en">94303-4900</postcode><country lang="en">U.S.A.</country></address></publisher><copyright lang="en"><year lang="en">1999</year><holder lang="en">     Sun Microsystems</holder></copyright><abstract lang="en"><para lang="en">This book leads the reader through the development of dynamic agents
and remote managers based on the components of the Java Dynamic Management Kit. Along the
way it explains the concepts and the architecture of the Java Management extensions and discusses
some design issues of management solutions. It is modeled after the learn-by-example
of <citetitle lang="en">The Java Tutorial</citetitle>, and based on the source code
of the example applications provided with the product. This book is aimed
at resource providers who wish to instrument their objects to make them manageable
and developers of agent and manager applications. Familiarity with Java programming
is assumed, and an understanding of network management concepts
is helpful.</para></abstract></bookbiblio><legalnotice lang="en"><para lang="en">This product or document is protected by copyright and distributed under licenses restricting its use, copying, distribution, and decompilation. No part of this product or related documentation may be reproduced in any form by any means without prior written authorization of Sun and its licensors, if any. Third party software, including font technology, is copyrighted and licensed from Sun suppliers. Federal Acquisitions: Commercial Software--Government Users Subject to Standard License Terms and Conditions.</para><para lang="en">Sun, Sun Microsystems, the Sun Logo, 
Java, Java Dynamic Management, JavaBeans, JavaScript, JDK, the Java Coffee Cup logo, docs.sun.com, AnswerBook, AnswerBook2, 
 and Solaris are trademarks or registered trademarks of Sun Microsystems, Inc in the U.S. and other countries. UNIX is a registered trademark in the U.S. and other countries, exclusively licensed through X/Open, Ltd.</para><para lang="en">All SPARC trademarks are used under license and are trademarks or registered trademarks of SPARC International, Inc. in the U.S. and other countries. Products bearing SPARC trademarks are based upon an architecture developed by Sun Microsystems, Inc.</para><para lang="en">DOCUMENTATION IS PROVIDED "AS IS" AND ALL EXPRESS OR IMPLIED CONDITIONS. REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. </para><para lang="fr"/><para lang="en">Ce produit ou document est protégé par un copyright et distribué avec des licences qui en restreignent l'utilisation, la copie, la distribution, et la décompilation. Aucune partie de ce produit ou document ne peut être reproduite sous aucune forme, par quelque moyen que ce soit, sans l'autorisation préalable et écrite de Sun et de ses bailleurs de licence, s'il y en a. Le logiciel détenu par des tiers, et qui comprend la technologie relative aux polices de caractères, est protégé par un copyright et licencié par des fournisseurs de Sun.</para><para lang="en">Sun, Sun Microsystems, le logo Sun, 
Java, Java Dynamic Management, JavaBeans, JavaScript, JDK, Java Coffee Cup logo, docs.sun.com, AnswerBook, AnswerBook2  
 et Solaris sont des marques de fabrique ou des marques déposées de Sun Microsystems, Inc. aux Etats-Unis et dans d'autres pays. UNIX est une marque enregistrée aux Etats-Unis et dans d'autres pays, et exclusivement licenciée par X/Open Ltd.</para><para lang="en">Toutes les marques SPARC sont utilisées sous licence et sont des marques de fabrique ou des marques déposées de SPARC International, Inc. aux  Etats-Unis et dans d'autres pays. Les produits portant les marques SPARC sont basés sur une architecture développée par Sun Microsystems, Inc.</para><para lang="en">LA DOCUMENTATION EST FOURNIE "EN L'ETAT" ET TOUTES AUTRES CONDITIONS, DÉCLARATIONS ET GARANTIES EXPRESSES OU TACITES SONT FORMELLEMENT EXCLUES DANS LA MESURE AUTORISÉE PAR LA LOI APPLICABLE, Y COMPRIS NOTAMMENT TOUTE GARANTIE IMPLICITE RELATIVE À LA QUALITÉ MARCHANDE, À L'APTITUDE À UNE UTILISATION PARTICULIÈRE OU À L'ABSENCE DE CONTREFAÇON.</para></legalnotice><subjectset lang="en"><subject lang="en"><subjectterm lang="en">Java</subjectterm><subjectterm lang="en">Network Software</subjectterm><subjectterm lang="en">Programming &amp; Tools</subjectterm></subject></subjectset></bookinfo><preface id="preface-1" lang="en" role="preface"><gentext type="text">Preface</gentext><gentext type="toc">Preface</gentext><title lang="en">Preface</title><highlights lang="en"><para lang="en">The <trademark class="trade" lang="en">Java Dynamic Management</trademark> Kit provides a set of <trademark class="trade" lang="en">Java</trademark> classes and tools for developing management solutions. This product conforms to the Java Management extensions (JMX), a specification which defines a three-level architecture: resource instrumentation, dynamic agents and remote management applications. The JMX architecture is applicable to network management, remote system maintenance, application provisioning and the new management needs of the service-based network.</para><para lang="en">To demonstrate the management features and tools, the Java Dynamic Management Kit provides a set of example applications. These sample programs show you the source code for implementing various sorts of agents and managers and give you an understanding of different management practices. The <citetitle lang="en">Java Dynamic Management Kit 4.0 Tutorial</citetitle> is intended to help you understand these examples and introduce you to programming with the Java Dynamic Management Kit. The examples covered by this tutorial will show you:<itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">The different ways of making your resources manageable</para></listitem><listitem lang="en"><para lang="en">How to write an agent and access it through a web browser</para></listitem><listitem lang="en"><para lang="en">How to load resources and services dynamically from a remote manager</para></listitem><listitem lang="en"><para lang="en">The details of programming SNMP managers and agents with the provided tools</para></listitem></itemizedlist></para><para lang="en">Taken as a whole, the examples demonstrate the complete development process for implementing a management solution in Java. This tutorial only covers certain examples, but all examples are documented in their respective directories.</para></highlights><sect1 id="preface-9" lang="en"><title id="preface-2" lang="en">Who Should Use This Book</title><para lang="en">This tutorial is aimed at developers who would like to learn how to instrument new or existing resources for management, write dynamic agents, or write management applications. Familiarity with Java programming is assumed. Some tutorials also rely on system and network management concepts: knowledge of these is helpful though not required.</para><para lang="en">This book is not intended to be an exhaustive reference. Management concepts and product features are covered in <citetitle lang="en">Getting Started with the Java Dynamic Management Kit 4.0</citetitle>, and the complete Javadoc API definitions are provided in the product documentation package.</para></sect1><sect1 id="preface-3" lang="en"><title lang="en">Before You Read This Book</title><para lang="en">In order to build and run the sample programs in this tutorial, or use the tool commands, you must have a complete installation of the Java Dynamic Management Kit on your machine. Please refer to the <citetitle lang="en">Java Dynamic Management Kit 4.0 Installation Guide and Release Notes</citetitle> document for instructions on how to install the product components and configure your environment.</para><para lang="en">Before programming with the Java Dynamic Management Kit, you should be familiar with the concepts and tools used throughout these tutorials. The following books are part of the product documentation set:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><citetitle lang="en">Getting Started with the Java Dynamic Management Kit 4.0</citetitle></para></listitem><listitem lang="en"><para lang="en"><citetitle lang="en">Java Dynamic Management Kit 4.0 Tools Reference</citetitle></para></listitem></itemizedlist><para lang="en">These books are available on-line after you have installed the documentation package of the Java Dynamic Management Kit. The on-line documentation also includes the Javadoc API for the Java packages and classes. Using any web browser, open the home-page corresponding to your platform:</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="column1" colwidth="88*"/><colspec colname="column2" colwidth="308*"/><thead lang="en"><row lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">Operating Environment</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Home-Page Location</para></entry></row></thead><tbody lang="en"><row rowsep="0" lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Solaris</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en"><replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/<replaceable lang="en">JDKversion</replaceable>/index.html</filename></para></entry></row><row lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Windows NT</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en"><replaceable lang="en">installDir</replaceable>\SUNWjdmk\jdmk4.0\<replaceable lang="en">JDKversion</replaceable>\index.html</filename></para></entry></row></tbody></tgroup></informaltable><para lang="en">In these file names, <replaceable lang="en">installDir</replaceable> refers to the base directory of your Java Dynamic Management Kit installation. The <replaceable lang="en">JDKversion</replaceable> is that of the <trademark class="trade" lang="en">JDK</trademark> (Java Development Kit) which you use and which you selected during installation; it can be either <filename moreinfo="none" lang="en">1.1</filename> or <filename moreinfo="none" lang="en">1.2</filename>. This convention is used throughout this book whenever referring to files or directories which are part of the installation. In a default installation procedure, <replaceable lang="en">installDir</replaceable> is:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">/opt</filename> on the Solaris platform</para></listitem><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">C:\Program Files </filename>on the Windows NT platform</para></listitem></itemizedlist></sect1><sect1 id="preface-11" lang="en"><title lang="en">Directories and Classpath</title><para lang="en">These tutorials are based on the example programs shipped with the Java Dynamic Management Kit. Each example is a set of Java source code files in a separate subdirectory. The following table gives the location of the main examples directory:</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="column1" colwidth="88*"/><colspec colname="column2" colwidth="308*"/><thead lang="en"><row lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">Operating Environment</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Examples Directory</para></entry></row></thead><tbody lang="en"><row rowsep="0" lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Solaris</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en"><replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/<replaceable lang="en">JDKversion</replaceable>/examples</filename></para></entry></row><row lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Windows NT</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en"><replaceable lang="en">installDir</replaceable>\SUNWjdmk\jdmk4.0\<replaceable lang="en">JDKversion</replaceable>\examples</filename></para></entry></row></tbody></tgroup></informaltable><para lang="en">Except where noted, the source code in this book is taken from these example programs. However, code fragments may be rearranged or comments may be changed. Program listings in the tutorials usually simplify comments and remove output statements for space considerations.</para><para lang="en">On the <trademark class="trade" lang="en">Solaris</trademark> platform, you must have root access in order to write in the installed examples directory. For this reason, it may be necessary to copy all examples to a different location before compiling them. Throughout the rest of this book, we will use the term <replaceable lang="en">examplesDir</replaceable> to refer to the main examples directory in a location where you can compile and run them.</para><para lang="en">When either compiling or running the example programs, the jar file for the Java Dynamic Management Kit runtime must be in your classpath:</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="column1" colwidth="64.46*"/><colspec colname="column2" colwidth="331.54*"/><thead lang="en"><row lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">JDK Version</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Classpath for Compiling or Running the Examples on Solaris</para></entry></row></thead><tbody lang="en"><row rowsep="0" lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">1.1</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en">.:<replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/1.1/lib/jdmkrt.jar:<replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/1.1/lib/collections.jar</filename></para></entry></row><row lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">1.2</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en">.:<replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/1.2/lib/jdmkrt.jar</filename></para></entry></row></tbody></tgroup></informaltable><para lang="en">The classpath is identical on the Windows NT platform, with the forward slashes (/) replaced with back-slashes (\), and the colons(:) replaced with semi-colons(;). </para><para lang="en">This classpath assumes that you are in the subdirectory of a particular example when compiling or running it. You specify the classpath on the command line of the <command moreinfo="none" lang="en">javac</command> and <command moreinfo="none" lang="en">java</command> tools with the <option lang="en"><gentext type="text">-</gentext>classpath</option> option. The JDK version must match the version of the <command moreinfo="none" lang="en">javac</command> or <command moreinfo="none" lang="en">java</command> command that you are using.</para><para lang="en">Throughout the rest of this book, we will use the term <replaceable lang="en">classpath</replaceable> in command line examples to indicate that you must use the classpath indicated above. You may also define this classpath in an environment variable according to your platform and omit its definition on the command line.</para><para lang="en">In order to use the <command moreinfo="none" lang="en">proxygen</command> and <command moreinfo="none" lang="en">mibgen</command> tools provided with the Java Dynamic Management Kit, you should add the installation's binaries directory your environment's path. The following table give the location of this directory:</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="column1" colwidth="88*"/><colspec colname="column2" colwidth="308*"/><thead lang="en"><row lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">Operating Environment</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Binaries Directory</para></entry></row></thead><tbody lang="en"><row rowsep="0" lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Solaris</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en"><replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/<replaceable lang="en">JDKversion</replaceable>/bin</filename></para></entry></row><row lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Windows NT</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><filename moreinfo="none" lang="en"><replaceable lang="en">installDir</replaceable>\SUNWjdmk\jdmk4.0\<replaceable lang="en">JDKversion</replaceable>\bin</filename></para></entry></row></tbody></tgroup></informaltable></sect1><sect1 id="preface-4" lang="en"><title lang="en">How This Book Is Organized</title><para lang="en">Each chapter of this book documents a single topic covered by the example application.</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="standard-1" lang="en">"Standard MBeans"</link> shows how to write a standard MBean by following the design patterns defined by the Java Management extensions. The example shows how an agent then accesses the attributes and operations.</para></listitem><listitem lang="en"><para lang="en"><link linkend="dynamic-1" lang="en">"Dynamic MBeans"</link> shows how to implement the <classname lang="en">DynamicMBean</classname> interface to expose a coherent management interface. Running the example highlights the similarities and differences between dynamic and standard MBeans, with an analysis of performance issues.</para></listitem><listitem lang="en"><para lang="en"><link linkend="minimal-1" lang="en">"The MBean Server in a Minimal Agent"</link> covers the interface of the MBean server which is used by all agents. We introduce the object name of an MBean which is its only reference in the MBean server. The only MBeans in the minimal agent are the communications MBeans, but this is enough to connect to the agent and manage it.</para></listitem><listitem lang="en"><para lang="en"><link linkend="htmladaptor-1" lang="en">"The HTML Protocol Adaptor"</link> gives us a management view of the MBeans in an agent through a web browser. It lets us create MBeans, update their attributes, invoke their operations, and remove them dynamically in a running agent.</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-1" lang="en">"The Base Agent"</link> is similar to the minimal agent but it shows how to manipulate MBeans programmatically through the instance of the MBean server. It covers the different ways of creating and interacting with MBeans in the MBean server. This topic also covers how to process the descriptor objects that represent MBean information.</para></listitem><listitem lang="en"><para lang="en"><link linkend="mletloader-1" lang="en">"The M-Let Class Loader"</link> is a service that lets agents and managers load MBean classes dynamically from the network. It is a fully manageable service, which means that a remote manager can create it in an agent and ask it to download classes for new services or new resources.</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-1" lang="en">"Creating an SNMP Agent"</link> demonstrates how the SNMP protocol adaptor makes a Java Dynamic Management agent also act as an SNMP agent. The MBeans generated by the <command moreinfo="none" lang="en">mibgen</command> tool represent SNMP MIBs which can be accessed by any SNMP manager connecting to the SNMP adaptor. This lets you implement your MIB through Java code and take advantage of the agent services.</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpmanager-1" lang="en">"Developing an SNMP Manager"</link> shows you how to use the SNMP manager API to program an SNMP manager. An SNMP manager handles Java objects representing peers, parameters, sessions, and requests to access SNMP agents and perform management operations.</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpproxy-1" lang="en">"Implementing an SNMP Proxy"</link> gives an example of an SNMP proxy that you can use in an SNMP agent developed with the SNMP adaptor. An SNMP proxy is an object that handles remote MIBs in a sub-agent. The proxy acts as a single point-of-entry to let a manager access a whole hierarchy of sub-agents.</para></listitem></itemizedlist></sect1><sect1 id="preface-5" lang="en"><title lang="en">Related Books</title><para lang="en">The Java Dynamic Management Kit relies on the management architecture of the Java Management extensions. The three specifications documents are provided in the product documentation package:</para><para lang="en"><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><citetitle lang="en">Java Management Extensions Instrumentation and Agent Specification</citetitle> (<filename moreinfo="none" lang="en">jmx_instr_agent.pdf</filename>)</para></listitem><listitem lang="en"><para lang="en"><citetitle lang="en">Java Management Extensions SNMP Manager API</citetitle> (<filename moreinfo="none" lang="en">jmx_snmp_api.pdf</filename>)</para></listitem></itemizedlist></para><para lang="en">The structure of this book was inspired by that of the <citetitle lang="en">The Java Tutorial</citetitle>:<itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">On-line version<ulink url="http://java.sun.com/docs/books/tutorial/index.html"><literal moreinfo="none" lang="en">http://java.sun.com/docs/books/tutorial/index.html</literal></ulink></para></listitem><listitem lang="en"><para lang="en">Paperback reference:<citetitle lang="en">The Java Tutorial Second Edition: Object-Oriented Programming for the Internet (Java Series) </citetitle> by Mary Campione and Kathy Walrath; 2nd book and CD-ROM edition (March 1998)                       Addison-Wesley Pub. Co.; ISBN: 0201310074</para></listitem></itemizedlist></para></sect1><sect1 id="sundocs-1" lang="en"><title lang="en">Ordering Sun Documents</title><para lang="en">Fatbrain.com, an Internet professional bookstore, stocks
selected product documentation from Sun Microsystems, Inc.</para><para lang="en">For a list of documents and how to order them, visit the Sun Documentation
Center on Fatbrain.com at <ulink url="http://www1.fatbrain.com/documentation/sun"><literal moreinfo="none" lang="en">http://www1.fatbrain.com/documentation/sun</literal></ulink>.</para></sect1><sect1 id="sundocs-2" lang="en"><title lang="en">Accessing Sun Documentation Online</title><para lang="en">The <trademark class="service" lang="en">docs.sun.com</trademark> Web site enables
you to access Sun technical documentation online. You can browse the docs.sun.com
archive or search for a specific book title or subject. The URL is <ulink url="http://docs.sun.com"><literal moreinfo="none" lang="en">http://docs.sun.com</literal></ulink>.</para></sect1><sect1 id="typeconv-1" lang="en"><title lang="en">Typographic Conventions</title><para lang="en">The following table describes the typographic changes used in this book.</para><table frame="all" id="typeconv-tbl-2" lang="en"><gentext type="text">Table P-1 </gentext><title lang="en">Typographic Conventions</title><tgroup cols="3" colsep="1" rowsep="1" lang="en"><colspec colname="colspec0" colwidth="80.00*"/><colspec colname="colspec1" colwidth="161.11*"/><colspec colname="colspec2" colwidth="154.89*"/><thead lang="en"><row lang="en"><entry align="left" valign="top" lang="en"><para lang="en">Typeface or Symbol</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Meaning</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Example</para></entry></row></thead><tbody lang="en"><row lang="en"><entry colname="colspec0" lang="en"><para lang="en"><emphasis lang="en">AaBbCc123</emphasis></para></entry><entry colname="colspec1" lang="en"><para lang="en">Book titles, new words or terms, or words to be emphasized</para></entry><entry colname="colspec2" lang="en"><para lang="en">Read Chapter 6 in <citetitle lang="en">User's Guide</citetitle>. </para><para lang="en">These are called <firstterm lang="en">class</firstterm> options. </para><para lang="en">You must be <emphasis lang="en">root</emphasis>
to do this.</para></entry></row><row lang="en"><entry colname="colspec0" lang="en"><classname lang="en">AaBbCc123</classname></entry><entry colname="colspec1" lang="en">Class or object names, methods, parameters or any other element of the Java programming language</entry><entry colname="colspec2" lang="en">Instantiate the <classname lang="en">MyBean</classname> class.</entry></row><row lang="en"><entry align="left" valign="top" lang="en"><para lang="en"><literal moreinfo="none" lang="en">AaBbCc123</literal></para></entry><entry align="left" valign="top" lang="en">The names of commands, files, and directories; on-screen computer output</entry><entry align="left" valign="top" lang="en"><para lang="en">Edit your <filename moreinfo="none" lang="en">.login</filename> file. </para><para lang="en">Use <command moreinfo="none" lang="en">ls <option lang="en"><gentext type="text">-</gentext>a</option> </command>to list all files. </para></entry></row><row lang="en"><entry align="left" valign="top" lang="en"><para lang="en"><userinput moreinfo="none" lang="en">AaBbCc123</userinput></para></entry><entry align="left" valign="top" lang="en">What you type, contrasted with on-screen computer output</entry><entry align="left" valign="top" lang="en"><computeroutput moreinfo="none" lang="en">machine_name%</computeroutput> <userinput moreinfo="none" lang="en">su</userinput><computeroutput moreinfo="none" lang="en">Password:</computeroutput></entry></row><row lang="en"><entry align="left" valign="top" lang="en"><para lang="en"><replaceable lang="en">AaBbCc123</replaceable></para></entry><entry align="left" valign="top" lang="en">A placeholder: replace with the appropriate name or intended value</entry><entry align="left" valign="top" lang="en"><para lang="en">To delete a file, type <userinput moreinfo="none" lang="en">rm</userinput> <replaceable lang="en">filename</replaceable>.</para></entry></row></tbody></tgroup></table></sect1><sect1 id="typeconv-3" lang="en"><title lang="en">Shell Prompts</title><para lang="en">The following table shows the default system prompts for the different platforms and shells.</para><para lang="en">Unless otherwise noted, the command examples in this book use the Korn shell.</para><table frame="all" id="typeconv-tbl-4" lang="en"><gentext type="text">Table P-2 </gentext><title lang="en">Shell Prompts</title><tgroup cols="2" colsep="1" rowsep="1" lang="en"><colspec colname="colspec3" colwidth="55*"/><colspec colname="colspec4" colwidth="45*"/><thead lang="en"><row lang="en"><entry align="left" valign="top" lang="en"><para lang="en">Shell</para></entry><entry align="left" valign="top" lang="en"><para lang="en">Prompt</para></entry></row></thead><tbody lang="en"><row lang="en"><entry align="left" valign="top" lang="en">C shell prompt</entry><entry align="left" valign="top" lang="en"><computeroutput moreinfo="none" lang="en">machine_name%</computeroutput></entry></row><row lang="en"><entry align="left" valign="top" lang="en">C shell superuser prompt</entry><entry align="left" valign="top" lang="en"><computeroutput moreinfo="none" lang="en">machine_name#</computeroutput></entry></row><row lang="en"><entry align="left" valign="top" lang="en">Bourne shell and Korn shell prompt</entry><entry align="left" valign="top" lang="en"><computeroutput moreinfo="none" lang="en">$</computeroutput></entry></row><row lang="en"><entry align="left" valign="top" lang="en">Bourne shell and Korn
shell superuser prompt</entry><entry align="left" valign="top" lang="en"><computeroutput moreinfo="none" lang="en">#</computeroutput></entry></row><row lang="en"><entry colname="colspec3" lang="en">Windows NT system prompt</entry><entry colname="colspec4" lang="en"><computeroutput moreinfo="none" lang="en">C:\</computeroutput></entry></row></tbody></tgroup></table></sect1></preface><chapter id="standard-1" lang="en"><gentext type="text">Chapter 1</gentext><gentext type="toc">1.  Standard MBeans</gentext><title lang="en">Standard MBeans</title><highlights lang="en"><para lang="en">A standard MBean is the simplest and fastest way to instrument a resource
from scratch: attributes and operations are simply methods which follow certain
design patterns. A standard MBean is composed of the <classname lang="en">MBean</classname>
interface which lists the methods for all exposed attributes and operations,
and of the class which implements this interface and provides the functionality
of the resource.</para><para lang="en">The code samples in this topic are taken from the files in the <filename moreinfo="none" lang="en">StandardMBean</filename> example directory located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> (see <link linkend="preface-11" lang="en">"Directories
and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="standard-2" lang="en">"Exposing the <classname lang="en">MBean</classname> Interface"</link> demonstrates the design patterns
for attributes and operations and gives some general rules for writing the <classname lang="en">MBean</classname> interface</para></listitem><listitem lang="en"><para lang="en"><link linkend="standard-5" lang="en">"Implementing the MBean"</link> shows how the <classname lang="en">MBean</classname> interface is related to the code for the manageable resource</para></listitem><listitem lang="en"><para lang="en"><link linkend="standard-7" lang="en">"Running the Standard MBean Example"</link> demonstrates the runtime
behavior of a standard MBean</para></listitem></itemizedlist></highlights><sect1 id="standard-2" lang="en"><title lang="en">Exposing the <classname lang="en">MBean</classname> Interface</title><para lang="en">Typically, you would first determine the management interface of your
resource, that is the information needed to manage it. This information is
expressed as attributes and operations. An attribute is a value of any type
that a manager can get or set remotely. An operation is a method with any
signature and any return type that the manager can invoke remotely.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">Attributes and operations are conceptually equivalent to properties
and actions on JavaBeans objects. However, their translation into Java code
is entirely different to accommodate the management functionality.</para></note><para lang="en">As specified by the Java Management extensions for instrumentation,
all attributes and operations are explicitly listed in an <classname lang="en">MBean</classname> interface. This is a Java interface that defines the full management
interface of an MBean. This interface must have the same name as the class
that implements it, followed by the <classname lang="en">MBean</classname> suffix. Since
the interface and its implementation are usually in different files, there
are two files which make up a standard MBean.</para><para lang="en">For example, the class <classname lang="en">SimpleStandard</classname> (in the
file <filename moreinfo="none" lang="en">SimpleStandard.java</filename>) will have its management interface
defined in the interface <classname lang="en">SimpleStandardMBean</classname> (in the
file <filename moreinfo="none" lang="en">SimpleStandardMBean.java</filename>).</para><example role="code" id="standard-ex-8" lang="en"><gentext type="text">Example 1-1 </gentext><title lang="en">The <classname lang="en">SimpleStandardMBean</classname>
Interface</title><programlisting format="linespecific" lang="en" role="complete">public interface <userinput moreinfo="none" lang="en">SimpleStandardMBean</userinput> {

    public String <userinput moreinfo="none" lang="en">getState</userinput>() ;
    
    public void <userinput moreinfo="none" lang="en">setState</userinput>(String s) ;
    
    public Integer <userinput moreinfo="none" lang="en">getNbChanges</userinput>() ;
    
    public void <userinput moreinfo="none" lang="en">reset</userinput>() ;
}</programlisting></example><para lang="en">Only <classname lang="en">public</classname> methods in the <classname lang="en">MBean</classname>
interface are taken into consideration for the management interface. When
present, non-public methods should be grouped separately, to make the code
clearer for human readers.</para><sect2 id="standard-3" lang="en"><title lang="en">Attributes</title><para lang="en">Attributes are conceptual variables that are exposed for management
through getter and setter methods in the <classname lang="en">MBean</classname> interface:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">A <emphasis lang="en">getter</emphasis> is any public method whose name
begins with <classname lang="en">get</classname> and which doesn't return void; it lets
a manager read the value of the attribute, whose type is that of the returned
object</para></listitem><listitem lang="en"><para lang="en">A public method whose name begins with <classname lang="en">is</classname> and which returns a boolean or <classname lang="en">Boolean</classname>
object is also a getter, though a boolean attribute may have only one getter
(it must be one form or the other)</para></listitem><listitem lang="en"><para lang="en">A <emphasis lang="en">setter</emphasis> is any public method
whose name begins with <classname lang="en">set</classname> and which takes a single
parameter; it lets a manager write a new value in the attribute, whose type
is that of the parameter</para></listitem></itemizedlist><para lang="en">Attribute types can be arrays of objects, but individual array elements
cannot be accessed individually through the getters and setters. Use operations
to access the array elements, as described below. The following code example
demonstrates an attribute with an array type:</para><programlisting format="linespecific" lang="en" role="fragment">public String[] getMessages();
public void setMessages(String[] msgArray);</programlisting><para lang="en">The name of the attribute is the literal part of the method name following <classname lang="en">get</classname>, <classname lang="en">is</classname>, or <classname lang="en">set</classname>.
This name is case sensitive in all Java Dynamic Management Kit objects that manipulate attribute
names. Using these patterns, we can determine the attributes exposed in the
code sample above:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">State</literal> is a readable and writeable attribute
of type String</para></listitem><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">NbChanges</literal> is a read-only attribute
of type Integer</para></listitem></itemizedlist><para lang="en">The specification of the design patterns for attributes implies the
following rules:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">Attributes may be read-only, write-only, or readable and writeable</para></listitem><listitem lang="en"><para lang="en">Attribute names cannot be overloaded: for any given
attribute name there can be at most one setter and one getter, and if both
are defined, they must use the same type</para></listitem></itemizedlist></sect2><sect2 id="standard-6" lang="en"><title lang="en">Operations</title><para lang="en">Operations are methods that management applications can call remotely
on a resource. They can be defined with any number of parameters of any type
and can return any type.</para><para lang="en">The design patterns for operations are simple: any public method defined
in the <classname lang="en">MBean</classname> interface that is not an attribute getter
or setter is an operation. For this reason, getters and setters are usually
declared first in the Java code, so that all operations are grouped afterwards.
The name of an operation is the name of the corresponding method.</para><para lang="en">The <classname lang="en">SimpleStandardMBean</classname> in the example defines
one operation, <classname lang="en">reset</classname>, which takes no parameters and
returns nothing.</para><para lang="en">While the following methods define valid operations (and not attributes),
these types of names should not be used to avoid confusion:</para><programlisting format="linespecific" lang="en" role="fragment">public void getFoo();
public Integer getBar(Float p);
public void setFoo(Integer one, Integer two);
public String isReady();</programlisting><para lang="en">For performance reasons, you may want to define operations for accessing
individual elements of an array type attribute. In this case, use non-ambiguous
operation names:</para><programlisting format="linespecific" lang="en" role="fragment">public String singleGetMessage(int index);
public void singleSetMessage(int index, String msg);</programlisting><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">The Java Dynamic Management Kit imposes no restrictions on attribute types, operation
attribute types, and operation return types. However, the developer must insure
that the corresponding classes are available to all applications manipulating
these objects, and that they are compatible with the type of communication
used. For example, attribute and operation types must be serializable in order
to be manipulated remotely using the RMI or HTTP protocol.</para></note></sect2></sect1><sect1 id="standard-5" lang="en"><title lang="en">Implementing the MBean</title><para lang="en">The second part of an MBean is the class that implements the <classname lang="en">MBean</classname> interface. This class encodes the expected behavior of the
manageable resource in its implementation of the attribute and operation methods.
Of course, the resource does not need to reside entirely in this class, the
MBean implementation can rely on other objects.</para><para lang="en">Beyond the implementation of the corresponding <classname lang="en">MBean</classname>
interface, there are two requirements on the MBean class:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">It must be a concrete class so that it can be instantiated</para></listitem><listitem lang="en"><para lang="en">It must expose at least one public constructor
so that any other class can create an instance</para></listitem></itemizedlist><para lang="en">Otherwise, the developer is free to implement the management interface
in any way, provided of course that the object has the expected behavior.
Here is the sample code that implements our <classname lang="en">MBean</classname> interface:</para><example role="code" id="standard-ex-9" lang="en"><gentext type="text">Example 1-2 </gentext><title lang="en">The <classname lang="en">SimpleStandard</classname>
Class</title><programlisting format="linespecific" lang="en" role="complete">public class <userinput moreinfo="none" lang="en">SimpleStandard</userinput> implements SimpleStandardMBean {

     public String <userinput moreinfo="none" lang="en">getState</userinput>() {
        return state;
    }

    public void <userinput moreinfo="none" lang="en">setState</userinput>(String s) {
        state = s;
        nbChanges++;
    }

    public Integer <userinput moreinfo="none" lang="en">getNbChanges</userinput>() {
        return new Integer(nbChanges);
    }

    public void <userinput moreinfo="none" lang="en">reset</userinput>() {
        state = "initial state";
        nbChanges = 0;
        nbResets++;
    }

    // This method is not a getter in the management sense because 
    // it is not exposed in the "SimpleStandardMBean" interface.
    public Integer <userinput moreinfo="none" lang="en">getNbResets</userinput>() {
        return new Integer(nbResets);
    }

    // internal variables for exposed attributes
    private String      <userinput moreinfo="none" lang="en">state</userinput> = "initial state";
    private int         <userinput moreinfo="none" lang="en">nbChanges</userinput> = 0;

    // other private variables
    private int         <userinput moreinfo="none" lang="en">nbResets</userinput> = 0;   
}</programlisting></example><para lang="en">In this example there is no constructor. Since the Java compiler provides
a public, no-argument constructor by default in such cases, this is a valid
MBean.</para><para lang="en">As in this example, attributes are usually implemented as internal variables
whose value is returned or modified by the getter and setter methods. However,
an MBean may implement any access and storage scheme to fit particular management
needs, provided getters and setters retain their read and write semantics.
Methods in the MBean implementation may have side-effects, but it is up to
the programmer to insure that these are safe and coherent within the full
management solution.</para><para lang="en">As we shall see later, management applications never have a direct handle
on an MBean. They only have an identification of an instance and the knowledge
of the management interface. In this case, the mechanism for exposing attributes
through methods in the <classname lang="en">MBean</classname> interface makes it impossible
for an application to access the MBean directly. Internal variables and methods,
and even public ones, are totally encapsulated and their access is controlled
by the programmer through the implementation of the <classname lang="en">MBean</classname>
interface.</para></sect1><sect1 id="standard-7" lang="en"><title lang="en">Running the Standard MBean Example</title><para lang="en">The <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/StandardMBean</filename>
directory contains the <filename moreinfo="none" lang="en">SimpleStandard.java</filename> and <filename moreinfo="none" lang="en">SimpleStandardMBean.java</filename> files which make up the MBean. This directory
also contains a simple agent application which instantiates this MBean, introspects
its management interface and manipulates its attributes and operations.</para><para lang="en">Compile all files in this directory with the <command moreinfo="none" lang="en">javac</command>
command. For example, on the Solaris platform with the Korn shell, you would
type:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/StandardMBean/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput></screen><para lang="en">To run the example, launch the agent class which will interact with
the <classname lang="en">SimpleStandard</classname> MBean:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> StandardAgent</userinput></screen><para lang="en">Type return when the application pauses to step through the example.
The agent application handles all input and output in this example and gives
us a view of the MBean at runtime.</para><para lang="en">We will look at how agents work in <link linkend="minimal-1" lang="en">"The
MBean Server in a Minimal Agent"</link>, but this example
demonstrates how the <classname lang="en">MBean</classname> interface limits the view
of what the MBean exposes for management. Roughly, the agent introspects the
MBean interface at runtime to determine what attributes and operations are
available. You then see the result of calling the getters, setters and operations.</para><para lang="en">The lesson on agents will also cover the topics of object names and
exceptions which you see when running this example.</para></sect1></chapter><chapter id="dynamic-1" lang="en"><gentext type="text">Chapter 2</gentext><gentext type="toc">2.  Dynamic MBeans</gentext><title lang="en">Dynamic MBeans</title><highlights lang="en"><para lang="en">A dynamic MBean implements its management interface programmatically,
instead of through static method names. To do this, it relies on descriptor
classes which represent the attributes and operations exposed for management.
Management applications then call generic getters and setters whose implementation
must resolve the attribute or operation name to its intended behavior.</para><para lang="en">One advantage of this instrumentation is that you can use it to quickly
make an existing resource manageable. The implementation of the <classname lang="en">DynamicMBean</classname> interface can provide an instrumentation wrapper
for an existing resource.</para><para lang="en">Another advantage is that the descriptor classes for the management
interface can provide human-readable descriptions of the attributes, operations
and MBean itself. This information could be displayed to a user on a management
console to describe how to interact with this particular resource.</para><para lang="en">The code samples in this topic are taken from the files in the <filename moreinfo="none" lang="en">DynamicMBean</filename> example directory located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> (see <link linkend="preface-11" lang="en">"Directories
and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="dynamic-2" lang="en">"Exposing the Management Interface"</link> explains the <classname lang="en">DynamicMBean</classname> interface and its generic methods common to all dynamic MBeans</para></listitem><listitem lang="en"><para lang="en"><link linkend="dynamic-6" lang="en">"Implementing a Dynamic MBean"</link> shows how to implement
this interface to expose specific attributes and operations</para></listitem><listitem lang="en"><para lang="en"><link linkend="dynamic-9" lang="en">"Running the Dynamic MBean Example"</link> demonstrates the runtime
behavior of a dynamic MBean</para></listitem></itemizedlist></highlights><sect1 id="dynamic-2" lang="en"><title lang="en">Exposing the Management Interface</title><para lang="en">In the standard MBean, attributes and operations are exposed statically
in the names of methods in the <classname lang="en">MBean</classname> interface. Dynamic
MBeans all share the same interface which defines generic methods to access
attributes and operations. Since the management interface is no longer visible
through introspection, dynamic MBeans must also provide a description of their
attributes and operations explicitly.</para><sect2 id="dynamic-12" lang="en"><title lang="en">The <classname lang="en">DynamicMBean</classname> Interface</title><para lang="en">The <classname lang="en">DynamicMBean</classname> class is a Java interface defined
by the Java Management extensions. It specifies the methods that a resource implemented as
a dynamic MBean must provide to expose its management interface. Here is an
uncommented version of the code:</para><example role="code" id="dynamic-ex-19" lang="en"><gentext type="text">Example 2-1 </gentext><title lang="en">The <classname lang="en">DynamicMBean</classname>
Interface</title><programlisting format="linespecific" lang="en" role="complete">public interface DynamicMBean {

    public Object <userinput moreinfo="none" lang="en">getAttribute</userinput>(String attribute) throws
        AttributeNotFoundException, MBeanException, ReflectionException; 

    public void <userinput moreinfo="none" lang="en">setAttribute</userinput>(Attribute attribute) throws
        AttributeNotFoundException, InvalidAttributeValueException,
        MBeanException, ReflectionException ; 

    public AttributeList <userinput moreinfo="none" lang="en">getAttributes</userinput>(String[] attributes);

    public AttributeList <userinput moreinfo="none" lang="en">setAttributes</userinput>(AttributeList attributes);

    public Object <userinput moreinfo="none" lang="en">invoke</userinput>(
        String actionName, Object params[], String signature[])
        throws MBeanException, ReflectionException ;

    public MBeanInfo <userinput moreinfo="none" lang="en">getMBeanInfo</userinput>();
 }</programlisting></example><para lang="en">The <classname lang="en">getMBeanInfo</classname> method is the one which provides
a description of the MBean's management interface. This method returns an <classname lang="en">MBeanInfo</classname> object which is the descriptor type that contains information
about attributes and operations.</para><para lang="en">The attribute getters and setters are generic, since they take the name
of the attribute which needs to be read or written. For convenience, dynamic
MBeans must also define bulk getters and setters to operate on any number
of attributes at once. These methods use the <classname lang="en">Attribute</classname>
and <classname lang="en">AttributeList</classname> classes to represent attribute name-value
pairs and lists of name-value pairs, respectively.</para><para lang="en">Since the names of the attributes are not revealed until runtime, the
getters and setters are necessarily generic. In the same way, the <classname lang="en">invoke</classname> method takes the name of an operation and its signature,
in order to invoke any method which might be exposed. </para><para lang="en">As a consequence of implementing generic getters, setters, and invokers,
the code for a dynamic MBean is more complex than for a standard MBean. For
example, instead of having a specific getter called by name, the generic getter
must verify the attribute name and then encode the functionality to read each
of the possible attributes.</para></sect2><sect2 id="dynamic-13" lang="en"><title lang="en">The MBean Descriptor Classes</title><para lang="en">A dynamic MBean has the burden of building the description of its own
management interface. The JMX specification defines the Java objects used
to completely describe the management interface of an MBean. Dynamic MBeans
use these objects to provide a complete self description as returned by the <classname lang="en">getMBeanInfo</classname> method. Agents also use these classes to describe
a standard MBean after it has been introspected.</para><para lang="en">As a group, they are referred to as the <emphasis lang="en">MBean descriptor classes</emphasis> because they provide information about the MBean. This information
includes the attributes and operations of the management interface but also
the list of constructors for the MBean class and the notifications that the
MBean may send. Notifications are messages in the event mechanism that is
defined by the JMX architecture; they are covered in the notification example
in <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Notification</filename>.</para><para lang="en">Each element is described by its descriptor object containing its name,
a description string, and its characteristics. For example, an attribute has
a type and is readable and/or writeable. The following table lists all MBean
descriptor classes:</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="0" lang="en"><colspec colname="column1" colwidth="33*"/><colspec colname="column2" colwidth="67*"/><thead lang="en"><row rowsep="1" lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">Class Name</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Purpose</para></entry></row></thead><tbody lang="en"><row lang="en"><entry align="left" valign="top" lang="en"><classname lang="en">MBeanInfo</classname></entry><entry align="left" valign="top" lang="en">Top-level object containing arrays of descriptors for all MBean elements; also includes the name of the MBean's Java class and a description string</entry></row><row lang="en"><entry align="left" valign="top" lang="en"><classname lang="en">MBeanFeatureInfo</classname></entry><entry align="left" valign="top" lang="en">Parent class for all other descriptors so that they inherit a name and a description string</entry></row><row lang="en"><entry colname="column1" valign="top" lang="en"><classname lang="en">MBeanOperationInfo</classname></entry><entry colname="column2" valign="top" lang="en">Describes an operation: the return type, the signature as an array of parameters, and the impact (whether the operation just returns information or modifies the resource)</entry></row><row lang="en"><entry colname="column1" valign="top" lang="en"><classname lang="en">MBeanConstructorInfo</classname></entry><entry colname="column2" valign="top" lang="en">Describes a constructor by its signature</entry></row><row lang="en"><entry colname="column1" valign="top" lang="en"><classname lang="en">MBeanParameterInfo</classname></entry><entry colname="column2" valign="top" lang="en">Gives the type of a parameter in an operation or constructor signature</entry></row><row lang="en"><entry colname="column1" valign="top" lang="en"><classname lang="en">MBeanAttributeInfo</classname></entry><entry colname="column2" valign="top" lang="en">Describes an attribute: its type, whether it is readable, and whether it is writeable</entry></row><row lang="en"><entry colname="column1" valign="top" lang="en"><classname lang="en">MBeanNotificationInfo</classname></entry><entry colname="column2" valign="top" lang="en">Contains an array of notification type strings</entry></row></tbody></tgroup></informaltable></sect2></sect1><sect1 id="dynamic-6" lang="en"><title lang="en">Implementing a Dynamic MBean</title><para lang="en">A dynamic MBean consists of a class that implements the <classname lang="en">DynamicMBean</classname> interface coherently. By this, we mean a class which
exposes a management interface whose description matches the attributes and
operations which are accessible through the generic getters, setters and invokers.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">MBeans are not allowed to be both standard and dynamic. When a class
is instantiated as an MBean, the agent checks the interfaces that it implements.
If the class implements or inherits an implementation of <emphasis lang="en">both</emphasis>
the corresponding MBean interface and the DynamicMBean interface, then an
exception is raised and the MBean cannot be created.</para></note><para lang="en">Beyond this restriction, a dynamic MBean must also follow the same two
rules as a standard MBean, namely: </para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">It must be a concrete class so that it can be instantiated</para></listitem><listitem lang="en"><para lang="en">It must expose at least one public constructor
so that any other class can create an instance</para></listitem></itemizedlist><para lang="en">Thereafter, a dynamic MBean class is free to declare any number of public
or private methods and variables. None of these are visible to management
applications, only the methods implementing the <classname lang="en">DynamicMBean</classname>
interface are exposed for management. A dynamic MBean is also free to rely
on other classes which may be a part of the manageable resource.</para><sect2 id="dynamic-15" lang="en"><title lang="en">Dynamic Programming Issues</title><para lang="en">An MBean is a manageable resource that exposes a specific management
interface. The name <emphasis lang="en">dynamic MBean</emphasis> refers to the fact
that the interface is revealed at runtime, as opposed to through the introspection
of static class names. The term <emphasis lang="en">dynamic</emphasis> is not meant
to imply that the MBean can dynamically change its management interface. The
management architecture defined by JMX and implemented in the Java Dynamic Management Kit
does not support MBeans whose management interface is modified during runtime.</para><para lang="en">This is not an issue with standard MBeans which would need to be recompiled
in order to change their interface. However, dynamic MBeans could be programmed
so that their interface description and their generic getters, setters and
the invoker have a different behavior at different times. In practice, this
type of MBean could be created but it couldn't be managed after any change
of interface.</para><para lang="en"> As a rule, the value returned by the <classname lang="en">MBeanInfo</classname>
method of a dynamic MBean, and the corresponding behavior of getters, setters
and the invoker, must never change over the lifetime of a given instance of
the MBean. However, it is permissible to have the same dynamic MBean class
expose different management interfaces depending upon the instantiation conditions.
This would be a valid MBean, since the agent architecture manages object instances,
not class types. It would also be a very advanced MBean for a complex management
solution, beyond the scope of this tutorial.</para></sect2><sect2 id="dynamic-14" lang="en"><title lang="en">The <classname lang="en">getMBeanInfo</classname> Method</title><para lang="en">Since the MBean description should never change, it is usually created
once at instantiation, and the <classname lang="en">getMBeanInfo</classname> method
just returns its reference at every call. The MBean constructor should therefore
build the <classname lang="en">MBeanInfo</classname> object from the MBean descriptor
classes such that it accurately describes the management interface. And since
most dynamic MBeans will always be instantiated with the same management interface,
building the <classname lang="en">MBeanInfo</classname> object is fairly straightforward.</para><para lang="en">The following code shows how the <classname lang="en">SimpleDynamic</classname>
MBean defines its management interface, as built at instantiation and returned
by its <classname lang="en">getMBeanInfo</classname> method:</para><example role="code" id="dynamic-ex-20" lang="en"><gentext type="text">Example 2-2 </gentext><title lang="en">Implemention of the <classname lang="en">getMBeanInfo</classname> Method</title><programlisting format="linespecific" lang="en" role="complete">// class constructor
public <userinput moreinfo="none" lang="en">SimpleDynamic</userinput>() {

    buildDynamicMBeanInfo();
}

// internal variables describing the MBean
private String <userinput moreinfo="none" lang="en">dClassName</userinput> = this.getClass().getName();
private String <userinput moreinfo="none" lang="en">dDescription</userinput> = "Simple implementation of a dynamic MBean.";

// internal variables for describing MBean elements
private MBeanAttributeInfo[] <userinput moreinfo="none" lang="en">dAttributes</userinput> = new MBeanAttributeInfo[2];
private MBeanConstructorInfo[] <userinput moreinfo="none" lang="en">dConstructors</userinput> = new MBeanConstructorInfo[1];
private MBeanOperationInfo[] <userinput moreinfo="none" lang="en">dOperations</userinput> = new MBeanOperationInfo[1];
private MBeanInfo <userinput moreinfo="none" lang="en">dMBeanInfo</userinput> = null;

// internal method
private void <userinput moreinfo="none" lang="en">buildDynamicMBeanInfo</userinput>() {

    dAttributes[0] = new MBeanAttributeInfo(
        "State",                 // name
        "java.lang.String",      // type
        "State: state string.",  // description
        true,                    // readable
        true);                   // writable
    dAttributes[1] = new MBeanAttributeInfo(
        "NbChanges",
        "java.lang.Integer",
        "NbChanges: number of times the State string has been changed.",
        true,
        false);

    // use reflection to get constructor signatures
    Constructor[] constructors = this.getClass().getConstructors();
    dConstructors[0] = new MBeanConstructorInfo(
        "SimpleDynamic(): No-parameter constructor",  //description
        constructors[0]);                  // the contructor object

    MBeanParameterInfo[] params = null;
    dOperations[0] = new MBeanOperationInfo(
        "reset",                     // name
        "Resets State and NbChanges attributes to their initial values",
                                     // description
        params,                      // parameter types
        "void",                      // return type
        MBeanOperationInfo.ACTION);  // impact

    dMBeanInfo = new MBeanInfo(dClassName,
                               dDescription,
                               dAttributes,
                               dConstructors,
                               dOperations,
                               new MBeanNotificationInfo[0]);
}

// exposed method implementing the DynamicMBean.getMBeanInfo interface
public MBeanInfo <userinput moreinfo="none" lang="en">getMBeanInfo</userinput>() {

    // return the information we want to expose for management:
    // the dMBeanInfo private field has been built at instantiation time,
    return(dMBeanInfo);
}</programlisting></example></sect2><sect2 id="dynamic-10" lang="en"><title lang="en">Generic Attribute Getters and Setters</title><para lang="en">Generic getters and setters take a parameter that indicates the name
of the attribute to read or write. There are two issues to keep in mind when
implementing these methods:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">Attribute names must be correctly mapped to their corresponding
internal representation</para></listitem><listitem lang="en"><para lang="en">Invalid attribute names and types should raise
an exception, including when writing to a read-only attribute (and vice-versa)</para></listitem></itemizedlist><para lang="en">The <classname lang="en">getAttribute</classname> method is the simplest, since
only the attribute name must be verified:</para><example role="code" id="dynamic-ex-21" lang="en"><gentext type="text">Example 2-3 </gentext><title lang="en">Implementation of the <classname lang="en">getAttribute</classname> Method</title><programlisting format="linespecific" lang="en" role="complete">public Object <userinput moreinfo="none" lang="en">getAttribute</userinput>(String attribute_name) 
    throws AttributeNotFoundException,
           MBeanException,
           ReflectionException {

    // Check attribute_name to avoid NullPointerException later on
    if (attribute_name == null) {
        throw new RuntimeOperationsException(
            new IllegalArgumentException("Attribute name cannot be null"), 
            "Cannot invoke a getter of " + dClassName +
                " with null attribute name");
    }

    // Call the corresponding getter for a recognized attribute_name
    if (attribute_name.equals("State")) {
        return getState();
    } 
    if (attribute_name.equals("NbChanges")) {
        return getNbChanges();
    }

    // If attribute_name has not been recognized
    throw(new AttributeNotFoundException(
        "Cannot find " + attribute_name + " attribute in " + dClassName));
}

// internal methods for getting attributes
public String <userinput moreinfo="none" lang="en">getState</userinput>() {
    return state;
}

public Integer <userinput moreinfo="none" lang="en">getNbChanges</userinput>() {
    return new Integer(nbChanges);
}

// internal variables representing attributes
private String      <userinput moreinfo="none" lang="en">state</userinput> = "initial state";
private int         <userinput moreinfo="none" lang="en">nbChanges</userinput> = 0;</programlisting></example><para lang="en">The <classname lang="en">setAttribute</classname> method is more complicated,
since you must also insure that the given type can be assigned to the attribute
and handle the special case for a null value:</para><example role="code" id="dynamic-ex-22" lang="en"><gentext type="text">Example 2-4 </gentext><title lang="en">Implementation of the <classname lang="en">setAttribute</classname> Method</title><programlisting format="linespecific" lang="en" role="complete">public void <userinput moreinfo="none" lang="en">setAttribute</userinput>(Attribute attribute)
    throws AttributeNotFoundException,
           InvalidAttributeValueException,
           MBeanException, 
           ReflectionException {

    // Check attribute to avoid NullPointerException later on
    if (attribute == null) {
        throw new RuntimeOperationsException(
            new IllegalArgumentException("Attribute cannot be null"),
            "Cannot invoke a setter of " + dClassName +
                " with null attribute");
    }
    // Note: Attribute class constructor ensures the name not null
    String name = attribute.getName();
    Object value = attribute.getValue();

    // Call the corresponding setter for a recognized attribute name
    if (name.equals("State")) {
        // if null value, try and see if the setter returns any exception
        if (value == null) {
            try {
                setState( null );
            } catch (Exception e) {
                throw(new InvalidAttributeValueException(
                    "Cannot set attribute "+ name +" to null")); 
            }
        }
        // if non null value, make sure it is assignable to the attribute
        else {
            try {
                if ((Class.forName("java.lang.String")).isAssignableFrom(
                        value.getClass())) {
                    setState((String) value);
                }
                else {
                    throw(new InvalidAttributeValueException(
                        "Cannot set attribute "+ name +
                            " to a " + value.getClass().getName() +
                            " object, String expected"));
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    // optional: recognize an attempt to set a read-only attribute
    else if (name.equals("NbChanges")) {
        throw(new AttributeNotFoundException(
            "Cannot set attribute "+ name +
                " because it is read-only"));
    }

    // unrecognized attribute name
    else {
        throw(new AttributeNotFoundException(
            "Attribute " + name + " not found in " +
                this.getClass().getName()));
    }
}

// internal method for setting attribute
public void <userinput moreinfo="none" lang="en">setState</userinput>(String s) {
    state = s;
    nbChanges++;
}</programlisting></example><para lang="en">Notice that the generic getter and setter methods usually hard-code
information about the attributes. If a change in your management solution
requires you to change your management interface, it will be harder to do
with a dynamic MBean. In a standard MBean, each attribute and operation is
a separate method, so unchanged attributes are unaffected. In a dynamic MBean,
you must modify these generic methods that encode all attributes.</para></sect2><sect2 id="dynamic-16" lang="en"><title lang="en">Bulk Getters and Setters</title><para lang="en">The <classname lang="en">DynamicMBean</classname> interface includes bulk getter
and setter methods for reading or writing more than one attribute at once.
These methods rely on the following classes:</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="0" lang="en"><colspec colname="column1" colwidth="33*"/><colspec colname="column2" colwidth="67*"/><thead lang="en"><row rowsep="1" lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">Class Name</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Purpose</para></entry></row></thead><tbody lang="en"><row lang="en"><entry align="left" valign="top" lang="en"><classname lang="en">Attribute</classname></entry><entry align="left" valign="top" lang="en">A simple object which contains the name string and value object of any attribute.</entry></row><row lang="en"><entry align="left" valign="top" lang="en"><classname lang="en">AttributeList</classname></entry><entry align="left" valign="top" lang="en">A dynamically extendable list of <classname lang="en">Attribute</classname> objects (extends <classname lang="en">java.util.ArrayList)</classname></entry></row></tbody></tgroup></informaltable><para lang="en">The <classname lang="en">AttributeList</classname> class extends the <classname lang="en">java.util.ArrayList</classname> class which is specific to Java 2. For this
class and others that rely on similar sets and collection, the Java Dynamic Management Kit
provides the <filename moreinfo="none" lang="en">collections.jar</filename> file for complete compatibility
using any JDK version 1.1.<replaceable lang="en">x</replaceable>. See <link linkend="preface-11" lang="en">Directories and Classpath</link> in the preface for more information.</para><para lang="en">The bulk getter and setter methods usually rely on the generic getter
and setter, respectively. This makes them independent of the management interface,
which can simplify certain modifications. In this case, their implementation
consists mostly of error checking on the list of attributes. However, all
bulk getters and setters must implement the following behavior: an error on
any one attribute does not interrupt or invalidate the bulk operation on the
other attributes.</para><para lang="en">If an attribute cannot be read, then its name-value pair does not figure
in the list of results. If an attribute cannot be written, it will not be
copied to the returned list of successful set operations. As a result, if
there are any errors, the lists returned by bulk operators will not have the
same length as the array or list passed to them. In any case, the bulk operators <emphasis lang="en">do not</emphasis> guarantee that their returned lists have the same ordering
of attributes as the input array or list.</para><para lang="en">The <classname lang="en">SimpleDynamic</classname> MBean shows one way of implementing
the bulk getter and setter methods:</para><example role="code" id="dynamic-ex-23" lang="en"><gentext type="text">Example 2-5 </gentext><title lang="en">Implementation of the Bulk Getter and Setter</title><programlisting format="linespecific" lang="en" role="complete">public AttributeList <userinput moreinfo="none" lang="en">getAttributes</userinput>(String[] attributeNames) {

    // Check attributeNames to avoid NullPointerException later on
    if (attributeNames == null) {
        throw new RuntimeOperationsException(
            new IllegalArgumentException(
                "attributeNames[] cannot be null"),
            "Cannot invoke a getter of " + dClassName);
    }
    AttributeList resultList = new AttributeList();

    // if attributeNames is empty, return an empty result list
    if (attributeNames.length == 0)
            return resultList;
        
    // build the result attribute list
    for (int i=0 ; i&lt;attributeNames.length ; i++){
        try {        
            Object value = getAttribute((String) attributeNames[i]);     
            resultList.add(new Attribute(attributeNames[i],value));
        } catch (Exception e) {
            // print debug info but continue processing list
            e.printStackTrace();
        }
    }
    return(resultList);
}

public AttributeList <userinput moreinfo="none" lang="en">setAttributes</userinput>(AttributeList attributes) {

    // Check attributesto avoid NullPointerException later on
    if (attributes == null) {
        throw new RuntimeOperationsException(
            new IllegalArgumentException(
                "AttributeList attributes cannot be null"),
            "Cannot invoke a setter of " + dClassName);
    }
    AttributeList resultList = new AttributeList();

    // if attributeNames is empty, nothing more to do
    if (attributes.isEmpty())
        return resultList;

    // try to set each attribute and add to result list if successful
    for (Iterator i = attributes.iterator(); i.hasNext();) {
        Attribute attr = (Attribute) i.next();
        try {
            setAttribute(attr);
            String name = attr.getName();
            Object value = getAttribute(name); 
            resultList.add(new Attribute(name,value));
        } catch(Exception e) {
            // print debug info but keep processing list
            e.printStackTrace();
        }
    }
    return(resultList);
}</programlisting></example></sect2><sect2 id="dynamic-11" lang="en"><title lang="en">Generic Operation Invoker</title><para lang="en">Finally, a dynamic MBean must implement the <classname lang="en">invoke</classname>
method so that operations in the management interface can be called. This
method requires the same considerations as the generic getter and setter:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">Operations and their parameters should be mapped to their
internal representation and the result must be returned</para></listitem><listitem lang="en"><para lang="en">Operation names and parameter types need to be
verified</para></listitem><listitem lang="en"><para lang="en">These verifications are usually hard-coded, again
making modifications to the management interface more delicate than in a standard
MBean</para></listitem></itemizedlist><para lang="en">The implementation in the <classname lang="en">SimpleDynamic</classname> MBean
is relatively simple due to the one operation with no parameters:</para><example role="code" id="dynamic-ex-24" lang="en"><gentext type="text">Example 2-6 </gentext><title lang="en">Implementation of the <classname lang="en">invoke</classname> Method</title><programlisting format="linespecific" lang="en" role="complete">public Object <userinput moreinfo="none" lang="en">invoke</userinput>(
        String operationName, Object params[], String signature[])
    throws MBeanException, ReflectionException {

    // Check operationName to avoid NullPointerException later on
    if (operationName == null) {
        throw new RuntimeOperationsException(
            new IllegalArgumentException(
                "Operation name cannot be null"),
            "Cannot invoke a null operation in " + dClassName);
    }

    // Call the corresponding operation for a recognized name
    if (operationName.equals("reset")){
        // this code is specific to the internal "reset" method:
        reset();     // no parameters to check
        return null; // and no return value
    } else { 
        // unrecognized operation name:
        throw new ReflectionException(
            new NoSuchMethodException(operationName), 
            "Cannot find the operation " + operationName +
                " in " + dClassName);
    }
}

// internal variable
private int         <userinput moreinfo="none" lang="en">nbResets</userinput> = 0;

// internal method for implementing the reset operation
public void <userinput moreinfo="none" lang="en">reset</userinput>() {
    state = "initial state";
    nbChanges = 0;
    nbResets++;
}

// Method not revealed in the MBean description and not accessible
// through "invoke" therefore it is only available for internal mgmt
public Integer <userinput moreinfo="none" lang="en">getNbResets</userinput>() {
    return new Integer(nbResets);
}</programlisting></example><para lang="en">As it is written, the <classname lang="en">SimpleDynamic</classname> MBean correctly
provides a description of its management interface and implements its attributes
and operations. However, this example demonstrates the need for a strict coherence
between what is exposed by the <classname lang="en">getMBeanInfo</classname> method
and what can be accessed through the generic getters, setters, and invoker.</para><para lang="en">A dynamic MBean whose <classname lang="en">getMBeanInfo</classname> method describes
an attribute or operation which cannot be accessed is not compliant with the Java Management extensions
and is technically not a manageable resource. Similarly, a class could make
attributes or operations accessible without describing them in the returned <classname lang="en">MBeanInfo</classname> object. Since MBeans should raise an exception when
an undefined attribute or operation is accessed, this would, again, technically
not be a compliant resource.</para></sect2></sect1><sect1 id="dynamic-9" lang="en"><title lang="en">Running the Dynamic MBean Example</title><para lang="en">The <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/DynamicMBean</filename>
directory contains the <filename moreinfo="none" lang="en">SimpleDynamic.java</filename> file which
makes up the MBean. The <classname lang="en">DynamicMBean</classname> interface is defined
in the <classname lang="en">javax.management</classname> package provided in the run-time
jar file (<filename moreinfo="none" lang="en">jdmkrt.jar</filename>) of the Java Dynamic Management Kit. This directory
also contains a simple agent application which instantiates this MBean, calls
its <classname lang="en">getMBeanInfo</classname> method to get its management interface
and manipulates its attributes and operations.</para><para lang="en">Compile all files in this directory with the <command moreinfo="none" lang="en">javac</command>
command. For example, on the Solaris platform, you would type:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/DynamicMBean/</userinput>$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput></screen><para lang="en">To run the example, launch the agent class which will interact with
the <classname lang="en">SimpleDynamic</classname> MBean:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> DynamicAgent</userinput></screen><para lang="en">Type return when the application pauses to step through the example.
The agent application handles all input and output in this example and gives
us a view of the MBean at runtime.</para><para lang="en">This example demonstrates how the management interface encoded in the <classname lang="en">getMBeanInfo</classname> method is made visible in the agent application.
We can then see the result of calling the generic getters and setters and
the <classname lang="en">invoke</classname> method. Finally, the code for filtering
attribute and operation errors is exercised, and we see the exceptions from
the code samples as they are raised at runtime.</para><sect2 id="dynamic-17" lang="en"><title lang="en">Comparison with the <classname lang="en">SimpleStandard</classname> Example</title><para lang="en">Now that we have implemented both types of MBeans we can compare how
they are managed. We purposely created a dynamic MBean and a standard MBean
with the same management interface so that we can do exactly the same operations
on them. On the Solaris platform, we can compare the relevant code of the
two agent applications with the <command moreinfo="none" lang="en">diff</command> utility (your output
may vary):</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable></userinput>
$ <userinput moreinfo="none" lang="en">diff ./StandardMBean/StandardAgent.java ./DynamicMBean/DynamicAgent.java</userinput>
[...]
41c40
&lt; public class StandardAgent {
---
&gt; public class DynamicAgent {
49c48
&lt;     public StandardAgent() {
---
&gt;     public DynamicAgent() {
77c76
&lt;       StandardAgent agent = new StandardAgent();
---
&gt;       DynamicAgent agent = new DynamicAgent();
88c87
&lt;       println("\n&gt;&gt;&gt; END of the SimpleStandard example:\n");
---
&gt;       println("\n&gt;&gt;&gt; END of the SimpleDynamic example:\n");
113c112
&lt;       String mbeanName = "SimpleStandard";
---
&gt;       String mbeanName = "SimpleDynamic";</screen><para lang="en">If the two agent classes had the same name, we see that the only programmatic
difference would be the following:</para><screen format="linespecific" lang="en">113c112
&lt;       String mbeanName = "SimpleStandard";
---
&gt;       String mbeanName = "SimpleDynamic";</screen><para lang="en">We can see that there is only one difference between the two example
agents handling different types of MBeans: the name of the MBean class that
is instantiated! In other words, standard and dynamic MBeans are indistinguishable
from the agent's point of view. This is the power of the JMX architecture:
managers interact with the attributes and operations of a manageable resource,
and the specification of the agent hides any implementation differences between
MBeans.</para><para lang="en">Since we know that the two MBeans are being managed identically, we
can also compare their runtime behavior. In doing so, we can draw two conclusions:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">The dynamic MBean was programmed to have the same behavior
as the standard MBean; the example output shows that this is indeed the case:
despite the different implementations, the functionality of the resource is
strictly the same</para></listitem><listitem lang="en"><para lang="en">The only functional difference between the two
is that the agent can obtain the self-description strings encoded in the dynamic
MBean: attributes and operations are associated with the explanation that
the programmer provides for them</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">There is no mechanism allowing a standard MBean to provide a self-description,
the agent provides a default description string for each feature in standard
MBeans--these descriptions are the same for all standard MBeans.</para></note></listitem></itemizedlist></sect2><sect2 id="dynamic-18" lang="en"><title lang="en">Dynamic MBean Execution Time</title><para lang="en">In the introduction to this topic we presented two structural advantages
of dynamic MBeans, namely the ability to wrap existing code to make it manageable
and the ability to provide a self-description of the MBean and its features.
Another advantage is that using dynamic MBeans can lead to faster overall
execution time. </para><para lang="en">The performance gain depends on the nature of the MBean and how it is
managed in the agent. For example, the <classname lang="en">SimpleDynamic</classname>
MBean, as it is used, is probably not measurably faster than the <classname lang="en">SimpleStandard</classname> example in the <link linkend="standard-1" lang="en">Chapter 1, Standard MBeans</link> topic.
When seeking improved performance, there are two situations which must be
considered: MBean introspection, and management operations.</para><para lang="en">Since the dynamic MBean provides its own description, the agent doesn't
need to introspect it as it would a standard MBean. Since introspection is
done only once by the agent, this is a one-time performance gain during the
lifetime of the MBean. In an environment where there are many MBean creations
and where MBeans have a short lifetime, a slight performance increase can
be measured.</para><para lang="en">However, the largest performance gain is in the management operations:
calling the getters, setters and invoker. As we shall see in the next lesson
(<link linkend="minimal-1" lang="en">"The MBean Server in a Minimal Agent"</link>), the agent makes MBeans manageable through generic getters, setters,
and invokers. In the case of standard MBeans, the agent must do the computations
for resolving attribute and operation names according to the design patterns.
Since dynamic MBeans necessarily expose the same generic methods, these are
called directly by the agent. When a dynamic MBean has a simple management
interface requiring simple programming logic in its generic methods, its implementation
can show a better performance than the same functionality in a standard MBean.</para></sect2></sect1></chapter><chapter id="minimal-1" lang="en"><gentext type="text">Chapter 3</gentext><gentext type="toc">3.  The MBean Server in a Minimal Agent</gentext><title lang="en">The MBean Server in a Minimal Agent</title><highlights lang="en"><para lang="en">An agent application is a program written in the Java language which contains an MBean server and some way of accessing its functionality. This would be a minimal agent, anything less couldn't be managed. In our example of a minimal agent, we will access the MBean server through the HTML protocol adaptor from a web browser.</para><para lang="en">In a real management solution, the agent could be instantiated and loaded with MBeans for all services and resources that it might need when launched. However, a minimal agent might also be used when resources and services are unknown at launch time. They would be instantiated dynamically at a later date by some management application connected to the agent. This flexibility shows how the Java Dynamic Management Kit lets you develop many different management solutions, depending on your intended deployment strategy.</para><para lang="en">For now we will focus on the functionality of the MBean server and how to interact with it through the HTML protocol adaptor. The code samples in this chapter are taken from the files in the <filename moreinfo="none" lang="en">MinimalAgent</filename> example directory located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> (see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="minimal-2" lang="en">"MBean Server Classes"</link> explains the interfaces of the MBean server classes</para></listitem><listitem lang="en"><para lang="en"><link linkend="minimal-3" lang="en">"The Minimal Agent"</link> shows the code needed to write an agent application</para></listitem><listitem lang="en"><para lang="en"><link linkend="minimal-4" lang="en">"Referencing MBeans"</link> covers the objects used to reference an MBean in an agent</para></listitem><listitem lang="en"><para lang="en"><link linkend="minimal-13" lang="en">"A Minimalist Agent"</link> shows the code for a very small but complete agent</para></listitem><listitem lang="en"><para lang="en"><link linkend="minimal-11" lang="en">"Running the Minimal Agent Example"</link> demonstrates its run-time behavior</para></listitem></itemizedlist></highlights><sect1 id="minimal-2" lang="en"><title lang="en">MBean Server Classes</title><para lang="en">Before writing an agent application, it is important to understand the functionality of the MBean server. It is actually an interface and a <emphasis lang="en">factory</emphasis> object defined by the agent specification level of the Java Management extensions. The Java Dynamic Management Kit provides an implementation of this interface and factory. The factory object finds or creates the MBean server instance, making it possible to substitute different implementations of the MBean server.</para><sect2 id="minimal-5" lang="en"><title lang="en">The <classname lang="en">MBeanServer</classname> Interface</title><para lang="en">The specification of the interface defines all operations that can be applied to resources and other agent objects through the MBean server. Its methods can be divided into three main groups:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">Methods for controlling MBean instances:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><classname lang="en">createMBean</classname>, or <classname lang="en">instantiate</classname> and <classname lang="en">registerMBean</classname> add a new MBean to the agent</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">unregisterMBean</classname> removes an MBean from the agent</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">isRegistered</classname> and <classname lang="en">getObjectInstance</classname> associate the class name with the MBean's management name</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">addNotificationListener</classname> and <classname lang="en">removeNotificationListener</classname> control event listeners for a particular MBean</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">deserialize</classname> is used to download new MBean classes</para></listitem></itemizedlist></listitem><listitem lang="en"><para lang="en">Methods for accessing MBean attributes and operations; these methods are identical to those presented in <link linkend="dynamic-12" lang="en">"The <classname lang="en">DynamicMBean</classname> Interface"</link>, except they all have an extra parameter for specifying the target MBean:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><classname lang="en">getMBeanInfo</classname></para></listitem><listitem lang="en"><para lang="en"><classname lang="en">getAttribute</classname> and <classname lang="en">getAttributes</classname></para></listitem><listitem lang="en"><para lang="en"><classname lang="en">setAttribute</classname> and <classname lang="en">setAttributes</classname></para></listitem><listitem lang="en"><para lang="en"><classname lang="en">invoke</classname></para></listitem></itemizedlist></listitem><listitem lang="en"><para lang="en">Methods for managing the agent as a whole:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><classname lang="en">getDefaultDomain</classname> (domains are a way of grouping MBeans in the agent)</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">getMBeanCount</classname> of all MBeans in an agent</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">queryMBeans</classname> and <classname lang="en">queryNames</classname> are used to find MBeans by name or by value</para></listitem></itemizedlist></listitem></itemizedlist></sect2><sect2 id="minimal-6" lang="en"><title lang="en">The MBean Server Implementation and Factory</title><para lang="en">The <classname lang="en">MBeanServerImpl</classname> class in the Java Dynamic Management Kit implements the <classname lang="en">MBeanServer</classname> interface. It is the class that will be instantiated in an agent. However, it is never instantiated directly by the agent application. Instead, you rely on the MBean server factory to return a new instance of the implementing class.</para><para lang="en">The <classname lang="en">MBeanServerFactory</classname> is a static class defined by the Java Management extensions that makes the agent application independent of the MBean server implementation. It resides in the Java virtual machine and centralizes all MBean server instantiation. It provides two static methods:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><classname lang="en">createMBeanServer</classname> which returns a new MBean server instance</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">findMBeanServer</classname> which returns a list of MBean servers in the Java virtual machine</para></listitem></itemizedlist><para lang="en">You must use this class to create an MBean server so that other objects can obtain its reference by calling the <classname lang="en">findMBeanServer</classname> method. This method allows dynamically loaded objects to find the MBean server in an agent which has already been launched.</para></sect2></sect1><sect1 id="minimal-3" lang="en"><title lang="en">The Minimal Agent</title><para lang="en">The minimal agent contains only the essential components of a complete agent application. These are:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">An instance of the MBean server</para></listitem><listitem lang="en"><para lang="en">Some means of communication</para></listitem></itemizedlist><para lang="en">The following code is the complete application from the <filename moreinfo="none" lang="en">MinimalAgent.java</filename> file:</para><example role="code" id="minimal-ex-17" lang="en"><gentext type="text">Example 3-1 </gentext><title lang="en">The Minimal Agent</title><programlisting format="linespecific" lang="en" role="complete">import javax.management.ObjectInstance;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;

public class MinimalAgent {

    public static void main(String[] args) {
        
        // Instantiate the MBean server
        System.out.println("\nCreate the MBean server");
        MBeanServer server = MBeanServerFactory.createMBeanServer();
        
        // Create and start some way of communicating:
        //    - an HTML protocol adaptor
        //    - an HTTP connector server
        //    - an RMI connector server
        // Any single one of these would be sufficient
        try {

            System.out.println(
                "\nCreate and start an HTML protocol adaptor");
            ObjectInstance html = server.createMBean(
                "com.sun.jdmk.comm.HtmlAdaptorServer", null);
            server.invoke(html.getObjectName(), "start",
                          new Object[0], new String[0]);

            System.out.println(
                "\nCreate and start an HTTP connector server");
            ObjectInstance http = server.createMBean(
                "com.sun.jdmk.comm.HttpConnectorServer", null);
            server.invoke(http.getObjectName(), "start",
                          new Object[0], new String[0]);

            System.out.println(
                "\nCreate and start an RMI connector server");
            ObjectInstance rmi = server.createMBean(
                "com.sun.jdmk.comm.RmiConnectorServer", null);
            server.invoke(rmi.getObjectName(), "start",
                          new Object[0], new String[0]);

        } catch(Exception e) {
            e.printStackTrace();
            return;
        }

        System.out.println(
"\nNow, you can point your browser to http://localhost:8082/");
        System.out.println(
"or start your management application to connect to this agent.\n");

    }
}</programlisting></example><para lang="en">Here we analyze the most important lines of code in this example. We start with the instantiation of the MBean server:</para><programlisting format="linespecific" lang="en" role="fragment">MBeanServer server = MBeanServerFactory.createMBeanServer();</programlisting><para lang="en">The MBean server is created through the static <classname lang="en">MBeanServerFactory</classname> object, and we store its object reference. Its true type is hidden by the factory object, which casts the returned object as an <classname lang="en">MBeanServer</classname> interface. The MBean server is the only functional class referenced directly in this application.</para><para lang="en">Then we just need some means of communicating (only the HTML adaptor is shown here):</para><programlisting format="linespecific" lang="en" role="fragment">ObjectInstance html = server.createMBean("com.sun.jdmk.comm.HtmlAdaptorServer", null);
server.invoke(html.getObjectName(), "start", new Object[0], new String[0]);</programlisting><para lang="en">For each of the communications protocols we ask the MBean server to create the corresponding MBean. We must provide the class name of the MBean which we want the MBean server to instantiate. The MBean server instantiates the object, registers it for management, and returns its <classname lang="en">ObjectInstance</classname> reference (see <link linkend="minimal-9" lang="en">"The <classname lang="en">ObjectInstance</classname> of an MBean"</link>).</para><para lang="en">When an MBean is created through the MBean server, you can never obtain a direct programmatic reference on an MBean. The object instance is the only handle the caller has on the MBean. The MBean server hides the MBean object instance and only exposes its management interface.</para><para lang="en">We then ask the server to invoke the <literal moreinfo="none" lang="en">start</literal> operation of the HTML adaptor's MBean. The object instance gives us the MBean's object name which is what we use to identify the MBean (see <link linkend="minimal-8" lang="en">"Object Names"</link>). The other parameters correspond to the signature of the <literal moreinfo="none" lang="en">start</literal> operation which takes no parameters. This operation activates the adaptor or connector so that it will respond to remote management operations on its default port. We can now connect to the HTML protocol adaptor from a web browser.</para></sect1><sect1 id="minimal-4" lang="en"><title lang="en">Referencing MBeans</title><para lang="en">As demonstrated by the minimal agent, most agent applications interact with MBeans through the MBean server. It is possible for an object to instantiate an MBean class itself which it can then register in the MBean server. In this case, it may keep a programmatic reference to the MBean instance. All other objects can only interact with the MBean through its management interface exposed by the MBean server. </para><para lang="en">In particular, service MBeans and connectivity MBeans rely solely on the MBean server to access resources. The MBean server centralizes the access to all MBeans: it unburdens all other objects from having to keep numerous object references. To insure this function, the MBean server relies on object names to uniquely identify MBean instances.</para><sect2 id="minimal-8" lang="en"><title lang="en">Object Names</title><para lang="en">Each MBean object registered in the MBean server is identified by an object name. The same MBean class can have multiple instances, but each must have a unique name. The <classname lang="en">ObjectName</classname> class encapsulates an object name which is composed of a domain name and a set of key properties. The object name can be represented as a string in the following format:</para><informaltable frame="none" lang="en"><tgroup cols="1" colsep="0" rowsep="0" align="center" lang="en"><colspec colwidth="50*"/><tbody lang="en"><row lang="en"><entry lang="en"><para lang="en"><literal moreinfo="none" lang="en"><replaceable lang="en">DomainName</replaceable>:<replaceable lang="en">property</replaceable>=<replaceable lang="en">value</replaceable>[,<replaceable lang="en">property2</replaceable>=<replaceable lang="en">value2</replaceable>]*</literal></para></entry></row></tbody></tgroup></informaltable><para lang="en">The <replaceable lang="en">DomainName</replaceable>, the <replaceable lang="en">properties</replaceable> and their <replaceable lang="en">values</replaceable> can be any alpha-numeric string, so long as they don't contain any of the following characters: <literal moreinfo="none" lang="en">: , = * ?</literal>. All elements of the object name are treated as literal strings, meaning that they are case sensitive.</para><sect3 id="minimal-15" lang="en"><title lang="en">Domains</title><para lang="en">A domain is an abstract category that can be used to group MBeans arbitrarily. The MBean server lets you easily search for all MBeans with the same domain. For example, all connectivity MBeans in the minimal server could have been registered into a domain called <literal moreinfo="none" lang="en">Communications</literal>.</para><para lang="en">Since all object names must have a domain, the MBeans in an MBean server necessarily define at least one domain. When the domain name is not important, the MBean server provides a default domain name which you can use. By default, it is called the <literal moreinfo="none" lang="en">DefaultDomain</literal>, but you may specify a different default domain name when creating the MBean server from its factory.</para></sect3><sect3 id="minimal-14" lang="en"><title lang="en">Key Properties</title><para lang="en">A key is a property-value pair that can also have any meaning that you assign to it. An object name must have at least one key. Keys and their values are independent of the MBean's attributes: the object name is a static identifier which should identify the MBean, whereas attributes are the exposed, run-time values of the corresponding resource. Keys are not positional and can be given in any order to identify an MBean.</para><para lang="en">Keys usually provide the specificity for identifying a unique MBean instance. For example, a better object name for the HTML protocol adaptor MBean might be: <literal moreinfo="none" lang="en">Communications:protocol=html,port=8082</literal>, assuming the port will not change.</para></sect3><sect3 id="minimal-16" lang="en"><title lang="en">Usage</title><para lang="en">All MBeans must be given an object name that is unique. It can be assigned by the MBean's pre-registration method, if the MBean supports pre-registration (see the JMX specification). Or it can be assigned by the object which creates or registers the MBean, which overrides the one given during pre-registration. However, if neither of these assign an object name, the MBean server will not create the MBean and raise an exception. Once an MBean is instantiated and registered, its assigned object name cannot be modified.</para><para lang="en">You are free to encode any meaning into the domain and key strings, the MBean server just handles them as literal strings. The contents of the object name should be determined by your management needs. Keys could be meaningless serial numbers if MBeans are always handled programmatically. On the other hand, the keys could be human-readable to simplify their translation to the graphical user interface of a management application. With the HTML protocol adaptor, object names are displayed directly to the user.</para></sect3></sect2><sect2 id="minimal-9" lang="en"><title lang="en">The <classname lang="en">ObjectInstance</classname> of an MBean</title><para lang="en">An object instance represents the complete reference of an MBean in the MBean server. It contains the MBean's object name and its Java class name. Object instances are returned by the MBean server when an MBean is created or in response to queries about MBeans. Since the object name and class name cannot change over the life of a given MBean, its returned object instance will always have the same value.</para><para lang="en">You cannot modify the class or object name in an object instance, this information can only be read. The object name is used to refer to the MBean instance in any management operation through the MBean server. The class name may be used to instantiate similar MBeans or introspect characteristics of the class.</para></sect2></sect1><sect1 id="minimal-13" lang="en"><title lang="en">A Minimalist Agent</title><para lang="en">The following code is one of the smaller agents you can write. It retains all of the functionality that we will need to connect to it with a web browser in the next topic (<link linkend="htmladaptor-1" lang="en">"The HTML Protocol Adaptor"</link>).</para><example role="code" id="minimal-ex-18" lang="en"><gentext type="text">Example 3-2 </gentext><title lang="en">A Minimalist Agent</title><programlisting format="linespecific" lang="en" role="complete">import javax.management.ObjectInstance;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;

public class MinimalistAgent {

    public static void main(String[] args) {
        
        MBeanServer server = MBeanServerFactory.createMBeanServer();
        
        try {

            ObjectInstance html = server.createMBean(
                "com.sun.jdmk.comm.HtmlAdaptorServer", null);
            server.invoke(html.getObjectName(), "start", null, null);

        } catch(Exception e) {
            e.printStackTrace();
            return;
        }
    }
}</programlisting></example><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">Only three classes are "imported" by this program and needed to compile it. However, the MBean server dynamically instantiates other classes such as the <classname lang="en">HtmlAdaptorServer</classname> which are needed at run-time. As a result, the Java Dynamic Management Kit runtime jar file (<filename moreinfo="none" lang="en">jdmkrt.jar</filename>) must be in the classpath of the Java virtual machine running the minimal agent.</para></note><para lang="en">The <classname lang="en">MinimalistAgent</classname> relies on the HTML adaptor, but we could have used any MBean that provides some way of accessing the MBean server. You could even use your own MBean that encodes some proprietary protocol, provided it makes all functionality of the MBean server available remotely.</para><para lang="en">It is important to realize that this minimalist agent is a fully functional agent that is every bit as powerful as any agent that may be deployed. Since we can connect to this agent, we can dynamically create new MBeans for it to manage, and classes that aren't available locally can be downloaded from the network (this is covered in <link linkend="mletloader-1" lang="en">"The M-Let Class Loader"</link>). Because resources, services and other connections may be added on-the-fly, this agent can participate in any management scheme.</para><para lang="en">Of course, it is more efficient for the agent application to perform the initialization, including the creation of all MBeans that are known to be necessary. Typically, an agent application also needs to set up data structures or launch specific applications that its resources require. For example, it may establish a database session that an MBean will use to expose stored information. The agent application usually includes everything necessary for making the intended resources ready to be managed within the intended management solution.</para><para lang="en">However, there is no single management solution. Many different agent applications could perform the same management function, requiring more or less intervention after they are launched. And the flexibility of the Java Dynamic Management Kit means that there are many different management solutions to achieve the same goal. For example, an MBean could also establish the database session itself during its registration phase (see "MBean Registration Control" in the JMX agent specification).</para></sect1><sect1 id="minimal-11" lang="en"><title lang="en">Running the Minimal Agent Example</title><para lang="en">The example code for the <classname lang="en">MinimalAgent</classname> application is located in the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/MinimalAgent</filename> directory. As we saw, this agent application only has minimal output and is intended to be accessed for management through one of its communications MBeans. However, you will need to compile and launch the minimal agent if you continue on to the next topic.</para><para lang="en">Compile the <filename moreinfo="none" lang="en">MinimalAgent.java</filename> file in this directory with the <command moreinfo="none" lang="en">javac</command> command. For example, on the Solaris platform, you would type:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/MinimalAgent/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput></screen><para lang="en">When we access the minimal agent through the HTML adaptor, we will instantiate the <classname lang="en">SimpleStandard</classname> and <classname lang="en">SimpleDynamic</classname> classes. Since we don't use a dynamic class loader, the agent will need these classes at runtime. You will need to have compiled the standard and dynamic MBean classes as described in <link linkend="standard-7" lang="en">"Running the Standard MBean Example"</link> and <link linkend="dynamic-9" lang="en">"Running the Dynamic MBean Example"</link>. To run the example, update your classpath to find the MBeans and launch the agent class:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable>:../StandardMBean:../DynamicMBean MinimalAgent</userinput></screen><para lang="en">This being a minimal agent, the example doesn't have much output. The MBean server is launched, and the three communication MBeans are created: it is now possible to connect to this agent. The HTTP and RMI connector servers communicate with connector clients in manager applications, see the applications in the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/SimpleClients</filename> directory.</para><para lang="en">The simplest way to communicate with the agent is through the HTML protocol adaptor. This adaptor provides a view of the agent and its MBeans through standard HTML pages which can be viewed on almost any web browser. To connect to the agent, load the following URL in your browser:</para><informaltable frame="none" lang="en"><tgroup cols="1" colsep="0" rowsep="0" align="center" lang="en"><colspec colwidth="50*"/><tbody lang="en"><row lang="en"><entry lang="en"><para lang="en"><ulink url="http://localhost:8082/">http://localhost:8082/</ulink></para></entry></row></tbody></tgroup></informaltable><para lang="en">If you get an error, you may have to switch off proxies in your preference settings or substitute your machine name for <literal moreinfo="none" lang="en">localhost</literal>. Any browser on your local network can also connect to this agent by using your machine name in this URL. In the next topic, <link linkend="htmladaptor-1" lang="en">"The HTML Protocol Adaptor"</link>, we will go into the details of managing this agent from its web view.</para></sect1></chapter><chapter id="htmladaptor-1" lang="en"><gentext type="text">Chapter 4</gentext><gentext type="toc">4.  The HTML Protocol Adaptor</gentext><title lang="en">The HTML Protocol Adaptor</title><highlights lang="en"><para lang="en">The HTML protocol adaptor provides a view of the agent and its registered
MBeans through a basic interface on any web browser. It is the easiest way
to access an agent since no further coding is necessary. For this reason,
it can be useful for testing and debugging your MBeans in the minimal agent.</para><para lang="en">In fact, we will use your browser to "manage" the minimal
agent and its MBeans. The HTML protocol adaptor outputs HTML pages which represent
the agent and its MBeans. The adaptor also interprets the commands sent back
by the buttons and fields appearing in your browser. It then interacts with
the agent's MBean server in order to get information about the MBeans that
it has registered and operate on them. </para><para lang="en">The HTML adaptor relies mostly on plain HTML. The only <trademark class="trade" lang="en">JavaScript</trademark> that the generated pages contain are pop-up windows for displaying
information. Browsers that are not JavaScript enabled might give an incompatibility
message and won't be able to display the information. Otherwise, the generated
pages contain no further scripting (JavaScript, Visual Basic or other), no
frames and no images that might slow down loading.</para><para lang="en">This topic relies on the minimal agent which you will need to launch
first, as explained in <link linkend="minimal-11" lang="en">"Running the Minimal Agent Example"</link>. Once you can connect to
the HTML protocol adaptor in the minimal agent, you are ready to go through
these topics:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="htmladaptor-2" lang="en">"The Agent View"</link> is the main page for managing
an agent through the HTML protocol adaptor</para></listitem><listitem lang="en"><para lang="en"><link linkend="htmladaptor-3" lang="en">"The MBean View"</link> exposes an MBean's
management interface</para></listitem><listitem lang="en"><para lang="en">The <link linkend="htmladaptor-4" lang="en">"Agent Administration"</link> lets you instantiate
new MBeans</para></listitem><listitem lang="en"><para lang="en"><link linkend="htmladaptor-5" lang="en">"Instantiating and Managing MBeans"</link> shows how to modify
attributes and invoke operations</para></listitem><listitem lang="en"><para lang="en"><link linkend="htmladaptor-6" lang="en">"Filtering MBeans"</link> is used to select
the MBeans displayed in the agent view</para></listitem></itemizedlist></highlights><sect1 id="htmladaptor-2" lang="en"><title lang="en">The Agent View</title><para lang="en">The first page displayed by the HTML adaptor is always the agent view.
It originally contains a list of all registered MBeans. The following figure
shows the agent view for the minimal agent. It contains four MBeans: the three
communication MBeans, one of which is the HTML adaptor, and the MBean server
delegate. The delegate is a special MBean covered in <link linkend="htmladaptor-13" lang="en">"The MBean Server Delegate"</link>.</para><figure float="0" id="htmladaptor-fig-14" lang="en"><gentext type="text">Figure 4-1 </gentext><title lang="en">Initial Agent View of the Minimal Agent</title><graphic filename="figures/HTMLadaptor.agent.gif" width="330" depth="232" scale="66" id="htmladaptor-gr-36" lang="en"/></figure><para lang="en">The text field for filtering by object name lets you modify the list
of displayed MBeans. The filter string is initially <literal moreinfo="none" lang="en">*:*</literal>,
which gives all registered MBeans. Further use of the filter is covered in <link linkend="htmladaptor-6" lang="en">"Filtering MBeans"</link>. The agent's registered domain tells you the name
of the default domain in this agent. The number of MBeans on this page is
the count of those listed beneath the separator line. </para><para lang="en">The "Admin" button is a link to the agent administration
page which we will cover in <link linkend="htmladaptor-4" lang="en">"Agent Administration"</link>.</para><sect2 id="htmladaptor-12" lang="en"><title lang="en">The MBean List</title><para lang="en">The MBean list contains all MBeans whose object name matches the filter
string. Object names can be filtered by their domain name and list of key-value
pairs. In this list, MBeans are sorted and grouped by their domain name. Each
MBean name listed is an active link to the page of the corresponding MBean
view.</para><para lang="en">After its initialization, the contents of an agent are dynamic: new
MBeans may be created and registered into new or existing domains and old
MBeans may be removed. These changes can also affect the functionality of
the agent: new agent services may be registered (or removed) as well. We will
demonstrate examples of dynamic management in <link linkend="htmladaptor-5" lang="en">"Instantiating and Managing MBeans"</link>.</para></sect2><sect2 id="htmladaptor-13" lang="en"><title lang="en">The MBean Server Delegate</title><para lang="en">The MBean server delegate is an MBean that is automatically instantiated
and registered by the MBean server when it is created. It provides information
about the version of the Java Dynamic Management Kit which is running, and it represents
the MBean server when sending notifications.</para><para lang="en">Notifications are events sent by MBeans, they are covered by the JMX
specification and the notification example in <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Notification</filename>. Since the MBean server
instance is not an MBean object, it relies on its delegate MBean to send notifications.
The MBean server delegate sends notifications to inform interested listeners
about such events as MBean registrations and de-registrations.</para><para lang="en">The exposed attributes of the delegate MBean provide vendor and version
information about the MBean server. This can let a remote management application
know which agent version is running and which version of the Java Runtime
Environment it is using. The delegate MBean also provides a unique identification
string for its MBean server.</para><procedure id="htmladaptor-proc-18" lang="en"><gentext type="text"></gentext><title lang="en">Viewing the MBean Server Delegate
Information</title><step performance="required" id="htmladaptor-step-37" lang="en"><para lang="en">Click on the name of the delegate MBean to see its attributes. Version,
vendor and identification information is listed in the table of attributes.</para></step><step performance="required" id="htmladaptor-step-20" lang="en"><para lang="en">Click on the <literal moreinfo="none" lang="en">Back to Agent View</literal> link or use your
browser's <emphasis lang="en">Previous page</emphasis> function to return to the MBean
list in the agent view.</para></step></procedure></sect2></sect1><sect1 id="htmladaptor-3" lang="en"><title lang="en">The MBean View</title><para lang="en">The MBean view has two functions: it presents the management interface
of the MBean and it lets you interact with its instance. The management interface
of an MBean is given through the name of the attributes, the operation signatures,
and a self-description. You may interact with the MBean by reloading its attribute
values, setting new values or invoking an operation. </para><procedure id="htmladaptor-proc-23" lang="en"><gentext type="text"></gentext><title lang="en">Preparation</title><step performance="required" id="htmladaptor-step-38" lang="en"><para lang="en">In the agent view, click on the object name of the HTML adaptor MBean: <literal moreinfo="none" lang="en">name=HTMLAdaptorServer</literal> in the default domain. This will bring up
its MBean view.</para></step></procedure><sect2 id="htmladaptor-10" lang="en"><title lang="en">The Header and Description</title><para lang="en">As shown in the following figure, the top part of the page contains
the description of the MBean and some controls for managing it:</para><figure float="0" id="htmladaptor-fig-15" lang="en"><gentext type="text">Figure 4-2 </gentext><title lang="en">Description in the MBean View</title><graphic filename="figures/HTMLadaptor.mbean.gif" width="329" depth="174" scale="66" id="htmladaptor-gr-39" lang="en"/></figure><para lang="en">The first two lines give the object instance (object name and class
name) for this MBean. The MBean name is the full object name of this MBean
instance, including the domain. The key-property pairs may or may not identify
the MBean to a human reader, depending on the agent developer's intention.
The MBean Java class is the full class name for the Java object of which this
MBean is an instance.</para><para lang="en">The reload controls include a text field for entering a reload period
and a manual "Reload" button. Originally, the reload period is set to zero
indicating that the contents of the MBean view are not automatically refreshed.
Clicking the reload button will force the page to reload, thereby updating
all of the attribute values displayed. If you have entered a reload period,
clicking the button will begin automatic reloading with the given period.
The reload period must be at least five seconds. </para><procedure id="htmladaptor-proc-24" lang="en"><gentext type="text"></gentext><title lang="en">Setting the Reload Period</title><step performance="required" id="htmladaptor-step-40" lang="en"><para lang="en">Enter a reload period of five and click the "Reload" button. Every five
seconds the page will blink as it reloads.</para></step><step performance="required" id="htmladaptor-step-25" lang="en"><para lang="en">In another browser window, open another connection to the HTML adaptor
at <literal moreinfo="none" lang="en">http://localhost:8082/</literal>. Observe the new values for the <literal moreinfo="none" lang="en">ActiveClientCount</literal> and <literal moreinfo="none" lang="en">LastConnectedClient</literal> attributes
in the original window. Due to the way the adaptor works, you may have to
try several connections before you see the attribute values change. </para></step></procedure><para lang="en">The reload period is reset to zero every time you open an MBean view.</para><para lang="en">The "Unregister" button is a shortcut for removing this MBean from the
agent. Unregistering is covered in <link linkend="htmladaptor-5" lang="en">"Instantiating and Managing MBeans"</link>. </para><para lang="en">The MBean description text provides some information about the MBean.
Because standard MBeans are statically defined, they cannot describe themselves,
and the MBean server provides a generic text. Dynamic MBeans are required
to provide their own description string at runtime according to the JMX specification.
Except for the class name, this is the only way to tell standard and dynamic
MBeans apart in the MBean view. </para></sect2><sect2 id="htmladaptor-9" lang="en"><title lang="en">The Table of Attributes</title><para lang="en">The second part of the MBean view is a table containing all attributes
exposed by the MBean. For each attribute, this table lists its name, its Java
type, its read-write access and a string representation of its current value.</para><para lang="en">While MBean attributes may be of any type, not all types can be displayed
in the MBean view. The HTML adaptor is limited to basic data types that can
be displayed and entered as strings. For the complete list, see the Javadoc
API of the <classname lang="en">HtmlAdaptorServer</classname> class. The table of attributes
contains only those which can be displayed or entered as a string. Read-only
attributes whose type support the <classname lang="en">toString</classname> method are
also displayed.</para><para lang="en">Attributes with array types are represented by a link to a page which
displays the array values in a table. If the attribute is writeable, you may
enter values for the array elements to set them. Attributes of type <classname lang="en">com.sun.jdmk.Enumerated</classname> or its sub-classes are displayed as a
menu with a pop-up selection list.</para><para lang="en">The name of each attribute is a link that pops up a dialog box containing
the description for this attribute. Like the MBean description, attribute
descriptions can only be provided by dynamic MBeans. The MBean server inserts
a generic message for standard MBean attributes. The following figure shows
the attributes of the HTML adaptor with a description of the <literal moreinfo="none" lang="en">Active</literal> attribute:</para><figure float="0" id="htmladaptor-fig-16" lang="en"><gentext type="text">Figure 4-3 </gentext><title lang="en">MBean Attributes with a Description Dialog</title><graphic filename="figures/HTMLadaptor.attr.gif" width="337" depth="268" scale="66" id="htmladaptor-gr-41" lang="en"/></figure><procedure id="htmladaptor-proc-26" lang="en"><gentext type="text"></gentext><title lang="en">Viewing Attribute Descriptions</title><step performance="required" id="htmladaptor-step-42" lang="en"><para lang="en">Click on the attribute names of the HTML adaptor to read their description.
Since the HTML adaptor is implemented as a dynamic MBean, its attribute descriptions
are meaningful.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">Due to the use of JavaScript commands in the generated HTML, these pop-up
windows might not be available on browsers that are not JavaScript-enabled.</para></note></step></procedure><para lang="en">Writable attributes have a text field for entering new values. To set
the value of a writable attribute, you would enter or replace its current
value in the text field and click the "Apply" button at the bottom
of the attributes table.</para><para lang="en">You should not try to modify the attributes of the HTML protocol adaptor
here, we will see why in <link linkend="htmladaptor-5" lang="en">"Instantiating and Managing MBeans"</link>. </para><para lang="en"> Because there is only one "Apply" button for all attributes,
this button has a particular behavior: it systematically invokes the setter
for all writeable attributes, whether or not their field has actually been
modified. This may impact the MBean if setters have side effects, such as
counting the number of modifications as in the <classname lang="en">SimpleStandard</classname>
and <classname lang="en">SimpleDynamic</classname> examples given in <link linkend="standard-1" lang="en">Chapter 1, Standard MBeans</link>
and <link linkend="dynamic-1" lang="en">Chapter 2, Dynamic MBeans</link>.</para><para lang="en">The HTML adaptor detects attributes which are <classname lang="en">ObjectName</classname> types and provides a link to the MBean view of the corresponding
MBean. This link is labeled <literal moreinfo="none" lang="en">view</literal>, and it is located just
under the displayed value of the object name. Since MBeans often need to reference
other MBeans, this provides a quick way of navigating through MBean hierarchies.</para></sect2><sect2 id="htmladaptor-8" lang="en"><title lang="en">The Operations</title><para lang="en">The last part of the MBean view contains all of the operations exposed
by the MBean. Each operation in the list is presented like a method signature:
there is a return type, then a button with the operation name, and if applicable,
a list of parameters, each with their type as well.</para><para lang="en">As with the table of attributes, the list of operations contains only
those involving types that can be represented as strings. The return type
must support the <classname lang="en">toString</classname> method and the type of each
parameter must be one of basic data types supported by the HTML adaptor. For
the complete list, see the Javadoc API of the <classname lang="en">HtmlAdaptorServer</classname> class.</para><para lang="en">Above each operation is a link to its description. Parameter names are
also active links which pop up a window with a description text. Again, descriptions
are only meaningful when provided by dynamic MBeans. The following figure
shows some of the operations exposed by the HTML adaptor MBean and a description
of the <literal moreinfo="none" lang="en">Start</literal> operation.</para><figure float="0" id="htmladaptor-fig-17" lang="en"><gentext type="text">Figure 4-4 </gentext><title lang="en">MBean Operations with a Description Dialog (Partial View)</title><graphic filename="figures/HTMLadaptor.oper.gif" width="339" depth="224" scale="66" id="htmladaptor-gr-43" lang="en"/></figure><para lang="en">We will not invoke any operations on this MBean until a brief explanation
in <link linkend="htmladaptor-5" lang="en">"Instantiating and Managing MBeans"</link>. If we stopped the HTML adaptor or blocked
it with an error, there would no longer be any way to connect with a browser:
we would have to stop the minimal agent and restart it. </para><para lang="en">To invoke an operation, you would fill in any and all parameter values
in the corresponding text fields and then click the operation's button. The
HTML adaptor would then display a page with the result of the operation: the
return value if successful or the reason the operation was unsuccessful.  </para></sect2></sect1><sect1 id="htmladaptor-4" lang="en"><title lang="en">Agent Administration</title><procedure id="htmladaptor-proc-27" lang="en"><gentext type="text"></gentext><title lang="en">Preparation</title><step performance="required" id="htmladaptor-step-44" lang="en"><para lang="en">Go back to the agent view by clicking the link near the top of the MBean
view page. Then click on the "Admin" button in the agent view
to bring up the agent administration page in your browser window.</para></step></procedure><para lang="en">The agent administration page contains a form for entering MBean information
when creating or unregistering MBeans. You may also instantiate an MBean through
one of its public constructors. In order to instantiate an MBean, its class
must be available in the agent application's classpath. Optionally, you may
specify a different class loader in the appropriate field if the agent contains
other class loader MBeans.</para><para lang="en">The first two fields, "Domain" and "Keys" are
mandatory for all administrative actions. The "Domain" field initially
contains the string representing the agent's default domain. Together, these
fields define the object name, whether for a new MBean to be created or the
name of an existing MBean to unregister. The "Java Class" is the
full class name of the object to be instantiated as a new MBean. This field
is ignored when unregistering an MBean. </para><para lang="en">Using the drop-down menu, you may select one of three actions on this
page:  </para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">Create - Instantiates the given Java class of an MBean and
registers the new instance in the MBean server. If successful, the MBean will
then appear in the agent view. The class must have a public constructor without
parameters in order to be created in this way.</para></listitem><listitem lang="en"><para lang="en">Unregister - Unregisters an MBean from the MBean
server so that it is no longer available in the agent. The class instance
is not explicitly deleted, though if no other references to it exist, it will
be garbage collected.</para></listitem><listitem lang="en"><para lang="en">Constructors - Displays the list of public constructors
at the bottom of the administration page for the given Java class. This lets
you provide parameters to a specific constructor and create the MBean in this
manner. This is the only way to create MBeans which do not have a no-parameter
constructor.</para></listitem></itemizedlist><para lang="en">When you click the "Send Request" button, the HTML adaptor
processes the action and updates the bottom of the page with the action results.
You may have to scroll down to see the result. The text fields are not cleared
after a request so that you can do multiple operations. The "Reset"
button will return the fields to their last posted value after you have modified
them. </para></sect1><sect1 id="htmladaptor-5" lang="en"><title lang="en">Instantiating and Managing MBeans</title><para lang="en">Sometimes, launching an MBean requires several steps: this is particularly
the case for agent services which require some sort of configuration. For
example, you can instantiate another HTML adaptor for connecting to a different
port. Usually, this would be done programmatically in the agent application,
but we need to do it through the browser for the minimal agent.</para><procedure id="htmladaptor-proc-28" lang="en"><gentext type="text"></gentext><title lang="en">Creating a New HTML Adaptor MBean</title><step performance="required" id="htmladaptor-step-45" lang="en"><para lang="en">On the agent administration page, fill in the fields as follows:</para><informaltable frame="all" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="colspec1" colwidth="29.16*" align="right"/><colspec colname="colspec2" colwidth="70.84*" align="left"/><tbody lang="en"><row rowsep="0" lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Domain:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">Communications</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Keys:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">protocol=html,port=8088</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Java Class:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">com.sun.jdmk.comm.HtmlAdaptorServer</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Class Loader:</literal></entry><entry colname="colspec2" lang="en"><emphasis lang="en">leave blank</emphasis></entry></row></tbody></tgroup></informaltable><para lang="en">Note: In previous versions of product, specifying the port number in
the object name would initialize communication MBeans. Starting with the Java Dynamic Management Kit
4.0, the names and contents of key properties no longer have any significance
for any components of the product. We must set the port in other ways.</para></step><step performance="required" id="htmladaptor-step-29" lang="en"><para lang="en">Make sure the selected action is "Create" and send the request.
If you scroll down the page, you should see if your request was successful.</para><para lang="en">We can't connect to this HTML adaptor quite yet, we need to configure
it first. </para></step><step performance="required" id="htmladaptor-step-30" lang="en"><para lang="en">Go to the new HTML adaptor's MBean view with the provided link. </para><para lang="en">We couldn't modify any of the adaptor's attributes before because the
implementation is designed so that they can't be modified while it is on-line.
Our new HTML adaptor is instantiated in the stopped state (the <literal moreinfo="none" lang="en">StateString</literal> attribute indicates "OFFLINE"), so we can change its
attributes. </para></step><step performance="required" id="htmladaptor-step-31" lang="en"><para lang="en">Set the <literal moreinfo="none" lang="en">Port</literal> attribute to "8088" and <literal moreinfo="none" lang="en">MaxActiveClientCount</literal> to "2", then click the "Apply"
button. If the page is reloaded and the new values are displayed, the attribute
write operation was successful. You may also click the attribute names to
get an explanation for them. </para></step><step performance="required" id="htmladaptor-step-32" lang="en"><para lang="en">Scroll down the MBean view to the <literal moreinfo="none" lang="en">Start</literal> operation
and click its button. This brings up a new page to tell us the operation was
successful. If you go back to the MBean view with the provided link, you can
see that the <literal moreinfo="none" lang="en">StateString</literal> is now indicating <literal moreinfo="none" lang="en">ONLINE</literal>.</para></step><step performance="required" id="htmladaptor-step-33" lang="en"><para lang="en">Now you should be able to access your minimal agent through a browser
on port 8088. Try going to a different machine on the same network and connecting
to the URL:</para><informaltable frame="none" lang="en"><tgroup cols="1" colsep="0" rowsep="0" align="center" lang="en"><colspec colwidth="50*"/><tbody lang="en"><row lang="en"><entry lang="en"><para lang="en"><ulink url="http://machineName:8088/">http://<replaceable lang="en">agentHostName</replaceable>:8088/</ulink></para></entry></row></tbody></tgroup></informaltable><para lang="en">where <replaceable lang="en">agentHostName</replaceable> is the name or IP address
of the machine where you launched the <classname lang="en">MinimalAgent</classname>.
If you reload the MBean view of the new HTML adaptor on either browser, the
name of this other machine should be the new value of the <literal moreinfo="none" lang="en">LastConnectedClient</literal> attribute. </para></step></procedure><para lang="en">Through this other connection, you could stop, modify or remove the
HTML adaptor MBean using port 8082. In that case, your original browser will
have to use <ulink url="http://localhost:8088/">http://localhost:8088/</ulink>
as well to connect. Instead, we will manage the minimal agent from this other
machine.</para><procedure id="htmladaptor-proc-30" lang="en"><gentext type="text"></gentext><title lang="en">Instantiating MBeans with Constructors</title><step performance="required" id="htmladaptor-step-46" lang="en"><para lang="en">From the browser on the other machine, go to the administration page.
Fill in the fields as follows and request the constructors:</para><informaltable frame="all" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="colspec1" colwidth="29.16*" align="right"/><colspec colname="colspec2" colwidth="70.84*" align="left"/><tbody lang="en"><row rowsep="0" lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Domain:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">Standard_MBeans</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Keys:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">name=SimpleStandard,number=1</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Java Class:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">SimpleStandard</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Class Loader:</literal></entry><entry colname="colspec2" lang="en"><emphasis lang="en">leave blank</emphasis></entry></row></tbody></tgroup></informaltable><para lang="en">The list of constructors for the <classname lang="en">SimpleStandard</classname>
class is given at the bottom of the page. The MBean name is also given: this
is the object name that will be assigned to the MBean when using one of the
listed constructors. As you can see, the <classname lang="en">SimpleStandard</classname>
class only has one constructor that takes no parameters. </para></step><step performance="required" id="htmladaptor-step-31a" lang="en"><para lang="en">Click on the "Create" button: the result will be appended
to the bottom of the page. Scroll down and go to the MBean view with the provided
link. </para><para lang="en">Since it is a standard MBean, all of its description strings are generic:
this shows the necessity of programming meaningful attribute names. </para></step><step performance="required" id="htmladaptor-step-32a" lang="en"><para lang="en">In the agent view on the original browser window, click in the filter
field and hit "Return" to refresh the agent view. Click on the
new MBean's name and set its reload period to 15. Back on the other machine,
type in a different string for the <literal moreinfo="none" lang="en">State</literal> attribute and
click "Apply". </para><para lang="en">On the original machine, you should see the MBean's attributes get updated
when the MBean view is periodically reloaded. </para></step><step performance="required" id="htmladaptor-step-33a" lang="en"><para lang="en">On the other machine, click the <literal moreinfo="none" lang="en">reset</literal> operation button
at the bottom of the MBean view page. This brings up the operation result
page which indicates the success of the operation.</para><para lang="en">This page also gives the return value of the operation when it is not
void. If you go back to the MBean view, you will see the result of the operation
on the attributes. You should also see it on the original machine after it
reloads. </para></step></procedure><para lang="en">The browser on the other machine is no longer needed, and we can remove
the HTML adaptor on port 8088.</para><procedure id="htmladaptor-proc-34" lang="en"><gentext type="text"></gentext><title lang="en">Unregistering MBeans</title><step performance="required" id="htmladaptor-step-47" lang="en"><para lang="en">Go to the administration page and fill in the object name of the HTML
adaptor we want to remove (you don't need its Java class to unregister it):</para><informaltable frame="all" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="colspec1" colwidth="29.16*" align="right"/><colspec colname="colspec2" colwidth="70.84*" align="left"/><tbody lang="en"><row rowsep="0" lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Domain:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">Communications</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Keys:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">protocol=html,port=8088</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Java Class:</literal></entry><entry colname="colspec2" lang="en"><emphasis lang="en">leave blank</emphasis></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Class Loader:</literal></entry><entry colname="colspec2" lang="en"><emphasis lang="en">leave blank</emphasis></entry></row></tbody></tgroup></informaltable></step><step performance="required" id="htmladaptor-step-31b" lang="en"><para lang="en">Select "Unregister" from the drop down menu and click the "Send
Request" button. The result then appears at the bottom of the page.</para><para lang="en">You can also unregister an MBean directly from its MBean view: just
click the "Unregister" button on the upper right hand-side of
the page.</para></step></procedure></sect1><sect1 id="htmladaptor-6" lang="en"><title lang="en">Filtering MBeans</title><para lang="en">Since an agent can manage hundreds of MBeans, the agent view provides
a filtering mechanism for the list that is displayed. An object name with
wildcard characters is used as the filter, and only those MBeans which match
are counted and displayed.</para><procedure id="htmladaptor-proc-32" lang="en"><gentext type="text"></gentext><title lang="en">Preparation</title><step performance="required" id="htmladaptor-step-48" lang="en"><para lang="en">Go to the administration page, and create three more of the standard
MBeans. Modify only the <literal moreinfo="none" lang="en">number</literal> value in their object name
so that they are numbered sequentially. In the same way, create four dynamic
MBeans starting with:</para><informaltable frame="all" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="colspec1" colwidth="29.16*" align="right"/><colspec colname="colspec2" colwidth="70.84*" align="left"/><tbody lang="en"><row rowsep="0" lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Domain:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">Dynamic_MBeans</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Keys:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">name=SimpleDynamic,number=1</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Java Class:</literal></entry><entry colname="colspec2" lang="en"><literal moreinfo="none" lang="en"><userinput moreinfo="none" lang="en">SimpleDynamic</userinput></literal></entry></row><row lang="en"><entry colname="colspec1" lang="en"><literal moreinfo="none" lang="en">Class Loader:</literal></entry><entry colname="colspec2" lang="en"><emphasis lang="en">leave blank</emphasis></entry></row></tbody></tgroup></informaltable></step><step performance="required" id="htmladaptor-step-33b" lang="en"><para lang="en">Go back to the agent view which should display all of the new MBeans.</para></step></procedure><para lang="en">Filters restrict the set of MBeans listed in the agent view. This may
not be particularly useful for our small agent, but it can help you find MBeans
among hundreds in a complex agent. In addition, management applications use
the same filter syntax when requesting an agent's MBeans through the programmatic
interface of a connector. The filtering lets managers get either lists of
MBean names or find a particular MBean instance.</para><para lang="en">Filters are entered as partial object names with wild-card characters
or as a full object name for which to search. Here are the basic substitution
rules for filtering: </para><orderedlist continuation="restarts" lang="en"><listitem lang="en"><para lang="en">You may search for partial <emphasis lang="en">domain</emphasis> names:the
asterisk (<literal moreinfo="none" lang="en">*</literal>) stands for any number (including zero) of
any characters;the question mark (<literal moreinfo="none" lang="en">?</literal>) stands
for any one character</para></listitem><listitem lang="en"><para lang="en">An empty domain name stands for the default domain
string;an empty key list is illegal</para></listitem><listitem lang="en"><para lang="en">Keys are atomic: you must search for the full <literal moreinfo="none" lang="en">property=value</literal> key, you may not search for a partial property name
or an incomplete value</para></listitem><listitem lang="en"><para lang="en">The asterisk (<literal moreinfo="none" lang="en">*</literal>) may be used
to terminate the key list, where it stands for any number of any keys (complete
property-value pairs)</para></listitem><listitem lang="en"><para lang="en">You must match all keys exactly: use the form <literal moreinfo="none" lang="en">property=value,*</literal> to search for one key in names with multiple keys</para></listitem><listitem lang="en"><para lang="en">Keys are unordered when filtering: giving one or
more keys (and an asterisk) finds all object names which contain that subset
of keys</para></listitem></orderedlist><para lang="en">Enter the following filter strings to see the resulting MBean list: </para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="0" lang="en"><colspec colname="colspec0" colwidth="55.18*" align="char" char=":" charoff="38"/><colspec colname="colspec1" colwidth="44.82*" align="left"/><tbody lang="en"><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">Standard_MBeans:*</literal></entry><entry colname="colspec1" lang="en">Gives all of the standard MBeans we created</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">*_MBeans:*</literal></entry><entry colname="colspec1" lang="en">Gives all of the standard and dynamic MBeans we created</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">  DefaultDomain:</literal></entry><entry colname="colspec1" lang="en">Not allowed by rule 2</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">:*</literal></entry><entry colname="colspec1" lang="en">Lists all MBeans in the default domain</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">*:name=Simple*,*</literal></entry><entry colname="colspec1" lang="en">Not allowed by rule 3</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">*:name=SimpleStandard</literal></entry><entry colname="colspec1" lang="en">Allowed, but list is empty (rule 5)</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">*:name=*</literal></entry><entry colname="colspec1" lang="en">Not allowed by rule 3</entry></row><row lang="en"><entry colname="colspec0" lang="en"><literal moreinfo="none" lang="en">*_??????:number=2,*</literal></entry><entry colname="colspec1" lang="en">Gives the second standard and dynamic MBean we created</entry></row><row lang="en"><entry colname="colspec0" lang="en"> <literal moreinfo="none" lang="en">Communications:port=8088,protocol=html</literal></entry><entry colname="colspec1" lang="en">Gives the one MBean matching the domain and both (unordered) keys </entry></row><row lang="en"><entry colname="colspec0" align="center" lang="en"><emphasis lang="en">empty string</emphasis></entry><entry colname="colspec1" lang="en">allowed: special case equivalent to <literal moreinfo="none" lang="en">*:*</literal></entry></row></tbody></tgroup></informaltable><para lang="en">Notice how the MBean count is updated with each filter: this count gives
the number of MBeans that were found with the current filter, which is the
number of MBeans appearing on the page. It is not the total number of MBeans
in the agent, unless the filter is <literal moreinfo="none" lang="en">*:*</literal>.</para><procedure id="htmladaptor-proc-34a" lang="en"><gentext type="text"></gentext><title lang="en">Quitting</title><step performance="required" id="htmladaptor-step-49" lang="en"><para lang="en">When you are ready to stop the minimal agent example, go to the window
where you launched its class and type Control-C.</para></step></procedure></sect1></chapter><chapter id="baseagent-1" lang="en"><gentext type="text">Chapter 5</gentext><gentext type="toc">5.  The Base Agent</gentext><title lang="en">The Base Agent</title><highlights lang="en"><para lang="en">The base agent demonstrates the programming details of writing an agent application. We will cover how to access the MBean server, ask it to create MBeans, and then interact with those MBeans. Everything that we could do through the HTML adaptor can be done through the code of your agent application.</para><para lang="en">Interacting programmatically with the MBean server gives you more flexibility in your agent application. This topic covers the three main ways to create MBeans through the MBean server, how to interact with MBeans, and how to unregister them. </para><para lang="en">The base agent is functionally equivalent to the minimal agent, but instead of writing the smallest agent, we will demonstrate good defensive programming with error checking. We will create the same three connectivity MBeans and do some of the same management operations that we did through the browser interface.</para><para lang="en">The program listings in this tutorial show only functional code: comments and output statements have been modified or removed for space considerations. However, all management functionality has been retained for the various demonstrations. The complete source code is available in the <filename moreinfo="none" lang="en">BaseAgent</filename> and <filename moreinfo="none" lang="en">StandardMBean</filename> example directories located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> (see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="baseagent-12" lang="en">"The Agent Application"</link> shows how to launch the base agent programmatically</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-2" lang="en">"Creating an MBean (Method 1)"</link> relies on the MBean server's <classname lang="en">register</classname> method after instantiating the MBean class ourselves</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-3" lang="en">"Creating an MBean (Method 2)"</link> relies on the MBean server's <classname lang="en">create</classname> method to instantiate and register an MBean in one step</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-4" lang="en">"Creating an MBean (Method 3)"</link> relies on the MBean server's <classname lang="en">instantiate</classname> and <classname lang="en">register</classname> methods</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-6" lang="en">"Managing MBeans"</link> demonstrates the same management operations that we did through the HTML protocol adaptor</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-5" lang="en">"Filtering MBeans"</link> shows how to get various lists of MBeans from the MBean server</para></listitem><listitem lang="en"><para lang="en"><link linkend="baseagent-14" lang="en">"Running the Base Agent Example"</link> demonstrates its run-time behavior</para></listitem></itemizedlist></highlights><sect1 id="baseagent-12" lang="en"><title lang="en">The Agent Application</title><para lang="en">The base agent is a stand-alone application with a <classname lang="en">main</classname> method, but it also has a constructor so that it may be instantiated dynamically by another class.</para><example role="code" id="baseagent-ex-13" lang="en"><gentext type="text">Example 5-1 </gentext><title lang="en">Constructor for the Base Agent</title><programlisting format="linespecific" lang="en" role="complete">public BaseAgent() {
    // Enable the TRACE level according to the level set in system properties
    try {
        Trace.parseTraceProperties();
    }
    catch (IOException e) {
        e.printStackTrace();
        System.exit(1);
    }

    println("\n\tInstantiating the MBean server of this agent...");
    myMBeanServer = MBeanServerFactory.createMBeanServer();

    // Retrieves ID of the MBean server from the delegate
    try {
        System.out.println("ID = "+ myMBeanServer.getAttribute(
            new ObjectName(ServiceName.DELEGATE), "MBeanServerId"));
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    }
}</programlisting></example><para lang="en">In the first statement of the constructor, we enable tracing messages for the agent. The tracing lets us see internal information at runtime, which is useful for debugging. See <link linkend="baseagent-20" lang="en">"Setting Trace Messages"</link> for specifying tracing parameters on the command line. Then we create the MBean server through its static factory class (see <link linkend="minimal-6" lang="en">"The MBean Server Implementation and Factory"</link>). Its reference is stored in a variable with class-wide scope so that all internal methods have access to the MBean server. Finally, we retrieve the MBean server identification string, for informational purposes only.</para><para lang="en">After the MBean server is initialized, we are going to create the same three communications MBeans that we saw in the minimal agent.</para></sect1><sect1 id="baseagent-2" lang="en"><title lang="en">Creating an MBean (Method 1)</title><para lang="en">The methods of the MBean server let you create an MBean in three different ways. The base agent demonstrates all three ways, and we will discuss the advantages of each.</para><para lang="en">One way of creating an MBean consists of first instantiating its class and then registering this instance in the MBean server. Registration is the internal process of the MBean server which takes a manageable resource's MBean instance and exposes it for management.</para><para lang="en">Bold text in this and the following code samples highlights the important statements that vary between the three methods.</para><example role="code" id="baseagent-ex-9" lang="en"><gentext type="text">Example 5-2 </gentext><title lang="en">Creating an MBean (Method 1)</title><programlisting format="linespecific" lang="en" role="complete">// instantiate the HTML protocol adaptor object to use the default port
HtmlAdaptorServer <userinput moreinfo="none" lang="en">htmlAdaptor = new HtmlAdaptorServer()</userinput>;

try {
    // We know that the HTML adaptor provides a default object name
    ObjectInstance htmlAdaptorInstance =
        <userinput moreinfo="none" lang="en">myMBeanServer.registerMBean(htmlAdaptor, null)</userinput>;
    println("CLASS NAME  = " + htmlAdaptorInstance.getClassName());
    println("OBJECT NAME = " + 
        htmlAdaptorInstance.getObjectName().toString());
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

// Now we need to explicitly start the html protocol adaptor
<userinput moreinfo="none" lang="en">htmlAdaptor.start()</userinput>;
    
while (htmlAdaptor.getState() == CommunicatorServer.STARTING) {
    sleep(1000);
}
println("STATE = " + <userinput moreinfo="none" lang="en">htmlAdaptor.getStateString()</userinput>);
[...]</programlisting></example><para lang="en">In this first case, we instantiate the <classname lang="en">HtmlAdaptorServer</classname> class and keep a reference to this object. We then pass it to the <classname lang="en">registerMBean</classname> method of the MBean server to make our instance manageable in the agent. During the registration, the instance can also obtain a reference to the MBean server, something it requires to function as a protocol adaptor.</para><para lang="en">In the minimal agent, we saw that the HTML adaptor gives itself a default name in the default domain. Its Javadoc API confirms this, so we can safely let it provide a default name. We print the object name in the object instance returned by the registration to confirm that the default was used.</para><para lang="en">Once the MBean is registered, we can perform management operations on it. Because we kept a reference to the instance, we don't need to go through the MBean server to manage this MBean. This lets us call the <classname lang="en">start</classname> and <classname lang="en">getStateString</classname> methods directly. The fact that these methods are publicly exposed is particular to the implementation: the HTML adaptor is a dynamic MBean, so without any prior knowledge of the class, we would have to go through its <classname lang="en">DynamicMBean</classname> interface.</para><para lang="en">In a standard MBean you would directly call the implementation of its management interface. Since the HTML adaptor is a dynamic MBean, the <classname lang="en">start</classname> method is just a shortcut for the <literal moreinfo="none" lang="en">start</literal> operation. For example, we could start the adaptor and get its <literal moreinfo="none" lang="en">StateString</literal> attribute with the following calls:</para><programlisting format="linespecific" lang="en" role="fragment">htmlAdaptor.invoke("start", new Object[0], new String[0]);
println("STATE = " + (String)htmlAdaptor.getAttribute("StateString"));</programlisting><para lang="en">This type of shortcut is not specified by the Java Management extensions, nor is its functionality necessarily identical to that of the <literal moreinfo="none" lang="en">start</literal> operation exposed for management. In the case of the HTML adaptor, its Javadoc API confirms that it is identical, and in other cases, it is up to the MBean programmer to guarantee this functionality if it is offered.</para></sect1><sect1 id="baseagent-3" lang="en"><title lang="en">Creating an MBean (Method 2)</title><para lang="en">The second way to create an MBean is the single <classname lang="en">createMBean</classname> method of the MBean server. In this case, the MBean server instantiates the class and registers it all at once. As a result, the caller never has a direct reference to the new object.</para><example role="code" id="baseagent-ex-10" lang="en"><gentext type="text">Example 5-3 </gentext><title lang="en">Creating an MBean (Method 2)</title><programlisting format="linespecific" lang="en" role="complete">ObjectInstance httpConnectorInstance = null;
try {
    String httpConnectorClassName = "com.sun.jdmk.comm.HttpConnectorServer";
    // Let the HTTP connector server provides its default name
    httpConnectorInstance =
        <userinput moreinfo="none" lang="en">myMBeanServer.createMBean(httpConnectorClassName, null)</userinput>;
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}
// We need the object name to refer to our MBean
ObjectName httpConnectorName = httpConnectorInstance.getObjectName();
println("CLASS NAME  = " + httpConnectorInstance.getClassName());
println("OBJECT NAME = " + httpConnectorName.toString());

// Now we demonstrate the bulk getter of the MBean server
try {
    String att1 = "Protocol";
    String att2 = "Port";
    String attNames[]= {att1, att2};
    AttributeList attList =
        myMBeanServer.getAttributes(httpConnectorName, attNames);
    Iterator attValues = attList.iterator();
    println(att1 + "\t=" + ((Attribute) attValues.next()).getValue());
    println(att2 + "\t=" + ((Attribute) attValues.next()).getValue());

} catch (Exception e) {
    e.printStackTrace();
    System.exit(0);
}

// Now we explicitly start the HTTP connector server
try {
    <userinput moreinfo="none" lang="en">myMBeanServer.invoke(httpConnectorName, "start",</userinput>
                         <userinput moreinfo="none" lang="en">new Object[0], new String[0])</userinput>;

    // waiting to leave starting state...
    while (new Integer(CommunicatorServer.STARTING).equals
           (myMBeanServer.getAttribute(httpConnectorName,"State"))) {
        sleep(1000);
    }
    println("STATE = " + 
         <userinput moreinfo="none" lang="en">myMBeanServer.getAttribute(httpConnectorName, "StateString")</userinput>);
} catch (Exception e) {
    e.printStackTrace();
    System.exit(0);
}
[...]</programlisting></example><para lang="en">The advantage of this method for creating MBeans is that the instantiation and registration are done in one call. In addition, if we have registered any class loaders in the MBean server, they will automatically be used if the class is not available locally. See <link linkend="mletloader-1" lang="en">"The M-Let Class Loader"</link> for more information on class loading.</para><para lang="en">The <classname lang="en">createMBean</classname> method has several forms, some with parameters for class constructors and some without. In this examples we don't give any parameters for the constructor because we wish to use the default constructor that takes no arguments. If your MBean class has no default constructor, you must use a form that allows you to pass parameters to a constructor.</para><para lang="en">The major difference is that we no longer have a reference to our MBean instance. The object instance that was only used for display purposes in the previous example now gives us the only reference we have on the MBean: its object name.</para><para lang="en">What can be seen as a drawback of this method is that all management of the new MBean must now be done through the MBean server. For the attributes of the MBean, we need to call the generic getter and setter of the MBean server, and for the operations we need to call the <classname lang="en">invoke</classname> method. When the agent needs to access the MBean, having to go through the MBean server adds some complexity to the code. It does have the advantage of not relying on any shortcuts provided by the MBean, making the code more portable and reusable.</para><para lang="en">However, the <classname lang="en">createMBean</classname> method is ideal for quickly launching new MBeans that the agent application doesn't need to manipulate. In just one call, the new objects are instantiated and exposed for management.</para></sect1><sect1 id="baseagent-4" lang="en"><title lang="en">Creating an MBean (Method 3)</title><para lang="en">The last way of creating an MBean relies on the <classname lang="en">instantiate</classname> method of the MBean server. In addition, we use a non-default constructor to instantiate the class with a different behavior.</para><example role="code" id="baseagent-ex-11" lang="en"><gentext type="text">Example 5-4 </gentext><title lang="en">Creating an MBean (Method 3)</title><programlisting format="linespecific" lang="en" role="complete">CommunicatorServer rmiConnector = null;
Object[] params = {new Integer(8086)};
String[] signature = {new String("int")};
try {
    String RmiConnectorClassName = "com.sun.jdmk.comm.RmiConnectorServer";
    // specify the RMI port number to use as a parameter to the constructor
    <userinput moreinfo="none" lang="en">rmiConnector = (CommunicatorServer)myMBeanServer.instantiate(</userinput>
                       <userinput moreinfo="none" lang="en">RmiConnectorClassName, params, signature)</userinput>;
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

try {
    // Let the RMI connector server provides its default name
    ObjectInstance rmiConnectorInstance =
        <userinput moreinfo="none" lang="en">myMBeanServer.registerMBean(rmiConnector, null)</userinput>;
    
    // Confirm the class and default object name
    println("CLASS NAME  = " + rmiConnectorInstance.getClassName());
    println("OBJECT NAME = " + 
        rmiConnectorInstance.getObjectName().toString());
} catch(Exception e) {
    e.printStackTrace();
    System.exit(0);
}

// Now we explicitly start the RMI connector server
<userinput moreinfo="none" lang="en">rmiConnector.start()</userinput>;

// waiting to leave starting state...
while (rmiConnector.getState() == CommunicatorServer.STARTING) {
    sleep(1000);
}
println("STATE = " + <userinput moreinfo="none" lang="en">rmiConnector.getStateString()</userinput>);

// Check that the RMI connector server is started
if (rmiConnector.getState() != CommunicatorServer.ONLINE) {
    println("Cannot start the RMI connector server");
    System.exit(0);
}
[...]</programlisting></example><para lang="en">As in the first example of MBean creation, we instantiate and register the MBean in separate steps. First, we instantiate the class using the <classname lang="en">instantiate</classname> method of the MBean server. This method lets you specify the constructor you wish to use when instantiating. Note that we could also have specified a constructor to the <classname lang="en">createMBean</classname> method in the previous example.</para><para lang="en">To specify a constructor, you give an array of objects for the parameters and an array of strings which defines the signature. If these arrays are empty or null, the MBean server will try to use the default no-parameter constructor. If the class does not have a default constructor, you must specify the parameters and signature of a valid, public constructor.</para><para lang="en">In our case, we specify an integer parameter to set the port through one of the constructors of the <classname lang="en">RmiConnectorServer</classname> class. Then, we register the MBean with the <classname lang="en">registerMBean</classname> method of the MBean server, as in the first example.</para><para lang="en">The advantage of this creation method is that the instantiate method of the MBean server also supports class loaders. If any are registered in the MBean server, they will automatically be used if the class is not available locally. See <link linkend="mletloader-1" lang="en">"The M-Let Class Loader"</link> for more information on class loading.</para><para lang="en">Since we don't take advantage of the class loaders here, we could have just called the class' constructor directly. The main advantage is that, like the first method of MBean creation, we retain a direct reference to the new object. The direct reference lets us again use the MBean's shortcut methods explicitly.</para><para lang="en">There are other combinations of instantiating and registering MBeans for achieving the same result. For example, we could use the default constructor and then set the <literal moreinfo="none" lang="en">port</literal> attribute of the MBean before starting it. Other combinations are left as an exercise to the reader.</para></sect1><sect1 id="baseagent-6" lang="en"><title lang="en">Managing MBeans</title><para lang="en">In <link linkend="baseagent-3" lang="en">"Creating an MBean (Method 2)"</link>, we rely totally on the MBean server to create and access an MBean. The code example in that section demonstrates how to get attributes and invoke operations through the MBean server. Here we will concentrate on the usage of MBean descriptor classes when accessing MBeans representing resources.</para><para lang="en">We will rely on the <classname lang="en">StandardAgent</classname> and <classname lang="en">DynamicAgent</classname> classes presented in <link linkend="standard-1" lang="en">Chapter 1, Standard MBeans</link> and <link linkend="dynamic-1" lang="en">Chapter 2, Dynamic MBeans</link>. As mentioned in <link linkend="dynamic-17" lang="en">"Comparison with the <classname lang="en">SimpleStandard</classname> Example"</link>, the two are nearly identical. We examine the MBean descriptor method that is common to both: the same code works for any registered MBean, whether standard or dynamic.</para><example role="code" id="baseagent-ex-17" lang="en"><gentext type="text">Example 5-5 </gentext><title lang="en">Processing MBean Information</title><programlisting format="linespecific" lang="en" role="complete">private MBeanServer server = null; // assigned by MBeanServerFactory

private void printMBeanInfo(ObjectName name) {

    println("Getting the management information for  " + name.toString() );
    MBeanInfo info = null;

    try {
        info = server.getMBeanInfo(name);
    } catch (Exception e) {
        e.printStackTrace();
        return;
    }
    println("\nCLASSNAME: \t"+ info.getClassName());
    println("\nDESCRIPTION: \t"+ info.getDescription());

    println("\nATTRIBUTES");
    MBeanAttributeInfo[] attrInfo = info.getAttributes();
    if (attrInfo.length&gt;0) {
        for(int i=0; i&lt;attrInfo.length; i++) {
            println(" ** NAME: \t"+ attrInfo[i].getName());
            println("    DESCR: \t"+ attrInfo[i].getDescription());
            println("    TYPE: \t"+ attrInfo[i].getType() +
                 "\tREAD: "+ attrInfo[i].isReadable() +
                 "\tWRITE: "+ attrInfo[i].isWritable());
        }
    } else println(" ** No attributes **");

    println("\nCONSTRUCTORS");
    MBeanConstructorInfo[] constrInfo = info.getConstructors();
    // Note: the class necessarily has at least one constructor
    for(int i=0; i&lt;constrInfo.length; i++) {
        println(" ** NAME: \t"+ constrInfo[i].getName());
        println("    DESCR: \t"+ constrInfo[i].getDescription());
        println("    PARAM: \t"+ constrInfo[i].getSignature().length +
                 " parameter(s)");
    }

    println("\nOPERATIONS");
    MBeanOperationInfo[] opInfo = info.getOperations();
    if (opInfo.length&gt;0) {
        for(int i=0; i&lt;constrInfo.length; i++) {
            println(" ** NAME: \t"+ opInfo[i].getName());
            println("    DESCR: \t"+ opInfo[i].getDescription());
            println("    PARAM: \t"+ opInfo[i].getSignature().length +
                     " parameter(s)");
        }
    } else println(" ** No operations ** ");

    println("\nNOTIFICATIONS");
    MBeanNotificationInfo[] notifInfo = info.getNotifications();
    if (notifInfo.length&gt;0) {
        for(int i=0; i&lt;constrInfo.length; i++) {
            println(" ** NAME: \t"+ notifInfo[i].getName());
            println("    DESCR: \t"+ notifInfo[i].getDescription());
        }
    } else println(" ** No notifications **");
}</programlisting></example><para lang="en"> The <classname lang="en">getMBeanInfo</classname> method of the MBean server gets the descriptor of an MBean's management interface and hides the MBean's implementation. This method returns an <classname lang="en">MBeanInfo</classname> object which contains the MBean's description. We can also get the lists of attributes, operations, constructors, and notifications to display their descriptions. Recall that the dynamic MBean provides its own meaningful descriptions and that those of the standard MBean are default strings provided by the introspection mechanism of the MBean server.</para></sect1><sect1 id="baseagent-5" lang="en"><title lang="en">Filtering MBeans</title><para lang="en">The base agent does very little filtering because it does very little management. Usually, filters are applied programmatically in order to get a list of MBeans to which some operations will apply. There are no management operations in the MBean server which apply to a list of MBeans: you must loop through your list and apply the desired operation to each MBean.</para><para lang="en"> Before exiting the agent application, we do a query of all MBeans so that we can unregister them properly. MBeans should be unregistered before being destroyed since they may need to perform some actions before or after being unregistered. See "MBean Registration Control" in the JMX agent specification for more information.</para><example role="code" id="baseagent-ex-19" lang="en"><gentext type="text">Example 5-6 </gentext><title lang="en">Unregistering MBeans</title><programlisting format="linespecific" lang="en" role="complete">public void removeAllMBeans() {

    try {
        Set allMBeans = myMBeanServer.queryNames(null, null);
        for (Iterator i = allMBeans.iterator(); i.hasNext();) {
            ObjectName name = (ObjectName) i.next();

            // Don't unregister the MBean server delegate
            if ( ! name.toString().equals( ServiceName.DELEGATE ) ) {
                println("Unregistering " + name.toString() );
                myMBeanServer.unregisterMBean(name);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(0);
    }
}</programlisting></example><para lang="en">We use the <classname lang="en">queryNames</classname> method because we only need the object names to operate on MBeans. The null object name as a filter gives us all MBeans in the MBean server. We then iterate through the resulting set and unregister each one, except for the MBean server delegate. As described in <link linkend="htmladaptor-13" lang="en">"The MBean Server Delegate"</link>, the delegate is also an MBean and so it will be returned by the query. However, if we unregister it, the MBean server can no longer function and remove the rest of our MBeans.</para><para lang="en">We recognized the delegate by its standard name which is given by the static field <classname lang="en">ServiceName.DELEGATE</classname>. The <classname lang="en">ServiceName</classname> class provides standard names and other default properties for communications and service MBeans. It also provides the version strings that are exposed by the delegate MBean. Note that, since the delegate is the only MBean created directly by the MBean server, it is the only one whose name cannot be overridden during its registration. This is why the delegate always has the same object name, and we are always sure to detect it.</para></sect1><sect1 id="baseagent-14" lang="en"><title lang="en">Running the Base Agent Example</title><para lang="en">The <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/BaseAgent/</filename> directory contains the source file of the <classname lang="en">BaseAgent</classname> application. Compile the <filename moreinfo="none" lang="en">BaseAgent.java</filename> file in this directory with the <command moreinfo="none" lang="en">javac</command> command. For example, on the Solaris platform, you would type:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/BaseAgent/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput></screen><para lang="en">Again, we don't need the MBean classes at compile time, but they will be needed at run-time, since we don't use a dynamic class loader. You will need to have compiled the standard and dynamic MBean classes as described in <link linkend="standard-7" lang="en">"Running the Standard MBean Example"</link> and <link linkend="dynamic-9" lang="en">"Running the Dynamic MBean Example"</link>. If you wish to load any other class in the base agent, you must include its directory or jar file in the classpath. To run the example, update your classpath to find the MBeans and launch the agent class:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable>:../StandardMBean:../DynamicMBean BaseAgent</userinput></screen><sect2 id="baseagent-20" lang="en"><title lang="en">Setting Trace Messages</title><para lang="en">Since the base agent enables internal tracing (see <link linkend="baseagent-ex-13" lang="en">Example 5-1</link>), you can also set the trace level and trace output on the command line. The tracing mechanism is covered in the Java Dynamic Management Kit 4.0 Tools Reference guide and in the Javadoc API of the <classname lang="en">Trace</classname> class. The simplest way to get the default tracing is to specify the <replaceable lang="en">filename</replaceable> for a trace log on the <command moreinfo="none" lang="en">java</command> command line:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> <option lang="en"><gentext type="text">-</gentext>DTRACE_OUTPUT=<replaceable lang="en">filename</replaceable></option> BaseAgent</userinput></screen></sect2><sect2 id="baseagent-21" lang="en"><title lang="en">Agent Output</title><para lang="en">Besides any trace information, this agent displays output for the three types of MBean creation.</para><para lang="en">When the connection MBeans have been created, it is possible to connect to the agent through one of the protocols. If you connect to the base agent through the HTML adaptor, you could go through the same procedures as with the minimal agent.</para><para lang="en">When you are done, type "Enter" to remove all MBeans from the agent and exit the agent application.</para></sect2></sect1></chapter><chapter id="mletloader-1" lang="en"><gentext type="text">Chapter 6</gentext><gentext type="toc">6.  The M-Let Class Loader</gentext><title lang="en">The M-Let Class Loader</title><highlights lang="en"><para lang="en">The "Dynamic" in Java Dynamic Management Kit not only stands for dynamic loading, it also stands for dynamic <emphasis lang="en">down</emphasis>loading. The agent service that provides this functionality is the m-let class loader. M-let stands for <emphasis lang="en">management applet</emphasis>, an HTML-style tag that tells the class loader how to retrieve the desired class. Using this information, the m-let loader can retrieve an MBean class from a remote location given as a URL (uniform resource locator) and create it in the agent.</para><para lang="en">The m-let resides in a separate text file which acts as a loading manifest. The contents of the m-let file let you specify any number of classes to load, possibly a different source for each, arguments for the class constructor, and the object name for the instantiated MBean. Since this mechanism is sometimes too heavy, the m-let loader can also be used to load classes directly and create MBeans in the agent.</para><para lang="en">The m-let loader is a service implemented as an MBean, so it can be called either directly by the agent or remotely by a management application. It can also be managed remotely, which allows a manager to effectively "push" MBeans to an agent: the manager instantiates the m-let loader in an agent and instructs it to load classes from a predetermined location.</para><para lang="en">Class loading is significantly different between Java 2 and JDK 1.1.<replaceable lang="en">x</replaceable>, and this leads to different implementations of the m-let loader. We cover these in separate sections. The example code in the <filename moreinfo="none" lang="en">MLetAgent</filename> and <filename moreinfo="none" lang="en">MLetClient</filename> directories of the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> corresponds to the Java version which you specified during installation (see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="mletloader-2" lang="en">"The M-Let Loader (JDK 1.1)"</link></para></listitem><listitem lang="en"><para lang="en"><link linkend="mletloader-3" lang="en">"M-Let Loading from a Manager (JDK1.1)"</link></para></listitem><listitem lang="en"><para lang="en"><link linkend="mletloader-4" lang="en">"The M-Let Loader (Java 2)"</link></para></listitem><listitem lang="en"><para lang="en"><link linkend="mletloader-5" lang="en">"M-Let Loading from a Manager (Java 2)"</link></para></listitem></itemizedlist></highlights><sect1 id="mletloader-2" lang="en"><title lang="en">The M-Let Loader (JDK 1.1)</title><para lang="en">In an agent application, we might need to load MBeans from remote hosts during the initialization. Or we might have local threads which need to load MBeans in the agent. In this example we demonstrate how to create the m-let loader service and use it to dynamically load new MBean classes.</para><para lang="en">In an installation of the Java Dynamic Management Kit for the JDK 1.1.<replaceable lang="en">x</replaceable>, the m-let loader is the <classname lang="en">MLetSrv</classname> class in the <classname lang="en">javax.management.loading</classname> package. It is an MBean which needs to be registered in the MBean server before we can use it to load classes.</para><example role="code" id="mletloader-ex-6" lang="en"><gentext type="text">Example 6-1 </gentext><title lang="en">Instantiating the <classname lang="en">MLetSrv</classname> Class</title><programlisting format="linespecific" lang="en" role="complete">MBeanServer server = MBeanServerFactory.createMBeanServer();

// Get the domain name from the MBeanServer.
String domain = server.getDefaultDomain();

// Create a new MLetSrv MBean and add it to the MBeanServer.
String mletClass = "javax.management.loading.MLetSrv";
ObjectName mletName = new ObjectName(domain + ":name=" + mletClass);
server.createMBean(mletClass, mletName);</programlisting></example><para lang="en">There is no special initialization that needs to be done for the m-let loader.</para><sect2 id="mletloader-7" lang="en"><title lang="en">Loading MBeans from a URL</title><para lang="en">In order to download an MBean, we must first have its corresponding m-let definition in an HTML file. In our example, we define the following file with three <literal moreinfo="none" lang="en">MLET</literal> tags:</para><example role="code" id="mletloader-ex-9" lang="en"><gentext type="text">Example 6-2 </gentext><title lang="en">The M-Let File</title><programlisting format="linespecific" lang="en" role="complete">&lt;HTML&gt;
&lt;MLET
  CODE=Square.class
  ARCHIVE=Square.jar
  NAME=MLetExample:name=Square,id=1
&gt;
&lt;ARG TYPE=java.lang.Integer VALUE=10&gt;
&lt;/MLET&gt;
&lt;MLET
  CODE=EquilateralTriangle.class
  ARCHIVE=EquilateralTriangle.jar
  NAME=MLetExample:name=EquilateralTriangle,id=1
&gt;
&lt;ARG TYPE=java.lang.Integer VALUE=8&gt;
&lt;/MLET&gt;
&lt;MLET
  CODE=EquilateralTriangle.class
  ARCHIVE=EquilateralTriangle.jar
  NAME=MLetExample:name=EquilateralTriangle,id=2
&gt;
&lt;ARG TYPE=java.lang.Integer VALUE=15&gt;
&lt;/MLET&gt;
&lt;/HTML&gt;</programlisting></example><para lang="en">This file tells the m-let loader to create three MBeans with the given object names, using the given classes in the jar files. The <literal moreinfo="none" lang="en">ARG</literal> tag gives a parameter to be passed to the class' constructor. The number and type of <literal moreinfo="none" lang="en">ARG</literal> tags specified must match one of the public constructors for the class</para><para lang="en">The jar files must be located in the same directory as this file, regardless of whether the directory is on a local or remote host. The <literal moreinfo="none" lang="en">MLET</literal> tag may also specify a <literal moreinfo="none" lang="en">CODEBASE</literal>, which is an alternate location for the jar file. This m-let loader relies on the definition of <literal moreinfo="none" lang="en">MLET</literal> tag given by the JMX specification.</para><para lang="en">Now we are ready to call the <classname lang="en">performLoadURL</classname> method of our m-let loader. In parsing the result vector, we use our knowledge of the class names that we wrote in the m-let file.</para><example role="code" id="mletloader-ex-10" lang="en"><gentext type="text">Example 6-3 </gentext><title lang="en">Calling the <classname lang="en">performLoadURL</classname> Method</title><programlisting format="linespecific" lang="en" role="complete">ObjectName squareMLetClassLoader = null;
ObjectName triangleMLetClassLoader = null;

// The url string is read from the command line
Object mletParams[] = {url};
String mletSignature[] = {"java.lang.String"};
Vector mbeanList = (Vector) server.<userinput moreinfo="none" lang="en">invoke(</userinput>
    <userinput moreinfo="none" lang="en">mletName, "performLoadURL", mletParams, mletSignature)</userinput>;

for (Enumeration enum = mbeanList.elements();
     enum.hasMoreElements(); ) {
    Object element = enum.nextElement();
    if (element instanceof Vector) {
        // Success, we retrieve the new object name
        Vector v = (Vector) element;
        ObjectInstance objectInstance = (ObjectInstance) v.elementAt(0);
        ObjectName classLoaderObjectName = (ObjectName) v.elementAt(1);
        if (objectInstance.getClassName().equals("Square")) {
            // Retrieve MBean that loaded the class Square
            squareMLetClassLoader = classLoaderObjectName;

        } else if (objectInstance.getClassName().equals(
                       "EquilateralTriangle")) {
            // Retrieve MBean that loaded the class EquilateralTriangle
            triangleMLetClassLoader = classLoaderObjectName;
        }
        println("\tOBJECT NAME = " + objectInstance.getObjectName());
    } else {
        // Failure, find out why
        println("\tEXCEPTION = " + ((Throwable)element).getMessage());
    }
}</programlisting></example><para lang="en">The result of the call to performLoadURL is a <classname lang="en">Vector</classname> object containing as many elements as there are <literal moreinfo="none" lang="en">MLET</literal> tags in the file designated by the URL. Each element is either a vector containing the object instance of the new MBean and the object name of its class loader, or a <classname lang="en">Throwable</classname> object containing the exception or error that prevented the MBean from being loaded. The order of the elements may differ from the order of the tags in the file.</para><para lang="en">In the result, we obtain the object name of the MBeans that were created from the downloaded classes. The management architecture specified by JMX is designed so that objects are manipulated through the MBean server, not by direct reference. Therefore, downloaded classes are directly registered in the MBean server by the m-let loader, and the caller never receives a direct reference to the new object.</para><para lang="en">The object names of the class loaders are references to the internal class loader objects used by the m-let service to actually fetch the classes. We save them because they can be used if we ever need to instantiate these classes again. We will see how in the next section.</para></sect2><sect2 id="mletloader-8" lang="en"><title lang="en">Shortcut for Loading MBeans</title><para lang="en">Loading MBeans from a URL requires some preparation and additional files. In some cases, we don't have the ability to create files ahead of time or modify them when we need different classes. In these cases, we would just like to load a class from a jar file and create its MBean.</para><para lang="en">The <classname lang="en">MLetSrv</classname> is not a class loader, we only ask it to load a class from a URL and it instantiates its private class loader for doing this. Even though the internal class loader object used by the m-let loader is a public type, it should not be instantiated to act as a class loader. The m-let loader stores internal information about its private class loaders, and it won't be able to handle one outside of its control.</para><para lang="en">Instead, use the class loader name that is returned when an MBean is successfully loaded. You can specify this class loader name when creating a class through the MBean server. You will be able to create new MBeans from the same class or from other classes in the associated archive (jar file).</para><para lang="en">This implies that you must first call the <classname lang="en">performLoadURL</classname> with a known URL and a known m-let file. The m-let loader will create one class loader for each code-base specified in the file, and one for the code-base of the file itself. For example, the class loader name returned with the "Square" MBean name is the one used to load its class from the <filename moreinfo="none" lang="en">Square.jar</filename> file in the same directory as the HTML file. We can create other instances of that MBean now just through the MBean server, without needing to call the m-let loader.</para><para lang="en">The following code sample uses the object name references that were declared and assigned in <link linkend="mletloader-ex-10" lang="en">Example 6-3</link>.</para><example role="code" id="mletloader-ex-11" lang="en"><gentext type="text">Example 6-4 </gentext><title lang="en">Loading Classes Directly</title><programlisting format="linespecific" lang="en" role="complete">// Create a new Square MBean from its class in the Square.jar file
String squareClass = "Square";
ObjectName squareName = new ObjectName(
    "MLetExample:name=" + squareClass + ",id=2");
Object squareParams[] = {new Integer(12)};
String squareSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">server.createMBean</userinput>(squareClass, squareName, <userinput moreinfo="none" lang="en">squareMLetClassLoader</userinput>,
                   squareParams, squareSignature);

// Create a new EquilateralTriangle MBean from its class in the
// EquilateralTriangle.jar file
String triangleClass = "EquilateralTriangle";
ObjectName triangleName = new ObjectName(
    "MLetExample:name=" + triangleClass + ",id=3");
Object triangleParams[] = {new Integer(20)};
String triangleSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">server.createMBean</userinput>(triangleClass, triangleName, <userinput moreinfo="none" lang="en">triangleMLetClassLoader</userinput>,
                   triangleParams, triangleSignature);</programlisting></example></sect2><sect2 id="mletloader-12" lang="en"><title lang="en">Running the M-Let Agent Example</title><para lang="en">To run the m-let agent example for the JDK 1.1.<replaceable lang="en">x</replaceable>, you must have installed the Java Dynamic Management Kit for 1.1, and set your classpath accordingly. This example is located in the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/MLetAgent/</filename> directory, see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface for details.</para><para lang="en">In our example, we have two MBeans representing geometrical shapes. Before running the example, we compile them and create a jar file for each. We also compile the agent application at the same time.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/MLetAgent/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput>

$ <userinput moreinfo="none" lang="en">jar cf Square.jar Square.class SquareMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm Square.class SquareMBean.class</userinput>

$ <userinput moreinfo="none" lang="en">jar cf EquilateralTriangle.jar EquilateralTriangle.class \</userinput>
<userinput moreinfo="none" lang="en">EquilateralTriangleMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm EquilateralTriangle.class EquilateralTriangleMBean.class</userinput></screen><para lang="en">Since the MBean classes are only found in the jar files now, they cannot be found in our usual classpath, even if it includes the current directory (<literal moreinfo="none" lang="en">.</literal>). However, these jar files are given as the archive in the <literal moreinfo="none" lang="en">MLET</literal> tags of the HTML file, so the m-let loader should find them.</para><para lang="en">The agent requires you to specify the URL of the m-let file on command line. We have left this file in the examples directory, but you could place it and the jar files on a remote machine. With the Korn shell on the Solaris platform, you would type the following command:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent file:${PWD}/GeometricShapes.html</userinput></screen><para lang="en">In the output of the agent, you can see it create the m-let service MBean, and then load the HTML file which specifies the three MBeans to be loaded. Once these have been loaded, we can see the two MBeans that were loaded directly through the class loader shortcut.</para><para lang="en">This agent uses the tracing mechanism, and you can select to receive the messages from the m-let loader by specifying the <option lang="en"><gentext type="text">-</gentext>DINFO_MLET</option> property on the command line. The tracing mechanism is covered in the <citetitle lang="en">Java Dynamic Management Kit 4.0 Tools Reference</citetitle> guide and in the Javadoc API of the <classname lang="en">Trace</classname> class.</para><para lang="en">The agent then launches an HTML adaptor so that we can easily view the new MBeans. In them we can see that the values contained in the <literal moreinfo="none" lang="en">ARG</literal> tags of the m-let file were used to initialize the MBeans. Point your web browser to the following URL and click on the MBeans in the <literal moreinfo="none" lang="en">MLetExample</literal> domain:<ulink url="http://localhost:8082/">http://localhost:8082/</ulink>. When you are done, type "Control-C" in the window where you launched the agent.</para></sect2></sect1><sect1 id="mletloader-3" lang="en"><title lang="en">M-Let Loading from a Manager (JDK1.1)</title><para lang="en">Like the other agent services of the Java Dynamic Management Kit, the m-let loader is an MBean and fully manageable from a remote manager. A manager application might want an agent to load a new MBean to represent a new resource or provide a new management service. In this example, we show a manager which interacts with the m-let loader of an agent to load exactly the same MBeans as in the previous example.</para><para lang="en">The agent that we will manage only contains an RMI connector and an HTML adaptor when it is launched. We will use the RMI connector to access the agent and perform all of our management operations. You can then view the new MBeans through the HTML adaptor.</para><para lang="en">The manager is just a simple application which creates its connector client, does its management operations and exits. Here is the code of its <classname lang="en">main</classname> method and the constructor that it calls.</para><example role="code" id="mletloader-ex-17" lang="en"><gentext type="text">Example 6-5 </gentext><title lang="en">The <classname lang="en">main</classname> Method of the M-Let Manager</title><programlisting format="linespecific" lang="en" role="complete">public Client() {
    
    // Enable the trace mechanism
    [...]

    // Connect a new RMI connector client to the agent
    connectorClient = new RmiConnectorClient();

    // Use the default address (localhost)
    RmiConnectorAddress address = new RmiConnectorAddress();
    try {
        <userinput moreinfo="none" lang="en">connectorClient.connect(address)</userinput>;
    } catch (Exception e) {
        println("Could not connect to the agent!");
        e.printStackTrace();
        System.exit(1);
    }
}

public static void main(String[] args) {

    // Parse command line arguments.
    [...]

    // Call the constructor to establish the connection
    Client client = new Client();

    // Run the MLet example (see below)
    client.runMLetExample();

    // Disconnect connector client from the connector server.
    client.connectorClient.disconnect();

    System.exit(0);
}</programlisting></example><sect2 id="mletloader-18" lang="en"><title lang="en">Asking the Agent to Load Classes</title><para lang="en">Now that the manager is connected to the client, we can "push" classes to it. We do this by first creating an m-let loader service, then having that loader create MBeans from the classes designated by our HTML file. The following code is taken from manager's <classname lang="en">runMLetExample</classname> method. The code is identical to the code of the agent example, except that we now go through the <classname lang="en">RemoteMBeanServer</classname> interface of the connector client instead of directly through the MBean server.</para><example role="code" id="mletloader-ex-19" lang="en"><gentext type="text">Example 6-6 </gentext><title lang="en">Calling the <classname lang="en">performLoadURL</classname> Method Remotely</title><programlisting format="linespecific" lang="en" role="complete">// Get the domain name from the Agent
String domain = connectorClient.getDefaultDomain();

// Create a new MLetSrv MBean and add it to the Agent
String mletClass = "javax.management.loading.MLetSrv";
ObjectName mletName = new ObjectName(domain + ":name=" + mletClass);
<userinput moreinfo="none" lang="en">connectorClient.createMBean(mletClass, mletName)</userinput>;
[...]

// Create and register new Square and EquilateralTriangle MBeans
// by means of an HTML document containing MLET tags
// The url string is read from the command line
ObjectName squareMLetClassLoader = null;
ObjectName triangleMLetClassLoader = null;

Object mletParams[] = {url};
String mletSignature[] = {"java.lang.String"};
Vector mbeanList = (Vector) <userinput moreinfo="none" lang="en">connectorClient.invoke(</userinput>
    <userinput moreinfo="none" lang="en">mletName, "performLoadURL", mletParams, mletSignature)</userinput>;

for (Enumeration enum = mbeanList.elements(); enum.hasMoreElements(); ) {
    Object element = enum.nextElement();
    if (element instanceof Vector) {
        // Success, we retrieve the new object name
        Vector v = (Vector) element;
        ObjectInstance objectInstance = (ObjectInstance) v.elementAt(0);
        ObjectName classLoaderObjectName = (ObjectName) v.elementAt(1);
        if (objectInstance.getClassName().equals("Square")) {
            // Retrieve the MBean that loaded the Square

            squareMLetClassLoader = classLoaderObjectName;
        } else if (objectInstance.getClassName().equals(
                       "EquilateralTriangle")) {
            // Retrieve the MBean that loaded the EquilateralTriangle
            triangleMLetClassLoader = classLoaderObjectName;
        }
        println("\tOBJECT NAME = " + objectInstance.getObjectName());
    } else {
        // Failure, find out why
        println("\tEXCEPTION = " + ((Throwable)element).getMessage());
    }
}</programlisting></example><para lang="en">As in the agent application, we may need a shortcut for instantiating other MBeans without specifying an m-let file. Again, we can use an existing class loader from a previously loaded class to download the same classes again. We use the <classname lang="en">createMBean</classname> method of the connector client which lets us specify a class loader name. The following code is the rest of the manager's <classname lang="en">runMLetExample</classname> method, and it is also nearly identical to the agent's code.</para><example role="code" id="mletloader-ex-20" lang="en"><gentext type="text">Example 6-7 </gentext><title lang="en">Asking the Agent to Load Classes Directly</title><programlisting format="linespecific" lang="en" role="complete">// Create a new Square MBean from its class in the Square.jar file.
String squareClass = "Square";
ObjectName squareName = new ObjectName(
    "MLetExample:name=" + squareClass + ",id=2");
Object squareParams[] = {new Integer(12)};
String squareSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">connectorClient.createMBean</userinput>(squareClass, squareName,
    <userinput moreinfo="none" lang="en">squareMLetClassLoader</userinput>, squareParams, squareSignature);

// Create a new EquilateralTriangle MBean from its class in the
// EquilateralTriangle.jar file.
String triangleClass = "EquilateralTriangle";
ObjectName triangleName = new ObjectName(
    "MLetExample:name=" + triangleClass + ",id=3");
Object triangleParams[] = {new Integer(20)};
String triangleSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">connectorClient.createMBean</userinput>(triangleClass, triangleName,
    <userinput moreinfo="none" lang="en">triangleMLetClassLoader</userinput>, triangleParams, triangleSignature);</programlisting></example><para lang="en">Simulating a "push" of the MBeans in this way is plausible, since the management application can specify a URL where it controls the contents of the HTML file and knows which classes are available.</para></sect2><sect2 id="mletloader-14" lang="en"><title lang="en">Running the M-Let Manager Example</title><para lang="en">The MBeans in the agent and manager (client) examples are identical, and we will set up the example in exactly the same manner.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/MLetClient/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput>

$ <userinput moreinfo="none" lang="en">jar cf Square.jar Square.class SquareMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm Square.class SquareMBean.class</userinput>

$ <userinput moreinfo="none" lang="en">jar cf EquilateralTriangle.jar EquilateralTriangle.class \</userinput>
<userinput moreinfo="none" lang="en">EquilateralTriangleMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm EquilateralTriangle.class EquilateralTriangleMBean.class</userinput></screen><para lang="en">The manager is written to be run on the same host as the agent application. If you wish to run it on a different host, you will need to modify the code for the <classname lang="en">Client</classname> class constructor where the agent address is specified (see <link linkend="mletloader-ex-17" lang="en">Example 6-5</link>). You could place the jar files and the m-let file on a remote machine and specify its new URL as the parameter to the manager application; we run the example with this file in the current directory.</para><para lang="en">Before launching the manager, you must launch the agent. Here we give commands for launching the applications from the same terminal window running the Korn shell. On the Windows NT platform, you will have to launch each application in a separate window.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent &amp;</userinput>
$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Client file:${PWD}/GeometricShapes.html</userinput></screen><para lang="en">In the output of the manager, you can see it create the m-let service MBean, and then ask it to load the HTML file. Finally, we can see the two MBeans that were loaded directly through the class loader shortcut. If you connect to the agent in a web browser at the following URL: <ulink url="http://localhost:8082/">http://localhost:8082/</ulink> and reload its agent view every time the manager pauses, you can see the MBeans as they are created.</para><para lang="en">The agent terminates after it disconnects its connector client. When you are done viewing the agent, type the following commands to stop the agent application:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">fg</userinput>
java [...] Agent <userinput moreinfo="none" lang="en">&lt;Control-C&gt;</userinput>
^C$ </screen></sect2></sect1><sect1 id="mletloader-4" lang="en"><title lang="en">The M-Let Loader (Java 2)</title><para lang="en">In the version of the Java Dynamic Management Kit for Java 2, the m-let loader is itself a class loader object. It extends the <classname lang="en">URLClassLoader</classname> class of the <classname lang="en">java.net</classname> package to simplify the downloading service it provides.</para><para lang="en">The m-let loader service is an instance of the <classname lang="en">MLet</classname> class in the <classname lang="en">javax.management.loading </classname>package. It is also an MBean that can be accessed remotely. It provides m-let file loading and the shortcut method seen in the version for the JDK 1.1. In addition, it inherits the behavior which lets it be used directly as a class loader, without requiring an m-let file.</para><para lang="en">We will start by demonstrating the usage of the m-let service as it would be used in an agent or in an MBean. In our example, the agent application creates an MBean server and then the m-let loader.</para><example role="code" id="mletloader-ex-21" lang="en"><gentext type="text">Example 6-8 </gentext><title lang="en">Instantiating the <classname lang="en">MLet</classname> Class</title><programlisting format="linespecific" lang="en" role="complete">// Parse debug properties and command line arguments.
[...]

// Instantiate the MBean server
MBeanServer server = MBeanServerFactory.createMBeanServer();
String domain = server.getDefaultDomain();

// Create a new MLet MBean and add it to the MBeanServer.
String mletClass = "javax.management.loading.MLet";
ObjectName mletName = new ObjectName(domain + ":name=" + mletClass);
server.createMBean(mletClass, mletName);</programlisting></example><para lang="en">There is no special initialization that needs to be done before loading classes through an m-let file.</para><sect2 id="mletloader-22" lang="en"><title lang="en">Loading MBeans from a URL</title><para lang="en">In this example we will only load <classname lang="en">EquilateralTriangle</classname> MBeans through the m-let file. We use the same m-let file which is shown in <link linkend="mletloader-ex-9" lang="en">Example 6-2</link>, but without the tags for the <classname lang="en">Square</classname> MBeans.</para><para lang="en">The code for downloading the MBeans specified in the m-let file is also similar. In the Java 2 version of the m-let loader, only the name of the method to call and the format of its return value is different. In this code we call the <classname lang="en">getMBeansFromURL</classname> method and analyze the result:</para><example role="code" id="mletloader-ex-23" lang="en"><gentext type="text">Example 6-9 </gentext><title lang="en">Calling the <classname lang="en">getMBeansFromURL</classname> Method</title><programlisting format="linespecific" lang="en" role="complete">// the url_2 string is read from the command line
println("\tURL = " + url_2);
Object mletParams_2[] = {url_2};
String mletSignature_2[] = {"java.lang.String"};
Set mbeanSet = (Set) <userinput moreinfo="none" lang="en">server.invoke(mletName, "getMBeansFromURL",</userinput>
     <userinput moreinfo="none" lang="en">mletParams_2, mletSignature_2)</userinput>;

for (Iterator i = mbeanSet.iterator(); i.hasNext(); ) {
    Object element = i.next();
    if (element instanceof ObjectInstance) {
        // Success, we display the new MBean's name
        println("\tOBJECT NAME = " +
            ((ObjectInstance)element).getObjectName());
    } else {
        // Failure, we display why
        println("\tEXCEPTION = " + ((Throwable)element).getMessage());
    }
}</programlisting></example><para lang="en">The structure of the returned set is much simpler than in the case of the JDK 1.1 loader. In the JDK 1.1 version, the m-let loader handles separate class loader objects, one for each code-base it has accessed. In the Java 2 version, the m-let loader is the class loader, and it handles just a list of code-bases that it has accessed directly. You can view this list by calling the <classname lang="en">getURLs</classname> method of the m-let loader MBean.</para><para lang="en">This behavior means that the <classname lang="en">getMBeansFromURL</classname> method does not need to return the object names of class loaders it has used. Instead it just returns either the object instance of the downloaded and registered MBean or a <classname lang="en">Throwable</classname> object in case or an error or an exception. These are returned in a <classname lang="en">Set</classname> object containing as many elements as there are <literal moreinfo="none" lang="en">MLET</literal> tags in the target m-let file.</para></sect2><sect2 id="mletloader-24" lang="en"><title lang="en">Shortcut for Loading MBeans</title><para lang="en">This behavior also simplifies any repeated loading of the classes after they have been loaded from an m-let file. Because the m-let loader has already used the code-base of the MBean, it is available to be used again. All you need to do is specify the object name of the m-let loader as the class loader when creating the MBean.</para><para lang="en">You can also load other MBeans in the same code-base, once the code-base has been accessed by a call to the <classname lang="en">getMBeansFromURL</classname> method. In our example we will just download another MBean of the <classname lang="en">EquilateralTriangle</classname> class.</para><example role="code" id="mletloader-ex-25" lang="en"><gentext type="text">Example 6-10 </gentext><title lang="en">Reloading Classes in the M-Let Class Loader</title><programlisting format="linespecific" lang="en" role="complete">// Create another EquilateralTriangle MBean from its class
// in the EquilateralTriangle.jar file.
String triangleClass = "EquilateralTriangle";
ObjectName triangleName = new ObjectName(
    "MLetExample:name=" + triangleClass + ",id=3");
Object triangleParams[] = {new Integer(20)};
String triangleSignature[] = {"java.lang.Integer"};

<userinput moreinfo="none" lang="en">server.createMBean</userinput>(triangleClass, triangleName, <userinput moreinfo="none" lang="en">mletName</userinput>,
                   triangleParams, triangleSignature);</programlisting></example></sect2><sect2 id="mletloader-26" lang="en"><title lang="en">Loading MBeans Directly</title><para lang="en">Since the m-let loader object is a class loader, you can use it to load classes directly, without needing to define an m-let file. This is the main advantage of the Java 2 version of the m-let loader service.</para><para lang="en">Before you can load an MBean directly, you need to add the URL of its code-base to the m-let loader's internal list. Then we just use the m-let loader's object name as the class loader name when creating the MBean. Here is the code to do this in the agent example:</para><example role="code" id="mletloader-ex-27" lang="en"><gentext type="text">Example 6-11 </gentext><title lang="en">Using the M-Let MBean as a Class Loader</title><programlisting format="linespecific" lang="en" role="complete">// Add a new URL to the MLet class loader
// The url_1 string is read from the command line
Object mletParams_1[] = {url_1};
String mletSignature_1[] = {"java.lang.String"};
<userinput moreinfo="none" lang="en">server.invoke(mletName, "addURL", mletParams_1, mletSignature_1)</userinput>;

// Create a Square MBean from its class in the Square.jar file.
String squareClass = "Square";
ObjectName squareName = new ObjectName(
    "MLetExample:name=" + squareClass);
Object squareParams[] = {new Integer(10)};
String squareSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">server.createMBean</userinput>(squareClass, squareName, <userinput moreinfo="none" lang="en">mletName</userinput>,
                   squareParams, squareSignature);</programlisting></example><para lang="en">You only need to add the URL to the m-let loader the first time you want to download a class. Once it is added, we can load it as many times as necessary by calling <classname lang="en">createMBean</classname> directly.</para><para lang="en">Since this loading mechanism doesn't use the <literal moreinfo="none" lang="en">MLET</literal> tag, the programmer must insure that either the downloaded class provides its own object name or, as in the example above, the agent provides one.</para><para lang="en">The fact that the m-let loader is also a class loader into which you can load multiple URLs brings up the issue of name spaces. If there exists two classes with the same name within the code-bases defined by the set of all URLs, the m-let loader will load one of them non-deterministically. In order to specify one of them precisely, you shouldn't add the URL of the second code-base to the m-let loader. Instead, you will have to create a second m-let loader MBean to which you can add the URL for the second version of the class. In this case, you will have one m-let MBean that can load one version of the class and another m-let MBean that can load the other.</para></sect2><sect2 id="mletloader-13" lang="en"><title lang="en">Running the M-Let Agent Example</title><para lang="en">To run the m-let agent example for Java 2, you must have installed the Java Dynamic Management Kit for 1.2, and set your classpath accordingly. This example is located in the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/MLetAgent/</filename> directory, see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface for details.</para><para lang="en">In our example, we have two MBeans representing geometrical shapes. Before running the example, we compile them and create a jar file for each. We also compile the agent application at the same time.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/MLetAgent/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput>

$ <userinput moreinfo="none" lang="en">jar cf Square.jar Square.class SquareMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm Square.class SquareMBean.class</userinput>

$ <userinput moreinfo="none" lang="en">jar cf EquilateralTriangle.jar EquilateralTriangle.class \</userinput>
<userinput moreinfo="none" lang="en">EquilateralTriangleMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm EquilateralTriangle.class EquilateralTriangleMBean.class</userinput></screen><para lang="en">The agent command line requires you to specify first the URL of a jar file for directly loading the <classname lang="en">Square</classname> class, then the URL of the m-let file. We have left these files in the examples directory, but you could place them on a remote machine. With the Korn shell on the Solaris platform, you would type the following command:</para><screen format="linespecific" lang="en">$ java <userinput moreinfo="none" lang="en">-classpath <replaceable lang="en">classpath</replaceable> Agent</userinput> \ 
<userinput moreinfo="none" lang="en">file:${PWD}/Square.jar file:${PWD}/GeometricShapes.html</userinput></screen><para lang="en">In the output of the agent, you can see it create the m-let loader MBean, and then download classes to create MBeans. It starts with the direct loading of the <classname lang="en">Square</classname> class, and then loads from the HTML file which specifies two <classname lang="en">EquilateralTriangle</classname> MBeans to be loaded. Once these have been loaded, we can see the third one that is loaded through the class loader shortcut.</para><para lang="en">This agent uses the tracing mechanism, and you can select to receive the messages from the m-let loader by specifying the <option lang="en"><gentext type="text">-</gentext>DINFO_MLET</option> property on the command line. The tracing mechanism is covered in the <citetitle lang="en">Java Dynamic Management Kit 4.0 Tools Reference</citetitle> guide and in the Javadoc API of the <classname lang="en">Trace</classname> class.</para><para lang="en">The agent then launches an HTML adaptor so that we can easily view the new MBeans. In them we can see that the values contained in the <literal moreinfo="none" lang="en">ARG</literal> tags of the m-let file were used to initialize the MBeans. Point your web browser to the following URL and click on the MBeans in the <literal moreinfo="none" lang="en">MLetExample</literal> domain: <ulink url="http://localhost:8082/">http://localhost:8082/</ulink>. When you are done, type "Control-C" in the window where you launched the agent.</para></sect2></sect1><sect1 id="mletloader-5" lang="en"><title lang="en">M-Let Loading from a Manager (Java 2)</title><para lang="en">Since the MLet class is an MBean, the m-let loader service is fully manageable from a remote manager. This lets a manager create an m-let loader in an agent, add URLs to its list of code-bases, and create MBeans whose classes must be downloaded first.</para><para lang="en">Using the Java 2 m-let class loader, we can again implement a "push" mechanism originating from a management application. In fact, it is even easier due to the direct class loading that the <classname lang="en">MLet</classname> MBean allows.</para><para lang="en">In the example, our manager will create an m-let loader in an agent and have it load new MBean classes. Launched with only an RMI connector and an HTML adaptor, we can see at the end that the agent contains all of the new MBeans loaded from jar files, along with the m-let loader MBean. The initialization of manager is very simple:</para><example role="code" id="mletloader-ex-28" lang="en"><gentext type="text">Example 6-12 </gentext><title lang="en">The <classname lang="en">main</classname> Method of the Manager Application</title><programlisting format="linespecific" lang="en" role="complete">public Client() {
    
    // Enable the trace mechanism
    [...]

    // Connect a new RMI connector client to the agent
    connectorClient = new RmiConnectorClient();

    // Use the default address (localhost)
    RmiConnectorAddress address = new RmiConnectorAddress();
    try {
        <userinput moreinfo="none" lang="en">connectorClient.connect(address)</userinput>;
    } catch (Exception e) {
        println("Could not connect to the agent!");
        e.printStackTrace();
        System.exit(1);
    }
}

public static void main(String[] args) {

    // Parse command line arguments.
    [...]

    // Call the constructor to establish the connection
    Client client = new Client();

    // Run the MLet example (see below)
    client.runMLetExample();

    // Disconnect connector client from the connector server.
    client.connectorClient.disconnect();

    System.exit(0);
}</programlisting></example><sect2 id="mletloader-29" lang="en"><title lang="en">Asking the Agent to Load Classes</title><para lang="en">Now that the manager is connected to the client, we can ask it to load classes. First we have it create an m-let loader MBean that we can use to download classes. Then we demonstrate the various ways of loading classes:<itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">Through an m-let file, which has the advantage of loading many MBeans at once</para></listitem><listitem lang="en"><para lang="en">Directly, from a code-base that was used in an m-let file</para></listitem><listitem lang="en"><para lang="en">Directly, after specifying a URL for the code-base</para></listitem></itemizedlist></para><para lang="en">The following code is taken from manager's <classname lang="en">runMLetExample</classname> method. The code is identical to the code of the agent example, except that we now go through the <classname lang="en">RemoteMBeanServer</classname> interface of the connector client instead of directly through the MBean server.</para><example role="code" id="mletloader-ex-30" lang="en"><gentext type="text">Example 6-13 </gentext><title lang="en">Calling the <classname lang="en">getMBeansFromURL</classname> Method Remotely</title><programlisting format="linespecific" lang="en" role="complete">// Get the domain name from the MBeanServer.
String domain = connectorClient.getDefaultDomain();

// Create a new MLet MBean and add it to the MBeanServer
String mletClass = "javax.management.loading.MLet";
ObjectName mletName = new ObjectName(domain + ":name=" + mletClass);
<userinput moreinfo="none" lang="en">connectorClient.createMBean(mletClass, mletName)</userinput>;

[...]

// Create new EquilateralTriangle MBeans through MLET tags
// The url_2 string is read from the command line
Object mletParams_2[] = {url_2};
String mletSignature_2[] = {"java.lang.String"};
Set mbeanSet = (Set) <userinput moreinfo="none" lang="en">connectorClient.invoke(</userinput>
    <userinput moreinfo="none" lang="en">mletName, "getMBeansFromURL", mletParams_2, mletSignature_2)</userinput>;

for (Iterator i = mbeanSet.iterator(); i.hasNext(); ) {
    Object element = i.next();
    if (element instanceof ObjectInstance) {
        // Success
        println("OBJECT NAME = " +
            ((ObjectInstance)element).getObjectName());
    } else {
        // Failure
        println("EXCEPTION = " + ((Throwable)element).getMessage());
    }
}</programlisting></example><para lang="en">Now that the class loader has used the code-base of the jar file, we can create more of the MBeans from the same jar file. We invoke the createMBean method of the server with the object name of the class loader.</para><example role="code" id="mletloader-ex-31" lang="en"><gentext type="text">Example 6-14 </gentext><title lang="en">Reloading MBeans from an Existing Code-Base</title><programlisting format="linespecific" lang="en" role="complete">// Create another EquilateralTriangle MBean from the same jar file
// used in the MLET file
String triangleClass = "EquilateralTriangle";
ObjectName triangleName = new ObjectName(
    "MLetExample:name=" + triangleClass + ",id=3");
Object triangleParams[] = {new Integer(20)};
String triangleSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">connectorClient.createMBean</userinput>(triangleClass, triangleName, <userinput moreinfo="none" lang="en">mletName</userinput>,
                            triangleParams, triangleSignature);</programlisting></example><para lang="en">Finally, if we have a different code-base not associated with an m-let file, we can give its URL directly to the loader. This allows us to ask the agent to create almost any MBean, imitating a class "push" mechanism. </para><example role="code" id="mletloader-ex-32" lang="en"><gentext type="text">Example 6-15 </gentext><title lang="en">Implementing a "Push" Operation</title><programlisting format="linespecific" lang="en" role="complete">// Add a new URL to the MLet MBean to look for classes
// The url_1 string is read from the command line
Object mletParams_1[] = {url_1};
String mletSignature_1[] = {"java.lang.String"};
<userinput moreinfo="none" lang="en">connectorClient.invoke(mletName, "addURL",</userinput>
                       <userinput moreinfo="none" lang="en">mletParams_1, mletSignature_1)</userinput>;

// Create a new Square MBean from its class in the Square.jar file
String squareClass = "Square";
ObjectName squareName = new ObjectName(
    "MLetExample:name=" + squareClass);
Object squareParams[] = {new Integer(10)};
String squareSignature[] = {"java.lang.Integer"};
<userinput moreinfo="none" lang="en">connectorClient.createMBean</userinput>(squareClass, squareName, <userinput moreinfo="none" lang="en">mletName</userinput>,
                            squareParams, squareSignature);</programlisting></example><para lang="en">In this way, the manager can make code available on the network, and it can direct its agents to load the classes to create new MBeans ready for management. This mechanism can be used to distribute new resources, provide new services,or update applications, all under the control of the manager.</para></sect2><sect2 id="mletloader-15" lang="en"><title lang="en">Running the M-Let Manager Example</title><para lang="en">The MBeans in the agent and manager (client) examples are identical, and we will set up the example in exactly the same manner.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/MLetClient/</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> *.java</userinput>

$ <userinput moreinfo="none" lang="en">jar cf Square.jar Square.class SquareMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm Square.class SquareMBean.class</userinput>

$ <userinput moreinfo="none" lang="en">jar cf EquilateralTriangle.jar EquilateralTriangle.class \</userinput>
<userinput moreinfo="none" lang="en">EquilateralTriangleMBean.class</userinput>
$ <userinput moreinfo="none" lang="en">rm EquilateralTriangle.class EquilateralTriangleMBean.class</userinput></screen><para lang="en">The manager is written to be run on the same host as the agent application. If you wish to run it on a different host, you will need to modify the code for the <classname lang="en">Client</classname> class constructor where the agent address is specified (see <link linkend="mletloader-ex-28" lang="en">Example 6-12</link>). You could place the jar files and the m-let file on a remote machine and specify its new URL as the parameter to the manager application; we run the example with this file in the current directory.</para><para lang="en">Before launching the manager, you must launch the agent. Here we give commands for launching the applications from the same terminal window running the Korn shell. On the Windows NT platform, you will have to launch each application in a separate window.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent &amp;</userinput>
$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Client \</userinput> 
<userinput moreinfo="none" lang="en">file:${PWD}/Square.jar file:${PWD}/GeometricShapes.html</userinput></screen><para lang="en">In the output of the manager, you can see it create the m-let service MBean, and then load all of the MBeans from different sources. If you connect to the agent in a web browser at the following URL: <ulink url="http://localhost:8082/">http://localhost:8082/</ulink> and reload its agent view every time the manager pauses, you can see the MBeans as they are created.</para><para lang="en">The agent terminates after it disconnects it connector client. When you are done viewing the agent, type the following commands to stop the agent application:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">fg</userinput>
java [...] Agent <userinput moreinfo="none" lang="en">&lt;Control-C&gt;</userinput>
^C$ </screen></sect2></sect1></chapter><chapter id="snmpadaptor-1" lang="en"><gentext type="text">Chapter 7</gentext><gentext type="toc">7.  Creating an SNMP Agent</gentext><title lang="en">Creating an SNMP Agent</title><highlights lang="en"><para lang="en">Using the Java Dynamic Management Kit, you can create an agent application that is
both an SNMP agent and normal JMX agent. SNMP MIBs can be represented as MBeans
and the SNMP protocol adaptor exposes them to SNMP managers. Only MBeans derived
from MIBs may be managed through the SNMP protocol adaptor, but other managers
may view and manage them through other protocols.</para><para lang="en">The provided <command moreinfo="none" lang="en">mibgen</command> tool generates the code for MBeans
that represent a MIB. Groups and table entries are represented as MBean instances
which expose the SNMP variables through their attributes. The <command moreinfo="none" lang="en">mibgen</command> tool creates skeleton getters and setters which only read or write
internal variables. In order to expose the behavior of your host machine or
device, you need to implement the code that will access the host- or device-specific
functionality.</para><para lang="en">The SNMP protocol adaptor interacts with your customized MIB MBeans
to implement a compatible SNMPv1 and SNMPv2 agent. It also provides the mechanisms
for sending traps and implementing both community-based access control and
message-level data security.</para><para lang="en">The program listings in this tutorial show only functional code: comments
and output statements have been modified or removed for space considerations.
However, all management functionality has been retained for the various demonstrations.
The complete source code is available in the <filename moreinfo="none" lang="en">Snmp/Agent</filename>
example directory located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename>
(see <link linkend="preface-11" lang="en">"Directories and Classpath"</link>
in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-6" lang="en">"MIB Development Process"</link> explains how MIBs are implemented
as MBeans</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-5" lang="en">"The SNMP Protocol Adaptor"</link> shows how to build
an SNMP agent with the components of the Java Dynamic Management Kit</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-19" lang="en">"Sending Traps"</link> demonstrates the
SNMP trap mechanism in the SNMP adaptor</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-2" lang="en">"Access Control Lists (ACL)"</link> shows how to specify
host communities to limit access and send traps</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-3" lang="en">"Message-Level Security"</link> demonstrates a more
flexible way of limiting access to the SNMP agent</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpadaptor-4" lang="en">"Stand-Alone SNMP Agents"</link> demonstrates an
alternative way of implementing an SNMP agent</para></listitem></itemizedlist></highlights><sect1 id="snmpadaptor-6" lang="en"><title lang="en">MIB Development Process</title><para lang="en">Here we describe the process for making MIBs manageable through the
SNMP protocol adaptor of the Java Dynamic Management Kit. In our example, we demonstrate
this process on a subset of the MIB-II defined by RFC 1213.</para><para lang="en">Once you have defined the MIB you want to manage in your SNMP agent
you need to generate its MBean representation using the <command moreinfo="none" lang="en">mibgen</command>
tool. This command-line tool and its output are fully described in the <citetitle lang="en">Java Dynamic Management Kit 4.0 Tools Reference</citetitle>
guide. This tool generates MBeans that represent the whole MIB, each of its
groups and each of its table entries.</para><sect2 id="snmpadaptor-34" lang="en"><title lang="en">Generating MIB MBeans</title><para lang="en">To run the mibgen tool for our example, go to the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Snmp/Agent</filename>directory and enter the following
command:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">mibgen -d . mib_II_subset.txt</userinput></screen><para lang="en">This will generate the following files in the current directory:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">The MBean (by inheritance) for the whole MIB: <filename moreinfo="none" lang="en">RFC1213_MIB.java</filename></para></listitem><listitem lang="en"><para lang="en">The MBean and its metadata class for the <literal moreinfo="none" lang="en">Snmp</literal> group: <filename moreinfo="none" lang="en">Snmp.java</filename>, <filename moreinfo="none" lang="en">SnmpMBean.java</filename>, <filename moreinfo="none" lang="en">SnmpMeta.java</filename></para></listitem><listitem lang="en"><para lang="en">The MBean and its metadata class for the <literal moreinfo="none" lang="en">System</literal> group:  <filename moreinfo="none" lang="en">System.java</filename>, <filename moreinfo="none" lang="en">SystemMBean.java</filename>, <filename moreinfo="none" lang="en">SystemMeta.java</filename></para></listitem><listitem lang="en"><para lang="en">The MBean and its metadata class for the <literal moreinfo="none" lang="en">Interfaces</literal> group: <filename moreinfo="none" lang="en">Interfaces.java</filename>, <filename moreinfo="none" lang="en">InterfacesMBean.java</filename>, <filename moreinfo="none" lang="en">InterfacesMeta.java</filename></para></listitem><listitem lang="en"><para lang="en">The class representing an interface table, and
the MBean representing entries in the table: <filename moreinfo="none" lang="en">TableIfTable.java</filename>, <filename moreinfo="none" lang="en">IfEntry.java</filename>, <filename moreinfo="none" lang="en">IfEntryMBean.java</filename>, <filename moreinfo="none" lang="en">IfEntryMeta.java</filename></para></listitem><listitem lang="en"><para lang="en">Classes representing enumerated types used in these
groups and entries: <filename moreinfo="none" lang="en">EnumSnmpEnableAuthenTraps.java</filename>, <filename moreinfo="none" lang="en">EnumIfOperStatus.java</filename>, <filename moreinfo="none" lang="en">EnumIfAdminStatus.java</filename>, <filename moreinfo="none" lang="en">EnumIfType.java</filename></para></listitem><listitem lang="en"><para lang="en">The OID table for representing the name definition
of all MIB variables: <filename moreinfo="none" lang="en">RFC1213_MIBOidTable.java</filename></para></listitem></itemizedlist><para lang="en">The MBean with the name of the MIB is just a central administrative
class for managing the other MBeans which implement the MIB. All of the other
MBeans contain the SNMP variables as attributes of their management interface.
The <command moreinfo="none" lang="en">mibgen</command> tool generates standard MBeans for the MIB,
so attributes are implemented with individual getter and setter methods.</para><para lang="en">These MBeans are just skeletons, meaning that the attributes do not
do anything. You must implement the getters and setters of the attributes
to read and write data with the correct semantic meaning of the SNMP variable.</para><para lang="en">Since SNMP does not support actions in MIBs, the only operations in
these MBeans are <emphasis lang="en">checkers</emphasis> for implementing the SNMP <literal moreinfo="none" lang="en">set</literal> request in writeable variable. You may add operations and expose
them in the <classname lang="en">MBean</classname> interface, but the SNMP manager will
not be able to access them. However, other managers will be able to call these
operations if they are connected through another protocol.</para></sect2><sect2 id="snmpadaptor-33" lang="en"><title lang="en">Implementing the MIB</title><para lang="en">Our example only implements a fraction of the attributes, those that
are used in this tutorial. The others are just initialized with some plausible
value. These implementations are contained in the classes with the <classname lang="en">Impl</classname> suffix. These implementation classes extend those that are
generated by <command moreinfo="none" lang="en">mibgen</command> so that we can regenerate them without
overwriting our customizations.</para><para lang="en">Here is a summary of the implementation shown in the agent example:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">InterfaceImpl.java</filename> - adds a new entry
notification listener to the <classname lang="en">IfTable</classname> object, then creates
two table entries with plausible values and adds them to the table; associated
with:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">TableEntryListenerImpl.java</filename> - the listener
for table entry creation and deletion notifications: prints out the new table
entry's values</para></listitem><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">IfEntryImpl.java</filename> - implements
a table entry and provides an internal method for switching the <literal moreinfo="none" lang="en">OperStatus</literal> variable that triggers a trap (see <link linkend="snmpadaptor-ex-25" lang="en">Example 7-2</link>);
this method is not exposed in the <classname lang="en">MBean</classname> interface,
so it is only available to the code of this agent application</para></listitem></itemizedlist></listitem><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">SnmpImpl.java</filename> - initializes
and implement variables of the Snmp group; many of these are state variables
of the SNMP agent, so we call the getter methods of the SNMP adaptor object
to return the information</para></listitem><listitem lang="en"><para lang="en"><filename moreinfo="none" lang="en">SystemImpl.java</filename> - initializes
the <literal moreinfo="none" lang="en">System</literal> group variables with some realistic values</para></listitem></itemizedlist><para lang="en">The <filename moreinfo="none" lang="en">SnmpImpl.java</filename> and <filename moreinfo="none" lang="en">SystemImpl.java</filename> files provide code that you may reuse when you need to implement
these common SNMP groups.</para><para lang="en">The only class that we need to replace is <classname lang="en">RFC1213_MIB</classname>.
Our implementation is located in <filename moreinfo="none" lang="en">patchfiles/RFC1213_MIB.java</filename>
so that we can overwrite the one generated by <command moreinfo="none" lang="en">mibgen</command>. The
main function of this MBean is to register the other MBeans of the MIB during
its pre-registration phase. Our customization consists of specifying our <classname lang="en">*Impl</classname> classes when instantiating the group and table entry MBeans.</para></sect2><sect2 id="snmpadaptor-32" lang="en"><title lang="en">Compiling the MBeans and Agents</title><para lang="en">We replace the generated file with our implementation before compiling
all of the classes in the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Snmp/Agent</filename> directory. The classpath must contain the current directory (<filename moreinfo="none" lang="en">.</filename>):</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cp -i patchfiles/RFC1213_MIB.java .</userinput>
cp: overwrite ./RFC1213_MIB.java (yes/no)? <userinput moreinfo="none" lang="en">y</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> -d . *.java</userinput></screen><para lang="en">We are now ready to look at the implementation of an SNMP agent and
run the example applications.</para></sect2></sect1><sect1 id="snmpadaptor-5" lang="en"><title lang="en">The SNMP Protocol Adaptor</title><para lang="en">Once your MIBs are implemented as MBeans, your agent application needs
an SNMP protocol adaptor in order to function as an SNMP agent. Since the
SNMP adaptor is also an MBean, it can be created and started dynamically in
your agent by a connected manager or through the HTML adaptor. In our simple <classname lang="en">Agent</classname> example, we will launch it through the code of the agent
application.</para><example role="code" id="snmpadaptor-ex-16" lang="en"><gentext type="text">Example 7-1 </gentext><title lang="en">The SNMP <classname lang="en">Agent</classname>
Application</title><programlisting format="linespecific" lang="en" role="complete">public class Agent {

    static SnmpAdaptorServer snmpAdaptor = null;

    public static void main(String args[]) {
        
        MBeanServer server;
        ObjectName htmlObjName;
        ObjectName snmpObjName;
        ObjectName mibObjName;
        ObjectName trapGeneratorObjName;
        int htmlPort = 8082;
        int snmpPort = 8085; // non-standard

        [...]    
        try {
            server = MBeanServerFactory.createMBeanServer();
            String domain = server.getDefaultDomain();

            // Create and start the HTML adaptor.
            //
            htmlObjName = new ObjectName( domain +
                ":class=HtmlAdaptorServer,protocol=html,port=" + htmlPort);
            HtmlAdaptorServer htmlAdaptor = new HtmlAdaptorServer(htmlPort);
            server.registerMBean(htmlAdaptor, htmlObjName);
            htmlAdaptor.start();

            // Create and start the SNMP adaptor.
            //
            snmpObjName = new ObjectName(domain +
                ":class=SnmpAdaptorServer,protocol=snmp,port=" + snmpPort);
            snmpAdaptor = new SnmpAdaptorServer(snmpPort);
            server.registerMBean(snmpAdaptor, snmpObjName);
            snmpAdaptor.start();

            // The rest of the code is specific to our SNMP agent

            // Send a coldStart SNMP Trap (use port = snmpPort+1)
            // Trap communities are defined in the ACL file
            //
            snmpAdaptor.setTrapPort(new Integer(snmpPort+1));
            snmpAdaptor.sendV1Trap(0, 0, null);

            // Create the MIB-II (RFC 1213) and add it to the MBean server.
            //
            mibObjName = new ObjectName("snmp:class=RFC1213_MIB");
            RFC1213_MIB mib2 = new RFC1213_MIB();
            // The MBean will register all group and table entry MBeans
            // during its pre-registration
            server.registerMBean(mib2, mibObjName);

            // Bind the SNMP adaptor to the MIB
            mib2.setSnmpAdaptorName(snmpObjName);

            [...]

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Needed to get a reference on the SNMP adaptor object
    static public SnmpAdaptorServer getSnmpAdaptor() {
        return snmpAdaptor;
    }
}</programlisting></example><sect2 id="snmpadaptor-18" lang="en"><title lang="en">Launching the SNMP Adaptor</title><para lang="en">We launch the SNMP adaptor in the same way that we launch the HTML adaptor.
First we create a meaningful object name for its MBean, then we instantiate
the class with a constructor allowing us to specify a non-default port, we
register the MBean with the MBean server, and we start the adaptor to make
it active.</para><para lang="en">By default, the SNMP protocol adaptor uses the standard SNMP port 161.
Since other applications may be using this port on a machine, our simple agent
uses port 8085. When we connect to this agent our SNMP manager will need to
specify this non-standard port number.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en"> On certain platforms, applications also require  super-user privileges
to assign the default SNMP port 161. If your SNMP adaptor uses this port,
its agent application will have to be launched with super-user privileges.</para></note></sect2><sect2 id="snmpadaptor-24" lang="en"><title lang="en">Creating MIB MBeans</title><para lang="en">Our agent application creates and manages one MIB, our subset of MIB-II.
To do so, it instantiates the corresponding <classname lang="en">RFC1213_MIB</classname>
MBean that has been generated by the <command moreinfo="none" lang="en">mibgen</command> tool (see <link linkend="snmpadaptor-6" lang="en">"MIB Development Process"</link>). We give it a meaningful object name and then we
register it in the MBean server.</para><para lang="en">The registration process lets the MBean instantiate and register other
MBeans that represent the groups of the MIB and the entries of its tables.
The set of all these MBeans at the end of registration make up the MBean representation
of the MIB.</para><para lang="en">If you do not wish to expose a MIB through the MBean server, you do
not have to register it. However, you still need to create all of its other
constituent objects. The generated code provides the <classname lang="en">init</classname>
method in the main MBean of the MIB. Calling this method will create all necessary
MBean objects without registering them in the MBean server.</para></sect2><sect2 id="snmpadaptor-17" lang="en"><title lang="en">Binding the MIB MBeans</title><para lang="en">Because the SNMP data model relies on MIBs, only MBeans representing
MIBs can be managed through SNMP. The main MBean of a MIB must be explicitly
bound to the instance of the SNMP adaptor. The SNMP adaptor does not interact
with any other MBeans, nor with the MBean server.</para><para lang="en">After a MIB is instantiated, you must set the <literal moreinfo="none" lang="en">SnmpAdaptorName</literal> attribute of its main MBean to bind it to the SNMP adaptor. You
can either call its <classname lang="en">setSnmpAdaptorName</classname> method directly
or, if the MIB's MBean was registered in the MBean server, another management
application may set the attribute through the MBean's exposed interface. In
this way, the SNMP adaptor will have a reference of all MIBs it must expose.</para><para lang="en">In the binding process, the SNMP adaptor obtains the root OID of the
MIB. The adaptor uses this OID to determine which variables are implemented
in the MIB's corresponding MBeans. In order for the SNMP adaptor to resolve
a request on a given OID, the root OID of all bound MIBs must be distinct.
This implies that no root OID may be equal to another or be a substring of
another.</para><para lang="en">Even though the SNMP adaptor may be registered in the MBean server,
the adaptor only makes MIBs visible to SNMP managers. Other MBeans in the
agent cannot be accessed or even represented in the SNMP protocol. The SNMP
manager is limited by its protocol: it cannot take full advantage of a Java
Dynamic Management agent through the basic MIBs, and it does not have access
to any other MBeans.</para></sect2><sect2 id="snmpadaptor-21" lang="en"><title lang="en">Accessing a MIB MBean</title><para lang="en">Once the MBean representing a MIB has been instantiated and bound to
the SNMP adaptor, it is accessible through the SNMP adaptor. SNMP managers
can send requests to operate on the contents of the MIB. The SNMP adaptor
interprets the SNMP management requests, performs the operation on the corresponding
MBean and returns the SNMP response to the manager. The SNMP protocol adaptor
is compatible with SNMPv1 and SNMPv2 PDUs, with the exception of <literal moreinfo="none" lang="en">InformRequest</literal> which it doesn't handle.</para><para lang="en">The advantage of having an SNMP agent "inside" a Java Dynamic
Management agent is that you can use the other communications protocols to
interact with MIBs and manage the SNMP adaptor. Since both the registered
MIBs and the adaptor are MBeans, they are exposed for management. In our simple
agent, the MIB was registered, and you can view its MBeans in a web browser
through the HTML protocol adaptor. Furthermore, the MIB implementations in
the SNMP agent could be easily upgraded using the m-let loader service (see <link linkend="mletloader-1" lang="en">Chapter 6, The M-Let Class Loader</link>).</para><para lang="en">If our agent included other connectors, management applications could
connect to the agent and also manage the MIB and the SNMP adaptor. A non-SNMP
manager could instantiate new MIB objects, bind them to the SNMP adaptor and
operate on the exposed attributes and operations of the MIB.</para><para lang="en">Non-SNMP managers may operate on the variables of a MIB, getting and
setting values regardless of any SNMP manager that might also be accessing
them through the SNMP adaptor. The designer of the agent and management applications
is responsible for all coherence issues when accessing MIBs concurrently through
different protocols.</para></sect2><sect2 id="snmpadaptor-23" lang="en"><title lang="en">Managing the SNMP Adaptor</title><para lang="en">Non-SNMP managers can also control the SNMP agent through the MBean
of the SNMP adaptor. Like the other communications MBeans, the port and other
attributes can be modified when the SNMP adaptor is stopped. You can also
get information about its state, and stop or restart it to control when it
is on-line. These administrative attributes and operations are defined in
the <classname lang="en">CommunicatorServerMBean</classname> interface.</para><para lang="en">The SNMP adaptor server also implements the <classname lang="en">SnmpAdaptorServerMBean</classname> interface to define its operating information. The <literal moreinfo="none" lang="en">Snmp</literal> group of MIB-II defines certain statistics variables that SNMP
agents must expose. For example, the SNMP adaptor provides methods for <literal moreinfo="none" lang="en">getSnmpInPkts</literal> and <literal moreinfo="none" lang="en">getSnmpOutBadValues</literal>. Non-SNMP
managers can read these variables as attributes of the SNMP adaptor MBean.</para><para lang="en">The SNMP adaptor also exposes other operating information that is unavailable
to SNMP managers. For example, the <literal moreinfo="none" lang="en">ActiveClientCount</literal> and <literal moreinfo="none" lang="en">ServedClientCount</literal> read-only attributes report on SNMP manager connections
to this agent. The read-write <literal moreinfo="none" lang="en">BufferSize</literal> attribute lets
you change the size of the message buffer when the adaptor is stopped. The
adaptor MBean also exposes operations for sending traps or implementing your
own security (see <link linkend="snmpadaptor-3" lang="en">"Message-Level Security"</link>).</para></sect2><sect2 id="snmpadaptor-30" lang="en"><title lang="en">Running the SNMP Agent Example</title><para lang="en">After building the example as described in <link linkend="snmpadaptor-6" lang="en">"MIB Development Process"</link>,
launch the simple agent with the following command:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent</userinput></screen><para lang="en">You should see some initialization messages, including our notification
listener giving information about the two table entries which are created.
Then, you should see the agent sending out a trap every two seconds. Traps
are covered in <link linkend="snmpadaptor-19" lang="en">"Sending Traps"</link>, and we can ignore these messages.
Access this agent's HTML adaptor by pointing a web browser to the following
URL: <ulink url="http://localhost:8082/">http://localhost:8082/</ulink>. Through
the HTML adaptor, you can see the MBeans representing the MIB:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">The <literal moreinfo="none" lang="en">class=RFC1213_MIB</literal> MBean in the <literal moreinfo="none" lang="en">snmp</literal> domain is the topmost MBean, it contains a name and information
about the SNMP adaptor to which the MIB is bound</para></listitem><listitem lang="en"><para lang="en">The <literal moreinfo="none" lang="en">RFC1213_MIB</literal> domain contains
the MBeans for each group; these MBeans contain variables with values provided
by our customizations</para></listitem><listitem lang="en"><para lang="en">The <literal moreinfo="none" lang="en">ifTable</literal> domain contains
the entries of the interface table</para></listitem></itemizedlist><para lang="en">In any of these MBeans, you could write new values into the text fields
of exposed attributes and click the "Apply" button. This will
set the corresponding SNMP variable, and thereafter, SNMP managers will see
the new value. This is an example of managing a MIB through a protocol other
than SNMP.</para><para lang="en">For any SNMP agent application, you can turn on trace messages for the
SNMP adaptor by specifying the <option lang="en"><gentext type="text">-</gentext>DINFO_ADAPTOR_SNMP</option> property
on the command line. The tracing mechanism is covered in the <citetitle lang="en">Java Dynamic Management Kit 4.0 Tools Reference</citetitle>
guide and in the Javadoc API of the <classname lang="en">Trace</classname> class.</para><para lang="en">Type "Control-C" when you are done viewing the agent.</para></sect2></sect1><sect1 id="snmpadaptor-19" lang="en"><title lang="en">Sending Traps</title><para lang="en">The simple SNMP agent application also demonstrates how the agent can
send traps. The customized class <classname lang="en">IfEntryImpl</classname> in the
example directory extends the <classname lang="en">IfEntry</classname> class generated
by <command moreinfo="none" lang="en">mibgen</command> in order to provide a method that switches the <literal moreinfo="none" lang="en">IfOperStatus</literal> variable and sends a trap. This is an example of customization
of the generated code: some agent-side entity will switch the operation status,
the MIB variable will be updated and a trap will be sent to SNMP managers.</para><para lang="en">The <filename moreinfo="none" lang="en">IfEntryImpl.java</filename> file provided in the example
directory contains the code for sending the trap through the SNMP adaptor.</para><example role="code" id="snmpadaptor-ex-25" lang="en"><gentext type="text">Example 7-2 </gentext><title lang="en">Sending a Trap in the <classname lang="en">IfEntryImpl</classname> Class</title><programlisting format="linespecific" lang="en" role="complete">public void switchifOperStatus() {
    // implements the switch and then calls sendTrap indirectly
    [...]
}

// Method called after the variable has been switched
// Should be called with generic==2 (up) or 3 (down or testing)
public void sendTrap(int generic) {

    SnmpAdaptorServer snmpAdaptor = null;

    // Retrieve the reference of the SNMP protocol adaptor through
    // the static method of the Agent or StandAloneSnmpAgent class
    snmpAdaptor = Agent.getSnmpAdaptor();
    if (snmpAdaptor == null) {
        snmpAdaptor = StandAloneSnmpAgent.getSnmpAdaptor();
    }
    if (snmpAdaptor == null) {
        return;
    }

    Vector varBindList = new Vector();

    // IfIndex is the index of the current If table entry
    SnmpOid oid1 = new SnmpOid("1.3.6.1.2.1.2.2.1.1." + IfIndex);
    SnmpInt value1 = new SnmpInt(IfIndex);
    SnmpVarBind varBind1 = new SnmpVarBind(oid1, (SnmpValue) value1);

    varBindList.addElement(varBind1);

    try {
        snmpAdaptor.sendV1Trap(generic, 0, varBindList);
    } catch (Exception e) {
        e.printStackTrace();
    }
}</programlisting></example><para lang="en">Since the <classname lang="en">sendTrap</classname> method runs in a different
thread, it needs to get a reference to the SNMP adaptor instance. Here we
call the static methods of our two possible agent implementations. This code
is specific to these agents and is only an example of how to retrieve this
information.</para><para lang="en">Since our example has no real operation status, we invent the <classname lang="en">LinkTrapGenerator</classname> class which will switch the status periodically.
It is an MBean which contains a thread which loops endlessly. The interval
period between traps and the index of the table entry can be modified through
the MBean's attributes.</para><example role="code" id="snmpadaptor-ex-26" lang="en"><gentext type="text">Example 7-3 </gentext><title lang="en">The Thread of the Link Trap Generator</title><programlisting format="linespecific" lang="en" role="complete">public void run() {
    while (true) {
        try {
            sleep(interval);
        } catch (Exception e) {
            e.printStackTrace();
        }
        sendTrap();
    }
}

public void sendTrap() {
    // get the entry whose status we will switch
    IfEntryImpl ifEntryImpl = InterfacesImpl.find(ifIndex);
    if (ifEntryImpl == null) {
        errors++;
        return;
    }
    ifEntryImpl.switchifOperStatus();
    successes++;
}</programlisting></example><para lang="en">To start the trap generator, the example application creates a <classname lang="en">LinkTrapGenerator</classname> MBean. During its registration, the MBean starts
the thread, sending a trap every two seconds by default.</para><example role="code" id="snmpadaptor-ex-20" lang="en"><gentext type="text">Example 7-4 </gentext><title lang="en">Starting the Trap Generator Example</title><programlisting format="linespecific" lang="en" role="complete">// Create a LinkTrapGenerator (specify the ifIndex in the object name)
//
String trapGeneratorClass = "LinkTrapGenerator";
int ifIndex = 1;
trapGeneratorObjName = new ObjectName("trapGenerator:class=" +
    trapGeneratorClass + ",ifIndex=" + ifIndex);
server.createMBean(trapGeneratorClass, trapGeneratorObjName);</programlisting></example><para lang="en">In order for an SNMP adaptor to send traps to remote managers, you must
define the trap group in the access control list (ACL) file. This group of
community definitions lists all of the hosts to which the agent will send
every trap. A community definition associates a community name with a list
of hosts specified either by their hostname or by their IP address. All hosts
in a community will receive the trap in a packet identified by the community
name.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">Since access control and trap recipients share the same file, you must
fully define the access control when you want to send traps.</para></note><para lang="en">See <link linkend="snmpadaptor-2" lang="en">"Access Control Lists (ACL)"</link> for a formal definition of the trap
group and instructions for defining the ACL file when starting the agent.
In this example we provide the following template:</para><example role="code" id="snmpadaptor-ex-27" lang="en"><gentext type="text">Example 7-5 </gentext><title lang="en">Sample <filename moreinfo="none" lang="en">jdmk.acl</filename>
File</title><programlisting format="linespecific" lang="en" role="complete">acl = {
 {
 communities = public
 access = read-only
 managers = <replaceable lang="en">yourmanager</replaceable>
 }
 {
 communities = private
 access = read-write
 managers = <replaceable lang="en">yourmanager</replaceable>
 } 
} 

trap = {
  {
  trap-community = public
  hosts = <replaceable lang="en">yourmanager</replaceable>
  }
}</programlisting></example><para lang="en">Traps are sent to the port specified by the <literal moreinfo="none" lang="en">TrapPort</literal>
attribute of the <classname lang="en">SnmpAdaptorServer</classname> MBean. In our simple
agent, we set the trap port to 8086, but this can be changed at any time by
a custom MIB implementation or a management application.</para><para lang="en">If the ACL file is not defined, or if the trap group is empty, the default
behavior of the SNMP adaptor is to send a trap to the localhost.</para><sect2 id="snmpadaptor-31" lang="en"><title lang="en">Traps in the Agent Example</title><para lang="en">Before launching the SNMP agent again, edit the <filename moreinfo="none" lang="en">jdmk.acl</filename>
file to replace the occurrences of <replaceable lang="en">yourmanager</replaceable>
with the name of a host running an SNMP manager. Then copy this file to the
configuration directory:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cp jdmk.acl <replaceable lang="en">installDir</replaceable>/SUNWjdmk/jdmk4.0/<replaceable lang="en">JDKversion</replaceable>/etc/conf</userinput></screen><para lang="en">If you don't have an SNMP manager or a second host, don't copy the ACL
file. In the absence of the trap-community definitions, the traps will be
addressed to the trap port on the local host. And even if no manager is running,
we can still see the agent sending the traps. See <link linkend="snmpmanager-12" lang="en">"SNMP Trap Handler"</link>
in the SNMP manager topic for details about receiving traps.</para><para lang="en">Then launch the simple agent example again:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent</userinput></screen><procedure id="snmpadaptor-proc-35" lang="en"><gentext type="text"></gentext><title lang="en">Interacting with the Trap Generator</title><step performance="required" lang="en"><para lang="en">Access this agent's HTML adaptor by pointing a web browser to the following
URL: <ulink url="http://localhost:8082/">http://localhost:8082/</ulink>. Click
on the <literal moreinfo="none" lang="en">class=LinkTrapGenerator,ifIndex=1</literal> MBean in the <literal moreinfo="none" lang="en">trapGenerator</literal> domain.</para><para lang="en">Through the HTML adaptor, you can see the MBean representing the trap
generator object. You can modify its attributes to change the table entry
that it operates on and to change the interval between traps.</para></step><step performance="required" id="snmpadaptor-step-36" lang="en"><para lang="en">Change the trap interval to <literal moreinfo="none" lang="en">10000</literal> so that traps are
sent every 10 seconds.</para></step><step performance="required" id="snmpadaptor-step-37" lang="en"><para lang="en">Go back to the agent view and click on the <literal moreinfo="none" lang="en">ifEntry.ifIndex=1</literal> MBean in the <literal moreinfo="none" lang="en">ifTable</literal> domain. Set the reload
period to 10, and click the "Reload" button.</para><para lang="en">You should see the effect of the trap generator which is to switch the
value of the <literal moreinfo="none" lang="en">IfOperStatus</literal> variable. It is our implementation
of the table entry which sends a trap when this status is changed.</para></step><step performance="required" id="snmpadaptor-step-38" lang="en"><para lang="en">Go back to the agent view and click on the <literal moreinfo="none" lang="en">name=Snmp</literal>
MBean in the <literal moreinfo="none" lang="en">RFC1213_MIB</literal> domain. Scroll down to see the <literal moreinfo="none" lang="en">SnmpOutPkts</literal> and <literal moreinfo="none" lang="en">SnmpOutTraps</literal> variables.</para><para lang="en">These variables should be the only non zero values, if no manager has
connected to the SNMP agent. The <literal moreinfo="none" lang="en">Snmp</literal> group shows information
about the SNMP adaptor, and we can see how many traps have been sent since
the agent was launched.</para></step><step performance="required" id="snmpadaptor-step-39" lang="en"><para lang="en">Type "Control-C" when you are done interacting with the
agent.</para></step></procedure><para lang="en">The <classname lang="en">LinkTrapGenerator</classname> MBean is not manageable
through the SNMP adaptor because it is not part of any MIB. It is an example
of another MBean providing some control of the SNMP agent, and this control
can be exercised by other managers connecting through other protocols. This
shows that designing an SNMP agent application involves both the implementation
of the MIB functionality and, if desired, the implementation of other dynamic
controls afforded by the JMX architecture and the services of the Java Dynamic Management Kit.</para></sect2></sect1><sect1 id="snmpadaptor-2" lang="en"><title lang="en">Access Control Lists (ACL)</title><para lang="en">For the SNMP adaptor, the Java Dynamic Management Kit provides access control based
on the IP address and community of the manager's host machine. Information
on the access rights for communities and host machines is stored in an ACL
file.</para><para lang="en">The ACL file also defines the hosts of managers to which to agent will
send traps. When a trap is sent, the agent will send it to all hosts listed
in the trap definitions of the ACL file.</para><para lang="en">To enable access control and traps for the SNMP adaptor, ensure that
an ACL file exists when any agents are started. The ACL file must be named <filename moreinfo="none" lang="en">jdmk.acl</filename> and must be located in the configuration directory.</para><informaltable frame="topbot" lang="en"><tgroup cols="2" colsep="0" rowsep="1" lang="en"><colspec colname="column1" colwidth="88*"/><colspec colname="column2" colwidth="308*"/><thead lang="en"><row lang="en"><entry align="left" valign="bottom" lang="en"><para lang="en">Operating Environment</para></entry><entry align="left" valign="bottom" lang="en"><para lang="en">Configuration Directory</para></entry></row></thead><tbody lang="en"><row rowsep="0" lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Solaris</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><replaceable lang="en">installDir</replaceable><filename moreinfo="none" lang="en">/SUNWjdmk/jdmk4.0/<replaceable lang="en">JDKversion</replaceable>/etc/conf/</filename></para></entry></row><row lang="en"><entry align="left" valign="middle" lang="en"><para lang="en">Windows NT</para></entry><entry align="left" valign="middle" lang="en"><para lang="en"><replaceable lang="en">installDir</replaceable><filename moreinfo="none" lang="en">\SUNWjdmk\jdmk4.0\<replaceable lang="en">JDKversion</replaceable>\etc\conf\</filename></para></entry></row></tbody></tgroup></informaltable><para lang="en">Alternatively, you may specify a different file by setting the <literal moreinfo="none" lang="en">jdmk.acl.file</literal> property when launching your agent. For example, if
the full pathname of your ACL file is <replaceable lang="en">MyAclFile</replaceable>,
use this command to launch the agent with access control enabled:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> -Djdmk.acl.file=<replaceable lang="en">MyAclFile</replaceable> <replaceable lang="en">MyAgent</replaceable></userinput></screen><para lang="en">If an ACL file exists, the access rights it defines apply to all managers
or proxy servers that access the agent through its SNMP adaptor. If the ACL
file does not exist when the agents are started, all managers are granted
full access to the agent through the SNMP adaptor.</para><sect2 id="snmpadaptor-7" lang="en"><title lang="en">ACL File Format</title><para lang="en">An ACL file contains an <literal moreinfo="none" lang="en">acl</literal> group defining community
and manager access rights and a <literal moreinfo="none" lang="en">trap</literal> group defining the
community and hosts for sending traps.</para><sect3 id="snmpadaptor-8" lang="en"><title lang="en">Format of the <literal moreinfo="none" lang="en">acl</literal> Group</title><para lang="en">The <literal moreinfo="none" lang="en">acl</literal> group contains one or more lists of community
configurations.</para><programlisting format="linespecific" lang="en" role="fragment">acl = {
   <replaceable lang="en">list1</replaceable>
   <replaceable lang="en">list2</replaceable>
     ...
   <replaceable lang="en">listN</replaceable>
}</programlisting><para lang="en">Each list has the following format: </para><programlisting format="linespecific" lang="en" role="fragment">{
   communities = <replaceable lang="en">communityList</replaceable>
   access = <replaceable lang="en">accessRights</replaceable>
   managers = <replaceable lang="en">hostList</replaceable>
}</programlisting><para lang="en">The <replaceable lang="en">communityList</replaceable> is a list of SNMP community
names to which this access control applies. The community names in this list
are separated by commas.</para><para lang="en">The <replaceable lang="en">accessRights</replaceable> specifies the rights to
be granted to all managers running on the machines specified in the <literal moreinfo="none" lang="en">managers</literal> item. There are two possible values: either <literal moreinfo="none" lang="en">read-write</literal> or <literal moreinfo="none" lang="en">read-only</literal>.</para><para lang="en">The <replaceable lang="en">hostList</replaceable> item specifies the host machines
of the managers to be granted the access rights. The <replaceable lang="en">hostList</replaceable> is a comma-separated list of hosts, each of which can be expressed
as any one of the following:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">A host name </para></listitem><listitem lang="en"><para lang="en">An IP address</para></listitem><listitem lang="en"><para lang="en">A subnet mask</para></listitem></itemizedlist><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">To distinguish between IP addresses and subnet masks in an ACL file,
each integer in a subnet mask is separated by an exclamation mark (<literal moreinfo="none" lang="en">!</literal>) instead of a dot.</para></note></sect3><sect3 id="snmpadaptor-9" lang="en"><title lang="en">Format of the <literal moreinfo="none" lang="en">trap</literal> Group</title><para lang="en">The <literal moreinfo="none" lang="en">trap</literal> group specifies the hosts to which the agent
can send traps. This group contains a one or more trap community definitions.</para><programlisting format="linespecific" lang="en" role="fragment">trap = {
   <replaceable lang="en">community1</replaceable>
   <replaceable lang="en">community2</replaceable>
   ...
   <replaceable lang="en">communityN</replaceable>
}</programlisting><para lang="en">Each defines the association between a set of hosts and the SNMP community
string in the traps to be sent to them. Each trap definition has the following
format:  </para><programlisting format="linespecific" lang="en" role="fragment">{
   trap-community = <replaceable lang="en">trapCommunityString</replaceable>
   hosts = <replaceable lang="en">trapInterestHostList</replaceable>
}</programlisting><para lang="en">The <replaceable lang="en">trapCommunityString</replaceable> item specifies the
SNMP community string. It will be included in the traps sent to the hosts
specified in the <literal moreinfo="none" lang="en">hosts</literal> item.</para><para lang="en">The <replaceable lang="en">trapInterestHostList</replaceable> item specifies a
comma-separated list of hosts. Each host must be identified by its name or
complete IP address.</para></sect3></sect2><sect2 id="snmpadaptor-13" lang="en"><title lang="en">Custom Access Control</title><para lang="en">The ACL file is the default access control mechanism in the SNMP adaptor.
The <classname lang="en">SnmpAdaptorServer</classname> class has constructors that let
you specify your own access control mechanism. For example, if your agent
runs on a device with no file system, you could implement a mechanism which
doesn't rely on the <filename moreinfo="none" lang="en">jdmk.acl</filename> file.</para><para lang="en">Your access mechanism must be a class that implements the <classname lang="en">IPAcl</classname> interface. This interface specifies the methods that the
SNMP adaptor uses to check permissions when processing a request. If you instantiate
the SNMP adaptor with your access control class, the adaptor will call your
implementation of the access control methods.</para><para lang="en">The <classname lang="en">JdmkAcl</classname> class implements the default access
mechanism that uses an ACL file. It is also an implementation of the <classname lang="en">IPAcl</classname> interface. By default, the SNMP adaptor will use this implementation
if no other is passed to its constructor.</para></sect2></sect1><sect1 id="snmpadaptor-3" lang="en"><title lang="en">Message-Level Security</title><para lang="en">The <classname lang="en">SecureAgent</classname> example shows another level of
security at the SNMP message level.  Whereas the access control mechanism
handles access rights to all MIBs for communities of manager hosts, message-level
security lets you control how PDUs (protocol data units) representing requests
are encoded and decoded by the SNMP protocol adaptor.</para><para lang="en">The data in an SNMP message is stored in its raw form as a byte array.
When receiving a message, the SNMP protocol adaptor must decode the data to
obtain the corresponding request, and before sending a request, the adaptor
must encode it as a message. By default, the basic encoding rules (BER) are
used to translate back and forth between message and decoded PDU. The SNMP
protocol adaptor provides a hook to let you implement your own encoding and
decoding mechanism.</para><para lang="en">Message-level security relies on the following classes in the <classname lang="en">javax.management.snmp</classname> package:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><classname lang="en">SnmpPduPacket</classname> class</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">SnmpMessage</classname> class</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">SnmpPduFactory</classname> interface</para></listitem></itemizedlist><para lang="en">An <classname lang="en">SnmpPduPacket</classname> object represents the fully
decoded description of an SNMP request. In particular, it includes the operation
type (get, set, ...), the list of variables to which the operation applies,
the request identifier, and the protocol version.</para><para lang="en">The <classname lang="en">SnmpMessage</classname> object is a partially decoded
representation of the SNMP request. The type of the request, the variables
and their values all remain encoded as a byte array. This object also contains
the default BER encoding and decoding methods. The <classname lang="en">SnmpMessage</classname> class is derived from the Message syntax in RFC 1157 and RFC1902.</para><para lang="en">Both of these classes also contain port and address information of the
SNMP manager. When implementing your own security mechanism, you also have
access to the contents of the PDU or message you are handling. This lets you
implement security based on several factors:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">The host or community of the SNMP manager in a message before
it is decoded</para></listitem><listitem lang="en"><para lang="en">The type or contents of a request after it is decoded</para></listitem><listitem lang="en"><para lang="en">Some encryption of the raw data</para></listitem></itemizedlist><para lang="en">Because message-based security gives you access to all these different
factors, you can perform elaborate filtering of incoming requests. For example,
you could limit the access of certain SNMP managers to certain variables,
or you could filter values, such as untrusted IP addresses, before they are
assigned.</para><para lang="en">If your security is based on the encryption of the message data, your
manager must also be using the same encryption. Since the SNMP classes are
also part of the SNMP manager API, you can reuse the same encryption code
in your manager if it is developed in the Java programming language. Security
in the SNMP manager API is implemented in the <classname lang="en">SnmpPeer</classname>
object, see <link linkend="snmpmanager-14" lang="en">"SNMP Manager Security"</link> for more information.</para><para lang="en">If your security scheme is based only on the sender of the message or
contents of the PDU, it can be applied unilaterally by the agent, without
requiring any coordination with the manager application. This is what is demonstrated
in the <classname lang="en">SecureAgent </classname>example.</para><sect2 id="snmpadaptor-10" lang="en"><title lang="en">Implementing the <classname lang="en">SnmpPduFactory</classname> Interface</title><para lang="en">In the SNMP protocol adaptor, the task of translating an <classname lang="en">SnmpMessage</classname> object into an <classname lang="en">SnmpPduPacket</classname>
object is delegated to an object which implements the <classname lang="en">SnmpPduFactory </classname>interface. This interface defines two methods, one for decoding
messages into PDUs and one for encoding PDUs into messages:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><classname lang="en">decodePdu</classname> takes an <classname lang="en">SnmpMessage</classname> and should return a decoded <classname lang="en">SnmpPduPacket</classname>
object; if it returns null or raises an exception, the incoming message is
assumed to have failed the security check</para></listitem><listitem lang="en"><para lang="en"><classname lang="en">encodePdu</classname> takes a <classname lang="en">SnmpPduPacket</classname> and should return an encoded <classname lang="en">SnmpMessage</classname> to send</para></listitem></itemizedlist><para lang="en">In our example, the <classname lang="en">SnmpPduFactoryImpl</classname> class
implements these methods and rejects messages if they originate from certain
hosts. The list of hosts to refuse is passed to the class constructor at instantiation.</para><example role="code" id="snmpadaptor-ex-14" lang="en"><gentext type="text">Example 7-6 </gentext><title lang="en"/><programlisting format="linespecific" lang="en" role="complete">public class SnmpPduFactoryImpl implements SnmpPduFactory {

    private String[] <userinput moreinfo="none" lang="en">hostNames</userinput>;

    // HostNames is the array of the host names whose requests will be
    // refused by the agent
    public <userinput moreinfo="none" lang="en">SnmpPduFactoryImpl</userinput>(String[] hostNames) {
        this.hostNames = hostNames;
    }

    public SnmpPduPacket <userinput moreinfo="none" lang="en">decodePdu</userinput>(SnmpMessage msg)
        throws SnmpStatusException {

        // Get the sender's hostname
        String from = msg.address.getHostName();
        for (int i = 0; i &lt; hostNames.length; i++) {
            if (from.equals(hostNames[i])) {
                java.lang.System.out.println("Pdu rejected from " + from);
                return null;
            }
        }
        // If the host is accepted, we return the standard BER decoding
        return msg.decodePdu();    
    }

    // No security when sending, just do the standard BER encoding
    public SnmpMessage <userinput moreinfo="none" lang="en">encodePdu</userinput>(SnmpPduPacket pdu, int maxPktSize)
        throws SnmpStatusException, SnmpTooBigException {
        
        SnmpMessage result = new SnmpMessage();
        result.encodePdu(pdu, maxPktSize);
        return result;    
    }
}</programlisting></example><para lang="en">Beyond our simple check of the sender's hostname, our example relies
on the standard BER encoding and decoding of the <classname lang="en">SnmpMessage</classname>
class. Even if you choose to implement encryption, it can still be implemented
on top of the standard BER for simplicity. In this case, you only need to
encrypt the message's byte array before sending it and decrypt it before the
standard decoding.</para></sect2><sect2 id="snmpadaptor-11" lang="en"><title lang="en">Using a Custom PDU Factory</title><para lang="en">To use your custom PDU factory in your SNMP agent, you need to call
the <classname lang="en">usePduFactory</classname> method of your <classname lang="en">SnmpAdaptorServer</classname> instance. First instantiate your PDU factory implementation and
then pass it to this method. Your encoding and decoding scheme will then replace
the standard one used by the SNMP protocol adaptor.</para><para lang="en">The <filename moreinfo="none" lang="en">SecureAgent.java</filename> file contains a simple agent
like the one presented in <link linkend="snmpadaptor-5" lang="en">"The SNMP Protocol Adaptor"</link>. It only adds the
call to force the SNMP adaptor to use the new PDU factory that we specify.</para><programlisting format="linespecific" lang="en" role="complete">// Use SnmpPduFactoryImpl class for SnmpPduFactory to filter requests.
// Enter your list of refused hosts as arguments when launching this agent.
// The agent will reject requests coming from the specified hosts.
//
String[] refusedHosts = new String[args.length];
refusedHosts = args;
snmpAdaptor.<userinput moreinfo="none" lang="en">usePduFactory</userinput>(new SnmpPduFactoryImpl(refusedHosts));</programlisting><para lang="en">The secure agent does not use the trap generator and does not demonstrate
traps. The <classname lang="en">LinkTrapGenerator</classname> class is not written to
function with the <classname lang="en">SecureAgent</classname> class.</para></sect2><sect2 id="snmpadaptor-28" lang="en"><title lang="en">Running the Secure Agent Example</title><para lang="en">You can only demonstrate the output of our custom PDU factory if you
have an SNMP manager application which can connect to the secure agent. See <link linkend="snmpmanager-1" lang="en">"Developing an SNMP Manager"</link> for
example applications you can use.</para><para lang="en">The <classname lang="en">SecureAgent</classname> class takes command line arguments
to create the list of hosts from which it will refuse SNMP requests. Use the
following command to launch the secure agent example:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent [<replaceable lang="en">host1..hostN</replaceable>]</userinput></screen><para lang="en">Whenever one of the refused hosts sends a request, you should see the
message displayed by our custom PDU factory implementation. Type "Control-C"
when you are done running the secure agent.</para><para lang="en">You can combine message-level security and access control defined by
the presence of an ACL file. The ACL file indicates trusted hosts and communities
from which requests are accepted. After they are accepted, they are decoded
with the message-level security you provide. This lets you provide more precise
security based on types of requests or the target variable, as well as any
encryption.</para></sect2></sect1><sect1 id="snmpadaptor-4" lang="en"><title lang="en">Stand-Alone SNMP Agents</title><para lang="en">The design of the SNMP protocol adaptor and of the MBeans generated
by <command moreinfo="none" lang="en">mibgen</command> give you the option of creating an SNMP agent
that is not a Java Dynamic Management agent.</para><para lang="en">This stand-alone agent has no MBean server and thus no possibility of
being managed other than through the SNMP protocol. The application must instantiate
all MIBs that the SNMP agent will need, as it will be impossible to create
them through some other manager. The advantage of a stand-alone agent is the
reduced size of application, in terms of memory usage.</para><example role="code" id="snmpadaptor-ex-40" lang="en"><gentext type="text">Example 7-7 </gentext><title lang="en">The <classname lang="en">StandAloneSnmpAgent</classname> Example</title><programlisting format="linespecific" lang="en" role="complete">import com.sun.jdmk.Trace;
import com.sun.jdmk.comm.SnmpAdaptorServer;

public class StandAloneSnmpAgent {

    static SnmpAdaptorServer snmpAdaptor = null;

    public static void main(String args[]) {

        // enable tracing
        [...]

        try {
            // The agent is started on a non standard SNMP port: 8085
            int port = 8085;
            snmpAdaptor = new SnmpAdaptorServer(port);
            java.lang.System.out.println(
                "NOTE: SNMP Adaptor is bound on port " + port);

            // Start the adaptor
            <userinput moreinfo="none" lang="en">snmpAdaptor.start()</userinput>;

            // Send a coldStart SNMP Trap
            snmpAdaptor.sendV1Trap(0, 0, null);

            // Create the MIB you want in the agent (ours is MIB-II subset)
            <userinput moreinfo="none" lang="en">RFC1213_MIB mib2 = new RFC1213_MIB()</userinput>;

            // Initialize the MIB so it creates the associated MBeans
            <userinput moreinfo="none" lang="en">mib2.init()</userinput>;

            // Bind the MIB to the SNMP adaptor
            <userinput moreinfo="none" lang="en">mib2.setSnmpAdaptor(snmpAdaptor)</userinput>;

            // Optional: create a LinkTrapGenerator
            int ifIndex = 1;
            LinkTrapGenerator trapGen = new LinkTrapGenerator(ifIndex);
            trapGen.start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Needed to get a reference on the SNMP adaptor object
    static public SnmpAdaptorServer getSnmpAdaptor() {
        return snmpAdaptor;
    }
}</programlisting></example><para lang="en">As this example demonstrates, the stand-alone agent uses exactly the
same MIB MBeans, with the same customization, as our other agents. However,
instead of registering them in MBean server, they are only instantiated. And
whereas the registration process creates all subordinate MBeans of the MIB,
now we must call its <classname lang="en">init</classname> method explicitly. The <classname lang="en">init</classname> method performs the same function as the <classname lang="en">preRegister</classname> method, only it does not register the MBean with the MBean server.</para><para lang="en">The <command moreinfo="none" lang="en">mibgen</command> tool automatically generates both the
pre-registration methods and the <classname lang="en">init</classname> methods in the
MIB MBeans. Therefore, no special action is necessary to use them in either
a regular agent or a stand-alone agent. If you use a stand-alone agent for
memory considerations, you can remove the registration process from the MBean
and only customize the init process.</para><para lang="en">In our example, we have applied the customizations to both processes
so that the MIB can be used by either agent. In the following code, customizations
are noted with <literal moreinfo="none" lang="en">MODIF_</literal> comments:</para><example role="code" id="snmpadaptor-ex-41" lang="en"><gentext type="text">Example 7-8 </gentext><title lang="en">Customizations in the Generated <filename moreinfo="none" lang="en">RFC1213_MIB.java</filename> File</title><programlisting format="linespecific" lang="en" role="complete">public class RFC1213_MIB extends SnmpMib implements Serializable {

    // Default constructor. Initialize the Mib tree
    public RFC1213_MIB() {
        mibName = "RFC1213_MIB";
    }

    // Initialization of the MIB with no registration in the MBean server
    public void init() throws IllegalAccessException {
        // Allow only one initialization of the MIB
        if (isInitialized == true) {
            return ;
        }

        // Initialization of the "Snmp" group
        {
            SnmpMeta meta = new SnmpMeta((SnmpMib)this);
// MODIF_BEGIN
            //meta.setInstance(new Snmp((SnmpMib)this));
            meta.setInstance(new SnmpImpl((SnmpMib)this));
// MODIF_END
            root.registerNode("1.3.6.1.2.1.11", (SnmpMibNode)meta);
        }

        // Initialization of the other groups
        [...]

        isInitialized = true;
    }

    // Initialization of the MIB with AUTOMATIC REGISTRATION
    // in the MBean server
    public ObjectName preRegister(MBeanServer server, ObjectName name)
        throws Exception {

        // Allow only one initialization of the MIB
        if (isInitialized == true) {
            throw new InstanceAlreadyExistsException();
        }

        // Initialize MBeanServer information
        this.server = server;

        // Initialization of the "Snmp" group
        {
            SnmpMeta meta = new SnmpMeta((SnmpMib)this);
// MODIF_BEGIN
            //Snmp instance = new Snmp((SnmpMib)this, server);
            Snmp instance = new SnmpImpl((SnmpMib)this, server);
// MODIF_END
            meta.setInstance(instance);
            root.registerNode("1.3.6.1.2.1.11", (SnmpMibNode)meta);
            server.registerMBean(
                instance, new ObjectName(mibName + ":name=Snmp"));
        }

        // Initialization of the other groups
        [...]

        isInitialized = true;
        return name;
    }

    private boolean isInitialized = false;

}</programlisting></example><para lang="en">After the MIB is initialized, it only needs to be bound to the SNMP
adaptor, as in the other agents. That is all you need to do when programing
a stand-alone SNMP agent.</para><sect2 id="snmpadaptor-29" lang="en"><title lang="en">Running the Stand-Alone Agent Example</title><para lang="en">Launch the stand-alone agent with the following command:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> StandAloneSnmpAgent</userinput></screen><para lang="en">You should see the same initialization messages as with the simple agent.
Then, you should see the agent sending out a trap every two seconds. If you
have an SNMP manager application, you can send requests to the agent and receive
its traps. See <link linkend="snmpmanager-1" lang="en">"Developing an SNMP Manager"</link> for example applications you can use.</para><para lang="en">The only limitation of a stand-alone agent is that you cannot access
or manage the SNMP adaptor and MIB MBeans in the dynamic management sense.
However, the SNMP adaptor still relies on the ACL file for access control
and traps, and you can implement other security schemes as described in <link linkend="snmpadaptor-3" lang="en">"Message-Level Security"</link>.</para><para lang="en">Type "Control-C" when you are done running the stand-alone
agent.</para></sect2></sect1></chapter><chapter id="snmpmanager-1" lang="en"><gentext type="text">Chapter 8</gentext><gentext type="toc">8.  Developing an SNMP Manager</gentext><title lang="en">Developing an SNMP Manager</title><highlights lang="en"><para lang="en">The Java Management extensions specify the SNMP manager API for implementing an SNMP manager application in the Java programming language. This API is covered in the JMX specification and in the Javadoc API provided with the Java Dynamic Management Kit (see <link linkend="preface-5" lang="en">"Related Books"</link> in the preface for more information). Here we explain the example applications that use this API.</para><para lang="en">The SNMP manager API can be used to access any SNMP agent, not just those developed with the Java Dynamic Management Kit. The manager only requires a definition of the MIB that it will access. We generate the class representing the OID table from the MIB file using the <command moreinfo="none" lang="en">mibgen</command> tool.</para><para lang="en">The complete source code for these applications is available in the <filename moreinfo="none" lang="en">Snmp/Manager</filename> example directory located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> (see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="snmpmanager-2" lang="en">"The Synchronous Manager Example"</link> shows the simplest way to program an SNMP manager in the Java programming language</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpmanager-5" lang="en">"The Asynchronous Manager Example"</link> demonstrates the more advanced features of the SNMP manager API</para></listitem></itemizedlist></highlights><sect1 id="snmpmanager-2" lang="en"><title lang="en">The Synchronous Manager Example</title><para lang="en">The synchronous SNMP manager is the simplest to program. The manager sends a request to an agent (peer) and waits a given timeout period for the answer. During the wait, the manager is blocked, and when the timeout delay expires, the manager can see if the request was answered or not.</para><para lang="en">A manager issues requests on variables identified by their full OID. If it has initialized the OID table description of the MIB it will access, it can also refer to the variables by their name. To do this, the manager should call the <classname lang="en">setSnmpOidTable</classname> method of the static <classname lang="en">SnmpOid</classname> object. It should pass in the OID table object instantiated from the <classname lang="en">SnmpOidTableSupport</classname> sub-class generated by the <command moreinfo="none" lang="en">mibgen</command> tool when "compiling" the MIB.</para><para lang="en">The SNMP manager API specifies the <classname lang="en">SnmpPeer</classname> object for describing an agent, and the <classname lang="en">SnmpParameters</classname> object for describing its read-write communities and its protocol version (SNMPv1 or SNMPv2). The <classname lang="en">SnmpSession</classname> is an object for sending requests and we can associate a default peer to it. The session instance has an <classname lang="en">SnmpOptions</classname> field which we can use to set multiplexing and error fixing behavior.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">The objects specified by the SNMP manager API are not MBeans and cannot be registered in an MBean server to create a manager that could be controlled remotely. However, you could write an MBean that uses these classes to retrieve and expose information from SNMP agents.</para></note><para lang="en">A manager can contain any number of peers, one for each agent it wishes to access, and any number of sessions, one for each type of behavior it wishes to implement. Once the peers and the sessions are initialized, the manager can build lists of variables and send session requests to operate on them. The session returns a request object, and the manager calls its <classname lang="en">waitForCompletion</classname> method with the desired timeout delay.</para><para lang="en">Finally, the manager analyzes the result of the request, first to see if there were any errors, then to extract the data returned by the request.</para><para lang="en">Here is the code of the <classname lang="en">main</classname> method of the <classname lang="en">SimpleManager</classname> application. It performs all of the above steps to perform a very simple management operation.</para><example role="code" id="snmpmanager-ex-16" lang="en"><gentext type="text">Example 8-1 </gentext><title lang="en">The <classname lang="en">SimpleManager</classname> Example</title><programlisting format="linespecific" lang="en" role="complete">// read the command line parameters
String host = argv[0];
String port = argv[1];

// Specify the OidTable containing all the MIB II knowledge
// Use the OidTable generated by mibgen when compiling MIB II
//
SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
SnmpOid.setSnmpOidTable(oidTable);
       
SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
     
// When creating the parameter object, you can specify the
// read and write community to be used when querying the agent.
SnmpParameters params = new SnmpParameters("public", "private");
agent.setSnmpParam(params);
     
SnmpSession session = new SnmpSession("SimpleManager session");
       
// When invoking a service provided by the SnmpSession, it
// will use the default peer if none is specified explicitly
session.setDefaultPeer(agent);
     
// Build the list of variables you want to query.
// For debugging, you can associate a name to your list.
SnmpVarbindList list= new SnmpVarbindList(
    "SimpleManager varbind list");
     
// We want to read the "sysDescr" variable.
list.addVariable("sysDescr.0");
    
// Make the SNMP get request and wait for the result.
SnmpRequest request = session.snmpGet(null, list);
boolean completed = request.waitForCompletion(10000);

// Check for a timeout of the request.
if (completed == false) {
    java.lang.System.out.println(
        "Request timed out. Check reachability of agent");
    java.lang.System.exit(0);
}

// Check if the response contains an error.
int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
    java.lang.System.out.println("Error status = " + 
        SnmpRequest.snmpErrorToString(errorStatus));
    java.lang.System.out.println("Error index = " +
        request.getErrorIndex());
    java.lang.System.exit(0);
}

// Now we can extract the content of the result.
SnmpVarbindList result = request.getResponseVbList();
java.lang.System.out.println("Result: \n" + result);

// End the session properly and we're done
session.destroySession();
java.lang.System.exit(0);</programlisting></example><sect2 id="snmpmanager-17" lang="en"><title lang="en">Running the <classname lang="en">SimpleManager</classname> Example</title><para lang="en">In the <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Snmp/Manager</filename> directory, we first need to generate the OID table description of MIB-II that our manager will access. Then we compile the example classes. To set up your environment, see <link linkend="preface-11" lang="en">"Directories and Classpath"</link> in the preface.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">mibgen -mo -d . mib_II.txt</userinput>
[output omitted]
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> -d . *.java</userinput></screen><para lang="en">Make sure that no other agent is running on port 8085, and launch the simple SNMP agent in <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Snmp/Agent</filename>. See <link linkend="snmpadaptor-6" lang="en">"MIB Development Process"</link> if you have not already built and run this example.</para><para lang="en">Here we give commands for launching the applications from the same terminal window running the Korn shell. On the Windows NT platform, you will have to launch each application in a separate window. We redirect the output messages since this agent sends traps periodically.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/Snmp/Agent</userinput>
$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent &gt; Agent.out &amp;</userinput></screen><para lang="en">Now we can launch the manager application to connect to this agent. If you wish to run the manager on a different host, replace <literal moreinfo="none" lang="en">localhost</literal> with the name of the machine where you launched the agent.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/Snmp/Manager</userinput>
$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> SimpleManager localhost 8085</userinput>
SimpleManager::main: Send get request to SNMP agent on localhost port 8085
Result: 
[Object ID : 1.3.6.1.2.1.1.1.0  (Syntax : String)
Value : SunOS sparc 5.7]</screen><para lang="en">Here we see the output of the SNMP request, it is the value of the <literal moreinfo="none" lang="en">sysDescr</literal> on the agent.</para><para lang="en">Leave the agent running if you are going on to the next example, otherwise remember to stop it with the following commands:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">fg</userinput>
java [...] Agent &gt; agent.out <userinput moreinfo="none" lang="en">&lt;Control-C&gt;</userinput>
^C$ <userinput moreinfo="none" lang="en">rm <replaceable lang="en">examplesDir</replaceable>/Snmp/Agent/agent.out</userinput></screen></sect2></sect1><sect1 id="snmpmanager-5" lang="en"><title lang="en">The Asynchronous Manager Example</title><para lang="en">The asynchronous SNMP manager lets you handle more requests in the same amount of time because the manager is not blocked waiting for responses. Instead it creates a request handler object which runs as a separate thread and processes several responses concurrently. Otherwise, the initialization of peers, parameters, sessions and options is identical to that of a synchronous manager.</para><para lang="en">In this example application, we also demonstrate how to implement and enable a trap listener for the traps sent by the agent. First we need to instantiate an <classname lang="en">SnmpEventReportDispatcher</classname> object. Then we add our listener implementation through its <classname lang="en">addEventReportListener</classname> method, and finally we start its thread. Trap listeners can be implemented in any manager using the SNMP manager API, not only asynchronous managers.</para><example role="code" id="snmpmanager-ex-18" lang="en"><gentext type="text">Example 8-2 </gentext><title lang="en">The <classname lang="en">AsyncManager</classname> Example</title><programlisting format="linespecific" lang="en" role="complete">// read the command line parameters
String host = argv[0];
String port = argv[1];
   
// Use the OidTable generated by migen when compiling MIB II.
SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
       
// Sample use of the OidTable.
SnmpOidRecord record = oidTable.resolveVarName("udpLocalPort");
java.lang.System.out.println(
    "AsyncManager::main: variable = " + record.getName() + 
        " oid = " + record.getOid() + " type = " + record.getType());

// Initialize the SNMP Manager API.
SnmpOid.setSnmpOidTable(oidTable);
  
// Create an SnmpPeer object for representing the agent 
SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
     
// Create parameters for communicating with the agent
SnmpParameters params = new SnmpParameters("public", "private");
agent.setSnmpParam(params);
     
// Build the session and assign its default peer
SnmpSession session = new SnmpSession("AsyncManager session");
session.setDefaultPeer(agent);

// Create a listener and dispatcher for SNMP traps:
// SnmpEventReportDispatcher will run as a thread and
// listens for traps in UDP port = agent port + 1
SnmpEventReportDispatcher trapAgent =
    new SnmpEventReportDispatcher(Integer.parseInt(port)+1);
// TrapListenerImpl will receive a callback
// when a valid trap PDU is received.
trapAgent.addEventReportListener(new TrapListenerImpl()); 
new Thread(trapAgent).start();       

// Build the list of variables to query
SnmpVarbindList list = new SnmpVarbindList("AsyncManager varbind list");
list.addVariable("sysDescr.0");
    
// Create a simple implementation of an SnmpHandler. 
AsyncRspHandler handler = new AsyncRspHandler();
       
// Make the SNMP walk request with our handler
SnmpRequest request = session.snmpWalkUntil(
    handler, list, new  SnmpOid("sysServices"));
       
// Here you could do whatever processing you need.
// In the context of the example, we are just going to wait
// 5 seconds so that we can receive trap PDUs.
Thread.sleep(5000);

// Here the handle should have resume the activity of the thread
// so the request is over
java.lang.System.out.println("done");
       
// End the session properly and we're done.
//
session.destroySession();
java.lang.System.exit(0);</programlisting></example><para lang="en">In this example, the manager performs an <literal moreinfo="none" lang="en">snmpWalkUntil</literal> request which will give a response for each variable that it gets. The response handler will be called every time to process the response.</para><sect2 id="snmpmanager-12" lang="en"><title lang="en">SNMP Trap Handler</title><para lang="en">A trap handler for the SNMP manager is an object that implements the <classname lang="en">SnmpEventReportListener</classname> interface in the <classname lang="en">javax.management.snmp.manager</classname> package. When this object is bound as a listener of an <classname lang="en">SnmpEventReportDispatcher</classname> object, its methods will be called to handle trap PDUs.</para><para lang="en">The interface defines two methods, one for processing SNMPv1 traps and the other for SNMPv2 traps. Trap PDUs have already been decoded by the dispatcher: the v1 handler must process an <classname lang="en">SnmpPduTrap</classname> object, and the v2 handler must process an <classname lang="en">SnmpPduRequest</classname> object representing a trap. In our implementation, we are only interested in v1 traps, and we just print out the trap information fields.</para><example role="code" id="snmpmanager-ex-19" lang="en"><gentext type="text">Example 8-3 </gentext><title lang="en">The <classname lang="en">SnmpEventReportListener</classname> Implementation</title><programlisting format="linespecific" lang="en" role="complete">public class TrapListenerImpl implements SnmpEventReportListener {

    public void processSnmpTrapV1(SnmpPduTrap trap) {
        java.lang.System.out.println(
            "NOTE: TrapListenerImpl received trap :");
        java.lang.System.out.println(
            "\tGeneric " + trap.genericTrap);
        java.lang.System.out.println(
            "\tSpecific " + trap.specificTrap);
        java.lang.System.out.println(
            "\tTimeStamp " + trap.timeStamp);
        java.lang.System.out.println(
            "\tAgent address " + trap.agentAddr.stringValue());
    }

    public void processSnmpTrapV2(SnmpPduRequest trap) {
        java.lang.System.out.println("NOTE: Trap V2 ignored");
    }
}</programlisting></example></sect2><sect2 id="snmpmanager-15" lang="en"><title lang="en">The Request Handler</title><para lang="en">A request handler for an asynchronous manager is an implementation of the <classname lang="en">SnmpHandler</classname> interface. When a handler object is associated with a request, its methods are called when the agent returns an answer or fails to return an answer. In these methods, you implement whatever actions you wish for processing the results of a request. </para><para lang="en">The timeout used by the request handler is the one specified by the <classname lang="en">SnmpPeer</classname> object representing the agent. The handler is also called to process any errors caused by the request in the session. This insures that the manager application is never interrupted after issue a request.</para><example role="code" id="snmpmanager-ex-20" lang="en"><gentext type="text">Example 8-4 </gentext><title lang="en">The <classname lang="en">SnmpHandler</classname> Implementation</title><programlisting format="linespecific" lang="en" role="complete">public class AsyncRspHandler implements SnmpHandler {
   
    // Empty constructor
    public AsyncRspHandler() {
    }

    // Called when the agent responds to a request
    public void processSnmpPollData( SnmpRequest request,
        int errStatus, int errIndex, SnmpVarbindList vblist) {

        java.lang.System.out.println(
            "Processing response: " + request.toString());
        java.lang.System.out.println(
            "errStatus = " + SnmpRequest.snmpErrorToString(errStatus) +
            " errIndex = " + errIndex);

        // Check if a result is available.
        if (request.getRequestStatus() ==
            SnmpRequest.stResultsAvailable) {

            // Extract the result for display
            SnmpVarbindList result = request.getResponseVbList();
            java.lang.System.out.println(
                "Result = " + result.vbListToString());
        }
    }

    // Called when the agent fails to respond to a request
    public void processSnmpPollTimeout(SnmpRequest request) {
        
        java.lang.System.out.println(
            "Request timed out: " + request.toString());
        
        if (request.getRequestStatus() == 
            SnmpRequest.stResultsAvailable) {

            // The result is empty and will display an error message
            SnmpVarbindList result = request.getResponseVbList();
            java.lang.System.out.println(
                "Result = " + result.vbListToString());
        }
    }

    // Called when there is an error in the session
    public void processSnmpInternalError(SnmpRequest request,
        String errmsg) {
        
        java.lang.System.out.println(
            "Session error: " + request.toString());
        java.lang.System.out.println("Error is: " + errmsg);
    }
}</programlisting></example></sect2><sect2 id="snmpmanager-14" lang="en"><title lang="en">SNMP Manager Security</title><para lang="en">These example applications do not cover the security features in the SNMP manager. However, security can be implemented at the message level with a custom PDU factory, in the same way that it is for SNMP agents (see <link linkend="snmpadaptor-3" lang="en">"Message-Level Security"</link>). The hook for using your own implementation of the PDU factory is the <classname lang="en">setPduFactory</classname> method of an <classname lang="en">SnmpPeer</classname> instance.</para><para lang="en">Message-level security can be implemented in this manner in both synchronous and asynchronous managers. See the <citetitle lang="en">Java Management Extensions SNMP Manager API</citetitle> document and its Javadoc API for more details.</para></sect2><sect2 id="snmpmanager-21" lang="en"><title lang="en">Running the <classname lang="en">AsyncManager</classname> Example</title><para lang="en">If you have not done so already, launch the simple SNMP agent in <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable>/Snmp/Agent</filename>, after making sure that no other agent is running on port 8085. The manager also requires the same OID table description that we generated for the synchronous manager. See <link linkend="snmpmanager-17" lang="en">"Running the <classname lang="en">SimpleManager</classname> Example"</link> for instructions to do this.</para><para lang="en">Here we give commands for launching the agent and manager from the same terminal window running the Korn shell. On the Windows NT platform, you will have to launch each application in a separate window. We redirect the output messages since this agent sends traps periodically.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/Snmp/Agent</userinput>
$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent &gt; Agent.out &amp;</userinput></screen><para lang="en">Now we can launch the manager application to connect to this agent. If you wish to run the manager on a different host, replace <literal moreinfo="none" lang="en">localhost</literal> with the name of the machine where you launched the agent.</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/Snmp/Manager</userinput>
$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> AsyncManager localhost 8085</userinput></screen><para lang="en">You should then see the output of the <literal moreinfo="none" lang="en">SnmpWalkUntil</literal> request: the response handler method is called for each variable that is returned. Since this manager also has a trap listener running in a different thread, the output may be interspersed with trap reports.</para><para lang="en">When you are done with the agent, don't forget to stop it with the following commands:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">fg</userinput>
java [...] Agent &gt; agent.out <userinput moreinfo="none" lang="en">&lt;Control-C&gt;</userinput>
^C$ <userinput moreinfo="none" lang="en">rm <replaceable lang="en">examplesDir</replaceable>/Snmp/Agent/agent.out</userinput></screen></sect2></sect1></chapter><chapter id="snmpproxy-1" lang="en"><gentext type="text">Chapter 9</gentext><gentext type="toc">9.  Implementing an SNMP Proxy</gentext><title lang="en">Implementing an SNMP Proxy</title><highlights lang="en"><para lang="en">It is easier to manage a large number of SNMP agents when they have
a hierarchical structure of master agents and sub-agents. Master agents concentrate
and relay the information in their sub-agents and can provide their own specific
information as well. Managers only communicate with the master agents and
access the sub-agents transparently, as if the information actually resided
in the master agent.</para><para lang="en">In order to do this, agents must contain an SNMP proxy for each sub-agent
that they manage. The proxy is a Java class that looks like a MIB MBean to
the agent, but which actually accesses the sub-agent to provide the information
that is requested of it. In order to access sub-agents, the proxy object relies
on the SNMP manager API.</para><para lang="en">The SNMP proxy is used in this simple example to allow a manager to
access two MIBs through one agent. You can reuse the proxy class in your management
solutions to implement any hierarchy of managers, master agents and sub-agents.</para><para lang="en">The source code for the proxy object and the sample application is available
in the <filename moreinfo="none" lang="en">Snmp/Proxy</filename> example directory located in the main <filename moreinfo="none" lang="en"><replaceable lang="en">examplesDir</replaceable></filename> (see <link linkend="preface-11" lang="en">"Directories
and Classpath"</link> in the preface).</para><para lang="en">Contents:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><link linkend="snmpproxy-2" lang="en">"The Proxy Roles"</link> explains how the example SNMP
proxy works and how it interacts with the manager and its sub-agent</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpproxy-9" lang="en">"The SNMP Proxy Implementation"</link> gives details about
how the proxy is programmed</para></listitem><listitem lang="en"><para lang="en"><link linkend="snmpproxy-5" lang="en">"Running the SNMP Proxy Example"</link> shows how to build
and launch the sub-agent, the master agent and the manager application</para></listitem></itemizedlist></highlights><sect1 id="snmpproxy-2" lang="en"><title lang="en">The Proxy Roles</title><para lang="en">As we saw in <link linkend="snmpadaptor-6" lang="en">"MIB Development Process"</link>, the MBeans that represent
a MIB are generated from the MIB definition by the <command moreinfo="none" lang="en">mibgen</command>
tool. This tool generates one main MBean representing the MIB and then one
MBean for each group and each table entry. The main MBean extends the <classname lang="en">SnmpMib</classname> class whose implementation of the abstract <classname lang="en">SnmpMibAgent</classname> class processes all requests on a MIB. </para><para lang="en">For example, if an agent receives a <literal moreinfo="none" lang="en">get</literal> request for
some variable, it will call the <classname lang="en">get</classname> method that the
main MBean inherits from <classname lang="en">SnmpMibAgent</classname>. The implementation
of this method relies on the MIB structure to find the MBean containing the
corresponding attribute and then call its getter to read the value of the
variable.</para><para lang="en">A proxy is another implementation of the <classname lang="en">SnmpMibAgent</classname>
class which, instead of resolving variables in local MBeans, reformulates
an SNMP request, sends it to a designated sub-agent and forwards the answer
that it receives. Since only the main MBean of a MIB is bound to the SNMP
adaptor, we bind the proxy instead, and the master agent transparently exposes
the MIB which actually resides in the sub-agent.</para><sect2 id="snmpproxy-17" lang="en"><title lang="en">The Master Agent</title><para lang="en">The master agent needs to instantiate one SNMP proxy object for each
sub-agent containing MIBs that it wishes to serve. The remote MIBs can be
on any number of sub-agents, and the master agent can have several proxy objects.
Sub-agents themselves may contain proxies: it is up to the designer to define
the complexity of the agent hierarchy.</para><para lang="en">The master agent may also contain a mix of proxies and MBeans for other
MIBs. In the proxy example, the master agent exposes the <literal moreinfo="none" lang="en">DEMO</literal>
MIB through local MBeans and a subset of the <literal moreinfo="none" lang="en">RFC1213</literal> MIB
through a proxy.</para><example role="code" id="snmpproxy-ex-6" lang="en"><gentext type="text">Example 9-1 </gentext><title lang="en">The Master Agent of the Example</title><programlisting format="linespecific" lang="en" role="complete">MBeanServerImpl server;
ObjectName snmpObjName;
ObjectName localMibObjName;
ObjectName remoteMibObjName;
int htmlPort = 8082;
int snmpPort = 8085;

// read sub-agent connection info from the command line
String host = argv[0];
String port = argv[1];

try {
    server = MBeanServerFactory.createMBeanServer();
    String domain = server.getDefaultDomain();

    // Create and start the HTML adaptor.
    [...]
      
    // Create and start the SNMP adaptor.
    [...]
  
    // Create, initialize, and bind the local MIB Demo.
    //
    localMibObjName = new ObjectName("snmp:class=DEMO_MIB");
    server.registerMBean(localMib, localMibObjName);
    localMib.setSnmpAdaptorName(snmpObjName);
  
    // Create and initialize the SNMP proxy.
    //
    remoteMibObjName = new ObjectName("snmp:class=proxy");
    SnmpMibAgentImpl <userinput moreinfo="none" lang="en">remoteMib = new SnmpMibAgentImpl()</userinput>;
    <userinput moreinfo="none" lang="en">server.registerMBean(remoteMib, remoteMibObjName)</userinput>;
    <userinput moreinfo="none" lang="en">remoteMib.initializeProxy(host, Integer.parseInt(port), "1.3.6.1.2.1")</userinput>;

    // Bind the MIB proxy to the SNMP adaptor
    //
    <userinput moreinfo="none" lang="en">((SnmpMibAgent)remoteMib).setSnmpAdaptorName(snmpObjName)</userinput>;
  
} 
catch (Exception e) {
    e.printStackTrace();
    java.lang.System.exit(1);
}</programlisting></example><para lang="en">We register the proxy object as any other MBean and then initialize
it. We call the <classname lang="en">initialize</classname> method of the proxy, giving
the host and port of the sub-agent it must communicate with and the root OID
of the MIB or subset that it represents.</para><para lang="en">A single proxy object can serve several MIBs on a sub-agent, and in
this case, the root OID is the common prefix of all objects. However, the
OIDs of all proxies and MIBs in an agent must be distinct, none may be a substring
of another (see <link linkend="snmpadaptor-17" lang="en">"Binding the MIB MBeans"</link>). Finally, we bind the proxy
to the SNMP adaptor just as we would a MIB MBean.</para></sect2><sect2 id="snmpproxy-13" lang="en"><title lang="en">The Sub-Agent</title><para lang="en">The sub-agent in our example is a stand-alone agent which serves a subset
of the <literal moreinfo="none" lang="en">RFC1213</literal> MIB. Since it implements no proxies of its
own, it is just a plain agent which responds to SNMP management requests that
happen to originate from a proxy object. Any SNMP manager could also send
requests to this agent.</para><para lang="en">Stand-alone agents are covered in <link linkend="snmpadaptor-4" lang="en">"Stand-Alone SNMP Agents"</link>. Since
this stand-alone agent contains no code that is specific to its role as a
sub-agent, we will not repeat its program listing here. The <filename moreinfo="none" lang="en">StandAloneAgent.java</filename> file only contains some extra code for reading its assigned port
from the command line. We will use this to launch the agent on a known port
to which the proxy can connect.</para></sect2><sect2 id="snmpproxy-18" lang="en"><title lang="en">The Manager Application</title><para lang="en">The manager application is not affected by proxies in the agents to
which it sends requests. It sends the requests to the master agent, in the
same way that it would send a request for a MIB that is not served by a proxy.
In fact, the SNMP manager cannot even distinguish between a MIB served by
an agent and another MIB served through a proxy in the agent, except perhaps
by analyzing the values returned.</para><para lang="en">There is one consideration for proxies, and that is the timeout interval.
Since the proxy issues another request and only answers at the end of its
timeout, the manager must have a longer timeout. The manager should be designed
with some knowledge of all sub-agents in a hierarchy, so that the timeout
can take into account all proxy delays and the multiple communication times.</para><para lang="en">As with any manager application written with the SNMP manager API, the
manager in the proxy example must have access to the OID table objects that
represent the MIBs that it will manage. Here is the code to initialize the
manager:</para><example role="code" id="snmpproxy-ex-19" lang="en"><gentext type="text">Example 9-2 </gentext><title lang="en">Initialization of the SNMP Proxy Manager</title><programlisting format="linespecific" lang="en" role="complete">String host = argv[0];
String port = argv[1];

// Initialize the SNMP Manager API.
// Specify the OidTables containing all the MIB Demo and MIB II knowledge.
// Use the OidTables generated by mibgen when compiling MIB Demo and MIB II.
//
SnmpOidDatabaseSupport oidDB = new SnmpOidDatabaseSupport();
SnmpOid.setSnmpOidTable(oidDB);
oidDB.add(new RFC1213_MIBOidTable());
oidDB.add(new DEMO_MIBOidTable());
  
SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
SnmpParameters params = new SnmpParameters("public", "private");
SnmpSession session = new SnmpSession("Manager session");
  
// We update the time out to let the agent enough time 
// to do his job before retry.
//
agent.setTimeout(100000);
agent.setSnmpParam(params);
session.setDefaultPeer(agent);</programlisting></example><para lang="en">The rest of the manager application is the code for synchronous <literal moreinfo="none" lang="en">get</literal> and <literal moreinfo="none" lang="en">set</literal> requests, similar to the one shown
in <link linkend="snmpmanager-2" lang="en">"The Synchronous Manager Example"</link>.</para></sect2></sect1><sect1 id="snmpproxy-9" lang="en"><title lang="en">The SNMP Proxy Implementation</title><para lang="en">The SNMP proxy is an extension of the abstract <classname lang="en">SnmpMibAgent</classname> which implements all of its abstract methods and can be instantiated.
The proxy implements a synchronous SNMP manager that forwards the requests
to the sub-agent. It does some error fixing for SNMPv2 requests but doesn't
claim to be extensive. In any case, the SNMP proxy implementation is provided
as an example and Sun Microsystems makes no claim as to its suitability for
any particular usage.</para><note lang="en" role="note"><gentext type="text">Note - </gentext><para lang="en">The implementation of the example proxy does not support the <literal moreinfo="none" lang="en">getBulk</literal> request or the <classname lang="en">check</classname> method that
allows the SNMP <literal moreinfo="none" lang="en">Walk</literal> request.</para></note><para lang="en">Before it is used, the proxy must be initialized with the hostname and
port of the sub-agent. It uses this information to create SNMP parameter and
SNMP peer objects. It then creates three SNMP sessions:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en">One for SNMPv1 requests</para></listitem><listitem lang="en"><para lang="en">One for SNMPv2 requests</para></listitem><listitem lang="en"><para lang="en">One for failed SNMPv2 requests that are retried
as v1 requests</para></listitem></itemizedlist><example role="code" id="snmpproxy-ex-14" lang="en"><gentext type="text">Example 9-3 </gentext><title lang="en">Internal Initialization of the SNMP Proxy</title><programlisting format="linespecific" lang="en" role="complete">public void initializeProxy(String h, int p,
                            String strOid, String name)
    throws UnknownHostException, SnmpStatusException {
	
    host = h;
    port = p;
    oid = strOid;
    mibName = name;
	
    // Initialization for SNMP v1 protocol.
    //
    SnmpParameters paramsV1 = new SnmpParameters("public", "private");
    paramsV1.setProtocolVersion(SnmpDefinitions.snmpVersionOne);
    SnmpPeer peerV1 = new SnmpPeer(host, port);
    peerV1.setSnmpParam(paramsV1);
    sessionV1 = new SnmpSession("SnmpMibAgentImpl session V1");
    sessionV1.setDefaultPeer(peerV1);

    // Using SNMP v1 protocol, errors are not fixed and
    // forwarded to the manager
    sessionV1.snmpOptions.setPduFixedOnError(false);

    // Initialization for SNMP v2 protocol.
    //
    SnmpParameters paramsV2 = new SnmpParameters("public", "private");
    paramsV2.setProtocolVersion(SnmpDefinitions.snmpVersionTwo);
    SnmpPeer peerV2 = new SnmpPeer(host, port);
    peerV2.setSnmpParam(paramsV2);
    // If we get an error, we don't retry the request using SNMP v2,
    // but we try the request using SNMP v1
    peerV2.setMaxRetries(0);
    sessionV2 = new SnmpSession("SnmpMibAgentImpl session V2");
    sessionV2.setDefaultPeer(peerV2);
    // Using SNMP v2 protocol, the error is fixed
    //
    sessionV2.snmpOptions.setPduFixedOnError(true);

    // Initialization for SNMP v2 protocol simulated
    // using SNMP v1 protocol
    //
    sessionV2WithV1 = new SnmpSession("SnmpMibAgentImpl session V2 with V1");
    sessionV2WithV1.setDefaultPeer(peerV1);
    // Simulating SNMP v2 with SNMP v1 protocol, the error is fixed.
    //
    sessionV2WithV1.snmpOptions.setPduFixedOnError(true);
}</programlisting></example><para lang="en">The proxy exposes the public methods for handling requests, and then
this algorithm for reducing errors is implemented by internal
methods. Roughly, the proxy must determine the version of the incoming request
and handle it as promised. Version 1 requests that timeout or fail are dropped,
and v2 requests that timeout or fail are retried as v1 requests. Here we only
show the code for implementing the <classname lang="en">get</classname> method.</para><example role="code" id="snmpproxy-ex-15" lang="en"><gentext type="text">Example 9-4 </gentext><title lang="en">Implementing a <literal moreinfo="none" lang="en">get</literal>
Request in the Proxy</title><programlisting format="linespecific" lang="en" role="complete">// the exposed method
public void get(Vector list, int version) throws SnmpStatusException {
			  	  	  
    java.lang.System.out.println(
        "Proxy: Sending get request to SNMP sub-agent on " + 
            host + " using port " + port);

    // Request using SNMP v1 protocol
    if (version == SnmpDefinitions.snmpVersionOne) {
        get(list, version, sessionV1);
    }

    // Request using SNMP v2 protocol.
    if (version == SnmpDefinitions.snmpVersionTwo) {
        get(list, version, sessionV2);
    }
}

// the internal implementation
private void get(Vector list, int version, SnmpSession session)
    throws SnmpStatusException {
			  	  	  
    SnmpVarbindList varbindlist = setVarBindList(list);

    try {
        request = session.snmpGet(null, varbindlist);
    } 
    catch (SnmpStatusException e) {
        throw new SnmpStatusException(SnmpDefinitions.snmpRspGenErr, 0);
    }
    java.lang.System.out.println("\nRequest:\n" + request.toString());
	
    boolean completed = request.waitForCompletion(10000);
    if (completed == false) {

        // If the completion failed using SNMP v1, we give up
        if (version == SnmpDefinitions.snmpVersionOne) {
            java.lang.System.out.println(
                "\nRequest timed out: check reachability of sub-agent.");
            return;
        }

        // If the completion failed using SNMP v2, we try again using v1

        if (version == SnmpDefinitions.snmpVersionTwo) {
            get(list, SnmpDefinitions.snmpVersionOne, sessionV2WithV1);
            return;
        }
    }

    int errorStatus = request.getErrorStatus();
    int errorIndex = request.getErrorIndex() + 1;
    if (errorStatus != SnmpDefinitions.snmpRspNoError) {

        // If there is an error status using v1, we throw an exception
        if (version == SnmpDefinitions.snmpVersionOne) {
            throw new SnmpStatusException(errorStatus, errorIndex);
        }
        // If there is an error status using v2, we try again using v1
        if (version == SnmpDefinitions.snmpVersionTwo) {
            get(list, SnmpDefinitions.snmpVersionOne, sessionV2WithV1);
            return;
        }
    }
    result = request.getResponseVbList();

    // Update the list parameter with the result
    Enumeration l = list.elements();
    for (Enumeration e = result.elements(); e.hasMoreElements();) {
        SnmpVarBind varres = (SnmpVarBind) e.nextElement();
        SnmpVarBind varbind = (SnmpVarBind) l.nextElement();
        varbind.value = varres.value;
    }
}</programlisting></example><para lang="en">The complete list of methods that a proxy must implement are the same
as for MIB MBeans represented by instances of the <classname lang="en">SnmpMib</classname>
class:</para><itemizedlist lang="en" mark="bullet"><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">long[] getRootOid()</literal> - Gets the root object
identifier of the MIB</para></listitem><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">void get(java.util.Vector list, int version)</literal> - Processes a <literal moreinfo="none" lang="en">get</literal> operation.</para></listitem><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">void getNext(java.util.Vector list, int
version)</literal> - Processes a <literal moreinfo="none" lang="en">getNext</literal> operation.</para></listitem><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">void getBulk(java.util.Vector list, int
nonRepeat, int maxRepeat, int version)</literal> - Processes a <literal moreinfo="none" lang="en">getBulk</literal> operation.</para></listitem><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">void check(java.util.Vector list)</literal>
- Prepares a <literal moreinfo="none" lang="en">walk</literal> operation.</para></listitem><listitem lang="en"><para lang="en"><literal moreinfo="none" lang="en">void set(java.util.Vector list, int version)</literal> - Processes a <literal moreinfo="none" lang="en">set</literal> operation.</para></listitem></itemizedlist></sect1><sect1 id="snmpproxy-5" lang="en"><title lang="en">Running the SNMP Proxy Example</title><para lang="en">First, we generate the MBeans for our MIBs using the <command moreinfo="none" lang="en">mibgen</command> tool:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cd <replaceable lang="en">examplesDir</replaceable>/Snmp/Proxy/</userinput>
$ <userinput moreinfo="none" lang="en">mibgen -a -d . mib_II_subset.txt mib_demo.txt</userinput></screen><para lang="en">Since the proxy acts as an SNMP manager, the master agent application
now needs the SNMP manager API in its classpath, for both compilation and
execution. Similarly, the proxy object also needs the class representing the
SNMP OID table of the MIB that it accesses on the sub-agent.</para><para lang="en">These issues are resolved in our case by having all classes in the example
directory and using dot (<filename moreinfo="none" lang="en">.</filename>) in our usual classpath. Replace
the two MIB files with those from the <filename moreinfo="none" lang="en">patchfiles</filename> directory
and then compile all of the classes in the example directory:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">cp -i patchfiles/*_MIB.java .</userinput>
cp: overwrite ./DEMO_MIB.java (yes/no)? <userinput moreinfo="none" lang="en">y</userinput>
cp: overwrite ./RFC1213_MIB.java (yes/no)? <userinput moreinfo="none" lang="en">y</userinput>
$ <userinput moreinfo="none" lang="en">javac -classpath <replaceable lang="en">classpath</replaceable> -d . *.java</userinput></screen><para lang="en">We can run all three applications on the same machine as long as we
choose different port numbers. Here we give commands for launching the applications
from the same terminal window running the Korn shell. On the Windows NT platform,
you will have to launch each application in a separate window, in which case
you will not see the sequence of the merged output.</para><procedure id="snmpproxy-proc-20" lang="en"><gentext type="text"></gentext><title lang="en">Running the SNMP Proxy Example</title><step performance="required" lang="en"><para lang="en">First we launch the sub-agent; by default it will reply to port 8086,
or we can specify a different one on the command line:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> StandAloneAgent 8090 &amp;</userinput>
Adding SNMP adaptor using port 8090

Initializing the MIB RFC1213_MIB</screen></step><step performance="required" id="snmpproxy-step-21" lang="en"><para lang="en">Then we launch the master agent, giving it the sub-agent's hostname
and port number:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Agent localhost 8090 &amp;</userinput>
NOTE: HTML adaptor is bound on TCP port 8082
NOTE: SNMP Adaptor is bound on UDP port 8085

Initializing the MIB snmp:class=DEMO_MIB

Initializing the SNMP proxy snmp:class=proxy to query host localhost using
port 8090</screen></step><step performance="required" id="snmpproxy-step-22" lang="en"><para lang="en">Now you can view the master agent through its HTML adaptor. In your
web browser, go to the following URL: <ulink url="http://localhost:8082/">http://localhost:8082/</ulink></para><para lang="en">The <literal moreinfo="none" lang="en">class=proxy</literal> MBean in the <literal moreinfo="none" lang="en">snmp</literal>
domain is the proxy object, but we cannot see the MBean that it represents.
In order to manage the actual MIB, we would have to connect to the sub-agent,
but in our example it is a stand-alone and therefore unreachable.</para><para lang="en">The <literal moreinfo="none" lang="en">name=Demo</literal> MBean in the <literal moreinfo="none" lang="en">DEMO_MIB</literal>
domain is the MIB that is served locally. If we click on its name, we can
see its initial values.</para></step><step performance="required" id="snmpproxy-step-23" lang="en"><para lang="en">Finally, we launch the manager application, giving it the master agent's
hostname and port:</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">java -classpath <replaceable lang="en">classpath</replaceable> Manager localhost 8085</userinput></screen><para lang="en">The manager will run through its requests to the master agent. If the
output is in the correct order, there are messages from the manager issuing
a request, and then the proxy which relays a request for two of the variable
to the sub-agent. The proxy then prints the result it received for the two
variables, then the manager receives the final response with all of the variables,
including the same two that the proxy just forwarded.</para></step><step performance="required" id="snmpproxy-step-24" lang="en"><para lang="en">In the <literal moreinfo="none" lang="en">name=Demo</literal> MBean on the web browser, you should
see the new values that were set in the DEMO_MIB as part of the manager's
set operation.</para></step><step performance="required" id="snmpproxy-step-25" lang="en"><para lang="en">Don't forget to stop the agent applications with the following commands
(just use "Control-C" if they are in separate terminal windows):</para><screen format="linespecific" lang="en">$ <userinput moreinfo="none" lang="en">fg</userinput>
java [...] Agent localhost 8090 <userinput moreinfo="none" lang="en">&lt;Control-C&gt;</userinput>
^C$ <userinput moreinfo="none" lang="en">fg</userinput>
java [...] StandAloneAgent 8090 <userinput moreinfo="none" lang="en">&lt;Control-C&gt;</userinput>
^C$ </screen></step></procedure></sect1></chapter></book>