Home > Articles > Programming > Windows Programming

This chapter is from the book

RequestsProcessor Discussion

We finish this chapter with a discussion of the RequestsProcessor class. As we look at RequestsProcessor, we jump briefly to a helper class that it calls RequestHandlerFactory, because it has some interesting and powerful code. As mentioned earlier, the RequestsProcessor class actually kicks off the processing efforts for each Request block in an incoming transaction. If all goes well, each Request block, identified by a separate <Request> . . . </Request> element in the incoming XML message, is processed by an appropriate RequestHandler, and an EAIResponse message is generated for return.

If one or more request blocks fail, however, RequestsProcessor needs to call the rollback() method on the RequestHandler objects. Any specific RequestHandler does not need to do any real rollback processing, but all RequestHandler components do need to implement (override) the abstract RequestHandlerBase.rollback() method.

The FailOnFirstError attribute, which can decorate the <Requests> XML element in the EAIRequest message, tells the RequestsProcessor whether to continue processing when the first Request processing returns an error. If FailOnFirstError is true, the overall processing is halted at that point and the rollback() method is called on the offending request by calling its RequestHandler object. Figure 7-11 shows an activity diagram detailing the important actions that take place, mostly in the RequestsProcessor class, for a synchronously processed transaction.

Figure 7.11Figure 7-11 Synchronous processing activity diagram.


One bit of sorcery called from the RequestsProcessor class is really fun. The name of the request handler class, which is subclassed from the RequestHandlerBase class, either is explicitly defined in the database table T_RequestDefinitions or is derived from the name of the Request sent in. This -name is identified in the Name attribute of each <Request> XML element. For example, a request to catalogue the MP3 files on a machine could be called like so:

  <Request Name="Catalogue">

Alright, so this might not solve a burning industry-wide need, but I wanted to compile a list of MP3 files on my server. Sure, I could have used the Search utility that comes with Windows 2003, but what fun would that be? Being the geek that I am, I instead wrote a RequestHandler to perform this function. When the server is connected to the Internet, I can get a list of files from anywhere in the world. Now, when I'm in a dark alley in Rome, and a thug says "Gimme a list of all yer MP3 files on yer server, or else," I can fire off an EAIRequest message and display the results. (Remind me to always have my wireless device in all dark alleys from now on.)

The name of the RequestHandler for the Request named Catalogue would be CatalogueHandler, in the EAIFramework.Handler namespace. RequestsProcessor generates the name, creates an instance of the class on the fly, and finally calls the process method. This is all accomplished through classes in the System.Reflection namespace. In the next section, you will see how you can use these classes to instantiate an appropriate handler class on the fly, given just the name of the handler class.

System.Reflection Namespace

The System.Reflection namespace contains a large number of utility classes that are very handy in certain situations. They enable you to interact with and manipulate types, assemblies, and so on. Major classes in this namespace include Assembly, Module, ConstructorInfo, MemberInfo, and MethodInfo.

With the tools available in System.Reflection, you now can plug in new functionality and allow for new <Request Name="xxx"> blocks simply by writing a subclass of EAIFramework.Handler.RequestHandlerBase and making it accessible to the EAIFramework engine. The framework figures out what class should be handling the request, attempts to create an instance of it, and then calls its process method.


Before we look at the entire RequestsProcessor class, let's just pull out the code that creates the instance of the handler class (see Listing 7-7). It is fairly small and performs quite a bit of functionality in these few lines. It has been placed in a factory class named RequestHandlerFactory.cs, and it is in the EAIFramework.Handler namespace.

Listing 7-7: RequestHandlerFactory.cs

using System;
using System.Xml;
using System.Runtime.Remoting;
using System.Reflection;

using EAIFramework.Messages;
using EAIFramework.Util;

namespace EAIFramework.Handler
 /// <summary>
 /// RequestHandlerFactory class follows a factory pattern
 /// and has a single static public method: getHandler(Req).
 /// It also has a private constructor.
 /// </summary>
 public class RequestHandlerFactory
  private RequestHandlerFactory(){}

  /// <summary>
  /// The only public method in this class, getHandler, 

  /// takes in a Request object. This method then 
  /// checks to see what type of request it is, checks to
  /// see what the handler name is (from the database), 
  /// and instantiates and returns the new object.
  /// If it cannot instantiate an appropriately named 
  /// object, it will return null.
  /// </summary>
  /// <param name="rIn">Request object to be processed
  ///  </param>
  /// <returns>Subclassed object of RequestHandlerBase
  ///  </returns>
  public static 
    getHandler(Request rIn)
   // First, get the Handler name for this request
   EAIFramework.Util.DBUtilProxy tdp = new
   string strHandlerName= tdp.getHandlerName(rIn.Name);
   // *** If we don't have a specific Handler Name 

   // from the database, use the Request.Name
   if( (strHandlerName == null) ||
    strHandlerName = rIn.Name;

   // Now get an ObjectHandle for the appropriate type
   // of RequestHandler object
   ObjectHandle oh = Activator.CreateInstance(
    "EAIFramework.Handler." + 
    strHandlerName + "Handler");
    "New Handler obj = " + oh.ToString());

   // Next, get a Type object
   Type ht = oh.Unwrap().GetType();
     "New Type from Handler = " + 
    ht.Name + "-" + ht.ToString());

   // Finally, get a ConstructorInfo object for this
   // Type. With this, we will Invoke the CI and

   // get a 'ready-to-run' object that we can return
   ConstructorInfo ci = ht.GetConstructor(new Type[]{
    new EAIFramework.Messages.Request().GetType()});
    "ConstructorInfo () returned: " + 
   // Now all we have to do is invoke the 
   // ConstructorInfo object to get the handler
   EAIFramework.Handler.RequestHandlerBase rhb =
      ci.Invoke(new object[]{rIn});

//   -------------------------------------------------
//     For testing, uncomment these lines, and the
//    newly created RequestHandlerBase (or 
//    descendent class) will process the Request.
//    This method can be called from a standalone
//    test program to test a specific Handler or
//    to ensure that the above code works as expected.

//   EAIFramework.Messages.Component cBack = 
//    rhb.process();
//   Logger.log(Logger.INFO,
//    "Component returned = " + cBack.Name + ", " + 
//    cBack.Status + ", " + cBack.ToString());
//   -------------------------------------------------

   return rhb;
  }//end getHandler()
 }//end class
}//end namespace

RequestHandlerFactory has a single public, static method named getHandler(). It takes in a Request object and returns an instance of the Handler class that it determines should be handling this type of request, or it returns null. Because all handlers should be subclassed from the RequestHandlerBase class, the abstract type RequestHandlerBase is returned. All concrete subclasses have process and rollback() methods that are called by the RequestsProcessor class during actual execution.

We include the System.Runtime.Remoting namespace to get access to the ObjectHandle class, used in the getHandler() method. Also included is the System.Reflection namespace for access to the ConstructorInfo class.

As the method is entered, it instantiates the DBUtilProxy class, used to interact with some of the support tables in the database. In this case, we want to see if a specific handler name is associated with the request name. The table T_RequestDefinitions has four columns:

  • RequestName

  • Description

  • Status

  • HandlerName

You might be asking why we have this table: We have already said that you get the name of the handler from the name of the request, right? Well, you might want to have several different requests handled by the same handler. For example, in this instance, we want to provide the following kinds of requests for EAIFramework reporting and statuses:

  • ListAllRequests (used to get a list of supported request types)

  • TransactionStatus (used to get the status/results of a given TransactionID)

  • ListToDo (used to get all requests sitting in the ToDo database table)

  • ListUsers (used to get a list of all authorized EAIFramework users)

This is just a brief list of the many possible support request types that the EAIFramework could provide to its users and administrators. Because each will have a different request name, each would have to have a separate RequestHandler component. However, each of the requests will be quite small in terms of processing, and it would be unnecessary overhead for each one to have its own handler. Instead, the T_RequestDefinitions table tells you what RequestHandlerBase subclass to use. In this way, you can process more than one request type in a single RequestHandler. For example, the rows in the T_RequestDefinitions table for the preceding scenario might look something like Table 7-2.

Table 7-2 Requests supported by Admin request handler






List supported requests




Return saved status info for TrnxID




Snapshot of current ToDo requests




Return list of all EAIFramework users



You can see that if any of the four Request blocks comes in for processing, they all will be handled with a handler named Admin. This handler name, as described earlier, is used to generate the full handler name. In this case, all four requests would generate the full handler name:


Then, in the process method of the AdminHandler class, a quick check of the request name is made to determine which functionality to perform.

After you have checked the database for a handler name for the supplied request name, a check is made to see if the database call returned null or a blank string. If either of these were returned, it means that the request name supplied does not exist in the table. If that's the case, just use the Request Name as the handler name. Essentially, the name of a request is the default handler name; you have to insert rows in the T_RequestDefinitions table only if more than one Request Name will be supported by a handler. For example, if the <Request Name="Catalogue"> request mentioned earlier is the only request handled by the CatalogueHandler class, it doesn't need to be in the database, because Catalogue will be used to generate the name of the handler on the fly.

Next, you get an ObjectHandle object for the handler class. The static method CreateInstance() of the Activator class is used for this purpose. You pass in the name of an assembly and the name of the class to be instantiated. In this case, you build the class name by using the strHandlerName member that should contain either the request name or the handler name returned from the database for this request and a constant. This is done with the following code:

strHandlerName + "Handler"

You use the ObjectHandle instance for the required handler to get a Type object. This is accomplished by calling the Unwrap() method on the ObjectHandle returned earlier and immediately calling the GetType() method. You are left with a Type object that represents the handler class you need.

Finally, you create a ConstructorInfo object from the System.Reflection namespace. This instance is used to create the actual handler instance that gets returned to the caller. The constructor for the ConstructorInfo class takes in an array of Type objects. This is used when the constructor for the object in question is called. In this case, the constructor for a RequestHandler subclass takes in a single parameter, a Request object. Therefore, the ConstructorInfo object takes in a Type array that contains a single Type, the EAIFramework.Messages.Request type.

With the newly created ConstructorInfo instance for the handler you need, you are ready to fire up the constructor and get an instance of the handler class. A call to Invoke() on the ConstructorInfo class accomplishes this. The last line of real code in the getHandler() method creates the instance:

EAIFramework.Handler.RequestHandlerBase rhb =
      ci.Invoke(new object[]{rIn});

The member rhb is returned to the caller. The Request object, sent into the getHandler() method, is sent into the constructor in the Invoke() call. Invoke() takes an array of Object objects. In this way, you could call any constructor with any signature by setting the appropriate input parameter list in the object array.

RequestsProcessor Code

Now we look at the RequestsProcessor class (see Listing 7-8). I'm going to blow past some of the helper methods, because they are really self-explanatory. However, I do want to spend some time on the process method. This is called from the Controller class when a transaction is sent in that needs to be processed synchronously. It is also called from the RequestQueMonitor class that watches the request message queue to process asynchronous transactions.

Listing 7-8: The RequestsProcessor Class

using System;
using System.Collections;

using EAIFramework.Util;
using EAIFramework.Messages;
using EAIFramework.Handler;
using EAIFramework.BusinessRules;

namespace EAIFramework.Controller
 /// <summary>
 /// Summary description for RequestsProcessor.
 /// </summary>
 public class RequestsProcessor
  protected EAIFramework.Messages.EAIRequest eaiReq = null;
  protected Requests reqObjects = new Requests ();
  /// <summary>
  /// Default constructor, takes only a single argument,
  ///  An EAIRequest.
  /// </summary>
  /// <param name="rIn"></param>
  public RequestsProcessor(
   EAIFramework.Messages.EAIRequest rIn)
   eaiReq = rIn;
   reqObjects = eaiReq.Requests;  
  }//end constructor()

There is nothing remarkable about the first portion of the class. It has a single constructor that takes an EAIRequest as its only parameter, which it stores in an instance member. Now on to the process method (see Listing 7-9).

Listing 7-9: The process Method

  /// <summary>
  /// This method adds each Request to the RequestsToDo
  /// table and then processes each Request.

  /// When it completes, each Request should remove
  /// its own entry from the RequestsToDo table.
  /// </summary>
    public ArrayList process()
   // Set up the ArrayList we'll return to the caller
   ArrayList alRet = new ArrayList();

   // Get the list of Request objects
   reqObjects = eaiReq.Requests; 
   // Set up a place to store handlers
   ArrayList alHandlers = new ArrayList();

   // Before anything gets added to the ToDo list,
   // see if this set of Requests passes
   // PreProcBusinessRules
   PreProcBusinessRules preBR = 
      new PreProcBusinessRules();
   EAIFramework.Messages.BusinessRuleResponse brr =
   if( brr.StatusCode != StatusCodes.OK )
    // This is an error. There was something wrong
    // with the PreProcessing Business Rule checks!
    // Generate response and return w/o processing.

     "PreProcBusinessRules.Check Failed: " +

    RequestResponse rBack = new RequestResponse();
    rBack.StatusCode = 
    rBack.Description = 
     "Failed PreProcBusinessRules checking: " +
    rBack.Name = "PreProcBusinessRules";
    return alRet;
   }//end if PreProcBusRules.Check()
    "PreProcBusinessRules.Check Passed");

   // Add to the RequestsToDo list

   // Now process each Request object
   foreach(Request req in reqObjects.RequestList)
    RequestResponse rBack = null;
     "Processing Request # " +
     // Here we check to see if the user is 
     // authorized to execute this type of
     // request.
     Authenticator auth = new Authenticator();
     EAIUser theUser = 
      // this is okay, they're an ADMIN
      // See if they have rights to process
      // this type of Request now.
      // If not, send them on their way w/o
      // processing this Request
      if( ! auth.UserCanExecuteRequest(
        // If we're here, then the user is
        // NOT an ADMIN, *AND* they're not
        // authorized for this Request type.
        // Therefore, create an error and
        // continue to the next one.
       rBack = new RequestResponse();
       rBack.StatusCode = 
       rBack.Description = 
    "User: " + theUser.Username + 
    " attempting Unauthorized Request Type: " +
       rBack.Name = req.Name;
      }//end if authed == false

     }//end else (they're NOT an ADMIN)


      RequestHandlerBase hand = 
      if(hand == null) {
      rBack = new RequestResponse();
      rBack.StatusCode = 
      rBack.Description = 
       "Cannot find Handler: " + 
       " EAIFramework.Handler." +
       req.Name + "Handler";
       rBack.Name = req.Name;
      }//end if handler == null
      else {
       // The Handler is not null, so add it
       // to the list, and then call
      // the process() method to do the work
      rBack = hand.process();
      }//end else
    } catch (Exception exc) {
     // Need to build a failed RequestResponse
     // here to return good info.
     rBack = new RequestResponse();
     rBack.StatusCode = 
     rBack.Description = 
      "Cannot find Handler: " +
      "EAIFramework.Handler." +
      req.Name + "Handler - " + exc.Message;
     rBack.Name = req.Name;
    }//end catch()

    // If there is an error AND we're supposed to

    //  FailOnFirstError, then break out and don't
    //  process any more Request objects.
    if( (rBack.StatusCode != StatusCodes.OK ) &&
     // However, b/f we go, we need to remove all
     // the Requests that are currently in the 
     // T_RequestToDo table for this TrnxID

     // Now call roll back on any of the Requests

     // that have already been fired
     for(int x = 0; x < req.Iteration; x++)
      Request rRoll = (Request)
      RequestHandlerBase hRoll =
       (RequestHandlerBase) alHandlers[x];
      RequestResponse rrRoll = hRoll.rollback();
     }//end for

    // Otherwise, just remove this processed Request
    // from the T_RequestsToDo table
   }//end foreach

   // Now, right before we leave,
   // see if this set of Requests passes
   // PostProcBusinessRules
   PostProcBusinessRules postBR = 
      new PostProcBusinessRules();
   brr = postBR.Check(reqObjects);

   if( brr.StatusCode != StatusCodes.OK )
    // This is an error. There was something wrong
    // with the PreProcessing Business Rule checks!
    // Generate response and return w/o processing.

     "PostProcBusinessRules.Check Failed: " +

    RequestResponse rBack = new RequestResponse();
    rBack.StatusCode = 
    rBack.Description = 
     "Failed PostProcBusinessRules checking: " +
    rBack.Name = "PostProcBusinessRules";
    return alRet;
   }//end if PostProcBusRules.Check()
    "PostProcBusinessRules.Check Passed");

   return alRet;
  }//end process()

process returns an ArrayList instance filled with RequestResponse objects. These are the RequestResponse instances sent back from each called RequestHandler instance. The method starts by creating the ArrayList that will be returned. It then pulls out the Requests object from the EAIRequest object and creates an ArrayList to hold the RequestHandler instances to be used for each Request. You create and store a list of handler objects, because it might be necessary to call the rollback() method on each one.

Next, all of the requests in the Requests object are added to the ToDo table in the database. This is accomplished by calling the helper method addToToDo(). It makes the code a little cleaner in this method to stick the call to the ToDo proxy class in a helper method.

A foreach loop then blasts through each Request contained in the Requests object. The handler object, cast as a RequestHandlerBase object, is created by calling the RequestHandlerFactory.getHandler() method, discussed earlier. Providing that the handler is found, instantiated, and returned, you can send off the request for processing. If all is well, the handler is added to the ArrayList of the handler, and a new RequestResponse object is created by calling the process method of the handler. If there is an error along the way for this Request, an error RequestResponse instance is created. You drop out of the code for this Request and add the RequestResponse, good or bad, to the ArrayList to be returned, named alRet.

Next, if the status was unsuccessful for any reason and this transaction is set for FailOnFirstError, you delete all requests for this transaction from the ToDo table, because you're bailing on this one. Then each handler that has already been called is cycled through, and the rollback() method is called. A RequestResponse object is returned from the rollback() method, just as the process method does. These status objects are added to the alRet list along with those returned from process.

Otherwise, you remove the current Request from the ToDo table, whether the response was successful or unsuccessful. When the foreach loop completes, either because of running through each Request processed or because of a failure and FailOnFirstError being set to true, the compiled ArrayList of RequestResponse objects is returned to the caller (see Listing 7-10).

Listing 7-10: addToToDo() Method

  /// <summary>
  /// Helper method to insert all Request objects into the
  /// RequestToDo table.
  /// </summary>
  /// <param name="rsIn"></param>
  private void addToToDo( Requests rsIn )
   DBUtilProxy tdprox = new DBUtilProxy();
   foreach( Request req in rsIn.RequestList)
   }//end foreach
  }//end addToToDo()

  private void removeAllFromToDo(string strReq)
   DBUtilProxy dbup = new DBUtilProxy();
   int nRow = dbup.removeToDoRequests(strReq);
  }//end removeAllFromToDo()

  private void removeFromToDo(Request req)
   DBUtilProxy dbup = new DBUtilProxy();
   int nRows = dbup.removeRequest(req);
  }//end removeFromToDo()

 }//end class
}//end namespace

As you can see in Listing 7-10, the method addToToDo() takes in a Requests object. It instantiates the DBUtilProxy class, which is used to interact with some of the utility tables. In this case, it inserts rows into the T_RequestsToDo table. This table is used to hold any Request steps that need to be processed. Because all the processing is happening in memory, if the box takes a nosedive for some reason, you would lose all traces of submitted but as yet unprocessed requests. Therefore, the first thing you must do is add each step to a database table. Now, if the cleaning crew decides to unplug your server so that it can use the floor polisher, you can query the ToDo table and continue processing (more or less) as if nothing had happened.

The removeAllFromToDo() method is called if the request processing is being aborted. You already know that you are not going to continue processing any subsequent requests, so just remove all of the requests in the T_RequestsToDo table.

removeFromToDo() performs essentially the opposite function as the addToToDo() method. It is called when an individual request has been processed. When that happens, you can safely remove this request from the T_RequestsToDo table so that it's not in danger of being reprocessed later.

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.


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.


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.


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.


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


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


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.


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.


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