blob: c6b8e7dfc7229e77ecfef1cb4aba4388cd06393c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The SF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.felix.hc.generalchecks.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.hc.api.FormattingResultLog;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.ResultLog;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
/** Script Helper to simplify interaction with scripting engines in OSGi context. Used by ScriptedHealthCheck and can be used by other custom checks that want to allow to evaluate expressions via a scripting engine. */
public class ScriptHelper {
public String getFileContents(String url) {
String content;
try {
URLConnection conn = new URL(url).openConnection();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
content = reader.lines().collect(Collectors.joining("\n"));
}
return content;
}catch(IOException e) {
throw new IllegalArgumentException("Could not read URL "+url+": "+e, e);
}
}
public ScriptEngine getScriptEngine(ScriptEnginesTracker scriptEnginesTracker, String language) {
ScriptEngine scriptEngine = scriptEnginesTracker.getEngineByLanguage(language);
if(scriptEngine == null) {
throw new IllegalArgumentException("No ScriptEngineFactory found for language "+ language + " (available languages: "+scriptEnginesTracker.getLanguagesByBundle()+")");
}
return scriptEngine;
}
public Object evalScript(BundleContext bundleContext, ScriptEngine scriptEngine, String scriptToExecute, FormattingResultLog log, Map<String,Object> additionalBindings, boolean logScriptResult) throws ScriptException, IOException {
final Bindings bindings = new SimpleBindings();
final ScriptHelperBinding scriptHelper = new ScriptHelperBinding(bundleContext);
StringWriter stdout = new StringWriter();
StringWriter stderr = new StringWriter();
bindings.put("scriptHelper", scriptHelper);
bindings.put("osgi", scriptHelper); // also register script helper like in web console script console
bindings.put("log", log);
bindings.put("bundleContext", bundleContext);
if (additionalBindings != null) {
for (Map.Entry<String, Object> additionalBinding : additionalBindings.entrySet()) {
bindings.put(additionalBinding.getKey(), additionalBinding.getValue());
}
}
SimpleScriptContext scriptContext = new SimpleScriptContext();
scriptContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
scriptContext.setWriter(stdout);
scriptContext.setErrorWriter(stderr);
try {
log.debug(scriptToExecute);
Object scriptResult = scriptEngine.eval(scriptToExecute, scriptContext);
appendStreamsToResult(log, stdout, stderr, scriptContext);
if(scriptResult instanceof Result) {
Result result = (Result) scriptResult;
for(ResultLog.Entry entry: result) {
log.add(entry);
}
} else if(scriptResult != null && logScriptResult){
log.info("Script result: {}", scriptResult);
}
return scriptResult;
} finally {
scriptHelper.ungetServices();
}
}
private void appendStreamsToResult(FormattingResultLog log, StringWriter stdout, StringWriter stderr, SimpleScriptContext scriptContext)
throws IOException {
scriptContext.getWriter().flush();
String stdoutStr = stdout.toString();
if(StringUtils.isNotBlank(stdoutStr)) {
log.info("stdout of script: {}", stdoutStr);
}
scriptContext.getErrorWriter().flush();
String stderrStr = stderr.toString();
if(StringUtils.isNotBlank(stderrStr)) {
log.critical("stderr of script: {}", stderrStr);
}
}
// Script Helper for OSGi available as binding 'scriptHelper'
class ScriptHelperBinding {
private final BundleContext bundleContext;
private List<ServiceReference<?>> references;
private Map<String, Object> services;
public ScriptHelperBinding(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
@SuppressWarnings("unchecked")
public <ServiceType> ServiceType getService(Class<ServiceType> type) {
ServiceType service = (this.services == null ? null : (ServiceType) this.services.get(type.getName()));
if (service == null) {
final ServiceReference<?> ref = this.bundleContext.getServiceReference(type.getName());
if (ref != null) {
service = (ServiceType) this.bundleContext.getService(ref);
if (service != null) {
if (this.services == null) {
this.services = new HashMap<String, Object>();
}
if (this.references == null) {
this.references = new ArrayList<ServiceReference<?>>();
}
this.references.add(ref);
this.services.put(type.getName(), service);
}
}
}
return service;
}
public <T> T[] getServices(Class<T> serviceType, String filter) throws InvalidSyntaxException {
final ServiceReference<?>[] refs = this.bundleContext.getServiceReferences(serviceType.getName(), filter);
T[] result = null;
if (refs != null) {
final List<T> objects = new ArrayList<T>();
for (int i = 0; i < refs.length; i++) {
@SuppressWarnings("unchecked")
final T service = (T) this.bundleContext.getService(refs[i]);
if (service != null) {
if (this.references == null) {
this.references = new ArrayList<ServiceReference<?>>();
}
this.references.add(refs[i]);
objects.add(service);
}
}
if (objects.size() > 0) {
@SuppressWarnings("unchecked")
T[] srv = (T[]) Array.newInstance(serviceType, objects.size());
result = objects.toArray(srv);
}
}
return result;
}
public void ungetServices() {
if (this.references != null) {
final Iterator<ServiceReference<?>> i = this.references.iterator();
while (i.hasNext()) {
final ServiceReference<?> ref = i.next();
this.bundleContext.ungetService(ref);
}
this.references.clear();
}
if (this.services != null) {
this.services.clear();
}
}
}
}