-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial Version 0.1.25 already working.
- Loading branch information
0 parents
commit d87f2e2
Showing
19 changed files
with
712 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"files.exclude": { | ||
"**/.classpath": true, | ||
"**/.project": true, | ||
"**/.settings": true, | ||
"**/.factorypath": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")' & |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
|
||
} | ||
} | ||
|
||
} |
Oops, something went wrong.