Skip to content

Commit

Permalink
Initial Version 0.1.25 already working.
Browse files Browse the repository at this point in the history
  • Loading branch information
carlescm committed Jun 1, 2020
0 parents commit d87f2e2
Show file tree
Hide file tree
Showing 19 changed files with 712 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"files.exclude": {
"**/.classpath": true,
"**/.project": true,
"**/.settings": true,
"**/.factorypath": true
}
}
131 changes: 131 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# ThingWorx Concurrency Extension

The aim of this extension it's to provide the Swiss Tool for Concurrency in ThingWorx.

## Contents

- [How It Works](#how-it-works)
- [Compatibility](#compatibility)
- [Installation](#installation)
- [Usage](#usage)
- [Mutex](#mutex)
- [Counter](#counter)
- [Concurrency Script Functions](#concurrency-script-functions)
- [Build](#build)
- [Acknowledgments](#acknowledgments)
- [Author](#author)


## How It Works

It publishes standard Java concurrency features in order to be used easily on ThingWorx Server Side Javascript. Also, it may try to solve
typical concurrency problems like doing an autoincrement counter.

## Compatibility

ThingWorx 7.3 and above. It's set to minimum ThingWorx 6.5 and built with ThingWorx 6.5 SDK but I didn't tested with it.

## Installation

Import the extension (ConcurrencyExtension.zip) with ThingWorx Composer Import Extension feature.

## Usage

### Mutex

We implemented a mutex ThingShape with Java ReentrantLook with fair execution enabled which allows to synchronize and block thread execution.
Blocking it's done at Thing's level, et means each Thing which implements the wupMutexTS ThingShape has it's own ReentrantLook.

#### Mutex Usage Samples

Just add the wupMutexTS to the Thing or ThingTemplate to whom you want to add mutex blocking features.

In order to lock a piece of code in Javascript, and ensure that only one thread its entering on it at a time:

```javascript
me.Lock_wupMutexTS();
try {
// -- whatever code that needs to be mutex
} finally {
me.Unlock_wupMutexTS();
}
```
You can also tryLock a piece of code, in order to allow one thread and only one and discard the others.
For instance it may be interesting if you have a timer which triggers once in a while and you don't want that two
consecutive triggers are executed at the same time:

```javascript
if (me.TryLock_wupMutexTS()===true) {
try {
// -- whatever code that needs to be mutex
} finally {
me.Unlock_wupMutexTS();
}
} else {
// -- The lock was already got and previous code its skipped
}
```

You can create more than one mutex per thing, all wupMutexTS services has an optional "id" parameter, which allows to create a more quirurgic mutex.
Each different passed "id" will create its own ReentrantLook. Sample with previous code but with a specific lock for one specific timer.

```javascript
if (me.TryLock_wupMutexTS({ id: "timer1" })===true) {
try {
// -- whatever code that needs to be mutex
} finally {
me.Unlock_wupMutexTS({ id: "timer1" });
}
} else {
// -- The lock was already got and previous code it's skipped
}
```

### Counter

A thread safe "autoincrement" ThingShape. It provides a "counter" property and the corresponding services in order to increase (one by one) it's value.

#### Counter Usage Samples

To increase the counter value, no worries about having any amount of threads incrementing the property value, all will get it's own and unique value:

```javascript
var newCounterValue = me.Increase_wupCounterTS();
```

You can reset or reset counter value with Set_wupCounterTS method:

```javascript
me.Set_wupCounterTS({ value: 0 });
```

The counter it's stored and update on a persistent property named: counter_wupCounterTS

### Concurrency Script Functions

List of script helper functions related with this concurrency extension. This services should go on a subsystem like entity, but subsystems on ThingWorx can't be created through extensions :(

#### GetTotalActiveLocks_wupMutexTS

Returns the total active locks, in the whole ThingWorx running system.

#### GetTotalActiveWaiting_wupMutexTS

Returns the total active threads which are waiting on a lock, in the whole ThingWorx running system.

#### GetTotalThingsLocksUsage_wupMutexTS

Returns the total number of mutex created on Things (ReentranLocks), in the whole ThingWorx running system since last start.

## Build

If you need to build it, it's built with ant and java 8 on a MacOS, the scripts are on the repository. Version change it's done by hand and should be automated.

## Acknowledgments

I've started from the [code](https://community.ptc.com/t5/ThingWorx-Developers/Concurrency-Synchronisation-ConcurrentModificationException/m-p/624921) posted by [@antondorf](https://community.ptc.com/t5/user/viewprofilepage/user-id/290654) on the ThingWorx Developer Community.


## Author

[Carles Coll Madrenas](https://linkedin.com/in/carlescoll)
Binary file added bin/ConcurrencyExtension.jar
Binary file not shown.
Binary file added bin/classes/com/wup/wupBaseThingShape.class
Binary file not shown.
Binary file added bin/classes/com/wup/wupConcurrencySC.class
Binary file not shown.
Binary file added bin/classes/com/wup/wupCounterTS.class
Binary file not shown.
Binary file added bin/classes/com/wup/wupMutexTS.class
Binary file not shown.
116 changes: 116 additions & 0 deletions build.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="ConcurrencyExtension" basedir="." default="build">

<property name="extension.jar" value="ConcurrencyExtension.jar"/>

<property name="target" value="1.8"/>
<property name="source" value="1.8"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="common" value="common"/>
<property name="ui" value="ui"/>
<property name="lib" value="lib"/>
<property name="src.dir" value="${basedir}/src"/>
<property name="build.dir" value="${basedir}/bin"/>
<property name="config.dir" value="${basedir}/configfiles" />
<property name="ui.dir" value="${basedir}/${ui}" />
<property name="lib.dir" value="${basedir}/${lib}" />
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="zip.dir" value="${basedir}/zip"/>

<!-- ExtensionPackage directory structure props -->
<property name="package.lib.basedir" value="${lib}"/>
<property name="package.ui.basedir" value="${ui}"/>
<!--<property name="package.common.lib.dir" value="${package.lib.basedir}/${common}"/>-->
<property name="package.common.lib.dir" value="${package.lib.basedir}/common"/>
<property name="package.common.ui.dir" value="${package.ui.basedir}/${common}"/>

<!-- Extension file info -->
<property name="zip.file.name" value="${ant.project.name}.zip" />

<tstamp>
<format property="NOW" pattern="yyyy-MM-dd HH:mm:ss" />
</tstamp>

<!-- define the classpath so it picks up the ThingWorx SDK jar relative to this basedir -->
<path id="jar.classpath">
<fileset dir="${lib.dir}" includes="*.jar"/>
<pathelement location="${classes.dir}"/>
</path>

<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${zip.dir}" />
</target>

<target name="init" depends="clean">

<mkdir dir="${build.dir}"/>
<mkdir dir="${classes.dir}"/>

<copy includeemptydirs="false" todir="${classes.dir}">
<fileset dir="${src.dir}">
<exclude name="**/*.launch"/>
<exclude name="**/*.java"/>
</fileset>
</copy>

</target>

<target name="build-source" depends="init">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="${classes.dir}" source="${source}" target="${target}" includeantruntime="false">
<src path="${src.dir}"/>
<classpath refid="jar.classpath"/>
</javac>
</target>

<target name="build-jars" depends="build-source">
<echo message="building ${extension.jar} to ${build.dir}..."/>

<jar destfile="${build.dir}/${extension.jar}">
<!-- generate MANIFEST inline -->
<manifest>
<attribute name="Built-By" value="${extension.package.vendor}"/>
<attribute name="Build-Date" value="${NOW}"/>
<section name="${extension.package.name}">
<attribute name="Package-Title" value="${extension.package.title}"/>
<attribute name="Package-Version" value="${extension.package.version}"/>
<attribute name="Package-Vendor" value="${extension.package.vendor}"/>
</section>
</manifest>

<fileset dir="${classes.dir}" />
</jar>
</target>

<target name="package-extension" depends="build-jars">
<zip destfile="${zip.dir}/${zip.file.name}">
<!-- ENTITY-Specific JARs: MUST be put in <entity_name> subdir -->
<mappedresources>
<fileset dir="${build.dir}" includes="${extension.jar}" />
<globmapper from="*" to="${package.common.lib.dir}/*"/>
</mappedresources>
<!-- widget directory -->
<zipfileset dir="${config.dir}" includes="metadata.xml" />
</zip>
</target>

<target name="build" depends="package-extension">
<echo message="Building ${ant.project.name} extension package..."/>
</target>

</project>














4 changes: 4 additions & 0 deletions compile_pack_extension.command
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
echo -n -e "\033]0;Packing ConcurrencySEE\007"
cd "`dirname "$0"`"
ant
#osascript -e 'tell application "Terminal" to close (every window whose name contains "Packing ConcurrencySEE")' &
52 changes: 52 additions & 0 deletions configfiles/metadata.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?><Entities>
<ExtensionPackages>
<ExtensionPackage
name="ConcurrencyExtension"
description="Concurrency Tools, based on the code provided by 'antondorf' on the ThingWorx Developers Community."
vendor=""
packageVersion="0.1.25"
minimumThingWorxVersion="6.5.0">
<JarResources>
<FileResource type="JAR" file="ConcurrencyExtension.jar"/>
</JarResources>
</ExtensionPackage>
</ExtensionPackages>
<ThingShapes>
<ThingShape
name="wupMutexTS"
description="Provides mutex blocking capabilities to the thing that implements this ThingShape, the mutex it's based on java ReentrantLock class with a fair policy activated."
className="com.wup.wupMutexTS"
aspect.isSystemObject="false">
<avatar/>
</ThingShape>
<ThingShape
name="wupCounterTS"
description="Provides a 'autoincrement' counter with concurrency support in order to ensure atomic increases."
className="com.wup.wupCounterTS"
aspect.isSystemObject="false">
<avatar/>
</ThingShape>
</ThingShapes>
<ScriptFunctionLibraries>
<ScriptFunctionLibrary
name="ConcurrencyExtension"
description="Some tools for concurrenty, also to recover Concurrency stats as we can't create a subsystem from one extension"
className="com.wup.wupConcurrencySC">
<FunctionDefinitions>
<FunctionDefinition name="GetTotalActiveLocks_wupMutexTS" description="Returns the total active locks.">
<ResultType name="result" baseType="LONG" description="The total ammount."/>
</FunctionDefinition>
<FunctionDefinition name="GetTotalActiveWaiting_wupMutexTS" description="Returns tht total active threads which are waiting on a lock.">
<ResultType name="result" baseType="LONG" description="The total ammount."/>
</FunctionDefinition>
<FunctionDefinition name="GetTotalThingsLocksUsage_wupMutexTS" description="Returns the total number of mutex created on Things.">
<ResultType name="result" baseType="LONG" description="The total ammount."/>
</FunctionDefinition>
</FunctionDefinitions>
</ScriptFunctionLibrary>
</ScriptFunctionLibraries>
<DataShapes>
</DataShapes>
<Things>
</Things>
</Entities>
Binary file added lib/logback-classic-1.0.13.jar
Binary file not shown.
Binary file added lib/logback-core-1.0.13.jar
Binary file not shown.
Binary file added lib/slf4j-api-1.7.6.jar
Binary file not shown.
Binary file added lib/thingworx-all-6.5.0-b460.jar
Binary file not shown.
40 changes: 40 additions & 0 deletions src/com/wup/wupBaseThingShape.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.wup;

import org.slf4j.Logger;

import com.thingworx.things.Thing;
import com.thingworx.thingshape.ThingShape;
import com.thingworx.logging.LogUtilities;
import com.thingworx.webservices.context.ThreadLocalContext;

public class wupBaseThingShape extends ThingShape {

private static final long serialVersionUID = 1L;

private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(wupBaseThingShape.class);


protected Thing getMe() throws Exception {
final Object meObj = ThreadLocalContext.getMeContext();
if (meObj instanceof Thing) {
return (Thing) meObj;
} else {
this.logError("getMe() Cannot cast me to Thing.");
throw new Exception("Cannot cast me to Thing");
}
}

protected String getMeName() throws Exception {
final Thing me = this.getMe();
return me.getName();
}

protected void logError(String text) {
try {
_logger.error("[wupBaseThingShape("+this.getMeName()+")]."+text);
} catch(Exception e) {

}
}

}
Loading

0 comments on commit d87f2e2

Please sign in to comment.