Home > Articles > Programming > Windows Programming

This chapter is from the book

This chapter is from the book

Remoting handles data between client and server in two ways: (1) marshaling the data by reference and (2) marshaling the data by value. Marshaling by reference is analogous to having a pointer, and marshaling by value is analogous to having a copy. If we change a reference object, the original is changed, and changes to a copy have no effect on the original. To get your feet wet let's start with a quick marshal-by-reference example. (The Marshaling Objects by Value section later in this chapter talks about the other way data is moved back and forth.)

NOTE

Occasionally I will be accused of writing or saying something condescending. That is never my intent. That said, depending on your level of comfort with technical jargon, words like marshal may sound ominous. This is an advanced book, but if you are not comfortable with COM and DCOM, the word marshal may trouble you. An easier term might be shepherd, as in herding sheep. Because remoting moves data across a network, the data must be packaged and unpackaged in an agreed-upon format and shepherded between the application that has the data (the server) and the application that wants the data (the client).

Several graphics on the Web depict this relationship—marshaling between client and server—but I am not sure if they aid understanding or add to confusion. Rather than repeat those images, I encourage you to think of the code that does the shepherding as the responsibility of .NET. These codified shepherds are referred to as proxies. The Remoting namespace contains the proxy code.

Hello, Remote World!

Rather than torture you with another Hello, World! application, I will use a sample application with a little more meat (not much but a bit more).

Suppose you work in the information technology department of a large insurance company. This company owns several broker dealers that sell mutual funds. As a result you are tasked with tracking all customer purchases of mutual funds, life and health products, and annuities. You can cobble together a solution that requires the remote broker dealer offices to run batch programs at night that upload data and combine the mutual fund trades with Universal Life payments, mixing and matching the client PC's database programs with your UDB, SQL Server, or Oracle databases. When you are finished you have VB6 applications on the client workstations running ObjectRexx dial-up scripts to FTP servers late at night. Or, you can use remoting and .NET to get everybody working together. Throw out the Perl, .cmd, .bat, and .ftp scripts; toss the various and sundry import and export utilities written in C, VB6, and Modula; and get everything working in real time.

Okay. We won't have enough time to tackle all of that in this section, but we can create a client application that requests a customer and a server application that simulates servicing that request. Because the code would take up a lot of space, we will simulate the client reading from the database. However, after you read Chapters 11, 12, and 16 on ADO.NET, you will be able to incorporate the code to read from the database too. Figure 8.1 shows a UML model of the design we will be using here. (I used Rational XDE, integrated into .NET, to create the UML class diagram.)

08fig01.gifFigure 8.1. The class diagram for our server application.

The class diagram accurately depicts the code that resides in the client and server. An assembly named Interface contains the two interfaces: IFactory and ITrade. The assembly named Server implements (realizes in the vernacular of the UML) IFactory and ITrade in Factory and Trade, respectively, and the assembly named Client is dependent on the two interfaces. Note that there is no dependency on the actual implementations of IFactory and ITrade in Client. If all the code on the server were on the client, then arguably the server would not be needed. (This isn't precisely true but logically makes sense.) Listings 8.1 and 8.2 contain the code for the Interface and Server assemblies, in that order.

Listing 8.1 The Interface.vb File Containing the IFactory and ITrade Interfaces

Public Interface IFactory

  Function GetTrade(ByVal customerId As Integer) As ITrade

End Interface

Public Interface ITrade

  Property NumberOfShares() As Double
  Property EquityName() As String
  Property EquityPrice() As Double
  ReadOnly Property Cost() As Double
  Property Commission() As Double
  Property RepId() As String

End Interface

Listing 8.2 The ServerCode.vb File Containing the Implementation of ITrade and IFactory

Imports System
Imports [Interface]
Imports System.Reflection

Public Class Factory
  Inherits MarshalByRefObject
  Implements IFactory

  Public Function GetTrade( _
    ByVal customerId As Integer) As ITrade _
    Implements IFactory.GetTrade
    Console.WriteLine("Factory.GetTrade called")

    Dim trade As Trade = New Trade()
    trade.Commission = 25
    trade.EquityName = "DYN"
    trade.EquityPrice = 2.22
    trade.NumberOfShares = 1000
    trade.RepId = "999"

    Return trade
  End Function

End Class

Public Class Trade
  Inherits MarshalByRefObject
  Implements ITrade

  Private FCustomerId As Integer
  Private FNumberOfShares As Double
  Private FEquityName As String
  Private FEquityPrice As Double
  Private FCommission As Double
  Private FRepId As String

  Public Property NumberOfShares() As Double _
    Implements ITrade.NumberOfShares
  Get
    Return FNumberOfShares
  End Get
  Set(ByVal Value As Double)
    FNumberOfShares = Value
  End Set
  End Property

  Public Property EquityName() As String _
    Implements ITrade.EquityName
  Get
    Return FEquityName
  End Get
  Set(ByVal Value As String)
    Console.WriteLine("EquityName was {0}", FEquityName)
    FEquityName = Value
    Console.WriteLine("EquityName is {0}", FEquityName)
    Console.WriteLine([Assembly].GetExecutingAssembly().FullName)

  End Set
  End Property

  Public Property EquityPrice() As Double _
    Implements ITrade.EquityPrice
  Get
    Return FEquityPrice
  End Get
  Set(ByVal Value As Double)
    FEquityPrice = Value
  End Set
  End Property

  ReadOnly Property Cost() As Double _
    Implements ITrade.Cost
  Get
    Return FEquityPrice * _
      FNumberOfShares + FCommission
  End Get
  End Property

  Property Commission() As Double _
    Implements ITrade.Commission
  Get
    Return FCommission
  End Get
  Set(ByVal Value As Double)
    FCommission = Value
  End Set
  End Property

  Property RepId() As String _
    Implements ITrade.RepId
  Get
    Return FRepId
  End Get
  Set(ByVal Value As String)
    FRepId = Value
  End Set
  End Property

End Class

The code in both listings is pretty straightforward. Listing 8.1 defines the two interfaces IFactory and ITrade. Listing 8.2 provides an implementation for each of these interfaces.

After scanning the code you might assume that all we need to do is add a reference in the client to each of the two assemblies containing the code in Listings 8.1 and 8.2 and we're finished. And you'd be right if we were building a single application. However, we are building two applications: client and server.

Suppose for a moment that we did add a reference to the Interface and Server assemblies. .NET would load all three assemblies—client, interface, and server—into the same application domain (AppDomain), and the client could create Trade and Factory objects directly or by using the interfaces. This is a valid model of programming, but it is not distributed. It works because .NET uses AppDomain for application isolation. All referenced assemblies run in the same AppDomain. However, when we run a client application and a separate server, we have two applications, each running in its own AppDomain. .NET Remoting helps us get data across application domains.

In our distributed example, Client.exe is an executable with a reference to Interface.dll. Both of these assemblies run in the AppDomain for Client.exe. Server.exe also has a reference to Interface.dll, and Server.exe and Interface.dll run in the AppDomain for Server.exe. The code we have yet to add is the code that creates the object on the client by making a remote request to the server.

Getting Client and Server Talking

Thus far we have written vanilla interface and class code. To get the client and server talking we have to use some code in the System.Runtime.Remoting namespace. The first step is to inherit from MarshalByRefObject. Listing 8.2 shows that both Factory and Trade inherit from MarshalByRefObject, which enables the classes to talk across application boundaries. The second piece of the puzzle is to tell the server to start listening, permitting the client to start making requests.

Listing 8.3 contains the code that instructs the server to start listening, and Listing 8.4 contains the code to get the client to start making requests. Both client and server are implemented as console applications (.exe) for simplicity. You can use .NET Remoting with a variety of hosting styles. (Refer to the Choosing a Host for Your Server subsection near the end of this chapter for more information.)

Listing 8.3 Telling the Server Application to Begin Listening for Requests

1:  Imports System.Runtime.Remoting
2:  Imports System.Runtime.Remoting.Channels
3:  Imports System.Runtime.Remoting.Channels.Http
4:
5:  Public Class Main
6:
7:    Public Shared Sub Main(ByVal args() As String)
8:
9:      Dim channel As HttpChannel = New HttpChannel(9999)
10:     ChannelServices.RegisterChannel(channel)
11:     RemotingConfiguration.RegisterWellKnownServiceType( _
12:       GetType(Factory), "Factory.soap", _
13:       WellKnownObjectMode.Singleton)
14:
15:     RemotingConfiguration.RegisterWellKnownServiceType( _
16:       GetType(Trade), "Trade.soap", _
17:       WellKnownObjectMode.Singleton)
18:
19:     Console.WriteLine("Server is running...")
20:     Console.ReadLine()
21:     Console.WriteLine("Server is shutting down...")
22:   End Sub
23:
24: End Class

From the code and the shared Main method you can tell that Listing 8.3 comes from a .NET console application. Lines 1 through 3 import namespaces relevant to remoting.

The first thing we need to do is declare a channel. I elected to use the HTTP protocol, and the HttpChannel constructor takes a port number. This is the port number on which the server will listen. If you want the server to automatically choose an available port, send 0 to the HttpChannel constructor. There are about 65,500 ports. If you want to specify a port number, just avoid obvious ports that are already in use like 80 (Web server), 23 (Telnet), 20 and 21 (FTP), and 25 (mail). Picking a port that is being used by another application will yield undesirable results. After we have elected a channel we need to call the shared method RegisterChannel (line 10).

Next we register the server as a well-known service type. (Inside the CLR there is a check to make sure that the service inherits from MarshalByRefObject.) We pass the Type object of the type to register, the Uniform Resource Identifier (URI) for the service, and the way we want the service instantiated. When you read URI, think URL. The URI identifies the service; by convention we use the class name and .soap or .rem for the URI. You can use any convention, but Internet Information Services (IIS) maps the .soap and .rem extensions to .NET Remoting. This is important when hosting remote servers in IIS. (Refer to the Choosing a Host for Your Server subsection near the end of this chapter.) You can pass the WellKnownObjectMode.Singleton or WellKnownObjectMode.SingleCall enumerated values to the registration method. Singleton is used to ensure that one object is used to service requests, and SingleCall will cause a new object to be created to service each request. (SingleCall causes a remoted server to respond like a Web application. The server has no knowledge of previous calls.)

The Factory type is registered in lines 11 through 13 and the Trade type in lines 15 through 17. After the server types are registered we use Console.ReadLine to prevent the server from exiting. To quit the server application, set the focus on the console running the server and hit the carriage return; the server will respond until then. Listing 8.4 contains the code that prepares the client to send requests to the server.

Listing 8.4 Preparing the Client Application to Begin Making Requests

1:  Private Sub Form1_Load(ByVal sender As System.Object, _
2:      ByVal e As System.EventArgs) Handles MyBase.Load
3:
4:      Dim channel As HttpChannel = New HttpChannel()
5:      ChannelServices.RegisterChannel(channel)
6:
7:      Dim instance As Object = _
8:        Activator.GetObject(GetType(IFactory), _
9:          "http://localhost:9999/Factory.soap")
10:
11:     Dim factory As IFactory = _
12:       CType(instance, IFactory)
13:
14:     Dim trade As ITrade = _
15:       factory.GetTrade(1234)
16:
17: End Sub

The client declares, creates, and registers a channel in lines 4 and 5. We don't need the port here when we register the channel; we will indicate the port when we request an instance of the object from the server. Lines 7 through 9 use the shared Activator.GetObject class to request an instance of the Factory class defined in the server. The URL (line 9) indicates the domain and port of the server and the name we registered the server with. Lines 11 and 12 convert the instance type returned by Activator to the interface type we know it to be, and lines 14 and 15 use the factory instance to request a Trade object.

To see that the value of the trade object (line 14) is actually a proxy, place a breakpoint in line 17 and use QuickWatch to examine the value of the trade variable (Figure 8.2).

08fig02.gifFigure 8.2. The local variable trade is an instance of the TransparentProxy class, indicating the unusual remoted relationship between client and server.

Using Server-Activated Objects

In the example above we created what is known as a server-activated object (SAO). When you construct an SAO—for example, with Activator.GetObject—only a proxy of the object is created on the client. The actual object on the server isn't created until you invoke an operation on that type via the proxy. (The proxy is transparent; thus the invocation occurs in the background when you call a method or access a property.) The lifetime of an SAO is controlled by the server, and only default constructors are called.

In a production application it is more than likely that you will want to permit the operator to manage the configuration of the server without having to recompile the server application. This can be handled in an application configuration file. Example3\Client.sln defines an application configuration file for server.vbproj. You can add an application configuration file by accessing the File|Add New Item menu in Visual Studio .NET and selecting the Application Configuration File template from the Add New Item dialog. Listing 8.5 contains the externalized XML settings used to register the server. The revision to the Main class in Listing 8.3, which accommodates the application configuration file, is provided in Listing 8.6.

Listing 8.5 An Application Configuration That Externalizes Server Registration Settings

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.remoting>
    <application>
      <channels>
        <channel ref="http" port="8080" />
      </channels>
      <service>
        <wellknown mode="Singleton"
          type="Server.Factory, Server"
          objectUri="Factory.soap" />
      </service>
    </application>
  </system.runtime.remoting>
</configuration>

The first statement describes the XML version and the text encoding (8-bit Unicode in the example). The configuration element indicates this is a configuration file. Typically, XML elements have an opening tag—for example, <configuration>—and a matching closing tag with a whack (/) inside the tag. Sometimes this is abbreviated to />, as demonstrated with the wellknown element in Listing 8.5.

The third element indicates the relevant namespace, system.runtime.remoting. The channel element indicates the channel type and port. The wellknown element indicates the WellKnownObjectMode (Singleton in the example), the type information for the type we are registering (Server.Factory, in Listing 8.6), and the URI (Factory.soap). This is precisely the same information we provided in Listing 8.3, programmatically. Now, however, if we find that port 8080 is in use by a proxy server or another HTTP server, we can reconfigure the channel without recompiling.

Having modified the server application to store the server registration information in the .config file, we can modify Listing 8.3 to simplify the registration of WellKnownServiceType. Listing 8.6 shows the shorter, revised code.

Listing 8.6 Revised Code after Moving Registration Settings to Server.exe.config

Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http

Public Class Main

  Public Shared Sub Main(ByVal args() As String)

    RemotingConfiguration.Configure("Server.exe.config")

    Console.WriteLine("Server is running...")
    Console.ReadLine()
    Console.WriteLine("Server is shutting down...")
  End Sub

End Class

In the example we have removed the channel construction and calls to the shared method RemotingConfiguration.RegisterWellKnownServiceType that appeared in Listing 8.3. All we need to do now is pass the name of our .config file to the RemotingConfiguration.Configure method in Listing 8.6.

Keep in mind that when you add the Application Configuration File template to your project you will see an App.config file in the Solution Explorer with the rest of your source. When you compile your application, the applicationname.exe.config file is written to the directory containing the executable. While in the debug configuration mode, for example, you will see the Server.exe.config file written to the .\bin directory.

Using Client-Activated Objects

Client-activated objects (CAOs) are registered and work a bit differently than server-activated objects. A CAO is created on the server as soon as you create an instance of the CAO, which you can do by using Activator.CreateInstance or the New constructor. The biggest difference between SAOs and CAOs is that CAOs do not use shared interfaces; rather, a copy of the shared code must exist on both the client and the server. Deploying code to client and server will mean more binaries on the clients, a more challenging deployment, and possible versioning problems.

To preclude re-reading all the code, I have reused the same Factory and Trade classes for our CAO example. However, I have gotten rid of the interfaces, placed the Factory class in the client (since we don't really need two server-side classes to demonstrate CAO), and shared the Trade class between client and server. Instead of literally sharing the Trade class in a third DLL assembly, I defined the Trade class in the Server assembly and used soapsuds.exe (a utility that ships with VS .NET) to generate the shared DLL. We'll go through each of these steps in the remaining parts of this section. (The code for this section can be found in the Example2\Client.sln solution.)

Implementing the Server for the CAO Example

The Server.vbproj file contains the same Trade class shown in Listing 8.3, so I won't relist that code here. The Factory class has been moved to the client (see the Implementing the Client subsection below). What's different about the server is how we register it. The revision to the Main class is shown in Listing 8.7.

Listing 8.7 Registering a Server for Client Activation

1:  Imports System.Runtime.Remoting
2:  Imports System.Runtime.Remoting.Channels
3:  Imports System.Runtime.Remoting.Channels.Http
4:
5:  Public Class Main
6:
7:    Public Shared Sub Main(ByVal args() As String)
8:
9:      Dim channel As HttpChannel = New HttpChannel(9999)
10:     ChannelServices.RegisterChannel(channel)
11:
12:     ' Code needed for client activation
13:     RemotingConfiguration.ApplicationName = "Server"
14:     RemotingConfiguration. _
15:       RegisterActivatedServiceType(GetType(Trade))
16:
17:     Console.WriteLine("Server is running...")
18:     Console.ReadLine()
19:     Console.WriteLine("Server is shutting down...")
20:   End Sub
21:
22: End Class

Registration for client activation is much simpler. We provide a name for the application and register the type we will be remoting. The application name is provided in line 13 and the Trade class is registered in lines 14 and 15 using the shared method RemotingConfiguration.RegisterActivatedServiceType, passing the type of the class to register. Recall that we actually have the implementation of the type—Trade—defined on the server.

That's all we need to do to the server's Main class—change the registration code.

Exporting the Server Metadata for the Trade Class

To construct an instance of a class in the client using the new operator, we need a class. Calling New on an interface—as in Dim T As ITrade = New ITrade()—won't work because interfaces don't have code. You can create a third assembly and share that code in both the client and server, or you can use the soapsuds.exe utility to generate a C# source code or a DLL that can be referenced in your client application. I implemented a batch file mysoapsuds.bat in the Example2\Server\bin directory that will create a DLL named server_metadata.dll. Here is the single command in that batch file.

soapsuds -ia:server -nowp -oa:server_metadata.dll

In this code, soapsuds is the name of the executable. The –ia switch is the name of the input assembly. (Note that the assembly extension—.exe for this example—is left off.). The –nowp switch causes soapsuds to stub out the implementations, permitting a dynamic transparent proxy to handle the method calls. The –oa switch indicates the output assembly name. In the example an assembly named server_metadata.dll will be generated. Next we will add a reference to this assembly in our client application.

Implementing the Client

The client application needs a definition of the interface and the type for client activation. We can actually share the code between client and server and use parameterized constructors for client activation; or, in our example, we use soapsuds.exe to generate a metadata DLL and give up parameterized constructors for remoted objects.

On the user's PC we need some kind of application as well as remoting registration code, and we can use a factory on the client to simulate constructor parameterization (if we are using soapsuds-generated metadata.) As a general rule it is preferable to use soapsuds to generate metadata and a factory for convenience, as opposed to shipping the server executable to every client. Listing 8.8 shows a Windows Forms implementation of the CAO client and a factory for the Trade class.

Listing 8.8 Implementing a Client-Activated Object and a Factory

1:  Imports System
2:  Imports System.Runtime.Remoting
3:  Imports System.Runtime.Remoting.Channels
4:  Imports System.Runtime.Remoting.Channels.Http
5:  Imports System.Runtime.Remoting.Activation
6:  Imports System.Reflection
7:  Imports Server
8:
9:  Public Class Form1
10:     Inherits System.Windows.Forms.Form
11:
12: [ Windows Form Designer generated code ]
13:
14:   Private Generator As Generator
15:
16:   Private Sub Form1_Load(ByVal sender As System.Object, _
17:     ByVal e As System.EventArgs) Handles MyBase.Load
18:
19:     Dim channel As HttpChannel = New HttpChannel()
20:     ChannelServices.RegisterChannel(channel)
21:
22:     ' Client-activated object code
23:     RemotingConfiguration.RegisterActivatedClientType( _
24:       GetType(Trade), _
25:       "http://localhost:9999/Server")
26:
27:     Dim Factory As Factory = New Factory()
28:     Dim Trade As Trade = Factory.GetTrade(5555)
29:     Trade.Commission = 25
30:     Trade.EquityName = "CSCO"
31:     Trade.EquityPrice = 11.0
32:     Trade.NumberOfShares = 2000
33:     Trade.RepId = 999
34:
35:     Generator = New Generator(Me, _
36:       GetType(Trade), Trade)
37:     Generator.AddControls()
38:
39:   End Sub
40:
41: End Class
42:
43: Public Class Factory
44:
45:   Public Function GetTrade( _
46:     ByVal customerId As Integer) As Trade
47:     Console.WriteLine("Factory.GetTrade called")
48:
49:     Dim trade As Trade = New Trade()
50:     trade.CustomerId = 555
51:     trade.Commission = 25
52:     trade.EquityName = "DYN"
53:     trade.EquityPrice = 2.22
54:     trade.NumberOfShares = 1000
55:     trade.RepId = "999"
56:
57:     Return trade
58:   End Function
59:
60: End Class

Listing 8.8 contains two classes: the Windows Forms class Form1 and the Factory class. The Form1 class creates and registers a channel in lines 19 and 20. Instead of using the Activator class to create the remote object, we call the shared method RemotingConfiguration.RegisterActivatedClientType, passing the type to register and the URI of the server-registered type (lines 23 through 25).

After the type that can be activated on the server is registered, we use the Factory class to create an instance of that type (lines 27 and 28). To provide you with additional calls to the server, I changed the values set by the Factory class. There is no requirement here, just extra code.

The Generator class used on lines 35 through 37 is extra code I added to create a Windows user interface. This code is included with the downloadable remoting example and creates a simple user interface comprised of text boxes and labels, created by reflecting the remote type.

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