Advanced Customization > Info*Engine User’s Guide > Custom Tag Libraries
  
Custom Tag Libraries
The following topics discuss implementing and using an Info*Engine custom tag library, and provides information on using expression language within custom tags.
About the Custom Tag Library
The Info*Engine task compiler supports the concept of custom tag libraries. Custom tag libraries for Info*Engine tasks are similar to those supported by JSP. When you implement a custom tag library, you are using sets of Info*Engine tasks, which can be organized in two ways: either within a common subdirectory, or organized as federated task delegates associated with a type identifier. This allows you to easily write reusable tag functionality without requiring you to write or compile Java code. If more advanced functionality is necessary, then you can implement your custom tag library as a set of Java classes instead. These Java classes are described by an XML document called a Tag Library Descriptor (TLD), which associates the classes with the appropriate tags for use by Info*Engine tasks.
Both task and Java tag library implementations allow you to specify whether an attribute is required, and whether or not a given attribute supports expressions or not. It is important to consider what is appropriate for each attribute exposed when writing a tag library. If an attribute is marked as required, then your tag implementation does not need to spend time validating its presence. Info*Engine does not compile a task that uses a tag which does not supply a required attribute value.
If an attribute supports expressions, the value supplied can still be a value other than null. More importantly, however, is whether or not an attribute should support embedded expressions. While expression evaluation is not overly expensive, logically you should mark attributes that are hard-coded to not support expressions. Even where an attribute is marked as supporting expressions, the Info*Engine task compiler examines the attribute value to decide whether or not it needs to evaluate expressions at runtime to avoid unnecessary overhead. For more information, see Expression Language Support.
Task-Based Tag Library Implementation
Tag libraries can be written using rules and procedures similar to those used when authoring Info*Engine tasks. Each task (or command delegate if using a federated type identifier) can be exposed as a single tag. The parameters documented using the taskdoc comments of a task are exposed as the attributes of the tag. Metadata on each parameter can be used to control whether a given attribute is required, and whether or not it supports expressions. By default, attributes support expressions and are not required. Parameters that override the defaults may appear as follows:
<!--com.infoengine.soap.rpc.def
This is the tag description.

@param boolean hardCoded No Expressions {rtexp:false}
@param string required Is required {required:true}
@return INFOENGINE_GROUP ${output}
-->
In this example, metadata on task comments is supplied in curly braces ({ }), and the name is separated from the value by a colon (:). In tags, the @return comment is not used explicitly, but should be included for completeness and to enhance the usefulness of the task.
Specifying a parameter with a type of INFOENGINE_GROUP provides a group from the VDB of the parent task to the VDB of the tag. As such, a tag has access to the contents of the calling task’s VDB, but only to those portions of it that are explicitly required. Array input is added to the tag’s @FORM input as a multivalued parameter. Upon input, the tag inherits all of the calling task’s @SERVER and @AUTH-MAP context information, but not @FORM. Following tag invocation, the tag’s VDB is merged back into the calling task’s VDB.
The calling task’s input and output streams are also associated with whichever tags it calls, so it is possible for a task-based tag to consume input and produce output.
A calling task and the tags it calls can share other information through the “tasklet context.” The tasklet context refers to a pair of maps containing data. Each task gets its own local map to store variable data, and that map is backed by the IeContext object of the overarching request. Each Info*Engine-based request has its own instance of IeContext (com.infoengine.util.IeContext) that can hold contextual information specific to a given request. However, locally-scoped variables in the context are only visible to the task in which they are defined. Sharing information between tasks (and also possibly tags) within the same scope requires request-scoped variables. For more information on the tasklet context, see Expression Language Support.
For example:
<c:set var=“shared” value=“${value}” requestScope=“true” />
Each tag based on an Info*Engine task implicitly supports a child tag that has the same prefix, is named “param,” and has name and data attributes. This tag can be used to supply undocumented parameters to a tag (for example, supplying parameter for a supporting adapter to an existing adapter-based SOAP task). The exception to this rule would be a situation in which the task-based tag libraries already contain a tag named “param”. In this situation the param task takes precedence, and nested param tags are not allowed within that tag library.
Tag Library Organization in the Task Root
There are two primary ways to organize a task-based tag library. The first is to simply create a subdirectory within the task root to hold your tags, and then specify the path of that directory (relative to the task root) as the URI when using your tag library. For example:
<%@taglib uri="/ext/myorg/tags" prefix="my"%>

...
<my:myTag hardCoded="true" required="${expression}" />
...
Each tag within the subdirectory is available as its own tag, and each of its parameters are exposed as its attributes.
Tag Library Organization Using a Federated Type Identifier
The second method of organizing an Info*Engine task tag library is using a federated type identifier. In this situation, the value you specify on the URI attribute is the type identifier with which your tags are associated. This method of organization has the advantage of allowing tags to support federation and hierarchy climbing. If you choose to use hierarchy climbing, a given tag within a tag library can map to multiple tasks, depending on the input given at task delegation. When using this mechanism, every tag implicitly supports an input attribute that allows the caller to supply the input group used to drive task delegation (the exception being tags that already explicitly define an input attribute). Both the unique federation identifier (UFID) and federated type identifier on the input group are implicitly qualified if not explicitly supplied.
If necessary, you can override the type hierarchy climber implementation within your task’s context. Prior to task delegation, you can create a key by replacing “.” with “_” in the federated type identifier, appending _climber (for example, wt_fc_Persistable_climber), and by placing the climber class name in the tasklet context. As a result, the new value for the climber implementation is discovered in the tasklet context and then used during task delegation. The default is to use a Windchill type hierarchy climber, which should be suitable for the vast majority of usages. For an example, see Dispatch-Tasks.
As an example, if you wrote a create method associated with wt.part.WTPart, it could be called as follows:
<%@taglib uri="wt.part.WTPart" uri="prt" %>
...
<prt:create name="myPart" />
...
As previously mentioned, however, this mechanism can be more effective if used with type hierarchies, which would allow for not only dynamic discovery of the appropriate delegate for a task, but also routing to an appropriate system. For example:
<%@page language="java"%>
<%@taglib uri="http://www.ptc.com/infoengine/taglib/core" prefix="ie"%>
<%@taglib uri="/org/myorg/util/" prefix="util"%>
<%@taglib uri="http://www.ptc.com/infoengine/taglib/log" prefix="log"%>
<%@taglib uri="/com/infoengine/tlds/iejstl.tld" prefix="c"%>
<%@taglib uri="WCTYPE|wt.fc.Persistable" prefix="obj"%>
...
<!-- create a document in two systems (assumed tag implementations) -->
<util:query type="wt.org.WTPrincipal"
where="uid='${@SERVER[0]auth-user[0]}'"
attribute="name,cabinetRef" groupOut="me" />

<!-- create the task delegate input -->
<ie:webject name="Create-Group" type="GRP">
<ie:param name="ELEMENT"

data="CLASS=wt.doc.WTDocument~name=${@FORM[]docName[]}~folderingInfo.cabinet=${me[0]cabinetRef[0]}"/>
<ie:param name="DELIMITER" data="~" />
<ie:param name="GROUP_OUT" data="doc"/>
</ie:webject>

<!-- create the document in this system -->
<log:debug message=”creating document ${doc[0]name[0]} locally” />
<obj:Create input=”${doc}” />
<!-- create the document in another system -->
<log:debug message=”creating document ${doc[0]name[0]} remotely” />
<c:set var=”${doc[0]obid}” value=”@otherdomain.myorg.org” />
<obj:Create input=”${doc}” />
...
* 
Some of the tags above do not actually exist, but are used here for illustration purposes only.
Java-Based Tag Library Implementation
In some cases, writing a Java-based tag library is more appropriate. For example, if you need to integrate with other Java-based APIs that are currently unavailable through Info*Engine, and as a result would require scriptlet code to be embedded within your tasks (or task-based tags). When writing a Java-based tag library implementation, there is a set of interfaces and support base classes that you can implement, and which can then be reused as tags within Info*Engine tasks. Refer to the com.infoengine.task.tagext package within the Javadoc released with your installation (/codebase/infoengine/docs/apidocs) to understand the classes and interfaces.
In this case, tags are simple event-driven Java classes, that you can group together into libraries of tags. You can do this using a simple XML document referred to as a Tag Library Descriptor (TLD) that defines the library and what tags it supports, what attributes each tag supports, and information about the tags and their attributes. A TLD consists of a root taglib node, an optional description node, and an arbitrary number of child tag nodes. Each tag node defines a tag within your tag library and associates that tag with its implementation class. The tag node must contain the name and tagclass child nodes (specifying the tag name and tag implementation class, respectively), and can optionally contain a description node and several attribute nodes.
If added, any attribute nodes specify the attributes a tag supports, whether or not an attribute is required, and if its values can be an expression. For these purposes an attribute node must have name, required, and rtexprvalue child nodes to supply this information. A fragment of a TLD file might look like the following:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<taglib>
<description>My taglibrary.</description>
<tag>
<name>myTag</name>
<description>what this taglibrary does</description>
<tagclass>org.myorg.tags.MyTag</tagclass>
<attribute>
<name>myAttribute<name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
...
</taglib>
In this case, the tag library contains a tag named “myTag,” which is implemented by the “org.myorg.tags.MyTag” class. The tag requires a single attribute, named “myAttribute,” which can be an expression. To include your tags within an Info*Engine task requires that you specify an @taglib directive in your task that defines a prefix for use with your tags within the task, and also specifies the location of your TLD file (located in the Info*Engine task root). For example, this tag could be used within an Info*Engine task as follows:
<%@taglib uri=”/org/myorg/myTags.tld” uri=”pfx”%>
...
<pfx:myTag myAttribute=”${@FORM[]myAttributeValue[]}” />
Tag attributes are supplied to your tag using standard Java bean-like set accessor methods. When loading a tag library, Info*Engine verifies all of these implementation details. An error is produced if your tag cannot be properly loaded because of an implementation detail, such as a missing setter for a supported attribute. In the example above, the tag might have a setter method as follows:
public void setMyAttribute ( String value )
{
// perhaps set an instance variable or other work
// related to the attribute value specified in the value
// parameter.
}
Tag attributes can have arbitrary data types, and at runtime values are validated or coerced, if necessary. The supported data types of parameter values are:
String
int
boolean
float
com.infoengine.object.factory.Group
Object
Coercion is only supported to the primitive types String, int, boolean, and float. Because Object is included, you can use attributes of any possible type, however in this case your setter method must supply any required type validation or coercion of the attribute value.
Individual tags (implementations of “com.infoengine.task.tagext.Tag”) can be simple, or they can contain child elements (implementations of “com.infoengine.task.tagext.BodyTag”). Instances of BodyTag can also support iteration. A Tag or BodyTag also have the option to implement TryCatchFinally as a means to handle exceptions.
Typically a tag extends either the TagSupport or BodyTagSupport base classes and completes the implementation to add attribute setters and functionality in the necessary do*Tag methods.
In addition, tags can be designed to implicitly set variables within tasks for reuse from scriplet code or from expression language. A tag can support this by implementing the following method defined on “com.infoengine.task.tagext.Tag”:
Tag.VariableInfo[] getVariableInfo ( Map<String,String>atts );
This method is only called at task compilation time to decide whether or not implicit variables need to be defined in the compiled task source. See the Javadoc for a complete description of the getVariableInfo method and how to use this functionality.
At runtime, tags are pooled to avoid performance issues related to constructing large numbers of short-lived objects, which may require excess garbage collection. Therefore, you must be careful that your tag uses optional attributes and stores attribute values in instance variables. You should then implement the doEndTag method and reinitialize instance variables within a finally block. If this is not done, subsequent usages of your tag may not perform as predicted if it carries instance variable values from previous uses.