diff --git a/docs/modules/operation/pages/deep-dive/events/scriptd.adoc b/docs/modules/operation/pages/deep-dive/events/scriptd.adoc
index 262434d78c22..edf70f6a5384 100644
--- a/docs/modules/operation/pages/deep-dive/events/scriptd.adoc
+++ b/docs/modules/operation/pages/deep-dive/events/scriptd.adoc
@@ -6,12 +6,39 @@ It can generate new events based on incoming data, enrich events with new data,
This feature enables better integration with external systems, based on data from sources internal and external to {page-component-title}.
The `$\{OPENNMS_HOME}/etc/scriptd-configuration.xml` file contains elements and attributes that define how scriptd processes events and executes scripts.
-Beanshell scripts executed by `scriptd` should be placed in `$\{OPENNMS_HOME}/etc/scripts/`
+Groovy scripts executed by `scriptd` should be placed in `$\{OPENNMS_HOME}/etc/scripts/`
-When an event is published on the event bus that Scriptd is configured to listen for, it executes the beanscript associated with that event.
-A common (though not _only_, as you can see in the examples) usage pattern is to compose a script that defines one or more a callable functions that accept an immutable event `IEvent` as a argument, which is sourced when scriptd starts, in `start-script` block.
-This makes the function available within the context of `scriptd` so that the script function can be called from an `event-script` block.
-Beanshell script can also be embedded directly in the `start-script` or `event-script` blocks, using xml `CDATA` tags.
+When an event is published on the event bus that Scriptd is configured to listen for, it executes the script associated with that event.
+A common (though not _only_, as you can see in the examples) usage pattern is to compose a script that defines one or more callable closures that accept an immutable event `IEvent` as an argument, which is evaluated when scriptd starts, in the `start-script` block.
+This makes the closure available within the context of `scriptd` so that it can be called from an `event-script` block.
+Groovy scripts can also be embedded directly in the `start-script` or `event-script` blocks, using xml `CDATA` tags.
+
+IMPORTANT: Groovy is the only scripting language that ships with `scriptd`.
+If you are upgrading from an earlier version that uses BeanShell, see <> for the changes you need to make to existing scripts.
+
+[[ga-scriptd-groovy-notes]]
+== Writing Groovy scripts for scriptd
+
+Scriptd executes each `start-script`, `stop-script`, and `event-script` block separately, but all of them share a single set of variables called the _binding_.
+Anything you want to reuse across blocks -- a logger, a helper object, a closure -- must live in that binding.
+This leads to three rules that differ from the BeanShell scripts used in earlier releases:
+
+Assign shared variables without `def` or a type::
+An assignment such as `log = bsf.lookupBean("log")` places `log` in the shared binding, where later blocks can read it.
+Writing `def log = ...` or `Logger log = ...` instead creates a variable local to that one block, and later blocks fail with a "No such property" error.
+
+Define reusable functions as closures, not methods::
+A method declared with `def myFunction() { ... }` belongs only to the script that declares it and is not visible to other blocks.
+Assign a closure to a binding variable instead: `myFunction = { ... }`.
+Closures stored in the binding can be called normally, including from inside other closures.
+
+Load external script files with `evaluate`, not `source`::
+Replace `source("/path/to/script.bsh")` with `evaluate(new File("/path/to/script.groovy"))`.
+The evaluated file shares the calling script's binding, so any closures and variables it assigns without `def` become available to every later block.
+
+Beyond these rules, Groovy accepts most Java syntax, so much of an existing script will carry over unchanged.
+The most common remaining edits are replacing the Java-style `for (item : collection)` loop with Groovy's `for (item in collection)`, replacing Java array literals such as `new int[]{1, 2, 4}` with Groovy list literals such as `[1, 2, 4]`, and adding imports for classes that BeanShell resolved implicitly.
+Semicolons are optional in Groovy.
== Examples
@@ -24,108 +51,118 @@ The following example performs a reverse DNS lookup on the content of a specifie
[source, xml]
----
-
+
-
- log = bsf.lookupBean("log"); <1>
- source("/opt/opennms/etc/scripts/scriptd-parm-rdns.bsh"); <2>
+
+ log = bsf.lookupBean("log") <1>
+ evaluate(new File("/opt/opennms/etc/scripts/scriptd-parm-rdns.groovy")) <2>
-
- log.debug("executing a stop script");
+
+ log.debug("executing a stop script")
-
+ <3>
- appendUei = "WithLookup"; <4>
- positions = new int[]{ 1, 2, 4 };
- event = bsf.lookupBean("event"); <5>
- mangleEvent(event, appendUei, positions); <6>
+ appendUei = "WithLookup" <4>
+ positions = [1, 2, 4] <5>
+ event = bsf.lookupBean("event") <6>
+ mangleEvent(event, appendUei, positions) <7>
----
<1> Boilerplate to define the log object, used for logging within your script.
-<2> The script is sourced on startup to define the functions it contains.
+ Note the absence of `def`, which keeps `log` in the shared binding so the other blocks can use it.
+<2> The script is evaluated on startup to define the closures it contains.
<3> The `uei` for which to listen that will execute this `event-script` block.
<4> This value is appended to the resulting event `uei`, making the emitted event uei `uei.opennms.org/corporate_networks/traps/OSPF-StateWithLookup`.
-<5> Fetch the incoming event.
-<6> This function is defined in `scriptd-parm-rdns.bsh`, sourced by the `start-script` block.
+<5> A Groovy list literal; the equivalent BeanShell script used a Java array literal here.
+<6> Fetch the incoming event.
+<7> This closure is defined in `scriptd-parm-rdns.groovy`, evaluated by the `start-script` block.
-.`$\{OPENNMS_HOME}/etc/scripts/scriptd-parm-rdns.bsh`
-[source, beanshell]
+.`$\{OPENNMS_HOME}/etc/scripts/scriptd-parm-rdns.groovy`
+[source, groovy]
----
-import java.net.InetAddress; <1>
-import org.opennms.core.spring.BeanUtils;
-import org.opennms.netmgt.events.api.model.IEvent; <2>
-import org.opennms.netmgt.events.api.model.IParm;
-import org.opennms.netmgt.events.api.EventForwarder;
-import org.opennms.netmgt.model.events.EventBuilder; <3>
-import org.opennms.netmgt.xml.event.Event; <4>
-import org.opennms.netmgt.xml.event.Parm;
+import java.net.InetAddress <1>
+import java.net.UnknownHostException
+import org.opennms.core.spring.BeanUtils
+import org.opennms.netmgt.events.api.model.IEvent <2>
+import org.opennms.netmgt.events.api.model.IParm
+import org.opennms.netmgt.events.api.EventForwarder
+import org.opennms.netmgt.model.events.EventBuilder <3>
+import org.opennms.netmgt.xml.event.Event <4>
+import org.opennms.netmgt.xml.event.Parm
+
+eventForwarder = BeanUtils.getBean("daemonContext", "eventForwarder", EventForwarder.class) <5>
+
+lookupParm = { IParm parm -> <6>
+
+ def value = parm.getValue().getContent()
+ def hostname
+ try {
+ InetAddress ip = InetAddress.getByName(value) <7>
+ hostname = ip.getCanonicalHostName()
+ } catch (UnknownHostException e) {
+ log.debug("Reverse DNS lookup failed for {}", value)
+ hostname = value //if the lookup fails, keep the original parm value
+ }
-eventForwarder = BeanUtils.getBean("daemonContext", "eventForwarder", EventForwarder.class);
+ return new Parm(parm.getParmName() + "-hostname", hostname) <8>
+}
-void mangleEvent(IEvent event, String appendUei, int[] positions) { <5>
+mangleEvent = { IEvent event, String appendUei, List positions -> <9>
- log.debug("Mangling an event: {}", event.uei); <6>
+ log.debug("Mangling an event: {}", event.uei)
- iparms = event.getParmCollection();
+ def iparms = event.getParmCollection() <10>
if (iparms == null) {
- log.debug("Parameters null");
+ log.debug("Parameters null")
+ return <11>
}
- else {
- log.debug("Parameters NOT null");
- newEvent = new EventBuilder(event.uei.concat(appendUei), "Scriptd", new Date()) <6>
- .setNodeid(event.nodeid)
- .setInterface(event.interfaceAddress)
- .setService(event.service)
- .getEvent();
- for(p : iparms) {
- parm = new Parm();
- newEvent.addParm(parm.copyFrom(p)); //eventForwarder won't forward an event built with immutableParms
- }
- for( i : positions ) {
- lookMeUp = lookupParm(iparms.get(i-1));
- //log.debug("parm #{}", i); <7>
- //log.debug("is appended as Name: {}", lookMeUp.getParmName());
- //log.debug(" with Value: {}", lookMeUp.getValue().getContent());
- newEvent.addParm(lookMeUp); <8>
- }
+ log.debug("Parameters NOT null")
+ def newEvent = new EventBuilder(event.uei.concat(appendUei), "Scriptd", new Date()) <12>
+ .setNodeid(event.nodeid)
+ .setInterface(event.interfaceAddress)
+ .setService(event.service)
+ .getEvent()
+ for (p in iparms) { <13>
+ def parm = new Parm()
+ newEvent.addParm(parm.copyFrom(p)) //eventForwarder won't forward an event built with immutableParms
}
- log.debug("New event {}", newEvent.toString());
- eventForwarder.sendNow(newEvent); <9>
-}
-
-Parm lookupParm(IParm parm) { <10>
-
- value = parm.getValue().getContent();
- try {
- InetAddress ip = InetAddress.getByName(value); <11>
- hostname = ip.getCanonicalHostName();
- } catch ( UnknownHostException e ) {
- log.debug("Reverse DNS lookup failed for {}", value);
- hostname = value; //if the lookup fails, keep the original parm value
+ for (i in positions) {
+ def lookMeUp = lookupParm(iparms.get(i - 1)) <14>
+ //log.debug("parm #{}", i) <15>
+ //log.debug("is appended as Name: {}", lookMeUp.getParmName())
+ //log.debug(" with Value: {}", lookMeUp.getValue().getContent())
+ newEvent.addParm(lookMeUp) <16>
}
- newParm = new Parm(parm.getParmName()+"-hostname", hostname); <12>
- return newParm;
+ log.debug("New event {}", newEvent.toString())
+ eventForwarder.sendNow(newEvent) <17>
}
----
<1> `java.net.InetAddress` is used to perform the reverse DNS lookup.
-<2> Incoming events are immutable `IEvent` objects, and event paramters are immutable `IParm` objects.
+ Groovy does not import the `java.net` package implicitly, so both it and `UnknownHostException` must be imported explicitly.
+<2> Incoming events are immutable `IEvent` objects, and event parameters are immutable `IParm` objects.
<3> The `EventBuilder` is used to create new events.
<4> The `EventForwarder` requires mutable `Event` and `Parm` objects.
-<5> This defines the `mangleEvent` function, called from the `event-script` block in `scripts-configuration.xml`.
-<6> Create a new event using the `EventBuilder` using the new, appended UEI and "Scriptd" as the source.
-<7> Additional debug logging can be added using the `log` object for troubleshooting; beanshell uses Java-style comments.
-<8> This adds the event parameter containing the rDNS hostname to the new event being created.
-<9> Using the `EventForwarder`, send the new event out on the event bus.
-<10> This function is called by `mangleEvent` to perform the rDNS lookup on the original event parameter.
-<11> Perform the rDNS loookup using `InetAddress.getByName()` from `java.net.InetAddress`.
-<12> Create a new event `Parm` object using the original `Parameter` name appended with `'-hostname'`, using the value returned by the `getByName()`
+<5> Assigned without `def` so that the closures below can resolve it from the shared binding.
+<6> This defines the `lookupParm` closure, called by `mangleEvent` to perform the rDNS lookup on the original event parameter.
+ Assigning a closure rather than declaring a method is what makes it reusable from the other script blocks.
+<7> Perform the rDNS lookup using `InetAddress.getByName()` from `java.net.InetAddress`.
+<8> Create a new event `Parm` object using the original `Parameter` name appended with `'-hostname'`, using the value returned by `getCanonicalHostName()`.
+<9> This defines the `mangleEvent` closure, called from the `event-script` block in `scriptd-configuration.xml`.
+<10> Local variables that are not shared between blocks should use `def`, as they are here.
+<11> Return early when the incoming event carries no parameters, as there is nothing to enrich.
+<12> Create a new event using the `EventBuilder` using the new, appended UEI and "Scriptd" as the source.
+<13> Groovy uses `for (item in collection)` where Java and BeanShell use `for (item : collection)`.
+<14> Closures held in the binding can be called from inside another closure, exactly like a method.
+<15> Additional debug logging can be added using the `log` object for troubleshooting; Groovy uses Java-style comments.
+<16> This adds the event parameter containing the rDNS hostname to the new event being created.
+<17> Using the `EventForwarder`, send the new event out on the event bus.
[[ga-scriptd-forward-snmpv3]]
=== Example 2: Forward events to an external system using SNMPv3
@@ -137,12 +174,12 @@ The `snmpv3TrapHelper` will not forward an immutable `IEvent` or an event contai
[source,xml]
----
-
+
-
- log = bsf.lookupBean("log"); <2>
+
+ log = bsf.lookupBean("log") <2>
- snmpTrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpTrapHelper(); <3>
+ snmpTrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpTrapHelper() <3>
snmpv3TrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpV3TrapEventForwarder( <4>
"192.168.1.202", /* destination ip */
162, /* destination port */
@@ -153,32 +190,32 @@ The `snmpv3TrapHelper` will not forward an immutable `IEvent` or an event contai
"0p3nNmsv3", /* priv pass phrase */
"DES", /* priv protocol: DES, AES, AES192, AES256 */
snmpTrapHelper /* helper object */
- );
+ )
// Use the default policy rule that forwards all events - we can manage the filtering ourselves in this script
- snmpv3TrapHelper.setEventPolicyRule(new org.opennms.netmgt.scriptd.helper.EventPolicyRuleDefaultImpl());
+ snmpv3TrapHelper.setEventPolicyRule(new org.opennms.netmgt.scriptd.helper.EventPolicyRuleDefaultImpl())
]]>
-
- snmpTrapHelper.stop(); <5>
+
+ snmpTrapHelper.stop() <5>
-
- iparms = event.getParmCollection(); <7>
- newEvent = new org.opennms.netmgt.model.events.EventBuilder(event.uei, "Scriptd", new Date()) <8>
+ def iparms = event.getParmCollection() <7>
+ def newEvent = new org.opennms.netmgt.model.events.EventBuilder(event.uei, "Scriptd", new Date()) <8>
.setNodeid(event.nodeid)
.setInterface(event.interfaceAddress)
.setService(event.service)
- .getEvent();
- for(p : iparms) {
- parm = new org.opennms.netmgt.xml.event.Parm(); <9>
- newEvent.addParm(parm.copyFrom(p)); //eventForwarder won't forward an event built with immutableParms
+ .getEvent()
+ for (p in iparms) { <9>
+ def parm = new org.opennms.netmgt.xml.event.Parm() <10>
+ newEvent.addParm(parm.copyFrom(p)) //eventForwarder won't forward an event built with immutableParms
}
- //log.debug("Forwarding SNMPv3 trap with: {}", snmpv3TrapHelper); <10>
- snmpv3TrapHelper.flushEvent(newEvent); <11>
+ //log.debug("Forwarding SNMPv3 trap with: {}", snmpv3TrapHelper) <11>
+ snmpv3TrapHelper.flushEvent(newEvent) <12>
}
]]>
@@ -187,16 +224,19 @@ The `snmpv3TrapHelper` will not forward an immutable `IEvent` or an event contai
<1> Here we wrap the script content in CDATA tags directly in the `start-script`
<2> Boilerplate to define the log object, used for logging within the script.
<3> Create a new `snmpTrapHelper` object.
+ Like `log`, it is assigned without `def` so that the `stop-script` block can still reach it.
<4> Create a new `snmpv3TrapHelper` with the desired parameters.
<5> The `stop-script` block can be used to clean up on scriptd restart.
<6> This checks if the incoming event originated from a daemon other than `Trapd` and is not `null`.
<7> Fetch all immutable `IParm` event parameters from the incoming event; incoming events are always immutable.
+ These values are only used within this block, so they are declared with `def`.
<8> Create a new `Event` using the `EventBuilder` and set the desired event attributes; the `snmpv3TrapHelper` will not forward immutable `IEvent` events.
-<9> Create a new, mutable event parameter `Parm` object; the `snmpv3TrapHelper` will not forward events with immutable `IParm` parameters.
-<10> Populate the new `Parm` object with the content of the `IParm` with `copyFrom()`.
-<11> Use the `snmpv3TrapHelper` to send the new, mutable event out to its destination.
+<9> Groovy uses `for (item in collection)` where Java and BeanShell use `for (item : collection)`.
+<10> Create a new, mutable event parameter `Parm` object and populate it from the immutable `IParm` with `copyFrom()`; the `snmpv3TrapHelper` will not forward events with immutable `IParm` parameters.
+<11> Additional debug logging can be added using the `log` object for troubleshooting.
+<12> Use the `snmpv3TrapHelper` to send the new, mutable event out to its destination.
[[ga-scriptd-other]]
=== Other examples
-Other scriptd examples are available in `$\{OPENNMS_HOME}/etc/examples/` directory of your {page-component-title} installation.
+Other scriptd examples are available in `$\{OPENNMS_HOME}/etc/examples/` directory of your {page-component-title} installation.
\ No newline at end of file
diff --git a/docs/modules/reference/pages/provisioning/detectors/BsfDetector.adoc b/docs/modules/reference/pages/provisioning/detectors/BsfDetector.adoc
index ac0b03028f0c..c97129d1c98f 100644
--- a/docs/modules/reference/pages/provisioning/detectors/BsfDetector.adoc
+++ b/docs/modules/reference/pages/provisioning/detectors/BsfDetector.adoc
@@ -7,6 +7,9 @@ The only requirement is that the script returns the string `OK` if it passes.
The BSFDetector passes the `map`, `ip_addr`, `svc_name`, and `results` variables as beans to the script.
+NOTE: Groovy is the scripting language that ships with {page-component-title}.
+BeanShell is no longer supported; rewrite any existing `.bsh` scripts in Groovy and update the `bsfEngine` and `langClass` parameters of the affected detectors.
+
== Detector facts
[options="autowidth"]
@@ -32,16 +35,14 @@ The BSFDetector passes the `map`, `ip_addr`, `svc_name`, and `results` variables
| Short name of the language the script is written in.
Choices:
-* jython
-* beanshell
* groovy
+* jython
| none
| bsfEngine
| BSF engine to use when running the script.
Choices:
-* `bsh.util.BeanShellBSFEngine`
* `org.codehaus.groovy.bsf.GroovyEngine`
* `org.apache.bsf.engines.jython.JythonEngine`
| none
@@ -91,15 +92,10 @@ The following table provides the required setup for commonly used languages.
| bsfEngine parameter
| Required library
-| http://www.beanshell.org[BeanShell]
-| beanshell
-| bsh.util.BeanShellBSFEngine
-| supported by default
-
| https://groovy-lang.org/[Groovy]
| groovy
| org.codehaus.groovy.bsf.GroovyEngine
-| groovy-all-[version].jar
+| ships with {page-component-title}
| http://www.jython.org[Jython]
| jython
@@ -107,38 +103,13 @@ The following table provides the required setup for commonly used languages.
| jython-[version].jar
|===
-IMPORTANT: If you are using a Groovy or Jython script, you need to place the required library file in the `$\{OPENNMS_HOME}/lib` directory and restart the {page-component-title} service.
+IMPORTANT: If you are using a Jython script, you need to place the required library file in the `$\{OPENNMS_HOME}/lib` directory and restart the {page-component-title} service.
== Example configurations
-=== BeanShell example
-
-.BeanShell example
-[source, xml]
-----
-
-
-
-
-
-
-----
-
-.BeanShell example `MinimalBeanShell.bsh` script file
-[source, java]
-----
-File testFile = new File("/tmp/TestFile");
-if (testFile.exists()) {
- return "OK";
-} else {
- results.put("reason", "file does not exist");
- return "NOK";
-}
-----
-
=== Groovy example
-The Groovy language requires the installation of an additional library.
+Groovy ships with {page-component-title}, so no additional library is required.
.Groovy example for `run-type` of `eval`
[source, xml]
diff --git a/docs/modules/reference/pages/service-assurance/monitors/BSFMonitor.adoc b/docs/modules/reference/pages/service-assurance/monitors/BSFMonitor.adoc
index f49fa49594ac..cf59f520c7e6 100644
--- a/docs/modules/reference/pages/service-assurance/monitors/BSFMonitor.adoc
+++ b/docs/modules/reference/pages/service-assurance/monitors/BSFMonitor.adoc
@@ -8,6 +8,9 @@ Users can write scripts to perform highly customized service checks.
IMPORTANT: This monitor is not optimized for scale.
It is intended for a small number of custom checks or prototyping of monitors.
+NOTE: Groovy is the scripting language that ships with {page-component-title}.
+BeanShell is no longer supported; rewrite any existing `.bsh` scripts in Groovy and update the `bsf-engine` and `lang-class` parameters of the affected services.
+
== BSFMonitor versus SystemExecuteMonitor
The BSFMonitor avoids the overhead of fork(2) that the SystemExecuteMonitor uses.
@@ -40,7 +43,6 @@ The BSFMonitor also grants access to a selection of {page-component-title} inter
| bsf-engine
| The BSF Engine to run the script in different languages:
-* bsh.util.BeanShellBSFEngine
* org.codehaus.groovy.bsf.GroovyEngine
* org.apache.bsf.engines.jython.JythonEngine
| n/a
@@ -52,7 +54,7 @@ The BSFMonitor also grants access to a selection of {page-component-title} inter
| eval
| lang-class
-| The BSF language class, like `groovy` or `beanshell`
+| The BSF language class, like `groovy`
| filename extension is interpreted by default
| file-extensions
@@ -155,7 +157,7 @@ If the response time should be persisted, add the following parameters:
-
+
@@ -196,15 +198,10 @@ The following table provides the required setup for commonly used languages.
| bsf-engine
| required library
-| http://www.beanshell.org[BeanShell]
-| beanshell
-| `bsh.util.BeanShellBSFEngine`
-| supported by default
-
| https://groovy-lang.org/[Groovy]
| groovy
| `org.codehaus.groovy.bsf.GroovyEngine`
-| `groovy-all-[version].jar`
+| ships with {page-component-title}
| http://www.jython.org[Jython]
| jython
@@ -212,38 +209,9 @@ The following table provides the required setup for commonly used languages.
| `jython-[version].jar`
|===
-== BeanShell example
-
-Note that you must include the `monitor` section for each service in your definition.
-
-.BeanShell example `poller-configuration.xml`
-[source, xml]
-----
-
-
-
-
-
-
-----
-
-.BeanShell example `MinimalBeanShell.bsh` script file
-[source, java]
-----
-bsf_monitor.log("ERROR", "Starting MinimalBeanShell.bsf", null);
-File testFile = new File("/tmp/TestFile");
-if (testFile.exists()) {
- return "OK";
-} else {
- results.put("reason", "file does not exist");
- return "NOK";
-}
-----
-
== Groovy example
-The use of the Groovy language requires an additional library.
-Copy a compatible `groovy-all.jar` into the `$\{OPENNMS_HOME}/lib` folder and restart {page-component-title} to make Groovy available for the BSFMonitor.
+Groovy ships with {page-component-title}, so no additional library is required.
Note that you must include the `monitor` section for each service in your definition.
@@ -259,7 +227,7 @@ Note that you must include the `monitor` section for each service in your defini
----
.Groovy example `MinimalGroovy.groovy` script file for `run-type` set to `eval`
-[source, java]
+[source, groovy]
----
bsf_monitor.log("ERROR", "Starting MinimalGroovy.groovy", null);
File testFile = new File("/tmp/TestFile");
@@ -284,7 +252,7 @@ if (testFile.exists()) {
----
.Groovy example `MinimalGroovy.groovy` script file for `run-type` set to `exec`
-[source, java]
+[source, groovy]
----
bsf_monitor.log("ERROR", "Starting MinimalGroovy", null);
def testFile = new File("/tmp/TestFile");
@@ -362,7 +330,7 @@ For Debian/Ubuntu, use `/var/lib/opennms/rrd/response`.
----
.Groovy example Bean referencing script file
-[source, java]
+[source, groovy]
----
bsf_monitor.log("ERROR", "Starting MinimalGroovy", null);
diff --git a/opennms-alarms/bsf-northbounder/pom.xml b/opennms-alarms/bsf-northbounder/pom.xml
index ed566dff2a14..01dbe2fcf101 100644
--- a/opennms-alarms/bsf-northbounder/pom.xml
+++ b/opennms-alarms/bsf-northbounder/pom.xml
@@ -60,9 +60,12 @@
bsfbsf
+
- org.apache-extras.beanshell
- bsh
+ org.opennms.dependencies
+ groovy-dependencies
+ pom
+ runtime
diff --git a/opennms-alarms/bsf-northbounder/src/main/java/org/opennms/netmgt/alarmd/northbounder/bsf/BSFEngineHandler.java b/opennms-alarms/bsf-northbounder/src/main/java/org/opennms/netmgt/alarmd/northbounder/bsf/BSFEngineHandler.java
index 128ddda5faab..eac132037318 100644
--- a/opennms-alarms/bsf-northbounder/src/main/java/org/opennms/netmgt/alarmd/northbounder/bsf/BSFEngineHandler.java
+++ b/opennms-alarms/bsf-northbounder/src/main/java/org/opennms/netmgt/alarmd/northbounder/bsf/BSFEngineHandler.java
@@ -59,15 +59,15 @@ public class BSFEngineHandler implements Destination {
private String m_filter;
/** The engine language. */
- @XmlElement(name = "language", required = false, defaultValue = "beanshell")
+ @XmlElement(name = "language", required = false, defaultValue = "groovy")
private String m_language;
/** The engine class name. */
- @XmlElement(name = "className", required = false, defaultValue = "bsh.util.BeanShellBSFEngine")
+ @XmlElement(name = "className", required = false, defaultValue = "org.codehaus.groovy.bsf.GroovyEngine")
private String m_className;
/** The engine file extensions. */
- @XmlElement(name = "extensions", required = false, defaultValue = "bsh")
+ @XmlElement(name = "extensions", required = false, defaultValue = "groovy,gy")
private String m_extensions;
/** The on-start content. */
@@ -136,7 +136,7 @@ public void setFilter(String filter) {
* @return the language
*/
public String getLanguage() {
- return m_language == null ? "beanshell" : m_language;
+ return m_language == null ? "groovy" : m_language;
}
/**
@@ -154,7 +154,7 @@ public void setLanguage(String language) {
* @return the class name
*/
public String getClassName() {
- return m_className == null ? "bsh.util.BeanShellBSFEngine" : m_className;
+ return m_className == null ? "org.codehaus.groovy.bsf.GroovyEngine" : m_className;
}
/**
@@ -172,7 +172,7 @@ public void setClassName(String className) {
* @return the extensions
*/
public String getExtensions() {
- return m_extensions == null ? "bsh" : m_extensions;
+ return m_extensions == null ? "groovy,gy" : m_extensions;
}
/**
diff --git a/opennms-alarms/bsf-northbounder/src/main/resources/xsds/bsf-northbounder-configuration.xsd b/opennms-alarms/bsf-northbounder/src/main/resources/xsds/bsf-northbounder-configuration.xsd
index 0296eebb6a46..772b6a87aaf3 100644
--- a/opennms-alarms/bsf-northbounder/src/main/resources/xsds/bsf-northbounder-configuration.xsd
+++ b/opennms-alarms/bsf-northbounder/src/main/resources/xsds/bsf-northbounder-configuration.xsd
@@ -20,9 +20,9 @@
-
-
-
+
+
+
diff --git a/opennms-base-assembly/src/main/filtered/etc/bsf-northbounder-configuration.xml b/opennms-base-assembly/src/main/filtered/etc/bsf-northbounder-configuration.xml
index 92e59b046d0f..e9b07aa31934 100644
--- a/opennms-base-assembly/src/main/filtered/etc/bsf-northbounder-configuration.xml
+++ b/opennms-base-assembly/src/main/filtered/etc/bsf-northbounder-configuration.xml
@@ -10,19 +10,20 @@
ExampleipAddr != '0.0.0.0'
- beanshell
- bsh.util.BeanShellBSFEngine
- bsh
+ groovy
+ org.codehaus.groovy.bsf.GroovyEngine
+ groovy,gy
- log = bsf.lookupBean("log");
- log.info("Starting...");
+ // assign without 'def' so the binding is shared with onStop and onAlarm
+ log = bsf.lookupBean("log")
+ log.info("Starting...")
- log.info("Stopping...");
+ log.info("Stopping...")
- a = bsf.lookupBean("alarm");
- log.info("processing alarm " + a);
+ a = bsf.lookupBean("alarm")
+ log.info("processing alarm " + a)
-->
diff --git a/opennms-base-assembly/src/main/filtered/etc/examples/event-proxy/scriptd-configuration.xml b/opennms-base-assembly/src/main/filtered/etc/examples/event-proxy/scriptd-configuration.xml
index f84957ae993b..bf5d1b5d08ba 100644
--- a/opennms-base-assembly/src/main/filtered/etc/examples/event-proxy/scriptd-configuration.xml
+++ b/opennms-base-assembly/src/main/filtered/etc/examples/event-proxy/scriptd-configuration.xml
@@ -1,52 +1,53 @@
-
-
+
+
- import org.opennms.netmgt.scriptd.ins.events.InsServerListener;
- import org.opennms.netmgt.config.DataSourceFactory;
+ import org.opennms.netmgt.scriptd.ins.events.InsServerListener
+ import org.opennms.netmgt.config.DataSourceFactory
- log = bsf.lookupBean("log");
- log.debug("Starting Script");
+ // assign without 'def' or a type so these stay in the binding shared
+ // with the stop-script and the event-script
+ log = bsf.lookupBean("log")
+ log.debug("Starting Script")
- log.debug("Start TCP PROXY for INS Event ");
+ log.debug("Start TCP PROXY for INS Event ")
+
+ isl = new InsServerListener()
- InsServerListener isl = new InsServerListener();
-
//optional (if not setted, default port (8154) is used)
- //isl.setListeningPort(8152);
-
+ //isl.setListeningPort(8152)
+
//optional (if not setted, no authentication is required)
- //isl.setSharedASCIIString("1234567890");
-
+ //isl.setSharedASCIIString("1234567890")
+
//required properties
- isl.setCriteriaRestriction("eventuei = 'uei.opennms.org/internal/alarms/AlarmRaised' and EXISTS (select 1 from alarms where alarmtype = 1 and severity > 3 and eventoperinstruct = alarmid and eventtime > lasteventtime)");
- isl.start();
+ isl.setCriteriaRestriction("eventuei = 'uei.opennms.org/internal/alarms/AlarmRaised' and EXISTS (select 1 from alarms where alarmtype = 1 and severity > 3 and eventoperinstruct = alarmid and eventtime > lasteventtime)")
+ isl.start()
-
+
- isl.interrupt();
- log.debug("executing a stop script");
+ isl.interrupt()
+ log.debug("executing a stop script")
-
- event = bsf.lookupBean("event");
-
+
+ event = bsf.lookupBean("event")
+
if (
(event.uei.equals("uei.opennms.org/internal/alarms/NotificationAlarm"))
|| (event.uei.equals("uei.opennms.org/internal/alarms/AlarmCleared"))
|| (event.uei.equals("uei.opennms.org/internal/alarms/AlarmRaised"))
)
{
-
- isl.flushEvent(event);
+
+ isl.flushEvent(event)
}
-
-
+
-
+
\ No newline at end of file
diff --git a/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-configuration.xml b/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-configuration.xml
index e13d60c9b3cd..be13de6d1ccb 100644
--- a/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-configuration.xml
+++ b/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-configuration.xml
@@ -1,35 +1,36 @@
-
+
-
+
- import org.opennms.core.utils.InetAddressUtils;
- import org.opennms.netmgt.snmp.SnmpTrapBuilder;
- import org.opennms.netmgt.config.DataSourceFactory;
- import org.opennms.netmgt.utils.SingleResultQuerier;
- import org.opennms.netmgt.events.api.model.IEvent;
+ import org.opennms.core.utils.InetAddressUtils
+ import org.opennms.netmgt.snmp.SnmpTrapBuilder
+ import org.opennms.netmgt.config.DataSourceFactory
+ import org.opennms.netmgt.utils.SingleResultQuerier
+ import org.opennms.netmgt.events.api.model.IEvent
- log = bsf.lookupBean("log");
- snmpTrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpTrapHelper();
+ // assign without 'def' so these stay in the binding shared with the
+ // stop-script, the event-script and the file evaluated below
+ log = bsf.lookupBean("log")
+ snmpTrapHelper = new org.opennms.netmgt.scriptd.helper.SnmpTrapHelper()
+
+ evaluate(new File('${install.dir}/etc/scriptd-event-forwarder.groovy'))
- source("${install.dir}/etc/scriptd-event-forwarder.bsh");
-
-
+
- snmpTrapHelper.stop();
- log.debug("executing a stop script");
+ snmpTrapHelper.stop()
+ log.debug("executing a stop script")
-
+
- event = bsf.lookupBean("event");
- forwardEvent(event);
+ event = bsf.lookupBean("event")
+ forwardEvent(event)
-
-
+
\ No newline at end of file
diff --git a/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-event-forwarder.bsh b/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-event-forwarder.groovy
similarity index 58%
rename from opennms-base-assembly/src/main/filtered/etc/examples/scriptd-event-forwarder.bsh
rename to opennms-base-assembly/src/main/filtered/etc/examples/scriptd-event-forwarder.groovy
index a51cb628d348..8821669604e4 100644
--- a/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-event-forwarder.bsh
+++ b/opennms-base-assembly/src/main/filtered/etc/examples/scriptd-event-forwarder.groovy
@@ -1,119 +1,129 @@
+import org.opennms.core.utils.InetAddressUtils
+import org.opennms.netmgt.events.api.model.IEvent
+import org.opennms.netmgt.snmp.SnmpTrapBuilder
-void forwardEvent(IEvent event) {
+/*
+ * This file is loaded by the scriptd start-script via evaluate(new File(...)).
+ * Note that a Groovy method declared with 'def' would not be visible to the
+ * scripts that run later, so forwardEvent is assigned (without 'def') as a
+ * closure in the shared binding instead. The 'log', 'snmpTrapHelper' and 'bsf'
+ * variables likewise come from that shared binding.
+ */
+forwardEvent = { IEvent event ->
if (event.snmp == null) {
try {
- log.debug("Forwarding an OpenNMS event.");
+ log.debug("Forwarding an OpenNMS event.")
- SnmpTrapBuilder trap = snmpTrapHelper.createV1Trap(".1.3.6.1.4.1.5813.1", "172.20.1.11", 6, 1, 0);
+ SnmpTrapBuilder trap = snmpTrapHelper.createV1Trap(".1.3.6.1.4.1.5813.1", "172.20.1.11", 6, 1, 0)
- /* This sends an enterprise id of ".1.3.6.1.4.1.5813.1" from agent "172.20.1.11" (einstein),
+ /* This sends an enterprise id of ".1.3.6.1.4.1.5813.1" from agent "172.20.1.11" (einstein),
which should be the IP address of the OpenNMS system, generic value of 6 and specific value of 1 */
if (event.hasDbid())
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.1", "OctetString", "text", event.dbid.toString());
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.1", "OctetString", "text", event.dbid.toString())
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.1", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.1", "OctetString", "text", "null")
if (event.distPoller != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.2", "OctetString", "text", event.distPoller);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.2", "OctetString", "text", event.distPoller)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.2", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.2", "OctetString", "text", "null")
if (event.creationTime != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.3", "OctetString", "text", event.creationTime.toString());
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.3", "OctetString", "text", event.creationTime.toString())
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.3", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.3", "OctetString", "text", "null")
if (event.masterStation != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.4", "OctetString", "text", event.masterStation);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.4", "OctetString", "text", event.masterStation)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.4", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.4", "OctetString", "text", "null")
if (event.uei != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.6", "OctetString", "text", event.uei);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.6", "OctetString", "text", event.uei)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.6", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.6", "OctetString", "text", "null")
if (event.source != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.7", "OctetString", "text", event.source);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.7", "OctetString", "text", event.source)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.7", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.7", "OctetString", "text", "null")
if (event.hasNodeid())
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.8", "OctetString", "text", event.nodeid.toString());
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.8", "OctetString", "text", event.nodeid.toString())
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.8", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.8", "OctetString", "text", "null")
if (event.time != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.9", "OctetString", "text", event.time.toString());
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.9", "OctetString", "text", event.time.toString())
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.9", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.9", "OctetString", "text", "null")
if (event.host != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.10", "OctetString", "text", event.host);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.10", "OctetString", "text", event.host)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.10", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.10", "OctetString", "text", "null")
if (event.getInterface() != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.11", "OctetString", "text", event.getInterface());
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.11", "OctetString", "text", event.getInterface())
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.11", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.11", "OctetString", "text", "null")
if (event.snmphost != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.12", "OctetString", "text", event.snmphost);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.12", "OctetString", "text", event.snmphost)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.12", "OctetString", "text", "einstein.example.com");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.12", "OctetString", "text", "einstein.example.com")
/* The snmphost field must be changed to the hostname of the OpenNMS system sending the event */
if (event.service != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.13", "OctetString", "text", event.service);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.13", "OctetString", "text", event.service)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.13", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.13", "OctetString", "text", "null")
if (event.descr != null) {
- descrString = event.descr.replaceAll("<.*>", " ").replaceAll(""", "\"").replaceAll("\\s+", " ");
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.16", "OctetString", "text", descrString);
+ def descrString = event.descr.replaceAll("<.*>", " ").replaceAll(""", "\"").replaceAll("\\s+", " ")
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.16", "OctetString", "text", descrString)
}
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.16", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.16", "OctetString", "text", "null")
if (event.logmsg.content != null) {
- logString = event.logmsg.content.replaceAll("<.*>", " ").replaceAll("\\s+", " ");
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.17", "OctetString", "text", logString);
+ def logString = event.logmsg.content.replaceAll("<.*>", " ").replaceAll("\\s+", " ")
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.17", "OctetString", "text", logString)
}
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.17", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.17", "OctetString", "text", "null")
if (event.severity != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.18", "OctetString", "text", event.severity);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.18", "OctetString", "text", event.severity)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.18", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.18", "OctetString", "text", "null")
if (event.pathoutage != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.19", "OctetString", "text", event.pathoutage);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.19", "OctetString", "text", event.pathoutage)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.19", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.19", "OctetString", "text", "null")
if (event.operinstruct != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.20", "OctetString", "text", event.operinstruct);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.20", "OctetString", "text", event.operinstruct)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.20", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.20", "OctetString", "text", "null")
- String retParmVal = null;
+ String retParmVal = null
if (event.getInterface() != null) {
try {
- retParmVal = InetAddressUtils.addr(event.getInterface()).getHostName();
- } catch (java.net.UnknownHostException e) {
+ retParmVal = InetAddressUtils.addr(event.getInterface()).getHostName()
+ } catch (Exception e) {
}
}
if (retParmVal != null)
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.21", "OctetString", "text", retParmVal);
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.21", "OctetString", "text", retParmVal)
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.21", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.21", "OctetString", "text", "null")
if (event.hasNodeid()) {
- node = bsf.lookupBean("node");
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.22", "OctetString", "text", node.label);
+ def node = bsf.lookupBean("node")
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.22", "OctetString", "text", node.label)
}
else
- snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.22", "OctetString", "text", "null");
+ snmpTrapHelper.addVarBinding(trap, ".1.3.6.1.4.1.5813.2.22", "OctetString", "text", "null")
- trap.send("172.20.1.15", 162, "public");
+ trap.send("172.20.1.15", 162, "public")
/* 172.20.1.15 is the destination for the trap, the port (162) and the community string (usually public for traps) */
- } catch (e) {
- sw = new StringWriter();
- pw = new PrintWriter(sw);
- e.printStackTrace(pw);
- log.debug(sw.toString());
+ } catch (Exception e) {
+ def sw = new StringWriter()
+ def pw = new PrintWriter(sw)
+ e.printStackTrace(pw)
+ log.debug(sw.toString())
}
}
-}
+}
\ No newline at end of file
diff --git a/opennms-base-assembly/src/main/filtered/etc/scriptd-configuration.xml b/opennms-base-assembly/src/main/filtered/etc/scriptd-configuration.xml
index 5e7bd5a2f350..46311e16a18f 100644
--- a/opennms-base-assembly/src/main/filtered/etc/scriptd-configuration.xml
+++ b/opennms-base-assembly/src/main/filtered/etc/scriptd-configuration.xml
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/opennms-config-model/src/test/java/org/opennms/netmgt/config/scriptd/ScriptdConfigurationTest.java b/opennms-config-model/src/test/java/org/opennms/netmgt/config/scriptd/ScriptdConfigurationTest.java
index b8b666819fe9..e754d68b554d 100644
--- a/opennms-config-model/src/test/java/org/opennms/netmgt/config/scriptd/ScriptdConfigurationTest.java
+++ b/opennms-config-model/src/test/java/org/opennms/netmgt/config/scriptd/ScriptdConfigurationTest.java
@@ -40,10 +40,10 @@ public static Collection
+ * are discovered from the classpath; Groovy ships with OpenNMS. BeanShell
+ * was removed along with BSF, so {@code .bsh} scripts no longer resolve to
+ * an engine and must be rewritten in Groovy.
*
* @author Jeff Gehlbach
* @author interpreterClass = Class.forName("bsh.Interpreter");
- final Object interpreter = interpreterClass.getDeclaredConstructor().newInstance();
- final java.lang.reflect.Method set = interpreterClass.getMethod("set", String.class, Object.class);
- for (Map.Entry entry : bindings.entrySet()) {
- set.invoke(interpreter, entry.getKey(), entry.getValue());
- }
- return interpreterClass.getMethod("eval", String.class).invoke(interpreter, source);
- } catch (java.lang.reflect.InvocationTargetException e) {
- final Throwable cause = e.getCause() != null ? e.getCause() : e;
- final ScriptException scriptException = new ScriptException(cause.getMessage());
- scriptException.initCause(cause);
- throw scriptException;
- } catch (ReflectiveOperationException e) {
- final ScriptException scriptException = new ScriptException("BeanShell interpreter not available: " + e);
- scriptException.initCause(e);
- throw scriptException;
- }
- }
-
private void warnAboutDeprecatedSwitches() {
if (getBsfEngine() != null || getFileExtensions() != null) {
LOG.warn("The 'bsf-engine' and 'file-extensions' switches are no longer supported now that the BSF notification strategy runs on JSR-223; they are ignored. Use 'lang-class' or the script file extension to select the engine.");
diff --git a/opennms-services/src/main/java/org/opennms/netmgt/scriptd/helper/SnmpTrapHelper.java b/opennms-services/src/main/java/org/opennms/netmgt/scriptd/helper/SnmpTrapHelper.java
index fffbe136d8c7..515a1c24bdc8 100644
--- a/opennms-services/src/main/java/org/opennms/netmgt/scriptd/helper/SnmpTrapHelper.java
+++ b/opennms-services/src/main/java/org/opennms/netmgt/scriptd/helper/SnmpTrapHelper.java
@@ -48,7 +48,7 @@
* forwarding SNMP traps. This class was created in order to make it easier to
* write simple scripts to generate traps based on events or to forward traps,
* using scripting languages that are able to access Java classes (such as
- * BeanShell).
+ * Groovy).
*
* @author Jim Doble
* @author OpenNMS.org
diff --git a/opennms-services/src/test/java/org/opennms/netmgt/notifd/BSFNotificationStrategyIT.java b/opennms-services/src/test/java/org/opennms/netmgt/notifd/BSFNotificationStrategyIT.java
index 7d30f47f40a2..e01c716e959e 100644
--- a/opennms-services/src/test/java/org/opennms/netmgt/notifd/BSFNotificationStrategyIT.java
+++ b/opennms-services/src/test/java/org/opennms/netmgt/notifd/BSFNotificationStrategyIT.java
@@ -95,19 +95,19 @@ public void tearDown() {
}
/**
- * Verifies that we can invoke a BSH script and that
+ * Verifies that we can invoke a Groovy script and that
* the an instance of the appropriate OnmsNode object is
* passed to the script.
*/
@Test
public void canUseNodeInScript() throws IOException {
- // Create a simple BSH script that verifies the node bean
- File notifyBsh = tempFolder.newFile("notify.bsh");
- FileUtils.write(notifyBsh, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\");");
+ // Create a simple Groovy script that verifies the node bean
+ File notifyScript = tempFolder.newFile("notify.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\")");
List arguments = new ArrayList<>();
// Point to our script
- arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
// Reference node 1
arguments.add(new Argument(NotificationManager.PARAM_NODE, null, node1Id(), false));
@@ -116,19 +116,49 @@ public void canUseNodeInScript() throws IOException {
}
@Test
- public void canEvalBeanshellScript() throws IOException {
+ public void canEvalScript() throws IOException {
// Under run-type=eval the script's last expression becomes the status
- File notifyBsh = tempFolder.newFile("notify-eval.bsh");
- FileUtils.write(notifyBsh, "node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\";");
+ File notifyScript = tempFolder.newFile("notify-eval.groovy");
+ FileUtils.write(notifyScript, "node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\"");
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
arguments.add(new Argument("run-type", null, "eval", false));
arguments.add(new Argument(NotificationManager.PARAM_NODE, null, node1Id(), false));
assertEquals(0, bsfNotificationStrategy.send(arguments));
}
+ @Test
+ public void beanshellScriptIsNoLongerSupported() throws IOException {
+ // BeanShell was removed along with BSF; a .bsh script must now fail
+ // cleanly with a diagnosable message rather than silently doing nothing
+ File notifyBsh = tempFolder.newFile("notify-legacy.bsh");
+ FileUtils.write(notifyBsh, "results.put(\"status\", \"OK\");");
+
+ List arguments = new ArrayList<>();
+ arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
+
+ assertEquals(-1, bsfNotificationStrategy.send(arguments));
+ MockLogAppender.assertLogMatched(Level.ERROR, "No JSR-223 script engine found");
+ MockLogAppender.resetState();
+ }
+
+ @Test
+ public void beanshellLangClassIsNoLongerSupported() throws IOException {
+ // likewise for an explicit lang-class left over from a BSF-era config
+ File notifyScript = tempFolder.newFile("notify-legacy-lang.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", \"OK\")");
+
+ List arguments = new ArrayList<>();
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
+ arguments.add(new Argument("lang-class", null, "beanshell", false));
+
+ assertEquals(-1, bsfNotificationStrategy.send(arguments));
+ MockLogAppender.assertLogMatched(Level.ERROR, "No JSR-223 script engine found");
+ MockLogAppender.resetState();
+ }
+
@Test
public void canUseGroovyScript() throws IOException {
File notifyGroovy = tempFolder.newFile("notify.groovy");
@@ -158,25 +188,26 @@ public void canResolveGyExtensionAsGroovy() throws IOException {
public void canResolveEngineViaLangClass() throws IOException {
// No usable extension; lang-class selects the engine by JSR-223 name
File notifyTxt = tempFolder.newFile("notify.txt");
- FileUtils.write(notifyTxt, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\");");
+ FileUtils.write(notifyTxt, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\")");
List arguments = new ArrayList<>();
arguments.add(new Argument("file-name", null, notifyTxt.getAbsolutePath(), false));
- arguments.add(new Argument("lang-class", null, "beanshell", false));
+ arguments.add(new Argument("lang-class", null, "groovy", false));
arguments.add(new Argument(NotificationManager.PARAM_NODE, null, node1Id(), false));
assertEquals(0, bsfNotificationStrategy.send(arguments));
}
@Test
- public void nullVariablesStayDefinedInBeanshell() throws IOException {
- // no -nodeid argument: node, node_label, foreign_source etc. are null;
- // BSF defined them as null and scripts test them against null
- File notifyBsh = tempFolder.newFile("notify-null.bsh");
- FileUtils.write(notifyBsh, "results.put(\"status\", (node == null && foreign_source == null) ? \"OK\" : \"NOT_OK\");");
+ public void nullVariablesStayDefined() throws IOException {
+ // no -nodeid argument: node, node_label, foreign_source etc. are null.
+ // They must still be *defined* so scripts can test them against null,
+ // rather than failing with a missing-property error.
+ File notifyScript = tempFolder.newFile("notify-null.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", (node == null && foreign_source == null) ? \"OK\" : \"NOT_OK\")");
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
assertEquals(0, bsfNotificationStrategy.send(arguments));
}
@@ -185,11 +216,11 @@ public void nullVariablesStayDefinedInBeanshell() throws IOException {
public void fileNameFromSubstitutionWorks() throws IOException {
// notifd passes "" as the value for switches without a notification
// parameter; the command's must be honored then
- File notifyBsh = tempFolder.newFile("notify-subst.bsh");
- FileUtils.write(notifyBsh, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\");");
+ File notifyScript = tempFolder.newFile("notify-subst.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\")");
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", notifyBsh.getAbsolutePath(), "", false));
+ arguments.add(new Argument("file-name", notifyScript.getAbsolutePath(), "", false));
arguments.add(new Argument(NotificationManager.PARAM_NODE, null, node1Id(), false));
assertEquals(0, bsfNotificationStrategy.send(arguments));
@@ -230,7 +261,7 @@ public void editedGroovyScriptIsRecompiled() throws IOException {
@Test
public void missingScriptFileReturnsFailure() {
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", null, new File(tempFolder.getRoot(), "no-such-script.bsh").getAbsolutePath(), false));
+ arguments.add(new Argument("file-name", null, new File(tempFolder.getRoot(), "no-such-script.groovy").getAbsolutePath(), false));
assertEquals(-1, bsfNotificationStrategy.send(arguments));
MockLogAppender.assertLogMatched(Level.WARN, "Cannot locate or read script file");
@@ -239,11 +270,11 @@ public void missingScriptFileReturnsFailure() {
@Test
public void nonOkStatusReturnsFailure() throws IOException {
- File notifyBsh = tempFolder.newFile("notify-nok.bsh");
- FileUtils.write(notifyBsh, "results.put(\"status\", \"NOT_OK\");");
+ File notifyScript = tempFolder.newFile("notify-nok.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", \"NOT_OK\")");
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
assertEquals(-1, bsfNotificationStrategy.send(arguments));
MockLogAppender.assertLogMatched(Level.WARN, "did not indicate successful notification");
@@ -252,11 +283,11 @@ public void nonOkStatusReturnsFailure() throws IOException {
@Test
public void invalidRunTypeReturnsFailure() throws IOException {
- File notifyBsh = tempFolder.newFile("notify-bogus.bsh");
- FileUtils.write(notifyBsh, "results.put(\"status\", \"OK\");");
+ File notifyScript = tempFolder.newFile("notify-bogus.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", \"OK\")");
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
arguments.add(new Argument("run-type", null, "bogus", false));
assertEquals(-1, bsfNotificationStrategy.send(arguments));
@@ -266,12 +297,13 @@ public void invalidRunTypeReturnsFailure() throws IOException {
@Test
public void deprecatedBsfEngineSwitchWarnsButStillWorks() throws IOException {
- File notifyBsh = tempFolder.newFile("notify-deprecated.bsh");
- FileUtils.write(notifyBsh, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\");");
+ File notifyScript = tempFolder.newFile("notify-deprecated.groovy");
+ FileUtils.write(notifyScript, "results.put(\"status\", node.id == " + node1Id() + " ? \"OK\" : \"NOT_OK\")");
List arguments = new ArrayList<>();
- arguments.add(new Argument("file-name", null, notifyBsh.getAbsolutePath(), false));
- arguments.add(new Argument("lang-class", null, "beanshell", false));
+ arguments.add(new Argument("file-name", null, notifyScript.getAbsolutePath(), false));
+ arguments.add(new Argument("lang-class", null, "groovy", false));
+ // a genuine BSF-era value: it must be ignored, not acted on
arguments.add(new Argument("bsf-engine", null, "bsh.util.BeanShellBSFEngine", false));
arguments.add(new Argument("file-extensions", null, "bsh", false));
arguments.add(new Argument(NotificationManager.PARAM_NODE, null, node1Id(), false));
diff --git a/pom.xml b/pom.xml
index 99df2b1b9915..f2b052d72f50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1246,7 +1246,8 @@
asm:*org.ow2.asm:asm-allbouncycastle:*
- bsh:*
+ bsh:*
+ org.apache-extras.beanshell:*c3p0:c3p0commons-logging:*com.github.detro:*
@@ -3980,11 +3981,6 @@
-
- org.apache-extras.beanshell
- bsh
- 2.0b6
- org.mongodbbson