0% found this document useful (0 votes)
382 views24 pages

ThingWorx Extension Development Guide 1

The document describes United States government rights regarding PTC software products and documentation. PTC software products and documentation are considered "commercial items" under relevant regulations. For civilian agencies and the Department of Defense, PTC software and documentation are provided under PTC's standard commercial license agreement. Use or disclosure of PTC software by the U.S. government is only permitted as outlined in the applicable PTC license agreement.

Uploaded by

MOHAMED HOUSNI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
382 views24 pages

ThingWorx Extension Development Guide 1

The document describes United States government rights regarding PTC software products and documentation. PTC software products and documentation are considered "commercial items" under relevant regulations. For civilian agencies and the Department of Defense, PTC software and documentation are provided under PTC's standard commercial license agreement. Use or disclosure of PTC software by the U.S. government is only permitted as outlined in the applicable PTC license agreement.

Uploaded by

MOHAMED HOUSNI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

UNITED STATES GOVERNMENT RIGHTS

PTC software products and software documentation are “commercial items” as


that term is defined at 48 C.F.R. 2.101. Pursuant to Federal Acquisition Regulation
(FAR) 12.212 (a)-(b) (Computer Software) (MAY 2014) for civilian agencies or
the Defense Federal Acquisition Regulation Supplement (DFARS) at 227.7202-1
(a) (Policy) and 227.7202-3 (a) (Rights in commercial computer software or
commercial computer software documentation) (FEB 2014) for the Department of
Defense, PTC software products and software documentation are provided to the
U.S. Government under the PTC commercial license agreement. Use, duplication
or disclosure by the U.S. Government is subject solely to the terms and conditions
set forth in the applicable PTC software license agreement.
PTC Inc., 140 Kendrick Street, Needham, MA 02494 USA

ThingWorx Extension
Development Guide
Version 4.5
June 2018

3
Contents

About this Guide .........................................................................................................7


Prerequisites ........................................................................................................8
Introduction to Extensions............................................................................................9
What is an Extension?.........................................................................................10
Why Build an Extension?.....................................................................................10
Creating Extensions .................................................................................................. 11
1
Extension Zip File Structure.................................................................................12
Metadata.xml File ...............................................................................................12
About this Guide
Entities Created in ThingWorx Composer .............................................................24 Prerequisites...............................................................................................................8
Java-Based Entities ............................................................................................25
Extension Migrators ............................................................................................46
This guide explains why and how you would develop an extension for
Third-Party JAR Files ..........................................................................................47
ThingWorx. It describes best practices for creating ThingWorx extensions.
Adding Custom Widgets ......................................................................................48
Extension Import and Management......................................................................49
Entity Characteristic Support ...............................................................................51
Using the Eclipse Plugin ............................................................................................52
Installing the Eclipse Plugin for ThingWorx Extension Development........................53
Creating an Extension Project..............................................................................53
Importing Existing Extensions ..............................................................................54
Creating Entities .................................................................................................55
Adding Services, Properties, Configuration Tables, Subscriptions, and
Events ............................................................................................................55
Creating Widgets ................................................................................................56
Adding Third-Party JAR Files...............................................................................56
Adding an Extension Migrator ..............................................................................56
Importing Composer-Created Entities...................................................................57
Building Extensions ............................................................................................57
Deleting Entities and Widgets ..............................................................................58
Best Practices...........................................................................................................59
Use the Eclipse Plugin ........................................................................................60
The Golden Rule: Use as Few External JARs as Possible......................................60
Develop on a Clean Platform Instance..................................................................60
Choosing Between a Thing Template and Thing Shape .........................................60
Customizing Media Entities .................................................................................61
Customizing Mashups.........................................................................................61
Customizing Style Definitions ..............................................................................61
Make Your Entities Non-Editable..........................................................................62
Tag Your Extension Entities .................................................................................62

5 7
2 3
Introduction to Extensions Creating Extensions
What is an Extension? ...............................................................................................10
Extension Zip File Structure .......................................................................................12
Why Build an Extension? ...........................................................................................10
Metadata.xml File ......................................................................................................12
Entities Created in ThingWorx Composer ....................................................................24
Java-Based Entities...................................................................................................25
Extension Migrators...................................................................................................46
Third-Party JAR Files.................................................................................................47
Adding Custom Widgets ............................................................................................48
Extension Import and Management ............................................................................49
Entity Characteristic Support ......................................................................................51

You can create ThingWorx extensions using common editors and development
tools, as long as the artifacts are created following the expected convention and
are packaged in the expected structure. This section explains how these artifacts
must be created so they work correctly after being imported into ThingWorx.

Tip
Because it can be challenging to correctly build the various pieces of an
extension, the Eclipse Plugin for ThingWorx Extension Development (Eclipse
Plugin) has been created to help developers build extensions quickly and
concentrate on developing their features rather than worrying about getting the
extension structured correctly. The productivity improvements provided by the
Eclipse Plugin make it a compelling tool to use when building ThingWorx
extensions. For more information, see Use the Eclipse Plugin.

9 11
<ExtensionPackage For example:
name=”MyExtension” <Entities>
packageVersion="1.0.0" <ThingPackages>
vendor="ThingWorx – A PTC Business" <ThingPackage name="MyThingPackage"
description="My Extension description" description="A description"
minimumThingWorxVersion="7.1.0" className="com.acmecorp.things.MyThingTemplate" />
dependsOn="ExtensionOne:2.5.1,ExtensionTwo:1.2.0"> </ThingPackages>
</ExtensionPackage> <ThingTemplates>
</ExtensionPackages> <ThingTemplate name="MyThingTemplate"
</Entities> description="My Thing Template description"
thingPackage="MyThingPackage">
</ThingTemplates>
</Entities>
Tip
If you are using the Eclipse Plugin, the New ThingWorx Project action will
generate a new metadata.xml file and fill in the details. For more Tip
information, see Creating an Extension Project.
If you are using the Eclipse Plugin, the New Thing Template action will
generate the necessary elements in the metadata.xml file and fill in the
<ExtensionPackage> details. For more information, see Creating Entities.
Attribute Description
name The name of the extension. This is a required
attribute whose value must follow the naming
convention for ThingWorx entities.
Note
packageVersion The version of the extension. This is a required
attribute whose value must follow the If a Java-based extension defines a thing template as Java classes using
<major>.<minor>.<patch> format, annotations but they are not included in the entity’s metadata.xml file,
where each part of the version is numeric. ThingWorx cannot detect if the entity’s base template changed and the import
Extensions should follow the semantic of the extension will fail.
versioning rules outlined at http://www.semver.
org. <ThingPackage> Attribute Description
vendor The name of the organization that created the name The name of the thing package. This is a
extension. This is an optional attribute. required attribute whose value must follow
description Information about what the extension does. the naming convention for ThingWorx
This is an optional attribute that is displayed in entities.
the Manage Extensions screen of Composer. description Provides information about the thing
package. This is an optional attribute.
minimumThingWorxVersion Specifies the minimum version of ThingWorx
with which the extension is compatible. This is className The fully-qualified Java class name of the
a required attribute whose value must follow ThingTemplate class that corresponds to
the <major>.<minor>.<patch> format, the thing package. This is a required
where each part of the version is numeric. If attribute.
you try to import the extension into a version of
ThingWorx that is older than what is specified
in this attribute, it will fail.

Creating Extensions 13 Creating Extensions 15


<ThingShape> Attribute Description <Authenticator> Attribute Description
name The name of the thing shape. This is name The name of the authenticator. This is
a required attribute whose value must a required attribute whose value must
follow the naming convention for follow the naming convention for
ThingWorx entities. ThingWorx entities.
description Provides information about the thing description Provides information about the
shape. This is an optional attribute. authenticator. This is an optional
className The fully-qualified Java class name attribute.
of the ThingShape class. This is a className The fully-qualified Java class name of
required attribute. the Authenticator class. This is a
aspect.isEditableExtensionObject Indicates if the entity can be edited in required attribute.
ThingWorx Composer. This is an aspect.isEditableExtensionObject Indicates if the entity can be edited in
optional attribute and defaults to ThingWorx Composer after import.
false. This should be set to true so it is
enabled in Composer after import.
Note
This attribute is supported in
ThingWorx 7.0 and higher.
Tip
For more information about deploying and enabling authenticators after
Defining Resources import into ThingWorx, see the Authenticator Sample Extension Configuration
Each Java-based resource must have a <Resource> element under the topic in the ThingWorx Help Center.
<Entities><Resources> element, which specifies the class name of the
resource.
For example: Defining Directory Services
<Entities>
<Resources> Each Java-based directory service must have a <DirectoryService> element
<Resource name="MyResource" under the <Entities><DirectoryServices> element, which specifies the
description="My Resource description" class name of the directory service.
className="com.acmecorp.resources.MyResource" />
</Resources> For example:
</Entities> <Entities>
<DirectoryServices>
<DirectoryService name="MyDirectoryService"
description="My Directory Service description"
Tip className="com.acmecorp.resources.MyDirectoryService"
aspect.isEditableExtensionObject=”true” />
If you are using the Eclipse Plugin, the New Resource action will generate the </DirectoryServices>
necessary elements in the metadata.xml file and fill in the details. For </Entities>
more information, see Creating Entities.

Creating Extensions 17 Creating Extensions 19


<ScriptFunctionLibrary name="MyScriptFunctionLibrary" <ResultType> Attribute Description
description="My Script Function Library description" name
className=" com.acmecorp.sfl.MyScriptFunctionLibrary">
The name of the return value. This is
<FunctionDefinitions> a required attribute.
<FunctionDefinition name=”calculateIt” description Provides information about the return
description=”Performs a calculation”> value. This description is displayed in
<ParameterDefinitions>
<FieldDefinition name=”parm1”
ThingWorx Composer.
description=”The first parameter” baseType The base type of the return value of
baseType=”INTEGER”/> the method. This is a required
<FieldDefinition name=”parm2” attribute whose value must be a valid
description=”The second parameter”
baseType=”INTEGER”/>
ThingWorx base type, such as
</ParameterDefinitions> STRING or NUMBER. When you
<ResultType name=”resultingValue” baseType=”INTEGER” create a property in Composer, you
description=”The return type”/> can see the full list in the Base Type
</FunctionDefinition> dropdown. For more information, see
</FunctionDefinitions>
</ScriptFunctionLibrary>
com.thingworx.types.BaseTypes in
</ScriptFunctionLibraries> the ThingWorx Platform API
</Entities> Documentation topic in the
ThingWorx Help Center.
Note
Tip The value must be uppercase.
If you are using the Eclipse Plugin, the New Script Function action will
generate the necessary elements in the metadata.xml file and fill in the
details. Function definition elements must be added manually. For more Defining JAR Resources
information, see Creating Entities.
For each JAR file included in the extension, an entry must be added in the
metadata.xml file. The <JarResources> element contains a reference to the
<ScriptFunctionLibrary> Attribute Description JAR that contains the extension’s Java-based entity classes. It is used to reference
name The name of the script function third-party JAR files on which the extension depends.
library. This is a required attribute For example:
whose value must follow the naming <ExtensionPackages>
convention for ThingWorx entities. <ExtensionPackage ...>
<JarResources>
description Provides information about the script
<FileResource type="JAR" file="MyExtension.jar"
function library. This is an optional description="Contains the extension’s custom classes" />
attribute. <FileResource type="JAR" file="ThirdPartyLib.jar
className The fully-qualified Java class name of description="My Extension depends on this JAR" />
the ScriptFunctionLibrary </JarResources>
</ExtensionPackage>
class. This is a required attribute.
</ExtensionPackages>

Creating Extensions 21 Creating Extensions 23


Once the class is created, you can add services, properties, configuration tables,
events, and subscriptions to the thing template. For more information, see Adding
Tip Services, Properties, and Other Methods to an Entity.
The easiest way to add Composer-created entities is to tag entities created for
the extension and export them using the Export Source Control Entities action.
In Composer, use Import/Export > Export > Source Control Tip
Entities to export your extension entities to an /Entities folder at the If you are using the Eclipse Plugin, the New Thing Template action will
root of the extension package structure. The resulting Entities folder under generate the Java source file with the appropriate annotations and
ThingworxStorage/repository/<RepositoryName> can be automatically update the metadata.xml file. For more information, see
placed directly into the extension zip file. Creating Entities.

You can override the following methods from the thing superclass to add custom
functionality in the thing template (see the Javadoc documentation for the com.
Tip thingworx.things.Thing class for more information):
If you are using the Eclipse Plugin, the Import ThingWorx Entities action places • protected void cleanupThing() throws Exception;
the files in the correct location in the project. For more information, see • protected void initializeThing() throws Exception;
Importing Composer-Created Entities.
• protected void
preprocessSetPropertyVTQ(ThingProperty, VTQ, boolean)
throws Exception;
Java-Based Entities • protected void processStartNotification();
Java-based entities created in an extension are basically the same as those created • protected void startThing() throws Exception;
in ThingWorx Composer. They can also define services, properties, events, • protected void stopThing() throws Exception;
configuration parameters, and so on.
For example, you can override the initializeThing() method. This method
The main difference between the two types of entities is that Composer-created is called when the thing is created or saved. It is used to set up any functionality
entities use JavaScript for services, and their source code is visible in Composer. needed for your Java code to function properly. It may be used to create a data
Java-based entities use Java for services, and the source code is not visible in table and associate it with your thing when the thing is created. This is
Composer. Java-based entities can also define configuration values and lifecycle accomplished by checking for the existence of the data table before creating it,
behavior. since the same method is called when the thing is saved.

Tip Creating Thing Shapes


Writing services in Java provides several advantages, including the ability to To create a thing shape, create a Java class that extends java.lang.Object (it
write unit tests and to more easily perform remote debugging with does not need to extend any ThingWorx classes) and update the metadata.xml file.
breakpoints. Each thing shape must have a <ThingShape> element. For more information,
see metadata.xml File.
Once the class is created, you can add services, properties, events, and
subscriptions to the thing shape. For more information, see Adding Services,
Properties, and Other Methods to Entities.

Creating Extensions 25 Creating Extensions 27


Tip Tip
If you are using the Eclipse Plugin, the New Resource action will generate the For more information about creating directory services, see the Directory
Java source file with the appropriate annotations and automatically update the Services Authentication topic in the ThingWorx Help Center.
metadata.xml file. For more information, see Creating Entities.

Creating Script Function Libraries


Creating Authenticators To create a script function library, create a Java class that extends
To create an authenticator, create a Java class that extends java.lang.Object and update the metadata.xml file.
com.thingworx.security.authentication.CustomAuthentica In the Java class, define public static methods that provide the desired
tor and update the metadata.xml file. Each authenticator must have an functionality. Each method must have a signature of the form:
<Authenticator> element in the metadata.xml file. For more information, see public static Object doSomething(
metadata.xml File. org.mozilla.javascript.Context cx,
The following methods need to be implemented. For more information, see the org.mozilla.javascript.Scriptable thisObj,
Object[] args,
Javadoc for the
org.mozilla.javascript.Function funObj) throws Exception {
com.thingworx.security.authentication.IAuthenticator
interface and the Authenticator Sample Extension Configuration topic in the // do something and return something
ThingWorx Help Center. }
• public void authenticate(HttpServletRequest, The org.mozilla.javascript classes are part of the Rhino JavaScript
HttpServletResponse) throws AuthenticatorException; library, which must be part of the extension’s build path.
• public void Each script function library must have a <ScriptFunctionLibrary>
issueAuthenticationChallenge(HttpServletRequest, element in the metadata.xml file and a corresponding
HttpServletResponse) throws AuthenticatorException; <FunctionDefinition> element for each method with the appropriate sub-
elements defining the parameters and return type. For more information, see
• public boolean matchesAuthRequest(HttpServletRequest)
metadata.xml File.
throws AuthenticatorException;

Tip
Note
If you are using the Eclipse Plugin, the New Script Function Library action will
The parameter classes and exceptions used in these methods are part of the
generate the Java source file with the appropriate annotations and
javax.servlet third-party JAR, which must be in the build path for the custom
automatically update the metadata.xml file. For more information, see
authenticator to compile. However, this JAR is a compile-time dependency
Creating Entities.
only and must not be added as part of the extension itself.

Adding Services, Properties, and Other Methods to


Entities
One of the main tasks when creating an extension is to add custom behavior to
entities by defining services, properties, events, and/or subscriptions on them.

Creating Extensions 29 Creating Extensions 31


The most commonly used attributes of this annotation are listed below. For a The @ThingworxServiceParameters Annotation
complete set of attributes, see the Javadoc documentation for the
ThingworxServiceParameters are used to associate ThingWorx inputs with
com.thingworx.metadata.annotations.ThingworxServiceDefi
method input parameters. This defines what input types will be available for
nition class.
binding in ThingWorx Platform. The annotations should be entered before the
@ThingworxServiceDefinition Description parameter definition, inside the method’s declaration.
Attribute
name For example, the following code snippet shows service parameters added to a
The name of the service. It is standard
method’s input parameters:
convention for service names to start with a // Property set of another Thing
capital letter. The name of the service must @ThingworxServiceDefinition( name="SetHelloWorldProperty",
be identical to the name of the method. This description="Set a property on Hello World" )
is a required attribute. public void SetHelloWorldProperty(
@ThingworxServiceParameter( name="thingToSet",
description A short description for the service. The description="The thing you're setting", baseType="THINGNAME",
description should provide information on aspects={"thingTemplate:HelloWorld"} ) String thingToSet,
functionality of the service and other @ThingworxServiceParameter( name="numberPropertyToSet",
pertinent information for users. This description="The property name you're setting",
baseType="STRING" ) String numberPropertyToSet,
description will appear in a tool tip when
@ThingworxServiceParameter( name="numberValueToSet",
hovering over the service name in description="The property value you're setting",
ThingWorx Composer. baseType="NUMBER") Double numberValueToSet) throws Exception {
category A category that conceptually groups related
Thing helloThing = ThingUtilities.findThing(thingToSet);
services. This is an optional attribute. helloThing.setPropertyValue(numberPropertyToSet,
new NumberPrimitive(numberValueToSet));
}
The @ThingworxServiceResult Annotation
For more information, see the Javadoc documentation for
The @ThingworxServiceResult annotation defines the output type of the com.thingworx.metadata.annotations.ThingworxServicePara
service. This base type is treated the same as property definitions and is case- meter.
sensitive. For some base types, such as INFOTABLE or THINGNAME, you may need
to add aspects to define more complex types.
Properties
The most commonly used attributes of this annotation are listed below. For a
complete set of attributes, see the Javadoc documentation for the Properties are defined using the @ThingworxPropertyDefinitions
com.thingworx.metadata.annotations.ThingworxServiceRe annotation. This annotation specifies a list of properties associated with the thing
sult class. template or thing shape that is being defined. Each property uses its own
annotation to define its name, description, baseType, and aspects. See
the HelloWorldThing example.

Note
If you are using the Eclipse Plugin, the Add Property action will add the
necessary property annotations described below to the Java source file. See
Adding Services, Properties, Configuration Tables, Subscriptions, and Events
for more information.

Creating Extensions 33 Creating Extensions 35


description="Sample string property", baseType="STRING", description="",baseType="BOOLEAN"),
aspects={"isPersistent:false","isReadOnly:false"}), @ThingworxFieldDefinition(name="field4",
@ThingworxPropertyDefinition(name="NumberProperty1", description="",baseType="USERNAME"),
description="Sample number property", baseType="NUMBER", } ) ),
aspects={"isPersistent:false","isReadOnly:false"}), @ThingworxConfigurationTableDefinition(
})
name="ConfigTableExample2",
public class HelloWorldThing extends Thing {
description="Example 2 config table", isMultiRow=true,
}
dataShape = @ThingworxDataShapeDefinition( fields = {
The image below shows the fields available when you are adding a new property @ThingworxFieldDefinition(name="columnA",
in ThingWorx Composer: description="",baseType="STRING"),
@ThingworxFieldDefinition(name="columnB",
description="",baseType="NUMBER"),
@ThingworxFieldDefinition(name="columnC",
description="",baseType="BOOLEAN"),
@ThingworxFieldDefinition(name="columnD",
description="",baseType="USERNAME"),
} ) )
})
public class GoodByeThing extends Thing {
}

XML Import/Export
You can also define configuration tables on things, thing templates, thing shapes,
and mashups through XML imports. The following is a configuration table
definition for an entity exported as XML:
<ConfigurationTableDefinitions>
<ConfigurationTableDefinition category="TemplateConfigTables"
dataShapeName="MyDS" description="Template Config Table" isHidden=
"false"
isMultiRow="true" name="ConfigTableOnTemplate" ordinal="2" source=
"REST"/>
</ConfigurationTableDefinitions>

Note
Configuration table defintions created by annotations do not appear in exported
XML.

The following is an example of configuration table data exported as XML:


<ConfigurationTables>
<ConfigurationTable description="Template Config Table"
isMultiRow="true" name="ConfigTableOnTemplate" ordinal="2">
<DataShape>
<FieldDefinitions>

Creating Extensions 37 Creating Extensions 39


Configuration Table and Configuration Data Inheritance
Configuration tables and their data that are created on thing template and thing
shape entities are inherited by implementing a thing entity. Any definition changes
made to the configuration tables at the template or shape level are reflected on
implementing things that inherited the tables from their template or shape.
The configuration table created above appears under the Configuration tab for the A new thing entity will inherit configuration data from its shapes or template.
thing in ThingWorx Composer as follows: However, after the thing is created, it will not take later data changes to its
inherited tables from its template or shapes.
For example:
• ThingTemplateA has configTable1
• ThingShapeA has configTable2
ThingA is created, which is based on ThingTemplateA and implements
ThingShapeA.
ThingA will inherit configTable1 and configTable2 with data from its
template and shape respectively.
If ThingTemplateA changes configTable1 from singleRow to multiRow,
then ThingA will reflect that change.

Creating Extensions 41 Creating Extensions 43


Tip Note
If you are using the Eclipse Plugin, the Add Subscriptions action will add the The extension migrator will only be invoked when importing all entities from
necessary annotations described below to the Java source file. For more the previous ThingWorx system. It will not be invoked when importing
information, see Adding Services, Properties, Configuration Tables, individual or subsets of entities.
Subscriptions, and Events.

@ ThingworxSubscription Description
Attribute Note
source The name of the entity, which is the source of Extension migrators are designed for migrating entities only. Stream data
the event. A blank value indicates that the associated with extension entities must be updated by other means before
event originates from the entity itself. importing into the new system.
eventName The name of the event to which you want to
subscribe.
sourceProperty The name of the property associated with the
event. It’s only applicable for the Alert, Third-Party JAR Files
AlertAck, AlertReset, and DataChange events. The /lib folder contains the JAR file including the custom Java classes written
handler The name of the service that should be for the extension and additional third-party JAR files required by the extension.
invoked whenever the subscription receives an
For example:
event. /lib
The service should have the following /MyExtension.jar
signature and be annotated appropriately as a /ThirdPartyLib.jar
ThingWorx service: For each of these JAR files, you must create an entry in the metadata.xml file. For
public void more information, see metadata.xml File.
HandleEvent(InfoTable eventData,
String eventName, DateTime
eventTime, String source, String Tip
sourceProperty)
If you are using the Eclipse Plugin, the New Jar Resource action will update
enabled Indicates if the subscription should listen for the metadata.xml file automatically. For more information, see Adding Third-
events Party JAR Files.
For more information, see the Javadoc documentation for
com.thingworx.metadata.annotations.ThingworxSubscrip
tion.
For example: Caution
@ThingworxSubscriptions( subscriptions = { Do not include third-party JAR files from your extension that are already
@ThingworxSubscription( source = "", eventName = "MyEvent", included in the ThingWorx Platform.
sourceProperty = "", handler = "MyService", enabled = "true" )
})

Creating Extensions 45 Creating Extensions 47


Extension Import and Management Entity Characteristic Support
The following table shows which entities support Services, Properties,
Importing an Extension into ThingWorx
Configuration Tables, Subscriptions, and Events:
Once an extension is packaged correctly, you can import it using ThingWorx
Composer: Entity Services Properties Configu- Subscrip- Events
Type ration tions
1. Go to Import/Export ▶ Import. Tables
The Import screen appears. Thing Supported Supported Supported Supported Supported
by REST
API
2. From the Import Option dropdown, select Extension. Thing Supported Supported Supported Supported Supported
3. Click Browse to select the .zip file for your extension. Template by REST
4. If you want to check the extension before importing, click Validate. API
Mashup Supported
Validation results appear and any errors are described. by REST
API
5. Click Import. Thing Supported Supported Supported Supported Supported
If the extension can be imported without error, an Import Successful message Shape by REST
appears. The ThingWorx Platform prompts you to refresh the window to load API
the new functionality. Resource Supported
Extension
Migrator
Authentica- Supported Supported
Note tor by REST
API
If you import an updated version of an extension that contains a JAR file, you will
see a message that instructs you to restart the platform to complete the upgrade. Directory Supported
Service by REST
API
Script
Function
Note Library
If a menu is imported in an extension, the Group Association property on
menu and the Groups property on menu items are editable. If you import an
updated version of the extension that contains the menu, changes made in
ThingWorx will be merged with any changes that were made in the extension.

Creating Extensions 49 Creating Extensions 51


Installing the Eclipse Plugin for Creating Entities
ThingWorx Extension Development 1. To create an entity, choose the ThingWorx menu and select the entity type you
want to create.
To install the plugin for ThingWorx Extension Development into your local
instance of the Eclipse IDE for Java EE Developers, Mars 4.5 release or newer, do 2. Select or enter your source folder and package.
the following: 3. Enter a name.
1. Download the Eclipse Plugin for ThingWorx Extensions zip file
from the ThingWorx Marketplace.
2. Open Eclipse and choose a workspace. Note
3. Choose Help ▶ Install New Software.... We recommend that you add a unique prefix to extension entity names to
avoid conflicts with other extension entities, as in <MyExtension>.
The Install screen appears. <MyEntityName>.
4. Click Add....
The Add Repository screen appears. 4. If you want to edit the aspects of your entity, click Next and edit the available
5. Choose the download location for the zip file (for example, thingworx- aspects. Or, to use the defaults, click Finish.
eclipse-plugin-[version].zip). The aspects will be different for each entity type.
6. Select ThingWorx Extension Builder. The Java source file is created for you in the specified package and the
You may have to deselect the Group items by category checkbox for the metadata.xml file is updated automatically.
ThingWorx Extension Builder plugin to appear in the list.
7. Click Next. Adding Services, Properties,
8. Accept the license and finish the installation.
9. Click OK to acknowledge the Eclipse security warning.
Configuration Tables, Subscriptions, and
10. Restart Eclipse. Events
11. To verify your install in Eclipse, choose Help ▶ Installation Details. ThingWorx To add services, properties, and other annotations to an entity, do the following:
Extension Builder appears in the list of installed software. 1. In Package Explorer, right-click on the Java file and choose the ThingWorx
Source menu.

Creating an Extension Project OR

To get started using the Eclipse Plugin for ThingWorx Extension Development, do In the Java editor of the entity, right-click to access the ThingWorx Source
the following: menu.

1. In Eclipse, choose File ▶ New ▶ Project.... From the submenu, you can choose to add a service, property, configuration
table, subscription, or event.
2. In the New Project screen, expand the ThingWorx menu, select ThingWorx
Extension Project, and click Next. 2. Enter the necessary information in the wizard and click Finish.
3. Enter a project name and browse to and select the latest ThingWorx- The Java and metadata.xml files are updated with the necessary annotations
Extension-SDK-[version]-latest.zip file. and XML elements.
4. Select Gradle or Ant as the build framework for the extension.

Using the Eclipse Plugin 53 Using the Eclipse Plugin 55


Importing Composer-Created Entities
1. Choose File ▶ Import.
2. On the Import screen, choose ThingWorx ▶ Entities and click Next.
3. Browse to the folder that contains the entity folders that were exported from
Composer (for example, ThingworxStorage/repository/
<RepositoryName>/<EntitiesFolder>).
4. If necessary, select or deselect individual XML files to be imported.
5. Select the project into which you want to import and click Finish.
The selected XML files are imported under the /Entities folder in the
extension project.
5
Best Practices
Use the Eclipse Plugin ...............................................................................................60
Tip
The Golden Rule: Use as Few External JARs as Possible ............................................60
When exporting entities from Composer, you should tag all entities created for Develop on a Clean Platform Instance ........................................................................60
the extension and then export them using the Export Source Control Entities Choosing Between a Thing Template and Thing Shape ................................................60
action. In the Export Source Control Entities window, specify the tag and a file Customizing Media Entities ........................................................................................61
repository and enter /Entities as the path. You can select the resulting Customizing Mashups ...............................................................................................61
ThingworxStorage/repository/<RepositoryName>/ Customizing Style Definitions .....................................................................................61
Entities folder in the Import wizard in Eclipse. Make Your Entities Non-Editable ................................................................................62
Tag Your Extension Entities ........................................................................................62
Add Localization Tokens ............................................................................................62
Avoid Tight-Coupling to Other Extensions ...................................................................63

Building Extensions Plan for Entity Visibility...............................................................................................64


Back Up Storage Before Delete ..................................................................................64
Logging ....................................................................................................................65
Gradle Build
In the Package Explorer, right-click build.gradle and choose Run As ▶ Gradle
Build.
There may be many different ways to accomplish a goal in extensions. The
following section outlines best practices for developing successful extensions.
If the Edit Configuration window appears, enter clean build in the task editor,
click Apply and then Run.
After refreshing your project, a zip file appears under build ▶ distributions. This is
a working extension that you can import into ThingWorx.

Ant Build
In the Package Explorer, right-click build-extension.xml and choose Run As ▶ Ant
Build.
After refreshing your project, a zip file appears under build ▶ distributions. This is
a working extension that you can import into ThingWorx.

Using the Eclipse Plugin 57 59


directly on a thing template, you would need to recreate all of your assets to use For more information, see the Localization Tables topic in the ThingWorx Help
the new functionality, because the base thing template cannot be changed for a Center.
thing template or a thing. These tokens must be included in your extension. The easiest way to do this is to
If you want to create subscriptions, you might choose to create thing templates use the Eclipse plugin to import the localization table XML entity file. You can
that implement one or more thing shapes for convenience, as subscriptions cannot also add these tokens manually by following the steps in the Packaging
be created on thing shapes. Extensions with Localization Tables topic in the ThingWorx Help Center.
For more information about these concepts, see the ThingWorx Model Definition If a specified token is not found in the localization table, tw.friendly-
and Composer section in the ThingWorx Help Center. names.[your value] is displayed in ThingWorx Composer.

Customizing Media Entities Avoid Tight-Coupling to Other Extensions


Media entities are images and icons used in menus, style definitions, and Avoid tight-coupling your extension to other extensions. If your extension is tight-
mashups. For more information, see Media Entities in the ThingWorx Platform coupled to another extension that needs to be upgraded to a new major version,
Help Center. you will be forced to delete your extension and its entire chain of dependencies
You can provide branded content by changing default logos (images). You can before you can perform the upgrade.
configure your media entities to be bound to the Image widget via a service. That The best way to do avoid tight-coupling is to create the things and thing templates
service can provide a default media entity or a customer-provided media entity. that you are need so that they are not considered part of your extension. This
approach is recommended instead of using out-of-the-box things or thing
templates that derive from other extension thing templates or implement other
Customizing Mashups extension thing shapes
Mashups can be bound to a Contained Mashup widget via a service. You can copy
a default (non-editable) mashup, modify it, and set the configuration to the copy.
Note
It might be difficult to avoid tight-coupling when using widgets from other
Caution extensions in your extension mashups.
Mashups are complex, and customizing them in this way can introduce problems
for extension upgrades. Example
Suppose that you created a thing that extends from the MailServer thing template
in Mail_Extensions. When you import a new major version of Mail_Extensions,
Customizing Style Definitions the platform forces you to delete the Mail_Extensions, which also forces you to
delete ThingWorx Utilities (and down the dependency chain) before you can
Since it is currently not possible to bind style definitions to a widget via a service, install the upgrade.
you could make style definitions editable in order to customize, or rebrand, them.
You should limit the number of editable style definitions. Instead, it would be better create you own thing that derives from MailServer and
configure it in ThingWorx Utililities. Then, if you import a new major version of
Mail_Extensions, you would only need to delete the thing that you created, and
not ThingWorx Utilities or other dependencies.
Note
You should bundle editable style definitions (and all editable entities) in a separate
extension.

Best Practices 61 Best Practices 63


Logging ThingTemplate Does Not Exist After
When you are trying to figure out what is happening on the Platform, use logging. Successful Import
Use ThingWorx LogUtilities to get a specific logger. There are multiple
kinds of loggers and log levels used in the ThingWorx Platform, but we This can happen when there is an issue creating the ThingTemplate once it has
recommend that you use the application or script loggers for logging anything been imported, but it is not a fatal exception. The most common cause of this is a
from inside extension services. missing JAR that is required for the ThingTemplate class. If this happens, check
your metadata.xml file for the JAR declaration of a required JAR. Then make
For example: sure the JAR is present in the extensions lib/common directory.
protected static Logger logger =
LogUtilities.getInstance().getApplicationLogger(myThing.class);
logger.info("This is an information message"); JAR Conflict on Import
logger.error("This is an error message");
This can occur when a JAR you are attempting to use is already loaded on
ThingWorx Platform. Usually you can remove the JAR from your
metadata.xml file to fix the problem. However, this can be risky because
different versions of the same JAR can cause conflicts in functionality. This could
affect your extension or the Platform itself. The best solution is to try to avoid
using this JAR, if possible.

Updated Extension Retains Old Version’s


Functionality
The cause of this problem is the loaded classes from the previous version. This
can be fixed by restarting Tomcat.

Failed to Create Data Shape


This will happen if there is an incorrect declaration in a Java annotation. For
example, you define a property as BOOL or Boolean instead of BOOLEAN.
Make sure you are using the proper string constants in a declaration and note that
all base type values are case-sensitive.

Thing Does Not Run After Create or Save


This usually means that a thing was created or saved based on a thing template
that has an error in its initialization method. Make sure you are catching
exceptions and logging them so you can deduce the reason for the initialization
failure.

Best Practices 65 Troubleshooting and Debugging 67


Enter any available port value for address (do not use well-known ports that
might be used by other processes), and note this value for the next step.

7
Appendix A: Examples
Platform Integration Example using Twitter..................................................................70
Example using Tomcat Monitor GUI and Eclipse ..........................................................70
HelloWorldThing .......................................................................................................71
GoodByeThing..........................................................................................................73
Common Code Snippets ............................................................................................76

2. Set up a remote debug connection in your IDE. This should be done


differently depending on the IDE you are using.
If you are using Eclipse:
a. Go to Run ▶ Debug Configurations.
b. Select Remote Java Applications and create a new configuration (right click
▶ New).
c. In the Project field, browse for and select your project.
d. In the Host field, enter the host of your Tomcat instance.
e. In the Port field, enter the debug port to which you intend to connect (this
should match the port/address you entered in step 1).
f. Run debug after Tomcat has started in debug mode, and you will be
connected.

HelloWorldThing
package com.helloinc.things.helloworld;

69 Appendix A: Examples 71
public void initializeThing() throws Exception {
} super.initializeThing();

@ThingworxServiceDefinition(name = "SayHello", description = "Hello world") _logger.warn("****** Initializing GoodByeThingInstance " + getName());
@ThingworxServiceResult(name = "Result",
description = "Result", baseType = "STRING")
// Examples of getting the configuration table values
public String SayHello() throws Exception {
String field1Value = (String)getConfigurationSetting("ConfigTableExample1", "field1");

return "Hello world."; Double field2Value = (Double)getConfigurationSetting("ConfigTableExample1", "field2");


} Boolean field3Value = (Boolean)getConfigurationSetting("ConfigTableExample1", "field3");
String field4Value = (String)getConfigurationSetting("ConfigTableExample1", "field4");
@ThingworxServiceDefinition(name = "Greeting", description = "Hello world")
@ThingworxServiceResult(name = "Result", // Example of setting a configuration table value
description = "Result", baseType = "STRING") setConfigurationSetting("ConfigTableExample1", "field2", (field2Value + 2.1));
public String Greeting(@ThingworxServiceParameter(name = "userToGreet", SaveConfigurationTables();
description = "The user you're greeting", // Another example
baseType = "USERNAME") String userToGreet)throws Exception {
setConfigurationSetting("ConfigTableExample1", "field2",
((Double)getConfigurationSetting("ConfigTableExample1", "field2") + 5.5));
fireSalutationSentEvent(userToGreet);
return "Hello " + userToGreet + ". How are you?"; SaveConfigurationTables();
}
// Example of getting multi-row configuration table data
// Event Firing Example ConfigurationTable tableTwo = getConfigurationTable("ConfigTableExample2");
private void fireSalutationSentEvent(String userName) throws Exception {
ThingworxEvent event = new ThingworxEvent(); _logger.warn("****** Iterating " + tableTwo.getName() + " of goodbye thing instance " +
event.setTraceActive(ThreadLocalContext.isTraceActive()); getName());
event.setSecurityContext(ThreadLocalContext.getSecurityContext()); _logger.warn("****** " + tableTwo.getName() + " has " + tableTwo.getLength() + " rows");
event.setSource(getName());
event.setEventName("SalutationSent");
int counter = 0;

// the name parameter isn't really used for(ValueCollection row : tableTwo.getRows()) {


InfoTable data =
InfoTableInstanceFactory.createInfoTableFromDataShape("SalutationSentEvent"); String columnA = row.getStringValue("columnA");
Double columnB = (Double)row.getValue("columnB");
ValueCollection values = new ValueCollection(); Boolean columnC = (Boolean)row.getValue("columnC");
values.put("userGreeted", new StringPrimitive(userName)); String columnD = row.getStringValue("columnD");

data.addRow(values); _logger.warn("columnA row " + counter++ + " value is " + columnA);


_logger.warn("columnB row " + counter++ + " value is " + columnB);
event.setEventData(data);
_logger.warn("columnC row " + counter++ + " value is " + columnC);

this.dispatchBackgroundEvent(event); _logger.warn("columnD row " + counter++ + " value is " + columnD);


} }
}
}
// Property set of another Thing
@ThingworxServiceDefinition( name="SetHelloWorldProperty",
description="Set a property on Hello world" )
GoodByeThing public void SetHelloWorldProperty(
@ThingworxServiceParameter( name="thingToSet", description="The thing you're setting",
package com.helloinc.things.goodbye;

Appendix A: Examples 73 Appendix A: Examples 75


String stringProperty1 = try{
((StringPrimitive)this.getPropertyValue("StringProperty1")).getValue(); String url = "https://" + host + path;
result = loader.PostText(url,content,"text/xml",null,
To access a thing’s configuration: null,headers,false,false,30000.0,null,null,null,
String field1Value = false,null,null,null);
getStringConfigurationSetting("ConfigTableExample1", "field1");
String fault = checkForFaultCode(result);
if (fault != "") {
return fault;
}
Setting Property and Configuration Values } catch(SSLHandshakeException e){
Setting property and configuration values is similar to getting properties. The logger.error("Unexpected Error while sending request,
biggest difference between the two is that configuration values need to be saved possible incorrect hostname?: " + e.getMessage());
once they are set. return "Unexpected exception while sending message.
Possible valid but incorrect host?";
Example of setting a property value: } catch(Exception e){
logger.error("Unexpected Error while sending request: " +
this.setPropertyValue(numberPropertyToSet,
e.getMessage());
new NumberPrimitive(numberValueToSet)); throw e;
Example of setting a configuration table value: }

this.setConfigurationSetting("ConfigTableExample1", "field2", (field2Value + 2.1)); return result;


SaveConfigurationTables(); }
Another example of setting a configuration table value:
this.setConfigurationSetting("ConfigTableExample1", "field2",
((Double)getConfigurationSetting("ConfigTableExample1", "field2")
+ 5.5));
SaveConfigurationTables();

File Management
The file system for ThingWorx does not have a guaranteed static path that can be
referenced. For this purpose, there is a FileRepositoryThing template that is used
to store and access files. The File Repository thing has services, such as
openFileForWrite() and openFileForRead(), that allow you to access files that are
stored in unique locations based on the file repository thing you are using.
Similarly, if you need to upload a file with an extension, then it should be done
through a JAR on the classpath, since the location of an uploaded file may not be
known. Here is an example using the file repository thing and a file pulled from
the classpath:
public String exportResource(String resourceName,
FileRepositoryThing repo) throws Exception {

InputStream stream = null;


OutputStream resStreamOut = null;
String jarFolder;

try {

Appendix A: Examples 77 Appendix A: Examples 79


Referencing Third-Party JavaScript };
this.updateProperty = function (updatePropertyInfo) {
Libraries and Files if (updatePropertyInfo.TargetProperty === "DisplayText") {
valueElem.text(updatePropertyInfo.SinglePropertyValue);
Third-party libraries, images, and other web artifacts needed by the widget should this.setProperty("DisplayText",
be placed in the /ui/<widgetname> folder or subfolders of that location. The *.ide. updatePropertyInfo.SinglePropertyValue);
js and *.runtime.js files can then reference any of those artifacts via that relative }
path of: };
};
../Common/extensions/<extensionname>/ui/<widgetname>/
For example, to include a third-party JavaScript library and CSS into your widget Additional Features
code, one can do the following:
if (!jQuery().qtip) { You can incorporate the following features into your widgets:
$("body").append('<script type="text/javascript"
src="../Common/extensions/MyExtension/ui/mywidget/ • Services that can be bound to events (such as Click of a Button, Selected Rows
include/qtip/jquery.qtip.js"></script>'); Changed, or Service Completed)
$("head").append('<link type="text/css" • Events that can be bound to various services (for example, invoke a service
rel="stylesheet" href="
../Common/extensions/MyExtension/ui/mywidget/include/
and navigate to a mashup)
qtip/jquery.qtip.css" />'); • Properties can be bound out
}
You can access the full power of JavaScript and HTML in your widget code at
runtime. Anything that can be done using HTML and JavaScript is available
Widget Example within your widget.

Mashup Builder Code Widget API: Mashup Builder


The <widgetname>.ide.js file must implement several functions to work
correctly in the Mashup Builder (see Widget API: Mashup Builder). Widgets can Widget Lifecycle in the Mashup Builder
declare widget properties, services, and events in functions.
A widget has the following lifecycle within the Mashup Builder. During each
Below is sample code for a widget named MyWidget with a bindable string lifecycle state, the specified functions on the widget are called by the Mashup
property named DisplayText. Builder.
TW.IDE.Widgets.mywidget = function () {
this.widgetIconUrl = function() { • Discovered
return "../Common/extensions/MyExtension/ui/" +
"mywidget/mywidget.ide.png";
The widget is being loaded into index.html and added to the widget toolbar/
}; palette.
○ widgetProperties()
this.widgetProperties = function () {
return { Called to get information about each widget (such as display name and
name : "My Widget", description)
description : "An example widget.",
category : ["Common"], ○ widgetEvents()
properties : {
DisplayText: {
Called to get information about the events each widget exposes
baseType: "STRING", ○ widgetServices()
defaultValue: "Hello, World!",
isBindingTarget: true Called to get information about the services each widget exposes
} • Created

Appendix B: Widget Implementation Guide 81 Appendix B: Widget Implementation Guide 83


This is the jquery element. builder and runtime.If you set a border of one pixel on the widget-content
• this.getProperty(name) element at design time, you are making the widget two pixels taller and
• this.setProperty(name,value) two pixels wider (one pixel on each side). To account for this discrepancy,
set the borderWidth property to make the design-time widget the same
Note that every call to this function will call afterSetProperty() if it’s defined number of pixels smaller. This property places the border inside the widget
in the widget. that you created and makes the width and height in the widget properties
• this.updatedProperties() accurate.
This function should be called anytime properties are changed in the widget so ○ isContainer - true or false (default). Controls whether an instance of this
that the Mashup Builder can update the widget properties window, the widget can be a container for other widget instances.
connections window, and so on. ○ customEditor – the name of the custom editor dialog to use for entering
• this.getInfotableMetadataForProperty(propertyName) and editing the widget configuration. The system assumes there is a dialog
If you need the infotable metadata for a property that you bound, you can get it named you created TW.IDE.Dialogs.<name>.
by calling this API; it returns undefined if it is not bound. ○ customEditorMenuText - The text that appears on the flyout menu of the
• this.resetPropertyToDefaultValue(propertyName) widget and the hover over text for the Configure Widget Properties button.
For example, “Configure Grid Columns.”
This call resets the named property to its default value.
○ allowPositioning - true (default) or false
• this.removeBindingsFromPropertyAsTarget(propertyName)
○ supportsLabel - true or false (default). If true, the widget exposes a Label
This call removes target data bindings from the propertyName. Use it only property whose value is used to create a text label that appears next to the
when the user has initiated an action that invalidates the property. widget in the Composer and at runtime.
• this.removeBindingsFromPropertyAsSource(propertyName) ○ supportsAutoResize - true or false (default). If true, the widget can be
This call removes source data bindings from the propertyName. Use this only placed in responsive containers (such as columns, rows, responsive tabs,
when the user has initiated an action that invalidates the property. responsive mashups).
• this.isPropertyBoundAsTarget(propertyName) ○ properties - A collection of JSON objects for the widget that describe the
This call returns a result that indicates if the property has been bound as a properties of the widget that can be modified when the widget is added to a
target. You can use it to determine if a property has been set or bound. For mashup. These properties are displayed in the properties window of the
example, the blog widgets validate() function is: Mashup Builder, with the name of each object used as the property name,
and the corresponding attributes controlling how the property value is set.
this.validate = function () {
var result = []; For example:
properties: {
var blogNameConfigured = this.getProperty('Blog');
Prompt: {
defaultValue: 'Search for...',
if (blogNameConfigured === '' || baseType: STRING,
blogNameConfigured === undefined) { isLocalizable: true
},
Width: {
if (!this.isPropertyBoundAsTarget('Blog')) {
defaultValue: 120
result.push({ severity: 'warning', },
message: 'Blog is not bound for {target-id}' }); Height: {
} defaultValue: 20,
} isEditable: false
return result;
},
}

Appendix B: Widget Implementation Guide 85 Appendix B: Widget Implementation Guide 87


○ sourcePropertyName - when the property’s baseType is ‘FIELDNAME’, Widget API: Runtime
this attribute is used to determine which INFOTABLE’s fields are to be
used to populate the FIELDNAME dropdown list. Widget Lifecycle in Runtime
○ baseTypeRestriction - when specified, this value is used to restrict the
• When a widget is first created, the runtime will obtain any declared properties
fields available in the FIELDNAME dropdown list.
by calling the runtimeProperties() function. See Callbacks from Runtime to
○ tagType - if the baseType is ‘TAGS’ this can be ‘DataTags’ (default) or Your Widget for more information.
‘ModelTags’.
• The property values that were saved in the mashup definition will be loaded
○ defaultValue - default undefined; used only for ‘property’ type into your object without your code being called in any way.
○ isBindingSource - true or false; allows the property to be a data binding • After your widget is loaded but before it’s put on to the screen, the runtime
source, default to false will call renderHtml() where you return the HTML for your object. The
○ isBindingTarget - true or false; allows the property to be a data binding runtime will render that HTML into the appropriate place in the DOM
target, default to false • Immediately after that HTML is added to the DOM, you will be called with
○ isEditable - true or false; controls whether the property can be edited in the afterRender(). This is the time to do the various jQuery bindings (if you need
Composer, default to true any). It is only at this point that you can reference the actual DOM elements
○ isVisible - true or false; controls whether the property is visible in the and you should only do this using code such as:
// note that this is a jQuery object
properties window, default to true var widgetElement = this.domElement;
○ isLocalizable - true or false; only important if baseType is ‘STRING’ -
This is important because the runtime actually changes your DOM element ID
controls whether the property can be localized or not.
and you should never rely on any id other than the id returned from this.
○ selectOptions - an array of value / (display) text structures domElementId)
For example: • If you have defined an event that can be bound, whenever that event happens
[{value: ‘optionValue1’, text: ‘optionText1’}, you should call the following:
{value: ‘optionValue2’, text: ‘optionText2’}] var widgetElement = this.domElement;
○ warnIfNotBoundAsSource - true or false; if true, then the property will be // change ‘Clicked’ to be whatever your event name is that
// you defined in your runtimeProperties that people bind to
checked by the Composer for whether it’s bound and generate a to-do item
widgetElement.triggerHandler('Clicked');
when it’s not
• If you have any properties bound as data targets, you will be called with
○ warnIfNotBoundAsTarget - true or false; if true, then the property will be
updateProperty(). You are expected to update the DOM directly if the changed
checked by the Composer for whether it’s bound and generate a to-do item
property affects the DOM - which is likely, otherwise why would the data be
when it’s not
bound
• afterLoad() [optional] - called after your object is loaded and properties have • If you have properties that are defined as data sources and they’re bound you
been restored from the file, but before your object has been rendered can be called with getProperty_{propertyName}() … if you don’t define this
• renderHtml() [required] - returns HTML fragment that the Composer will function, the runtime will simply get the value from the property bag.
place in the screen; the widget’s content container (e.g. div) must have a
‘widget-content’ class specified, after this container element is appended to Runtime APIs Available to Widgets
the DOM, it becomes accessible via jqElement and its DOM element id will
be available in jqElementId The following APIs can be accessed by a widget in the context of the runtime:
• widgetEvents() [optional] - a collection of events; each event can have the • this.jqElementId
following properties: This is the DOM element ID of your object after renderHtml().
• this.jqElement

Appendix B: Widget Implementation Guide 89 Appendix B: Widget Implementation Guide 91


• afterRender() [optional] - called after the widget html fragment is inserted into get the value of that property. This is used when the runtime is pulling data
the dom. Use this.domElementId to find the DOM element ID. Use this. from your widget to populate parameters for a service call.
jqElement to use the jQuery reference to this dom element
• beforeDestroy() [optional but highly recommended] - this is called anytime the Tips
widget is unloaded, this is the spot to...
• Use this.jqElement to limit your element selections. This will reduce the
○ unbind any bindings chance of introducing unwanted behaviors in the application when there might
○ clear any data set with .data() be duplicate IDs and/or classes in the DOM.
○ destroy any third party libraries or plugins, call their destructors, etc. ○ Don’t do the following: $('.add-
○ free any memory you allocated or are holding on to in closures, by setting btn').click(function(e){...do something...});
the variables to null ○ Do this:
○ there is no need to destroy the DOM elements inside the widget, they will this.jqElement.find('.add-btn').click(function(e){
...do something...});
be destroyed for you by the runtime
• Logging - we recommend that you use the following methods to log in the
• resize(width,height) [optional – only useful if you declare
Mashup Builder and Runtime environment:
supportsAutoResize: true] - this is called anytime your widget is resized. Some
widgets don’t need to handle this. For example, if the widget’s elements and ○ TW.log.trace(message[, message2, ... ][, exception])
CSS auto-scale. But others (most widgets) need to actually do something ○ TW.log.debug(message[, message2, ... ][, exception])
based on the widget changing size.
○ TW.log.info(message[, message2, ... ][, exception])
• handleSelectionUpdate(propertyName, selectedRows, selectedRowIndices) -
○ TW.log.warn(message[, message2, ... ][, exception])
called whenever selectedRows has been modified by the data source you’re
bound to on that (PropertyName. selectedRows is an array of the actual data ○ TW.log.error(message[, message2, ... ][, exception])
and selectedRowIndices is an array of the indices of the selected rows. ○ TW.log.fatal(message[, message2, ... ][, exception])
You can view the log messages in the Mashup Builder by opening the log
window via the Help>Log menu item; in the Mashup runtime, you can now
Note click on the "Show Log" button on the top left corner of the page to show log
To get the full selectedRows event functionality without having to bind a window. If the browser you use supports console.log(), then the messages will
list or grid widget, this function must be defined. also appear in the debugger console.
• Formatting - if you have a property with baseType of STYLEDEFINITION,
• serviceInvoked(serviceName) - serviceInvoked() is called whenever a service you can get the style information by calling
you defined is triggered. var formatResult = TW.getStyleFromStyleDefinition(
widgetProperties['PropertyName']);
• updateProperty(updatePropertyInfo) - updatePropertyInfo is an object with the
following JSON structure{ If you have a property of baseType of STATEFORMATTING:
{ var formatResult = TW.getStyleFromStateFormatting({
DataShape: metadata for the rows returned DataRow: row,
ActualDataRows: actual Data Rows StateFormatting: thisWidget.properties['PropertyName']
SourceProperty: SourceProperty });
TargetProperty: TargetProperty
In both cases formatResult is an object with the following defaults:
RawSinglePropertyValue: value of SourceProperty
{
in the first row of ActualDataRows
image: '',
SinglePropertyValue: value of SourceProperty
backgroundColor: '',
in the first row of ActualDataRows
foregroundColor: '',
converted to the defined baseType of the

Appendix B: Widget Implementation Guide 93 Appendix B: Widget Implementation Guide 95

You might also like