Chapter 9. The AdminControl Object

“...We will control the horizontal. We will control the vertical...”

—The Outer Limits

The AdminControl object is similar in many ways to the AdminConfig object. The primary difference between the two is that the AdminConfig object is used to manipulate configuration objects (represented using XML), and the AdminControl object is used to manipulate active application server objects (by interacting with managed beans—MBeans). One implication of this is that in order for this object to function, wsadmin must be connected to an active application server (the AdminControl object is not available when wsadmin is used in local mode).

Environment Information and Manipulation

As in previous chapters, the AdminControl methods here are grouped together to help you understand their respective roles. The first of these groups contains methods used to obtain information about the current environment. This group contains the following methods, each of which returns a string containing the specified value from the server to which wsadmin is connected:

getCell()—Returns the cell name.

getHost()—Returns the host name.

getNode()—Returns the node name.

getPort()—Returns the port number.

getType()—Returns the type of connection being used.

A script that demonstrates the use of these AdminControl methods, named envInfo.py is available from the IBM Press® website for this book at http://www.ibmpressbooks.com/bookstore/product.asp?isbn=9780137009527.

The other methods in this group are as follows:

reconnect()—Attempts to reconnect to the server.

startServer()—Starts the specified server.

stopServer()—Stops the specified server.

The last two methods in this group can only be used in a federated, or clustered, cell. Why is that? Well, unlike the startServer command script (for example, *.bat or *.sh), the AdminControl.startServer() method can only start a managed AppServer when wsadmin is connected to the Deployment Manager, and the managing node agent is also active. The AdminControl.stopServer() method, on the other hand is available, and can be used when wsadmin is connected to an unmanaged server. However, calling the AdminControl.stopServer() method when connected to an unmanaged node does not stop wsadmin, just the server. What happens about subsequent wsadmin requests? As shown in Listing 9.1, an exception is raised (lines 8–10).

Listing 9.1 Stopping and Reconnecting to an Unmanaged Server

image

When and why might you need to reconnect to the server? Consider the situation where you have a long running administrative script, and the server being administered becomes unavailable for one reason or another. AdminControl.reconnect() could be used to reestablish a connection to the server after it becomes available. Listing 9.1 shows how the unmanaged server is stopped using the AdminControl.stopServer() method. What you don’t see is how, in a different command prompt, the startServer command script was used to restart the server before we were able to successfully reconnect to it (lines 11–13).

Anyway, let’s get back to the stopServer() and startServer() methods. What can we do with these? One thing that came to mind while working with these methods was the usefulness of creating the following scripts, which are also available from the book’s website:

StartServers.py—Starts all managed application servers.

StopServer.py—Stops all managed application servers.

The logic of each of these scripts is really straightforward. Each of them performs the following steps to start or stop all managed application servers:

1. Verify that the script was executed and not imported.

2. Verify that no user specified parameters were provided on the command line.

3. Verify that wsadmin has a connection to an active Deployment Manager (such as Network Deployment (ND) server).

4. Get the list of config IDs for all of the defined nodes.

5. For each non-ND node, obtain the list of application servers.

6. For each non-node agent, use the appropriate AdminControl method to either start or stop the server.

While writing and testing these scripts, we found that a little additional effort would allow these capabilities to be combined into a single script. This combined version, StartOrStop.py, which is also available from the book’s website, can be used to start of stop all managed servers in a cell.

With a little more testing, we figured out that by adding some trivial checks we could look for an MBean for a particular application server before trying to start or stop it. If an MBean for a server exists, it doesn’t make sense to try starting the server. Conversely, if an MBean for a server doesn’t exist, it doesn’t make sense to try stopping the server. The modified script that includes these checks is SmartStartStop.py. In addition to looking for an MBean before starting or stopping a server, this code also:

• Checks for the existence of extra application servers on the Deployment Manager node.

• Locates the node agent for each node and verifies that it is active before trying to start or stop the application servers on the node.

During the testing of this script, we found that we were frequently stopping every node in the cell, including all node agents and the Deployment Manager. It didn’t take us long to realize that it would be fairly simple and more efficient to create a single script to do this for us. So the StopCell.py script was created.

One advantage of using a single wsadmin script to do this became clear once we realized how much overhead is associated with starting and initializing the Java Virtual Machine (JVM) to execute a wsadmin script. Consider for a moment an environment having a cell that has one Deployment Manager, two node agents, and two application servers. To use the command scripts provided with the product (for example, the stopServer, stopNode, and stopManager batch or script files) to stop the entire cell would require a JVM be started and initialized for each server instance. So this environment would require five JVM initializations to stop all of the servers. Using a single wsadmin script to do this means that only one JVM initialization occurs.

Are there any other advantages to having a script stop all of the servers in a cell? Sure. In fact, the one that leaps to mind is the fact that the servers are terminated gracefully in the proper order. You might not remember the appropriate order to terminate the servers in the cell. The script can do this for you, and you don’t have to worry because the servers will always be terminated in the appropriate order.

What steps are used for StopCell.py to stop all of the servers in a cell?

1. Verify that the script was executed and not imported.

2. Verify that no user-specified parameters were provided on the command line.

3. Verify that wsadmin has a connection to an active Deployment Manager.

4. For each of the configured nodes, perform this for each server in the node: If the server is a node agent, save the server name; otherwise, stop the server Stop the node agent.

5. Stop the Deployment Manager.

After writing and testing this script did we learn anything? And as important a question, did we figure out any improvements? Certainly. One improvement would be to have the script check the status of the node agent before trying to stop its managed servers. Did we write this script and make it available on the book’s website? Yes, it is named SmartCellStop.py.

After writing these scripts, it was pleasing to see how few wsadmin administrative objects and method calls were actually required to produce them. Each script needed at most the following objects and methods:

AdminConfig methods:

list()

getid()

getObjectName()

showAttribute()

AdminControl methods:

getAttribute()

getCell()

getNode()

completeObjectName()

startServer()

stopServer()

So using four AdminConfig and six AdminControl methods, some useful scripts can be developed. This is pretty neat!

Because many of these scripts are checking server status during the course of events, what is required to write a script to display the status of all application servers in the cell? The result can be found in CellStatus.py.

Before this script was available, in order to determine the status of the application servers on the machine, a batch file such as Listing 9.2 is necessary. Unfortunately, it is a very simple command script that generates multiple lines of output for each application server.

Listing 9.2 Windows Status.bat File

image

Of course, this script does have some limitations. Specifically, if the Deployment Manager is inactive, the script can’t be used to gain any useful information. Additionally, if any node agents are inactive, the state of their managed servers are indeterminate. All the servers in the cell had to be on the same physical computer for this script to work. All the profiles had to be located in the <WAS_INSTALL_ROOT>/profiles directory. All the profiles are required to have the same name as the directory in which they are stored. Finally, the script in Listing 9.2 will only work if WebSphere Application Server is installed on a Windows computer. All of these restrictions are lifted if we write a wsadmin script. In addition, as described earlier, the performance gains obtained by using a single wsadmin script, and therefore a single JVM initialization, are significant especially for multiple node configurations.

Besides generating lots of output, it can take a significant amount of time to complete, especially as the number of inactive servers increases. It appears that the serverStatus command1 that is issued by this script has some kind of maximum time that is given for a server to respond to the “status” request. When a server is not active, the serverStatus command waits the entire time before deciding that an application server “appears to be stopped.”

1 The serverStatus command is provided with WebSphere Application Server product and is documented in the online documentation (e.g., http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp).

So you have a trade-off. If your Deployment Manager and node agents are (mostly) active, the CellStatus.py script can be used. However, if you have unmanaged application servers, or if your node agents might be inactive, you may want to use something like the script shown in Listing 9.2.

Because some people don’t like voluminous output like that generated by the Status.bat in Listing 9.2, we wrote a Python script that essentially performs the same action, filters the output of the serverStatus command to condense the information into something more usable. This script is called wasStatus.py and is available with the wsadmin script files for this chapter. Please note, however, that this Python script can’t be run using wsadmin; it requires a Python interpreter to be executed. Listing 9.3 shows the output of a sample execution of this script.

Listing 9.3 Sample Output of wasStatus.py

image

MBean Support Methods

The next methods to discuss are grouped together as a matter of convenience. The first of these, is the help() method, which was covered in Chapter 7, “Introduction to Admin Objects,” in the “Help for Other Administrative Objects” section. There is no need for additional discussion here.

The next two methods (getDefaultDomain() and getDomainName()) return a string containing the domain name associated with the wsadmin environment (that is, WebSphere).

The next method, getMBeanCount(), can be used to determine the number of registered MBean types. It should not be confused with the number of MBean objects that have been instantiated. In fact, it returns the same value when connected to a Deployment Manager node with no node agents active as it did when the cell had the Deployment Manager, two node agents, and two managed application servers active.

The last method in this group, queryMBeans(), is unusual because unlike most of the Administrative object methods, which return a string to be processed, it returns a list of object instances. Unfortunately, this is not a Jython list; it is a Java list. This means that to use the result, it must be converted into something that the scripts can use. You can do this by invoking the toArray() method that exists for Java list objects. So instead of using AdminControl.queryMBeans('type=Server,*') to obtain the list of server MBeans to be used or processed by our script, remember to use something like AdminControl.queryMBeans( 'type=Server,*' ).toArray().

Unfortunately, the discussion of working with Java objects within wsadmin Jython scripts is more aligned to the topic of a WebSphere Application Server Administration using JMX than it is to Jython. Therefore, the amount of discussion concerned with the use of Java-related objects is somewhat terse and abbreviated. There will be some discussion when other methods are encountered that return or require Java objects, but not much.

Objects, Names, and Instances

The next group of AdminControl methods deals more directly with MBeans. The first method, completeObjectName(), can be used to obtain a unique MBean object name string given the supplied information. If several MBean objects match the specified information, only the first is returned. Listing 9.4 shows an interactive session where wsadmin is connected to an unmanaged application server. When the completeObjectName() method is used (line 5) to retrieve an MBean string for an object of type=Server (only one exists in this environment), the method has no difficulty identifying the unique MBean being referenced.

Listing 9.4 Use of completeObjectName()

image

image

Listing 9.4 makes use of two of the utility routines that are included in the WAuJ_utilities module. MBnameAsDict() (line 8) converts an MBean name string into a dictionary, and displayDict() (line 9) displays the contents of a dictionary in an easy to read format.

Should wsadmin be connected to a Deployment Manager with active node agents and possibly active managed application servers, the call to the completeObjectName() method will display a warning message (with message id of WASX7026W) indicating that only one of the matching MBean object name strings is being returned. Unfortunately, there is no way to tell from the result that the specified information matches multiple beans.

However, another option (method) exists that can resolve this situation. It is the queryNames() method. Passing this same search string to the queryNames() method returns all of the matching MBean string names. Again, it is best to use the splitlines() string method on this result in order to separate the lines of text into list elements. For example, this statement would look something like this:

serverBeans = AdminControl.queryNames( kind ).splitlines()

Here’s a word of caution, though. Be sure that the search string used to find MBeans ends with a “wildcard” pattern (such as 'type=Server,*'). Otherwise, the search string is interpreted as a complete MBean name string. It is much more likely that you will be supplying an incomplete MBean query and providing the wildcard asterisk to match the unspecified fields.

What other methods exist in this category, and what can they do for us? The AdminControl.getConfigId() method, for example, can be used to obtain the configuration ID for the specified MBean name string. This is the reciprocal of the AdminConfig.getObjectName() method discussed previously in Chapter 8, “The AdminConfig Object.”

This leaves us with the isRegistered() method, which returns a 1 to indicate that the specified parameter identifies a registered object. Because the parameter is supposed to be an MBean name, it is hard for me to imagine how you might have an MBean name that wouldn’t be registered.

Attribute-Related Methods

The next group or category of methods are related to retrieving (getting) and assigning (setting) the attribute values associated with MBeans. Here is the complete list of the regular methods:

getAttribute()

setAttribute()

getAttributes()

setAttributes()

Looking closely at this list of methods, you can see that there are singular and plural versions of getter and setter methods. What’s the difference? Well, the singular version will either get or set a specific (singular) attribute value, whereas the plural may be used to get or set one or more attribute values all at the same time.

Is there any particular reason for using one form over the other? The singular methods tend to be a bit simpler, for example to get a single value, such as the processType, you could really use either form. All you would have to deal with would be the syntax of specifying the attribute to be retrieved and the value being returned. Listing 9.5 demonstrates this difference as well as shows another useful utility routine, MBattrAsDict().

Listing 9.5 Example Use of getAttribute() and getAttributes()

image

Table 9.1 explains this example in detail. From this, you might be able to decide whether (or if) you prefer the singular, getAttribute(), or plural, getAttributes(), version or if you might prefer the MBattrAsDict() utility routine.

Table 9.1 getAttribute versus getAttributes Explained

image

image

Even though the help for the getAttributes() method doesn’t show it, the list of attributes to be returned is optional. Be careful though. Should you try something as simple as print AdminControl.getAttributes(server), you might be surprised by the amount of information that is returned (as a single string). When this was tested while connected to an unmanaged server, more than 20,000 characters of data were returned. If you are going to be dealing with multiple attributes, you are likely to prefer the MBattrAsDict() utility routine.

Given that the singular getAttribute() method can be used to retrieve a specific MBean attribute value, what steps are required to use this method? Fortunately, these steps are easy to describe:

1. Get the MBean for the object of interest (for example, a server).

2. Get the attribute name for this kind of object.

3. Use AdminControl.getAttribute(mbean, attributeName).

The real question at this point is, “How do you know the names of the attributes that exist for a particular MBean?” In the “Help for MBeans” section of Chapter 7 you saw how the Help.attributes() method can be used obtain this information. In addition to providing the names and data types for each attribute, the returned string shows you whether or not each attribute can be modified. Is there an easy way to get the names of modifiable attributes? Well, as often is the case, there are a number of different ways to do this—some easier to understand than others. Let’s take a look at some different approaches to the same problem.

Each of the following examples use the output of the Help.attributes() method to identify the modifiable attributes for a server MBean. The first, in Listing 9.6, uses a regular expression and list comprehension to perform this job. It could be written in fewer lines, but this would make the line too long to fit on one line in this book, and the explanation of this line would be harder to understand.

Listing 9.6 Extracting RW Attributes

image

image

The explanation of this example can be found in Table 9.2. You might consider writing lines 4–6 in one statement. Before you do, think about how much harder a single line might be to understand (and maintain). And remember how hard a single statement might be for someone to read. Who knows, you could be the person who has trouble understanding it in six months or a year.

Table 9.2 RW Attribute Extraction Explained

image

If you had any trouble understanding the example in Listing 9.6, you might prefer the example found in Listing 9.7. Granted, it might not be as efficient as the previous example, but whoever’s responsible for the administrative scripts might understand this more easily.

Listing 9.7 Verbose Extraction of Modifiable Attributes

image

The differences between the two listings are this: Lines 3–7 are used to obtain the text associated with the attributes for the specified MBean and replace all occurrences of multiple blanks with a single blank. Lines 9–13 are used to build the list of “read-write” attribute entries without using list comprehension. The two figures are extremes, though. You get to choose one technique over the other or something in the middle. In either case, remember to provide appropriate comments that explain the statements that you use and why.

Now that you’ve seen these two extremes, take another look at Listing 9.5. You might have missed it the first time through. Look how easy it was there to use the MBattrAsDict() utility routine to put all of the MBean attributes into a dictionary. Now think how easy it would be to combine this dictionary of server attributes with the list of modifiable attribute names generated to change their values. This is what setter methods are all about.

Having discussed the singular and plural getter methods for attributes, it’s easy for us to understand that only some MBean attributes are (dynamically) modifiable. What does this mean? Can’t we change everything? Well, sort of. The modifiable attributes are allowed to be changed on an active MBean. To change other attributes, you need to look at the configuration attributes that exist for this object. To change these attribute values requires a configuration change and a stop and restart of the appropriate resources (such as the application server):

image

For the example shown in Listings 9.6 and 9.7, you can see that the modifiable attributes (for an application server) are those that have threadMonitor as a prefix. The singular form of the attribute setter method is pretty straightforward. For example, say you wanted to half the threadMonitorInterval attribute for the given application server. The first thing you must realize is that the data type returned by the getAttribute() method is a string and needs to be converted to a numeric type in order to perform the desired mathematical operation.

Note that by putting the name of the attribute to be modified into a variable makes the getting and setting a bit easier to read, as well as guaranteeing that the value being modified is the same one that was retrieved. You should also note that not only is the value returned by the getter a string, but the value to be modified needs to be a string as well.

What about the plural setter method (AdminControl.setAttributes())? What does it look like?

result = AdminControl.setAttributes( bean, attributes )

What does attributes represent? Well, it can be either a string or a list of strings. If the string form is used, it should be a string representation of a list. For example, something like the following:

attributes = '[ [ name1 value1 ] [ name2 value2 ] ]'

The list of strings form would look very similar, e.g.,2

2 Note how the list form requires a comma delimiter between the list elements.

attributes = [ [ 'name1', 'value1' ], [ 'name2', 'value2' ] ]

Does it have to be used to modify a multiple values? No, you could use the singular form of the method for each attribute. Can it be used to modify a single value? Yes, it really doesn’t matter how many attributes are specified, as long as each specified attribute is modifiable. You can even specify an empty string or list of attributes to be modified that changes the same value multiple times.3 What is returned by the method? The result is a string list containing the attributes that were successfully modified by the call. Listing 9.8 shows a complete (interactive) example use of the setAttributes() method call that makes use of the MBattrAsDict() utility function described earlier. Some things to note from this example include the following:

3 If the same attribute name occurs multiple times, the last occurring value will be the one used.

1. The assignment of "ts" (the variable used to hold the TraceService configuration ID) and "attr" (the variable used to hold the attribute values to be modified) each uses two lines (lines 2–3 and 13–14, respectively) so that a long statement would not be wrapped across multiple lines.

2. The fact that the MBattrAsDict() utility function adds a 'Modifiable' entry to the returned dictionary is very convenient (lines 5–8).4

4 This attribute was specifically added because of the testing that was done related to the examples shown in Listings 9.6 and 9.7.

3. Even though the 'ringBufferSize' attribute is “modifiable” (as seen by the list of modifiable attributes on line 10), and the modification was successfully made (as seen on line 16), the modification didn’t really occur (as seen on line 18). Why? Because the current configuration of this TraceService is configured as tracingToFile and not to memory. So the request to modify the 'ringBufferSize' was ignored.

4. Note that sometimes the value passed to the setter method may be “changed” when the actual attribute modification occurs. For example, when the application server traceSpecification value was set to 'com.ibm.*=all=disabled', the actual value saved was '*=info'.5 Listing 9.8 not only demonstrates how to use the setAttributes() method call, it also shows that you might not actually be making the modifications that you think you are. Just be careful and test your scripts thoroughly. You may even consider having your script get the attribute that was just set and compare the two values.

5 Why? Because the assigned trace string didn’t match the expected and currently supported format.

Listing 9.8 Example Use of setAttributes()

image

What would a script look like that allowed all of the TraceService configuration settings to be viewed and optionally modified? The TraceService.py script does just this and is also available with from the book’s website that appears to do the trick. One of the interesting things demonstrated by this script is the fact that configuration values that exist on multiple screens on the administrative console can, in fact, be modified by single execution of an administrative script.

Miscellaneous Methods

The penultimate group of methods cover some unrelated topics. This group of methods consists of the following:

invoke()—Calls an MBean method.

testConnection()—Tests the connection to a DataSource object.

trace()—Sets the wsadmin trace specification.

Let’s discuss these in reverse order, taking AdminControl.trace() first. The first time that we encountered this, we hoped that it would allow us to easily change the traceSpecification of an active application server. Unfortunately, that was not the case. For that, you need to use one of the setAttribute methods shown earlier. This method is used to change the trace settings for the wsadmin process. So a command like the following would be used to enable verbose tracing of wsadmin.6

6 Most likely you would do this if requested to do so by IBM WebSphere Application Server technical support.

AdminControl.trace( 'com.ibm.ws.scripting.*=all=enabled' )

This trace setting corresponds to the com.ibm.ws.scripting.traceString property found in the wsadmin.properties file.

Next, you can take a look at the testConnection() method. To use this method, you need to pass it the config ID of a dataSource object, for example, something trivial such as:

ds = AdminConfig.list( 'DataSource' ).splitlines()[ 0 ]
print AdminControl.testConnection( ds )

It is important to remember, however, that should the connection attempt fail, an exception is raised. So it is a good idea to remember to use a try/except error handler around this call.

The invoke() method can be used to execute MBean object methods. Chapter 7’s “Help for MBean Operations” provided an example use of the invoke() method to obtain product version information. Let’s take another look at this method in a bit more detail. When we say that the invoke() method can be used to execute MBean object methods, what does this really mean, and more importantly, how do you find out what kind of methods exist that can be called?

Let’s start by looking at the online documentation:

1. Point your browser to the WebSphere Application Server Library page, http://www.IBM.com/software/webservers/appserv/was/library/.

2. Select the tab for the version of the product being used (such as V 6.1).

3. Select the “View page” link for the product edition being used (“Network Deployment—distributed platforms”).

4. Select the “View page” link for the product edition7 (“WebSphere Application Server Network Deployment, Version 6.1”).

7 Why there are multiple layers of this variation, we do not know.

5. In the left frame, expand the document of interest (“Network Deployment (Distributed platforms and Windows), Version 6.1”).

6. Expand the “Reference” section.

7. Select the “MBean interfaces” section.

8. Scroll the “MBean Type List” selector to the MBean of interest and click a link (such as “Server”).

9. Scroll the MBean details selector (the far right panel) to the “Operation Summary” to review the list of available methods for MBeans of this type.

We were surprised to find that one of the available methods for Server type MBeans is the restart() method. What is required to actually use this method?

You must obtain a MBname string for an object of the appropriate type and then use the AdminControl.invoke() method to call the method and pass it the necessary parameters.

Is that it? In a word, yes. So to execute the restart() method for an active application server, all you have to do is something like the following:

image

This even worked when wsadmin was connected to an unmanaged application server, which is pretty neat. So once you know how to find the MBean methods, it’s pretty simple to have your scripts call them using AdminControl.invoke().

What? You want another example? Oh, alright. Let’s take another look at the TraceService mentioned earlier. In fact, in Listing 9.8 you saw how you could modify some attributes of a TraceService. What kinds of methods exist on the TraceService MBean that you can invoke? Well, from the online documentation, if you search for TraceService, one of the entries that is found is called the “TraceService MBean Definition Table.” The list of methods that can be called can be found in the table labeled “Operation Summary.” So to call or execute one of these methods you only need to:

1. Obtain an MBean of the appropriate type.

ts = AdminControl.queryNames( 'name=TraceService,*' )

2. Use the documentation to identify the method to be executed.

3. Use the AdminControl.invoke() method to execute the MBean method.

print AdminControl.invoke( ts, 'listAllRegisteredGroups' )

Wow, this really doesn’t look that bad. It does, however, require investigation into finding out what kinds of MBean methods exist to execute, but that really isn’t too bad.

*_jmx Methods

We have seen and discussed singular and plural forms of attribute getter and setter methods. If you happened to look closely at the AdminControl.help() output, you would have seen some other methods having the same names, but with a "_jmx" suffix. What is this all about? Well, they are very similar to the methods described earlier, with the following differences:

1. Object names, and not object name strings, are used to identify the object instance to be manipulated. For example, instead of using something like this:

image

2. Scripts would need to use something along the lines of:

image

In this trivial example, it doesn’t make much sense to have the additional conversion of the object name string value to an object string, just so that a different variant of the routine can be used. However, if your script is already using object names instead of object name strings, then it might make sense to use the getAttribute_jmx() method instead.

The getAttributes_jmx() method is a bit different. Earlier, we didn’t see that the getAttributes() method has an optional second parameter that can be used to identify the attributes to be returned. For this method, the parameter is required, not optional. So instead of using something like:

image

scripts would need to use something like:

image

And another difference is what is returned. The non-*_jmx methods return a string that can be easily manipulated or parsed using Jython idioms. The *_jmx methods, on the other hand, might return Java management attribute object references, which are a bit more challenging to manipulate with Jython, but may be expected by other *_jmx methods.

An example script (setAttr_jmx.py) is provided that demonstrates the use of the setAttributes_jmx() method to modify the traceSpecification attribute of a selected TraceService MBean object instance. However, because the focus of this book is on Jython scripting and not on JMX programming using Jython, we won’t discuss these JMX-related routines further.

Summary

This chapter has been all about the AdminControl scripting object. When you, as a WebSphere Application Server administrator, need to control or manipulate active resource objects, you will probably make use of this object and its methods. We have seen that some of these methods simply allow you to query the AppServer for information about its environment. There are some really valuable AdminControl methods, however, that are specifically related to the manipulation of MBean attributes. This allows us to dynamically change some of the AppServer environment without making a persistent configuration change. It also allows us to make changes to active MBean objects, which means that the AppServer or application doesn’t have to be recycled (stopped and restarted) for these changes to take effect. This is what makes these methods really useful and worth learning.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.175.164