5 Preparing to Develop using the Oracle Virtual Assembly Builder Plug-in SDK

The following sections describes how

5.1 Introduction

Developers can use Oracle Virtual Assembly Builder to create an introspector plug-in.

5.1.1 What Does a Plug-in Do?

The process of capturing the state of the reference host begins with introspection. Your plug-in participates in introspection by performing dehydration of the reference software installation. The output of dehydration is metadata that describes the reference installation.

Later instantiation of the software appliance as a virtual machine in the target environment is known as deployment. Your plug-in participates in deployment by performing rehydration of the reference software within the context of the running virtual machine. Metadata recorded at dehydration time is available to the plug-in during rehydration.

5.1.2 Packaging and Distribution

Your plug-in is implemented as a .jar file that is packaged with the product you support. Your plug-in enters the Oracle Virtual Assembly Builder environment at runtime through the dynamic plug-in discovery process. After discovery, the Oracle Virtual Assembly Builder product framework arranges to invoke your plug-in's dehydration and rehydration entry points at the appropriate time and place: on the reference host during introspection for dehydration and within the running virtual machine late in deployment for rehydration.

5.2 Preparing to Develop a Plug-in

As you begin to design and implement your introspector plug-in these are the first things you should do.

5.2.1 Name the Plug-in

Choose a name for your introspector. Introspector plug-in jar files live within the Oracle Virtual Assembly Builder software installation directory tree, each plug-in in its own subdirectory. These subdirectories are named according to the plug-in name, so each plug-in name must be unique. Choose a name related to the product that will be introspected - this will make it easier for users of your plug-in to figure out how to invoke it. The name of the plug-in is used to create the CLI command name for introspecting your product. A command name of the format introspect<plug-in name> is constructed. In the GUI your plug-in name will show up "as is" in a list of component types that can be introspected.

5.2.2 Plug-in or Plug-in Extension

Decide if you will be writing a plug-in or an extension to an already existing plug-in. If your product runs on top of another product for which an Oracle Virtual Assembly Builder plug-in already exists (such as WLS), writing an extension to that plug-in may make sense.

5.2.3 Version the Plug-in

Devise a plan for dealing with the introspection of different versions of your product. This will have an impact on the name you pick for your introspector plug-in. There are two distinct strategies, one of which may work better for you than the other. One strategy is to handle the introspection of different product versions entirely within a single plug-in; this may be convenient if the product versions being supported have similar configuration data. For a product called "foo" you could name your introspector plug-in "foo", and the plug-in would detect the product version (version 1 or version 2) and do the appropriate thing for that version.

Alternatively, if the product versions are so different that it's more convenient to keep the introspector code completely separate, you could write two plug-ins named "foo1" and "foo2", each of which is capable of introspecting only a single product version. In this case a customer who had both product versions installed somewhere on their network might have both introspector plug-ins present in a single Oracle Virtual Assembly Builder instance, and the customer would have to know what product version they want to introspect. Note that due to Oracle Virtual Assembly Builder's classloading scheme you must use different package names in the different versions of your plug-in.

5.2.4 Developing an Introspection Plug-in

As you begin to write code for your introspector plug-in, see Chapter 6, "Developing an Introspection Plug-in" and Chapter 7, "Introspection Plug-in Module Reference". These chapters contain tutorial code samples, explain common plug-in operations, and provide a reference to the modules in the Oracle Virtual Assembly Builder introspection framework. You could read through the tutorials and copy the sample code to produce a plug-in that can be invoked by Oracle Virtual Assembly Builder, to which you can add your product-specific code.

5.3 Plug-in Architecture and Workflow

Introspector plug-ins are organized by the Introspector Manager. It is the core of the Introspector framework and acts as the controller for all plug-ins. The Introspector Manager is the layer that sits between the user interfaces (the Studio GUI and the command line utility abctl) and the plug-ins. It tracks all the plug-ins and invokes the plug-ins' dehydration and rehydration methods.

Since there can be a variable set of plug-ins, a mechanism is needed to inform the Introspector Manager of the exact set of plug-ins that are available. This is done using a service model provided by Oracle Virtual Assembly Builder called SPIF. To make the Introspector Manager aware of a new plug-in it need only be registered as an IntrospectorPlugin service. The details of how this registration is accomplished are described in Chapter 6, "Developing an Introspection Plug-in". Once a plug-in is registered the rest is handled by the Introspector Framework.

5.3.1 Workflow

The process of using Oracle Virtual Assembly Builder, and triggering interactions with the plug-in, is as follows:

  1. The OVAB user begins an introspection, indicating the type of product component to be introspected, the host where that component resides, and other information necessary to obtain the product configuration data - such as passwords, directory paths, or user names.

  2. The appropriate IntrospectorPluginLocator for the plug-in is located and is used determine if a newer version of the plug-in exists on the reference host. If so, the user is required to upgrade before introspection can be done.

  3. If no newer version of the plug-in is found then the plug-in's Dehydrator.dehydrate() method is invoked, executing on either the local or a remote host, as specified by the user. The dehydrator captures the key configuration information and creates Oracle Virtual Assembly Builder metadata. It indicates what product executables, data and configuration need to be copied from the introspected system, describing them in terms of file set definitions.

  4. The Oracle Virtual Assembly Builder framework captures file sets to be created from the introspector plug-in's file set definitions - by default this is done immediately after introspection completes, but at the user's option it may be deferred until a later time.

  5. The OVAB user creates an assembly from various introspected components (represented in the metadata as appliances or atomic assemblies), describing their relationship to each other in terms of network connections. The user may edit other configuration parameters at this time, as well.

  6. The OVAB user creates a deployable VM template, which combines the file sets for the appliances in the assembly with a VM system base image. These templates, along with the assembly metadata and the Oracle Virtual Assembly Builder bits necessary for deployment (including the introspector plug-ins) are then combined into an assembly archive (.ova file).

  7. The OVAB user creates a deployment plan. The deployment plan can be used to override any editable metadata. It is also used to specify network configuration for the target OVM environment that will be used for deployment.

  8. At deployment time, a VM is instantiated for each appliance, and the introspector plug-in's Rehydrator.rehydrate() method is run. The plug-in performs the component rehydration by converting the saved metadata state into a product configuration appropriate for the VM environment, with a modified network configuration, a new host name, etc. After the rehydrate() method completes, the Rehydrator.start() method is called. The start() method performs the necessary actions to start the component.

  9. After the assembly is deployed it can be stopped and started as a whole, and scalable appliances can be scaled up or down.

5.3.2 How Does Your Plug-in Fit in

An introspector plug-in participates in both the capturing (dehydration) of a product from a reference host as well as the deployment of that product as a virtual appliance (rehydration). Each plug-in must inspect the configuration of the product on a reference host and capture enough of the data to be able to deploy the product on a virtual system. During rehydration the product configuration must be fixed to make the product work on the new host, and to apply any configuration overrides the user has specified.

5.3.2.1 Job Parameters

Your plug-in can specify parameters to be supplied by the user when they introspect your component. These parameters appear as command line options when using abctl, or as text field entries in the GUI. For details on the different job parameter types and how they are presented to the user read the Job Parameters tutorial in Chapter 6, "Developing an Introspection Plug-in".

Note that password type job parameters are treated differently from other parameter types by abctl for security reasons. Instead of the user entering the password so it appears in clear text on the command line, the user will be prompted to enter the password when the plug-in or extension asks for the value.

5.3.2.2 Dehydration

Dehydration is the process of capturing the relevant configuration information for a given product into metadata and a file set definition. The definition of 'relevant' is product-specific - there is no need to capture every possible configuration element so that the user can reconfigure the system completely from the introspected reference system to the deployed virtual system, because reconfiguration is the domain of Oracle Enterprise Manager. The dehydration process must capture only enough information for the critical portions of a configuration that you expect a user would change from one system to another. For example, if your product has a password, it would be good if the user were allowed to change the password prior to deploying a new system.

Note that the product's installation paths specified in the file set definition remain the same on the deployed VM as they were on the reference host, so your plug-in doesn't need to do any path fix up and should not expose file set paths to the user for customization.

Your plug-in must provide a Dehydrator implementation. The Dehydrator.dehydrate(DehydrateContext context) method is called during introspection. The DehydrateContext passed in to this method gives you access to the JobParameter values supplied by the user. It also has methods used to create the Appliance or Assembly that will be the result of dehydration.

There are different types of metadata that can be used to describe different aspects of your product and these will be described in detail in Section 5.4, "Metadata Overview".

5.3.2.3 Rehydration

Rehydration is the opposite of dehydration. Rehydration is the process of taking captured metadata and applying it to a virtual system. Rehydration occurs on the deployed virtual server and is invoked by the Deployer as the last step of deployment.

Your plug-in must provide a Rehydrator implementation. The Rehydrator.rehydrate(RehydrateContext context) method is called when it is time for your plug-in to reconfigure your product during deployment. The RehydrateContext passed in to your rehydrate() method provides access to the metadata associated with the appliance being deployed.

The following methods may be of particular interest:

  • RehydrateContext.getTargetAppliance() gets the target appliance (the appliance being deployed) and access properties, inputs, outputs, etc.

  • RehydrateContext.getHostForTargetAppliance() gets the VM hostname

There are some things you should know about the virtual machine environment during rehydration:

  • The Rehydrate job always runs as 'root' on the deployed system. Any files you copy or create will have root ownership, etc. You will need to change the user and group ownership of any files you create during rehydration as appropriate.

  • The files included in a file set definition will automatically get set to the 'owner' and 'group' values in the definition. If no value is specified by the plug-in during dehydration or by the user when the assembly is edited, both the owner and group will be set to 'oracle'.

  • The files included in file set definitions will be created with their original permissions on the VM.

  • RehydrateUtils in the oracle.as.assemblybuilder.introspector.plugin.util module has the ability to run commands as a specific user. Make sure to start processes as 'oracle' where applicable.

  • Your plug-in code has access to all the metadata in the assembly during rehydration. You can use the RehydrateContext.getAssemblyNavigator() method to get an AssemblyNavigator to help in finding the metadata you need.

5.3.2.4 Post-Deployment Operations

Introspector plug-in Rehydrators are involved in other operations in addition to rehydration. Deployed assemblies can be stopped and started . The Rehydrator.stop() method gives you a chance to shut down your component gracefully when the deployed assembly is stopped. The Rehydrator.start() method is called during deployment right after the Rehydrator.rehydrate() method is called, and it will also be invoked when an assembly is started after being stopped.

5.4 Metadata Overview

These sections describe the various Oracle Virtual Assembly Builder metadata constructs that the introspector plug-in writer should be familiar with. The Oracle Virtual Assembly Builder metadata is mostly stored as XML files in the Oracle Virtual Assembly Builder Catalog, under the $AB_INSTANCE/catalog directory.

5.4.1 Type of Metadata

Before you continue through the metadata overview, consider what kind of metadata your plug-in should create as a result of introspecting your product.

5.4.1.1 Appliance

The simplest option is to produce a single appliance, which is metadata and associated product configuration data that may eventually be deployed as a single Virtual Machine.

5.4.1.2 Atomic Assembly

A more complex option is to make your plug-in create an atomic assembly, which is a collection of related appliances whose shape cannot be altered by the user - that is, appliances cannot be added to or removed from the atomic assembly, and any connections between those appliances cannot be changed during assembly editing. Each appliance in an atomic assembly will be instantiated as its own VM.

5.4.2 Appliances

An appliance is the primary output of the introspection process. An appliance is a metadata construct that can (after additional configuration steps) be instantiated as a running VM. Part of the appliance metadata describes the configuration of the product environment on the introspected system. An appliance can also have associated content resources, such as product configuration and other files, that are necessary to instantiate the product on a VM. This product data and appliance metadata is stored in the Oracle Virtual Assembly Builder catalog.

The appliance also has a set of resource requirements, which for instance allow the plug-in writer to specify that a VM instantiating the appliance should have a minimum amount of memory or a minimum number of CPUs available to it.

Each appliance also has scalability information that describes the number of VMs that should be created from the appliance - minimum, target, maximum, and absolute maximum. The "target" value is the initial number of VM instances of the Appliance that will be created by default. The minimum and maximum values are bounds on the number of VM instances, and these bounds are in effect after the Assembly containing the Appliance is deployed. The "absolute maximum" value is an upper limit on the maximum value. Oracle Virtual Assembly Builder does not support an absolute maximum of more than 2000.

An appliance also has interfaces, which describe its relationship to the network configuration on the VM's host, as well as inputs and outputs, which describe network connections to other appliances.

The appliance creator is the introspector plug-in or extension name, and cannot be altered by plug-ins. It is added to the appliance automatically. This value is used by the Oracle Virtual Assembly Builder framework to determine what plug-in to use to operate on the appliance.

The appliance type defaults to the plug-in or extension name, but a different value can be specified by the plug-in if it is desired. It generally only makes sense to specify a type different from the default if the same plug-in can create multiple appliance types.

5.4.2.1 Scalability

After an assembly is deployed, the user may choose to scale the appliances, which is to say that the user may create additional VMs that instantiate the same appliance. The introspector plug-in can specify restrictions on the scaling behavior of the appliances it creates, by specifying minimum and maximum numbers of instantiations of each appliance. Decide whether it makes sense for your appliance(s) to be scalable, and if so what the bounds should be.

5.4.3 Assemblies

An assembly is a container for appliances and sub-assemblies, allowing us to maintain connections between them. Only a non-atomic assembly can have a deployment plan, so a lone appliance or atomic assembly not contained in a 'top-level' assembly cannot be deployed to the virtual environment. (The simplest assembly contains only a single appliance, and the deployment of that assembly would result in a single appliance instance, or VM.)

5.4.4 Atomic Assemblies

Assemblies created by introspector plug-ins are called atomic assemblies. Atomic assemblies may not contain sub-assemblies. Oracle Virtual Assembly Builder Studio restricts the editing operations that users can effect on atomic assemblies - the connections between appliances created by the plug-in in an atomic assembly cannot be edited by the user, nor can appliances be added to or deleted from an atomic assembly.

An atomic assembly is deployed as a unit, so the appliances created in the assembly by the plug-in should have their network connections in place where appropriate.

All appliances in an atomic assembly must share file sets. This is because the introspection process only goes to one host to capture the file sets. Appliances that share file sets are called siblings; after the first appliance is created, siblings are added to the atomic assembly using the Assembly.createSibling() method.

For example, in Oracle WebLogic Server, the Admin Server contains all the same files as any of the Managed Servers, so the Admin Server is the only machine that needs its file sets captured, and the other appliances can be siblings of the Admin Server appliance.

If, however, your product consists of multiple types of different appliances that need to have file sets captured separately, then you need to have your plug-in generate appliances and not an atomic assembly. You may need multiple plug-ins, or you may be able to accomplish this using a single plug-in. Each appliance may then have its file sets captured separately and then it will be left to the User to assemble these appliances into a proper assembly with all the necessary connections.

5.4.5 Properties

A property is a name-value pair that stores values of various types (string, integer, password, boolean and file reference). Most other Oracle Virtual Assembly Builder metadata constructs can have properties attached to them, including assemblies, appliances, inputs, outputs and interfaces.

5.4.5.1 Property Categories

There are two categories of properties: user and system, which differ in that user properties can be overridden by the OVAB user whereas system properties are read-only. Either type of property can be marked as required or not, and in the case of a user property, an initial value can be specified, or not. Before an assembly can be deployed, values must be specified for all required properties on all metadata associated with that assembly. These values can be specified in the assembly metadata during assembly editing or via the deployment plan.

5.4.5.2 Property Groups

Properties can be sorted into property groups by attaching a common prefix to the names of all the properties that are meant to be grouped together. Oracle Virtual Assembly Builder Studio uses groups as a way to organize the properties when they are displayed to the user. In plug-in code, groups of properties can be retrieved by specifying the name prefix associated with the group.

Properties not in defined groups are displayed in Oracle Virtual Assembly Builder Studio according to a heuristic that groups properties based on common prefixes (if any) and splits the property name string on the '.' character.

Note that property group names starting with "OVAB", "Ovab" or "ovab" are reserved for internal use.

5.4.5.3 Synthetic Properties

Synthetic properties are properties that do not exist in appliance metadata and are not created by plug-ins; they exist only in the deployment plan. For example, there is a synthetic property named com.oracle.ovab.deployer.anti-affinity-min-servers that may be overridden in a deployment plan to specify how many physical machines should be used to host the VMs for an appliance. This property is only relevant for instantiated appliances and hence does not exist in the appliance metadata.

5.4.5.4 Setting and Retrieving Properties

For most property types, creating the property during dehydration and retrieving its value during rehydration is straightforward. Password properties and file reference properties need some additional explanation though.

5.4.5.4.1 Passwords

Passwords are encrypted when the metadata is persisted to disk in the Oracle Virtual Assembly Builder catalog. When retrieving the password value during rehydration you need to use the TypedValue.getPasswordValue().getAsChar() method to get the clear text value. You will get a char[] which, for security reasons, you should clear out as soon as you are done with the password, as follows: Arrays.fill(passwd, (char) 0);

5.4.5.4.2 File References

A file reference property has a value that points to a file. The file can be provided by the plug-in during dehydration by specifying an existing content resource. A null value can also be specified. If the property is marked required and a null value is specified by the plug-in, the file must be provided by the user via the deployment plan.

To override a file reference type property via the deployment plan the user specifies a local file from disk. This file is then sent to the VM during deployment and can be accessed from the plug-in rehydration code.

Note:

File reference properties are only visible in the deployment plan - they cannot be seen or edited during assembly editing.

To access the file during rehydration the plug-in must get the value of the FileRef property and parse it to find the name of a content resource. The property value will be a URI value like "contentresource:<name>". The methods java.net.URI.getScheme() and java.net.URI.getSchemeSpecificPart() may be useful for parsing the value. Once the content resource name is obtained the content resource can be accessed using the usual SDK methods provided for accessing content resources.

Oracle Virtual Assembly Builder reserves the right to support other URI schemes for FileRef property types in the future. Plug-in code should be robust to this, that is, check the URI scheme to ensure that it is "contentresource", and ignoring properties with an unknown scheme, before trying to look up a corresponding content resource. This is especially true if a plug-in is scanning through all available properties.

5.4.6 Inputs and Outputs

Inputs and outputs are features of appliances and atomic assemblies that describe network endpoints used by the appliance or atomic assembly. (The data structures are a bit different for appliances and assemblies, so you'll see classes for ApplianceInput, AssemblyInput, ApplianceOutput, and AssemblyOutput.)

Inputs and outputs are created by the introspector plug-in to indicate possible attachment points for connections, either to other endpoints in the same assembly, or to external resources. For instance, a database appliance has an input describing the port where the database listens for connections, and a WLS appliance has a JDBC output describing the host that is used for back-end storage. The WLS output can be connected to the database input. It is via this connection that the database host and port can be known to the WLS plug-in at deployment time, so it can fix its JDBC configuration.

When viewing appliances in the Oracle Virtual Assembly Builder Studio assembly editor, inputs appear as bisected circles on the left side of the appliances, whereas outputs appear as bisected circles on the right side.

Inputs have an associated port number, which is the network port number where they accept network connections, and a list of network protocols that are supported on that port. Inputs are bound to one of the Appliance's network interfaces.

Both inputs and outputs can have properties attached to them by the introspector plug-in.

Outputs have a single network protocol associated with them, the one that is used for communication from that output.

5.4.6.1 Output Connection Properties

Outputs can have an optional associated set of connection properties, which (if present) are used to ensure that the output is connected to a compatible input - if the names of all output connection properties match names of input properties, the input and output are deemed to be compatible. The connection properties are also used to seed the input properties of an external resource created for that output.

Connection properties and input properties are a part of the external surface of the plug-in-generated metadata; they must be documented so that other components understand how to connect to your appliances and assemblies.

5.4.6.2 Connections

A connection represents the network connection from an output to an input. The two endpoints included in the connection can belong either to appliances or assemblies. When a connection is to or from an assembly, the endpoint is actually owned by an appliance in that assembly.

5.4.6.3 External Resources

In cases where an output refers to a network resource that is not represented by an appliance in the assembly, the user will be able to connect that output by creating an external resource in the assembly - the unbound appliance output will be configured to connect to an input on the new external resource. An external resource is a sort of placeholder, in that no VM is created for an external resource when the assembly is instantiated. They exist in the assembly solely to allow the user to specify configuration information (hostname, port, etc.) for pre-existing entities on the user's network.

For example many Oracle products depend on an LDAP server. Currently Oracle Virtual Assembly Builder does not support deploying an LDAP appliance into the virtual environment, so an external resource is used in the assembly to represent the LDAP server.

5.4.6.4 Vnets

When a non-atomic assembly is created, it is given a default Vnet, and additional Vnets can be added to it later. When the assembly is instantiated, these Vnets are associated with networks from the OVM environment. Appliances are created with interfaces that must be associated with an assembly Vnet before the assembly can be instantiated.

Manipulation of Vnets is not done by introspection plug-ins. Instead, users create and configure Vnets while composing a non-atomic assembly.

5.4.6.5 Interfaces

Interfaces in the metadata represent network interface cards (NICs). There are two types of interfaces. A physical interface represents a physical network interface card. Physical interfaces can then have associated virtual interfaces which are basically additional IP addresses on that same physical NIC.

There must be at least one interface on an appliance. If the plug-in does not create an interface on an appliance the introspection framework will automatically add a default one.

5.4.7 Content Resources

Content resources are files that are saved in the Oracle Virtual Assembly Builder catalog during dehydration for retrieval and use by the plug-in during rehydration. Content resources should not be used to transfer unmodified files from the introspected system to the deployed system - that is what file set definitions are for.

Content resources are meant to be used for files that you wish to turn into Velocity templates (or something similar) during dehydration for later token replacement during rehydration - without modifying the original file on the introspected system.

Content resources can also be used for files that you want the user to be able to update as a whole at assembly editing time. For instance so users could provide custom SSL certificates. In this case you must expose the content resource as a FileRef user property. Doing so allows the user to add a different copy of that file at deployment time.

Content resources can be associated with an appliance or an atomic assembly.

5.4.8 File Set Definitions

(These were previously known as Package Definitions and are created via the PackageDefinitionBuilder class.) In the simple case, File Set Definitions are essentially lists of files that are to be copied from the introspected system to the catalog, and later built in to the templates that will be used to instantiate the appliances.

Typically, the files listed in the definition are compressed and zipped (captured) into file sets (previously "packages") by the Oracle Virtual Assembly Builder framework for copying from the introspected system to the catalog. The file sets are unzipped onto VM disk images during the template creation process, and the disk images are mounted as directories on the running VMs as appliances are instantiated.

There are two types of file set definitions - user and system. User definitions can be edited by the user including being completely removed. System definitions cannot be edited other than selecting the 'type' as shared or local. System definitions are meant to indicate a set of files that are necessary for the product to function properly.

5.4.8.1 Exclusions

When creating your file set definition you can specify excludes. These are files that exist somewhere under the specified root directory that you do not want included when the file set is captured. Typical candidates for exclusion would be log files, which it does not make sense to carry over onto the deployed VM.

The exclude semantics are described in Table 5-1:

Table 5-1 Exclude Semantics

Directory Relative to the Root of the Definition Effect of Exclusion

some/relative/path/foo

Excludes the entire directory 'foo'. The 'foo' directory will not exist at all on the deployed VM.

some/relative/path/bar/*

Excludes all the files under directory 'bar'. The empty directory will exist on the deployed VM.

some/relative/path/logs/*.log

Excludes all files in the logs dir with names ending in '.log'. Any other file is still captured. The logs directory will exist even if all files were removed from it.


5.4.8.2 Shared File Sets

The more complex scenarios supported by file set definitions involve shared file sets. In these cases various arrangements are made for a volume to be shared after VMs are instantiated. File set capture (known in the APIs as "packaging") is optional for shared file sets; when no file set is created, the file set definition may refer to a pre-existing volume or an empty volume.

When shared file sets are captured by the Oracle Virtual Assembly Builder framework, the file system type may be either NFS or DISK_IMAGE. In the NFS case, Oracle Virtual Assembly Builder will arrange for the contents of a temporary disk image to be copied on to an NFS file system that can be accessed by all the appliances. Subsequent appliance instances just mount the NFS file system. Note that NFS file sets that are captured can only be used between members of a clustered appliance. In the case of a captured DISK_IMAGE file set each appliance will see the contents of the file set mounted as a read-only disk. This is also only available to members of a clustered appliance.

If file sets are not being captured and added to the template by the Oracle Virtual Assembly Builder framework, a pre-existing shared volume is assumed, and the file system type should be either NFS or RAW.

Here is a summary of the shared file set possibilities:

Table 5-2 Shared File Sets (Captured)

File System Type Sharing Rule

NFS

Can only be shared by members of a clustered appliance. An NFS volume external to the VM and specified by 'mount-target' in the deployment plan is created, populated and mounted when the first appliance in a cluster is instantiated. Each subsequent instantiation of a cluster member will simply NFS-mount this volume.

DISK_IMAGE

Can only be shared by members of a clustered appliance. Oracle Virtual Assembly Builder constructs a volume containing the specified files. The volume is included in each appliance's configuration by OVM. These volumes are read-only.

RAW

Not available.


Table 5-3 Shared File Sets (Not Captured)

File System Type Sharing Rule

NFS

A preexisting NFS volume, specified by 'mount-target' in the deployment plan, is mounted by each appliance.

DISK_IMAGE

DO NOT USE: This will result in a read-only volume of the size specified that will remain forever empty. You do not want your plug-in to create one of these.

RAW

Can only be shared by members of a clustered appliance. Oracle Virtual Assembly Builder configures Linux raw devices and /dev for owner, group, permissions, and symbolic name. (Note: this exists to support the RAC database appliance, and most likely will not be used by any other plug-in type.)


Each file set definition includes a root directory, which is scanned recursively to collect files as file sets are captured. Files under the root directory may be excluded based on file names or patterns, in case particular files such as logs or temporary files do no need to be copied from the introspected system to the instantiated VM.

Atomic assemblies are restricted in their configurations in that all appliances in an atomic assembly share the same file sets. (Content resources, described previously, are specific to each appliance in the atomic assembly.)

5.5 Introspector Plug-in Extensions

A plug-in extension can be used to add dehydration and rehydration operations to an existing plug-in. This is a useful capability when one product is built on top of another. Rather than duplicating the work of the underlying product, a plug-in extension can be added to run when the underlying plug-in runs and contribute additional metadata and logic when appropriate.

5.5.1 Semantics of Plug-in Extensions

Plug-in extensions follow these semantics:

  • A plug-in extension is always executed when its parent plug-in is executed.

  • A plug-in extension can contribute to the set of parameters accepted by a plug-in during dehydration job submissions. These additional parameters are presented to plug-in extensions along with the parameters specified by the plug-in.

  • A plug-in extension can choose to be represented as an "alias" of the parent plug-in for launching dehydration operations. The effect of this is for it to appear as if a separate additional introspection choice is available to the user though this does not have an effect on execution as the underlying plug-in is always executed along with all of the plug-in extensions that extend it.

  • A plug-in extension can extend a plug-in or another plug-in extension (the entity from which a plug-in extension extends is considered the parent).

  • A plug-in extension always executes after its parent executes.

  • A plug-in extension can have only one direct parent.

  • A plug-in or plug-in extension can have multiple children plug-in extensions.

  • "Sibling" plug-in extensions execute in an arbitrary order.

5.5.2 Restrictions on Plug-in Extensions

Plug-in extensions follow these restrictions:

  • Plug-in extensions are usually dependent on the work performed by its parents. A plug-in extension often needs to know what its parent contributes in order to use the data provided by the parent and not create any conflicting data. This implies that a plug-in extension may require modification when a parent is modified.

  • Plug-ins must remain independent of the plug-in extensions that extend from it. A plug-in is not provided access to the plug-in extensions that extend from it and a plug-in extension should take care to do no harm to the operation of its parents.

  • Plug-in extensions cannot create assemblies or appliances unless the parent plug-in permits it. Without agreement from the plug-in owner and careful management, the creation of appliance or assemblies by a plug-in extension could interfere with subsequent operations performed by the plug-in or other plug-in extensions. Without such an agreement, plug-in extensions must restrict their operations to only those that contribute to the metadata of assemblies and appliances created by the plug-in.

  • A plug-in extension must do nothing when there is nothing for it to do - that is, when the component the extension is handling is not installed. Plug-in extensions always execute when the parent plug-in executes which implies that some executions will not require any work from a particular plug-in extension. It is the responsibility of the plug-in extension to anticipate this and not report any errors when there is nothing to do. This is true during both dehydration and rehydration.

5.5.3 Plug-in Extension Execution

At execution of dehydration, the plug-in is executed first with the output consisting of an Assembly/Appliance. Upon completion of the plug-in, each plug-in extension in turn is passed both the entire set of user specified job parameters and the Assembly/Appliance including any modifications that may have been made by other plug-in extension implementations. Each plug-in extension is responsible for processing all necessary input parameters, examining product configuration, and determining if there is anything for the plug-in extension to do. It is expected that many plug-in extensions will often have no work to do and will just return after making that determination.

Plug-in extension implementations are invoked along with their parent plug-in for all rehydration operations including start, stop, ping, and rehydrate. For each operation, the base plug-in will be executed first. Following successful completion of an operation executed on the plug-in, the corresponding operation will be invoked on each plug-in extension sequentially. Each plug-in extension is responsible for determining if it needs to do anything during the execution of any particular operation.

The first error encountered during a dehydration/rehydration operation in the plug-in or any plug-in extension will abort the operation and the error will be returned immediately without initiating execution of any additional plug-in extension. This means that failure of a plug-in extension will lead to failure of the entire operation.

5.5.4 Plug-in Extension Implementation

The procedures for implementing a plug-in extension are virtually identical the procedures for implementing a plug-in. The primary difference is that a plug-in extension must implement the IntrospectorPluginExtension interface instead of the IntrospectorPlugin interface.

The IntrospectorPluginExtension interface has the following 3 additional methods:

String getParentName();

Returns the name of the parent plug-in or plug-in extension that the plug-in extension extends. The name returned here must be identical to the name returned from getName() of an existing plug-in or another existing plug-in extension.

String getParentPluginName();

Returns the name of the parent plug-in. If extending directly from a plug-in, getParentName() returns the same value.

boolean isAlias();

Determines whether or not applications can present the name of this plug-in extension as a distinct choice when presenting the set of possible introspection choices. Example: when this method returns true, abctl will create a separate introspectXXX command where XXX is the name of the plug-in extension, though when users execute introspectXXX the underlying parent plug-in will be executed normally just as if the command with the name of the parent plug-in was executed.

More information about each of these methods can be found in the JavaDoc for IntrospectorPluginExtension. All the other methods on the IntrospectorPluginExtension are identical to the methods of IntrospectorPlugin.

5.5.5 Plug-In Extension SPIF Service Activation

A plug-in extension is registered as a service in the same way that a plug-in is registered with the exception that a plug-in extension must be registered using IntrospectorPluginExtension.class.getName() as the service name instead of IntrospectorPlugin.class.getName():

this.registration = serviceContext.registerService(
      IntrospectorPluginExtension.class.getName(), pluginExtension);

See Chapter 6, "Developing an Introspection Plug-in" for more information on registering your plug-in.

5.6 Support Services

This section describes support services for Oracle Virtual Assembly Builder.

5.6.1 SPIF

The Service Provider Interface Framework (SPIF) provides an OSGi-like infrastructure that can be used directly by Java clients. This framework provides a custom class-loading mechanism, application services, general services, and service discovery. SPIF is built on top of the simple service model Java provides called SPI.

5.6.1.1 Class Loading

Within the Oracle Virtual Assembly Builder ORACLE_HOME directory structure, there is a 'jlib' directory containing the SPIF jar and a number of subdirectories. The subdirectory named 'apps' has a special meaning for the SPIF framework: it contains additional subdirectories, which define the applications provided by Oracle Virtual Assembly Builder. The other directories are there to allow us to group related modules together. This is done to make dependencies more clear, but also to allow for more fine-grained class-loading. Applications are able to specify the list of these top level jlib directories that they should load classes from. If one of these top-level jlib directories has subdirectories, these will be loaded as well. For instance, if you look at the 'packager' directory, you will see it has a sub-directory called 'plugin', and 'plugin' has additional jar files. So, adding 'packager' to an application's list of directories means that the application will load any jar file located within that directory tree.

5.6.1.2 Applications

SPIF Applications are the entry point into the Oracle Virtual Assembly Builder architecture. Plug-in developers should know that each of the Oracle Virtual Assembly Builder applications that execute plug-ins will automatically load the plug-ins that have been installed. Other jars that are required by the apps are specified via the spif.properties file.

5.6.1.3 Services

SPIF Services are a key component to developing a plug-in. A Service Activator is used to both register services as well as obtain service instances registered from other Service Activators.

A service is nothing more than an instance of a class that implements some interface. The consumers of the service don't have to know the name of the implementation class nor how to instantiate it. They know that there is an instance that has been registered as a specific interface, and they can get a reference to it and use the instance without caring how it was created.

5.6.1.4 Creating a Service

As a plug-in developer, you must create at least one service. The interface between the Introspection Framework and the plug-in is oracle.as.assemblybuilder.introspector.plugin.IntrospectorPlugin. This interface is located in ORACLE_HOME/jlib/introspector/oracle.as.assemblybuilder.introspector_0.1.0.jar. Tutorial One in Chapter 6, "Developing an Introspection Plug-in" shows an example of creating an implementation of this interface, as well as the rest of the process of connecting the implementation into the Introspection Framework.

5.6.1.5 Service Activator

A ServiceActivator implementation is used as an entry point for SPIF. It provides both a start() and a stop() method. Multiple Service Activators are allowed within a single jar file. If there are multiple Service Activators, each of them will be invoked; however, the order they are invoked is not defined. The start() method will be invoked when the jar is first loaded and before the SPIF application is started. This makes sure that all services are registered prior to application logic being invoked. The stop() method will be called after the SPIF application has stopped and before the JVM has terminated.

The Service Activator interface name is 'oracle.as.assemblybuilder.spif.ServiceActivator'. It is found in the 'AB_HOME/jlib/oracle.as.assemblybuilder.spif_0.1.0.jar' file. In order for an implementation of this class to be invoked, it first needs to be connected to SPIF. This is done by adding a file named 'META-INF/services/oracle.as.assemblybuilder.spif.ServiceActivator' to your jar. In the ServiceActivator file, add the fully-qualified class name of the Service Activator implementations. Each class name must be on a line by itself, with a new-line at the end of each line, including the last line.

When Oracle Virtual Assembly Builder loads the jar file, the activator will be loaded and the start() method will be called. The Service Activator can then be used to either register a service or locate an existing service.

5.6.1.6 Registering a Service

After creating an implementation of an interface and associating it with a Service Activator, you'll want to register it as a service.

In the start method of the Service Activator, create an instance of the class that provides the service. Then using the ServiceContext, call registerService(), passing in the name of the interface that is the service and the instance. Make sure to save the ServiceReference that is returned from this method so that in the stop() method you can call unregister() on the ServiceReference.

Here is an example of registering a service and unregistering it:

public class PluginActivator implements ServiceActivator {
  private ServiceRegistration registration = null;
 
  @Override
  public void start(ServiceContext serviceContext) throws Exception {
    IntrospectorPlugin plug-in = new SamplePlugin();
 
    registration =
        serviceContext.registerService(
            IntrospectorPlugin.class.getCanonicalName(),
            plugin);
  }
 
  @Override
  public void stop(ServiceContext serviceContext) throws Exception {
    if (this.registration != null) {
      this.registration.unregister();
    }
  }
}

5.6.1.7 Finding a Service

SPIF service instances may be obtained via multiple methods and interfaces provided from the ServiceContext. A recommended approach is to obtain a Collector from the ServiceContext and use the Collector to keep track of the SPIF services you need.

It is important that ServiceActivator implementations do not attempt to acquire any services from within the start() method. When this is not done, there exists a chance that a desired service will not be found. Ideally each service instance is obtained only at the point where it is needed and long after the start() method has returned.

To use a Collector, first create the Collector during execution of the start() method of your ServiceActivator:

myCollector = serviceContext.createCollector();

This Collector instance would then be passed to your own service implementation either directly or by wrapping the instance with an Interface defined in your plug-in implementation in order to hide the details of how services are obtained.

Near the point where a service (or services) are needed, one of the following Collector methods may be invoked:

  • SomeService myInstance = myCollector.getSingletonService(SomeService.class);

  • List<SomeService> myInstances = myCollector.getServices(SomeService.class);

Use getSingletonService() when you require exactly one instance of a given service. If more than one instance of the service has been registered than an instance will be selected arbitrarily and returned. If no instances exist than an error will be thrown.

Use getServices() when you need to acquire multiple instances of a given service. If no instances exist then the returned list will be empty.

5.6.2 Validation Framework

Plug-ins can provide validation code for various Oracle Virtual Assembly Builder lifecycle operations. This code can be shipped in the same jar as the other plug-in code and can be registered in the same ServiceActivator as the IntrospectorPlugin.

5.6.2.1 File Set Capture Validation

During the process of file set capture (previously known as "Packaging" and still referred to that way in the SDK), the files described by the file set definitions are zipped into file sets (previously "packages"). This typically happens immediately after introspection is complete, but that initial creation of file sets may be deferred, and the OVAB user may choose to recreate the file sets at a later time.

The Oracle Virtual Assembly Builder framework allows the SDK user to provide a plug-in to be invoked at the start of the file set capture process, the goal of which is to ensure that the product is in a state conducive to being captured. For instance, it may be that a product needs to be shut down before file set capture can proceed; if the validation plug-in finds that the product is running, the file set capture can be aborted. The plug-in can provide an error message to be displayed to the OVAB user.

See complete details starting at the PackagerValidationPlugin interface in the SDK.

Registration of the PackagerValidationPlugin is just the same as the registration of the introspector plug-in. The implementation of the PackagerValidationPlugin interface can even be in the same jar as the introspector plug-in, or it could be delivered in a different jar placed in the same directory.

5.6.2.2 Platform and OS Validation

A plug-in may have restrictions on the types of platform and OS it can support. The Oracle Virtual Assembly Builder framework provides a facility for enforcing these requirements after an appliance has been introspected. The JVM of the validation may be different from that of the introspection, so this validation should not rely on state information beyond what is provided in assembly metadata.

To use this facility, create a class to implement the interface PlatformOsValidationPlugin. The installation of this class is similar to the installation of the introspector class. You can deliver the class in the same jar file as the introspector plug-in or in its own jar file in the plug-in directory.

The Oracle Virtual Assembly Builder framework invokes the platform and OS validation for all implementations belonging to the plug-in and its plug-in extensions. Each such implementation provides a validation response, with this response indicating compliance or non-compliance.

Non-compliance can be communicated as either a warning or a rejection, along with a plug-in-specified error message. All warnings are relayed to the user if no rejection is indicated.

5.6.2.3 Template Validation

The Oracle Virtual Assembly Builder framework provides a facility that plug-in writers can use to ensure that any OS base image used for template creation meets the needs of the plug-in's product. Plug-ins can arrange for checks of user and group accounts, software (RPM) packages, kernel configuration parameters, and system swap space.

To use this facility, create a class that implements the interface BaseImageValidationPlugin. The installation of the class is similar to the installation of the introspector class. You can deliver the class in the same jar file as the introspector plug-in or in its own jar file in the plug-in directory.

For each validation that should be performed, the plug-in creates a Validator object describing the validation. The Oracle Virtual Assembly Builder framework traverses the list of validators during the templating process and checks that the proposed base image meets the requirements, emitting the plug-in-specified warning or error message if not.

5.6.3 Plug-in Discovery and Installation

Starting with the 12.1.2.0.0 version of Oracle Virtual Assembly Builder, the introspection plug-ins are not shipped with Oracle Virtual Assembly Builder itself, but are instead shipped and installed with the product that they support. The plug-ins are 'discovered' by Oracle Virtual Assembly Builder and installed into the Oracle Virtual Assembly Builder instance for use. In order to facilitate this, the plug-in writer has a few responsibilities:

  • Install your plug-in with your product into a specific location.

  • Create a plugin.config file and ship/install it along side your plug-in.

  • Create an IntrospectorPluginLocator service and register it from your ServiceActivator (more info on your plug-in's ServiceActivator can be found in Chapter 6, "Developing an Introspection Plug-in").

5.6.3.1 Plug-in Location

Install your introspection plug-in or extension to the following location:

$ORACLE_HOME/plugins/ovab/introspector/<plugin-name>/*

5.6.3.2 plugin.config File

Each plug-in or extension must ship a properties file for use during plug-in discovery, initial installation, and upgrade. This file will reside in each plug-in's directory at the following location:

$ORACLE_HOME/plugins/ovab/introspector/<plug-in name>/plugin.config

The following properties should be published in the plugin.config file:

plugin.name

Name of the plugin/extension, as returned by getName() and same as the directory name. Should be indicative of the product which this plugin/extension is applied to.

plugin.version

The version of the plug-in that changes as plug-in implementation changes. This is different than the version of the product with which the plug-in ships as this version may change more or less frequently. The value must conform to the version format described below.

product.version.min

The minimum version of the product that this plug-in supports. This property is used only for informational purposes. The value must conform to the version format described below.

product.version.max

The maximum version of the product that this plug-in supports. This will usually be equivalent to the version of the product in the installation containing the plug-in. This property is used only for informational purposes. The value must conform to the version format described below.

plugin.description

A brief description of the plug-in/extension. This should include the product(s) which the plug-in/extension operates on.

ovabsdk.version

The minimum version of Oracle Virtual Assembly Builder's Introspector plug-in SDK required by this plugin/extension. This is different than the "OVAB version" and changes less frequently: Oracle Virtual Assembly Builder itself can rev without the SDK actually changing. For the 12.1.2.0.0 version of Oracle Virtual Assembly Builder, the SDK version is 3.0 - so use 3.0 in your plugin.config file. Plug-in writers will be informed whenever the SDK version changes.

parent.plugin.name

The name of the parent plug-in or extension. Set only for extensions (top level plug-ins do not have a parent).

parent.plugin.version

The minimum version of the parent plugin/extension required by this extension. Set only for extensions (top level plug-ins do not have a parent). The value must conform to the version format described below.

Interaction with the property file will include the following behaviors:

  • Oracle Virtual Assembly Builder will report an error if a property file does not exist in a <plugin-name> directory

  • Oracle Virtual Assembly Builder will not report an error when encountering a property that is unknown. This may occur if we are looking at a plug-in home that is newer than the Oracle Virtual Assembly Builder product in use.

  • Oracle Virtual Assembly Builder should document defaults when a property is not specified. This may occur if we are looking at a plug-in home that is older than the Oracle Virtual Assembly Builder product in use.

Version Number Format

All version numbers in the property file must be take the form of a simple "number-dot-number" style version. These version numbers use a syntax that consists of positive decimal integers separated by periods ".", for example "5", "2.0" or "1.2.3.4.5.6.7". This allows an extensible number to be used to represent major, minor, micro, etc. versions. The version specification is described by the following formal grammar:

DotVersion:

Digits RefinedVersion

RefinedVersion:

. Digits

. Digits RefinedVersion

Digits:

Digit

Digits

Digit:

any character for which Character.isDigit(char) returns true, for example, 0, 1, 2, ...

Version numbers are compared by sequentially comparing corresponding components of the version number. Each component is treated as an integer and the values compared. If the value is greater than the desired value true is returned. If the value is less false is returned. If the values are equal the next pair of components is compared. Where one version has more components than the other the missing components are assumed to be 0 and treated as if they were present during comparison.

5.6.4 Implementing an IntrospectorPluginLocator Service

Plug-ins and extensions are, by definition, capable of determining the product root directory of their own product from the job parameters supplied supplied by the user at introspection. Often the product root directory is an explicit job parameter though other times it is derivable from one or more of the specified job parameters. The Introspector Framework is not capable of knowing how to derive this information from the job parameters it passes to plug-ins and extensions, yet it needs to know this information in order to be able to automatically check for plug-in and extension updates at the beginning of each introspection.

To resolve this issue plug-ins and extensions should publish an IntrospectorPluginLocator service implementation.

At the beginning of every introspection (dehydration) the IntrospectorFramework takes the following steps:

  1. Determine the plug-in and any extensions participating in the introspection.

  2. Look for IntrospectorPluginLocator implementations that return the same names from their getPluginOrExtensionName() method.

  3. Build a list of product roots to search by executing the getProductInstallHome() method on each IntrospectorPluginLocator implementation found in step 2. Duplicates are consolidated.

  4. Search in the product roots derived in step 3 for newer versions of the plug-in and extensions found in step 1. If any are found then abort the introspection and report the discovered upgrade candidates back to the user.

IntrospectorPluginLocator implementations are tracked as SPIF services and may be registered in the same manner and from within the same ServiceActivator implementation used to register the corresponding plug-in or extension.

5.6.5 Plug-in Backward Compatibility Requirements

When installing a plug-in or extension, the information in plugin.config is consulted to determine if the plug-in or extension is compatible with what is already installed. It is assumed that it is always safe to update an older version with a newer version. In order for this to work, both plugins and extensions are expected to be backward compatible according to the following definition (applies to both plugins and extensions):

  • A plug-in is assumed to always be backward compatible with respect to the product versions it can dehydrate/rehydrate. In other words, a newer plug-in shipped with a new product must continue to dehydrate/rehydrate the older versions of products it was previously capable of capturing.

  • A plug-in is assumed to always be backward compatible with respect to the catalog metadata it can process. In other words, a newer plug-in must continue to be able to rehydrate a product using metadata created by prior versions of itself.

  • A plug-in is assumed to always be backward compatible with respect to how it interacts with extensions that extend from it. An extension developed against one version of a parent must still continue to work against newer versions of the same parent.

5.6.6 Plug-in Installation Guidelines

Operations related to plug-in and extension installations must adhere to the following guidelines:

  • An extension may not be installed without the parent already installed.

  • A plug-in may not be downgraded to a version older than the version installed unless no appliance created by the plug-in exists. Doing so may break appliances created by the plugin.

  • A plug-in may not be deleted when an appliance created by the plug-in exists. Doing so would break appliances created by the plugin.

  • The deletion of a plug-in also deletes recursively all children of the plugin.

  • Disabling a plug-in also disables recursively all children of the plugin.

  • An extension may not be enabled if its parent is disabled.

These guidelines are enforced by the various plug-in installation operations provided by Oracle Virtual Assembly Builder applications.

5.7 Utilities and Helpers

The plug-in SDK provides some utilities and helper classes for common tasks that may need to be performed by more than one plug-in. The introspector.plug.util package contains UtilFactory, which is registered as a SPIF service. You can get a handle to this service by using the following in your plug-in's SPIF service activator class:

UtilFactory utilFactory =
    (UtilFactory) myCollector.getSingletonService(UtilFactory.class.getName());

5.7.1 OCM Registration

UtilFactory.getOcmUtil()

Use this utility to add Oracle Configuration Manager related properties to an appliance during dehydration, and to register the appliance with OCM during rehydration based on those properties.

5.7.2 OUI Central Inventory

UtilFactory.getOuiUtil()

Use this utility to create the metadata necessary for constructing an OUI Central Inventory and registering the ORACLE_HOME(s) in that inventory during deployment.

5.7.3 SSH

UtilFactory.openSshConnection()

Use the SshConnection class if you need to SSH to another machine to run a command or send/retrieve files.

5.7.4 Velocity

UtilFactory.getContentBuilder()

Velocity is a utility for doing token replacement in files. The ContentBuilder class helps you make a content resource with optional token replacement performed by Velocity.

The RehydrateUtil class also has some Velocity related methods that can be used during rehydration to fix up config files.

5.7.5 Misc Utilities

UtilFactory.getRehydrateUtil()

You can find various utilities for copying files and running commands in the RehydrateUtil class.

5.8 Best Practices

Here are some best practices, caveats and things to keep in mind while designing and developing your introspector plug-in.

5.8.1 Avoid Modifying the Reference System

Your plug-in should not modify the reference host during introspection. Temporary files should not be created under the reference product's directories. The reference product configuration and runtime state should not be altered.

If you need to create temporary files, use the Oracle Virtual Assembly Builder tmp directory. The AbConfig class has a method for finding the location of the Oracle Virtual Assembly Builder tmp directory.

5.8.2 Avoid Static Variables

In Oracle Virtual Assembly Builder Studio your introspector plug-in gets instantiated once and is potentially used repeatedly to do introspection and file set capture operations. You should not use static variables as this may cause unexpected results if multiple introspections of your product are performed in one Studio session.

5.8.3 Do not Attempt to Access SPIF Services during ServiceActivator.start()

While calling ServiceActivator.start(), do not attempt to access SPIF services.

5.8.4 Reserved Names and Naming Restrictions

There are certain restrictions on the names your plug-in can give to the different metadata elements.

5.8.4.1 Appliance and Assembly Names

Appliance and Assembly names must contain only ASCII letters (A-Z a-z), digits (0-9), and these symbols: ! # % ( ) * + , - . : = ? @ [ ] ^ _ { } ~

Name length must be 40 characters or less.

5.8.4.2 Property Names

Property names have a similar character set restriction to Appliance and Assembly names: ASCII letters (A-Z a-z), digits (0-9), and these symbols: ! # % ( ) * + , - . : = ? @ [ ] ^ _ { } ~

There is currently no length restriction for property names, but one may be added in the future.

Do not create any property names that start with 'OVAB', 'Ovab' or 'ovab' as those names are reserved for internal use.

5.8.4.3 Type

The appliance type should not be set to anything starting with 'External', as types that start with that string are reserved for internal use.

5.8.5 Managing External Dependencies

Plug-in developers should be aware that all Oracle Virtual Assembly Builder services, including introspector plug-ins, are loaded by a single classloader. This can create conflicts when the same class is supplied by multiple plug-ins; the plug-ins may not expect to share the class. Plug-ins that supply external jars, that also might be included by other plug-ins, should use the ImplProvider facility described below to load those jars.

A plug-in may need to make use of jars from the target product, for instance to examine or change product configuration using a proprietary API shipped with the product. Using product jars within a plug-in introduces additional maintenance issues because the plug-in code using these APIs may need to change to accommodate new product versions and changed APIs.

Often the use of product jars can be avoided with a little effort but if you determine that you must make use of jars from the target product then there are a couple of options available to you.

The first option is to add those jars to the directory where the plug-in is deployed. The downside of this approach is that the external product jars need to ship both with the product and with Oracle Virtual Assembly Builder. Also, as the product version changes multiple copies of the product jars may need to ship with Oracle Virtual Assembly Builder in order for the plug-in to support each new product version.

The second option is to load the product jars dynamically at runtime from where they are installed with the product. This approach requires a little more effort but eliminates the need to ship product jars (potentially multiple versions!) with Oracle Virtual Assembly Builder. This is the recommended approach.

Ideally, an introspection plug-in or plug-in extension would be able to interact with multiple versions of a product. This becomes more of a challenge in the case where product jars need to be used, as the APIs in those product jars may change from one version to the next. There are multiple solutions to this but we have provided one potential path worth considering.

The basic idea is to load the necessary product jars from the installation dynamically during execution and also create an abstraction layer through which those jars are accessed. Here is an outline of what can be done to use this approach:

  1. Create a "wrapper" interface that abstracts the tasks performed using the product jars. This abstraction should be at a high enough level to be applicable across product versions. The interface(s) can reside in the plug-in's jar that always gets loaded.

  2. Create an implementation of the above interface for each product version where the product API has changed. Initially only one such implementation would be created and more would be added as the product's API changes in subsequent product releases. These implementations can either reside in a jar file within the product installation or can reside in a separate jar within the Oracle Virtual Assembly Builder installation. In either case, the implementation should get loaded dynamically at run time.

  3. At run time, determine the product version and load the corresponding "wrapper" implementation plus the product jars needed by that implementation.

To facilitate this approach:

  • Oracle Virtual Assembly Builder, at startup, skips loading any jars located in directories named "dynlib" found anywhere within Oracle Virtual Assembly Builder's ORACLE_HOME/jlib/ directory. Multiple "wrapper" implementations can be put into this directory and can be loaded dynamically as needed.

  • UtilFactory.createImplProvider() has been provided to assist in dynamically loading "wrapper" implementations and the necessary jars from product installations. Take a look at the javadoc for this method and ImplProvider in the oracle.as.assemblybuilder.introspector.plugin.util package.

Hypothetical example:

/**
 * Interface for interactions with MDS. 
 *
 * Resides in jlib/stuff/myplugin.jar
 */
interface MdsWrapper {
  String getMdsFoo();
  String getMdsBar();
  void setMdsFoo(String value);
  void setMdsBar(String value);
}
 
/**
 * Uses 11g APIs to interact with MDS (fabric-core.jar).
 *
 * Resides in jlib/stuff/dynlib/mdswrapper-v1.jar
 */
class MdsWrapperImplVersion1 implements MdsWrapper {
  ...
}
 
/**
 * Uses 12g APIs to interact with MDS.
 *
 * Resides in jlib/stuff/dynlib/mdswrapper-v2.jar
 */
class MdsWrapperImplVersion2 implements MdsWrapper {
  ...
}

Given the above code the plug-in could obtain the needed implementation according to the product version:

  void doMdsStuff(String oracleHome) {
 
    String fabricJarPath = oracleHome + "/jlib/fabric-core.jar";
 
    String productVersion = getVersion(oracleHome);
 
    MdsWrapper mdsWrapper = null;
  
    // obtain product version specific implementation
    if (productVersion.equals(VER_11G)) {
      ImplProvider implProvider =
          utilFactory.createImplProvider("mdswrapper-v1.jar", fabricJarPath);
      mdsWrapper =
          implProvider.createInstance(MdsWrapper.class, "MdsWrapperImplVersion1");
    }
    else if (productVersion.equals(VER_12G)) {
      ImplProvider implProvider =
          utilFactory.createImplProvider("mdswrapper-v2.jar", fabricJarPath);
      mdsWrapper =
          implProvider.createInstance(MdsWrapper.class, "MdsWrapperImplVersion2");
    }
 
    // use implementation generically
    String foo = mdsWrapper.getMdsFoo();
    String bar = mdsWrapper.getMdsBar();
    mdsWrapper.setMdsFoo(foo);
    mdsWrapper.setMdsBar(bar);
  }

5.9 Testing your Introspector Plug-in

Describes testing of the dehydration and rehydration functions of your plug-in.

5.9.1 Dehydration Testing

Testing the dehydration functionality in your introspector plug-in is straightforward - simply perform an introspection. Refer to the Section 5.6.3, "Plug-in Discovery and Installation" for information on how to get your plug-in installed into the Oracle Virtual Assembly Builder environment.

5.9.2 Rehydration Testing and Troubleshooting

Testing the rehydration functionality in an introspection plug-in can be time consuming and trouble shooting in the OVM environment can be a challenge. This section provides guidance. See also "Troubleshooting" in Using Oracle Virtual Assembly Builder.

5.9.2.1 Deployment Lifecycle

Understanding the steps that occur when Oracle Virtual Assembly Builder deploys an assembly can help you triage failures. There are several actions taken on the VM before your plug-in code is even invoked.

  1. Virtual Machine Creation and Start

    The Oracle Virtual Assembly Builder Deployer instructs OVM manager to create virtual machines for all appliances in the assembly you are deploying. After all the virtual machines are created they are started based on the dependencies between the appliances.

  2. AB Service Creation on the VM

    When the VM starts up, a special script called oraclevm-template.sh is automatically run. This script installs a Linux service called 'ab'. The ab service is responsible for network configuration and for launching the 'Appliance Deploy Driver'.

  3. VM Network Configuration

    The ab service is started by the oraclevm-template.sh script and configures the network settings on the VM.

  4. Late Bindings Retrieved through Phone Home

    After the network setup is complete the ab service launches a Java process: the 'Appliance Deploy Driver'. This Java code handles communication to the Oracle Virtual Assembly Builder Deployer to retrieve the late bindings. It also launches the rehydrator code which calls the plug-ins, and then contacts the Oracle Virtual Assembly Builder Deployer again to report the success or failure of the rehydration.

    The Appliance Deploy Driver contacts the Oracle Virtual Assembly Builder Deployer to retrieve the late binding information.

    Once the late binding information has been retrieved from the Oracle Virtual Assembly Builder Deployer, the metadata on the VM is updated to reflect the late bindings. This updated metadata is under /assemblybuilder/catalog/metadata/root.

  5. Introspector Plug-in Invoked

    After the late bindings are saved to the catalog on the VM (which is a subset of the catalog on your Oracle Virtual Assembly Builder host, containing only as much metadata as is needed to rehydrate the current appliance being deployed), the plug-in's Rehydrator.rehydrate() method is invoked.

  6. Oracle Virtual Assembly Builder Deployer is sent rehydration result

    If the base plug-in and all its extensions complete successfully, the Appliance Deploy Driver sends the successful result to the Oracle Virtual Assembly Builder Deployer. If there is a failure at any point (the base plug-in or any extension throws an exception) then the failure result is sent when the failure occurs.