1. ThingWorx扩展开发环境搭建
ThingWorx 平台自带大量开箱即用的构件(Thing、ThingTemplate、Mashup、Service 等),但当业务需要接入私有协议、封装专有算法、复用已有 Java 类库,或对平台行为做底层增强时,就必须走扩展(Extension)这条路。扩展本质上是一个遵循固定目录约定的 Java 工程,编译打包成 .zip 后由平台导入,其中的自定义服务、资源、配置项会被注册进运行时,像平台原生构件一样被调用。本章的目标很明确:把工具链装好、把工程骨架建对、把第一个自定义服务真正跑起来。
1.1 扩展开发的三件套
要开发 ThingWorx 扩展,需要三样东西:JDK、Extension SDK、IDE。三者版本必须匹配,否则最常见的报错就是编译期找不到 com.thingworx.* 包,或者打包后平台导入失败。
JDK 版本取决于目标平台版本。ThingWorx 8.x 系列通常使用 JDK 8,9.x 及以后逐步迁移到 JDK 11 或 17。判断方法很简单:登录平台后查看 System > Information 中的 Java 版本,或直接询问平台管理员。SDK 必须与平台版本严格对应,例如平台是 9.4,就应使用 9.4 对应的 Extension SDK,跨大版本混用极易出现 API 不兼容。
Extension SDK 可以从 PTC 支持站点下载,解压后得到的是一个包含 lib、samples、templates 等目录的文件夹。其中 lib 下的 thingworx-common.jar、thingworx-platform-common.jar 等是编译扩展时必须引用的核心依赖;samples 里则提供了官方示例工程,是理解目录结构最好的参考。
1.2 配置 Eclipse 或 IntelliJ IDEA
IDE 的选择不影响最终产物,只影响开发体验。Eclipse 与 SDK 的契合度更高,官方示例多为 Eclipse 工程;IntelliJ IDEA 在代码提示、重构和 Maven 集成上更顺手。下面以 IntelliJ IDEA 为例说明配置要点,Eclipse 的操作逻辑一致。
第一步,新建一个普通 Java 工程,不要选 Maven 或 Gradle 模板,因为 SDK 的构建脚本是自带的 Ant 脚本。第二步,在 Project Structure > Libraries 中把 SDK 的 lib 目录整体加入依赖,这样 ThingworxService、ThingworxBaseThing 等基类才能被识别。第三步,把 SDK 中的 build.xml、build-common.xml 等构建文件复制到工程根目录,并修改其中的 SDK 路径变量,使其指向本机实际解压位置。
第四步,配置编译输出。扩展工程通常约定源码放在 src,编译产物放在 bin,最终打包目录为 dist。在 IDEA 中把 Project Structure > Modules > Paths 的输出路径设为 bin,并确保 src 被标记为 Sources Root。第五步,验证环境:在工程中新建一个类,尝试继承 ThingworxBaseThing,如果 IDE 能正常解析且无红色报错,说明 SDK 依赖已生效。
1.3 扩展项目的目录结构
扩展工程的目录结构不是随意约定的,平台在导入时会按固定路径扫描元数据。一个最小可用的扩展工程结构如下:
HelloWorldExtension/
├── build.xml # Ant 构建脚本
├── build-common.xml # SDK 公共构建逻辑
├── src/
│ └── com/example/hello/
│ └── HelloWorldThing.java
├── configfiles/
│ └── metadata.xml # 扩展元数据:名称、版本、厂商
├── lib/ # 第三方 jar(可选)
├── bin/ # 编译输出
└── dist/ # 打包输出 HelloWorldExtension.zip
其中 metadata.xml 是扩展的“身份证”,必须包含扩展名称、版本号、供应商、平台版本范围等信息。平台导入时会读取它,若版本范围与当前平台不匹配,导入会被拒绝。src 下按 Java 包名组织源码,包名建议使用反向域名,避免与平台类冲突。
1.4 创建第一个 Hello World 自定义服务
下面编写一个最简单的自定义服务:接收一个字符串参数 name,返回拼接后的问候语。在 src/com/example/hello/ 下新建 HelloWorldThing.java:
package com.example.hello;
import com.thingworx.types.InfoTable;
import com.thingworx.types.collections.ValueCollection;
import com.thingworx.types.primitives.StringPrimitive;
import com.thingworx.entities.utils.ThingUtilities;
import com.thingworx.metadata.annotations.ThingworxServiceDefinition;
import com.thingworx.metadata.annotations.ThingworxServiceParameter;
import com.thingworx.metadata.annotations.ThingworxServiceResult;
import com.thingworx.things.Thing;
public class HelloWorldThing extends Thing {
@ThingworxServiceDefinition(
name = "SayHello",
description = "返回问候语",
category = "Hello")
@ThingworxServiceResult(
name = "result",
baseType = "STRING",
description = "问候结果")
public String SayHello(
@ThingworxServiceParameter(
name = "name",
baseType = "STRING",
description = "用户名") String name) throws Exception {
if (name == null || name.trim().isEmpty()) {
name = "ThingWorx";
}
return "Hello, " + name + "!";
}
}
这段代码的关键在于三个注解:@ThingworxServiceDefinition 声明服务名与分类,@ThingworxServiceParameter 声明入参,@ThingworxServiceResult 声明返回值。平台在导入扩展时会扫描这些注解,自动在 Thing 上生成对应的服务定义,无需手工在 Composer 中配置。
1.5 打包与部署
在工程根目录执行 Ant 构建:
ant -f build.xml
构建成功后,dist 目录下会生成 HelloWorldExtension.zip。部署有两种方式:一是登录 Composer,进入 Import/Export > Import,选择该 zip 上传;二是把 zip 直接放入平台安装目录的 ThingworxStorage/extensions/ 下,然后重启平台。前者适合开发调试,后者适合批量部署。
导入成功后,在 Composer 中新建一个 Thing,将其 Base Thing Template 设为 GenericThing,然后在 Implement Shape 中勾选 HelloWorldThing。保存后进入该 Thing 的 Services 页签,就能看到 SayHello 服务。点击执行,在 name 参数填入 Engineer,返回结果应为 Hello, Engineer!。若返回为空或报错,优先检查扩展是否真正导入成功、Thing 是否实现了对应 Shape。
1.6 常见问题与排查
扩展开发初期最容易踩的坑集中在三处。第一,SDK 版本与平台版本不匹配,表现为导入时提示版本范围校验失败,解决方法是核对平台版本并更换对应 SDK。第二,注解缺失或拼写错误,表现为服务导入后在 Composer 中看不到,需检查 @ThingworxServiceDefinition 是否写在 public 方法上、参数注解是否完整。第三,打包时未包含 metadata.xml,表现为平台提示扩展元数据缺失,需确认 build.xml 的打包目标是否把 configfiles 目录纳入 zip。