Info*Engine Task Invocation with SOAP
For an Info*Engine task to be invoked through a SOAP request, the following requirements must be met:
• The task should be located in a directory path under the task root directory that corresponds with the logical CLASS argument. This keeps the tasks (or methods) supported by the class logically and hierarchically organized. The task can be located anywhere within the task root, as long as the associated task delegate entry properly references the task. Tasks can be located anywhere within the task hierarchy.
• The task must be registered as a task delegate within a type identifier whose name reflects the CLASS used within the SOAP request. Task delegates can be manually created, either using the Delegate Administration utility, or using supplied tools.
• The task must include special SOAP comments lines within the SOAP markup comment if the task requires input parameters or returns a response other than an Info*Engine group. For more information, see the section “Inserting SOAP Comments”.
|
Despite the fact that leaving the @return comment out and specifying a comment produce the same results in the client, PTC recommends that you include a comment such as the following:
@return INFOENGINE_GROUP $(output)
Not specifying an @return comment means the data is serialized as XML and tunneled in the response as a string, requiring the client to deserialize the SOAP and process the internal XML. When returning an Info*Engine Group, specifying the @return line is suggested since doing this produces a more useful interface and performs better.
|
Supported Data Types
The Info*Engine SOAP RPC servlet supports the following data types, which include primitive types, Java classes, and special Info*Engine classes:
• boolean, java.lang.Boolean
• double, java.lang.Double
• long, java.lang.Long
• float, java.lang.Float
• int, java.lang.Integer
• short, java.lang.Short
• byte, java.lang.Byte
• string, java.lang.String
• dateTime, java.util.Date
• java.math.BigDecimal
The following Info*Engine-specific data types are also supported for purposes of transferring Info*Engine data structures as embedded XML within SOAP requests:
• INFOENGINE_COLLECTION
• INFOENGINE_GROUP
• INFOENGINE_ELEMENT
• INFOENGINE_ATTRIBUTE
Parameters of each of these types are provided to the supporting task as the equivalent Java class. Single values of each of these types may be used as input parameters or as an output value.
|
In order for the SOAP service to supply strongly-typed input parameters, the SOAP client must supply a strongly-typed SOAP request. Some third-party clients, such as Microsoft’s SOAP Toolkit, do not do this and as a result all input parameters are supplied to the task as strings.
|
In addition to basic data types and arrays of basic data types, Info*Engine SOAP also supports complex types in the form of Java beans, both individually and in arrays. Indexed properties and nested beans are also supported. Returning nested beans requires generation of complex Info*Engine groups. These complex groups must be created manually as Info*Engine webjects do not currently generate complex nested groups.
The class of a Java bean must be found in the Info*Engine server and SOAP RPC servlet CLASSPATH so that the bean can be instantiated, examined, and manipulated at runtime. Similarly, if the client side of the SOAP communication uses Info*Engine classes, such as the Info*Engine Java EE connector, the Java bean class must also be available in the client CLASSPATH. For more information on the Info*Engine Java EE connector, see
Info*Engine Java EE Connector.
SOAP Comments
An Info*Engine SOAP task must contain a comment section that describes the input parameters and response type. This SOAP comment section is used by Info*Engine to generate a WSDL that describes the interface each Info*Engine task exposes to external SOAP clients. The generated WSDL can then be used by third-party software to verify input parameters and cast response types, or to generate client-side source code used to interact with the Info*Engine SOAP service. Certain tools supplied with Info*Engine make use of the WSDL to generate client side data access objects (DAOs) that can be used to invoke Info*Engine tasks and abstract all of the details involved in using SOAP as the communication protocol. For more information, see
Generating DAOs.
The SOAP comment section can also be used to generate Javadoc-like documentation for tasks. For instructions on generating this documentation, and a link to the generating tool, see the document available in Windchill at the following URL:
http://<host>/<servlet>/infoengine/jsp/tools/doc/howto.html
where <servlet> is the application URL defined for the SOAP servlet.
|
This comment section should be included in all SOAP tasks, even if the task includes no input parameters and returns the default Info*Engine group as a response.
|
The comment section is as follows:
<!--com.infoengine.soap.rpc.def
description
special_comments
-->
where description is an optional task description, and special_comments are the special SOAP comment lines.
Special SOAP comment lines can be included in the comment sections to specify input parameters and output values. These special comment lines are discussed in the next two sections.
@param
The @param comment line is used to specify input parameters. The comment line is specified in the following format:
@param type[] name comment
where:
• type is the data type of the parameter.
• [] indicates an array of values. There are no spaces between the parameter type and []. Arrays are available in the @FORM context group as multi-valued attributes. This allows for simple manipulation of multi-valued input with standard Info*Engine substitution syntax.
• name is the name of the parameter.
• comment is additional text describing the parameter. This comment is optional.
|
Some of the Info*Engine-specific data types are processed differently from the other data types in that they do not need scriptlet code to extract the input.
|
When a collection is given as a parameter, the collection itself is not given as input to the task. Instead, all groups within the input collection are placed in the VDB so that they can be used by the task. The task author is expected to know the names of the groups. For example:
@param INFOENGINE_COLLECTION input_collection A collection of Groups.
When a group is given as a parameter, that group is placed directly in the task’s VDB so that it can be used directly. Additionally, the group is renamed using the parameter name if the group supplied by the caller is named differently. The contents of the group parameter below are available in a task using form substitution such as $(input_group[]attribute[]):
@param INFOENGINE_GROUP input_group A group.
When an element or array of elements is supplied as input, they are placed in a group named using the parameter name and added to the VDB. The input element specified with the parameter below would be available in a task using form substitution such as $(input_element[0]attribute[]). In the following example the group named input_element would contain a single element:
@param INFOENGINE_ELEMENT input_element An element.
@return
The @return comment line is used to specify output or response type. If no @return comment line is present, then the Info*Engine SOAP service responds with a serialized Info*Engine XML representation of the output group. The output group must be named using the $(@FORM[]group_out[]) parameter value. The comment line is specified in the following format:
@return type[] substitution comment
where:
• type is the data type of the response.
• [] indicates an array of values. There is no white space between the response type and [].
• substitution is a form similar to the standard Info*Engine substitution syntax:
$(groupName[elementIndex]attributeName[attributeIndex])
where:
◦ groupName is the name of the output group.
◦ elementIndex is the position of the element. If not specified, the elementIndex value defaults to 0, the first element.
◦ attributeName is the name of the attribute.
◦ attributeIndex is the position of the attribute on the element. If not specified, the attributeIndex value defaults to 0, the first attribute.
To populate a return array, the asterisk character “*” must be present in either the elementIndex or attributeIndex portion of the substitution syntax, but not both, as two dimensional arrays are not supported. If “*” is specified as the attributeIndex, then the array is generated from a multi-valued attribute from within a single element of the output group. If “*” is specified as the elementIndex, then the array is generated using the first value of each attributeName from each element of the output group.
When returning a Java bean, the @return comment line is specified as above, with the following two exceptions:
• The attributeName[attributeIndex] portion of the substitution syntax is not present.
• To return an array of Java beans, the “*” character must be in the elementIndex location of the substitution syntax.
The Java bean (or array of Java beans) are populated based on attributes found in referenced elements of the output group. Attribute names in the response group correspond directly to the properties of the Java bean. Attribute names in the response group might need to be manipulated (for example, using the Change-Group webject) to force the group attribute names to match the corresponding properties of the Java bean.
When returning Info*Engine data using the Info*Engine-specific data types, the format for the @return parameter is as follows:
@return INFOENGINE_COLLECTION
@return INFOENGINE_GROUP $(group_Name)
@return INFOENGINE_ELEMENT $(groupName[elementIndex])
@return INFOENGINE_ATTRIBUTE $(groupName[]attributeName)
where:
• groupName is the name of the output group.
• elementIndex is the position of the element. If not specified, the elementIndex value defaults to 0, the first element.
• attributeName is the name of the attribute.
Simple Data Type Examples
The following examples do not represent the types of activities that Info*Engine tasks should be written to perform. Rather, they are intended to offer an example of how input and output parameters function.
|
Because these tasks contain scriptlet code that assumes strongly-typed input parameters, they therefore cannot be called directly from a web browser using the Info*Engine servlet because the @FORM data would not be properly typed. These special SOAP tasks can be successfully called only by SOAP clients that generate strongly-typed SOAP requests. If the request is not strongly typed, a ClassCastException occurs.
|
The following example specifies two integers as input parameters, and returns their sum:
<%@page language="java"%>
<%@ taglib uri="http://www.ptc.com/infoengine/taglib/core"
prefix="ie"%>
<!--com.infoengine.soap.rpc.def
this task takes two integers and adds them together
@param int x
@param int y
@return int $(output[]sum[])
-->
<%
Integer x = (Integer)getParam ( "x" );
Integer y = (Integer)getParam ( "y" );
String element = "sum=" + (x.intValue()+y.intValue());
%>
<ie:webject name="Create-Group" type="GRP">
<ie:param name="ELEMENT" data="<%=element%>"/>
<ie:param name="GROUP_OUT" data="output"/>
</ie:webject>
The following example returns the sum of an array of floats:
<%@page language="java"%>
<%@ taglib uri="http://www.ptc.com/infoengine/taglib/core"
prefix="ie"%>
<!--com.infoengine.soap.rpc.def
this task sums an array of floats
@param float[] toSum
@return float $(output[0]sum[0])
-->
<%
java.util.Vector floats = getParams ( "toSum" );
Float [] toSum = new Float[floats.size()];
floats.copyInto ( toSum );
float sum = 0.0;
for ( int i = 0; toSum != null && i < toSum.length; i++ )
sum +=toSum[i].floatValue();
String element = "sum=" + sum;
%>
<ie:webject name="Create-Group" type="GRP">
<ie:param name="ELEMENT" data="<%=element%>"/>
<ie:param name="GROUP_OUT" data="output"/>
</ie:webject>
The following example queries an employee database and returns an array of all employee numbers:
<%@page language="java"%>
<%@ taglib uri="http://www.ptc.com/infoengine/taglib/core"
prefix="ie"%>
<!--com.infoengine.soap.rpc.def
this task queries an employee database and returns an array of all
employee numbers
@return string[] $(output[*]empno[])
-->
<ie:webject name="Query-Objects" type="OBJ">
<ie:param name="INSTANCE" data="jdbcAdapter"/>
<ie:param name="CLASS" data="EMP"/>
<ie:param name="WHERE" data="()"/>
<ie:param name="GROUP_OUT" data="output"/>
</ie:webject>
Java Bean Examples
|
These Java bean examples are using a JNDI adapter to create user objects in an LDAP directory. These user objects should not be confused with user objects that a Windchill can use.
|
The examples in this section use the following Java bean:
package my.org;
// exposes properties:
// cn, sn, dn, uid and indexed property mail
// properties could be any primitive types or java beans
// but for the sake of an LDAP example things are just strings
import java.util.Vector;
public class Person {
private String cn;
private String sn;
private String dn;
private String uid;
private Vector mails = new Vector ( 1 );
public void setCn ( String n ) { cn = n; }
public String getCn () { return cn; }
public void setSn ( String n ) { sn = n; }
public String getSn () { return sn; }
public void setDn ( String n ) { dn = n; }
public String getDn () { return dn; }
public void setUid ( String n ) { uid = n; }
public String getUid () { return uid; }
public void setMail ( int index, String m ) {
if ( index == mails.size() )
mails.addElement ( m );
else
mails.setElementAt ( m, index );
}
public String getMail ( int index ) {
return (String)mails.elementAt ( index );
}
public void setMail ( String vals[] ) {
mails.clear();
for ( int i = 0; vals != null && i < vals.length; i++ )
mails.addElement ( vals[i] );
}
public String [] getMail () {
if ( mails.isEmpty() ) return null;
String arr [] = new String[mails.size()];
mails.copyInto ( arr );
return arr;
}
}
The following example looks up a user entry by user id (uid):
<%@page language="java"%>
<%@ taglib uri="http://www.ptc.com/infoengine/taglib/core" prefix="ie"%>
<!--com.infoengine.soap.rpc.def
looks up a user entry by uid
@param string uid
@return my.org.Person $(output[0])
-->
<ie:webject name="Query-Objects" type="OBJ">
<ie:param name="INSTANCE" data="com.myCompany.myHost.ldap"/>
<ie:param name="BASE" data="ou=People,o=Company"/>
<ie:param name="SCOPE" data="subtree"/>
<ie:param name="FILTER" data="uid=$(@FORM[]uid[])"/>
<ie:param name="GROUP_OUT" data="output"/>
</ie:webject>
<ie:webject name="Change-Group" type="GRP">
<ie:param name="GROUP_IN" data="output"/>
<ie:param name="GROUP_OUT" data="output"/>
<ie:param name="RENAME" data="'object'='dn'"/>
</ie:webject>
The following example searches for a list of users using an LDAP search filter:
<%@page language="java"%>
<%@ taglib uri="http://www.ptc.com/infoengine/taglib/core" prefix="ie"%>
<!--com.infoengine.soap.rpc.def
searches for a list of users by an LDAP search filter
@param string filter
@return my.org.Person[] $(output[*])
-->
<ie:webject name="Query-Objects" type="OBJ">
<ie:param name="INSTANCE" data="com.myCompany.myHost.ldap"/>
<ie:param name="BASE" data="ou=People,o=Company"/>
<ie:param name="SCOPE" data="subtree"/>
<ie:param name="FILTER" data=" $(@FORM[]filter[])" default="uid=*"/>
<ie:param name="GROUP_OUT" data="output"/>
</ie:webject>
<ie:webject name="Change-Group" type="GRP">
<ie:param name="GROUP_IN" data="output"/>
<ie:param name="GROUP_OUT" data="output"/>
<ie:param name="RENAME" data="'object'='dn'"/>
</ie:webject>
The following example creates a user in an LDAP directory. The results are returned in Info*Engine XML format, as there is no @return comment specified:
<%@page language="java"%>
<%@ taglib uri="http://www.ptc.com/infoengine/taglib/core" prefix="ie"%>
<!--com.infoengine.soap.rpc.def
creates a user in LDAP. returns I*E XML.
@param string dbuser
@param string passwd
@param my.org.Person toCreate
-->
<%
my.org.Person toCreate = (my.org.Person)getParam ( "toCreate" );
String dn = toCreate.getDn();
String uid = "uid=" + toCreate.getUid();
String cn = "cn=" + toCreate.getCn();
String sn = "sn=" + toCreate.getSn();
String [] mails = toCreate.getMail();
StringBuffer sb = new StringBuffer();
for ( int i = 0; mails != null && i < mails.length; i++ )
sb.append ( "mail=" )
.append ( mails[i] )
.append ( ( i < (mails.length-1) ) ? ";" : "" );
%>
<ie:webject name="Create-Object" type="ACT">
<ie:param name="INSTANCE" data="com.myCompany.myHost.ldap"/>
<ie:param name="DBUSER" data="$(@FORM[]dbuser[])"/>
<ie:param name="PASSWD" data="$(@FORM[]passwd[])"/>
<ie:param name="DN" data="<%=dn%>"/>
<ie:param name="FIELD" data="objectClass=inetOrgPerson"/>
<ie:param name="FIELD" data="objectClass=person"/>
<ie:param name="FIELD" data="<%=uid%>"/>
<ie:param name="FIELD" data="<%=cn%>"/>
<ie:param name="FIELD" data="<%=sn%>"/>
<ie:param name="FIELD" data="<%=sb.toString()%>" delim=";"/>
<ie:param name="GROUP_OUT" data="output"/>
</ie:webject>
BLOB Attachments on SOAP Requests
Attachments can be used with SOAP requests to upload and download small amounts of binary data.
|
Only relatively small amounts of binary data should be transferred using SOAP. For information on uploading and downloading larger BLOBs, see Uploading and Downloading BLOBs .
|
Two special data types are used when attaching data to a SOAP request: javax.activation.DataSource and java.io.InputStream. These special data types support a specially formatted SOAP comment optionally listing the binary content type. Specifying the content type allows for more complete generated WSDL, including what type of content data is expected or is returned. Any valid content type can be specified, for example:
• image/.gif
• image/.jpeg
• application/octet-stream
If no content type is specified, then the default application/octet-stream is used.
javax.activation.DataSource
Indicates that the client is attaching binary data. This data type is used only in an @param SOAP comment.
Use the following format:
@param javax.activation.DataSource file {contentType:content_type}
where content_type is the content type of the binary data.
java.io.InputStream
Indicates that the task responds with binary data. Used only in an @return SOAP comment.
Use the following format:
@return java.io.InputStream {contentType:content_type}
where content_type is the content type of the binary data. When the response is an attachment, no substitution syntax is specified.
Info*Engine does not support returning both an attachment and data. When a task produces a BLOB for SOAP, the SOAP response contains an empty response element.
Example BLOB Upload
The following task adds a BLOB to a database row.
<%@page language="java" session="false"%>
<%@taglib uri="http://www.ptc.com/infoengine/taglib/core"
prefix="ie"%>
<!--com.infoengine.soap.rpc.def
upload a blob
@param string filename - the filename to store
@param javax.activation.DataSource file - the file to store
(should be gif or jpeg)
-->
<ie:unit>
<ie:webject name="Do-Sql" type="ACT">
<ie:param name="INSTANCE" data="soapJDBCAdapter"/>
<ie:param name="SQL" data="DELETE FROM BLOBTABLE WHERE
name='$(@FORM[]filename[0])'"/>
<ie:param name="CLASS" data="BLOBTEST"/>
<ie:param name="GROUP_OUT" data="deleteResult"/>
<ie:param name="BLOB_COUNT" data="0"/>
</ie:webject>
<ie:failure/>
</ie:unit>
<ie:webject name="Do-Sql" type="ACT">
<ie:param name="INSTANCE" data="soapJDBCAdapter"/>
<ie:param name="SQL" data="INSERT INTO BLOBTABLE VALUES
('$(@FORM[]filename[0])', NULL)"/>
<ie:param name="CLASS" data="BLOBTEST"/>
<ie:param name="GROUP_OUT" data="insertResult"/>
<ie:param name="BLOB_COUNT" data="0"/>
</ie:webject>
<ie:webject name="Put-Blob-Stream" type="OBJ">
<ie:param name="INSTANCE" data="soapJDBCAdapter"/>
<ie:param name="CLASS" data="BLOBTABLE"/>
<ie:param name="ATTRIBUTE" data="FILECONTENT"/>
<ie:param name="WHERE" data="(NAME='$(@FORM[]filename[0])')"/>
<ie:param name="GROUP_OUT" data="$(@FORM[]group_out[])"
default="output"/>
</ie:webject>
Example BLOB Download
The following task retrieves a BLOB from a database row.
<%@page language="java" session="false"%>
<%@taglib uri="http://www.ptc.com/infoengine/taglib/core"
prefix="ie"%>
<!--com.infoengine.soap.rpc.def
download a blob
@param string filename - the file name of the blob/image to
download (value from ListBlobs call)
@return java.io.InputStream a stream that contains the blob
-->
<ie:webject name="Send-Blob-Stream" type="OBJ">
<ie:param name="INSTANCE" data="soapJDBCAdapter"/>
<ie:param name="CLASS" data="BLOBTABLE"/>
<ie:param name="ATTRIBUTE" data="FILECONTENT"/>
<ie:param name="MIMETYPE" data="'application/octet-stream'"/>
<ie:param name="WHERE" data="(NAME='$(@FORM[]filename[0])')"/>
<ie:param name="FILENAME" data="test.doc"/>
<ie:param name="GROUP_OUT" data="$(@FORM[]group_out[])"
default="output"/>
</ie:webject>