18
18
import java .io .File ;
19
19
import java .io .IOException ;
20
20
import java .io .InputStream ;
21
- import java .net .URI ;
22
21
import java .nio .channels .SeekableByteChannel ;
22
+ import java .nio .file .AccessMode ;
23
23
import java .nio .file .FileSystems ;
24
- import java .nio .file .Files ;
24
+ import java .nio .file .LinkOption ;
25
25
import java .nio .file .OpenOption ;
26
26
import java .nio .file .Path ;
27
27
import java .nio .file .Paths ;
28
28
import java .nio .file .attribute .FileAttribute ;
29
+ import java .util .Collections ;
30
+ import java .util .Map ;
29
31
import java .util .Set ;
30
32
import java .util .function .Consumer ;
31
33
import java .util .function .Function ;
36
38
import org .graalvm .polyglot .Context ;
37
39
import org .openhab .automation .jsscripting .internal .fs .DelegatingFileSystem ;
38
40
import org .openhab .automation .jsscripting .internal .fs .PrefixedSeekableByteChannel ;
41
+ import org .openhab .automation .jsscripting .internal .fs .ReadOnlySeekableByteArrayChannel ;
39
42
import org .openhab .automation .jsscripting .internal .scriptengine .InvocationInterceptingScriptEngineWithInvocable ;
40
43
import org .openhab .core .OpenHAB ;
41
44
import org .openhab .core .automation .module .script .ScriptExtensionAccessor ;
@@ -56,7 +59,10 @@ public class OpenhabGraalJSScriptEngine extends InvocationInterceptingScriptEngi
56
59
private static final String REQUIRE_WRAPPER_NAME = "__wraprequire__" ;
57
60
private static final String MODULE_DIR = String .join (File .separator , OpenHAB .getConfigFolder (), "automation" , "lib" ,
58
61
"javascript" , "personal" );
59
-
62
+ // final CommonJS search path for our library
63
+ private static final Path OH_NODE_PATH = Paths .get ("/node_modules/@oh.js" );
64
+ // resource path of our oh js library
65
+ private static final Path OH_FILE_PATH = Paths .get ("/oh.js" );
60
66
// these fields start as null because they are populated on first use
61
67
private @ NonNullByDefault ({}) String engineIdentifier ;
62
68
private @ NonNullByDefault ({}) Consumer <String > scriptDependencyListener ;
@@ -69,60 +75,65 @@ public class OpenhabGraalJSScriptEngine extends InvocationInterceptingScriptEngi
69
75
*/
70
76
public OpenhabGraalJSScriptEngine () {
71
77
super (null ); // delegate depends on fields not yet initialised, so we cannot set it immediately
78
+ delegate = GraalJSScriptEngine .create (null ,
79
+ Context .newBuilder ("js" ).allowExperimentalOptions (true ).allowAllAccess (true )
80
+ .option ("js.commonjs-require-cwd" , MODULE_DIR ).option ("js.nashorn-compat" , "true" ) // to
81
+ // ease
82
+ // migration
83
+ .option ("js.commonjs-require" , "true" ) // enable CommonJS module support
84
+ .hostClassLoader (getClass ().getClassLoader ())
85
+ .fileSystem (new DelegatingFileSystem (FileSystems .getDefault ().provider ()) {
86
+ @ Override
87
+ public SeekableByteChannel newByteChannel (Path path , Set <? extends OpenOption > options ,
88
+ FileAttribute <?>... attrs ) throws IOException {
89
+ LOGGER .debug ("SeekableByteChannel {}" , path );
90
+ if (scriptDependencyListener != null ) {
91
+ scriptDependencyListener .accept (path .toString ());
92
+ }
72
93
73
- try {
74
- File tmpDir = Files .createTempDirectory (null ).toFile ();
75
- tmpDir .deleteOnExit ();
76
- InputStream is = getClass ().getResourceAsStream ("/oh.js" );
77
- if (is != null ) {
78
- File ohFile = new File (tmpDir , "oh.js" );
79
- ohFile .deleteOnExit ();
80
- LOGGER .debug ("writing globals to {}" , ohFile );
81
- Files .write (ohFile .toPath (), is .readAllBytes ());
82
- }
83
-
84
- delegate = GraalJSScriptEngine .create (null ,
85
- Context .newBuilder ("js" ).allowExperimentalOptions (true ).allowAllAccess (true )
86
- .option ("js.commonjs-require-cwd" , MODULE_DIR ).option ("js.nashorn-compat" , "true" ) // to
87
- // ease
88
- // migration
89
- .option ("js.commonjs-require" , "true" ) // enable CommonJS module support
90
- .hostClassLoader (getClass ().getClassLoader ())
91
- .fileSystem (new DelegatingFileSystem (FileSystems .getDefault ().provider ()) {
92
- @ Override
93
- public SeekableByteChannel newByteChannel (Path path , Set <? extends OpenOption > options ,
94
- FileAttribute <?>... attrs ) throws IOException {
95
- if (scriptDependencyListener != null ) {
96
- scriptDependencyListener .accept (path .toString ());
94
+ if (path .toString ().endsWith (".js" )) {
95
+ SeekableByteChannel sbc = null ;
96
+ if (OH_FILE_PATH .equals (path )) {
97
+ InputStream is = getClass ().getResourceAsStream (OH_FILE_PATH .toString ());
98
+ if (is != null ) {
99
+ sbc = new ReadOnlySeekableByteArrayChannel (is .readAllBytes ());
100
+ }
97
101
}
98
-
99
- if (path .toString ().endsWith (".js" )) {
100
- return new PrefixedSeekableByteChannel (
101
- ("require=" + REQUIRE_WRAPPER_NAME + "(require);" ).getBytes (),
102
- super .newByteChannel (path , options , attrs ));
103
- } else {
104
- return super .newByteChannel (path , options , attrs );
102
+ if (sbc == null ) {
103
+ sbc = super .newByteChannel (path , options , attrs );
105
104
}
105
+ return new PrefixedSeekableByteChannel (
106
+ ("require=" + REQUIRE_WRAPPER_NAME + "(require);" ).getBytes (), sbc );
107
+ } else {
108
+ return super .newByteChannel (path , options , attrs );
106
109
}
110
+ }
107
111
108
- @ Override
109
- public Path parsePath (URI uri ) {
110
- return parsePath (uri .toString ());
112
+ @ Override
113
+ public void checkAccess (Path path , Set <? extends AccessMode > modes ,
114
+ LinkOption ... linkOptions ) throws IOException {
115
+ if (!OH_NODE_PATH .equals (path )) {
116
+ super .checkAccess (path , modes , linkOptions );
111
117
}
118
+ }
112
119
113
- @ Override
114
- public Path parsePath (String path ) {
115
- if (path .indexOf (tmpDir .getPath ()) != 0
116
- && path .indexOf (MODULE_DIR .toString ()) != 0 ) {
117
- return tmpDir .toPath ();
118
- }
119
- return Paths .get (path );
120
+ @ Override
121
+ public Map <String , Object > readAttributes (Path path , String attributes ,
122
+ LinkOption ... options ) throws IOException {
123
+ if (OH_NODE_PATH .equals (path )) {
124
+ return Collections .singletonMap ("isRegularFile" , true );
120
125
}
121
- }));
122
- } catch (IOException e ) {
123
- LOGGER .error ("Could not load gloabls" , e );
124
- throw new RuntimeException ("Could not load gloabls" , e );
125
- }
126
+ return super .readAttributes (path , attributes , options );
127
+ }
128
+
129
+ @ Override
130
+ public Path toRealPath (Path path , LinkOption ... linkOptions ) throws IOException {
131
+ if (OH_NODE_PATH .equals (path )) {
132
+ return OH_FILE_PATH ;
133
+ }
134
+ return super .toRealPath (path , linkOptions );
135
+ }
136
+ }));
126
137
}
127
138
128
139
@ Override
0 commit comments