|
22 | 22 | import java.util.Set; |
23 | 23 | import java.util.concurrent.ScheduledExecutorService; |
24 | 24 | import java.util.concurrent.TimeUnit; |
| 25 | +import java.util.concurrent.locks.Lock; |
25 | 26 |
|
26 | 27 | import javax.script.Invocable; |
27 | 28 | import javax.script.ScriptContext; |
|
31 | 32 |
|
32 | 33 | import org.eclipse.jdt.annotation.NonNullByDefault; |
33 | 34 | import org.eclipse.jdt.annotation.Nullable; |
| 35 | +import org.openhab.core.automation.module.script.LockableScriptEngine; |
34 | 36 | import org.openhab.core.automation.module.script.ScriptDependencyTracker; |
35 | 37 | import org.openhab.core.automation.module.script.ScriptEngineContainer; |
36 | 38 | import org.openhab.core.automation.module.script.ScriptEngineFactory; |
@@ -159,32 +161,69 @@ public boolean loadScript(String engineIdentifier, InputStreamReader scriptData) |
159 | 161 | ScriptEngineContainer container = loadedScriptEngineInstances.get(engineIdentifier); |
160 | 162 | if (container == null) { |
161 | 163 | logger.error("Could not load script, as no ScriptEngine has been created"); |
162 | | - } else { |
163 | | - ScriptEngine engine = container.getScriptEngine(); |
| 164 | + return false; |
| 165 | + } |
| 166 | + ScriptEngine engine = container.getScriptEngine(); |
| 167 | + if (engine instanceof LockableScriptEngine lockable) { |
| 168 | + Lock lock = lockable.getLock(); |
| 169 | + long timeout = lockable.getLockAcquisitionTimeoutMs(); |
| 170 | + boolean locked; |
164 | 171 | try { |
165 | | - engine.eval(scriptData); |
166 | | - if (engine instanceof Invocable inv) { |
167 | | - try { |
168 | | - inv.invokeFunction("scriptLoaded", engineIdentifier); |
169 | | - } catch (NoSuchMethodException e) { |
170 | | - logger.trace("scriptLoaded() is not defined in the script: {}", engineIdentifier); |
171 | | - } |
| 172 | + locked = lock.tryLock(timeout, TimeUnit.MILLISECONDS); |
| 173 | + } catch (InterruptedException e) { |
| 174 | + Thread.currentThread().interrupt(); |
| 175 | + logger.error("Interrupted while waiting to acquire the lock while loading script for engine '{}'", |
| 176 | + engineIdentifier); |
| 177 | + logger.trace("", e); |
| 178 | + return false; |
| 179 | + } |
| 180 | + if (locked) { |
| 181 | + try { |
| 182 | + return runScript(engineIdentifier, engine, scriptData); |
| 183 | + } finally { |
| 184 | + lock.unlock(); |
| 185 | + } |
| 186 | + } else { |
| 187 | + if (timeout < 2000L) { |
| 188 | + logger.error( |
| 189 | + "Failed to acquire the lock while loading script for engine '{}' within {} milliseconds. Aborting loading of script.", |
| 190 | + engineIdentifier, timeout); |
172 | 191 | } else { |
173 | | - logger.trace("ScriptEngine does not support Invocable interface"); |
| 192 | + logger.error( |
| 193 | + "Failed to acquire the lock while loading script for engine '{}' within {} seconds. Aborting loading of script.", |
| 194 | + engineIdentifier, TimeUnit.MILLISECONDS.toSeconds(timeout)); |
174 | 195 | } |
175 | | - return true; |
176 | | - } catch (Exception ex) { |
177 | | - logger.error("Error during evaluation of script '{}': {}", engineIdentifier, ex.getMessage()); |
178 | | - // Only call logger if debug level is actually enabled, because OPS4J Pax Logging holds (at least for |
179 | | - // some time) a reference to the exception and its cause, which may hold a reference to the script |
180 | | - // engine. |
181 | | - // This prevents garbage collection (at least for some time) to remove the script engine from heap. |
182 | | - if (logger.isDebugEnabled()) { |
183 | | - logger.debug("", ex); |
| 196 | + return false; |
| 197 | + } |
| 198 | + } else { |
| 199 | + return runScript(engineIdentifier, engine, scriptData); |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + private boolean runScript(String engineIdentifier, ScriptEngine engine, InputStreamReader scriptData) { |
| 204 | + try { |
| 205 | + engine.eval(scriptData); |
| 206 | + if (engine instanceof Invocable inv) { |
| 207 | + try { |
| 208 | + inv.invokeFunction("scriptLoaded", engineIdentifier); |
| 209 | + } catch (NoSuchMethodException e) { |
| 210 | + logger.trace("scriptLoaded() is not defined in the script: {}", engineIdentifier); |
184 | 211 | } |
| 212 | + } else { |
| 213 | + logger.trace("ScriptEngine does not support Invocable interface"); |
| 214 | + } |
| 215 | + return true; |
| 216 | + } catch (Exception ex) { |
| 217 | + logger.error("Error during evaluation of script '{}': {}", engineIdentifier, ex.getMessage()); |
| 218 | + // Only call logger if debug level is actually enabled, because OPS4J Pax Logging holds (at least for |
| 219 | + // some time) a reference to the exception and its cause, which may hold a reference to the script |
| 220 | + // engine. |
| 221 | + // This prevents garbage collection (at least for some time) to remove the script engine from heap. |
| 222 | + if (logger.isDebugEnabled()) { |
| 223 | + logger.debug("", ex); |
185 | 224 | } |
| 225 | + return false; |
186 | 226 | } |
187 | | - return false; |
188 | 227 | } |
189 | 228 |
|
190 | 229 | @Override |
|
0 commit comments