This chapter describes how to write a provider program, and includes the following topics:
For detailed information on the WBEM provider APIs in javax.wbem.provider, see file:/usr/sadm/lib/wbem/doc/index.html.
Providers are special classes that communicate with managed resources, such as disk drives and CPUs, to access data. The providers then forward the data to the CIM Object Manager (CIMOM), the primary WBEM agent that coordinates Solaris WBEM Services, for integration and interpretation. These providers can relieve the CIMOM by assuming the task of managing distinct subsets of WBEM resources. Providers use the javax.wbem.provider API to transfer this data. When the CIMOM receives a request from an application for data that is not available in the CIM Object Manager Repository, the CIMOM forwards the request, using the provider interfaces, to the appropriate provider.
Solaris software providers exist for a variety of areas:
Users
Groups
Aliases
Roles
File systems
Disks
Processes
The cron tool
Network configuration
Product registry
Device and system performance monitoring
Providers create, modify, and delete instances rather than classes, which serve as templates for the instances. Instances can exist in persistent storage or be used dynamically.
Although providers have their own process and memory, providers perform work delegated by the CIMOM. The CIMOM must know the location of each provider in order to coordinate WBEM. You inform the CIMOM about new or modified providers by including those providers in a MOF file. A MOF file defines the classes and instances that a provider supports. You register a MOF file using the mofcomp(1M) command.
Providers perform the following tasks:
Provide data to management applications – When a management application requests data about a managed resource that is not available in the CIM Object Manager Repository, the CIMOM forwards the request to a provider. The provider accesses the data from the managed resource and passes the data back to the CIMOM. If the data received from a managed resource is in a native format such as C code, the provider maps the data to Java CIM classes prior to passing the data to the CIMOM.
Control management resources – When a management application sends data to the CIMOM to control a managed resource, the CIMOM passes the data to the appropriate provider. If the managed resource requires data in a native format, the provider maps the CIM classes to the resource's native format prior to passing the data along.
Providers must reside on the same machine as the CIMOM.
Providers can retrieve data from the following sources:
Non-persistent data – Variables that are local to the provider class that exist only when the provider's methods are run.
Persistent memory that is local to the provider – Used by creating global variables in the provider class. This provider memory is erased when the CIMOM is stopped and restarted.
CIM Object Manager Repository – This persistent memory is erased when Solaris WBEM Services software is uninstalled. The provider must use CIMOM handles and an internal provider to access this memory through the CIMOM.
Files and databases maintained by the provider, or dynamic data – Providers can generate data dynamically by retrieving data from a system. For example, a provider can make a system call to retrieve the number of processes currently running.
Providers are categorized according to the types of requests the providers handle. Client programs communicate with the CIMOM and access WBEM data through the client API. The CIMOM maps the provider methods to the corresponding client methods in the client API. However, the argument lists and return values of corresponding methods might differ. See file:/usr/sadm/lib/wbem/doc/index.html.
If a provider stores data in the CIM Object Manager Repository, then the provider accesses the Repository using handles to the CIMOM. These handles call the methods of the client API. See Implementing the Provider Interfaces.
If a provider needs to create instances or associations in the CIM Object Manager Repository, then it uses an internal provider. The provider calls methods of Instance or Associator Providers that are internal to WBEM. See Implementing the Provider Interfaces.
Ensure that your argument list and return type are correct for the method and class used.
The types of providers in the Solaris WBEM SDK are shown in the following table.
Table 6–1 Provider Types
Type |
Class Name |
Description |
---|---|---|
Instance |
CIMInstanceProvider |
Supply dynamic instances of a given class, and support instance retrieval, enumeration, modification, and deletion |
Method |
MethodProvider |
Supply methods of one or more classes |
Associator |
CIMAssociatorProvider |
Supply instances of dynamic association classes |
Indication |
EventProvider |
Handle indications of CIM events |
Authorizable |
None |
A marker interface that indicates to the CIMOM that the provider does its own authorization check |
A single provider can act as one or more of the provider types by registering and implementing the relevant methods.
You can include providers in a single Java class file or store each provider in a separate class. The provider name identifies the Java class that serves as the provider for the class. Currently, the CIMOM supports only providers that are written in the Java language.
Provider and class names must follow these rules:
The class name must be a valid CIM class. The name must contain a prefix of characters, followed by an underscore, followed by more characters.
For example, green_apples and red_apples are valid CIM class names, whereas apples, apples_, and _apples are not.
The provider name that is specified in the MOF file must match the name of the provider class file.
For example, SimpleCIMInstanceProvider is the provider and Ex_SimpleCIMInstanceProvider is the class.
You must prepend “java:” to every provider qualifier to notify the CIMOM that the provider is written in the Java language.
Follow standard Java class and package naming conventions to create your provider names. The prefix of a package name is written in lowercase ASCII letters and must be one of the top-level domain names (com, edu, gov, mil, net, org), or one of the English two-letter country codes specified in ISO Standards 3166, 1981.
Subsequent components of the package name can vary according to your organization's internal naming conventions. Such conventions might specify that certain directory name components are division, department, project, machine, or login names. For example, the provider name java:com.sun.wbem.cimom indicates the following information:
java: – Language used to write the provider
com – Top-level domain name
sun – Company name
wbem – Product name
cimom – Type of class files that implement the CIMOM
When you write a provider, you must determine the interfaces that your provider supports. You must implement all the methods of each interface that your provider supports. In addition, every provider must implement the CIMProvider interface, which has two methods:
initialize(CIMOMHandle cimom) – If your provider stores data in the CIM Object Manager Repository, it must assign the passed CIMOM handle to the CIMOM handle that it will use to contact the CIMOM. These requirements are illustrated in the following example.
private CIMOMHandle cimom = null; ... public void initialize(CIMOMHandle cimom) throws CIMException { this.cimom = (CIMOMHandle) cimom;
Before the provider can create instances or manipulate associations in the CIM Object Manager Repository, the provider must first cast the passed CIMOM handle to the subclass ProviderCIMOMHandle. Then the provider must fetch an internal instance or association provider. The following example illustrates how to perform these tasks.
private ProviderCIMOMHandle cimom = null; private CIMAssociatorProvider ap = null; ... public void initialize(CIMOMHandle cimom) throws CIMException { this.cimom = (ProviderCIMOMHandle) cimom; ap = pcimom.getInternalProvider();
The initialize command automatically runs each time a provider is initialized after the CIMOM restarts.
cleanup – Currently acts as a placeholder.
This following sample code implements the enumerateInstances and getInstance methods of the Ex_SimpleCIMInstanceProvider class. For brevity, the implementations of the deleteInstance, createInstance, setInstance, and execQuery methods in this example just throw a CIMException.
For information on implementing the execQuery method, see Parsing Queries.
/* * "@(#)SimpleCIMInstanceProvider.java" */ import javax.wbem.cim.*; import javax.wbem.client.*; import javax.wbem.provider.CIMProvider; import javax.wbem.provider.CIMInstanceProvider; import javax.wbem.provider.MethodProvider; import java.util.*; import java.io.*; public class SimpleCIMInstanceProvider implements CIMInstanceProvider { static int loop = 0; public void initialize(CIMOMHandle cimom) throws CIMException { } public void cleanup() throws CIMException { } public CIMObjectPath[] enumerateInstanceNames(CIMObjectPath op, CIMClass cc) throws CIMException { return null; } /* * enumerateInstances: * The entire instances and not just the names are returned. */ public CIMInstance[] enumerateInstances(CIMObjectPath op, boolean localOnly,boolean includeQualifiers, boolean includeClassOrigin,String[] propertyList, CIMClass cc) throws CIMException { if (op.getObjectName().equalsIgnoreCase ("Ex_SimpleCIMInstanceProvider")) { Vector instances = new Vector(); CIMInstance ci = cc.newInstance(); if (loop == 0){ ci.setProperty("First", new CIMValue("red")); ci.setProperty("Last", new CIMValue("apple")); // only include the properties that were requested ci = ci.filterProperties(propertyList, includeQualifier, includeClassOrigin); instances.addElement(ci); loop += 1; } else { ci.setProperty("First", new CIMValue("red")); ci.setProperty("Last", new CIMValue("apple")); // only include the requested properties ci = ci.filterProperties(propertyList, includeQualifier, includeClassOrigin); instances.addElement(ci); ci = cc.newInstance(); ci.setProperty("First", new CIMValue("green")); ci.setProperty("Last", new CIMValue("apple")); // only include the requested properties ci = ci.filterProperties(propertyList, includeQualifier, includeClassOrigin); instances.addElement(ci); } return (CIMInstance[])instances.toArray(); } throw new CIMException(CIM_ERR_INVALID_CLASS); } public CIMInstance getInstance(CIMObjectPath op, boolean localOnly, boolean includeQualifiers, boolean includeClassOrigin, String[] propertyList, CIMClass cc) throws CIMException { if (op.getObjectName().equalsIgnoreCase ("Ex_SimpleCIMInstanceProvider")) { CIMInstance ci = cc.newInstance(); // we need to get the keys from the passed in object path, // this will uniquely identify the instance we want to get java.util.Vector keys = cop.getKeys(); // Since this is a contrived example we will simply place // the keys into the instance and be done. ci.setProperties(keys); // if we had other non-key properties we should add them //here. // only include the properties that were requested ci = ci.filterProperties(propertyList, includeQualifiers, includeClassOrigin); return ci; } throw new CIMException(CIM_ERR_INVALID_CLASS); } public CIMInstance[] execQuery(CIMObjectPath op, \ String query, String ql, CIMClass cc) throws CIMException { throw(new CIMException(CIMException.CIM_ERR_NOT_SUPPORTED)); } public void setInstance(CIMObjectPath op, CIMInstance ci, boolean includeQualifiers, String[] propertyList) throws CIMException { throw(new CIMException(CIMException.CIM_ERR_NOT_SUPPORTED)); } public CIMObjectPath createInstance(CIMObjectPath op, CIMInstance ci) throws CIMException { throw(new CIMException( CIMException.CIM_ERR_NOT_SUPPORTED)); } public void deleteInstance(CIMObjectPath cp) throws CIMException { throw(new CIMException(CIMException.CIM_ERR_NOT_SUPPORTED)); } }
The method invokeMethod is the only way that a client program can call the methods of Solaris WBEM providers. This condition is true for both providers that are built in or that are added by developers.
Built–in – The “platform-free” CIM_* providers or the Solaris_* providers specific to the Solaris platform.
Added by developers – For example, a method provider, whether the provider supplies provider or non-WBEM methods, is created by implementing the MethodProvider interface.
The following sample code creates the Solaris_ComputerSystem provider class that routes requests from the CIMOM to one or more specialized providers. These providers handle requests for dynamic data for a particular type of managed object. For example, the Solaris_Package provider handles requests to execute methods in the Solaris_Package class.
The method provider implements a single method, invokeMethod. This method calls the appropriate provider to either reboot a system, shut down a system, or delete a serial port.
... public class Solaris_ComputerSystem implements MethodProvider { ProviderCIMOMHandle pch = null; public void initialize(CIMOMHandle ch) throws CIMException { pch = (ProviderCIMOMHandle)ch; } public void cleanup() throws CIMException { } public CIMValue invokeMethod(CIMObjectPath op, String methodName, Vector inParams, Vector outParams) throws CIMException { if (op.getObjectName().equalsIgnoreCase("solaris_computersystem")) { if (methodName.equalsIgnoreCase("reboot")) { // call helper function, not shown here return new CIMValue(rebootSystem()); } if (methodName.equalsIgnoreCase("shutdown")) { // call helper function, not shown here return new CIMValue(shutdownSystem()); } } if (op.getObjectName().equalsIgnoreCase("solaris_serialport")) { if (methodName.equalsIgnoreCase("disableportservice")) { // call helper function, not shown here return new CIMValue(deletePort(op)); } } // error if we get here throw new CIMException(CIMException.CIM_ERR_NOT_SUPPORTED, "The requested function does not exist"); } // helper functions would be defined below ... }
The objectName argument in each of the association methods called by your client program. In other words, CIMObjectPath must be the object path of an instance, not a class.
Unless the CIMOM can locate the object path of an instance, it assumes that the client wants the class definitions of the association that are in the CIM Object Manager Repository. The class definition of the association includes the templates from which the association's member instances are derived. Therefore, the CIMOM uses the client API's association method and not the provider's association method.
The most important part of designing and coding an association is the association class. Your association will only be as complex as the contents of the association class. The number of members of the association equals the number of references in the association class. Roles can be used to model more complicated associations. The following examples show some sample association classes.
An asymmetrical pair relationship, such as a one-to-one relationship between a teacher and a student, with two roles defined (teaches and taughtby):
class TeacherStudent { Teacher REF teaches; Student REF taughtby; };
A one-to-many relationship:
class Classroom { Teacher REF teaches; Student1 REF taughtby; Student2 REF taughtby; Student3 REF taughtby; Student4 REF taughtby; };
A many-to-many relationship:
class TeachingAssistants { Assistant1 REF assists; Assistant2 REF assists; Student1 REF assistedby; Student2 REF assistedby; Student3 REF assistedby; Student4 REF assistedby; Student5 REF assistedby; };
An association of more than two members of equal standing:
class Club { Member1 REF; Member2 REF; Member3 REF; };
The following code sample implements the associators method. The CIMOM passes values for associatorNames, objectName, role, resultRole, includeQualifiers, includeClassOrigin, and propertyList to the association provider. In addition, the code prints the name of the CIM associator class and the CIM class or instance whose associated objects are to be returned. This provider handles instances of example_teacher and example_student classes.
... public CIMInstance[] associators(CCIMObjectPath assocName, CIMObjectPath objectName, String resultClass, String role, String resultRole, boolean includeQualifiers, boolean includeClassOrigin, String[] propertyList) throws CIMException { System.out.println("Associators "+assocName+" "+objectName); if (objectName.getObjectName()equalsIgnoreCase("example_teacher")) { Vector v = new Vector(); if ((role != null) && (!role.equalsIgnoreCase("teaches"))) { // Teachers only play the teaches role. return v; } if ((resultRole != null) && (!resultRole.equalsIgnoreCase ("taughtby"))) { // Teachers only result in taughtby role return v; } // Get the associators of a teacher CIMProperty nameProp = (CIMProperty)objectName.getKeys().elementAt (0); String name = (String)nameProp.getValue().getValue(); // Get the student class CIMObjectPath tempOp = new CIMObjectPath("example_student"); tempOp.setNameSpace(assocName.getNameSpace()); CIMClass cc = cimom.getClass(tempOp, false); // Test the instance name passed by objectName // and return the associated instances of the student class. if(name.equals("teacher1")) { // Get students for teacher1 CIMInstance ci = cc.newInstance(); ci.setProperty("name", new CIMValue("student1")); v.addElement(ci.filterProperties(propertyList, includeQualifiers, includeClassOrigin)); ci = cc.newInstance(); ci.setProperty("name", new CIMValue("student2")); v.addElement(ci.filterProperties(propertyList, includeQualifiers, includeClassOrigin)); return v; } } }
To generate an indication for a CIM event, you need to perform the following tasks:
Use the methods in the EventProvider interface to detect when to start and stop delivering indications of the CIM event.
Create an instance of one or more subclasses of the CIM_Indication class to store information about the CIM event that occurred.
Use the deliverEvent method in the ProviderCIMOMHandle interface to deliver indications to the CIMOM.
Implement the EventProvider interface.
For example:
public class sampleEventProvider implements InstanceProvider EventProvider{ // Reference for provider to contact the CIM Object Manager private ProviderCIMOMHandle cimom; }
Execute each of the methods listed in Table 6–2 for each instance indication that the provider handles.
Create an indication for create, modify, and delete instance event type.
For example, in the createInstance method:
public CIMObjectPath createInstance(CIMObjectPath op, CIMInstance ci) throws CIMException { CIMObjectpath newop = ip.createInstance(op, ci); CIMInstance indication = new CIMInstance(); indication.setClassName("CIM_InstCreation"); CIMProperty cp = new CIMProperty(); cp.setName("SourceInstance"); cp.setValue(new CIMValue(ci)); Vector v = new Vector(); v.addElement(cp); indication.setProperties(v); ... }
Deliver the event indication to the CIM Object Manager.
cimom.deliverEvent(op.getNameSpace(), indication);
An event provider implements the EventProvider interface. This interface contains methods that the CIMOM uses to notify the provider when a client has subscribed for indications of CIM events. This method is also used when a client has cancelled the subscription for CIM events. These methods allow the provider to indicate whether the CIMOM should poll for some event indications and whether the provider should authorize the return of an indication to a handler.
The following table lists the methods in the EventProvider interface that must be implemented by an event provider.
Table 6–2 EventProvider Methods
Method |
Description |
---|---|
activateFilter |
When a client creates a subscription, the CIMOM calls this method to ask the provider to check for CIM events. |
authorizeFilter |
When a client creates a subscription, the CIMOM calls this method to test if the specified filter expression is allowed. |
deActivateFilter |
When a client removes a subscription, the CIMOM calls this method to ask the provider to deactivate the specified event filter. |
mustPoll |
When a client creates a subscription, the CIMOM calls this method to test whether the specified filter expression is allowed by the provider, and if it must be polled. |
The CIMOM passes values for the following arguments to all methods:
filter – SelectExp that specifies the CIM events for which indications must be generated.
eventType – String that specifies the type of CIM event, which can also be extracted from the FROM clause of the select expression.
classPath – CIMObjectPath that specifies the name of the class for which the event is required.
In addition, the activateFilter method takes the boolean firstActivation, indicating that this filter is the first filter for this event type. The deActivateFilter method takes the boolean lastActivation, indicating that this filter is the last filter for this event type.
A client application subscribes for indications of CIM events by creating an instance of the CIM_IndicationSubscription class. The CIMOM then forwards the request to the appropriate provider. If the provider implements the EventProvider interface, the CIMOM notifies the provider when to start sending indications for the specified events. The provider performs this notification by calling the provider's activateFilter method. In addition, the CIMOM notifies the provider when to stop sending indications for the specified events by calling the provider's deActivateFilter method.
The provider responds to the CIMOM's requests by creating and delivering an indication each time the provider creates, modifies, or deletes an instance. A provider typically defines a flag variable that is set when the CIMOM calls the activateFilter method. This flag is cleared when the CIMOM calls the deActivateFilter method. Then in each method that creates, modifies, or deletes an instance, the provider checks the status of the activate filter flag. If the flag is set, the provider creates an indication containing the created CIM instance object. The provider uses the deliverEvent method to return the indication to the CIMOM. If the flag is not set, the provider does not create and deliver an indication of the event.
A provider starts delivering indications when the activateFilter method is called. The provider creates instances of concrete subclasses of CIM_Indication and invokes the ProviderCIMOMHandled.deliverIndication method. The CIMOM receives the indication and delivers the indication to the appropriate indication handlers. A provider can handle multiple event types. For example, in the case of life cycle indications, a provider can handle CIM_InstCreation, CIM_InstDeletion, and CIM_InstModification.
To keep track of types that have subscriber interest, the provider can use the firstActivation and lastActivation flags passed in the activateFilter and deActivateFilter calls, respectively. The firstActivation flag is true when the subscription is the first subscription for the particular event type. Similarly, lastActivation is true when the last subscription for the particular event type is removed. By checking these flags, the provider can easily allocate or deallocate resources to monitor the specified event types.
A provider that handles sensitive data can check authorizations for requests for indications. The provider must implement the Authorizable interface to indicate that the provider handles authorization checking. The provider also implements the authorizeFilter method. The CIMOM calls this method to test whether the owner (UID) of an event handler is authorized to receive the indications that result from evaluating a filter expression. The UID for the owner of the event destination, the event handler, can be different than the owner of the client application requesting the filter activation.
Providers get information from and set information on managed devices. A native provider is a program specifically written for a particular managed device. For example, a provider that accesses data on a Solaris system usually includes C functions to query the system.
The common reasons for writing a native provider are as follows:
Efficiency – You may want to implement a small portion of time-critical code in a lower-level programming language, such as Assembly, and then have your Java application call these functions.
Need to access platform-specific features – The standard Java class library might not support the platform-dependent features required by your application.
Legacy code – You want to continue to use your legacy code with a Java provider.
The Java Native Interface is part of the JDK software. By writing programs using the Java Native Interface, you ensure that your code is completely portable across all platforms. The Java Native Interface enables Java code to operate with applications and libraries that are written in other languages, such as C, C++, and assembly.
For more information on writing and integrating Java programs with native methods, visit the Java Web site at http://java.sun.com.
Follow these steps to create a provider:
Create or edit your provider program.
Compile the Java program to create the class files.
Copy any shared object files (.so) to /usr/sadm/lib/wbem.
Set your CLASSPATH to the location of your .class and .jar files.
You set the provider CLASSPATH to tell the CIM Object Manager where the .class and .jar files are located.
Create an instance of the Solaris_ProviderPath class.
For example:
/* Create a name space object initialized with root\system (name of name space) on the local host. */ CIMNameSpace cns = new CIMNameSpace("", "root\system"); // Connect to the root\system name space as root. cc = new CIMClient(cns, "root", "root_password"); // Get the Solaris_ProviderPath class cimclass = cc.getClass(new CIMObjectPath("Solaris_ProviderPath"); // Create a new instance of Solaris_ProviderPath. class ci = cimclass.newInstance();
Set the pathurl property to the location of the files by using standard URL format.
For example:
/* Set the provider CLASSPATH to /myhome/myproviders */ ci.setProperty("pathurl", new CIMValue(new String ("file:///myhome/myproviders/")));
The standard URL format is shown in the following table.
Provider CLASSPATH |
Standard URL Format |
---|---|
Absolute path to directory |
file:///a/b/c/ |
Absolute path to .jar file |
file:///a/b/my.jar |
Create the instance.
For example:
// Pass the updated instance to the CIM Object Manager cc.createInstance(new CIMObjectPath(), ci);
You register a new or modified provider with the CIM Object Manager to communicate information about the data and operations that the provider supports. You also register a provider to notify the CIM Object Manager of the provider's location. The CIM Object Manager uses this information to load and initialize the provider, and to determine the appropriate provider for a particular client request.
Create a Managed Object Format (MOF) file that defines the classes that the provider supports.
For more information on creating MOF files, see the DMTF Web site at http://www.dmtf.org.
Include the provider qualifier in the MOF file to specify the provider type and location for the CIMOM.
For example:
[Provider("java:com.sun.providers.myprovider")] Class_name { … };
This qualifier indicates the following information:
java: – The provider is written in the Java language and implements the javax.wbem.provider interfaces
com.sun.providers.myprovider – The name of the Java class that implements the provider
Compile the MOF file by using the mofcomp(1M) command.
This MOF file declares the Ex_SimpleCIMInstanceProvider class that is served by SimpleCIMInstanceProvider.
// ======================================================== // Title: SimpleCIMInstanceProvider // Filename: SimpleCIMInstanceProvider.mof // Description: // ================================================================== // ================================================================== // Pragmas // ================================================================== #pragma Locale ("en-US") // ================================================================== // SimpleCIMInstanceProvider // ================================================================== [Provider("java:SimpleCIMInstanceProvider")] class Ex_SimpleCIMInstanceProvider { // Properties [Key, Description("First Name of the User")] string First; [Description("Last Name of the User")] string Last; };