系列
- Thingworx自定义扩展开发(一)- 开发环境搭建、Extension Demo Coding
- Thingworx自定义扩展开发(二)- Widget Demo Coding
- Thingworx自定义扩展开发(三)- echarts集成
Create An Extension
Overview
Build extensions quickly and extend your application functionality with the Eclipse Plugin.
GUIDE CONCEPT
Extensions enable you to quickly and easily add new functionality to an IoT solution. Extensions can be service (function/method) libraries, connector templates, functional widgets, and more.
The Eclipse Plugin for ThingWorx Extension Development (Eclipse Plugin) is designed to streamline and enhance the creation of extensions for the ThingWorx Platform. The plugin makes it easier to develop and build extensions by automatically generating source files, annotations, and methods as well as updating the metadata file to ensure the extension can be imported.
These features allow you to focus on developing functionality in your extension, rather than worrying about getting the syntax and format of annotations and the metadata file correct.
THINGS USED IN THIS GUIDE
Environment:
This guide may be completed within any environment where you have access to the ThingWorx Foundation server. This guide is based on the Extension SDK version 8.3.
This includes both the hosted evaluation server, as well as the downloadable trial. You can use either Gradle or Ant to build your ThingWorx Extension Project.
Before you start, ensure you have the following software installed.
- Download Java Development Kit: jdk1.8.
- Download Eclipse Photon for Java Developers.
Completed Example: You are provided a complete example that can be utilized as a reference for completion as you go through the guide or for insight into how the following steps can be completed.
NOTE: If you use Gradle as the build tool, in your Eclipse application, download the Gradle STS IDE plugin from Help->Eclipse Marketplace, then restart your workspace.
Step 1: Completed Example
Download the completed files for this tutorial: Extension Sample Files.
The ExtensionSampleFiles.zip file provided to you contains a completed example of the scenario you will be walk through in the following steps. Utilize this file if you would like to see a finished example as a reference or if you become stuck during this guide and need some extra help.
Step 2: Download Plugin and SDK
The ThingWorx Extension SDK provides supported classes and APIs to build Java-based extensions. The APIs included in this SDK allow manipulation of ThingWorx platform objects to create Java based extensions that can extend the capability of the existing ThingWorx platform.
The Eclipse Plugin assists in working with the Extension SDK to create projects, entities, and samples.
- Download the Eclipse Plugin from ThingWorx Marketplace.
- Download Extension SDK from ThingWorx Marketplace.
- Make a note of the directory where the plugin and the extension SDK are stored. The path of the directory will be required in upcoming steps. Do not extract the zip files.
Step 3: Install and Configure
Before you install the plugin, ensure that software requirements are met for proper installation of the plugin.
-
Open Eclipse Mars and choose a suitable directory as a workspace.
-
Go to the menu bar of the Eclipse window and select Help->Install New Software…
-
After the Install window opens, click Add to add the Eclipse Plugin repository.
-
Click Archive… and browse to the directory where the Eclipse Plugin zip file is stored and click Open.
-
Enter a name (for example, Eclipse Plugin).
-
Click OK.
NOTE: Do not extract this zip file.
-
Ensure that the Group items by category checkbox is not selected.
-
Select ThingWorx Extension Builder in the items list of the Install window.
-
Click Next and the items to be installed are listed.
-
Click Next and review the license agreement.
-
Accept the license agreement and click Finish to complete the installation process.
NOTE: If a warning for unsigned content is displayed, click OK to complete the installation process.
-
Restart Eclipse.
-
When Eclipse starts again, ensure that you are in the ThingWorx Extension perspective. If not, select Window->Perspective->Open Perspective->Other->ThingWorx Extension, then click OK.
NOTE: Opening any item from File->New->Other…->ThingWorx will also change the perspective to ThingWorx Extension.
You are ready to start a ThingWorx Extension Project!
Step 4: Create Extension Project
In this tutorial, you will create a ThingWorx extension that retrieves weather information using OpenWeatherMap API.
Create Account
In this part of the lesson, you will create a free account in OpenWeatherMap that creates an AppKey so you can access their REST API.
-
Sign-up for a free account.
-
Log in to your account.
-
NOTE: We will use this generated API key as a parameter in the REST calls.
Create New Extension Project
NOTE: Make sure that you are in the ThingWorx Extension Perspective. To verify, you should see a plus icon:in the menu bar. If you don’t see this, you are probably in the wrong perspective. Go back to the previous step to learn how to set the perspective to ThingWorx Extension in Eclipse.
-
Go to File->New->Project.
-
Click ThingWorx->ThingWorx Extension Project.
-
Click Next.
NOTE: A New ThingWorx Extension window will appear.
-
Enter the Project Name (for example, MyThingworxWeatherExtension).
-
Enter the SDK location by browsing to the directory where the Extension SDK is stored.
NOTE: The Eclipse Plugin accepts the Extension SDK version 6.6 or higher.
-
Enter the Vendor information (for example, ThingWorx Labs).
-
Change the default package version from 1.0.0 to support extension dependency .
NOTE: The information from ThingWorx Extension Properties is used to populate the metadata.xml file in your project. The metadata.xml file contains information about the extension and details for the various artifacts within the extension. The information in this file is used in the import process in ThingWorx to create and initialize the entities.
-
Select the JRE version to 1.8.
-
Click Next then click Finish. Your newly created project is added to the Package Explorer tab.
Create New Entity
-
Select your project and click Add to create a new entity.
NOTE: You can also access this from the ThingWorx menu on the menu bar.
-
Create a Thing Template for your MyThingWorxWeatherExtension Project.
NOTE: In this guide, we are using a Template, but in a real-world scenario, you may consider using a Thing Shape to encapsulate extension functionality. By using Thing Shapes you give users of your extension the ability to easily add new functionality to existing Things. It is simple to add a new Thing Shape to an existing Thing Template, while using the properties or services defined by a Thing Template would require recreating all existing assets using the new Template. Since subscriptions cannot be created on Thing Shapes, you might choose to create Thing Templates that implement one or more subscriptions for convenience.
-
In the pop-up window, browse to add the source folder of your project in Source Folder.
NOTE: It should default to the src directory of your project. In our case it will be MyThingworxWeatherExtension/src.
-
Browse to add the package where you want to create this new class, or simply give it a name (such as com.thingworx.weather).
-
Enter a name and description to your Thing Template (WeatherThingTemplate).
NOTE: By default, the Base Thing Template is set to GenericThing.
-
Select Next.
NOTE: If you want to give other users of this entity permission to edit it in ThingWorx Composer, select the entity as an editable entity. Only non-editable entities can be upgraded in place; editable entities must be deleted and recreated when your extension is updated. If you need to make it possible to customize the extension, consider using a configuration table to save user customizations.
-
Select Finish.
-
Verify that you have a WeatherThingTemplate class created that extends the Thing class.
@ThingworxBaseTemplateDefinition(name = "GenericThing") public class WeatherThingTemplate extends Thing { public WeatherThingTemplate() { // TODO Auto-generated constructor stub } }
-
You might see a warning to add a serial version. You can add a default or generated serial value.
Step 5: Add Properties
In this section, you are going to add CurrentCity, Temperature and WeatherDescription properties to the WeatherThingTemplate. These properties are associated with the Thing Template and add the @ThingworxPropertyDefinitions annotation before the class definition in the code.
-
Right click inside the WeatherThingTemplate class or right click on the WeatherThingTemplate class from the Package Explorer.
-
Select ThingWorx Source-> Add Property.
-
In the popup window, create a property to store the city name.
-
Name = CurrentCity, Base Type = STRING, Description = ‘’
-
Select the Has Default Value checkbox and enter a city name (eg. Boston). This will be the default value unless a specific value is passed.
-
Select the “Persistent” checkbox. This will maintain the property value if a system restart occurs. NOTE: If you select the Logged checkbox, the property value is logged to a data store. If you select the Read Only checkbox, the data will be static.
-
Select VALUE from the Data Change Type drop down menu.
NOTE: This allows any Thing in the system to subscribe to a data change event for this property.
-
-
Choose to use one of the following Data Change Types:
Data Change Type Description Always Fires the event to subscribers for any property value change Never Does not fire a change event On For most values, any change will trigger this. Off Fires the event if the new value is false Value For numbers, if the new value has changed by more than the threshold value, fire the change event. For non-numbers, this setting behaves the same as Always. -
Select Finish.
-
Create another property called Temperature with a base type of NUMBER. You can keep the default values for the other parameters.
-
Create another property called WeatherDescription with a base type of STRING. Keep the default values for the other parameters.
Step 6: Create Configuration Table
In this part of the lesson, we will create a configuration table to store the API Id that you generated from the openMapsWeather. Configuration tables are used for Thing Templates to store values similar to properties that do not change often.
-
Right-click inside the WeatherThingTemplate class and select ThingWorx Source->Add Configuration Table.
-
Create a new configuration table with name OpenWeatherMapConfigurationTable.
-
Click Add in the Data Shape Field Definitions frame.
NOTE: Configuration tables require fields (columns) with a defined table structure (DataShape).
-
Enter appid as the name with a base type STRING.
-
Select the Required checkbox.
-
Click OK, then Finish to add the Configuration Table.
-
To use the appid in the REST calls, you need to obtain the value from the configuration table and assign it to a field variable in the Java code. We will use the initializeThing method to obtain the appid value at runtime.
NOTE: The initializeThing() method acts as an initialization hook for the Thing. Every time a Thing is created or modified, this method is executed and the value of appid is obtained from the configuration table and stored in a global field variable of the class. initializeThing() must call super.initializeThing() to ensure it performs initialization of the Thing.
-
Create the initializeThing() method and field variable _appid with base type STRING anywhere in the WeatherThingTemplate class.
private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(WeatherThingTemplate.class); private String _appid; @Override public void initializeThing() throws Exception { super.initializeThing(); _appid = (String) this.getConfigurationSetting("OpenWeatherMapConfigurationTable", "appid"); }
NOTE: In the code above we used ThingWorx LogUtilities to get a reference to the ThingWorx logging system, then assigned the reference to the variable _logger. In the steps below we will use this variable to log information. There are multiple kinds of loggers and log levels used in the ThingWorx Platform, but we recommend that you use the application or script loggers for logging anything from inside extension services. If prompted to import the logger, use slf4j.
Step 7: Add JAR Resources
You can add external JARs for use by your extension. Every third-party JAR that is not already included in either the ThingWorx Extension SDK or ThingWorx Core needs to be added to your extension project as a JAR Resource. These JAR resources are added to your metadata.xml as a tag and are used to reference the Java classes in the third-party JAR on which the extension depends. You can either use the Add buttonor the ThingWorx Menu from the menu bar to add a new JAR resource. By doing so, the JAR is automatically updated in the metadata file and added to the build path.
Although ThingWorx allows developers to include jar files for third-party libraries in their code, it is recommended that you avoid adding jar files for common libraries and instead use the jar files that are included with the SDK. Adding jar files to an extension could cause conflicts with jar files already used by the ThingWorx server, either when uploading an extension, or when an extension executes. Even if your extension works today, future updates to ThingWorx may require updates to your extensions. Similarly, packaging a verison of a commonly used library may mean that a customer will not be able to use your extension together with someone else’s extension.
-
Select the project to which you want to add the jar file to and select New Jar Resource.
-
Open the directory in which you have stored the training files for this tutorial. Browse to the Jars directory. 3.Select the json-simple-1.1.1.jar file. Add a description and click Finish.
NOTE: This will automatically add json-simple-1.1.1.jar to the lib folder and to your project’s build path.
-
Add httpclient-4.5.6.jar, httpcore-4.4.10.jar and commons-logging-1.2.jar directly into the twx-lib folder in the Project folder.
NOTE: These JARs are included in the group of JARs used by ThingWorx by default.
-
In order to build your extension locally, without bundling the jars into your extension that are available on the ThingWorx server, add the above JARs to your project’s build path by right-clicking on your project in the Package Explorer, right-click Your_Extension_Project (ie, MyThingWorxWeatherExtension) and select Build Path -> Configure Build Path. Verify the jars we added are in the build path. Otherwise, click Add JARs, then browse to the directory containing these JARs (lib) and add them.
NOTE: twx-lib folder is a hidden folder and does not appear in the Eclipse package explorer. The twx-lib folder can be found in the WeatherExtension project inside the Eclipse workspace directory.
Step 8: Create Services
Now that you have created properties, configuration tables and added the required jars, you can begin to add services to your WeatherThingTemplate.
In this part of the lesson, we’ll add a service, UpdateWeatherInfo that will take a City parameter and update the properties of this template using the values obtained from the openWeatherMap API.
-
Right click inside the WeatherThingTemplate and select ThingWorx Source->Add Service.
-
Create a new service with name UpdateWeatherInfo.
-
Click Add in the Input Parameters frame to add City parameter with a base type STRING.
-
Set the name and base type of the Output Parameter based on the value that you want the service to return. For simplification, assume this service returns nothing. Set the Base Type to NOTHING.
-
Click Finish to create the service.
-
Copy and Paste the code for UpdateWeatherInfo as specified below.
@ThingworxServiceDefinition(name = "UpdateWeatherInfo", description = "updates weather description and temperature properties", category = "", isAllowOverride = false, aspects = {
"isAsync:false" })
@ThingworxServiceResult(name = "Result", description = "", baseType = "NOTHING")
public void UpdateWeatherInfo(
@ThingworxServiceParameter(name = "City", description = "city name", baseType = "STRING") String City) throws Exception {
_logger.trace("Entering Service: UpdateWeatherInfo");
String cityProp = this.getPropertyValue("CurrentCity").getStringValue();
if (City == null){
City = cityProp;
} else {
this.setPropertyValue("CurrentCity", new StringPrimitive(City));
}
String url = "http://api.openweathermap.org/data/2.5/weather?q=" +URLEncoder.encode(City,"UTF-8") + "&appid="+ _appid+"&units=imperial";
// create a http client
HttpClient client = new DefaultHttpClient();
// create a get request with the URL
HttpGet getRequest = new HttpGet(url);
// add Accept header to accept json format response
getRequest.addHeader("Accept", "application/json");
// send the get request and obtain a response
HttpResponse response = client.execute(getRequest);
// if response is successful the status code will be 200.
if (response.getStatusLine().getStatusCode() == 200) {
BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuilder sb = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(sb.toString());
JSONArray weather = (JSONArray) json.get("weather");
Iterator<JSONObject> it = weather.iterator();
String description = (String) it.next().get("description");
this.setPropertyValue("WeatherDescription", new StringPrimitive(description));
double temp = (Double) ((JSONObject) json.get("main")).get("temp");
this.setPropertyValue("Temperature", new NumberPrimitive(temp));
/* fire event BadWeather */
_logger.trace("Exiting Service: UpdateWeatherInfo");
}
}
Troubleshooting
Issue | Solution |
---|---|
The iterator() is undefined in JSONArray | Import only org.json.simple.*. Importing other JSON libraries can give this error. |
HttpClient/HttpGet could not be resolved to a type. | Make sure you have imported the jars: httpclient-4.5.2.jar, httpcore-4.4.5.jar and commons-logging-1.2.jar, json-simple-1.1.1.jar as indicated in the previous chapter. Make sure you have imported the following packages in your template by Eclipse’s quick fix feature. If not, add the code below to the very beginning of your Template file. |
Your code should be similar to the following:
package com.thingworx.weather;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.Iterator;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import com.thingworx.logging.LogUtilities;
import com.thingworx.metadata.annotations.ThingworxBaseTemplateDefinition;
import com.thingworx.metadata.annotations.ThingworxConfigurationTableDefinition;
import com.thingworx.metadata.annotations.ThingworxConfigurationTableDefinitions;
import com.thingworx.metadata.annotations.ThingworxDataShapeDefinition;
import com.thingworx.metadata.annotations.ThingworxFieldDefinition;
import com.thingworx.metadata.annotations.ThingworxPropertyDefinition;
import com.thingworx.metadata.annotations.ThingworxPropertyDefinitions;
import com.thingworx.metadata.annotations.ThingworxServiceDefinition;
import com.thingworx.metadata.annotations.ThingworxServiceParameter;
import com.thingworx.metadata.annotations.ThingworxServiceResult;
import com.thingworx.things.Thing;
import com.thingworx.types.primitives.NumberPrimitive;
import com.thingworx.types.primitives.StringPrimitive;
@ThingworxBaseTemplateDefinition(name = "GenericThing")
@ThingworxPropertyDefinitions(properties = {
@ThingworxPropertyDefinition(name = "CurrentCity", description = "", category = "", baseType = "STRING", isLocalOnly = false, aspects = {
"defaultValue:Boston", "isPersistent:true", "isLogged:true", "dataChangeType:VALUE" }), @ThingworxPropertyDefinition(name = "Temperature", description = "", category = "", baseType = "NUMBER", isLocalOnly = false, aspects = {
"defaultValue:0", "isPersistent:true", "isLogged:true", "dataChangeType:VALUE" }), @ThingworxPropertyDefinition(name = "WeatherDescription", description = "", category = "", baseType = "STRING", isLocalOnly = false, aspects = {
"dataChangeType:VALUE" }) })
@ThingworxConfigurationTableDefinitions(tables = {
@ThingworxConfigurationTableDefinition(name = "OpenWeatherMapConfigurationTable", description = "", isMultiRow = false, ordinal = 0, dataShape = @ThingworxDataShapeDefinition(fields = {
@ThingworxFieldDefinition(name = "appid", description = "", baseType = "STRING", ordinal = 0, aspects = {
"isRequired:true" }) })) })
public class WeatherThingTemplate extends Thing {
private static final long serialVersionUID = -5294151832877452442L;
public WeatherThingTemplate() {}
private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(WeatherThingTemplate.class);
private String _appid;
@Override
public void initializeThing() throws Exception {
super.initializeThing();
_appid = (String) this.getConfigurationSetting("OpenWeatherMapConfigurationTable", "appid");
}
@ThingworxServiceDefinition(name = "UpdateWeatherInfo", description = "updates weather description and temperature properties", category = "", isAllowOverride = false, aspects = {
"isAsync:false" })
@ThingworxServiceResult(name = "Result", description = "", baseType = "NOTHING")
public void UpdateWeatherInfo(
@ThingworxServiceParameter(name = "City", description = "city name", baseType = "STRING") String City) throws Exception {
_logger.trace("Entering Service: UpdateWeatherInfo");
String cityProp = this.getPropertyValue("CurrentCity").getStringValue();
if (City == null){
City = cityProp;
} else {
this.setPropertyValue("CurrentCity", new StringPrimitive(City));
}
String url = "http://api.openweathermap.org/data/2.5/weather?q=" +URLEncoder.encode(City,"UTF-8") + "&appid="+ _appid+"&units=imperial";
// create a http client
HttpClient client = HttpClientBuilder.create().build();
// create a get request with the URL
HttpGet request = new HttpGet(url);
// add Accept header to accept json format response
request.addHeader("Accept", "application/json");
// send the get request and obtain a response
HttpResponse response = client.execute(request);
// if response is successful the status code will be 200.
if (response.getStatusLine().getStatusCode() == 200) {
BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuilder sb = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(sb.toString());
JSONArray weather = (JSONArray) json.get("weather");
Iterator<JSONObject> it = weather.iterator();
String description = (String) it.next().get("description");
this.setPropertyValue("WeatherDescription", new StringPrimitive(description));
Double temp = (Double) ((JSONObject) json.get("main")).get("temp");
Number number = (Number) temp;
this.setPropertyValue("Temperature", new NumberPrimitive(number));
_logger.trace("Exiting Service: UpdateWeatherInfo");
}
}
}
Step 9: Create Event and Subscription
In this section, you will create a mechanism to notify the user when inclement weather occurs. For example, whenever the weather indicates storm/snow/rain, an Event should be triggered. This event is then handled by a service called a Subscription.
For this tutorial, while updating weather information inside the service UpdateWeatherInfo,we need to fire an Event: BadWeather when the weather description indicates either storm/snow/rain. This Event is handled by a Subscription: HandleWeather, which is a service that gets called whenever the BadWeather Event is fired. The subscription service HandleWeather updates a property called AlertNotification.
Create Event
In this part of the lesson, we will create an Event: BadWeather that updates weather information inside the service UpdateWeatherInfo when the weather description indicates either storm/snow/rain.
-
Create a property AlertNotification with a baseType STRING using the Add Property feature of the plugin we discussed before.
-
Right click inside your java file->ThingWorx Source->Add Event.
-
Set the name to BadWeather and set a name for the Data Shape to Weather.
NOTE: This custom Data Shape has to be created in Composer. Importing Datashapes and other custom entities created in Composer into your extension will be discussed later in the tutorial. Our DataShape Weather includes one field called WeatherDescription with a STRING base type.
-
Click Finish.
NOTE: This will create annotation for your EventDefinition.
Create Subscription
In this part of the lesson, we will create a Subscription: HandleWeather, which is a service that gets called whenever the BadWeather Event is fired. The subscription service HandleWeather updates a property called AlertNotification.
-
Right click inside your java file ->ThingWorx Source->Add Subscription
-
Set the Event Name to BadWeather and Handler Service to HandleWeather.
NOTE: This means that whenever the BadWeather event is fired, the HandleWeather service will be executed. Source is left blank if the event belongs to the same template.
-
Click Finish. This creates annotation for subscription service and also creates a new service called HandleWeather.
Modify Service
In this part of the lesson, you’ll ensure that when the properties are updated, BadWeather event is triggered if the description indicates rain/snow/thunderstorm. To do this, we will modify the UpdateWeatherInfo service.
After we have called the setPropertyValue method for the properties WeatherDescription and Temperature, we can check if the weather description contains snow/rain/thunderstorm. We will create an InfoTable from the Weather Datashape. InfoTables represent data sets that take the structure of the Datashape. Each row of an InfoTable can be passed as a ValueCollection to hold data within the table. When an event is fired, we need to send data along with it. This data will be passed as an InfoTable and it is then handled by the Subscription handling the Event.We use a ValueCollection to add the weatherDescription to the InfoTable. Then, this InfoTable is set as the Event Data.
-
Add the code snippet to UpdateWeatherInfo section at the end of the service after setting the properties- Temperature and WeatherDescription.
/* fire event BadWeather */ if (description.contains("snow") || description.contains("rain") || description.contains("thunderstorm")) { ValueCollection v = new ValueCollection(); v.put("weatherDescription", new StringPrimitive(description)); InfoTable data = InfoTableInstanceFactory.createInfoTableFromDataShape("Weather"); data.addRow(v); _logger.info("Firing event"); ThingworxEvent event = new ThingworxEvent(); event.setEventName("BadWeather"); event.setEventData(data); this.dispatchEvent(event); }
-
Inside the HandleWeather service, set the property AlertNotification. Ensure that you have created the property AlertNotification using the eclipse plugin.
-
Add the following code snippet to the HandleWeather service.
@ThingworxServiceDefinition(name = "HandleWeather", description = "Subscription handler", category = "", isAllowOverride = false, aspects = {"isAsync:false"}) public void HandleWeather( @ThingworxServiceParameter(name = "eventData", description = "", baseType = "INFOTABLE") InfoTable eventData, @ThingworxServiceParameter(name = "eventName", description = "", baseType = "STRING") String eventName, @ThingworxServiceParameter(name = "eventTime", description = "", baseType = "DATETIME") DateTime eventTime, @ThingworxServiceParameter(name = "source", description = "", baseType = "STRING") String source, @ThingworxServiceParameter(name = "sourceProperty", description = "", baseType = "STRING") String sourceProperty) throws Exception { _logger.trace("Entering Service: HandleWeather with: Source: \"\", Event: \"BadWeather\", Property: \"\""); this.setPropertyValue("AlertNotification", new StringPrimitive("Alert:"+eventData.getFirstRow().getStringValue("weatherDescription"))); _logger.trace("Exiting Service: HandleWeather"); }
Now we have an event BadWeather fired every time weather description indicates storm/snow/rain and it is handled by HandleWeather service that sets the AlertNotification property to the InfoTable data passed by the event.
Step 10: Add Composer Entities
In previous parts of this tutorial, we assumed we had a datashape Weather available with field weatherDescription as the Datashape of our event: BadWeather.
In this part of the lesson, we’ll create a DataShape.
-
Go to ThingWorx Composer. Click the + button.
-
In the dropdown, select Data Shape.
-
Enter a name, for example: Weather.
-
Add a Field Definition weatherDescription with baseType STRING.
-
Click check mark in the top left, then Save.
-
Click the More drop-down, then click Export. Export the DataShape entity from Composer, it will download in your system as an xml file.
-
Go back to Eclipse, right-click on your project ->Import…->ThingWorx->Entities.
-
Click Next. Browse to the directory where the xml file was downloaded. Select the xml file and Click Finish.
NOTE: This adds the xml file to the Entities folder in your project.
-
Following the same procedure, import other entities required for this Extension stored in the Entities folder of the download we provided for this training.
NOTE: You can uncheck the box for importing DataShapes_Weather.xml, if you already loaded your Datashape in the previous steps.
Step 11: Build Extension
You can use either Gradle or Ant to build your ThingWorx Extension Project. Ant is the preferred method.
Build Extension with Gradle
- Right click on your project ->Gradle (STS)->Tasks Quick Launcher.
NOTE: This opens up a new window.
- Set Project from the drop-down menu to your project name and type Tasks as build.
- Press Enter.
NOTE: This will build the project and any error will be indicated in the console window.
- Your console window will display BUILD SUCCESSFUL. This means that your extension is created and stored as a zip file in your_project->build->distributions folder.
Build Extension with Ant
-
Go to the Package explorer -> your_project->.
-
Right click on build-extension.xml->Run As->Ant Build.
-
Your console output will indicate BUILD SUCCESSFUL.
NOTE: This will build your project and create the extension zip in the your_project->build->distributions folder of your project.
Step 12: Import Extension
If you have valuable data on your ThingWorx server, save the current state before importing an untested extension by duplicating and renaming the ThingworxStorage directory. This will save all current entities and a new, empty ThingworxStorage directory will be generated when Tomcat is restarted. To restore your saved state, rename the duplicate directory back to ThingworxStorage. Alternatively, If you do not back up your storage, make sure that any entities you want to save are exported into xml format. This way you will be able to restore your ThingWorx server to its initial state by deleting the storage directory before importing the saved entities.
Import Extension
- In the lower left corner, click
Import/Export
, then selectImport
.
NOTE: The build produces a zip file in ProjectName->build->distributions folder. This zip file will be required for importing the extension.
- For the Import Option option, select Extension.
- Click
Browse
and choose the zip file in the distributions folder (located in the Exclipse Project’s build directory). ClickImport
.
Create a Thing
-
Create a Thing using the ThingWorx Composer with the Thing Template set to the WeatherThingTemplate.
-
Open the ConfigurationTable tab and add the appid from the OpenWeatherMap.org site.
-
Open the WeatherAppMashup Mashup by searching for WeatherAppMashup in the Search bar.
-
Click
View Mashup
in the WeatherAppMashup Mashup window. -
Type the name of a city (eg. Boston) and click
go
.
NOTE: You can now see the current temperature reading and weather description of your city in the Mashup.
Troubleshooting
If your import did not get through with the two green checks, you may want to modify your metadata.xml or java code to fix it depending on the error shown in the logs.
Issue | Solution |
---|---|
JAR Conflict arises between two similar jars | JAR conflicts arise when a similar jar is already present in the Composer database. Try to remove the respective jar resources from the metadata.xml. Add these jars explicitly in twx-lib folder in the project folder inside the workspace directory. Now, build the project and import the extension in ThingWorx Composer once again. |
JAR is missing | Add the respective jar resource in metadata.xml using the ThingWorx->New Jar Resource. Now, build the project and import the extension in ThingWorx Composer once again. |
Minimum Thingworx Version [ 7.2.1] requirements are not met because current version is: 7.1.3 | The version of SDK you have used to build your extension is higher than the version of the ThingWorx Composer you are testing against. You can manually edit the configfiles->metadata.xml file to change the Minimum ThingWorx version to your ThingWorx Composer version. |
Step 13: Next Steps
Congratulations! You’ve successfully completed the Create an Extension tutorial, and learned how to:
- Install the Eclipse Plugin and Extension SDK
- Create and configure an Extension project
- Create Services, Events and Subscriptions
- Add Composer entities
- Build and import an Extension
Learn More
We recommend the following resources to continue your learning experience:
Capability | Guide | |
---|---|---|
Build | Application Development Tips & Tricks |
Additional Resources
If you have questions, issues, or need additional information, refer to:
Resource | Link | |
---|---|---|
Community | Developer Community Forum | |
Support | Extension Development Guide | |
Marketplace | Extension Library |