CROSS-REFERENCE TO RELATED APPLICATIONS
This application is related to the following co-pending U.S. patent applications: U.S. Pat. No. 7,103,874 entitled “MODEL-BASED MANAGEMENT OF COMPUTER SYSTEMS AND DISTRIBUTED APPLICATIONS” filed on Oct. 23, 2003; U.S. Publication No. 20050114494 entitled “SCALABLE SYNCHRONOUS AND ASYNCHRONOUS PROCESSING OF MONITORING RULES” filed on Oct. 24, 2003; U.S. Publication No. 20050091640 entitled “RULES DEFINITION LANGUAGE” filed on Oct. 24, 2003; and, U.S. Publication No. 20050091635 entitled “USE OF ATTRIBUTION TO DESCRIBE MANAGEMENT INFORMATION” filed on Oct. 23, 2003.
TECHNICAL FIELD
This invention is related to software identifiers, and more specifically, to uniform resource identifiers.
BACKGROUND OF THE INVENTION
Traditional systems management is largely ad-hoc. Application developers do not have a structured framework for managing applications and hardware while achieving high reliability. Systems can include a number of abstract and/or physical resources in the form of software and hardware. However, there is no way to identify a class or category of resources that all have common characteristics. Such a mechanism is important when trying to discover the resources. For example, an administrator may want to learn what kind of information is available about disk drives without needing to know how many drives are actually available on any given computer or the drive names in order to access such information.
What is needed is an improved mechanism for identifying system resources.
SUMMARY OF THE INVENTION
The following presents a simplified summary of the invention in order to provide a basic understanding of some aspects of the invention. This summary is not an extensive overview of the invention. It is not intended to identify key/critical elements of the invention or to delineate the scope of the invention. Its sole purpose is to present some concepts of the invention in a simplified form as a prelude to the more detailed description that is presented later.
The present invention disclosed and claimed herein, in one aspect thereof, comprises the use of Uniform Resource Identifiers (URIs) to uniquely identify classes of resources. A schema for a resource can be identified using a URI with a placeholder substituted for the resource name, effectively creating a URI template. If specificity is required, this URI template can be converted to a specific URI by replacing the placeholder with the name of a given resource or instance to get information about that particular instance. URI templates allow probes to be identified and the probe characteristics to be understood without actually retrieving the probe for a particular instance. URIs can also be used to uniquely identify an abstract or physical resource or collection of resources.
In another aspect of the invention, the underscore character (‘_’) is chosen to represent an instance placeholder within the URI.
In still another aspect thereof, the URI contains multiple placeholders. This feature is utilized when multiple keys are needed to reach out to a particular instance, such as identifying a given table within a given database.
To the accomplishment of the foregoing and related ends, certain illustrative aspects of the invention are described herein in connection with the following description and the annexed drawings. These aspects are indicative, however, of but a few of the various ways in which the principles of the invention may be employed and the present invention is intended to include all such aspects and their equivalents. Other advantages and novel features of the invention may become apparent from the following detailed description of the invention when considered in conjunction with the drawings.
BRIEF DESCRIPTION OF THE DRAWINGS
FIG. 1 illustrates a block representation of the unique identifier architecture of the present invention.
FIG. 2 illustrates a flow chart of a process for utilizing the resource class identifier of the present invention.
FIG. 3 illustrates a block diagram of a system where an instrumentation catalog is generated using attribution and the URI architecture of the present invention.
FIG. 4 illustrates a model-based management architecture that utilizes the URI architecture of the present invention.
FIG. 5 illustrates a drawing map related to describing the principal components of the model-based management architecture.
FIG. 6A illustrates blocks associated with the models component of the model-based management architecture.
FIG. 6B illustrates blocks associated with the manifest component of the model-based management architecture.
FIG. 6C illustrates a block diagram of core system APIs of the system component utilized for managing an application or service in accordance with the model-based management architecture.
FIG. 6D illustrates a block diagram of management-related APIs of the system component of the model-based management architecture.
FIG. 6E illustrates principal subcomponents of the tasks component of the model-based management architecture.
FIG. 7 illustrates a flow chart of a process of model-based management.
FIG. 8 illustrates a more detailed flow chart of a process of implementing the model-based management.
FIG. 9 illustrates a flow chart of a process of implementing desired states of the model-based management.
FIG. 10 illustrates a block diagram of a computer operable to execute the disclosed architecture.
FIG. 11 illustrates a schematic block diagram of an exemplary computing environment in accordance with the present invention.
DETAILED DESCRIPTION OF THE INVENTION
The present invention is now described with reference to the drawings, wherein like reference numerals are used to refer to like elements throughout. In the following description, for purposes of explanation, numerous specific details are set forth in order to provide a thorough understanding of the present invention. It may be evident, however, that the present invention may be practiced without these specific details. In other instances, well-known structures and devices are shown in block diagram form in order to facilitate describing the present invention.
As used in this application, the terms “component” and “system” are intended to refer to a computer-related entity, either hardware, a combination of hardware and software, software, or software in execution. For example, a component may be, but is not limited to being, a process running on a processor, a processor, an object, an executable, a thread of execution, a program, and/or a computer. By way of illustration, both an application running on a server and the server can be a component. One or more components may reside within a process and/or thread of execution and a component may be localized on one computer and/or distributed between two or more computers.
Referring now to FIG. 1, there is illustrated a block representation of the unique identifier architecture of the present invention. A system 100 typically includes a number of resources that include one or more abstract resources 102 and/or one or more physical resources 104. In the disclosed implementation, an identifier in the form of a URI (Uniform Resource Identifier) is used to map to the specific type of resource (102 or 104), class of resources 106, or collection of the resources 108. Here, a specific abstract URI 110 is mapped to the abstract resource 102, a specific physical URI 112 is mapped to the physical resource 104, a resource class URI 114 is mapped to the resource class 106, and a collection URI 116 is mapped to the collection of resources 108.
One implementation of the disclosed URI architecture includes instrumentation catalogs, which is an instrumentation description resource at the heart of a monitoring system. Protecting the capability to pre-define instrumentation separately from instances makes the deployment and authoring of monitoring rules easier, and is important in making a manageable operating system (OS). For example, the schema for disk drives can be identified by the class URI 114 with a placeholder for the actual drive name, i.e., a URI template. The instrumentation catalog can rely on URI templates to describe instrumentation without referring to specific instances. This template can be converted to a specific URI (e.g., URI 110 or URI 112) by replacing the placeholder with the name of a given drive to get information about that particular instance.
Class URI templates 114 allow probes (health management information) to be identified and characteristics understood without actually retrieving the probe for a particular instance. Class URIs use placeholders for instance identifiers. The underscore (‘_’) character is chosen to represent the placeholder within the class URI. However, any suitable non-conflicting character can be used. Additionally, class URIs can contain multiple placeholders when multiple keys are needed to get to a particular instance such as identifying a given table within a given database.
Referring now to FIG. 2, there is illustrated a flow chart of a process for utilizing the resource class identifier of the present invention. While, for purposes of simplicity of explanation, the one or more methodologies shown herein, e.g., in the form of a flow chart, are shown and described as a series of acts, it is to be understood and appreciated that the present invention is not limited by the order of acts, as some acts may, in accordance with the present invention, occur in a different order and/or concurrently with other acts from that shown and described herein. For example, those skilled in the art will understand and appreciate that a methodology could alternatively be represented as a series of interrelated states or events, such as in a state diagram. Moreover, not all illustrated acts may be required to implement a methodology in accordance with the present invention.
At 200, the user determines the available abstract and physical resources of the system. At 204, the resources are tagged according to particular classes of resources, e.g., drives. At 206, a class URI is assigned with a unique resource class representation. At 208, the resource class URI is processed to return class information about the class of resources. The process then reaches a Stop block.
The following example shows a class URI template for identifying a class of logical disk drives. The template is:
Each entry for a template in the instrumentation catalog includes information about its purpose, usage, what values should be used for placeholders, and what the returned value looks like:
|
| <Uri name=‘#System/Drive/Name=_’> |
| <Description> Returns logical drive information for the |
| specified drive </Description> |
| <UriHelp> |
| <_1>The drive letter of the logical drive in the following |
| format: [Drive letter]:\ </_1> |
| <Example> #System/Drive/Name=c:\ </Example> |
| </UriHelp> |
| <returnValue model=“xsd”> |
| <xs:schema elementFormDefault=“qualified” |
| xmlns:xs=“http://www.w3.org/2001/XMLSchema”> |
| <xs:element name=“Drive” nillable=“ |
| true” type=“Drive” /> |
| <xs:complexType name=“Drive”> |
| <xs:sequence> |
| <xs:element minOccurs=“0” max- |
| Occurs=“1” |
| name=“VolumeLabel” type=“xs:string” /> |
| <xs:element minOccurs=“1” maxOccurs=“1” |
| name=“SectorsPerCluster” type=“xs:int” /> |
| <xs:element minOccurs=“1” maxOccurs=“1” |
| name=“BytesPerSector” type=“xs:int” /> |
| <xs:element minOccurs=“1” maxOccurs=“1” |
| name=“FreeClusters” type=“xs:int” /> |
| <xs:element minOccurs=“1” maxOccurs=“1” |
| name=“TotalClusters” type=“xs:int” /> |
| <xs:element minOccurs=“1” maxOccurs=“1” |
| name=“PercentFree” type=“xs:int“ |
| <xs:element minOccurs=“0” maxOccurs=“1” |
| name=“DriveName” type=“xs:string” /> |
| </xs:sequence> |
| </xs:complexType> |
| </xs:schema> |
| </returnValue> |
| /Uri> |
|
Information about a specific drive can then be retrieved by filling in the place holder for a given drive following the instructions for the URI template in the catalog. Thus for the C drive, the following URI,
yields information consistent with the schema associated with the following URI template.
|
<VolumeLabel>NEW2</VolumeLabel> |
|
<SectorsPerCluster>8</SectorsPerCluster> |
|
<BytesPerSector>512</BytesPerSector> |
|
<FreeClusters>274278</FreeClusters> |
|
<TotalClusters>2620603</TotalClusters> |
|
<PercentFree>10</PercentFree> |
|
<DriveName>C:\</DriveName> |
The use of placeholders can be applied to other situations besides just retrieving information about instances. Place holders can also be used to pass values to methods associated with the instances. For example, the following URI template describes how to set the volume label on a drive,
|
|
|
#System/Drive/Name=_/VolumeLabel=— |
|
|
where the first underscore (‘_’) is the drive letter of the drive to be modified, and the second underscore (‘_’) is the new volume label.
As a second example, the following URI template describes how to invoke the format method on a drive:
|
|
|
#System/Drive/Name=_/Format(blocksize=_,quick=_) |
|
|
In this case, there are multiple parameters to the format method each indicated with ‘_’s.
Probes are identified using URIs within an administrator namespace. These URIs may be different than the internal development names for the component. Probes can identify instances of a type, and this is captured within the probe by using a place holder (such as ‘_’) to indicate where the instance identity must be supplied. In the decoupled case, the developer also adds a register call and an unregister call, on startup and shutdown, to notify the system that it is available to answer probes. Beyond this, there is no additional work on the part of the developer to deal with inter-process communication.
Referring now to FIG. 3, there is illustrated a block diagram of a system where an instrumentation catalog is generated using attribution and the URI architecture of the present invention. There is provided a client 300 and a web host 302 demarcated by a machine boundary 304. The client 300 includes a software client API DLL (Dynamic Link Library) 306, which is an executable program module that facilitates communication 308 (e.g., web services, denoted as WS) with the web host 302. The web host 302 includes a host client API DLL 310 that facilitates communication with a number of resident processes 314 (also called providers). The host client API 310 can interface to the respective providers 314 in a number of ways, which provider processes 314 are demarcated from the web host 302 by a demarcation line 316. The separate host providers 314 include a programming language application provider 318 (e.g., C#), a native service provider 320, and a classic provider 322. Note, however, that the application provider 318 can be any suitable programming application.
The description includes both an authoring phase and a runtime phase. During the authoring phase, an application or service provider is prepared (or authored) by attribution of the management information for use during the runtime phase. The application or service is authored using the application 318. The application 318 applies at least type information to the code according to predetermined criteria used to stipulate which areas of the code are deemed to be important for determining the health of the application. A software tool is then applied to the attributed code that sweeps the code for all attributed information and generates an instrumentation manifest 324 for that application, as indicated by a generate process 326. The instrumentation manifest 324 includes probe definitions 328. The manifest 324 is then installed into an installed applications catalog 330 that includes a collection (or catalog) of instrumentation manifests generated for the various applications and services that are installed on the host system 302. Programs interested in the health information and accessing the host 302, e.g., the client DLL 306, can further access the linking process 330 through the host client DLL 310 to obtain the instrumentation definitions 328 of all applications installed on the host system 302.
At runtime, the client DLL 306 (or consumer of health information) that wants to determine something about the health of one or more applications accesses the web host 302 using the connection protocol 308 to access the web host client DLL 310, which in turn communicates with the application 318 via a Local RPC (Local Remote Procedure Call) channel 332. The host client DLL 310 retrieves the health information and returns it to the client 306.
There are several different provider scenarios that can be addressed, which include the application provider 318 that was just described, the native service provider 320, and the classic provider 322. The native service 320 is written in unmanaged code (or native code). Thus, managed code needs to be “wrapped” around the native code such that the native service 320 can be suitable for management in accordance with the present invention. The native service 320 communicates with the host DLL 310 using WIN32 calls 334 over an IPC (InterProcess Communication) link 337. In this case, the health information of the native service 320 is instrumented by attributing the managed code wrapper 336 facilitating communication of the health information to the client 306.
It is to be appreciated that there can be the classic provider 322 where attribution cannot be obtained in accordance with the present invention. Here, the type of data that already describes the health of that provider 322 or of one or more internal processes P1, P2, and P3, is available to the host client DLL 310 via an adapter 338. The adapter 338 facilitates communication to the management provider 322 via a COM (Common Object Model) link 340. The health information of the classic provider 322 is provided via a file formatting process 342 (e.g., managed object format (MOF)) that is converted into an instrumentation manifest according to a convert process 344. Thus, there is no need to attribute the code and develop a manifest in accordance with the present invention. Extensions of the MOF definitions can be added to allow customization of the MOF to conversion of the instrumentation manifest.
A running application table 346 is provided with a table of ports that map to applications of the host system 302, such that a consumer of health information can access the table 346 and further access the desired application(s). When an application starts, registration is performed via dedicated registration API(s) implemented in a DLL 348, and from any part of the application 318. It registers with the table 346 such that by accessing the table 346, at any given time, it is known what instrumented applications are running on the system.
The application 318 also includes an internal state component 350 that is whatever health information that the application has to provide, e.g., statistics about successes or failures. The internal state data 350 is the data used to generated the instrumentation manifest 324 via the generation process 326. Note that attribution can also include operations that are to be performed and runtime states that can be configured, in addition to the health data.
The managed wrapper 336 used for the native service 320 can be managed code classes used to make WIN32 calls 334 in its implementation.
The following class exposes a registry key as an in-process provider:
|
[Folder(Uri=“#System/RegKey”)] |
|
public class RegKey |
|
{ |
|
[Probe(Uri=“/name=_”)] |
|
public static String GetKey(String name) |
|
{ |
|
Registry.CurrentUser.OpenSubKey(“MyApp”).GetValue ( name ) ; |
which exposes one URI: #System/RegKey/name=
—
Instrumentation
Instrumentation is a technique whereby an administrator can surface or expose information that might be useful for tools trying to manage an application. When the application has been instrumented, objects and events can be discovered, monitored, and configured from diverse data sources. The source of the information might be a piece of hardware, operating system, or software application. The information provided by the data source is known as instrumentation. The purpose of instrumentation is very similar to the purpose served by the instrument panel of a car dashboard. Car instrumentation includes gauges, lights and indicators that allow for monitoring information about various components (such as the fuel gauge) when various events occur (such as the open door alarm). All this instrumentation allows decisions to be made on how the car is driven and maintained. Computer components that provided instrumentation allow management software to diagnose and correct problems in an enterprise computing environment.
Other instrumentation technologies exist, such as tracing or log files, restrict applications to providing a raw block of unstructured diagnostic information (such as a simple string). To facilitate exposing application or service information, a set of classes are used that describe the information that they will provide through instrumentation. These class definitions are published, and are accessible to management tools. The class definitions are available at any time after the application is installed, and not just when the application is running. At run time, the application provides the actual data described by the classes. The concept of class definitions is at the heart of programming in managed code.
Application information for management is mostly exposed by making declarations—no extensive extra coding is required. The developer marks the objects as manageable by using the attribution capabilities and defines how the objects map into the management schema.
Using URIs and Attribution to Develop Instrumentation
Attribution of types allow for a very convenient way of adding additional semantics to that particular type. Following is a disclosed attribution scheme.
Folder Attribute: The developer uses the Folder attribute to specify that the type is instrumented. This attribute is required on all types that are instrumented. Types that can be instrumented are Classes and Structures (i.e. value types)
Folder Attribute and Default URIs: The default URI for a Probe type is defined by using namespace hierarchy where the type is defined. If an explicit URI has been set via the Folder attribute, it must be a full URI, since can override the namespace hierarchy.
Folder Attribute and Custom URIs: It is possible to override the default URI naming mechanism by setting the URI property in the Folder attribute declaration. It is possible to specify an empty string as the folder URI, in which case the member URI(s) are moved up one level. Following is a process probe with no folder URI specified.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder(Uri=“”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
NavigationTypes.EndPoint)] |
|
public int Id { get { ; } } |
|
[Probe(Uri=“ProcessName”, |
|
NavigationTypes.EndPoint)] |
|
public string Name { get { ; } } |
Folder Attribute and Member Separators: By default, URI fragments are separated by the ‘/’ to form the complete URI:
|
|
|
#System/OS/Windows/Process/ProcessId=0 |
|
|
The standard separator can be overridden at the type level by setting the Separator property in the Folder attribute declaration.
Probe Attribute: The developer uses the Probe attribute to indicate that a member of a type (decorated with the Folder attribute) is a Probe.
Probe Attribute and Default URIs: By default, the parameter-less version of the attribute uses a member name to which the attribute is defined as the URI fragment. Following is a sample defaulted type locator URI for the process class.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder] |
|
public class Process |
|
{ |
|
public Process ( int pid ) { } |
The URI(s) for the Probe type is:
|
|
|
System/Diagnostics/Process/pid=— |
|
|
The following table shows which members are valid instrumentation members, as well as the default naming for each.
|
|
|
Member Type |
Default Uri |
|
|
|
Field |
<FieldName> |
|
Property |
Get method: <PropertyName>, Set method: |
|
|
<PropertyName>=— |
|
Method |
<MethodName>(<parameterName>=_, |
|
|
<parameterName>=_,...) |
|
*Constructor |
<parameterName>=_, <parameterName>= ,... |
|
|
|
*Note that even though a constructor is a method, the default URI generation is special cased to provide more logical URI(s). |
A more complete example of how to default URI(s) on a Probe type is the following:
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder] |
|
public class Process |
|
{ |
|
public Process ( int pid ) { } |
|
[Probe] |
|
public int Id { get { ; } } |
In this case, the following URI(s) are automatically generated:
|
|
|
#System/Diagnostics/Process/pid=— |
|
#System/Diagnostics/Process/pid=_/Id |
|
|
Probe Attribute and Custom URIs: It is possible to override the default URI naming mechanism by using an overloaded form of the Probe attribute that takes a string that specifies the URI. Note that the URI(s) specified at the member level are relative to the URI at the type level.
Probe Attribute and Types/Navigation: The Probe attribute provides two properties that allow the developer to specify how that URI fragment should be interpreted. The NavigationTypes enumeration provides EndPoint and Navigational options.
Depending on the characteristics of the member that is decorated the developer can either rely on the default setting or choose from one of the above. The default settings depend on the return type of member that the attribute is applied to:
|
|
|
Return Type |
Default Setting |
|
|
|
Primitive |
EndPoint |
|
Complex |
Navigation/EndPoint |
|
|
Types are considered primitive, if they fall into one of the following: a definition of primitive type, and a string reference. Complex types are non-primitive types. A more precise definition of the modes is given below.
If the URI results in the return of another class and this class in turn is instrumented, the mode of this URI fragment is navigational. The member merely facilitates moving from one level in the class hierarchy to another. A more complex version of the process class with navigational characteristics is provided below.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
public Thread[] Threads { get { return threads; } } |
|
} |
|
[Folder] |
|
public class Thread |
|
{ |
|
internal Thread ( ) { } |
|
[Probe (Uri=“ThreadId”, |
|
NavigationType=NavigationTypes.EndPoint )] |
|
public int Id { get { return id; } } |
The version above has been extended to include a property that returns all threads within a process, each thread being represented by the Thread class. Simply by attributing the Threads property with the Probe attribute, this property becomes a navigational aid from the Process class to the Thread class. Note that a navigation type is not specified, since the default is Navigation. Examining the following URI makes it clear how the Navigational feature works:
|
|
|
#System/OS/Windows/Process/ProcessId=0/Threads/ThreadId=12 |
|
|
The type locator URI is #System/OS/Windows/Process and allows the infrastructure to locate the type associated with this URI. The /ProcessId=0 is mapped to constructor of the Process class (by matching it with ProcessId=_). 0 is passed into the constructor which acts as a navigational method (navigating to an instance of the process class). The /Threads is mapped to the public Threads Property whose getter is invoked and returns a collection of Thread instances. The /ThreadId is mapped to the public Id property of the Thread class and its getter is invoked for each instance in the collection
Note that in the Thread class itself that the constructor is not marked with the Folder attribute. The reason is that the Thread constructor is marked internal and is only creatable by the Process class. By not exposing construction methods it can be avoided to define URI(s) to a type that is not directly accessible.
An endpoint indicates a member that simply returns a non-Probe result, thereby indicating the end of the URI. In previous examples these are, for example, ProcessId and ThreadId properties.
In addition to the NavigationTypes enumeration, one more enumeration exists that allows the developer to specify how the URI fragment should be interpreted. The ProbeTypes enumeration defines the following members: Get, Set, and Method.
Depending on the characteristics of the member that is decorated the developer can either rely on the default setting or choose from one of the above. The default settings depend on the member to which the attribute is applied
|
|
|
MemberInfo |
Default Value |
|
|
|
Method |
Method |
|
Property |
Get or Set or both |
|
Field |
Get| Set |
|
|
ProbeTypes.Method: Indicates a method execution that has side affects (such as a Kill method on the Process class). ProbeTypes. Get: Indicates retrieval of value(s). By default fields and properties are getters. ProbeTypes.Set: Indicates setting of value(s). By default fields and properties are setters.
Probe Attribute and Results: An even more complicated scenario arises when the navigational member returns a more generic form of objects (such as the object type). This can occur if the member is implemented based on a generic interface definition. As an illustration of this, the Process class may have been implemented as below, where Process class implements a navigational member which returns a generic object collection.
|
|
|
namespace System.Diagnostics |
|
{ |
|
public interface ISomeInterface |
|
{ |
|
object[ ] SomeMethod { get ; } |
|
} |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process : ISomeInterface |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
| [Probe(Uri=“Threads”,NavigationType=NavigationTypes.Navigational, |
| ResultType=typeof(Thread)] |
|
public object[ ] SomeMethod { get { return (object[ ]) |
|
internal Thread ( ) { } |
|
[Probe (Uri=“TreadId”, |
| NavigationType=NavigationTypes.EndPoint )] |
|
public int Id { get { return id; } } |
In situations like this, some more information is required in order to successfully navigate to the Thread class. More specifically, the developer will have to set the ResultType property in the Instrumentation attribute declaration which allows them to specify the type of objects that are contained in the collection or array.
It should be noted that Probe members can return primitive, as well as complex types. The schema of a return type for a Probe member will be stored in the manifest.
Note that the schema for a return type includes all public members unless the public member is attributed with the Probe attribute and is a complex type in which case a link to the URI is returned instead. If a developer chooses to ignore members, they can do so via the XMLIgnore attribute.
The ability to reach classes via URI(s) provides a uniform way of accessing code, as well as an implementation independent URI structure. In order to provide the capability of retrieving objects based on URI syntax, the infrastructure has to know, in detail, the construction semantics/syntax of that Probe type. Instrumentation supports custom object creation (i.e., via a separate dedicated method) and also supports three of the main construction mechanisms.
There are essentially two main programming paradigms for object construction: construction via constructor; and construction via static methods. The code illustration behind this discussion will be a slimmed down version of the process class altered slightly to illustrate each of the programming models. Following is a sample of process class using a constructor with one parameter as the object creation mechanism.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder (Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe (Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
[Probe(Uri=“ProcessId”,NavigationType=NavigationTypes.EndPo |
|
public int Id { get { ; } } |
|
[Probe(Uri=“ProcessName”,NavigationType=NavigationTypes.End |
|
public string Name { get { ; } } |
The following sample URI to this object constructs a process object that represents the OS process with process id=0.
|
|
|
#System/OS/Windows/Process/ProcessId=0 |
|
|
Another sample URI to this object constructs a process object that represents the OS process with process id=12 and then returns the ProcessName property.
|
|
|
#System/OS/Windows/Process/ProcessId=0/ProcessName |
|
|
Following is an example of a slightly modified process class where the constructor takes two parameters.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder (Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe (Uri=“ProcessId=_,ProcessName=_”) |
|
public Process (int pid, |
|
{ |
|
} |
|
[Probe (Uri=“ProcessId”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public int Id { get { ; } } |
|
[Probe (Uri=“ProcessName”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public string Name { get { ; } } |
If a method takes multiple parameters, they must be specified in the correct order (i.e., they are regarded as positional parameters). From the code above, a URI that constructs a process object that represents the OS process with process id=12 and name svchost would look like following:
|
| #System/OS/Windows/Process/ProcessId=12,ProcessName=svchost |
|
A URI that constructs a process object that represents the OS process with process id=12 and name svchost and then returns the ProcessId property would look like following:
|
|
|
#System/OS/Windows/Process/ProcessId=12,ProcessName=svchost |
A URI that constructs a process object that represents the OS process with process id=12 and name svchost and then returns the ProcessName property would look like following:
|
|
|
#System/OS/Windows/Process/ProcessId=0,ProcessName=svchost/ |
Note that if the defaulted Probe attribute is used with a method that takes multiple parameters, the auto-generated URI would look like:
|
|
|
#System/Diagnostics/Process/pid=_,pName=— |
|
|
There may be cases where there are a fairly large number of constructors. As an illustration of this scenario, consider the Process class and redesign it to have one required identifier (Id) and three non-identifiers (username, password, machine which all default to current user and local machine). The code may look like following:
|
|
|
namespace System.Diagnostics |
|
[Folder (Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe (Uri=“ProcessId=_”)] |
|
public Process ( int pid ) |
|
{ |
|
} |
|
[Probe |
|
(Uri=“ProcessId=_,UserName=_, Password=_,Machine=_”)] |
|
public Process ( int pid, |
|
string user, |
|
string password, |
|
string machine) |
|
{ |
|
} |
|
[Probe (Uri=“ProcessId=_,Machine=_”)] |
|
public Process ( int pid, |
|
{ |
|
} |
|
[Probe (Uri=“UserName”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public string UserName { get { ; } } |
|
[Probe (Uri=“Machine”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public string MachineName { get { ; } } |
|
[Probe (Uri=“ProcessId”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public int Id { get { ; } } |
The first constructor uses the process id as the parameter and assumes current user and local machine. The second constructor requires: process id, user name, password and machine name to be specified. The third constructor assumes current user and allows you to specify a machine name and pid.
In cases where there are a number of overloaded constructors, all relevant constructor parameters need to be attributed. Each constructor that should be used is attributed with the Probe attribute. The following sample URI constructs a process object that represents the OS process with process id=12 using the first constructor.
|
|
|
#System/OS/Windows/Process/ProcessId=12 |
|
|
The following sample URI constructs a process object that represents the OS process with process id=12 on machine marioh-dev using the third constructor.
|
|
|
#System/OS/Windows/Process/ProcessId=12,Machine=marioh-dev |
|
|
The following sample URI constructs a process object that represents the OS process with process id=12 on machine marioh-dev with user marioh and password (password). This will use the second constructor.
|
|
|
#System/OS/Windows/Process/ProcessId=12,UserName=ntdev\mari |
| oh,Password=xyz,Machine=marioh-dev |
|
Following is a sample of process class using a static method as means of construction.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
private Process ( ) { } |
|
[Probe(Uri=“ProcessId=_”)] |
|
public static Process GetById ( int pid ) ; |
|
[Probe(Uri=“ProcessId”), |
|
NavigationType=NavigationTypes.EndPoint] |
|
public int Id { get { ; } } |
|
[Probe(Uri=“ProcessName” , |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public string Name { get { ; } } |
The above code illustrates another common programming model, whereby the construction of the object is handled by a static method (i.e., GetById in example above). In this case, the static method itself is marked with the Probe attribute. The following sample URI constructs a process object that represents the OS process with process id=12.
|
|
|
#System/OS/Windows/Process/ProcessId=12 |
|
|
The following sample URI constructs a process object that represents the OS process with process id=12 and then accesses the ProcessId property.
|
|
|
#System/OS/Windows/Process/ProcessId=12/ProcessId |
|
|
The following sample URI constructs a process object that represents the OS process with process id=12 and then accesses the ProcessName property.
|
|
|
#System/OS/Windows/Process/ProcessId=12/ProcessName |
|
|
To summarize object construction, if object construction is via a constructor, the following rules apply: the constructor is marked with the Probe attribute; if the constructor takes two or more parameters, they are always positional; parameter types are limited to primitive types; multiple constructors can be decorated with the Probe attribute; Probe constructors cannot use the ResultType property of the Probe attribute; mode property is set to the default NavigationTypes.Navigational; specify DocName and DocPath properties of the Probe attribute for documentation purposes; constructor parameters is in-only; and the constructor is public.
If object construction is via a static method, the following rules apply: the static method is marked with the Probe attribute; if the static method takes two or more parameters, they are always positional; parameter types are limited to primitive types; multiple static construction methods can be decorated with the Probe attribute; the ResultType property of the Probe attribute can be used to indicate a different return type; method parameters can be in-only; method is public; mode property is set the default, which is NavigationTypes.Navigational; and specify DocName and DocPath properties of the Probe attribute for documentation purposes.
While enumerating data from a particular type is as simple as attaching an Probe attribute to the enumeration method, sometimes it is very convenient to be able to quickly find the URI that corresponds to the enumeration. Imagine looking through 200 URIs for a particular type just to find the method that enumerates objects of this type. The operation is common enough to warrant the introduction of an Enumerator attribute that specifies clearly which URI corresponds to the enumeration.
There are essentially two models for enumerations in classes. One form of enumeration falls into the scope of the Enumerator attribute, where enumeration of all instances of the class (such as all running processes) take the form of static methods in the class. Another form, is enumeration of a subset of data contained within the class (such as Threads within a Process class). These types of enumerations typically take the form of methods or properties of an instance of the class. This form of enumeration may fall into the scope of the Enumerator attribute depending on the overall design of the class.
The following code shows decoration of a process class with static GetProcesses method returning a collection of running processes.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
InstrumentationMode.EndPoint)] |
|
public int Id { get { ; } } |
|
[Probe(Uri=“*”)] |
|
[Enumerator] |
|
public static Process[ ] GetProcesses ( ) { } |
The static GetProcesses method returns a collection of all currently running processes. By simply decorating the method with the Probe attribute (and associated URI), as well as the Enumerator attribute, it can be ensured that this method is invoked properly when referenced in a URI. The following sample URI returns a collection of all running processes.
|
|
|
#System/OS/Windows/Process/* |
|
|
The following sample URI returns a collection of all running processes and gets the process object that represents the OS process with process id=12.
|
|
|
#System/OS/Windows/Process/*/ProcessId=12 |
|
|
If the static method or property that the Enumerator attribute is applied to does not specify a type (using an overload of the attribute) it is assumed that the returned collection contains instances of the parent type. In the above sample code, it is assumed that the collection returns instances of the Process class (although it may appear obvious, in certain cases, the returned collection may contain instances of ‘object’ such as in interface implementations.
Most of the time, standard collections will be used and post-filtering is required to get specific instances. In cases where the returned collection is a specialized collection containing an Identifier, e.g., for the instances in the collection, instance can be more effectively reached. Using the same example as above, the Threads property of the process class may return a ThreadCollection that contains an indexer that serves as the Identifier. Following is an example where the returned ThreadCollection implements a fast retrieval mechanism via the indexer.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
[Probe(Uri=“ProcessId”, |
|
InstrumentationMode.EndPoint)] |
|
public int Id { get { ; } } |
|
[Probe(Uri=″Threads″, |
|
NavigationTypes.Navigational, typeof(Thread))] |
|
public ThreadCollection Threads { get { } } |
|
internal Thread ( ) { } |
|
[Probe(Uri=“CPUTime”, |
|
InstrumentationMode.EndPoint)] |
|
public int CPUTime { get { } } |
|
} |
|
public class ThreadCollection : ICollection |
|
{ |
|
// |
|
// Standard ICollection implementation |
|
// |
|
... |
|
... |
|
... |
|
[Probe (Uri=“ThreadId=_”)] |
|
public Thread this[int tId] { get { } } |
If the returned collection implements a member for efficient retrieval it can be marked with the Probe attribute. The member is not limited to an indexer.
If the return type is a strongly typed array or collection that is not instrumented, a mechanism exists to do post filtering. In order for this to work, the type that is contained in the array/collection is instrumented and contains a member (property or field) that is marked with the Key attribute. If the contained type does not contain any members with the Key attribute, URI generation will stop at this point, since there is no way to figure out where to post filter. Below is an example of this scenario whereby the Process class returns a Thread array, the returned ThreadCollection implements a fast retrieval mechanism via the indexer.
|
|
|
namespace System.Diagnostics |
|
{ |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
[Probe(Uri=“ProcessId”)] |
|
public int Id { get { ; } } |
|
[Probe(Uri=″Threads″,ResultType=typeof(Thread))] |
|
public Thread [ ] Threads { get { } } |
|
internal Thread ( ) { } |
|
[Probe] |
|
[Key] |
|
public int Id { get { } } |
|
[Probe(Uri=“CPUTime”)] |
|
public int CPUTime { get { } } |
An examples of a valid URI for the above is the following:
|
|
|
#System/Os/Windows/Process/ProcessId=_/Threads/Id=— |
|
|
Note that while the static portion of the URI is case insensitive, the variable portion may not be and it is up to the instrumentation developer to choose how to handle the case sensitivity issue for their particular provider.
In summary, if a member exists that represents an enumeration of the data space, the following rules apply: the member is be decorated with the Probe attribute; the member specifies a return type (using ResultType property) unless the return type is a strongly typed array; the returned type implements IEnumerable or an array; if the returned collection contains a member that can retrieve a particular instance efficiently, it is marked with the Probe attribute; the member can be decorated with the Enumerator attribute to clearly indicate its intentions; any member parameters are primitive types; any member parameters are in-only; the member can be (1) Method (2) Property (3) Field; and specify DocName and DocPath properties of the Probe attribute for documentation purposes.
Typically, classes support methods that correspond to a limited form of queries. The Process class in System.Diagnostics exposes a method called GetProcessesByName which is equivalent to SELECT*FROM PROCESS WHERE NAME=<value>. This form of “stored procedure” can be marked with the Probe attribute with an associated URI, as illustrated in the following code.
|
|
|
namespace System.Diagnostics |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
[Probe(Uri=“ProcessId”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public int Id { get { ; } } |
|
[Probe(Uri=“ProcessName”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public string Name { get { ; } } |
|
[Probe(Uri=″ProcessName=_″)] |
|
public static Process [ ] GetProcessesByName |
|
( string processName ){ } |
The following URI returns all process objects that represents the OS processes with process name=SVCHOST.
|
|
|
#System/OS/Windows/Process/ProcessName=SVCHOST] |
|
|
If a class is willing to handle queries in its entirety, the method handling the query can be attributed with the Probe attribute and take one variable as part of the URI fragment, representing the query, as in the following example code, where Process class implements a query method.
|
|
|
namespace System.Diagnostics |
|
[Folder(Uri=“#System/OS/Windows/Process”)] |
|
public class Process |
|
{ |
|
[Probe(Uri=“ProcessId=_”)] |
|
public Process ( int pid ) { } |
|
[Probe(Uri=“ProcessId”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public int Id { get { ; } } |
|
[Probe(Uri=“ProcessName”, |
|
NavigationType=NavigationTypes.EndPoint)] |
|
public string Name { get { ; } } |
|
[Probe(Uri=″ProcessName=_″)] |
|
public static Process [ ] GetProcessesByName |
|
( string processName ){ } |
|
[Probe(Uri=“GetByQuery=_”)] |
|
[Query(“WQL”)] |
|
public static Process [ ] GetByQuery ( string |
A sample URI follows that returns a collection of process instances that satisfy the query. Note that the query method takes a string as input parameter.
|
|
|
#System/OS/Windows/Process/GetByQuery=SELECT * |
|
FROM PROCESS |
In summary, methods corresponding to queries are attributed with the Probe attribute. If a class wants to support queries in full, it attributes the method with the Probe attribute, the method signature takes a string parameter representing the query, the type is sufficiently described (including what query language), the method is attributed with the Query attribute specifying the query language it supports, and query methods can be static as well as non-static.
All methods exposed as Probe are marked as such with the ProbeTypes.Method. The definition of a method execution is that is has side-effects. An example of this would be the Kill method on the Process class. Methods are marked so that administrators have a clear understanding that execution of these methods result in a state change. Consider a slightly altered Process class that implements a kill process.