Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

Publish and Subscribe with CORBA Web Events


Jul00: Publish and Subscribe with CORBA Web Events

David is an infrastructure architect for The Technical Resource Connection, and can be contacted at david.houlding@ trcinc.com.


Publish and subscribe is a well- established communications paradigm that allows any number of publishers to communicate with any number of subscribers asynchronously and anonymously via an event channel. In this article, I'll examine one approach to delivering publish/subscribe capability in the context of web-enabled distributed-object systems. In doing so, I'll present an architecture and development paradigm that leverages both the high accessibility and ease-of-use of web-based information systems and the reliability, scalability, location, implementation transparency, and standards compliance of distributed objects. Using this approach, even developers without CORBA or Java skills can rapidly develop new, thin HTML/ JavaScript publish/subscribe web clients. Such clients can then be automatically and securely deployed over intranets or the Internet to users in the context of their web browsers. A demo implementation of this architecture is available electronically at http://www.trcinc.com/corbabeans/.

Figure 1 describes a typical text Chat application. Users can load a Chat client in their browsers and use it to communicate with others via text messages. The virtual Chat room is the forum in which users communicate and is implemented using a CORBA event channel. Each event that propagates through this event channel has a single argument, consisting of a message in the form of a text string. Every Chat client is both a publisher and a subscriber to events from that event channel, allowing users to broadcast messages to, and receive broadcast messages from, other users in the forum. The Chat client is implemented using HTML and JavaScript, which communicates with the CORBA servers via a generic embedded invisible applet adapter. The complete Chat client is available electronically; see "Resource Center," page 5.

Developing the Chat Application

Figure 2 is an architecture that facilitates rapid development of thin web clients that may reuse back-end CORBA services. The interface of a CORBA server and the services it provides are defined using CORBA Interface Definition Language (IDL). The CORBA Interface Repository is a standard CORBA server responsible for disseminating the IDL interfaces of CORBA servers to clients that may use this interface information to form and execute requests on the associated CORBA servers. However, before clients invoke requests on a CORBA server, they must obtain an object reference for that server.

The CORBA Naming Service is a standard CORBA server responsible for providing services whereby clients obtain object references for servers. At server startup time, CORBA servers register themselves with the CORBA Naming Service. Clients use the CORBA Naming Service to look up registered servers and obtain object references for them.

The CORBA Event Service is a standard CORBA server responsible for providing services for the creation and management of CORBA event channels that may be used by CORBA publisher/subscriber clients to communicate anonymously and asynchronously.

CORBA event channels support either untyped or typed events that propagate via either a push or pull model. With the Chat application, untyped events are propagated via the push model. (For more information regarding CORBA IDL and standard servers, see the CORBA specs at http:// www.omg.org/.) Live Software's JRun Servlet Engine (http://www.allaire .com/) provides a standards-compliant API for running Java servlets and serves to decouple servlets it runs from particular web-server implementations that have nonstandard servlet support. In the case of the architecture in Figure 2, JRun is used to host the Naming Service Gateway Servlet that provides a web interface for browsing, discovering, and reusing CORBA services.

The Naming Service Gateway Servlet is based on the CORBA Beans framework and is responsible for creating and delivering to you proxies in the form of JavaBeans that encapsulate details associated with locating a particular server and requesting a particular service from that server. These proxies can be integrated into client applications that may reuse the associated service by interacting with the proxy via its JavaBean-compliant interface. (For more information on the CORBA Beans framework, see http:// www.trcinc.com/ corbabeans/ and my articles "A CORBA Bean Framework," DDJ, November 1998, and "An Architecture for Web Services, DDJ, July 1999.)

Creating and Publishing the Chat Event Channel

System administrators are responsible for ensuring that the servers in Figure 2 are running correctly. To facilitate the Chat application, it is necessary to configure the servers to create and publish an event channel that implements the virtual Chat room for the application. To do this, the event channel must first be created in the CORBA Event Service. Once created, the event channel can be published on the Web for reuse in the development of publish/subscribe web clients. This is accomplished in two steps:

  • First, the standard IDL interface of the event channel must be loaded into the CORBA Interface Repository.
  • Second, the event channel must be registered with the CORBA Naming Service.

Developing the Chat Client GUI

You are responsible for developing and publishing the Chat client. Figure 3 shows the Chat client GUI that can be developed with any HTML editor by dragging and dropping HTML GUI components onto the development canvas, then positioning and configuring these components. The Chat client GUI contains a text field where users can enter their name. All messages created by a given user are stamped with that user's name. The text area appearing in the center of the GUI shows the dialog that has occurred in the virtual Chat room since the current user entered the room. The text field appearing at the bottom of the GUI lets users enter a new message, then send it by clicking on the Send button. Finally, the Connect/Disconnect buttons let users either reenter or leave the virtual Chat room.

Developing the Chat Client Behavior and Connectivity

Because the Chat client is capable of both talking (publishing) and listening (subscribing) to text messages (events) in the virtual Chat room (event channel), the first step in developing Chat client connectivity is to download the JavaBean proxies for both publishing and subscribing. These proxies are used to help the client interact with CORBA servers that implement the virtual Chat room. This can be done by first locating the event channel for the virtual Chat room. Using a browser, you can browse published CORBA servers by first navigating with the browser to the URL for the Naming Service Gateway Servlet. When the servlet receives a request, it contacts the CORBA Naming Service to obtain a snapshot of the elements in the root naming context.

A naming context and CORBA server object reference in the CORBA Naming Service are analogous to a directory and file in a typical file system, respectively, just as a root naming context is analogous to a root directory. The elements in the root naming context therefore may consist of subnaming contexts and server object references. The servlet formats this information into HTML and returns it via the server to the browser for display. You can then select a subnaming context in the resulting view by clicking on its associated hyperlink to show the elements contained by that subnaming context in a similar manner as already discussed for the root naming context. In this way, you can navigate through the information in the CORBA Naming Service using a familiar tree-based search paradigm well established in web information systems and implemented, for example, by the Yahoo search engine (http://www.yahoo.com/).

When you reach the naming context view showing the CORBA server of interest, you can select that server to view the services it provides. This again results in a request to the Naming Service Gateway Servlet. The servlet responds by looking up the selected CORBA server in the CORBA Naming Service. It then retrieves the interface for that server from the CORBA Interface Repository via the server itself. The servlet again formats this information into HTML and returns it via the web server to the browser for display. The resulting browser view shows the services that the server provides. For example, locating and selecting the CORBA server that implements the event channel for the virtual Chat room results in the browser view in Figure 4.

To obtain a proxy to publish events to the event channel, you select the publishToChannel() service in Figure 4 by clicking on its hyperlink. This results in a request to the servlet to generate the proxy. The servlet responds by creating a JavaBean proxy that knows both how to locate the Chat event channel and how to create and publish events to it. The servlet serializes this proxy, packages it in the form of a Java Archive (JAR) file, and downloads it via the server to the browser. The browser you are using then responds by asking where you would like to save the downloaded proxy in the local file system. You can then enter a local file location and complete the download of the proxy.

This process can be repeated to obtain a proxy to subscribe to events from the event channel, except in this case, you would select the hyperlink for the subscribeToChannel() service in Figure 4. The JAR files containing the downloaded proxies contain no Java class files. Rather, they contain only the serialized information representing the state of the JavaBean proxies created by the Naming Service Gateway Servlet. To instantiate and run these proxies at run time, a client application must include the class files that implement and support the proxies. These are the same classes used by the servlet to create the proxies and are in this case implemented in the CORBA Beans framework.

Figure 5 shows some of the key classes in the CORBA Beans framework that implement and support the subscriber proxy. The key components are the DynamicEventSubscriber interface and its implementation in the DynamicEventSubscriberImpl class that encapsulates all of the information and behavior required to locate the event channel and interact with it as a subscriber. A subscriber proxy can be instantiated by a Java applet or application using the CORBA Beans framework and the syntax:

DynamicEventSubscriber subscriber= (DynamicEventSubscriber)

Beans.instantiate(null,"corbabeans. ChatChannel_subscribe");

The corbabeans.ChatChannel_subscribe argument defines the location of the downloaded proxy. The result of executing this code is a JavaBean subscriber proxy object configured in exactly the same state it was in the Naming Service Gateway Servlet before it got serialized, packaged in a JAR, and downloaded to the browser.

Once the proxy is instantiated, it may locate the associated event channel using the ServerLocator. This abstract class is subclassed by a range of classes implementing specific methods of locating CORBA servers. For example, Figure 5 shows the NamingServiceServerLocator and FileIorServerLocator classes that know how to locate CORBA servers using the CORBA Naming Service and Interoperable Object References (IORs) stored in files, respectively. Many other classes exist in the CORBA Beans framework that may be used to locate CORBA servers via other mechanisms.

Once the subscriber proxy has located the event channel, it can register itself as a subscriber to events from that channel. To receive such events, the proxy must itself be a CORBA server and implement the CORBA standard untyped event push consumer interface. To receive requests via this interface DynamicEventSubscriberImpl subclasses _PushConsumerImplBase and implements its abstract methods that get called when a new CORBA request arrives; for example, during the delivery of a new event. When such a new event arrives, the DynamicEventSubscriberImpl proxy extracts the argument parameter that accompanies the event and creates a JavaBean DynamicEvent object (see Figure 6) that contains the extracted parameter. It then sends this JavaBean event to all local JavaBean listeners to the proxy that implement the DynamicEventListener interface in Figure 5. A framework similar to that in Figure 5 is used to implement the publisher proxy, except the DynamicEventPublisher, DynamicEventPublisherImpl, and _PushSupplierImplBase interface and classes replace the DynamicEventSubscriber, DynamicEventSubscriberImpl, and _PushConsumerImplBase interface and classes, respectively.

Because the DynamicEvent object is a JavaBean event, it subclasses the standard Java EventObject base class. The parameter that accompanies the event (for example, a text string message in the case of the Chat application) implements the abstract Parameter class. This class is subclassed by various specific types of CORBA parameters (the string and Boolean types are implemented by the StringParameter and BooleanParameter classes, for example), while the more complex struct type is implemented by the StructParameter class. Other base types and complex nested user-defined types are also supported by the CORBA Beans framework, but are not included in the interest of clarity. Each parameter class encapsulates both the data representing the value of its associated type, as well as all behavior required to convert the parameter to or from its corresponding CORBA representation.

To instantiate and run the JavaBean proxies for the publisher and subscriber, an invisible applet is embedded in the Chat client HTML page; see Listing One. While this syntax is specifically for Microsoft Internet Explorer, a similar syntax can be used for Netscape.

The applet is assigned the name RequestManager, which is later used by JavaScript to reference the applet. Furthermore, the use of the MAYSCRIPT parameter in the applet tag indicates to the browser that the applet may call JavaScript methods implemented by the client. The syntax used to embed the applet also contains various parameters with names beginning with useslibrary. These parameters inform the browser when it loads the client that the embedded applets require Version 2.4.0.0 of the CORBA Beans framework to run. If the browser either does not have this framework already installed or the current version of the installed framework is older than that required, the browser automatically (and securely) downloads and installs Version 2.4.0.0 of the digitally signed CORBA Beans framework from the same server and URL document base from which the web page containing the embedded applet originated. In a real deployment, this only occurs the first time the client is accessed, or on subsequent updates of the framework.

The application-specific behavior of the Chat client is completely defined by the JavaScript embedded in its web page. This JavaScript takes the form of a number of functions, each of which is invoked in response to a browser event. Such events may originate from the browser (for instance, when a page first loads, or from active components embedded in the browser from applets, or from the HTML GUI, for example, when a button is pressed).

The first event caught by the Chat client occurs when the page defining the Chat client first loads as specified in the BODY tag:

<BODY BGCOLOR=''#c0c0c0'' onLoad= ''initialize()''>

This event is handled by the initialize() JavaScript function (see Listing Two), which first sets the error handler for the client. This error handler is another JavaScript function implemented in the Chat client that gets called by the browser in response to any error that occurs during loading or running of the client. The next step in the initialize() function is to obtain a reference to the invisible applet adapter embedded in the Chat client. The loadPublisher() and loadSubscriber() methods on this applet are then called to load the publisher/subscriber proxies for the Chat client, respectively. These proxies are retrieved from the same server and URL document base from which the Chat client originated, and from the file name specified by the first argument of the load method.

In the case of the publisher, for example, the proxy is loaded from the file ChatChannel_publisher.ser stored in the same directory on the server as the Chat HTML JavaScript client. The second argument to the load method provides a text string reference that may later be used by JavaScript functions to retrieve a handle to the loaded proxies. Handles to loaded proxies are also returned from the load call itself and in the case of the Chat client are cached for later use by the JavaScript in the publisher/subscriber JavaScript variables. The third and fourth arguments to the loadPublisher() and loadSubscriber() methods on the applet adapter define the JavaScript callback functions that are invoked by the applet in response to publish/subscribe events. These are the JavaBean events published to listeners through the Java DynamicEventListener interface of the CORBA Beans framework in Figure 5 and Listing Three.

The first callback function is invoked when the proxy publishes a new event via the publish(DynamicEvent) method in this interface, while the second callback function is invoked when the proxy receives an instruction to disconnect from the event channel and sends an event via the disconnect(DynamicEvent) method of the interface. If no JavaScript callback is provided for a given event (as in the case of the publish event for the publisher proxy), that event is effectively ignored by the JavaScript client. This is required in the case of the Chat client because each client is both a publisher and subscriber to events, and catching the publish event from both the publisher/subscriber proxies would cause a given client to receive doubles of all messages it sends. When a proxy is loaded by the applet adapter, the applet sets up an adapter object that simply receives events via the DynamicEventListener interface and invokes the associated JavaScript callback.

The last step performed in the initialize() function is to call the JavaScript connect() function. This is the same function invoked when users click the Connect button on the Chat client HTML GUI causing an event to be generated and handled:

<INPUT TYPE=button VALUE= ''Connect''onClick=''connect()">

The JavaScript connect() function in Listing Four calls the public Java connect() methods on the DynamicEventPublisher and DynamicEventSubscriber proxy interfaces, causing each proxy to first locate the event channel via its ServerLocator and then register themselves with the event channel as a publisher/subscriber to events. Lastly, the connect() function sets the status message at the bottom of the browser to "Connected" so users know they are currently connected to the event channel and in the virtual Chat room.

Similarly, the JavaScript disconnect() function is handled in response to users clicking on the Disconnect button of the Chat client HTML GUI:

<INPUT TYPE=button VALUE=''Disconnect'' onClick=''disconnect()''>

The JavaScript disconnect() method (Listing Five) disconnects the publisher/subscriber proxies from the event channel causing them to leave the virtual Chat room, and again sets the browser status appropriately.

The last HTML GUI event handled by the JavaScript in the Chat client occurs when users click on the Send button of the HTML GUI:

<INPUT TYPE=button VALUE=''Send'' onClick=''send()''>

The JavaScript send() function called in response to this event is implemented in Listing Six. This function first gets the user's name from the text field at the top of the Chat HTML GUI. It then appends the user's text message from the text field at the bottom of the Chat HTML GUI and calls the public Java publishString() method on the DynamicEventPublisher proxy interface. This is a convenience method that simply creates a DynamicEvent with a parameter that is a StringParameter and then calls the publish(DynamicEvent) method on the proxy to send the event to the event channel, thereby broadcasting it to all subscribers (in this case other Chat clients in the virtual Chat room). Lastly the JavaScript send() function clears the message text field at the bottom of the Chat client HTML GUI in preparation for a new message to be entered.

When the subscriber proxy receives a publish event the JavaScript subscriberPublishEvent(event) callback function (Listing Seven) gets invoked as defined in the JavaScript initialize() method discussed previously. The JavaScript subscriberPublishEvent(event) function takes a single argument, which is a Java DynamicEvent object. In this function, the Chat extracts the value of the parameter associated with this event, which, in this case, is a StringParameter containing a text message from a Chat client user and appends it to the dialog text area in the center of the Chat HTML GUI.

Similarly, when the subscriber proxy gets disconnected from the event channel it invokes the JavaScript subscriberDisconnectEvent() function again as specified in the JavaScript initialize() function (Listing Eight), which displays a dialog to users of the Chat client, warning that their subscriber proxy has been disconnected from the event channel and that they are no longer able to receive messages from the associated virtual Chat room. The window status is also set appropriately to indicate this to the user. The implementation of the JavaScript publisherDisconnectEvent(event) function is similar, except a different alert message is displayed to the user.

The JavaScript handleError() function implemented in the Chat client (Listing Nine) is called by the browser when any error or exception arises; for example, if the applet adapter throws an exception, or if there is a problem loading the Chat client, and so forth. This function takes arguments for a message associated with the error, the URL of the JavaScript client from which the error originated, and the lineNumber of the JavaScript function that was being executed at the time the error occurred. This implementation simply displays an alert with supporting information to users of the Chat client to facilitate problem diagnosis. More sophisticated and user-friendly implementations of the JavaScript handleError() function are possible.

The client can be tested by uploading the HTML JavaScript web page, as well as the associated publisher/subscriber proxies it loads, to a test web server. The Chat client may then be tested by loading the Chat web page with a browser and sending some test messages. Once testing is complete, the Chat page and associated proxies can be published by uploading them to a deployment server where they are visible to users.

Running the Chat Application

Figure 7 shows the run-time architecture required to load and run the Chat application. To load the Chat client, users must navigate with their browser to the URL for the web page defining the Chat client. The browser then loads the web page and detects both the applet adapter embedded in the client and the fact that the applet requires a library (in this case the CORBA Beans framework) to be installed on the browser before it may be run. The browser then checks if the required library has already been installed; if not, it securely and automatically downloads and installs the digitally signed library. On the other hand, if the required library has already been installed on the browser, the browser checks the version of the installed library against that required to run the embedded applet. If there is a mismatch, the browser similarly downloads and installs the required version of the library.

Once the correct version of the CORBA Beans framework library required to run the applet adapter has been installed, the browser starts the applet and completes loading by generating an onLoad JavaScript event. This event causes the JavaScript initialize() function of the Chat client to be executed. Using the applet adapter, the publisher/subscriber proxies used by the client are then loaded from the same web server and URL document base as that from which the Chat client web page was loaded. The applet adapter then sets up JavaScript callbacks to handle JavaBean events originating from each of the proxies. Finally, the JavaScript connect() function of the Chat client is executed, invoking the Java connect() methods on each of the proxies to get them to first locate the event channel for the virtual Chat room using the CORBA Naming Service via the Internet Inter-ORB Protocol (IIOP) and the VisiBroker CORBA Gatekeeper, and then register themselves with the event channel. Lastly, the HTML GUI for the Chat client is activated and begins listening for GUI events. Due to browser security restrictions, JavaScript or Java applet clients may in general only communicate with servers running on the web server host from which they originated.

The VisiBroker CORBA Gatekeeper (see http://www .inprise.com/) is an implementation of a CORBA proxy responsible for redirecting IIOP client-server communications to facilitate communications between Java applet or JavaScript clients and CORBA servers that may be running on hosts other than the web-server host. Furthermore, such CORBA proxies also typically support HTTP tunneling, which may be required if firewalls that only allow HTTP traffic to propagate exist in the client-server communications path. HTTP tunneling involves the wrapping of IIOP network packets to disguise them as HTTP packets so that they may propagate through firewalls that do not recognize and block IIOP traffic.

Users may then enter a message in the message text field at the bottom of the Chat client GUI in Figure 3 and press the Send button. This generates a GUI event that is caught and handled by the JavaScript send() function of the Chat client. This function extracts the user's name from the name text field at the top of the Chat client GUI and appends to it the message from the message text field before invoking the publishString() method on the publisher proxy. This causes the proxy to create a new event that contains a single argument consisting of the message to be sent. The proxy then sends this event via IIOP and the VisiBroker CORBA Gatekeeper to the event channel for the virtual Chat room that was located previously by the proxy when it connected to the channel. The event channel accepts the event asynchronously completing the publication of the event. The event channel then forwards the event to all subscriber proxies connected to the channel. The subscriber proxies receive the event again via IIOP and the VisiBroker CORBA Gatekeeper and each respond by first creating a JavaBean event and then publishing it to all JavaBean listeners in the same client process as the proxy. This JavaBean event is in turn caught by the applet adapter that loaded the subscriber proxy, causing the JavaScript subscriberPublishEvent() callback function of the Chat client to be invoked. This function extracts the text message from the event and displays it on the dialog text area appearing in the center of the Chat client GUI in Figure 3.

Architectural and Business Benefits Analysis

Although the architecture here is based on CORBA and IIOP, it can also be applied to other types of servers and protocols (for example, Java Remote Method Invocation), or even basic well-established services such as FTP and SMTP. By integrating all such services into this architecture, these services may all be made available for reuse by developers through the same unified web interface. Furthermore, hypertext documentation supporting the correct reuse of these services may be integrated in this unified web interface. This encourages browsing, discovery, and reuse of all the services, thereby leveraging investment and facilitating rapid development of reliable new web-enabled distributed applications.

Both the development and run-time architectures in Figures 2 and 7 can be secured for development over untrusted networks using the secure HTTP and IIOP protocols. Furthermore, security-related behavior may be integrated into downloaded proxies to enable them to request secure services at run time. The publish/subscribe communications paradigm demonstrated in the Chat application requires the use of server callbacks (for example, when the event channel sends new events to subscribers). Therefore, if there are any firewalls in the run-time communications path between the clients and servers, these firewalls must be configured to allow such callbacks.

The run-time communications path between clients and servers is direct, rather than through the web server. This leads to an architecture that is not server centric, does not fail if the web server fails, and does not result in a communications bottleneck at the web server. Furthermore, client/server communications is not limited by the HTTP protocol, which was not intended for interprocess communication and does not, for example, allow server callbacks. Rather, communication is via the more efficient, strongly typed, and client- or server-driven IIOP protocol that, for example, allows server callbacks. This leads to a more powerful, efficient, and scalable architecture.

Application clients developed using this approach are inherently web enabled and enable growth as the user-base expands. The architecture in Figure 2 relies on the CORBA Naming Service to facilitate a tree-based search paradigm whereby application developers may locate and reuse web services. By straightforward extension, other search paradigms may also be implemented for this purpose. For example, a keyword or criteria-based search paradigm can be implemented in the context of this architecture using the CORBA Trader Service. This would only require development of a similar gateway servlet to the Naming Service Gateway Servlet in Figure 2. The proxies and underlying CORBA Beans framework would remain the same.

DDJ

Listing One

<APPLET CODE="corbabeans.DynamicRequestManagerApplet.class" 
    NAME="RequestManager" WIDTH="1" HEIGHT="1" MAYSCRIPT>
<PARAM NAME="useslibrary" VALUE="CORBA Beans">
<PARAM NAME="useslibrarycodebase" VALUE="CORBABeans.cab">
<PARAM NAME="useslibraryversion" VALUE="2,4,0,0">
</APPLET>

Back to Article

Listing Two

// The manager of all CORBA requests.
var manager;

// Load publish and subscribe proxies. Could load more ...
var publisher;
var subscriber;

function initialize() {
    // Set up out own error handler.
    window.onerror = handleError
    // Get handles to CORBA request manager, load proxies 
    // and connect to event channel.
    manager = document.RequestManager;
    publisher = manager.loadPublisher(
        "ChatChannel_publish.ser",
        "publisher",
        "", // Ignore my publish events.
        "publisherDisconnectEvent");
    subscriber = manager.loadSubscriber(
        "ChatChannel_subscribe.ser",
        "subscriber",
        "subscriberPublishEvent",
        "subscriberDisconnectEvent");
    connect();
}

Back to Article

Listing Three

package corbabeans;
import java.util.EventListener;
public interface DynamicEventListener extends EventListener 
{
    public void publish(DynamicEvent event);
    public void disconnect(DynamicEvent event);
}

Back to Article

Listing Four

function connect() {
   publisher.connect();
   subscriber.connect();
   window.status = 'Connected';
}

Back to Article

Listing Five

function disconnect() {
   publisher.disconnect();
   subscriber.disconnect();
   window.status = 'Disconnected';
}

Back to Article

Listing Six

function send() {
   publisher.publishString( document.ChatForm.NameTextField.value +
                            ": " +
                            document.ChatForm.InputTextField.value );
   document.ChatForm.InputTextField.value = "";
}

Back to Article

Listing Seven

function subscriberPublishEvent(event) {
   document.ChatForm.DialogTextArea.value += 
      event.getParameter().getValue() + "\n";
}

Back to Article

Listing Eight

function subscriberDisconnectEvent(event) {
    alert( "Disconnected subscriber from event channel. Please re-connect." );
    window.status = 'Disconnected';
}

Back to Article

Listing Nine

function handleError( message, url, lineNumber ) {
   alert( "An unexpected error occurred:" + message + "\n" +
          "In JavaScript client loaded from URL: " + url + "\n" +
          "In JavaScript function at line number: " + lineNumber + "\n" +
          "Please restart and try again." );
   return true;
}




Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.