Home > Articles > Programming > Java

This chapter is from the book

Using the JMS Publish/Subscribe Model

Now we are going to look at an example of implementing the notification functionality using the Pub/Sub model. Although this example might seem like a stretch to require the Pub/Sub model, it will give you an idea of the differences between the two and what must be done differently for each model. Actually, there might even be a need for this design over the PTP model based on performing different tasks for different subscribers, so it might not be that much of a stretch.

As stated earlier, the Pub/Sub model in JMS involves using a topic as the destination rather than a queue. For this example, we are going to have two subscribers to the topic. One will be the handler that generates an e-mail message to the user and the second will be the subscriber that is responsible for logging the event for the administrator. Using separate subscribers allows us to modify the behavior that is taken for the admin notification separately from the user notification. We could possibly add a third subscriber where a page was sent to someone's pager. The point here is that using a Pub/Sub pattern allows you to specialize the behavior for each subscriber to the topic.

Creating the JMS Administered Objects

For this example, we will add a line to the auction_jms.properties file for the new destination. The resource file should look like the one in Listing 10.9 after adding the new line for the topic. We will not remove the lines from the Queue example from the previous sections. Listing 10.9 shows what the properties file should look after adding the new line.

Listing 10.9 The auction_jms.properties File with the Line for the Topic Added

AUCTION_CONNECTION_FACTORY=com.que.ejb20book.AuctionConnectionFactory
AUCTION_NOTIFICATION_QUEUE=com.que.ejb20book.EmailQueue
AUCTION_NOTIFICATION_TOPIC=com.que.ejb20book.AuctionNotificationTopic

To create the necessary administered objects for the topic, you will need to follow similar steps that you did when you created the queue for the last example. You don't have to create a new ConnectionFactory. We will reuse the one that you have already added.

Adding Subscribers to a Topic

We will support two subscribers for this example. One is the AuctionWinnerNotificationSubscriber and the other is the AuctionWinAdminNotificationSubscriber. Both classes extend an abstract super class called AuctionExternalNotificationSubscriber. For this example, we don't have a notification for a trailing bid as we did in the last chapter, but you could easily develop one by subclassing the abstract class as the other two do here. Listing 10.10 shows the abstract super class that both subscribers will extend. The only method is the onMessage method that is required by the MessageListener interface. The subscribers must implement this method to perform the functionality that is unique to that subscriber.

Listing 10.10 Source Code for AuctionExternalNotificationSubscriber.java

/**
 * Title:    AuctionExternalNotificationSubscriber
 * Description: This class is an abstract JMS Topic subscriber.
 */
package com.que.ejb20.notification;

import javax.jms.*;
import java.io.*;
import java.util.*;
import javax.naming.*;
import com.que.ejb20.services.email.*;

abstract public class AuctionExternalNotificationSubscriber
 implements Runnable, MessageListener {
  // The reference to the JNDI Context
  private InitialContext ctx = null;
  // Private static names for the Administered JMS Objects
  private static String connectionFactoryName = null;
  private static String topicName = null;

  private TopicConnectionFactory tcf = null;
  private TopicSubscriber subscriber = null;
  private TopicConnection topicConnection = null;
  private Topic topic = null;

 /**
  * Default Constructor
  */
 public AuctionExternalNotificationSubscriber() {
  super();
  loadProperties();
 }

 /**
  * This is the method that must be implemented from the MessageListener
  * interface. This method will be called when a message has arrived at the
  * Topic and the container calls this method and passes the Message.
  */
 abstract public void onMessage( Message msg );
 /**

Listing 10.10 Continued

  * The run method is necessary because this method implements the
  * Runnable interface to keep the thread alive and waiting for messages.
  * Otherwise, this would would not stay alive and would not be able to
  * listen for messages asyncronously.
  */
 public void run() {
  while( true ) {
   synchronized( this ){
    try{
     wait();
    }catch( InterruptedException ex ){
    }
   }
  }
 }
 // Private Accessors for Connection Factory Name
 private static String getConnectionFactoryName() {
  return connectionFactoryName;
 }

 // Private mutator for the Connection factory Name
 private static void setConnectionFactoryName( String name ) {
  connectionFactoryName = name;
 }

 // Private Accessors for the Topic Name
 private static String getTopicName() {
  return topicName;
 }

 // Private mutator for Topic Name
 private static void setTopicName( String name ) {
  topicName = name;
 }

 /**
  * This method is called to set up and initialize the necessary
  * Connection and Session references.
  */
 public void init( String msgSelector ) throws JMSException, NamingException {
  try{
   // Look up the jndi factory
    ctx = new InitialContext();

   // Get a connection to the QueueConnectionFactory
   tcf = (TopicConnectionFactory)ctx.lookup( getConnectionFactoryName() );

  // Create a connection
  topicConnection = tcf.createTopicConnection();

  // Create a session that is non-transacted and is notified automatically
  TopicSession ses =
   topicConnection.createTopicSession( false, Session.AUTO_ACKNOWLEDGE );

  // Look up a destination

Listing 10.10 Continued

  topic = (Topic)ctx.lookup( getTopicName() );

  // Create the receiver with a msgSelector. The msgSelector may
  // be null. The noLocal parameter is set so that this subscriber
  // will not receive copies of its own messages
  subscriber = ses.createSubscriber( topic, msgSelector, true );

  // It's a good idea to always put a finally block so that the
  // context is closed
  }catch( NamingException ex ) {
   ex.printStackTrace();
   System.exit( -1 );
  }finally {
   try {
    // Close up the JNDI connection since we have found what we needed
    ctx.close();
   }catch ( Exception ex ) {
    ex.printStackTrace();
   }
  }

  // Inform the received that the callbacks should be sent to this instance
  subscriber.setMessageListener( this );

  // Start listening
  topicConnection.start();
  System.out.println( "Listening on topic " + topic.getTopicName() );
 }

 /**
  * This method is called to load the JMS resource properties
  */
 private void loadProperties() {
  String connectionFactoryName = null;
  String topicName = null;

  // Uses a Properties file to get the properties for the JMS objects
  Properties props = new Properties();
  try {
   props.load(getClass().getResourceAsStream( "/auction_jms.properties" ));
  }catch( IOException ex ){
   ex.printStackTrace();
  }catch( Exception ex ){
   System.out.println( "Had a problem locating auction_jms.properties");
   ex.printStackTrace();
  }

  connectionFactoryName = props.getProperty( "AUCTION_CONNECTION_FACTORY" );
  topicName = props.getProperty( "AUCTION_NOTIFICATION_TOPIC" );

  // Set the JMS Administered values for this instance
  setConnectionFactoryName( connectionFactoryName );
  setTopicName( topicName );
 }
 /**

Listing 10.10 Continued

  * For now, this method only prints out that an email is to be sent.
  * You'll see later how to do this using the JavaMail API.
  */
 private void sendEmail( NotificationEmail email ) {
  /* Delegate the actual sending of the email message to the
    horizontal email service */
  try{
   EmailService.sendEmail( email );
  }catch( EmailException ex ){
   ex.printStackTrace();
  }
 }
}

Listings 10.11 and 10.12 show the concrete subclasses that extend the abstract class in Listing 10.10. Each one is designed to perform specific business logic when a message arrives at the topic.

Listing 10.11 Source Code for AuctionWinnerNotificationSubscriber.java

/**
 * Title:    AuctionWinnerNotificationSubscriber<p>
 * Description: A Topic subscriber to handle a notification for a winner of
 *        an Auction.<p>
 */
package com.que.ejb20.notification;

import javax.jms.*;
import java.io.*;
import java.util.*;
import javax.naming.*;
import com.que.ejb20.services.email.*;

public class AuctionWinnerNotificationSubscriber
 extends AuctionExternalNotificationSubscriber {

 public AuctionWinnerNotificationSubscriber() {
 }
 /**
  * The onMessage method here generates an email through the horizontal
  * email service.
  */
 public void onMessage( Message msg ) {
  if ( msg instanceof ObjectMessage) {
   try {
    Object obj = ((ObjectMessage)msg).getObject();
    if ( obj instanceof AuctionNotification ) {
     sendEmail( (AuctionNotification)obj );
    }
   } catch( JMSException ex ) {
    ex.printStackTrace();
   }
  }

Listing 10.11 Continued

 }
 /**
  * Delegate the sending of the email to the horizontal service.
  */
 private void sendEmail( AuctionNotification msg ) {
  NotificationEmail email = new NotificationEmail();
  email.setToAddress( msg.getNotificationEmailAddress() );
  email.setBody( msg.toString() );
  email.setFromAddress( "AuctionSite" );
  email.setSubject( msg.getNotificationSubject() );
  // Delegate to the horizontal service
  EmailService.sendEmail( email );
 }
 /**
  * Main Method
  * This is the main entry point that starts the Email listening for
  * messages in the Topic.
  */
 public static void main( String args[]) {
  // Create an instance of the client
  AuctionWinnerNotificationSubscriber subscriber = null;

  try {
   subscriber = new AuctionWinnerNotificationSubscriber();
   subscriber.init( "NotificationType = 'AuctionWinner'" );
  }catch( NamingException ex ){
   ex.printStackTrace();
  }catch( JMSException ex ){
   ex.printStackTrace();
  }

  // Start the client running
  Thread newThread = new Thread( subscriber );
  newThread.start();
 }
}

Listing 10.12 Source Code for AuctionWinAdminNotificationSubscriber.java

/**
 * Title:    AuctionWinAdminNotificationSubscriber<p>
 * Description: A Topic subscriber to handle a notification for a winner of
 *        an Auction.<p>
 */
package com.que.ejb20.notification;

import javax.jms.*;
import java.io.*;
import java.util.*;
import javax.naming.*;
import com.que.ejb20.services.logging.*;

public class AuctionWinAdminNotificationSubscriber
 extends AuctionExternalNotificationSubscriber {

Listing 10.12 Continued

 /**
  * Default Constructor
  */
 public AuctionWinAdminNotificationSubscriber() {
  super();
 }

 /**
  * If the Message is an ObjectMessage and is an instance
  * of AuctionNotification, then log the message to the
  * horizontal logging service.
  */
 public void onMessage( Message msg ) {
  if ( msg instanceof ObjectMessage) {
   try {
    Object obj = ((ObjectMessage)msg).getObject();
    if ( obj instanceof AuctionNotification ) {
     try{
      String msgStr = ((AuctionNotification)obj).getNotificationSubject();
      ILogger logger = new Logger();
      logger.logMessage( new LogMessage( msgStr, LogMessage.INFO ));
      logger.close();
     }catch( LoggingException ex ){
      ex.printStackTrace();
     }
    }
   } catch( JMSException ex ) {
    ex.printStackTrace();
   }
  }
 }
 /**
  * Main Method
  * This is the main entry point that starts the Email listening for
  * messages in the Queue.
  */
 public static void main( String args[]) {
  // Create an instance of the client
  AuctionWinAdminNotificationSubscriber subscriber = null;

  try {
   subscriber = new AuctionWinAdminNotificationSubscriber();
   subscriber.init( "" );
  }catch( NamingException ex ){
   ex.printStackTrace();
  }catch( JMSException ex ){
   ex.printStackTrace();
  }

  // Start the client running
  Thread newThread = new Thread( subscriber );
  newThread.start();
 }
} 

Both of the classes in Listings 10.11 and 10.12 extend the AuctionExternalNotificationSubscriber class and override the abstract onMessage method. This is to allow for each subclass to do something special when a message arrives.

In the case of the AuctionWinnerNotificationSubscriber class in Listing 10.11, the sendMail method is called on the EmailService component that is part of the horizontal service. With Listing 10.12, the logMessage method is called on the LogService that is also a part of the horizontal services. The horizontal service class that handles logging appears in Listing 10.13. For now, the logger will only print out a message to the console. This component will be developed further in Chapter 21.

Listing 10.13 The Horizontal Component for Logging

/**
 * Title:    LogService<p>
 * Description: Horizontal Service Component for Logging<p>
*/
package com.que.ejb20.services.logging;

public class LogService {

 public static void logMessage( String msg ) {
  System.out.println( msg );
 }
} 

The logMessage method in the LogService class in Listing 10.13 is extremely basic and will be modified and further developed in Chapter 21. For now, we are just trying to provide stubs for the classes that need to use them.

Sending Messages to a Topic

To help test the Pub/Sub example, the AuctionWinnerPublisher class will be used. Just as with the Queue example from before, we are going to use a regular Java client to help us test the example. This message publisher code would normally reside in the EJB container and be triggered when one of the events occurred that needed to generate a notification, but to keep the example simple, we will just use this class for now.

Listing 10.14 shows the AuctionWinnerPublisher that will we use. The publisher will execute these general steps:

  1. Locate the necessary JMS administered objects.

  2. Create a new JMS message.

  3. Publish the message to the topic.

  4. Exit.

Listing 10.14 Source Code for AuctionWinnerPublisher.java

/**
 * Title:    AuctionWinnerPublisher<p>
 * Description: This class is used to test the AuctionNotificationTopic<p>
*/
package com.que.ejb20.notification;

import javax.jms.*;
import java.io.*;
import java.util.*;
import javax.naming.*;

/**
 * This class can be used to test sending an AuctionWinnerNotification
 * to the AuctionNotificationTopic. All of the subscribers will get a
 * copy of the JMS Message, which encapsulates an AuctionWinnerNotification
 * object with the details of the Auction win. Only one message will be sent
 * each time this class is executed.
 *
 * Usage: java AuctionWinnerPublisher
 */
public class AuctionWinnerPublisher {

  // The reference to the JNDI Context
  private InitialContext ctx = null;
  // Private static names for the Administered JMS Objects
  private static String connectionFactoryName = null;
  private static String topicName = null;
  // Private instance references
  private TopicConnectionFactory tcf = null;
  private TopicConnection topicConnection = null;
  private TopicSession ses = null;
  private Topic topic = null;

 /**
  * Default Constructor
  */
 public AuctionWinnerPublisher() {
  super();
  loadProperties();
 }

 public void publishWinnerNotification( AuctionWinnerNotification winMsg ) {
  // Local reference to a TopicPublisher
  TopicPublisher publisher = null;

  try{
   // Lookup the jndi factory
   ctx = new InitialContext();

   // Get a connection to the QueueConnectionFactory
   tcf = (TopicConnectionFactory)ctx.lookup( getConnectionFactoryName() );

   // Create a connection
   topicConnection = tcf.createTopicConnection();

Listing 10.14 Continued

   // Create a session that is non-transacted and is notified automatically
   ses =
    topicConnection.createTopicSession( false, Session.AUTO_ACKNOWLEDGE );

   // Lookup a destination
   topic = (Topic)ctx.lookup( getTopicName() );

   // Create the publisher
   publisher = ses.createPublisher( topic );

   // Wrap the AuctionWinnerNotification inside of a JMS Message
   Message msg = ses.createObjectMessage( winMsg );

   // Set the property that will be used by the message selector
   msg.setStringProperty( "NotificationType", "AuctionWinner" );

   // Publish the message
   publisher.publish( msg );

   // Close the openresources
   topicConnection.close();

  }catch( NamingException ex ) {
   ex.printStackTrace();
   System.exit( -1 );
  }catch( JMSException ex ) {
   ex.printStackTrace();
  // It's a good idea to always put a finally block to ensure the
  // context is closed
  }finally {
   try {
    // Close up the JNDI connection since we have found what we needed
    ctx.close();
   }catch ( Exception ex ) {
    ex.printStackTrace();
   }
  }
 }

 // Private Accessors for Connection Factory Name
 private static String getConnectionFactoryName() {
  return connectionFactoryName;
 }

 // Private mutator for the Connection factory Name
 private static void setConnectionFactoryName( String name ) {
  connectionFactoryName = name;
 }

 // Private Accessors for the Topic Name
 private static String getTopicName() {
  return topicName;
 }

 // Private mutator for Topic Name

Listing 10.14 Continued

 private static void setTopicName( String name ) {
  topicName = name;
 }

 /**
  * This method is called to load the JMS resource properties
  */
 private void loadProperties() {
  String connectionFactoryName = null;
  String topicName = null;

  // Uses a Properties file to get the properties for the JMS objects
  Properties props = new Properties();
  try {
   props.load(getClass().getResourceAsStream( "/auction_jms.properties" ));
  }catch( IOException ex ){
   ex.printStackTrace();
  }catch( Exception ex ){
   System.out.println( "Had a problem locating auction_jms.properties");
   ex.printStackTrace();
  }

  connectionFactoryName = props.getProperty( "AUCTION_CONNECTION_FACTORY" );
  topicName = props.getProperty( "AUCTION_NOTIFICATION_TOPIC" );

  // Set the JMS Administered values for this instance
  setConnectionFactoryName( connectionFactoryName );
  setTopicName( topicName );
 }

 /**
  * Main Method. This is the entry point to test sending an
  * AuctionWinnerNotification to the Topic
  */
 public static void main( String args[]) {

  // Get the email address passed in on the command line
  if ( args.length == 0 ) {
   System.out.println( "Usage: AuctionWinnerPublisher <emailAddress>");
   System.exit( 0 );
  }

  String emailAddress = args[0];

  AuctionWinnerPublisher publisher = null;
  // Create an instance of this class
  publisher = new AuctionWinnerPublisher();
  // Load the properties from the jms bundle so that we can
  // locate the ConnectionFactory and the Topic

  // Create the Winner Notification
  AuctionWinnerNotification msg = new AuctionWinnerNotification();
  // Fill in some details for the Auction Win
  msg.setAuctionName( "Some Auction Item" );
  msg.setNotificationEmailAddress( emailAddress );

Listing 10.14 Continued

  // Obviously there is no Internationalization supported here. This is
  // just for testing purposes.
  msg.setAuctionWinPrice( "$75.00" );
  // Publish the message to the Topic
  publisher.publishWinnerNotification( msg );
 }
}

Running the Topic Example

Running the Topic example is not much different from the Queue example seen earlier in this chapter. To run the Topic example, you will need to follow these steps:

  1. Start the JMS service with the administered topic objects for this example.

  2. Run the AuctionWinnerNotificationSubscriber client.

  3. Run the AuctionWinAdminNotificationSubscriber client.

  4. Run the AuctionWinnerPublisher program and provide an e-mail address.

You will need to make sure that you have both the JNDI and JMS services up and running before you run either the subscriber or publisher programs. Both client programs need to have the JNDI and JMS JAR files included in the classpath.

To start either of the subscriber programs, just run them like any other Java program:

java com.que.ejb20.notification.AuctionWinnerNotificationSubscriber
or
java com.que.ejb20.notification.AuctionWinAdminNotificationSubscriber

The program will tell you that it's listening on the topic.

NOTE

Remember, there might be a difference between the JNDI name and the actual name for a destination. For example, the JNDI name given to the topic is com.que.ejb20book.AuctionNotificationTopic, but the actual property name you assign it when setting it up in the JMS administration properties might be different. Don't confuse the two.

To create a notification using the publisher, run the AuctionWinnerPublisher and pass in an e-mail address like the following:

java com.que.ejb20.notification.AuctionWinnerPublisher me@foo.com

The AuctionWinnerPublisher will not display any output before exiting. Both of the subscribers should print out a message on their consoles.

If you are having trouble running the example, see the "Troubleshooting" section at the end of this chapter for general JMS troubleshooting tips.

Durable Subscription

In terms of JMS, durability describes whether or not the JMS server will hold onto a JMS message if a subscriber is temporarily inactive. Message durability is different from message persistence. Durability is defined by the relationship that exists between a Topic subscriber and the JMS server. A subscriber that is set up as durable will have messages sent to it held by the server if the subscriber is temporarily distracted doing something else or its session becomes inactive for some reason. Durability can only be established for the Pub/Sub (Topic) message model.

Message persistence, on the other hand, is a relationship that is defined between a MessageProducer and the JMS server. Persistence can be established for both messaging models, as you'll see later in the "Message Persistence" section.

A cost overhead is involved with using durable subscribers. The subscriber registers the subscription with a unique identity that is retained by the JMS server. When a message arrives at the topic and one or more durable subscribers are inactive, the JMS server retains the messages for that subscription. When the subscriber becomes active again, the subscriber will receive the messages that are waiting for it. This is, of course, unless the message expires based on its time-to-live expiration date.

The Client ID

The client ID is a unique identifier that associates a JMS connection and the objects created through the connection with state that is maintained on behalf of a specific client. This is the means by which the JMS server knows how to deliver durable messages to a subscriber when it connects or becomes active.

You can define this ID in two ways:

  • Configure the TopicConnectionFactory with the client ID.

  • Set the client ID after acquiring a connection.

The JMS specification recommends that you set up a ConnectionFactory with a client-specific identifier and then the client looks up their specific ConnectionFactory when a connection is needed. Any connection obtained through this ConnectionFactory would be assigned this client ID.

The client can alternatively assign the ID after a connection is obtained from a ConnectionFactory. In this manner, a client-specific ConnectionFactory does not need to be configured.

CAUTION

Clients that use the alternative approach and use a default ConnectionFactory must remember to assign a unique client ID as soon as a connection is obtained from the factory. There is a chance that a unique ID already exists for a client. If this occurs, an exception will not be thrown and behavior is not predictable. Clients must be sure they have not already used a client ID for another connection. This is only if you are using durable subscriptions.

Although both message models use a client ID, only the Pub/Sub actually uses it. You will see a client ID for a QueueConnection, but JMS does not currently use them. Some vendors might be using client IDs for something internal to their JMS server for queues. Check with your vendor's documentation to be safe.

Creating Durable Subscribers

You can create durable topic subscribers by using one of the following two methods that exist on the TopicSession interface:

public TopicSubscriber createDurableSubscriber(Topic topic, String name)
throws JMSException
public TopicSubscriber createDurableSubscriber(Topic topic, String name,
  String messageSelector, boolean noLocal)
throws JMSException

The name argument in the two method signatures is the unique client ID. You can also specify a messageSelector, which you saw in the section "Specify a Message Selector Query String" earlier in this chapter.

You can specify whether a client receives a copy of the messages it sends. This can happen because a message that is sent to a topic is distributed to all subscribers. If an application uses the same connection to both publish and subscribe to a topic, a client can receive the messages that it sends. To prevent this from happening, the client should set the noLocal argument above to true. The noLocal default is false, and therefore a subscriber can receive a copy of the messages that it sends.

Only one session can define a subscriber for a particular durable subscription at any given time. Multiple subscribers can access this subscription, but not at the same time.

Deleting Durable Subscribers

To delete a durable subscriber, you must use the following method on the TopicSession:

public void unsubscribe(String name) throws JMSException;

The name argument is the name of the durable subscriber that was used when the durable subscriber was created. You can't delete a durable subscriber if either of the following is true:

  • A TopicSubscriber is still active on the session

  • The subscriber is in the middle of a transacted message or has not acknowledged the incoming message

Modifying Durable Subscribers

To modify an existing durable subscription, you can optionally delete the existing durable subscriber and then re-create it using a new name. You also will get a new durable subscriber if you change either the messageSelector or noLocal values in the createDurableSubscriber method.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020