/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under 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 ASF 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 com.streamsets.datacollector.el;
import com.streamsets.datacollector.http.WebServerTask;
import com.streamsets.datacollector.main.RuntimeInfo;
import com.streamsets.datacollector.util.Configuration;
import com.streamsets.pipeline.api.ElConstant;
import com.streamsets.pipeline.api.ElFunction;
import com.streamsets.pipeline.api.ElParam;
import com.streamsets.pipeline.api.impl.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Collections;
import java.util.Properties;
import java.util.Set;
public class RuntimeEL {
private static final Logger LOG = LoggerFactory.getLogger(RuntimeEL.class);
private static final String SDC_PROPERTIES = "sdc.properties";
private static final String RUNTIME_CONF_LOCATION_KEY = "runtime.conf.location";
private static final String RUNTIME_CONF_LOCATION_DEFAULT = "embedded";
private static final String RUNTIME_CONF_PREFIX = "runtime.conf_";
private static Properties RUNTIME_CONF_PROPS = null;
private static String AUTH_TOKEN = null;
private static String HOSTNAME = null;
private static RuntimeInfo runtimeInfo;
@ElConstant(name = "NULL", description = "NULL value")
public static final Object NULL = null;
private RuntimeEL() {}
@ElFunction(
prefix = "runtime",
name = "conf",
description = "Retrieves the value of the config property from sdc runtime configuration")
public static String conf(
@ElParam("conf") String conf) throws IOException {
String value = null;
if(RUNTIME_CONF_PROPS != null) {
value = RUNTIME_CONF_PROPS.getProperty(conf);
}
if(value == null) {
//Returning a null value instead of throwing an exception results in coercion of the value to the expected
//return type. This leads to counter intuitive validation error messages.
throw new IllegalArgumentException(Utils.format("Could not resolve property '{}'", conf).toString());
}
return value;
}
@ElFunction(prefix = "runtime", name = "loadResource",
description = "Loads the contents of a file under the Data Collector resources directory. " +
"If restricted is set to 'true', the file must be readable only by its owner."
)
public static String loadResource
(
@ElParam("fileName") String fileName,
@ElParam("restricted") boolean restricted) {
String resource = null;
try {
if (fileName != null && !fileName.isEmpty()) {
File file = new File(runtimeInfo.getResourcesDir(), fileName);
if (file.exists() && file.isFile()) {
if (restricted) {
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file.toPath());
if (permissions.contains(PosixFilePermission.GROUP_READ) ||
permissions.contains(PosixFilePermission.OTHERS_READ) ||
permissions.contains(PosixFilePermission.GROUP_WRITE) ||
permissions.contains(PosixFilePermission.OTHERS_WRITE)) {
throw new IllegalArgumentException(Utils.format("File '{}' should be owner read/write only", file));
}
}
}
byte[] bytes = Files.readAllBytes(file.toPath());
resource = new String(bytes, StandardCharsets.UTF_8);
}
} catch (IllegalArgumentException ex) {
throw ex;
} catch (Exception ex) {
LOG.warn("Could not load resource '{}': {}", fileName, ex.toString(), ex);
}
return resource;
}
public static void loadRuntimeConfiguration(RuntimeInfo runtimeInfo) throws IOException {
RuntimeEL.runtimeInfo = runtimeInfo;
/*
The sdc.properties file has a property 'runtime.conf.location' with possible values - 'embedded' or 'properties
file name'.
If the value is 'embedded', the runtime configuration properties will be read from the sdc.properties,
from all properties prefixed with 'runtime.conf_'.
The property names will be trimmed out of the 'runtime.conf_' prefix.
If the value is a properties file, the file will be looked up in etc/ and loaded as the runtime conf properties,
no property trimming there.
*/
Configuration configuration = new Configuration();
File configFile = new File(runtimeInfo.getConfigDir(), SDC_PROPERTIES);
if (configFile.exists()) {
try(FileReader reader = new FileReader(configFile)) {
configuration.load(reader);
} catch (IOException ex) {
LOG.error("Error loading configuration file {} : {}", configFile, ex.getMessage());
throw new RuntimeException(ex);
}
AUTH_TOKEN = runtimeInfo.getAppAuthToken();
HOSTNAME = configuration.get(WebServerTask.HTTP_BIND_HOST, InetAddress.getLocalHost().getCanonicalHostName());
RUNTIME_CONF_PROPS = new Properties();
String runtimeConfLocation = configuration.get(RUNTIME_CONF_LOCATION_KEY, RUNTIME_CONF_LOCATION_DEFAULT);
if(runtimeConfLocation.equals(RUNTIME_CONF_LOCATION_DEFAULT)) {
//runtime configuration is embedded in sdc.properties file. Find, trim, cache.
for(String confName : configuration.getNames()) {
if(confName.startsWith(RUNTIME_CONF_PREFIX)) {
RUNTIME_CONF_PROPS.put(confName.substring(RUNTIME_CONF_PREFIX.length()).trim(),
configuration.get(confName, null));
}
}
} else {
File runtimeConfFile = new File(runtimeInfo.getConfigDir(), runtimeConfLocation);
try (FileInputStream fileInputStream = new FileInputStream(runtimeConfFile)) {
RUNTIME_CONF_PROPS.load(fileInputStream);
} catch (IOException e) {
LOG.error("Could not read '{}': {}", runtimeConfFile.getAbsolutePath(), e.toString(), e);
throw e;
}
}
} else {
LOG.error("Error did not find sdc.properties at expected location: {}", configFile);
}
}
@ElFunction(
prefix = "sdc",
name = "authToken",
description = "Returns the auth token of this data collector")
public static String authToken() {
return AUTH_TOKEN;
}
public static Set<Object> getRuntimeConfKeys() {
return (RUNTIME_CONF_PROPS != null) ? RUNTIME_CONF_PROPS.keySet() : Collections.emptySet();
}
@ElFunction(
prefix = "sdc",
name = "hostname",
description = "Return hostname where SDC runs")
public static String hostname() throws UnknownHostException {
return HOSTNAME;
}
@ElFunction(
prefix = "runtime",
name = "availableProcessors",
description = "Returns the number of CPU cores as reported by Java"
)
public static int availableProcessors() {
return Runtime.getRuntime().availableProcessors();
}
}