package com.newrelic.apm.enterprise; import com.google.inject.Inject; import com.google.inject.name.Named; import com.newrelic.apm.enterprise.bmc.ImpactManager; import com.newrelic.apm.enterprise.bmc.ImpactManager_Service; import com.newrelic.apm.enterprise.http.HttpClient; import com.newrelic.apm.enterprise.log.Log; import io.iron.ironmq.Message; import io.iron.ironmq.Queue; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.SimpleBindings; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Map; public class EventConsumer implements Runnable { private static final Log LOG = new Log(); private final CloseableHttpClient httpClient; private Thread thread = new Thread(this, "Event Consumer"); private boolean running = false; private Queue queue; private int timeout; private File root; private String updateKey; private Map<String, String> config; private final ScriptEngineManager scriptEngineManager; @Inject public EventConsumer(Queue queue, @Named("ironmq.timeout") int timeout, @Named("root") File root, @Named("remote.scriptUpdateKey") String updateKey, @Named("config") Map<String, String> config) { this.queue = queue; this.timeout = timeout; this.root = root; this.updateKey = updateKey; this.config = config; scriptEngineManager = new ScriptEngineManager(); httpClient = HttpClientBuilder.create().build(); } public void start() { LOG.info("Starting queue consumer..."); thread.start(); } public void stop() { running = false; thread.interrupt(); } @Override public void run() { running = true; while (running) { try { Message message = queue.get(1, timeout).getMessage(0); processMessage(message); queue.deleteMessage(message); } catch (IndexOutOfBoundsException e) { try { Thread.sleep(10 * 1000); } catch (InterruptedException ie) { return; } } catch (IOException e) { LOG.warn("Unable to connect to queue, will wait 60 seconds and try again", e); try { Thread.sleep(60 * 1000); } catch (InterruptedException ie) { return; } } catch (Exception e) { LOG.severe("Unable to process message, will keep trying", e); } } } private void processMessage(Message message) throws URISyntaxException, ScriptException, IOException { String body = message.getBody(); if (updateKey != null && !"".equals(updateKey) && body.startsWith("// script update: " + updateKey)) { // save the script LOG.info("Receiving updated script; writing to script.js now"); body = body.substring(body.indexOf('\n') + 1); try (FileWriter writer = new FileWriter(new File(root, "script.js"))) { writer.write(body); } return; } if (body.startsWith("alert=")) { body = new URI(body.substring(6)).getPath(); } else if (body.startsWith("deployment=")) { body = new URI(body.substring(11)).getPath(); } LOG.info("Received alert event, processing"); ScriptEngine engine = scriptEngineManager.getEngineByName("JavaScript"); SimpleBindings bindings = new SimpleBindings(); bindings.put("eventStr", body); Object eventJson = engine.eval("JSON.parse(eventStr)", bindings); engine.put("event", eventJson); Log log = new Log("script.js"); engine.put("log", log); engine.put("console", new Console(log)); engine.put("utils", new Utils()); engine.put("http", new HttpClient(httpClient, engine)); engine.put("config", config); if (config.containsKey("bmc.wsdl")) { // todo: this probably should be a singleton/cached, but then again I guess most of the stuff above should too! String wsdl = config.get("bmc.wsdl"); ImpactManager_Service impactManagerService = new ImpactManager_Service(new URL(wsdl)); ImpactManager impactManager = impactManagerService.getImpactManager(); engine.put("impactManager", impactManager); } engine.eval(new FileReader(new File(root, "script.js"))); } }