JBoss.orgCommunity Documentation

Chapter 19. Errai Bus

19.1. Messaging
19.1.1. Messaging Overview
19.1.2. MessageBuilder API
19.1.3. Single-Response Conversations & Psuedo-Synchronous Messaging
19.1.4. Sender Inferred Subjects
19.1.5. Broadcasting
19.1.6. Client-to-Client Communication
19.1.7. Message Routing Information
19.2. Handling Errors
19.3. Asynchronous Message Tasks
19.4. Repeating Tasks
19.5. Remote Procedure Calls (RPC)
19.5.1. Making calls
19.5.2. Handling exceptions
19.6. Queue Sessions
19.6.1. Scopes
19.6.2. Lifecycle
19.7. Serialization
19.7.1. Serialization of external types
19.8. Wiring server side components
19.9. Bus Configuration
19.9.1. web.xml and appserver configuration
19.9.2. ErraiService.properties
19.9.3. ErraiApp.properties
19.10. Dispatchers
19.10.1. SimpleDispatcher
19.10.2. AsyncDispatcher
19.11. Servlet Implementations
19.11.1. DefaultBlockingServlet
19.11.2. TomcatCometServlet
19.11.3. JettyContinuationsServlet
19.11.4. JBossCometServlet
19.11.5. GrizzlyCometServlet
19.11.6. WeblogicAsyncServlet
19.12. Debugging Errai Applications
19.13. What is Errai Bus?
19.14. Messaging
19.14.1. Messaging Overview
19.14.2. MessageBuilder API
19.14.3. Single-Response Conversations & Psuedo-Synchronous Messaging
19.14.4. Sender Inferred Subjects
19.14.5. Broadcasting
19.14.6. Client-to-Client Communication
19.14.7. Message Routing Information
19.14.8. Messaging Overview
19.14.9. MessageBuilder API
19.14.10. Single-Response Conversations & Psuedo-Synchronous Messaging
19.14.11. Sender Inferred Subjects
19.14.12. Broadcasting
19.14.13. Message Routing Information
19.15. Handling Errors
19.16. Asynchronous Message Tasks
19.17. Repeating Tasks
19.18. Remote Procedure Calls (RPC)
19.18.1. Making calls
19.18.2. Handling exceptions
19.19. Queue Sessions
19.19.1. Scopes
19.19.2. Lifecycle
19.19.3. Lifecycle
19.19.4. Scopes
19.20. Serialization
19.20.1. Serialization of external types
19.20.2. Serialization of external types
19.21. Wiring server side components
19.22. Bus Configuration
19.22.1. web.xml and appserver configuration
19.22.2. ErraiService.properties
19.22.3. ErraiApp.properties
19.22.4. ErraiApp.properties
19.22.5. ErraiService.properties
19.22.6. web.xml and appserver configuration
19.23. Dispatchers
19.23.1. SimpleDispatcher
19.23.2. AsyncDispatcher
19.23.3. AsyncDispatcher
19.23.4. SimpleDispatcher
19.24. Servlet Implementations
19.24.1. DefaultBlockingServlet
19.24.2. TomcatCometServlet
19.24.3. JettyContinuationsServlet
19.24.4. JBossCometServlet
19.24.5. GrizzlyCometServlet
19.24.6. WeblogicAsyncServlet
19.24.7. DefaultBlockingServlet
19.24.8. GrizzlyCometServlet
19.24.9. JBossCometServlet
19.24.10. JettyContinuationsServlet
19.24.11. TomcatCometServlet
19.24.12. WeblogicAsyncServlet
19.25. Debugging Errai Applications

This section covers the core messaging concepts of the ErraiBus messaging framework.

The MessageBuilder is the heart of the messaging API in ErraiBus. It provides a fluent / builder API, that is used for constructing messages. All three major message patterns can be constructed from the MessageBuilder .

Components that want to receive messages need to implement the MessageCallback interface.

But before we dive into the details, let look at some use cases first.

Sending Messages with the Client BusIn order to send a message from a client you need to create a Message and send it through an instance of MessageBus . In this simple example we send it to the subject 'HelloWorldService'.

In the above example we build and send a message every time the button is clicked. Here's an explanation of what's going on as annotated above:

Recieving Messages on the Server Bus / Server ServicesEvery message has a sender and at least one receiver. A receiver is as it sounds--it receives the message and does something with it. Implementing a receiver (also referred to as a service) is as simple as implementing our standard MessageCallback interface, which is used pervasively across, both client and server code. Let's begin with server side component that receives messages:

He we declare an extremely simple service. The @Service annotation provides a convenient, meta-data based way of having the bus auto-discover and deploy the service.

Sending Messages with the Server BusIn the following example we extend our server side component to reply with a message when the callback method is invoked. It will create a message and address it to the subject ' HelloWorldClient ':

The above example shows a service which sends a message in response to receiving a message. Here's what's going on:

Receiving Messages on the Client Bus/ Client ServicesMessages can be received asynchronously and arbitriraily by declaring callback services within the client bus. As ErraiBus maintains an open COMET channel at all times, these messages are delivered in real time to the client as they are sent. This provides built-in push messaging for all client services.

In the above example, we declare a new client service called "BroadcastReceiver" which can now accept both local messages and remote messages from the server bus. The service will be available in the client to receive messages as long the client bus is and the service is not explicitly de-registered.

ConversationsConversations are message exchanges which are between a single client and a service. They are a fundmentally important concept in ErraiBus, since by default, a message will be broadcast to all client services listening on a particular channel.

When you create a reply with an incoming message, you ensure that the message you are sending back is received by the same client which sent the incoming message. A simple example:

Note that the only difference between the example in the previous section (2.4) and this is the use of the createConversation()}}method with {{MessageBuilder .

Every message that is sent between a local and remote (or server and client) buses contain session routing information. This information is used by the bus to determine what outbound queues to use to deliver the message to, so they will reach their intended recipients. It is possible to manually specify this information to indicate to the bus, where you want a specific message to go.

The utility class org.jboss.errai.bus.server.util.ServerBusUtils contains a utility method for extracting the String-based SessionID which is used to identify the message queue associated with any particular client. You may use this method to extract the SessionID from a message so that you may use it for routing. For example:

The SessionID can then be stored in a medium, say a Map, to cross-reference specific users or whatever identifier you wish to allow one client to obtain a reference to the specific SessionID of another client. In which case, you can then provide the SessionID as a MessagePart to indicate to the bus where you want the message to go.

By providing the SessionID part in the message, the bus will see this and use it for routing the message to the relevant queue.

Now you're routing from client-to-client!

It may be tempting however, to try and include destination SessionIDs at the client level, assuming that this will make the infrastructure simpler. But this will not achieve the desired results, as the bus treats SessionIDs as transient. Meaning, the SessionID information is not ever transmitted from bus-to-bus, and therefore is only directly relevant to the proximate bus.

Asynchronous messaging necessitates the need for asynchronous error handling. Luckily, support for handling errors is built directly into the MessageBuilder API, utilizing the ErrorCallback interface. In the examples shown in previous exceptions, error-handing has been glossed over with aubiquitous usage of the noErrorHandling() method while building messaging. We chose to require the explicit use of such a method to remind developers of the fact that they are responsible for their own error handling, requiring you to explicitly make the decision to forego handling potential errors.

As a general rule, you should always handle your errors . It will lead to faster and quicker identification of problems with your applications if you have error handlers, and generally help you build more robust code.

The addition of error-handling at first may put off developers as it makes code more verbose and less-readable. This is nothing that some good practice can't fix. In fact, you may find cases where the same error-handler can appropriately be shared between multiple different calls.

A little nicer.

The error handler requires that return a boolean value. This is to indicate whether or not Errai should perform the defautl error handling actions it would normally take during a failure. You will almost always want to return true here, unless you are trying to expicitly supress some undesirably activity by Errai, such as automatic subject-termination in conversations. But this is almost never the case.

In some applications, it may be necessary or desirable to delay transmission of, or continually stream data to a remote client or group of clients (or from a client to the server). In cases like this, you can utilize the replyRepeating() , replyDelayed() , sendRepeating() and sendDelayed() methods in the MessageBuilder .

Delayed TasksSending a task with a delay is straight forward. Simply utilize the appropriate method (either replyDelayed() or sendDelayed() ).

or

A repeating task is sent using one of the MessageBuilder's repeatXXX() methods. The task will repeat indefinitely until cancelled (see next section).

The above example sends a message very 1 second with a message part called "time" , containing a formatted time string. Note the use of the withProvided() method; a provided message part is calculated at the time of transmission as opposed to when the message is constructed.

Cancelling an Asynchronous TaskA delayed or repeating task can be cancelled by calling the cancel() method of the AsyncTask instance which is returned when creating a task. Reference to the AsyncTask object can be retained and cancelled by any other thread.

ErraiBus supports a high-level RPC layer to make typical client-server RPC communication easy on top of the bus. While it is possible to use ErraiBus without ever using this API, you may find it to be a more useful and concise approach to exposing services to the clients.

Please note that this API has changed since version 1.0. RPC services provide a way of creating type-safe mechanisms to make client-to-server calls. Currently, this mechanism only support client-to-server calls, and not vice-versa.

Creating a service is straight forward. It requires the definition of a remote interface, and a service class which implements it. See the following:

The @Remote annotation tells Errai that we'd like to use this interface as a remote interface. The remote interface must be part of of the GWT client code. It cannot be part of the server-side code, since the interface will need to be referenced from both the client and server side code. That said, the implementation of a service is relatively simple to the point:

That's all there is to it. You use the same @Service annotation as described in Section 2.4. The presence of the remote interface tips Errai off as to what you want to do with the class.

The ErraiBus maintains it's own seperate session management on-top of the regular HTTP session management. While the queue sessions are tied to, and dependant on HTTP sessions for the most part (meaning they die when HTTP sessions die), they provide extra layers of session tracking to make dealing with complex applications built on Errai easier.

Serialization on the ErraiBus supports serialization within the same scope and limitations as the default GWT RPC serialization rules. In order to expose your domain objects to the bus so they can be exported across the bus, you must annotate them with the org.jboss.errai.bus.server.annotations.ExposeEntity annotation. The presence of this annotation will cause Errai's GWT compiler extensions to generate marshall/demarshall stubs for the annotated objects at compile-time.

For example:

By default, ErraiBus uses Google Guice to wire components. However, we plan on standardizing on JSR-330 Dependency Injection specification in the near future. When deploying services on the server-side, it is currently possible to obtain references to the MessageBus , RequestDispatcher , the ErraiServiceConfigurator , and ErraiService by declaring them as injection dependencies in Service classes, extension components, and session providers.

This section contains information on configuring the server-side bus.

he ErraiService.properties file contains basic configuration for the bus itself.

Example Configuration:

Dispatchers encapsulate the strategy for taking messages that need to be delivered somewhere and seeing that they are delivered to where they need to go. There are two primary implementations that are provided with Errai, depending on your needs.

Errai has several different implementations for HTTP traffic to and from the bus. We provide a universally-compatible blocking implementation that provides fully synchronous communication to/from the server-side bus. Where this introduces scalability problems, we have implemented many webserver-specific implementations that take advantage of the various proprietary APIs to provide true asynchrony.

These inlcuded implementations are packaged at: org.jboss.errai.bus.server.servlet

Errai includes a bus monitoring application, which allows you to monitor all of the message exchange activity on the bus in order to help track down any potential problems It allows you to inspect individual messages to examine their state and structure.

To utilize the bus monitor, you'll need to include the _errai-tools _ package as part of your application's dependencies. When you run your application in development mode, you will simply need to add the following JVM options to your run configuration in order to launch the monitor: -Derrai.tools.bus_monitor_attach=true


The monitor provides you a real-time perspective on what's going on inside the bus. The left side of the main screen lists the services that are currently available, and the right side is the service-explorer, which will show details about the service.

To see what's going on with a specific service, simply double-click on the service or highlight the service, then click "Monitor Service...". This will bring up the service activity monitor.


The service activity monitor will display a list of all the messages that were transmitted on the bus since the monitor became active. You do not need to actually have each specific monitor window open in order to actively monitor the bus activity. All activity on the bus is recorded.

The monitor allows you select individual messages, an view their individual parts. Clicking on a message part will bring up the object inspector, which will allow you to explore the state of any objects contained within the message, not unlike the object inspectors provided by debuggers in your favorite IDE. This can be a powerful tool for looking under the covers of your application.

ErraiBus forms the backbone of the Errai framework's approach to application design. Most importantly, it provides a straight-forward approach to a complex problem space. Providing common APIs across the client and server, developers will have no trouble working with complex messaging scenarios from building instant messaging clients, stock tickers, to monitoring instruments. There's no more messing with RPC APIs, or unweildy AJAX or COMET frameworks. We've built it all in to one, consice messaging framework. It's single-paradigm, and it's fun to work with.

This section covers the core messaging concepts of the ErraiBus messaging framework.

The MessageBuilder is the heart of the messaging API in ErraiBus. It provides a fluent / builder API, that is used for constructing messages. All three major message patterns can be constructed from the MessageBuilder .

Components that want to receive messages need to implement the MessageCallback interface.

But before we dive into the details, let look at some use cases first.

Sending Messages with the Client BusIn order to send a message from a client you need to create a Message and send it through an instance of MessageBus . In this simple example we send it to the subject 'HelloWorldService'.

In the above example we build and send a message every time the button is clicked. Here's an explanation of what's going on as annotated above:

Recieving Messages on the Server Bus / Server ServicesEvery message has a sender and at least one receiver. A receiver is as it sounds--it receives the message and does something with it. Implementing a receiver (also referred to as a service) is as simple as implementing our standard MessageCallback interface, which is used pervasively across, both client and server code. Let's begin with server side component that receives messages:

He we declare an extremely simple service. The @Service annotation provides a convenient, meta-data based way of having the bus auto-discover and deploy the service.

Sending Messages with the Server BusIn the following example we extend our server side component to reply with a message when the callback method is invoked. It will create a message and address it to the subject ' HelloWorldClient ':

The above example shows a service which sends a message in response to receiving a message. Here's what's going on:

Receiving Messages on the Client Bus/ Client ServicesMessages can be received asynchronously and arbitriraily by declaring callback services within the client bus. As ErraiBus maintains an open COMET channel at all times, these messages are delivered in real time to the client as they are sent. This provides built-in push messaging for all client services.

In the above example, we declare a new client service called "BroadcastReceiver" which can now accept both local messages and remote messages from the server bus. The service will be available in the client to receive messages as long the client bus is and the service is not explicitly de-registered.

ConversationsConversations are message exchanges which are between a single client and a service. They are a fundmentally important concept in ErraiBus, since by default, a message will be broadcast to all client services listening on a particular channel.

When you create a reply with an incoming message, you ensure that the message you are sending back is received by the same client which sent the incoming message. A simple example:

Note that the only difference between the example in the previous section (2.4) and this is the use of the createConversation()}}method with {{MessageBuilder .

Every message that is sent between a local and remote (or server and client) buses contain session routing information. This information is used by the bus to determine what outbound queues to use to deliver the message to, so they will reach their intended recipients. It is possible to manually specify this information to indicate to the bus, where you want a specific message to go.

The utility class org.jboss.errai.bus.server.util.ServerBusUtils contains a utility method for extracting the String-based SessionID which is used to identify the message queue associated with any particular client. You may use this method to extract the SessionID from a message so that you may use it for routing. For example:

The SessionID can then be stored in a medium, say a Map, to cross-reference specific users or whatever identifier you wish to allow one client to obtain a reference to the specific SessionID of another client. In which case, you can then provide the SessionID as a MessagePart to indicate to the bus where you want the message to go.

By providing the SessionID part in the message, the bus will see this and use it for routing the message to the relevant queue.

Now you're routing from client-to-client!

It may be tempting however, to try and include destination SessionIDs at the client level, assuming that this will make the infrastructure simpler. But this will not achieve the desired results, as the bus treats SessionIDs as transient. Meaning, the SessionID information is not ever transmitted from bus-to-bus, and therefore is only directly relevant to the proximate bus.

The MessageBuilder is the heart of the messaging API in ErraiBus. It provides a fluent / builder API, that is used for constructing messages. All three major message patterns can be constructed from the MessageBuilder .

Components that want to receive messages need to implement the MessageCallback interface.

But before we dive into the details, let look at some use cases first.

Sending Messages with the Client BusIn order to send a message from a client you need to create a Message and send it through an instance of MessageBus . In this simple example we send it to the subject 'HelloWorldService'.

In the above example we build and send a message every time the button is clicked. Here's an explanation of what's going on as annotated above:

Recieving Messages on the Server Bus / Server ServicesEvery message has a sender and at least one receiver. A receiver is as it sounds--it receives the message and does something with it. Implementing a receiver (also referred to as a service) is as simple as implementing our standard MessageCallback interface, which is used pervasively across, both client and server code. Let's begin with server side component that receives messages:

He we declare an extremely simple service. The @Service annotation provides a convenient, meta-data based way of having the bus auto-discover and deploy the service.

Sending Messages with the Server BusIn the following example we extend our server side component to reply with a message when the callback method is invoked. It will create a message and address it to the subject ' HelloWorldClient ':

The above example shows a service which sends a message in response to receiving a message. Here's what's going on:

Receiving Messages on the Client Bus/ Client ServicesMessages can be received asynchronously and arbitriraily by declaring callback services within the client bus. As ErraiBus maintains an open COMET channel at all times, these messages are delivered in real time to the client as they are sent. This provides built-in push messaging for all client services.

In the above example, we declare a new client service called "BroadcastReceiver" which can now accept both local messages and remote messages from the server bus. The service will be available in the client to receive messages as long the client bus is and the service is not explicitly de-registered.

ConversationsConversations are message exchanges which are between a single client and a service. They are a fundmentally important concept in ErraiBus, since by default, a message will be broadcast to all client services listening on a particular channel.

When you create a reply with an incoming message, you ensure that the message you are sending back is received by the same client which sent the incoming message. A simple example:

Note that the only difference between the example in the previous section (2.4) and this is the use of the createConversation()}}method with {{MessageBuilder .

Broadcasting messages to all clients listening on a specific subject is quite simple and involves nothing more than forgoing use of the reply API. For instance:

If sent from the server, all clients currently connected, who are listening to the subject "MessageListener" will receive the message. It's as simple as that.

Every message that is sent between a local and remote (or server and client) buses contain session routing information. This information is used by the bus to determine what outbound queues to use to deliver the message to, so they will reach their intended recipients. It is possible to manually specify this information to indicate to the bus, where you want a specific message to go.

The utility class org.jboss.errai.bus.server.util.ServerBusUtils contains a utility method for extracting the String-based SessionID which is used to identify the message queue associated with any particular client. You may use this method to extract the SessionID from a message so that you may use it for routing. For example:

The SessionID can then be stored in a medium, say a Map, to cross-reference specific users or whatever identifier you wish to allow one client to obtain a reference to the specific SessionID of another client. In which case, you can then provide the SessionID as a MessagePart to indicate to the bus where you want the message to go.

By providing the SessionID part in the message, the bus will see this and use it for routing the message to the relevant queue.

Now you're routing from client-to-client!

It may be tempting however, to try and include destination SessionIDs at the client level, assuming that this will make the infrastructure simpler. But this will not achieve the desired results, as the bus treats SessionIDs as transient. Meaning, the SessionID information is not ever transmitted from bus-to-bus, and therefore is only directly relevant to the proximate bus.

Asynchronous messaging necessitates the need for asynchronous error handling. Luckily, support for handling errors is built directly into the MessageBuilder API, utilizing the ErrorCallback interface. In the examples shown in previous exceptions, error-handing has been glossed over with aubiquitous usage of the noErrorHandling() method while building messaging. We chose to require the explicit use of such a method to remind developers of the fact that they are responsible for their own error handling, requiring you to explicitly make the decision to forego handling potential errors.

As a general rule, you should always handle your errors . It will lead to faster and quicker identification of problems with your applications if you have error handlers, and generally help you build more robust code.

The addition of error-handling at first may put off developers as it makes code more verbose and less-readable. This is nothing that some good practice can't fix. In fact, you may find cases where the same error-handler can appropriately be shared between multiple different calls.

A little nicer.

The error handler requires that return a boolean value. This is to indicate whether or not Errai should perform the defautl error handling actions it would normally take during a failure. You will almost always want to return true here, unless you are trying to expicitly supress some undesirably activity by Errai, such as automatic subject-termination in conversations. But this is almost never the case.

In some applications, it may be necessary or desirable to delay transmission of, or continually stream data to a remote client or group of clients (or from a client to the server). In cases like this, you can utilize the replyRepeating() , replyDelayed() , sendRepeating() and sendDelayed() methods in the MessageBuilder .

Delayed TasksSending a task with a delay is straight forward. Simply utilize the appropriate method (either replyDelayed() or sendDelayed() ).

or

A repeating task is sent using one of the MessageBuilder's repeatXXX() methods. The task will repeat indefinitely until cancelled (see next section).

The above example sends a message very 1 second with a message part called "time" , containing a formatted time string. Note the use of the withProvided() method; a provided message part is calculated at the time of transmission as opposed to when the message is constructed.

Cancelling an Asynchronous TaskA delayed or repeating task can be cancelled by calling the cancel() method of the AsyncTask instance which is returned when creating a task. Reference to the AsyncTask object can be retained and cancelled by any other thread.

ErraiBus supports a high-level RPC layer to make typical client-server RPC communication easy on top of the bus. While it is possible to use ErraiBus without ever using this API, you may find it to be a more useful and concise approach to exposing services to the clients.

Please note that this API has changed since version 1.0. RPC services provide a way of creating type-safe mechanisms to make client-to-server calls. Currently, this mechanism only support client-to-server calls, and not vice-versa.

Creating a service is straight forward. It requires the definition of a remote interface, and a service class which implements it. See the following:

The @Remote annotation tells Errai that we'd like to use this interface as a remote interface. The remote interface must be part of of the GWT client code. It cannot be part of the server-side code, since the interface will need to be referenced from both the client and server side code. That said, the implementation of a service is relatively simple to the point:

That's all there is to it. You use the same @Service annotation as described in Section 2.4. The presence of the remote interface tips Errai off as to what you want to do with the class.

The ErraiBus maintains it's own seperate session management on-top of the regular HTTP session management. While the queue sessions are tied to, and dependant on HTTP sessions for the most part (meaning they die when HTTP sessions die), they provide extra layers of session tracking to make dealing with complex applications built on Errai easier.

One of the things Errai offers is the concept of session and local scopes.

Serialization on the ErraiBus supports serialization within the same scope and limitations as the default GWT RPC serialization rules. In order to expose your domain objects to the bus so they can be exported across the bus, you must annotate them with the org.jboss.errai.bus.server.annotations.ExposeEntity annotation. The presence of this annotation will cause Errai's GWT compiler extensions to generate marshall/demarshall stubs for the annotated objects at compile-time.

For example:

By default, ErraiBus uses Google Guice to wire components. However, we plan on standardizing on JSR-330 Dependency Injection specification in the near future. When deploying services on the server-side, it is currently possible to obtain references to the MessageBus , RequestDispatcher , the ErraiServiceConfigurator , and ErraiService by declaring them as injection dependencies in Service classes, extension components, and session providers.

This section contains information on configuring the server-side bus.

he ErraiService.properties file contains basic configuration for the bus itself.

Example Configuration:

he ErraiService.properties file contains basic configuration for the bus itself.

Example Configuration:

Dispatchers encapsulate the strategy for taking messages that need to be delivered somewhere and seeing that they are delivered to where they need to go. There are two primary implementations that are provided with Errai, depending on your needs.

Errai has several different implementations for HTTP traffic to and from the bus. We provide a universally-compatible blocking implementation that provides fully synchronous communication to/from the server-side bus. Where this introduces scalability problems, we have implemented many webserver-specific implementations that take advantage of the various proprietary APIs to provide true asynchrony.

These inlcuded implementations are packaged at: org.jboss.errai.bus.server.servlet

Errai includes a bus monitoring application, which allows you to monitor all of the message exchange activity on the bus in order to help track down any potential problems It allows you to inspect individual messages to examine their state and structure.

To utilize the bus monitor, you'll need to include the _errai-tools _ package as part of your application's dependencies. When you run your application in development mode, you will simply need to add the following JVM options to your run configuration in order to launch the monitor: -Derrai.tools.bus_monitor_attach=true


The monitor provides you a real-time perspective on what's going on inside the bus. The left side of the main screen lists the services that are currently available, and the right side is the service-explorer, which will show details about the service.

To see what's going on with a specific service, simply double-click on the service or highlight the service, then click "Monitor Service...". This will bring up the service activity monitor.


The service activity monitor will display a list of all the messages that were transmitted on the bus since the monitor became active. You do not need to actually have each specific monitor window open in order to actively monitor the bus activity. All activity on the bus is recorded.

The monitor allows you select individual messages, an view their individual parts. Clicking on a message part will bring up the object inspector, which will allow you to explore the state of any objects contained within the message, not unlike the object inspectors provided by debuggers in your favorite IDE. This can be a powerful tool for looking under the covers of your application.