Home > Articles > Programming > Java

This chapter is from the book

5.3 Value List Iterator Pattern

The client programs in the previous chapter read in all the recordings from the Music Collection database by invoking the getMusicList() method in the Music EJB. These clients use Swing components and HTML Optiontags to display the recording list on the user's display screen. This works just fine if we have a short list, say something under 25 items. What happens if our database contains 100 items, 1,000 items, or even 10,000 items?

For a client application to effectively deal with a list that big presents logistic problems. No user wants to page through a 1,000-item list. In fact, a user machine with a browser and a large list may not be able to run the application at all, due to limited resources and memory constraints. Furthermore, a user may not be interested in seeing the entire list of items, resulting in wasted resources and time required to transmit all the list items.

The Value List Iterator Pattern can help with large lists of data. This pattern allows clients to request data a page at a time. A Value List Iterator applies to many common situations. For example, it applies when a user wants to access only portions of a list, or if the list doesn't fit in memory or on the user's display. A Value List Iterator can also be handy if transmitting the entire list takes too much time or if a client application has no idea how large the list is.

There are several consequences to implementing a Value List Iterator. First of all, the client controls how much data to transmit. By breaking up a list into multiple remote calls for retrieval, the client makes more requests on the server, thereby increasing network traffic. Also, the Value List Iterator should be applied to read-only lists, since concurrent updates to mutable lists could possibly invalidate the data.

There are several strategies for implementing the Value List Iterator Pattern.

  • Stateful session bean. The bean implementation keeps track of the client's current page and desired page size. This presents a clean and simple interface to the client, because a client can request the next page, grab the previous page, or ask if there are more elements to read (either forward or in reverse). The downside is that a stateful session bean must be tied to a single client. This can be a strain on the server's resources when many clients make simultaneous requests. Most alarmingly, the server must cache multiple copies of the data.

  • Stateless session bean. The bean implementation does not keep track of client-specific data. This means more bookkeeping tasks for the client, who is now responsible for the current page, page size, and any other variables needed to effectively manage the list. However, the EJB container can reuse a stateless session bean easily, requiring far fewer copies of the data in the server's memory.

  • Stateless session bean/stateful session bean combination. This strategy uses a stateless session bean to access the data and a stateful session bean to manage the iteration of the list. A stateful session bean presents a clean interface to a client and keeps track of all the client-specific data. The stateful session bean also translates client-specific parameters into calls to the stateless bean, which holds the data. This approach provides the best features of both of the previous implementation strategies.

Design Guideline

The combination approach unburdens clients by encapsulating client-specific data into a stateful bean. Moreover, the stateless bean requires less server-side resources because the EJB container can easily switch instances among clients. This is the strategy we use to implement our Value List Iterator Pattern.

Now we are ready to show you the implementations for the MusicIterator EJB and the MusicPage EJB. Figure 5-3 shows a class diagram of the Value List Iterator Pattern participants. You can see how these EJB components interact to implement this pattern. The MusicIterator EJB is a stateful session bean that keeps track of client-specific data and provides methods to page through the recording data forward and backward. The MusicPage EJB is a stateless session bean that uses the MusicDAO to read and hold the recording data from the Music Collection database. (Note that the box labeled MusicDAO is an implementation of the MusicDAO interface.) Although the MusicPage EJB does not track any client-specific data, it provides a method to get a page of recording data for the MusicIterator EJB.

Figure 5-3 Class Diagram Showing the Participants of the Value List Iterator Pattern

Figure 5-4 is a sequence diagram showing the method invocations (messages) sent between client, MusicIterator EJB, MusicPage EJB, and the MusicDAO implementation object. This interaction diagram shows the messages required to create the data (the ArrayListof RecordingVOs) and read a page (using MusicIterator method nextpage()).

Figure 5-4 Sequence Diagram Showing the Interactions for Reading a Page of Data from the MusicDAO Object

Let's look at the MusicPage stateless session bean first.

MusicPage Stateless Session Bean

The MusicPage EJB is similar to the Music EJB from Chapter 4. Listing 5.6 shows the home interface for the MusicPage EJB. Since this is a stateless session bean, it has only one create() method.

Listing 5.6 MusicPageHome.java

// MusicPageHome.java
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface MusicPageHome extends EJBHome {

  MusicPage create() throws CreateException,
      RemoteException;
}

Listing 5.7 shows the remote interface for the MusicPage EJB, consisting of three business methods. The getSize()method returns the number of recordings in the Music Collection database and getPage()returns a single page of data. Method getTrackList() reads the track list from a specific recording.

Listing 5.7 MusicPage.java

// MusicPage.java
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
import java.util.*;

public interface MusicPage extends EJBObject {

  public ArrayList getTrackList(RecordingVO rec)
    throws NoTrackListException, RemoteException;
  public int getSize() throws RemoteException;
  public ArrayList getPage(int currentIndex,
    int pageSize) throws RemoteException;
}

The MusicPage EJB implementation class is shown in Listing 5.8. To access the Music Collection database, we use a MusicDAO object to encapsulate the vendor-dependent details. This is the same DAO pattern technique we used with the Music EJB in Chapter 4 (see "DAO Pattern Implementation" on page 121).

The getPage() method is responsible for returning a page of recordings from the recording list. To do this, the method builds an ArrayList with the page of data. As you will soon see, the MusicIterator EJB calls this method with a page index (currentIndex) and a page size (pageSize). Inside the getpage() method, we do some sanity checks on the arguments before creating the page as an ArrayList.

Since MusicPage EJB is a stateless session bean, the EJB container is free to assign the same instance to a new client as soon as the current client returns from a method call. This allows one instance to be shared among many clients.

Listing 5.8 MusicPageBean.java

// MusicPageBean.java
import java.util.*;
import javax.ejb.*;
import javax.naming.*;

public class MusicPageBean implements SessionBean {

  // Instance variable MusicDAO object
  // to access database.
  // MusicDAO object is instantiated in ejbCreate()
  // and provides implementation of the MusicDAO interface
  // for the particular database we're using.
  private MusicDAO dao;

  // Instance variable ArrayList that holds all recordings
  private ArrayList musicList;

  // Business methods

  public int getSize() { return musicList.size(); }

  public ArrayList getPage(int currentIndex, int pageSize)
  {

   // perform a sanity check on the arguments
   if (currentIndex > musicList.size())
     currentIndex = musicList.size();
   else if (currentIndex < 0)
     currentIndex = 0;
   if (pageSize <= 0) pageSize = 1;
   else if (pageSize > musicList.size())
     pageSize = musicList.size();

   // create the sublist
   ArrayList page = new ArrayList();

   // initialize an iterator to point to
   // the requested element
   ListIterator current =
     musicList.listIterator(currentIndex);

   // grab the elements for the desired page size
   for (int i = 0;
      current.hasNext() && i < pageSize; i++)
    page.add(current.next());
   return page;
  }

  public ArrayList getTrackList(RecordingVO rec)
        throws NoTrackListException {
   ArrayList trackList;
   try {
    // Encapsulate database calls in MusicDAO
    trackList = dao.dbLoadTrackList(rec);
   } catch (MusicDAOSysException ex) {
    throw new EJBException("getTrackList: " +
        ex.getMessage());
   }

   if (trackList.size() == 0) {
    throw new NoTrackListException(
        "No Track List found for RecordingID " +
        rec.getRecordID());
   }
   return trackList;
  }

  // EJB methods

  public void ejbCreate() {
   try {
     // The MusicDAOFactory class returns an
     // implementation of the MusicDAO interface
     dao = MusicDAOFactory.getDAO();
     // initialize the shared music ArrayList
     musicList = dao.dbLoadMusicList();
     System.out.println("MusicPageBean:ejbCreate():" +
       "initialized musicList from DAO object");
   }
   catch (MusicDAOSysException ex) {
     throw new EJBException(ex.getMessage());
   }
  }
  public MusicPageBean() {}
  public void ejbRemove() {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void setSessionContext(SessionContext sc) {}

} // MusicPageBean

Deployment Descriptor

Listing 5.9 contains the deployment descriptor for the MusicPage EJB. It is similar to the one for the Music EJB, shown previously in Listing 4.7 on page 105. We'll include it here, since this descriptor also includes the environment entry for specifying the DAO class. Note that MusicPageBean is stateless.

Listing 5.9 MusicPage EJB Deployment Descriptor

<ejb-jar>
  <display-name>MusicPageJAR</display-name>
  <enterprise-beans>
   <session>
     <display-name>MusicPageBean</display-name>
     <ejb-name>MusicPageBean</ejb-name>
     <home>MusicPageHome</home>
     <remote>MusicPage</remote>
     <ejb-class>MusicPageBean</ejb-class>
     <session-type>Stateless</session-type>
     <transaction-type>Bean</transaction-type>
   <env-entry>
     <env-entry-name>MusicDAOClass</env-entry-name>
     <env-entry-type>java.lang.String</env-entry-type>
     <env-entry-value>MusicDAOCloudscape</env-entry-value>
   </env-entry>

       <security-identity>
        <description></description>
        <use-caller-identity></use-caller-identity>
       </security-identity>

     <resource-ref>
       <res-ref-name>jdbc/MusicDB</res-ref-name>
       <res-type>javax.sql.DataSource</res-type>
       <res-auth>Container</res-auth>
       <res-sharing-scope>Shareable</res-sharing-scope>
     </resource-ref>
   </session>
  </enterprise-beans>
</ejb-jar>

MusicIterator Stateful Session Bean

We're now ready to examine the MusicIterator EJB. This stateful session bean keeps track of the client's page size and current position in the recording list. Here is the home interface, found in Listing 5.10. Note that even though Music-Iterator EJB is a stateful session bean, its home interface contains only a single create() method.

Listing 5.10 MusicIteratorHome.java

// MusicIteratorHome.java
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface MusicIteratorHome extends EJBHome {
   MusicIterator create() throws RemoteException,
     CreateException;
}

We'll look at the remote interface for the MusicIterator EJB in a moment. Before we do, let's define a set of useful operations that apply to all Value List Iterator objects. To do this formally, we'll create an interface called Value-ListIteratorand define methods for the operations. Then we can extend our MusicIteratorremote interface from ValueListIteratorand implement the methods in the MusicIterator bean class. This has two advantages. One, it separates the general operations of the Value List Iterator Pattern from the application-specific operations we require in the MusicIterator EJB. Second, the ValueListIterator interface is reusable with other EJB applications that require paging.

Listing 5.11 contains the source code for the ValueListIterator interface. This interface defines methods common to all value lists, such as setting page size, fetching the next or previous page, and determining if there are more elements to read. Method resetIndex()resets the index for the data and method setPageSize() returns the old page size.

Listing 5.11 ValueListIterator.java

// ValueListIterator.java
import java.rmi.RemoteException;
import java.util.*;

public interface ValueListIterator {

  public static final int defaultPageSize = 10;

  public int getSize() throws RemoteException;
  public int setPageSize(int numberOfElements)
       throws RemoteException;

  public int getPageSize() throws RemoteException;
  public ArrayList previousPage() throws RemoteException;
  public ArrayList nextPage() throws RemoteException;

  public void resetIndex() throws RemoteException;
  public boolean hasMoreElements() throws RemoteException;
  public boolean hasPreviousElements() throws
       RemoteException;
} // ValueListIterator

Design Guideline

When an enterprise bean implements a reusable set of operations, isolate the reusable parts in an interface. Then, extend this "reusable" specification in the enterprise bean's remote interface. Make sure all the methods in this reusable interface have RemoteException in their throws clauses.

Listing 5.12 is the remote interface for the MusicIterator EJB. Note that this interface extends ValueListIterator (in addition to EJBObject). This means we inherit all the method definitions of ValueListIteratorimplicitly. All we have to do is add getTrackList() to complete the remote interface for our MusicIterator EJB.

Listing 5.12 MusicIterator.java

// MusicIterator.java
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.util.*;

public interface MusicIterator extends EJBObject,
   ValueListIterator {

  public ArrayList getTrackList(RecordingVO rec) throws
      NoTrackListException, RemoteException;

} // MusicIterator

The only thing left to show you is the MusicIteratorBeanimplementation class in Listing 5.13. This class contains the code for all methods in the remote interface, including those from ValueListIterator as well as the ejbCre-ate()method (corresponding to create()in the home interface). Since this is a stateful session bean, the class also includes the declaration and management of client-specific instance variables. These fields keep track of the client's request for reading the recordings from the Music Collection. Recording data can be accessed forwards or backwards with a page size that the client may customize.

As you peruse the code, note that MusicIterator EJB uses instance variable pageBeanto forward calls to the MusicPage EJB. This instance variable is initialized in ejbCreate().

Listing 5.13 MusicIteratorBean.java

// MusicIteratorBean.java
import java.rmi.RemoteException;
import javax.rmi.PortableRemoteObject;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;

public class MusicIteratorBean implements SessionBean {

  // Instance variables
  private MusicPage pageBean = null;
  private int currentIndex;
  private int previousStart;
  private int pageSize;
  private int arraySize;

  // Business methods

  public ArrayList getTrackList(RecordingVO rec)
   throws NoTrackListException {

   try {
    return pageBean.getTrackList(rec);
   } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
   }
  }

  public int getSize() { return arraySize; }

  public int setPageSize(int numberOfElements) {
   int oldSize = pageSize;
   if (numberOfElements < 1 )
    { pageSize = 1; }
   else if (numberOfElements > arraySize)
    { pageSize = arraySize; }
   else
    { pageSize = numberOfElements; }
   return oldSize;
  }

  public int getPageSize() { return pageSize; }

  public ArrayList previousPage()
  {
   // Return the previous page to the client
   // Perform some basic sanity checks
   // on the parameters

   try {
     currentIndex = previousStart - pageSize;
     if (currentIndex < 0) currentIndex = 0;
     int newIndex = currentIndex;
     previousStart = currentIndex;
     if (previousStart == 0) previousStart = -1;
     currentIndex += pageSize;
     if (currentIndex > arraySize)
       currentIndex = arraySize;
     return pageBean.getPage(newIndex, pageSize);

   } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
   }
  }

  public ArrayList nextPage()
  {
   // Return the next page to the client
   // Perform some basic sanity checks
   // on the parameters

   try {
     int newIndex = currentIndex;
     previousStart = currentIndex;
     currentIndex += pageSize;
     if (currentIndex > arraySize)
       currentIndex = arraySize;
     return pageBean.getPage(newIndex, pageSize);

   } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
   }
  }

  public void resetIndex() {
   currentIndex = 0;
   previousStart = 0;
  }

  public boolean hasMoreElements() {
    return (currentIndex < arraySize);
  }

  public boolean hasPreviousElements() {
   return (previousStart >=0);
  }

  // EJB Methods

  public MusicIteratorBean() {}

  // We must initialize all instance variables here
  public void ejbCreate() {
   try {
     Context initial = new InitialContext();
     Object objref =
       initial.lookup("java:comp/env/ejb/MusicPage");
     MusicPageHome pageHome = (MusicPageHome)
       PortableRemoteObject.narrow(objref,
       MusicPageHome.class);
     pageBean = pageHome.create();
     arraySize = pageBean.getSize();
   } catch (Exception ex) {
    throw new EJBException(ex.getMessage());
   }

   // set page size from ValueListIterator default
   pageSize = ValueListIterator.defaultPageSize;
   currentIndex = 0;
   previousStart = 0;
   System.out.println("MusicIteratorBean:ejbCreate()");
  }
  public void ejbRemove() {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void setSessionContext(SessionContext sc) {}
} // MusicIteratorBean

Deployment Descriptor

Listing 5.14 contains the deployment descriptor for MusicIterator EJB. It indicates that MusicIterator EJB is stateful, and it includes the EJB reference for MusicPage EJB.

Listing 5.14 MusicIterator EJB Deployment Descriptor

<ejb-jar>
 <display-name>MIteratorJAR</display-name>
 <enterprise-beans>

 <session>
   <display-name>MusicIteratorBean</display-name>
   <ejb-name>MusicIteratorBean</ejb-name>
   <home>MusicIteratorHome</home>
   <remote>MusicIterator</remote>
   <ejb-class>MusicIteratorBean</ejb-class>
   <session-type>Stateful</session-type>
   <transaction-type>Bean</transaction-type>

   <ejb-ref>
    <ejb-ref-name>ejb/MusicPage</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <home>MusicPageHome</home>
    <remote>MusicPage</remote>
   </ejb-ref>

   <security-identity>
    <description></description>
    <use-caller-identity></use-caller-identity>
   </security-identity>
  </session>
 </enterprise-beans>
</ejb-jar>

Invocation Patterns

Suppose that our application has more than one client active at the same time. How does the EJB container handle multiple client requests? When does the EJB container instantiate the MusicIterator EJB and when does it instantiate the MusicPage EJB?

Figure 5-5 shows three clients, each with their own instance of the stateful MusicIterator EJB. (We show this ownership with a solid arrow from the client to the MusicIterator EJB instance.) The EJB container will instantiate the stateless MusicPage EJB as needed. Depending on the timing of the requests, it is possible that a single instance could service all requests. How significant is this?

Figure 5-5 Stateful vs. Stateless Session Bean Objects

In the MusicIterator EJB implementation code, the following statement makes a request to instantiate the MusicPage EJB.

pageBean = pageHome.create(); 

Recall that when a client invokes the MusicPage EJB's create() method, the EJB container maps this call to ejbCreate()unless an instance of the bean already exists. If an instance exists, the container does not execute the ejbCreate() method. This is significant in our example, since the MusicPage EJB uses the MusicDAO to read in the entire database! Fortunately, we can avoid this expensive step because the EJB container is free to assign the same instance of the MusicPage EJB to another client request. We show this "dynamic-assignable" relationship in Figure 5-5 with a dotted arrow from each MusicIterator EJB to the single instance of the MusicPage EJB.

Because the MusicPage EJB is a stateless bean, we can economize on the expensive resources of reading the database and caching the data. The EJB container must assign one stateful MusicIterator EJB to each client for the duration of the session. However, since the MusicIterator EJB does not consume a lot of resources, we don't pay a high price to make it stateful.

Design Guideline

The MusicPage EJB uses the MusicDAO to read the entire Music Collection database into memory. In the real world, a large database may not make this approach feasible. In this situation, the MusicDAO could implement its own caching scheme to deliver data for paging. Note that this capability does not affect the overall architecture of our application, since the details would be confined to the MusicPage EJB and its helper class, MusicDAO.

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