6 Best Practices
This chapter offers some best practices to help you create high quality cartridges.
Choosing Whether to Extend a Cartridge or to Plan a New Cartridge
If you are considering adding support for a new service type using the SDK, you can either add that to one of your pre-existing service cartridges, or you can choose to create a new service cartridge.
In general, the guidelines are as follows:
-
Services that have inter-dependencies (other than interface/sub-interface creation) must have their implementations co-exist in the same cartridge.
-
Service types that will have many instances on a device (for instance, VPLS service compared to SNMP setup service) must exist in their own cartridge.
-
Go with the minimal set of cartridges to implement the desired set of services within the above constraints (inter-dependencies, model size, choice of Java vs. XQuery). This is because there is overhead when you add each service cartridge. The actual overhead will depend on the set of cartridges and the set of services on a given device.
Choosing the Cartridge Implementation
You can choose to create either Java or XQuery-based cartridges. This section provides some information to help you choose.
Developer Knowledge
Java knowledge (and experience with additional libraries such as XMLBeans, SAX, DOM) is more commonly available than XQuery knowledge.
Transformation Complexity
XQuery is very good at mining data from the source document and putting it into the destination document. However, it does not excel at complex flow logic, modularization, or computation. If you require any of these, you have two options:
-
Write your basic flow in XQuery, as well as any simple transforms, and write the more complex parts in Java. Call the Java methods from XQuery.
or
-
Write everything in Java.
Model Size
The size of the Service Model and Device Model depend on a number of factors:
-
The number of instances of services of this kind that will go on one device.
-
The size of an individual instance (for example, some PHB instances can get very large).
-
The number of commands generated for an instance.
Typically, the larger the model, the more Java has to figure in the transformation, either through an XQuery framework seeded with Java callouts, or by using only Java.
Time to Complete
With knowledgeable developers, XQuery typically delivers working cartridges earlier than Java. Prototyping is typically easier with XQuery.
XQuery Advantages and Disadvantages
The advantages of using XQuery are:
-
Faster development cycles (at least, initially)
-
Very rich language for the functionality it covers
-
Easily field modifiable
The disadvantages to using XQuery are:
-
Very primitive modularization concepts (and object orientation concepts)
-
No schema awareness, limited type checking
-
Control flow structures are difficult to use, especially beyond simple flow control
-
Runtime exceptions come from Saxon and are difficult to debug
-
Additional IDE for full developer convenience required (e.g. oXygen, XMLspy)
-
Very easy to write sub-optimal code and XPath expressions (both in terms of memory use and execution time)
-
Code flow must mimic the sequence and structure of the output XML document which leads to convoluted (spaghetti) code without a lot of constant effort
Java Advantages and Disadvantages
The advantages of using Java are:
-
Full fledged programming language
-
Multiple ways to deal with XML data
-
Easier to layout responsibilities for behavior and data abstraction
-
Can be written much faster
-
Standard Java development, debugging, and profiling tools are enough
-
Allows developer to greatly optimize the generation of the Device Model from the Service Model by viewing it as a set of modifications to be performed on (a copy of) the Last Device Model, rather than as a complete re-generation of the new Device Model
XQuery Transform Best Practices
This section offers best practices to create high quality XQuery transforms for cartridges.
XQuery Performance Optimization
XQuery is not a procedural language, so care must be taken to not write code in a procedural fashion. The XQuery engines implement performance optimizations that can often cause side-effects when attempting to write procedural code with XQuery.
Consider the code below:
declare function myFunction($source as element()*) as xs:boolean { let $list := for $item in $source return $item/itemvalue return if (fn:count($list) > 0) then true() else false() ;
This code returns the correct Boolean value, but the inner loop may not execute on all possible items in the source. Optimizations may cause the loop to terminate once the following ‘if' expression is fully satisfied (i.e. there is something in the list).
This is just one example of how the optimizations take place. Many more can occur and most are driven by determining dependencies and the engine only evaluating things that need to be evaluated.
Another optimization example follows:
declare function myFunction2($source as element()*) as xs:boolean { let $list := myFunctionA ($source/element) let $list2 := myFunctionB ($source/element2) return $list
This can evaluate $list by calling the function myFunctionA, but the return value specifies only $list and not $list2 so myFunctionB may never be called.
XQuery Searches
Care must be taken while creating XQuery searches. XQuery always checks for all possible search combinations. In some cases, the application designer is aware that there is only a single match for something for a search item, but XQuery does not provide an accurate representation of it. Instead, it evaluates all the possible combinations. The easiest way to reduce the performance cost of a search is to be careful about the scope of all searches.
For more efficient searches, avoid searching with “//" as this causes two problems:
-
Performance impact
-
Can unexpectedly return results at different levels in the document. It is always better to be more explicit about scope.
Best Practices for Coding XQuery
The following points define the best practices while writing codes in XQuery:
-
Care should be taken while writing loops, especially nested loops. Nested looping, even when constrained with a ‘where' clause, can be very risky. The behavior is very similar to general searching where all the possible combinations are evaluated.
-
It is advisable to break code into separate modules, and use smaller functions wherever possible.
-
Some functions are not always appropriate for implementing in XQuery. Very complex methods are more appropriate to do in Java. For example, cases where data can be held and stored in Maps instead of continually searching, are more appropriately done in Java.
Syntax For Entering Control Characters in XQuery
You can enter any characters from the ASCII character set (0-255) in any text editor using the following procedure:
-
On your keyboard, press the Num Lock key so that Num Lock is turned on
-
Press and hold the Alt key
-
Using the keypad on your keyboard, enter the decimal code of the character as a 3 digit number, padded on the left with zeros, if necessary (for example, 009 for tab)
-
Release the Alt key
Alternatively, many text editors allow you to select control characters from a list for insertion. For example, in TextPad, use the command View->Clip Library.
In addition to entering ASCII characters on Windows editors like Notepad and Wordpad, there are ways to enter these characters in UNIX editors like vi and emacs.
For example:
-
Ctrl + I = horizontal TAB (numerical decimal value = 9)
-
Ctrl + J = Line Feed (10)
-
Ctrl + M = carriage return (13)
Java Transform Best Practices
his section provides some best practices for creating quality Java transforms.
Java Searches
Similar to the best practices for XQuery, the same care should be taken when using XPath statements to search for objects. This is most important when constructing the device model. Since you are creating the device model using Java, unlike with XQuery, you have random access to any part of the device model that you have already created. To improve search performance, use XmlCursor or even better, XmlBookmarks as you are creating the device model. The bookmarks allows you to quickly go back to a part of the device model you have created without using XPath to search for it.
Service Model to Device Model Java Transform
When implementing an Sm2Dm transform in Java, you will need to create a class that extends the ModelTransformer class. Then you will need to provide two methods. The first is transform(), which the Network Processor will look for and call to perform the service model to device model transform.
The second method you must supply is getAppInfo(). This method is used to get the version of your device model. This value is used to determine if any upgrade procedures need to be done to your device model between releases of your cartridge.
An example of a skeleton Sm2Dm class follows:
public class Sm2dm extends ModelTransformer {
public XmlObject transform(
XmlObject doc,
Map params,
Map uris) throws Exception {
// get the last DM
com.metasolv.serviceactivator.devicemodel.DeviceDocument lastDm;
try {
lastDm = (com.metasolv.serviceactivator.devicemodel.DeviceDocument)((com.metasolv.serviceactivator.devicemodel.DeviceDocument)uris.get("last_dm")).copy();
} catch (Exception e) {
lastDm = null;
}
com.metasolv.serviceactivator.servicemodel.DeviceDocument sm;
sm = (com.metasolv.serviceactivator.servicemodel.DeviceDocument)doc.copy();
com.metasolv.serviceactivator.devicemodel.DeviceDocument targetDm;
// Your transform here.
return targetDm;
}
public XmlObject getAppInfo() {
AppInfo appInfo = AppInfo.Factory.newInstance();
appInfo.setVersion("1.0.0");
return appInfo;
}
}
Annotated Device Model to CLI Document Java Transform
When implementing a Dm2Cli transform in Java, you will need to create a class that extends the ModelTransformer class. Then, you will need to provide a method called transform() that the network processor will look for and call to perform the device mAn example of a skeleton Dm2Cli class is shown below:
An example of a skeleton Dm2Cli class is shown below:
public class AnnotatedDm2Cli extends ModelTransformer {
public XmlObject transform(XmlObject doc, Map params, Map uris) throws Exception {
com.metasolv.serviceactivator.devicemodel.DeviceDocument lastDm;
try {
lastDm = (com.metasolv.serviceactivator.devicemodel.DeviceDocument)uris.get("last_dm");
} catch (Exception e) {
lastDm = null;
}
XmlObject annotatedDm = doc.copy();
CommandSessionDocument commandSessionDoc;
// Your transform here.
return commandSessionDoc;
}odel to CLI transform.
Best Practices for Extending the Device Model
Some best practices for creating device model extension .xsd files are:
-
Define one element per command and one subordinate element for each command parameter. For example, keyword param1 param2 below:
<keyword> <param1>value_1</param1> <param2>value_2</param2> </keyword>
-
When defining the type for an element that maps to a command, extend lib:Changeable. This will allow annotate() (the DM compare processor) to mark it
@changeType="ADD"|"DELETE"
. -
Make an element Changeable and Identifiable if the command supports modifications (as opposed to delete and re-add). This will allow annotate() to mark it
@changeType="ADD"|"DELETE"|"MODIFY"
. An element is Identifiable if it belongs to a lib:Container and some of its subordinated elements make up a key within the scope of the container. -
Make an element Identifiable but not Changeable if it is only used as context for configuration items managed by Oracle Communications IP Service Activator (such as interfaces).
-
Use *Ref in the name of an element that references other elements.
-
Use XML schema validation as much as possible. For example, use min/maxOccurs, min/maxInclusive, pattern.
-
Do not define unnecessary elements or types. Use containers only for defining scope for Identifiable elements.
-
Do not use any unnecessary abstraction in the DM. Instead of generating multiple command types from a single DM element type, define one element type for each CLI command that needs to be generated. The SM2DM transformation should take care of the abstract-concrete mapping.