Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 38 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
38
Dung lượng
190,64 KB
Nội dung
230 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application> <service> <wellknown mode="SingleCall" type="SomeRecordCompany.Licensing, Licensing" objectUri="Licensing.soap"/> </service> </application> </system.runtime.remoting> </configuration> The service element contains the configuration for objects exposed to other applications. It can contain either a wellknown or activated element. (I cover configuring client-activated objects later in the chapter.) You use the wellknown element to configure well-known objects. The element must contain three attributes: mode, type, and objectUri. The mode attribute indicates whether the well- known object should be hosted as a single call (SingleCall) or a singleton (Singleton). The type attribute contains the full type name and the assembly of the object that should be exposed by the Remoting runtime. The objectUri attribute specifies the URI where the object can be accessed. In the case of the Licensing Web service, the URI specified is Licensing.soap. Licensing.soap does not physically exist; requests sent to the URI are intercepted and processed by the Remoting runtime. The Licensing Web service can be hosted within any process. Because many clients might access the Licensing Web service simultaneously, a good option is to host the Web service within Microsoft Internet Information Services (IIS). This allows the Web service to be hosted in a Web farm that takes advantage of applications that improve the manageability of the Web farm (such as Microsoft Application Center). The Licensing Web service can be deployed within any IIS Web application. You can create a new Web application by opening the IIS MMC, right- clicking on the Web site, selecting New, and then choosing Virtual Directory. Once the Web application has been created, the contents of the configuration file I just created can be copied into the Web application’s web.config file. The Remoting runtime will expect the assemblies to be located within the Web application’s bin directory. Therefore, you must create a bin subdirectory and copy the Licensing.dll assembly into the new directory. You can access the Licensing Web service by addressing the Licensing.soap file within the Web application directory. For example, if the Licensing Web service is located in the SomeRecordCompany Web application on my local server, I can address it using the following URL: http://localhost/SomeRecordCompany/Licensing.soap If the Web service is hosted in IIS, the WSDL document will be automatically generated if wsdl is appended as a query string on the URL. The WSDL document for the Licensing Web service is available at the following URL: 231 http://localhost/SomeRecordCompany/Licensing.soap?wsdl Creating a WinForm-Hosted Web Service The primary purpose of Grabber.NET is to facilitate the exchange of files, so it needs a way to obtain a file from a remote computer. In this section, I create the SoapFileShare Web service, which supports two endpoints, one for retrieving files and the other for navigating directories. The File endpoint allows a client to obtain the contents of the requested file from a remote computer. Here is the implementation: using System; using IO = System.IO; namespace SoapFileShare { public class File : MarshalByRefObject { string rootDirectory = @"c:\temp\"; public byte[] GetFile(string fileName, string license) { // Validate the license // Obtain the contents of the requested file. IO.Stream s = IO.File.Open(rootDirectory + fileName, IO.FileMode.Open); byte[] fileContents = new byte[s.Length]; s.Read(fileContents, 0, (int)s.Length); return fileContents; } } The File endpoint exposes the GetFile method. The name of the targeted file and the necessary licensing information are passed to the GetFile method, which uses the information to determine whether the client is licensed to receive the file. If the client is licensed to receive the file, the GetFile method obtains a byte array for the requested file and returns it to the client. Later in the chapter, I discuss how to access the Licensing Web service, which validates the request. Grabber.NET also needs to browse the remote computer to see what files are available, so I need to create the Directory endpoint. The Directory endpoint exposes methods that allow the client to navigate the directory hierarchy on the remote computer and obtain a list of files within a particular directory: public class Directory : MarshalByRefObject 232 { string rootDirectory = @"c:\temp"; // Get the list of files at the root directory. public string[] GetFiles(string path) { // Obtain the list of files in the directory. string [] fileNames = IO.Directory.GetFiles(rootDirectory + path); // Truncate the path information so that it is relative // to the root directory. for(int i = 0; i < fileNames.Length; i++) { char [] newFileName = new char[fileNames[i].Length - rootDirectory.Length]; fileNames[i].CopyTo(rootDirectory.Length, newFileName, 0, fileNames[i].Length - rootDirectory.Length); fileNames[i] = new string(newFileName); } return fileNames; } // Get the list of files at the root directory. public string[] GetDirectories(string path) { string [] directories = IO.Directory.GetDirectories(rootDirectory + path); // Truncate the path information so that it is relative // to the root directory. for(int i = 0; i < directories.Length; i++) { char [] newDirectory = new char[directories[i].Length - rootDirectory.Length]; 233 directories[i].CopyTo(rootDirectory.Length, newDirectory, 0, directories[i].Length - rootDirectory.Length); directories[i] = new string(newDirectory); } return directories; } } } The Directory endpoint exposes two methods, GetFiles and GetDirectories . GetFiles returns a list of files within a specified directory, and GetDirectories returns a list of subdirectories within a specified directory. You can use these two methods to navigate a directory hierarchy. The primary purpose of Grabber.NET is to allow the exchange of files between peers. A peer-to-peer application has to act as both a client and a server. Therefore, the WebForms application itself must act as a Remoting server. One major advantage of the Remoting framework over technologies such as ASP.NET is its ability to host a Web service in any process over any transport protocol. In this case, I want the Grabber.NET WinForm application to listen for SOAP requests over HTTP. Any .NET application can listen for incoming requests by calling the Configure static method on the RemotingConfiguration object to initialize the appropriate listener. The method accepts the path to a configuration file as its only parameter. The configuration file is similar to the one I created for the Licensing Web service. However, unlike a Remoting Web service hosted in IIS, a Remoting Web service hosted within a process is not limited to HTTP. Therefore, the channel needs to be configured. The following configuration file configures Remoting to listen on port 88 for HTTP requests for the File or Directory endpoint: <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application name="SoapFileShare"> <service> <wellknown mode="SingleCall" type="SoapFileShare.Directory, SoapFileShare" objectUri="Directory.soap"/> <wellknown mode="SingleCall" type="SoapFileShare.File, SoapFileShare" objectUri="File.soap"/> </service> <channels> <channel port="88" type="System.Runtime.Remoting.Channels.Http. HttpChannel, System.Runtime.Remoting" /> 234 </channels> </application> </system.runtime.remoting> </configuration> As with the Licensing Web service hosted in IIS, you must configure the Remoting runtime to host the Web service. Recall that a Remoting Web service can be hosted via any number of transport protocols. Because the Licensing Web service is hosted in IIS, the HTTP transport was assumed. Also, because the Licensing Web service is hosted in a particular Web application, I needed to specify only the filename of the endpoint. But the SoapFileShare Web service is not hosted within IIS, so I need to specify the channel as well as the full endpoint to the path. As in Web services hosted in IIS, the objectUri property of the wellknown element specifies the filename that will serve as the address of the endpoint. But unlike in Web services hosted in IIS, you must specify the path to the file using the name attribute of the application element. In the case of Grabber.NET, the directory is SoapFileShare, so the Directory endpoint is addressable at http://localhost/SoapFileShare/Directory.soap. You can also specify a subdirectory. For example, if the name attribute is set to Grabber.NET/SoapFileShare, the Directory endpoint is addressable at http:// localhost/Grabber.NET/SoapFileShare/Directory.soap. The transport protocols supported by Remoting are defined within the channels element. Each supported transport protocol is referenced within an individual channel element. The channel element contains two attributes, port and type. The port attribute specifies the port the transport protocol will use to communicate with the remote application, and the type attribute specifies the .NET type that implements the channel and the type’s assembly. By convention, the configuration file for the application has the same name as the assembly, with .config appended to it. In the case of Grabber.NET, the WinForm application is named SoapFileExplorer.exe, so I will name the configuration file SoapFileExplorer.exe.config. Once the configuration file has been created, it must be explicitly loaded by the application. You do this by passing the path of the configuration file to the Configure static method exposed by the RemotingConfiguration object. In the case of Grabber.NET, the Configure method is called within the constructor for the main WinForm, as shown here: public ExplorerForm() { // // Required for Windows Form Designer support // InitializeComponent(); // Load the Remoting configuration files. RemotingConfiguration.Configure("SoapFileExplorer.exe.config"); } 235 You can also configure a well-known object within the application code itself so that you can dynamically configure a well-known object at run time. The following code configures the File and Directory well-known objects: RemotingConfiguration.ApplicationName = "SoapFileShare"; RemotingConfiguration.RegisterWellKnownServiceType(typeof(SoapFileSh are.File), "File.soap", WellKnownObjectMode.SingleCall); RemotingConfiguration.RegisterWellKnownServiceType(typeof(SoapFileSh are. Directory), "Directory.soap", WellKnownObjectMode.SingleCall); Regardless of how the well-known objects are configured, Remoting will spin up another thread to listen for and process incoming requests. Accessing Web Services Now that I have created the Licensing and SoapFileShare Web services for Grabber.NET, it is time to write the client portion of Grabber.NET to access these Web services. In this section, I discuss three ways to create a Remoting proxy that will be used to access a Web service. Recall that the GetFile method of the File object is responsible for sending the requested file to the client. Before the file is sent, the licensing information received from the client must be verified against the Licensing Web service. For this example, I will use the new operator to create the proxy object. new Keyword–Generated Proxy You can configure the Remoting runtime to intercept calls to the new operator and return a dynamically generated proxy instead of the object itself. You do this by registering the well- known object within the Remoting configuration file. The following is the modified version of the SoapFileExplorer.exe.config file: <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application name="SoapFileShare"> <service> <wellknown mode="SingleCall" type="SoapFileShare.Directory, SoapFileShare" objectUri="Directory.soap" /> <wellknown mode="SingleCall" type="SoapFileShare.File, SoapFileShare" objectUri="File.soap" /> </service> <client> <wellknown type="SomeRecordCompany.Licensing, Licensing" url="http://localhost/SomeRecordCompay/Licensing.soap" /> </client> 236 <channels> <channel port="88" type="System.Runtime.Remoting.Channels.Http. HttpChannel, System.Runtime.Remoting" /> </channels> </application> </system.runtime.remoting> </configuration> I added a client element that contains a list of Web service endpoints used by the client. Individual endpoints are referenced by adding a wellknown child element. Note that the wellknown element under the client element is different from the wellknown element under the server element. The client wellknown element must contain two attributes, type and url. The url attribute contains the address of the targeted endpoint. The type attribute references the .NET type within a particular assembly that describes the remote object. The assembly can be the Licensing.dll that I previously deployed on IIS. You can also register the well-known object reference using the RegisterWellKnownClientType static method exposed by the RemotingConfiguration class. The following example registers the Licensing well-known object: RemotingConfiguration.RegisterWellKnownClientType(typeof(SomeRecordC ompany. Licensing), "http://localhost/SomeRecordCompany/Licensing.soap"); Because the generated proxy is strongly typed, the same Licensing assembly that was referenced within the Remoting configuration file also needs to be referenced by the client application itself. In this case, the SoapFileShare Web service application must reference the Licensing.dll assembly. In many cases, it is not practical to have clients reference the assembly that contains the implementation of the Web service. Later in this chapter, I discuss how to build a .NET assembly containing the necessary metadata from the Web service’s WSDL file. Once the configuration file has been modified and the License.dll assembly is referenced by the SoapFileShare project, the Remoting runtime will automatically create a proxy object on behalf of the client when the new operator is called. The Validate method can then be called on the resulting proxy object and the proxy will forward the call to the Licensing Web service. Here is the implementation: // Validate the license info if it was sent by the client. if(licenseInfo != null) { SomeRecordCompany.Licensing licensing = new SomeRecordCompany.Licensing(); licensing.Validate(fileName, licenseInfo); } Other than the fact that I load the Remoting configuration file when the WinForms application is initialized, the code for accessing the Licensing Web service is no different than if I were directly accessing the assembly. 237 GetObject-Generated Proxy Next I need to create the client code to access the SoapFileShare Web service. Because the SoapFileShare Web service can be hosted by any number of Grabber.NET peers, using the new keyword to create the proxy raises a significant issue: once a well-known object has been configured by the Remoting runtime, the resulting proxy will always be associated with the same endpoint. Another way to create a proxy for a well-known object is by calling the GetObject static method on the Activator class. When the new keyword is used to create a proxy object, the Remoting runtime actually calls the GetObject method to obtain the proxy. Because the Activator object is public, you can call it directly. The GetObject method is overloaded and supports two method signatures. Both versions of the GetObject method accept two parameters, the type of object that should be created and the URL where the well-known object is located. The second GetObject method signature also accepts an object containing channel-specific data. Recall that SoapFileExplorer has a look and feel similar to that of Windows Explorer. The right pane contains a TreeView control for navigating the directory structure, and the left pane has a ListView control. When a particular node in the TreeView control is selected, the ListView control is refreshed with all of the files contained within the particular directory. The following code updates the ListView control based on the list of files obtained from the SoapFileShare Web service: private void directoryTree_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e) { // Create an instance of the SoapFileShare.Directory object. string url = ((DirectoryTreeNode)e.Node).Url + "Directory.soap"; SoapFileShare.Directory directory = (SoapFileShare.Directory)Activator. GetObject(typeof(SoapFileShare.Directory), url); // Obtain the files within the selected directory. string [] filePaths = directory.GetFiles(((DirectoryTreeNode)e.Node).Path); // Display the files within the list view. this.fileList.Clear(); foreach(string filePath in filePaths) { this.fileList.Items.Add(new FileListViewItem(url, filePath)); } } First the URL of the targeted Directory endpoint of the SoapFileShare Web service is dynamically built. This URL is then passed to the GetObject method to obtain a proxy object 238 for the Directory endpoint. A list of files is then obtained for the selected directory by calling the GetFiles method on the proxy. By default, GetObject will create a proxy object that communicates with the well-known object via SOAP over HTTP. Therefore, you need not configure the Remoting runtime to execute the preceding code. WSDL-Generated Proxy To dynamically create a proxy, the Remoting runtime needs access to an assembly that contains type information that describes the targeted Web service. As I mentioned earlier, this can be the assembly that contains the implementation of the Web service. Because Grabber.NET both hosts and consumes the SoapFileShare Web service, it is practical to have the Remoting runtime dynamically generate a proxy for the SoapFileShare Web service. However, it is not practical to have Grabber.NET reference the assembly that contains the implementation of the Licensing Web service. You need some way of creating an assembly that contains the type information used to describe the Web service without containing the implementation. You can use one of the tools provided by the Remoting framework, SoapSuds, to convert the type information contained in a WSDL document into .NET type information. This type information can then be used by the Remoting runtime to create a proxy dynamically. The following command creates an assembly containing metadata that describes the Licensing Web service: soapsuds -url:http://localhost/SomeRecordLabel/Licensing.soap?wsdl -oa:Licensing.dll –gc -nowp This command creates an assembly called Licensing.dll as well as the source code for the assembly. Either the assembly can be referenced or the source code can be included by a client application such as Grabber.NET that creates proxies using the new keyword or the GetObject method. Table 8-1 describes the command-line parameters supported by SoapSuds. Table 8-1: Command-Line Parameters Supported by SoapSuds Switch Description -domain:domainName or - d:domainName The domain against which the passed credentials should be authenticated. -generatecode or –gc Tells SoapSuds to generate source code for the proxy. -httpproxyname:proxy or -hpn:proxy The name of the proxy server that should be used to connect to the Web server to obtain the WSDL. -httpproxyport:port or -hpp:port The port number for the proxy server that should be used to connect to the Web server to obtain the WSDL. -inputassemblyfile:fileName or - ia:fileName The name of the assembly file from which to obtain type information. Do not include the extension when specifying the filename. 239 Table 8-1: Command-Line Parameters Supported by SoapSuds Switch Description -inputdirectory:directory or - id:directory The directory of the input assembly files. -inputschemafile:fileName or - is:fileName The name of the WSDL file from which to obtain type information. -nowrappedproxy or -nowp Specifies that the transparent proxy should not be wrapped within a derived version of the RemotingClientProxy class. -outputassemblyfile:fileName or -oa:fileName The name of the assembly file that will contain the generated proxy. Whenever an assembly is created, the associated source code will also be created. -outputdirectory:directory or - od:directory The directory where all output files will be saved. -outputschemafile:fileName or - os:fileName The filename of the generated WSDL or SDL document. -password:password or -p:password The password that should be used to authenticate against the server from which the WSDL or SDL document is obtained. -proxynamespace:namespace or -pn:namespace The namespace in which the resulting proxy class will reside. -sdl Specifies that SoapSuds should generate an SDL file that describes the types contained within a particular assembly. -serviceendpoint:URL or -se:URL The URL that should be placed within a generated WSDL or SDL file to describe the endpoint. -strongnamefile:fileName or - sn:fileName The file that contains the key pair that should be used to sign the generated assembly. -types:type1,assembly[,endpointUrl] [type1,assembly[,endpointUrl]] [ ] The specific types that will serve as input. -urltoschema:URL or -url:URL The URL from which the WSDL or SDL file can be obtained. -username:username or -u:username The username that should be used to authenticate against the server from which the WSDL document is obtained. -wrappedproxy or -wp Specifies that the transparent proxy should be wrapped within a derived version of the RemotingClientProxy class. -wsdl Specifies that SoapSuds should generate a WSDL f ile that describes the types contained [...]... defined by the Header class Table 8-3: Properties of the Header Class Property Description HeaderNamespace The XML namespace of the header in which the element is defined The default is http://schemas .microsoft. com/clr/soap MustUnderstand Determines whether the header must be understood by the Web service The default is true Name The name of the header Sets the name of the root element for the header... } } The preceding code displays the DirectoryForm dialog box to obtain from the user the directory to which the files should be copied Then, for each file selected by the user, the file is copied into the destination directory To place the License header in each GetFile SOAP request, I called the SetHeaders method each time just before I called the GetFile method The GetFile method can retrieve the. .. { // Validate the licensing information against // the Licensing Web service } 243 // The rest of the implementation } First the GetHeaders method is called to obtain an array of Header objects Then the array is iterated through until the Licensing header is found or the end of the array is reached Finally, if the Licensing header is found, the information is passed to the Licensing Web service You... synchronous Web request has to complete before the request is aborted The default is infinite (−1) Url The URL of the Web service’s endpoint UserAgent The value of the user agent HTTP header sent to the Web service Username The username that should be used to authenticate against the Web service By default, the HTTP channel uses the Internet settings configured on the client’s machine using Control Panel,... Determines whether the proxy will honor a redirect request sent by the server Cookies Used to access the cookies that have been sent from the server Domain The domain against which the passed credentials should be authenticated EnableCookies Specifies whether cookies will be accepted by the proxy Password The password that should be used to authenticate against the Web service Path The URL of the Web service’s... endpoints The preceding command will generate a file named 244 SoapFileShare.wsdl that contains one Web service definition with two endpoints, one for the File class and one for the Directory class One issue with the SoapSuds -generated WSDL documents is that there is no way of specifying the name of the Web service The name of the Web service defaults to the name of the first endpoint specified by the types... query string containing WSDL to the Web service endpoint However, this is not available to Remoting Web services that are hosted by processes other than IIS For these cases, you can use SoapSuds to generate a WSDL document to describe the interfaces supported by the Web service The resulting WSDL document can then be sent directly to the developer or posted on a Web site The following SoapSuds command... that you are not limited only to sharing files with other Grabber.NET peers You can create an application, potentially on other platforms, that can interact with Grabber.NET peers To implement a compatible Web service and proxy, you need access to the interface definition for the various Web services that are used by Grabber.NET As you have seen, the WSDL describing a Remoting component hosted in IIS... message formats it supports, you can use Remoting to create Web services The primary purpose of the Remoting framework is to serve as a distributed object infrastructure, not a development platform for creating and consuming Web services In a number of scenarios, Remoting is a better choice than other technologies, such as ASP.NET, if not the only choice It makes sense to build Remoting Web services. .. the current as well as the previous generic The registry must also support generic version 1 The UDDI API methods can be divided into two categories: the inquiry methods and the publishing methods The inquiry methods allow you to search and browse the directory, and the publishing methods allow you to modify the contents of the directory The messages for the inquiry methods have a root element in the . Internet Information Services (IIS). This allows the Web service to be hosted in a Web farm that takes advantage of applications that improve the manageability of the Web farm (such as Microsoft. to access a Web service. Recall that the GetFile method of the File object is responsible for sending the requested file to the client. Before the file is sent, the licensing information received. can then be called on the resulting proxy object and the proxy will forward the call to the Licensing Web service. Here is the implementation: // Validate the license info if it was sent by the