├── .gitignore
├── LICENSE
├── build.gradle.kts
├── changelog.md
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jasper
├── jasper-rt
│ ├── jasper-rt.gradle.kts
│ ├── module-include.xml
│ ├── module-permissions.xml
│ ├── module.lexicon
│ ├── module.palette
│ ├── moduleTest-include.xml
│ ├── src
│ │ └── jasper
│ │ │ ├── service
│ │ │ ├── BJasperService.java
│ │ │ ├── JasperIndex.java
│ │ │ ├── JasperPoint.java
│ │ │ └── JasperSource.java
│ │ │ ├── servlet
│ │ │ ├── BJasperServlet.java
│ │ │ └── JasperServletException.java
│ │ │ └── util
│ │ │ ├── JasperUtil.java
│ │ │ ├── JsonReader.java
│ │ │ └── JsonWriter.java
│ └── srcTest
│ │ └── test
│ │ └── jasper
│ │ ├── BJasperUtilTest.java
│ │ └── BJsonTest.java
└── niagara-module.xml
├── niagara.signing.properties
├── readme.md
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | /build/
4 | /building.md
5 | /jasper/jasper-rt/build/
6 | /releases/
7 | /secrets/
8 | /niagara.signing.credentials
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Novant LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Novant. All Rights Reserved.
3 | */
4 |
5 | plugins {
6 | // Base Niagara plugin
7 | id("com.tridium.niagara")
8 |
9 | // The vendor plugin provides the vendor {} extension to set the default group
10 | // for Maven publishing; the default vendor attribute for installable
11 | // manifests; and the default module and dist version for their respective
12 | // manifests
13 | id("com.tridium.vendor")
14 |
15 | // The signing plugin configures signing of all executables, modules, and
16 | // dists. It also registers a factory only on the root project to avoid
17 | // overhead from managing signing profiles on all subprojects
18 | id("com.tridium.niagara-signing")
19 |
20 | // The niagara_home repositories convention plugin configures !bin/ext and
21 | // !modules as flat-file Maven repositories to allow modules to compile against
22 | // Niagara
23 | id("com.tridium.convention.niagara-home-repositories")
24 | }
25 |
26 |
27 | vendor {
28 | // defaultVendor sets the "vendor" attribute on module and dist files; it's
29 | // what's shown in Niagara when viewing a module or dist.
30 | defaultVendor("Novant")
31 |
32 | // defaultModuleVersion sets the "vendorVersion" attribute on all modules
33 | defaultModuleVersion("0.17")
34 | }
35 |
36 | niagaraSigning {
37 |
38 | // load secrets
39 | var sec = HashMap()
40 | val f = rootProject.file("secrets/signing.secrets")
41 | if (!f.exists()) throw Exception("ERR: signing secrets not found")
42 | f.forEachLine {
43 | if (it[0] != '#')
44 | sec[it.split("=")[0]] = it.split("=")[1]
45 | }
46 |
47 | aliases.set(listOf(sec["key_alias"]))
48 | signingProfileFile.set(
49 | project.rootProject.layout.projectDirectory.file("niagara.signing.properties")
50 | )
51 | }
52 |
53 | ////////////////////////////////////////////////////////////////
54 | // Dependencies and configurations... configuration
55 | ////////////////////////////////////////////////////////////////
56 |
57 | subprojects {
58 | repositories {
59 | mavenCentral()
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## Version 0.17 (working)
4 |
5 | ## Version 0.16 (29-Nov-2024)
6 | * Support for new `/write` endpoint
7 |
8 | ## Version 0.15 (9-Nov-2024)
9 | * Beef up `JsonWriter` support for `+/-INF`
10 |
11 | ## Version 0.14 (8-Oct-2024)
12 | * Log `reindex` action fails to LOG instead of `stderr`
13 | * Trap each `comp` lookup so whole `reindex` does not fail if error
14 |
15 | ## Version 0.13 (9-Aug-2024)
16 | * Support for new `/batch` endpoint
17 |
18 | ## Version 0.12 (6-Jun-2024)
19 | * Update to use `getDisplayName()` for source and points
20 |
21 | ## Version 0.11 (3-Jun-2024)
22 | * Update enum discovery to use `BEnumRange` API instead of manually parsing
23 |
24 | ## Version 0.10 (14-Mar-2024)
25 | * Update to use HSM code signer
26 | * Add additional `trace` level logging
27 |
28 | ## Version 0.9 (23-Aug-2023)
29 | * Fix point type assignments (again)
30 |
31 | ## Version 0.8 (22-Aug-2023)
32 | * Fix point type assignment of writable points
33 |
34 | ## Version 0.7 (22-Aug-2023)
35 | * New `/sources` endpoint with nested points design
36 | * Change point `addr` to use relative paths
37 | * Bump point lease time to `2min`
38 |
39 | ## Version 0.6 (8-Aug-2023)
40 | * Return additional debug info on errors during API requests
41 | * Fix to lease `BComponents` inside `/values` endpoint
42 | * Fix `JsonWriter` to wrap `NaN` as `"na"`
43 | * Add `status` field to `/values` endpoint
44 | * Add support for `max_points` arg on `/points` and `/values` endpoints
45 |
46 | ## Version 0.5 (13-Jul-2023)
47 | * Add support for `path_prefix` arg on `/points` and `/values` endpoints
48 | * Unescape URL-encoding for point `name` and `path` on `/points` endpoint
49 |
50 | ## Version 0.4 (3-May-2023)
51 | * Set min build target to `4.10` for better compatibility
52 | * Remove `size` props to match updated Jasper spec
53 |
54 | ## Version 0.3 (1-Mar-2023)
55 | * Add support for publicly-signed module signing
56 |
57 | ## Version 0.2 (20-Jan-2023)
58 | * Fix `JaspserPoint.id` -> `addr` to match Jasper spec
59 |
60 | ## Version 0.1 (20-Jan-2023)
61 | * Initial release
62 | * Unsigned module
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Uncomment and set to the path of the Gradle plugins if they are not in niagara_home
2 | gradlePluginHome=C:\\niagara\\Niagara_4_Developer_Tools-4.14\\gradlePlugins
3 |
4 | # The path to the installation of Niagara you are building against
5 | # NOTE: JRE below must also be updated
6 | # 4.7 -> 4.7.110.32
7 | # 4.8 -> 4.8.0.110
8 | # 4.10 -> 4.10.0.154
9 | # 4.11 -> 4.11.0.142
10 | # 4.12 -> 4.12.0.156
11 | # 4.13 -> 4.13.0.186
12 | niagara_home=C:\\niagara\\Niagara-4.10.0.154
13 |
14 | # The path to niagara_user_home for the version of Niagara you are building against
15 | niagara_user_home=C:\\Users\\andy\\Niagara4.10\\tridium
16 |
17 | #Uncomment and set to the path of your node install if you are building JavaScript modules
18 | #nodeHome=
19 |
20 | #AUTOMATICALLY GENERATED BY GRADLE -- DO NOT MODIFY
21 | org.gradle.java.installations.auto-detect=false
22 | #AUTOMATICALLY GENERATED BY GRADLE -- DO NOT MODIFY
23 | org.gradle.java.installations.auto-download=false
24 | #AUTOMATICALLY GENERATED BY GRADLE -- DO NOT MODIFY
25 | org.gradle.java.installations.paths=C:\\niagara\\Niagara-4.10.0.154\\jre
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/novant-io/jasper-n4/c624f6245c907e33fd1ace39bef59a7a49e48ea3/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/jasper-rt.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Novant. All Rights Reserved.
3 | */
4 |
5 | import com.tridium.gradle.plugins.module.util.ModulePart.RuntimeProfile.*
6 |
7 | plugins {
8 | // The Niagara Module plugin configures the "moduleManifest" extension and the
9 | // "jar" and "moduleTestJar" tasks.
10 | id("com.tridium.niagara-module")
11 |
12 | // The signing plugin configures the correct signing of modules. It requires
13 | // that the plugin also be applied to the root project.
14 | id("com.tridium.niagara-signing")
15 |
16 | // The bajadoc plugin configures the generation of Bajadoc for a module.
17 | // id("com.tridium.bajadoc")
18 |
19 | // Configures JaCoCo for the "niagaraTest" task of this module.
20 | id("com.tridium.niagara-jacoco")
21 |
22 | // The Annotation processors plugin adds default dependencies on "Tridium:nre"
23 | // for the "annotationProcessor" and "moduleTestAnnotationProcessor"
24 | // configurations by creating a single "niagaraAnnotationProcessor"
25 | // configuration they extend from. This value can be overridden by explicitly
26 | // declaring a dependency for the "niagaraAnnotationProcessor" configuration.
27 | id("com.tridium.niagara-annotation-processors")
28 |
29 | // The niagara_home repositories convention plugin configures !bin/ext and
30 | // !modules as flat-file Maven repositories so that projects in this build can
31 | // depend on already-installed Niagara modules.
32 | id("com.tridium.convention.niagara-home-repositories")
33 | }
34 |
35 | description = "Jasper API for N4"
36 |
37 | moduleManifest {
38 | moduleName.set("jasper")
39 | runtimeProfile.set(rt)
40 | }
41 |
42 | // See documentation at module://docDeveloper/doc/build.html#dependencies
43 | // for the supported dependency types
44 | dependencies {
45 | // NRE dependencies
46 | nre("Tridium:nre")
47 |
48 | // Niagara module dependencies
49 | api("Tridium:baja")
50 | api("Tridium:control-rt")
51 | api("Tridium:web-rt")
52 |
53 | uberjar("javax.servlet:javax.servlet-api:3.0.1")
54 |
55 | // Test Niagara module dependencies
56 | moduleTestImplementation("Tridium:test-wb")
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/module-include.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/module-permissions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/module.lexicon:
--------------------------------------------------------------------------------
1 | #
2 | # Lexicon for the jasper module.
3 | #
4 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/module.palette:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/moduleTest-include.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/service/BJasperService.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 19 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.service;
10 |
11 |
12 | import javax.baja.log.*;
13 | import javax.baja.sys.*;
14 | import javax.baja.control.*;
15 | import javax.baja.registry.*;
16 | import jasper.servlet.*;
17 | import jasper.util.*;
18 |
19 | /**
20 | * JasperService.
21 | */
22 | public final class BJasperService extends BAbstractService
23 | {
24 | /*-
25 | class BJasperService
26 | {
27 | properties
28 | {
29 | servlet: BJasperServlet
30 | default{[ new BJasperServlet() ]}
31 |
32 | allowWrite: boolean
33 | default {[ false ]}
34 | }
35 |
36 | actions
37 | {
38 | rebuildIndex()
39 | }
40 | }
41 | -*/
42 | /*+ ------------ BEGIN BAJA AUTO GENERATED CODE ------------ +*/
43 | /*@ $jasper.service.BJasperService(4038923251)1.0$ @*/
44 | /* Generated Wed Apr 21 21:32:17 EDT 2021 by Slot-o-Matic 2000 (c) Tridium, Inc. 2000 */
45 |
46 | ////////////////////////////////////////////////////////////////
47 | // Property "servlet"
48 | ////////////////////////////////////////////////////////////////
49 |
50 | /**
51 | * Slot for the servlet
property.
52 | * @see jasper.service.BJasperService#getServlet
53 | * @see jasper.service.BJasperService#setServlet
54 | */
55 | public static final Property servlet = newProperty(0, new BJasperServlet(),null);
56 |
57 | /**
58 | * Get the servlet
property.
59 | * @see jasper.service.BJasperService#servlet
60 | */
61 | public BJasperServlet getServlet() { return (BJasperServlet)get(servlet); }
62 |
63 | /**
64 | * Set the servlet
property.
65 | * @see jasper.service.BJasperService#servlet
66 | */
67 | public void setServlet(BJasperServlet v) { set(servlet,v,null); }
68 |
69 | ////////////////////////////////////////////////////////////////
70 | // Property "allowWrite"
71 | ////////////////////////////////////////////////////////////////
72 |
73 | /**
74 | * Slot for the allowWrite
property.
75 | * @see jasper.service.BJasperService#getAllowWrite
76 | * @see jasper.service.BJasperService#setAllowWrite
77 | */
78 | public static final Property allowWrite = newProperty(0, false, null);
79 |
80 | /**
81 | * Get the allowWrite
property.
82 | * @see jasper.service.BJasperService#allowWrite
83 | */
84 | public boolean getAllowWrite() { return getBoolean(allowWrite); }
85 |
86 | /**
87 | * Set the allowWrite
property.
88 | * @see jasper.service.BJasperService#allowWrite
89 | */
90 | public void setAllowWrite(boolean v) { setBoolean(allowWrite, v, null); }
91 |
92 | ////////////////////////////////////////////////////////////////
93 | // Action "rebuildIndex"
94 | ////////////////////////////////////////////////////////////////
95 |
96 | /**
97 | * Slot for the rebuildIndex
action.
98 | * @see jasper.service.BJasperService#rebuildIndex()
99 | */
100 | public static final Action rebuildIndex = newAction(0,null);
101 |
102 | /**
103 | * Invoke the rebuildIndex
action.
104 | * @see jasper.service.BJasperService#rebuildIndex
105 | */
106 | public void rebuildIndex() { invoke(rebuildIndex,null,null); }
107 |
108 | ////////////////////////////////////////////////////////////////
109 | // Type
110 | ////////////////////////////////////////////////////////////////
111 |
112 | public Type getType() { return TYPE; }
113 | public static final Type TYPE = Sys.loadType(BJasperService.class);
114 |
115 | /*+ ------------ END BAJA AUTO GENERATED CODE -------------- +*/
116 |
117 | ////////////////////////////////////////////////////////////////
118 | // BAbstractService
119 | ////////////////////////////////////////////////////////////////
120 |
121 | public Type[] getServiceTypes()
122 | {
123 | Type[] t = { getType() };
124 | return t;
125 | }
126 |
127 | public void serviceStarted() throws Exception
128 | {
129 | }
130 |
131 | public void atSteadyState()
132 | {
133 | doRebuildIndex();
134 | LOG.message("JasperService ready [version=" + moduleVer() + "]");
135 | }
136 |
137 | public void doRebuildIndex()
138 | {
139 | try
140 | {
141 | // start
142 | LOG.message("JasperReindexJob started...");
143 | BAbsTime t1 = BAbsTime.now();
144 | int numPoints = 0;
145 |
146 | // clear index
147 | index.clear();
148 |
149 | // scan station for points
150 | BStation station = Sys.getStation();
151 | BComponent[] comps = station.getComponentSpace().getAllComponents();
152 | for (int i=0; i getSources()
42 | {
43 | return smap.values();
44 | }
45 |
46 | ////////////////////////////////////////////////////////////////
47 | // Package-Private
48 | ////////////////////////////////////////////////////////////////
49 |
50 | /** Clear all entries from index. */
51 | void clear()
52 | {
53 | smap.clear();
54 | }
55 |
56 | /** Add a new source to index. */
57 | void addSource(JasperSource s)
58 | {
59 | smap.put(s.id, s);
60 | }
61 |
62 | ////////////////////////////////////////////////////////////////
63 | // Attributes
64 | ////////////////////////////////////////////////////////////////
65 |
66 | private HashMap smap = new HashMap(); // source.id : JasperSource
67 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/service/JasperPoint.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 19 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.service;
10 |
11 | import java.util.*;
12 | import javax.baja.sys.*;
13 |
14 | /**
15 | * JasperPoint.
16 | */
17 | public final class JasperPoint
18 | {
19 | /** Constructor */
20 | public JasperPoint(String addr, String name, String enums, String unit)
21 | {
22 | this.addr = addr;
23 | this.name = name;
24 | this.enums = enums;
25 | this.unit = unit;
26 | }
27 |
28 | /** Address for this point under parent source. */
29 | public final String addr;
30 |
31 | /** Name of this point. */
32 | public final String name;
33 |
34 | /** Enum ordinal names for this point or null if not defined. */
35 | public final String enums;
36 |
37 | /** Unit for this point or null if not defined. */
38 | public final String unit;
39 |
40 | public String toString() { return name; }
41 |
42 | // TODO FIXIT: cleanup encapsulation here (and JasperSource)
43 | // package private: backing comp
44 | //BComponent comp;
45 | public BComponent comp;
46 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/service/JasperSource.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 21 Aug 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.service;
10 |
11 | import java.util.*;
12 | import javax.baja.sys.*;
13 |
14 | /**
15 | * JasperSource.
16 | */
17 | public final class JasperSource
18 | {
19 | /** Constructor */
20 | public JasperSource(String id, String name, String path)
21 | {
22 | this.id = id;
23 | this.name = name;
24 | this.path = path;
25 | }
26 |
27 | /** Unique id for this source. */
28 | public final String id;
29 |
30 | /** Name of this source. */
31 | public final String name;
32 |
33 | /** Path of this source. */
34 | public final String path;
35 |
36 | /** Get point for given addr or null if not found. */
37 | public JasperPoint getPoint(String addr) { return (JasperPoint)pmap.get(addr); }
38 |
39 | /** Get point list for this source. */
40 | public Collection getPoints() { return pmap.values(); }
41 |
42 | public String toString() { return name; }
43 |
44 | // framework use only
45 | public String slotPath() { return comp.getSlotPath().toString(); }
46 |
47 | // package private: backing comp
48 | BComponent comp;
49 |
50 | // package private
51 | void addPoint(JasperPoint p) { pmap.put(p.addr, p); }
52 |
53 | private HashMap pmap = new HashMap();
54 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/servlet/BJasperServlet.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 19 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.servlet;
10 |
11 | import java.io.*;
12 | import java.util.*;
13 | import javax.baja.control.*;
14 | import javax.baja.io.*;
15 | import javax.baja.naming.*;
16 | import javax.baja.status.*;
17 | import javax.baja.sys.*;
18 | import javax.baja.util.*;
19 | import javax.baja.web.*;
20 | import javax.servlet.http.*;
21 | import jasper.service.*;
22 | import jasper.util.*;
23 |
24 | /**
25 | * BJasperServlet
26 | */
27 | public final class BJasperServlet extends BWebServlet
28 | {
29 | /*-
30 | class BJasperServlet
31 | {
32 | }
33 | -*/
34 | /*+ ------------ BEGIN BAJA AUTO GENERATED CODE ------------ +*/
35 | /*@ $jasper.servlet.BJasperServlet(2852803851)1.0$ @*/
36 | /* Generated Wed Apr 21 15:57:23 EDT 2021 by Slot-o-Matic 2000 (c) Tridium, Inc. 2000 */
37 |
38 | ////////////////////////////////////////////////////////////////
39 | // Type
40 | ////////////////////////////////////////////////////////////////
41 |
42 | public Type getType() { return TYPE; }
43 | public static final Type TYPE = Sys.loadType(BJasperServlet.class);
44 |
45 | /*+ ------------ END BAJA AUTO GENERATED CODE -------------- +*/
46 |
47 | ////////////////////////////////////////////////////////////////
48 | // Constructor
49 | ////////////////////////////////////////////////////////////////
50 |
51 | /** Constructor. */
52 | public BJasperServlet()
53 | {
54 | super.setServletName("jasper");
55 | super.setFlags(getSlot("servletName"), Flags.READONLY | Flags.SUMMARY);
56 | }
57 |
58 | /** Set backing index */
59 | public void setIndex(JasperIndex index) { this.index = index; }
60 |
61 | ////////////////////////////////////////////////////////////////
62 | // Servlet
63 | ////////////////////////////////////////////////////////////////
64 |
65 | public void doPost(WebOp op) throws IOException
66 | {
67 | try
68 | {
69 | // NOTE: getPathInfo removes 'jasper' prefix from path already
70 | HttpServletRequest req = op.getRequest();
71 | String[] path = JasperUtil.splitPath(req.getPathInfo());
72 |
73 | // sanity check path is long enough
74 | if (path.length < 2)
75 | {
76 | JasperUtil.sendNotFound(op);
77 | return;
78 | }
79 |
80 | // key off version
81 | if (path[0].equals("v1"))
82 | {
83 | if (path[1].equals("about"))
84 | {
85 | JsonWriter w = startRes(op);
86 | doAbout(w);
87 | endRes(w);
88 | return;
89 | }
90 | if (path[1].equals("sources"))
91 | {
92 | JsonWriter w = startRes(op);
93 | doSources(w);
94 | endRes(w);
95 | return;
96 | }
97 | if (path[1].equals("points"))
98 | {
99 | JsonWriter w = startRes(op);
100 | doPoints(getFormParams(req), w);
101 | endRes(w);
102 | return;
103 | }
104 | if (path[1].equals("values"))
105 | {
106 | JsonWriter w = startRes(op);
107 | doValues(getFormParams(req), w);
108 | endRes(w);
109 | return;
110 | }
111 | if (path[1].equals("write"))
112 | {
113 | JsonWriter w = startRes(op);
114 | doWrite(getFormParams(req), w);
115 | endRes(w);
116 | return;
117 | }
118 | if (path[1].equals("batch"))
119 | {
120 | // parse input
121 | HashMap args = (HashMap)(new JsonReader(req.getInputStream()).readVal());
122 |
123 | // service
124 | JsonWriter w = startRes(op);
125 | doBatch(args, w);
126 | endRes(w);
127 | return;
128 | }
129 | }
130 |
131 | // if we get here then 404
132 | JasperUtil.sendNotFound(op);
133 | }
134 | catch (JasperServletException jse)
135 | {
136 | jse.printStackTrace();
137 | JasperUtil.sendErr(op, jse.errCode, jse.getMessage(), jse);
138 | }
139 | catch (Exception ex)
140 | {
141 | ex.printStackTrace();
142 | JasperUtil.sendErr(op, 500, "Unexpected error", ex);
143 | }
144 | }
145 |
146 | private HashMap getFormParams(HttpServletRequest req)
147 | {
148 | HashMap map = new HashMap();
149 | Enumeration e = req.getParameterNames();
150 | while (e.hasMoreElements())
151 | {
152 | String key = (String)e.nextElement();
153 | String val = req.getParameter(key);
154 | map.put(key, val);
155 | }
156 | return map;
157 | }
158 |
159 | private JsonWriter startRes(WebOp op) throws IOException
160 | {
161 | HttpServletResponse res = op.getResponse();
162 | res.setStatus(200);
163 | res.setHeader("Content-Type", "application/json");
164 |
165 | JsonWriter json = new JsonWriter(res.getOutputStream());
166 | return json;
167 | }
168 |
169 | private void endRes(JsonWriter json) throws IOException
170 | {
171 | json.flush().close();
172 | }
173 |
174 | ////////////////////////////////////////////////////////////////
175 | // Endpoint /about
176 | ////////////////////////////////////////////////////////////////
177 |
178 | /** Service /v1/about request. */
179 | private void doAbout(JsonWriter json) throws IOException
180 | {
181 | // TODO: watch trailing comma bugs; should JsonWriter handle internally?
182 | json.write('{');
183 |
184 | // required fields
185 | json.writeKey("name").writeVal(Sys.getStation().getStationName()).write(',');
186 | json.writeKey("vendor").writeVal("Tridium").write(',');
187 | json.writeKey("model").writeVal("Niagara 4").write(',');
188 | json.writeKey("version").writeVal(BComponent.TYPE.getVendorVersion().toString()).write(',');
189 |
190 | // additional fields
191 | BModule module = BJasperService.TYPE.getModule();
192 | json.writeKey("moduleName").writeVal(module.getModuleName()).write(',');
193 | json.writeKey("moduleVersion").writeVal(BJasperService.TYPE.getVendorVersion().toString());
194 |
195 | json.write('}');
196 | }
197 |
198 | ////////////////////////////////////////////////////////////////
199 | // Endpoint /sources
200 | ////////////////////////////////////////////////////////////////
201 |
202 | /** Service /v1/sources request. */
203 | private void doSources(JsonWriter json) throws IOException
204 | {
205 | // response
206 | json.write('{');
207 | json.writeKey("sources").write('[');
208 |
209 | Iterator iter = index.getSources().iterator();
210 | int num = 0;
211 |
212 | while (iter.hasNext())
213 | {
214 | JasperSource s = iter.next();
215 |
216 | // prefix trailing commas
217 | if (num > 0) json.write(',');
218 |
219 | json.write('{');
220 | json.writeKey("id").writeVal(s.id).write(',');
221 | json.writeKey("name").writeVal(s.name).write(',');
222 | json.writeKey("path").writeVal(s.path);
223 | json.write('}');
224 | num++;
225 | }
226 | json.write(']');
227 | json.write('}');
228 | }
229 |
230 | ////////////////////////////////////////////////////////////////
231 | // Endpoint /points
232 | ////////////////////////////////////////////////////////////////
233 |
234 | /** Service /v1/points request. */
235 | private void doPoints(HashMap params, JsonWriter json) throws IOException
236 | {
237 | // request args
238 | String sourceId = reqArgStr(params, "source_id");
239 | JasperSource source = index.getSource(sourceId);
240 | if (source == null) throw new JasperServletException(404, "Source not found");
241 |
242 | // response
243 | json.write('{');
244 | json.writeKey("points").write('[');
245 | Iterator iter = source.getPoints().iterator();
246 | int num = 0;
247 | while (iter.hasNext())
248 | {
249 | JasperPoint p = iter.next();
250 |
251 | // prefix trailing commas
252 | if (num > 0) json.write(',');
253 |
254 | json.write('{');
255 | json.writeKey("addr").writeVal(p.addr).write(',');
256 | json.writeKey("name").writeVal(p.name);
257 | if (p.enums != null)
258 | {
259 | json.write(',');
260 | json.writeKey("enum").writeVal(p.enums);
261 | }
262 | if (p.unit != null)
263 | {
264 | json.write(',');
265 | json.writeKey("unit").writeVal(p.unit);
266 | }
267 | json.write('}');
268 | num++;
269 | }
270 | json.write(']');
271 | json.write('}');
272 | }
273 |
274 | ////////////////////////////////////////////////////////////////
275 | // Endpoint /values
276 | ////////////////////////////////////////////////////////////////
277 |
278 | /** Service /v1/values request. */
279 | private void doValues(HashMap params, JsonWriter json) throws IOException
280 | {
281 | BJasperService service = (BJasperService)this.getParent();
282 |
283 | // request args
284 | String sourceId = reqArgStr(params, "source_id");
285 | JasperSource source = index.getSource(sourceId);
286 | if (source == null) throw new JasperServletException(404, "Source not found");
287 |
288 | // response
289 | json.write('{');
290 | json.writeKey("values").write('[');
291 | Iterator iter = source.getPoints().iterator();
292 | int num = 0;
293 | while (iter.hasNext())
294 | {
295 | JasperPoint p = iter.next();
296 | Object val = null;
297 | String status = "unknown";
298 |
299 | // bump lease time
300 | p.comp.lease(1, leaseTime);
301 |
302 | // get point value
303 | BStatusValue pv = JasperUtil.getPointValue(p.comp);
304 | if (pv != null)
305 | {
306 | val = pv.getStatus().isValid() ? pv : "na";
307 | status = pv.getStatus().flagsToString(null);
308 | }
309 |
310 | // prefix trailing commas
311 | if (num > 0) json.write(',');
312 |
313 | json.write('{');
314 | json.writeKey("addr").writeVal(p.addr).write(',');
315 | json.writeKey("val").writeVal(val).write(',');
316 | json.writeKey("status").writeVal(status);
317 | json.write('}');
318 | num++;
319 | }
320 | json.write(']');
321 | json.write('}');
322 | }
323 |
324 | ////////////////////////////////////////////////////////////////
325 | // Endpoint /write
326 | ////////////////////////////////////////////////////////////////
327 |
328 | /** Service /v1/write request. */
329 | private void doWrite(HashMap params, JsonWriter json) throws IOException
330 | {
331 | BJasperService service = (BJasperService)this.getParent();
332 |
333 | // verify writable
334 | if (service.getAllowWrite() == false)
335 | throw new JasperServletException(403, "Write operations are not permitted");
336 |
337 | // request args
338 | String sourceId = reqArgStr(params, "source_id");
339 | String paddr = reqArgStr(params, "point_addr");
340 | String sval = reqArgStr(params, "val");
341 | String slevel = optArgStr(params, "level", "16");
342 |
343 | // verify args
344 | JasperSource source = index.getSource(sourceId);
345 | if (source == null) throw new JasperServletException(404, "Source not found");
346 | JasperPoint point = source.getPoint(paddr);
347 | if (point == null) throw new JasperServletException(404, "Point not found");
348 | int level = Integer.parseInt(slevel);
349 |
350 | // verify control point
351 | boolean isControlPoint = point.comp instanceof BControlPoint;
352 | if (!isControlPoint) throw new JasperServletException(403, "Point is not writable");
353 |
354 | // verify writable
355 | BControlPoint cpoint = (BControlPoint)point.comp;
356 | if (!cpoint.isWritablePoint()) throw new JasperServletException(403, "Point is not writable");
357 |
358 | // write point
359 | Double dval = sval.equals("null") ? null : Double.parseDouble(sval);
360 | JasperUtil.setPointValue(cpoint, dval, level);
361 |
362 | // response
363 | json.write('{');
364 | json.writeKey("status").writeVal("ok");
365 | json.write('}');
366 | }
367 |
368 | ////////////////////////////////////////////////////////////////
369 | // Endpoint /batch
370 | ////////////////////////////////////////////////////////////////
371 |
372 | /** Service /v1/batch request. */
373 | private void doBatch(HashMap args, JsonWriter json) throws IOException
374 | {
375 | // response
376 | json.write('{');
377 | json.writeKey("results").write('[');
378 |
379 | ArrayList ops = (ArrayList)args.get("ops");
380 | for (int i=0; i 0) json.write(',');
383 |
384 | HashMap r = (HashMap)ops.get(i);
385 | String op = (String)r.get("op");
386 | if (op.equals("about")) { doAbout(json); continue; }
387 | if (op.equals("sources")) { doSources(json); continue; }
388 | if (op.equals("points")) { doPoints(r, json); continue; }
389 | if (op.equals("values")) { doValues(r, json); continue; }
390 | throw new JasperServletException(400, "Invalid op '" + op + "'");
391 | }
392 |
393 | json.write(']');
394 | json.write('}');
395 | }
396 |
397 | ////////////////////////////////////////////////////////////////
398 | // Support
399 | ////////////////////////////////////////////////////////////////
400 |
401 | /** Get HTTP request argument as 'String' or return 'defVal' if not found. */
402 | private String reqArgStr(HashMap params, String name)
403 | {
404 | String val = (String)params.get(name);
405 | if (val == null) throw new JasperServletException(400, "Missing required '" + name + "' param");
406 | return val;
407 | }
408 |
409 | /** Get HTTP request argument as 'int' or return 'defVal' if not found. */
410 | private int reqArgInt(HashMap params, String name)
411 | {
412 | String val = reqArgStr(params, name);
413 | return Integer.parseInt(val);
414 | }
415 |
416 | /** Get HTTP request argument as 'String' or return 'defVal' if not found. */
417 | private String optArgStr(HashMap params, String name, String defVal)
418 | {
419 | String val = (String)params.get(name);
420 | if (val == null) val = defVal;
421 | return val;
422 | }
423 |
424 | ////////////////////////////////////////////////////////////////
425 | // Attributes
426 | ////////////////////////////////////////////////////////////////
427 |
428 | private JasperIndex index;
429 | private final long leaseTime = 120000; // 2min in millis
430 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/servlet/JasperServletException.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2024, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 9 Aug 2024 Andy Frank Creation
7 | //
8 |
9 | package jasper.servlet;
10 |
11 | /**
12 | * JasperServletException
13 | */
14 | public final class JasperServletException extends RuntimeException
15 | {
16 | /**
17 | * Construct a new servlet exception with given error status
18 | * code and error message.
19 | */
20 | public JasperServletException(int errCode, String errMessage)
21 | {
22 | super(errMessage);
23 | this.errCode = errCode;
24 | }
25 |
26 | /**
27 | * HTTP error status code for this exception.
28 | */
29 | final int errCode;
30 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/util/JasperUtil.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 19 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.util;
10 |
11 | import java.io.*;
12 | import javax.baja.io.*;
13 | import javax.baja.control.*;
14 | import javax.baja.control.enums.*;
15 | import javax.baja.naming.*;
16 | import javax.baja.registry.*;
17 | import javax.baja.status.*;
18 | import javax.baja.sys.*;
19 | import javax.baja.util.*;
20 | import javax.baja.web.*;
21 | import javax.servlet.http.*;
22 | import jasper.service.*;
23 |
24 | /**
25 | * JasperUtil
26 | */
27 | public final class JasperUtil
28 | {
29 |
30 | ////////////////////////////////////////////////////////////////
31 | // BComponent
32 | ////////////////////////////////////////////////////////////////
33 |
34 | /**
35 | * Conveneince to check object type.
36 | * */
37 | public static boolean isType(BObject obj, String type)
38 | {
39 | TypeInfo info = Sys.getRegistry().getType(type);
40 | return obj.getType().getTypeInfo().is(info);
41 | }
42 |
43 | /**
44 | * Get the Jasper point id for given point.
45 | */
46 | public static String getSourceId(BComponent c)
47 | {
48 | // strip h: from handle ord
49 | String handle = c.getHandleOrd().toString();
50 | String suffix = handle.substring(2);
51 | return suffix;
52 | }
53 |
54 | /**
55 | * Get the Jasper point addr for given point.
56 | */
57 | public static String getPointAddr(JasperSource source, BComponent c)
58 | {
59 | // get relative slot path from parent source
60 | String sslot = source.slotPath();
61 | String pslot = c.getSlotPath().toString();
62 | String suffix = pslot.substring(sslot.length() + 1);
63 |
64 | // cleanup slotpath suffix
65 | suffix = JasperUtil.slotPathToSuffix(suffix);
66 |
67 | // point type
68 | if (isType(c, "control:NumericWritable")) return "av." + suffix;
69 | if (isType(c, "control:NumericPoint")) return "ai." + suffix;
70 | if (isType(c, "control:BooleanWritable")) return "bv." + suffix;
71 | if (isType(c, "control:BooleanPoint")) return "bi." + suffix;
72 | if (isType(c, "control:EnumWritable")) return "ev." + suffix;
73 | if (isType(c, "control:EnumPoint")) return "ei." + suffix;
74 |
75 | // unsupported type
76 | throw new RuntimeException("Unsupported point type '" + c.getDisplayName(null) + "' [" + c.getSlotPath() + "]");
77 | }
78 |
79 | /**
80 | * Get handle Ord from component point id.
81 | */
82 | public static BOrd getOrdFromId(String id)
83 | {
84 | String handle = "h:" + id.substring(3);
85 | return BOrd.make(handle);
86 | }
87 |
88 | /**
89 | * Unescape component slot path.
90 | */
91 | public static String unescapeSlotPath(String orig)
92 | {
93 | StringBuffer buf = new StringBuffer();
94 | StringBuffer temp = new StringBuffer();
95 |
96 | for (int i=0; i= range.getOrdinals().length)
193 | throw new IllegalArgumentException("Ordinal out of range: " + ord);
194 | BEnum enm = range.get(ord);
195 | se.setValue(enm);
196 | se.setStatus(BStatus.ok);
197 | }
198 | ew.set("in" + level, se);
199 | }
200 | else
201 | {
202 | throw new IllegalArgumentException("Unsupported writable point: " + point.getSlotPath() + " [" + point.getClass() + "]");
203 | }
204 | }
205 |
206 | /**
207 | * Parse a BFacet range value into a Jaspser compatible
208 | * string or 'null' if string is empty.
209 | */
210 | public static String parseEnumRange(BEnumRange range)
211 | {
212 | // short-circuit if empty range
213 | if (range == null || range.isNull()) return null;
214 |
215 | // TODO: for now assume zero-based and ordered
216 | // {alpha=0,beta=1,gamma=2} -> alpha,beta,gamma
217 |
218 | StringBuffer buf = new StringBuffer();
219 | int[] ords = range.getOrdinals();
220 | for (int i=0; i 0) buf.append(',');
223 | String tag = range.get(ords[i]).getTag();
224 | buf.append(unescapeSlotPath(tag));
225 | }
226 |
227 | // sanity check
228 | if (buf.length() == 0) return null;
229 | return buf.toString();
230 | }
231 |
232 | ////////////////////////////////////////////////////////////////
233 | // Servlet
234 | ////////////////////////////////////////////////////////////////
235 |
236 | /** Convenience for sendErr(404) */
237 | public static void sendNotFound(WebOp op) throws IOException
238 | {
239 | sendErr(op, 404, "Not Found");
240 | }
241 |
242 | /** Send an error repsponse as JSON with code and msg. */
243 | public static void sendErr(WebOp op, int code, String msg) throws IOException
244 | {
245 | sendErr(op, code, msg, null);
246 | }
247 |
248 | /** Send an error repsponse as JSON with code and msg. */
249 | public static void sendErr(WebOp op, int code, String msg, Exception cause) throws IOException
250 | {
251 | HttpServletResponse res = op.getResponse();
252 | res.setStatus(code);
253 | res.setHeader("Content-Type", "application/json");
254 |
255 | JsonWriter json = new JsonWriter(res.getOutputStream());
256 | json.write('{');
257 | json.writeKey("err_msg").writeVal(msg);
258 | if (cause != null)
259 | {
260 | json.write(',');
261 | json.writeKey("err_trace");
262 | json.writeVal(printStackTraceToString(cause));
263 | }
264 | json.write('}');
265 | json.flush().close();
266 | }
267 |
268 | /** Read content from request. */
269 | public static String readContent(WebOp op) throws IOException
270 | {
271 | StringBuffer sb = new StringBuffer();
272 | InputStream in = new BufferedInputStream(op.getRequest().getInputStream());
273 | BufferedReader br = new BufferedReader(new InputStreamReader(in));
274 | String line = null;
275 | while ((line = br.readLine()) != null)
276 | {
277 | sb.append(line);
278 | }
279 | return sb.toString();
280 | }
281 |
282 | ////////////////////////////////////////////////////////////////
283 | // URI
284 | ////////////////////////////////////////////////////////////////
285 |
286 | /** Split a path string into array. */
287 | public static String[] splitPath(String path)
288 | {
289 | String[] orig = path.split("/");
290 |
291 | // get non-empty size
292 | int size = 0;
293 | for (int i=0; i 0) size++;
295 |
296 | // filter out empty
297 | String[] acc = new String[size];
298 | int p = 0;
299 | for (int i=0; i 0) acc[p++] = orig[i];
301 | return acc;
302 | }
303 |
304 | ////////////////////////////////////////////////////////////////
305 | // Exceptions
306 | ////////////////////////////////////////////////////////////////
307 |
308 | /** Print stack trace to string. */
309 | public static String printStackTraceToString(Exception ex)
310 | {
311 | StringWriter sw = new StringWriter();
312 | PrintWriter pw = new PrintWriter(sw);
313 | ex.printStackTrace(pw);
314 | return sw.toString();
315 | }
316 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/util/JsonReader.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 19 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.util;
10 |
11 | import java.io.*;
12 | import java.util.*;
13 |
14 | /**
15 | * JsonReader.
16 | */
17 | public final class JsonReader
18 | {
19 | /** Constructor. */
20 | public JsonReader(InputStream in) throws IOException
21 | {
22 | this.in = new BufferedReader(new InputStreamReader(in, "UTF-8"));
23 | }
24 |
25 | /** Close underlying input stream. */
26 | public JsonReader close() throws IOException
27 | {
28 | in.close();
29 | return this;
30 | }
31 |
32 | /** Parse Json and return HashMap instance. */
33 | public Object readVal() throws IOException
34 | {
35 | // check if we need to init reader
36 | if (!init)
37 | {
38 | init = true;
39 | this.cur = -1;
40 | this.peek = in.read();
41 | }
42 |
43 | // eat leading whitespace
44 | eatWhitespace();
45 |
46 | // bool
47 | if (peek == 't') return readBool();
48 | if (peek == 'f') return readBool();
49 |
50 | // num
51 | if (peek == '-') return readNum();
52 | if (Character.isDigit(peek)) return readNum();
53 |
54 | // str
55 | if (peek == '\"') return readStr();
56 |
57 | // map
58 | if (peek == '{') return readMap();
59 |
60 | // list
61 | if (peek == '[') return readList();
62 |
63 | throw unexpectedChar(peek);
64 | }
65 |
66 | /** Read a 'true' or 'false value. */
67 | private Boolean readBool() throws IOException
68 | {
69 | if (peek == 't')
70 | {
71 | read('t');
72 | read('r');
73 | read('u');
74 | read('e');
75 | return Boolean.TRUE;
76 | }
77 | else
78 | {
79 | read('f');
80 | read('a');
81 | read('l');
82 | read('s');
83 | read('e');
84 | return Boolean.FALSE;
85 | }
86 | }
87 |
88 | /** Read a Number value. */
89 | private Double readNum() throws IOException
90 | {
91 | StringBuffer buf = new StringBuffer();
92 | if (peek == '-') buf.append((char)read());
93 | while (Character.isDigit(peek) || peek == '.' || peek == 'E' || peek == '-')
94 | buf.append((char)read());
95 | return Double.parseDouble(buf.toString());
96 | }
97 |
98 | /** Read a Str value. */
99 | private String readStr() throws IOException
100 | {
101 | StringBuffer buf = new StringBuffer();
102 | read('\"');
103 | while (peek != '\"')
104 | {
105 | if (peek == '\\')
106 | {
107 | int p = read();
108 | if (peek == 'u')
109 | {
110 | read();
111 | int n3 = hex(read());
112 | int n2 = hex(read());
113 | int n1 = hex(read());
114 | int n0 = hex(read());
115 | int uc = ((n3 << 12) | (n2 << 8) | (n1 << 4) | n0);
116 | buf.append((char)uc);
117 | }
118 | else
119 | {
120 | // TODO FIXIT!
121 | buf.append((char)p);
122 | buf.append((char)read());
123 | }
124 | }
125 | else
126 | {
127 | buf.append((char)read());
128 | }
129 | }
130 | read('\"');
131 | return buf.toString();
132 | }
133 |
134 | /** Read a ArrayList value. */
135 | private ArrayList readList() throws IOException
136 | {
137 | ArrayList list = new ArrayList();
138 | read('[');
139 | while (peek != ']')
140 | {
141 | // add key:value pair
142 | eatWhitespace();
143 | Object val = readVal();
144 | list.add(val);
145 |
146 | // verify next char is valid
147 | eatWhitespace();
148 | if (peek == ',') { read(); continue; }
149 | if (peek == ']') continue;
150 | throw unexpectedChar(peek);
151 | }
152 | read(']');
153 | return list;
154 | }
155 |
156 | /** Read a HashMap value. */
157 | private HashMap readMap() throws IOException
158 | {
159 | HashMap map = new HashMap();
160 | read('{');
161 | while (peek != '}')
162 | {
163 | // add key:value pair
164 | eatWhitespace();
165 | String key = readStr();
166 | eatWhitespace();
167 | read(':');
168 | eatWhitespace();
169 | Object val = readVal();
170 | map.put(key, val);
171 |
172 | // verify next char is valid
173 | eatWhitespace();
174 | if (peek == ',') { read(); continue; }
175 | if (peek == '}') continue;
176 | throw unexpectedChar(peek);
177 | }
178 | read('}');
179 | return map;
180 | }
181 |
182 | /** Read the next char from stream. */
183 | private int read() throws IOException
184 | {
185 | cur = peek;
186 | peek = in.read();
187 | pos++;
188 | return cur;
189 | }
190 |
191 | /** Read the next char from stream and validate it matches expected. */
192 | private int read(int expected) throws IOException
193 | {
194 | int ch = read();
195 | if (ch < 0) throw new IOException("Unexpected EOS");
196 | if (ch != expected) throw unexpectedChar(ch);
197 | return ch;
198 | }
199 |
200 | /** Eat leading whitespace. */
201 | private void eatWhitespace() throws IOException
202 | {
203 | while (peek == ' ') read();
204 | }
205 |
206 | /** Convert hex char to base 10 digit */
207 | static int hex(int c)
208 | {
209 | if ('0' <= c && c <= '9') return c - '0';
210 | if ('a' <= c && c <= 'f') return c - 'a' + 10;
211 | if ('A' <= c && c <= 'F') return c - 'A' + 10;
212 | return -1;
213 | }
214 |
215 | private IOException unexpectedChar(int ch)
216 | {
217 | return new IOException("Unexpected char '" + ((char)ch) + "' [" + pos + "]");
218 | }
219 |
220 | private BufferedReader in;
221 | private boolean init = false;
222 | private int cur;
223 | private int peek;
224 | private int pos;
225 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/src/jasper/util/JsonWriter.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 19 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package jasper.util;
10 |
11 | import java.io.*;
12 | import java.util.*;
13 | import javax.baja.status.*;
14 |
15 | /**
16 | * JsonWriter.
17 | */
18 | public final class JsonWriter
19 | {
20 | /** Constructor. */
21 | public JsonWriter(OutputStream out)
22 | {
23 | this.out = new PrintWriter(out);
24 | }
25 |
26 | /** Flush underlying output stream. */
27 | public JsonWriter flush() throws IOException
28 | {
29 | out.flush();
30 | return this;
31 | }
32 |
33 | /** Close underlying output stream. */
34 | public JsonWriter close() throws IOException
35 | {
36 | out.close();
37 | return this;
38 | }
39 |
40 | /** Write given char to output stream. */
41 | public JsonWriter write(char val) throws IOException
42 | {
43 | out.print(val);
44 | return this;
45 | }
46 |
47 | /** Write given name as "": to output stream. */
48 | public JsonWriter writeKey(String name) throws IOException
49 | {
50 | out.print('\"');
51 | out.print(name); // TODO: escape
52 | out.print('\"');
53 | out.print(':');
54 | return this;
55 | }
56 |
57 | /** Write given int to output stream. */
58 | public JsonWriter writeVal(int val) throws IOException
59 | {
60 | out.print(val);
61 | return this;
62 | }
63 |
64 | /** Write given int to output stream. */
65 | public JsonWriter writeVal(double val) throws IOException
66 | {
67 | if (Double.isNaN(val)) { out.print("\"na\""); return this; }
68 | if (val == Double.POSITIVE_INFINITY) { out.print("\"na\""); return this; }
69 | if (val == Double.NEGATIVE_INFINITY) { out.print("\"na\""); return this; }
70 | out.print(val);
71 | return this;
72 | }
73 |
74 | /** Write given object to output stream. */
75 | public JsonWriter writeVal(Object val) throws IOException
76 | {
77 | // null
78 | if (val == null)
79 | {
80 | out.print("null");
81 | return this;
82 | }
83 |
84 | // String
85 | if (val instanceof String)
86 | {
87 | // TODO: escape
88 | out.print('\"');
89 | out.print(val);
90 | out.print('\"');
91 | return this;
92 | }
93 |
94 | // Integer
95 | if (val instanceof Integer)
96 | {
97 | int i = ((Integer)val).intValue();
98 | this.writeVal(i);
99 | return this;
100 | }
101 |
102 | // Double
103 | if (val instanceof Double)
104 | {
105 | double d = ((Double)val).doubleValue();
106 | this.writeVal(d);
107 | return this;
108 | }
109 |
110 | // BStatusBoolean
111 | if (val instanceof BStatusBoolean)
112 | {
113 | BStatusBoolean b = (BStatusBoolean)val;
114 | out.print(b.getValue() ? 1 : 0);
115 | return this;
116 | }
117 |
118 | // BStatusNumeric
119 | if (val instanceof BStatusNumeric)
120 | {
121 | BStatusNumeric n = (BStatusNumeric)val;
122 | double d = n.getValue();
123 | this.writeVal(d);
124 | return this;
125 | }
126 |
127 | // BStatusEnum
128 | if (val instanceof BStatusEnum)
129 | {
130 | BStatusEnum e = (BStatusEnum)val;
131 | out.print(e.getValue().getOrdinal());
132 | return this;
133 | }
134 |
135 | // HashMap
136 | if (val instanceof HashMap)
137 | {
138 | HashMap map = (HashMap)val;
139 | out.print('{');
140 | int i = 0;
141 |
142 | Iterator iter = map.entrySet().iterator();
143 | while (iter.hasNext())
144 | {
145 | if (i > 0) out.print(',');
146 | Map.Entry e = (Map.Entry)iter.next();
147 | writeKey((String)e.getKey());
148 | writeVal(e.getValue().toString());
149 | i++;
150 | }
151 |
152 | out.print('}');
153 | return this;
154 | }
155 |
156 | // unsupported type
157 | throw new IOException("Unsupported type '" + val + "' [" + val.getClass().getName() + "]");
158 | }
159 |
160 | private PrintWriter out;
161 | }
--------------------------------------------------------------------------------
/jasper/jasper-rt/srcTest/test/jasper/BJasperUtilTest.java:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2023, Novant LLC
3 | // Licensed under the MIT License
4 | //
5 | // History:
6 | // 20 Jan 2023 Andy Frank Creation
7 | //
8 |
9 | package test.jasper;
10 |
11 | import java.io.*;
12 | import java.util.*;
13 |
14 | import javax.baja.nre.annotations.*;
15 | import javax.baja.registry.*;
16 | import javax.baja.sys.*;
17 | import javax.baja.test.BTestNg;
18 | import javax.baja.util.*;
19 |
20 | import org.testng.annotations.AfterMethod;
21 | import org.testng.annotations.AfterTest;
22 | import org.testng.annotations.BeforeMethod;
23 | import org.testng.annotations.Test;
24 |
25 | import jasper.util.*;
26 |
27 | /** BJasperUtilTest */
28 | @NiagaraType
29 | public class BJasperUtilTest extends BTestNg
30 | {
31 |
32 | /*+ ------------ BEGIN BAJA AUTO GENERATED CODE ------------ +*/
33 |
34 | ////////////////////////////////////////////////////////////////
35 | // Type
36 | ////////////////////////////////////////////////////////////////
37 |
38 | @Override
39 | public Type getType() { return TYPE; }
40 | public static final Type TYPE = Sys.loadType(BJasperUtilTest.class);
41 |
42 | /*+ ------------ END BAJA AUTO GENERATED CODE -------------- +*/
43 |
44 | @BeforeMethod public void beforeMethod() {}
45 | @AfterMethod public void afterMethod() {}
46 |
47 | ////////////////////////////////////////////////////////////////
48 | // testIsType
49 | ////////////////////////////////////////////////////////////////
50 |
51 | @Test public void testIsType() throws IOException
52 | {
53 | BFolder f = new BFolder();
54 | verifyEq(JasperUtil.isType(f, "baja:Folder"), true);
55 | verifyEq(JasperUtil.isType(f, "baja:Component"), true);
56 | verifyEq(JasperUtil.isType(f, "baja:Complex"), true);
57 | verifyEq(JasperUtil.isType(f, "baja:Object"), true);
58 |
59 | BComponent c = new BComponent();
60 | verifyEq(JasperUtil.isType(c, "baja:Folder"), false);
61 | verifyEq(JasperUtil.isType(c, "baja:Component"), true);
62 | verifyEq(JasperUtil.isType(c, "baja:Complex"), true);
63 | verifyEq(JasperUtil.isType(c, "baja:Object"), true);
64 | }
65 |
66 | ////////////////////////////////////////////////////////////////
67 | // testUnescapeSlotPath
68 | ////////////////////////////////////////////////////////////////
69 |
70 | @Test public void testUnescapeSlotPath() throws IOException
71 | {
72 | verifyEq(JasperUtil.unescapeSlotPath(""), "");
73 | verifyEq(JasperUtil.unescapeSlotPath("/Foo"), "/Foo");
74 | verifyEq(JasperUtil.unescapeSlotPath("/Foo/Bar"), "/Foo/Bar");
75 |
76 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$205"), "/Foo 5");
77 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$2dBar"), "/Foo-Bar");
78 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$24Bar"), "/Foo$Bar");
79 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$2fBar"), "/Foo/Bar");
80 |
81 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$205$2dBar$2dCar"),"/Foo 5-Bar-Car");
82 |
83 | // these cases should never happen, but just in case cover
84 | // when not enough chars to decode
85 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$"), "/Foo$");
86 | verifyEq(JasperUtil.unescapeSlotPath("/Foo$2"), "/Foo$2");
87 | }
88 |
89 | ////////////////////////////////////////////////////////////////
90 | // testSlotPathToSuffix
91 | ////////////////////////////////////////////////////////////////
92 |
93 | @Test public void testSlotPathToSuffix() throws IOException
94 | {
95 | verifyEq(JasperUtil.slotPathToSuffix(""), "");
96 | verifyEq(JasperUtil.slotPathToSuffix("Foo"), "Foo");
97 | verifyEq(JasperUtil.slotPathToSuffix("Foo/Bar"), "Foo.Bar");
98 | verifyEq(JasperUtil.slotPathToSuffix("Foo/Bar/Car"), "Foo.Bar.Car");
99 |
100 | verifyEq(JasperUtil.slotPathToSuffix("Foo$205"), "Foo5");
101 | verifyEq(JasperUtil.slotPathToSuffix("Foo$2dBar"), "FooBar");
102 | verifyEq(JasperUtil.slotPathToSuffix("Foo$24Bar"), "FooBar");
103 | verifyEq(JasperUtil.slotPathToSuffix("Foo$2fBar"), "FooBar");
104 |
105 | verifyEq(JasperUtil.slotPathToSuffix("Foo$205$2dBar$2dCar"),"Foo5BarCar");
106 |
107 | // these cases should never happen, but just in case cover
108 | // when not enough chars to decode
109 | verifyEq(JasperUtil.slotPathToSuffix("Foo$"), "Foo");
110 | verifyEq(JasperUtil.slotPathToSuffix("Foo$2"), "Foo");
111 | }
112 |
113 | ////////////////////////////////////////////////////////////////
114 | // testSplitPath
115 | ////////////////////////////////////////////////////////////////
116 |
117 | @Test public void testSplitPath() throws IOException
118 | {
119 | verifyPath("/", new String[] {});
120 | verifyPath("/foo", new String[] { "foo" });
121 | verifyPath("/alpha/beta/gamma", new String[] { "alpha", "beta", "gamma" });
122 | }
123 |
124 | private void verifyPath(String path, String[] acc)
125 | {
126 | String[] test = JasperUtil.splitPath(path);
127 | verifyEq(test.length, acc.length);
128 | for (int i=0; i
2 |
3 |
--------------------------------------------------------------------------------
/niagara.signing.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2024, Novant LLC
3 | # Licensed under the MIT License
4 | #
5 |
6 | niagara.signing.profileType=com.tridium.gradle.plugins.signing.profile.JarSignerSigningProfile
7 |
8 | # If the jarsigner on your PATH does not support PKCS11 signing, set
9 | # the full path to a working jarsigner here.
10 | jarsigner.cmd=jarsigner
11 |
12 | # -keystore NONE
13 | jarsigner.args+=-keystore
14 | jarsigner.args+=NONE
15 |
16 | # -storetype PKCS11
17 | jarsigner.args+=-storetype
18 | jarsigner.args+=PKCS11
19 |
20 | # -storepass NONE
21 | jarsigner.args+=-storepass
22 | jarsigner.args+=NONE
23 |
24 | # -sigalg SHA256withRSA
25 | jarsigner.args+=-sigalg
26 | jarsigner.args+=SHA256withRSA
27 |
28 | # -providerClass sun.security.pkcs11.SunPKCS11
29 | jarsigner.args+=-providerClass
30 | jarsigner.args+=sun.security.pkcs11.SunPKCS11
31 |
32 | # -providerArg
33 | jarsigner.args+=-providerArg
34 | jarsigner.args+="${jarsigner.provider}"
35 |
36 | # -signedjar
37 | jarsigner.args+=-signedjar
38 | jarsigner.args+=${destfile}
39 | jarsigner.args+=${srcfile}
40 | # NOTE: ensure the alias in build.gradle.kts matches what's registered
41 | # in your HSM and wrap in double quotes in contains spaces
42 | jarsigner.args+=${alias}
43 |
44 | # -tsa http://timestamp.digicert.com
45 | jarsigner.args+=-tsa
46 | jarsigner.args+=http://timestamp.digicert.com
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Jasper N4
2 |
3 | [jasper]: https://github.com/novant-io/jasper
4 |
5 | An implementation of the [Jasper][jasper] JSON API for Niagara N4.
6 |
7 | ## Quick Start
8 |
9 | See [Installation](https://github.com/novant-io/jasper-n4/wiki/1.-Installation)
10 | for more details:
11 |
12 | 1. [Check supported Niagara versions](https://github.com/novant-io/jasper-n4/wiki/1.-Installation#supported-versions)
13 | 2. [Install Jasper Module](https://github.com/novant-io/jasper-n4/wiki/1.-Installation#install-module)
14 | 3. [User Setup](https://github.com/novant-io/jasper-n4/wiki/1.-Installation#user-setup)
15 | 4. [Jasper Setup](https://github.com/novant-io/jasper-n4/wiki/1.-Installation#jasper-setup)
16 | 5. [Rebuild Index](https://github.com/novant-io/jasper-n4/wiki/1.-Installation#rebuild-index)
17 |
18 | ## API Examples
19 |
20 | See [API Documentation](https://github.com/novant-io/jasper-n4/wiki/2.-API-Documentation)
21 | for more details.
22 |
23 | ### About
24 |
25 | $ curl host/jasper/v1/about -XPOST -u username:password
26 |
27 | {
28 | "name": "demo",
29 | "vendor": "Tridium",
30 | "model": "Niagara 4",
31 | "version": "4.12.0.156",
32 | "moduleName": "jasper",
33 | "moduleVersion": "0.13"
34 | }
35 |
36 | ### Sources
37 |
38 | $ curl host/jasper/v1/sources -XPOST -u username:password
39 |
40 | {
41 | "sources": [
42 | {
43 | "id": "54d",
44 | "name": "Chiller",
45 | "path": "/Drivers/NiagaraNetwork/JACE-02/points/Chiller"
46 | },
47 | {
48 | "id": "620",
49 | "name": "VAV-1",
50 | "path": "/Drivers/NiagaraNetwork/JACE-05/points/VAV-1"
51 | },
52 | {
53 | "id": "621",
54 | "name": "VAV-2",
55 | "path": "/Drivers/NiagaraNetwork/JACE-05/points/VAV-2"
56 | },
57 | {
58 | "id": "622",
59 | "name": "VAV-3",
60 | "path": "/Drivers/NiagaraNetwork/JACE-05/points/VAV-3"
61 | }
62 | ]
63 | }
64 |
65 | ### Points
66 |
67 | $ curl host/jasper/v1/points -XPOST -d source_id=620 -u username:password
68 |
69 | {
70 | "points": [
71 | {
72 | "addr": "av.DamperPosition",
73 | "name": "Damper Position"
74 | },
75 | {
76 | "addr": "bv.",
77 | "name": "Fan Status"
78 | },
79 | {
80 | "addr": "av.ZoneTemp",
81 | "name": "Zone Temp",
82 | "unit": "°F"
83 | },
84 | {
85 | "addr": "ao.ReturnTemp",
86 | "name": "Return Temp",
87 | "unit": "°F"
88 | },
89 | {
90 | "addr": "ao.DischargeTemp",
91 | "name": "Discharge Temp",
92 | "unit": "°F"
93 | }
94 | ]
95 | }
96 |
97 | ### Values
98 |
99 | $ curl host/jasper/v1/values -XPOST -d source_id=620 -u username:password
100 |
101 | {
102 | "values": [
103 | { "addr":"av.DamperPosition", "val":72.0 },
104 | { "addr":"bv.FanStatus", "val":1 },
105 | { "addr":"av.ZoneTemp", "val":72.0 },
106 | { "addr":"ao.ReturnTemp", "val":73.142 },
107 | { "addr":"ao.DischargeTemp", "val":68.230 }
108 | ]
109 | }
110 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Novant. All Rights Reserved.
3 | */
4 |
5 | import com.tridium.gradle.plugins.settings.MultiProjectExtension
6 | import com.tridium.gradle.plugins.settings.LocalSettingsExtension
7 |
8 | pluginManagement {
9 | val niagaraHome: Provider = providers.gradleProperty("niagara_home").orElse(
10 | providers.systemProperty("niagara_home").orElse(
11 | providers.environmentVariable("NIAGARA_HOME").orElse(
12 | providers.environmentVariable("niagara_home")
13 | )
14 | )
15 | )
16 |
17 | val gradlePluginHome: String = providers.gradleProperty("gradlePluginHome").orElse(
18 | providers.environmentVariable("GRADLE_PLUGIN_HOME").orElse (
19 | niagaraHome.map { "$it/etc/m2/repository" }
20 | )
21 | ).orNull ?: throw InvalidUserDataException(buildString {
22 | val isWindows = providers.systemProperty("os.name").map { it.toLowerCase(java.util.Locale.ENGLISH) }.get().contains("windows")
23 | val propsFile = File(rootDir, "gradle.properties")
24 |
25 | appendLine("************************************************************")
26 | appendLine("ERROR: Invalid project configuration: Cannot derive value of 'gradlePluginHome'.")
27 | appendLine()
28 | if (propsFile.exists()) {
29 | appendLine("You can set it by editing the properties file at:")
30 | } else {
31 | appendLine("You can set it by creating a properties file at:")
32 | }
33 | appendLine()
34 | appendLine(" $propsFile")
35 | appendLine()
36 | appendLine("and adding 'gradlePluginHome':")
37 | appendLine()
38 | if (isWindows) {
39 | appendLine(" gradlePluginHome=C:\\\\path\\\\to\\\\plugins")
40 | } else {
41 | appendLine(" gradlePluginHome=/path/to/plugins")
42 | }
43 | appendLine()
44 | appendLine("You can also set it by defining the 'GRADLE_PLUGIN_HOME' environment varaible:")
45 | appendLine()
46 | if (isWindows) {
47 | appendLine(" set GRADLE_PLUGIN_HOME=C:\\\\path\\\\to\\\\plugins")
48 | } else {
49 | appendLine(" export GRADLE_PLUGIN_HOME=/path/to/plugins")
50 | }
51 | appendLine()
52 | appendLine("------------------------------------------------------------")
53 | appendLine()
54 | append("If you are using the plugins shipped with the version of Niagara you are building against, ")
55 | if (propsFile.exists()) {
56 | appendLine("you can edit the properties file at:")
57 | } else {
58 | appendLine("you can create a properties file at:")
59 | }
60 | appendLine()
61 | appendLine(" $propsFile")
62 | appendLine()
63 | appendLine("and add 'niagara_home':")
64 | appendLine()
65 | if (isWindows) {
66 | appendLine(" niagara_home=C:\\\\Niagara\\\\Niagara-4.x.y.z")
67 | } else {
68 | appendLine(" niagara_home=/opt/Niagara-4.x.y.z")
69 | }
70 | appendLine()
71 | appendLine("You can also set it by defining the 'NIAGARA_HOME' environment variable:")
72 | appendLine()
73 | if (isWindows) {
74 | appendLine(" set NIAGARA_HOME=C:\\\\Niagara\\\\Niagara-4.x.y.z")
75 | } else {
76 | appendLine(" export NIAGARA_HOME=/opt/Niagara-4.x.y.z")
77 | }
78 | appendLine()
79 | appendLine("************************************************************")
80 | })
81 |
82 | val gradlePluginRepoUrl = "file:///${gradlePluginHome.replace('\\', '/')}"
83 |
84 | val gradlePluginVersion: String = "7.6.17"
85 | val settingsPluginVersion: String = "7.6.3"
86 |
87 | repositories {
88 | maven(url = "$gradlePluginRepoUrl")
89 | gradlePluginPortal()
90 | }
91 |
92 | plugins {
93 | id("com.tridium.settings.multi-project") version (settingsPluginVersion)
94 | id("com.tridium.settings.local-settings-convention") version (settingsPluginVersion)
95 |
96 | id("com.tridium.niagara") version (gradlePluginVersion)
97 | id("com.tridium.vendor") version (gradlePluginVersion)
98 | id("com.tridium.niagara-module") version (gradlePluginVersion)
99 | id("com.tridium.niagara-signing") version (gradlePluginVersion)
100 |
101 | id("com.tridium.convention.niagara-home-repositories") version (gradlePluginVersion)
102 | }
103 | }
104 |
105 | plugins {
106 | // Discover all subprojects in this build
107 | id("com.tridium.settings.multi-project")
108 |
109 | // Apply local settings from local/my-niagara.gradle(.kts) if they
110 | // are present
111 | id("com.tridium.settings.local-settings-convention")
112 | }
113 |
114 | configure {
115 | loadLocalSettings()
116 | }
117 |
118 | configure {
119 | // Note: If you have specific subfolder(s) to include, you can pass their relative
120 | // path as an argument:
121 | //
122 | // findProjects("folder1")
123 | // findProjects("folder2")
124 | //
125 | // Otherwise, this will find all projects under the root directory with the following layout:
126 | //
127 | // project-rt/project-rt.gradle.kts
128 | // project-rt/project-rt.gradle
129 | // project-rt/build.gradle.kts
130 | // project-rt/build.gradle
131 | findProjects()
132 | }
133 |
134 | rootProject.name = "jasper"
135 |
--------------------------------------------------------------------------------