Binaries.java [src/csip/utils] Revision: 5361f9568617edb3e9d14a5770e76c883ce9f96c Date: Mon Apr 04 10:46:52 MDT 2016
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* 2010-2013, Olaf David and others, Colorado State University.
*
* CSIP is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1.
*
* CSIP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OMS. If not, see <http://www.gnu.org/licenses/lgpl.txt>.
*/
package csip.utils;
import csip.Config;
import csip.Executable;
import csip.ServiceException;
import csip.SessionLogger;
import csip.annotations.Resource;
import csip.annotations.ResourceType;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLConnection;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.pool.PoolProperties;
/**
*
* @author od
*/
public class Binaries {
public static final String STDOUT = "-stdout.txt";
public static final String STDERR = "-stderr.txt";
private static class JDBCPool {
DataSource datasource;
String url;
String resolved_url = "";
Map<String, String> env;
JDBCPool(String url, Map<String, String> env) {
this.url = url;
this.env = env;
}
Connection getConnection(SessionLogger logger) throws ServiceException {
try {
// probe for configuration change.
String res_url = resolve(url);
if (datasource == null || !res_url.equals(resolved_url)) {
//Close previous datasource if it exists
if (datasource != null) {
shutdown();
}
//Remember this connection string in case it has changed.
resolved_url = res_url;
PoolProperties p = new PoolProperties();
defaultProperties(p);
p.setUrl(resolved_url);
logger.info(" JDBC pool property: url:" + resolved_url);
// load the driver
if (resolved_url.toLowerCase().contains(":postgres:") && !env.containsKey("driverClassName")) {
env.put("driverClassName", "org.postgresql.Driver");
}
if (resolved_url.toLowerCase().contains(":sqlserver:") && !env.containsKey("driverClassName")) {
env.put("driverClassName", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
}
// overwrite the defaults.
for (String key : env.keySet()) {
BeanUtils.setProperty(p, key, env.get(key));
logger.info("env JDBC pool property: " + key + ": " + env.get(key));
}
logger.info("JDBC Pool properties:" + p.toString());
datasource = new org.apache.tomcat.jdbc.pool.DataSource(p);
// datasource.setLoginTimeout();
logger.info("Created JDBC datasource: " + datasource.toString());
}
return datasource.getConnection();
} catch (IllegalAccessException | InvocationTargetException | SQLException ex) {
logger.log(Level.SEVERE, null, ex);
throw new ServiceException("Unable to connect.. Please check the configuration parameter");
}
}
private void defaultProperties(PoolProperties p) {
p.setDefaultAutoCommit(false);
p.setJmxEnabled(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(600);
p.setRemoveAbandoned(true);
}
void shutdown() {
if (datasource != null && datasource instanceof DataSourceProxy) {
((DataSourceProxy) datasource).close(true);
System.out.println("Closed datasource " + datasource.toString());
}
datasource = null;
}
}
/**
* Map of JDBCPools.
*/
private static final Map<String, JDBCPool> jdbcPools = new HashMap<>();
private static final Resource[] NO_RES = new Resource[0];
/**
* Shuts down all JDBC pools.
*/
public static void shutdownJDBC() {
synchronized (jdbcPools) {
for (JDBCPool pool : jdbcPools.values()) {
pool.shutdown();
}
jdbcPools.clear();
}
}
/**
* Get the architecture
* @return
*/
public static String getArch() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("bsd")) {
os = "bsd";
} else {
os = os.substring(0, 3);
}
return os + "-" + System.getProperty("os.arch").toLowerCase();
}
private static Executable asExecutable(final Execution p1, final File workspace, final SessionLogger LOG) {
return new Executable() {
{
try {
p1.redirectOutput(new FileWriter(stdout()));
p1.redirectError(new FileWriter(stderr()));
} catch (IOException ex) {
LOG.log(Level.WARNING, null, ex);
}
p1.setWorkingDirectory(workspace);
p1.setLogger(LOG);
}
@Override
public File stdout() {
return new File(workspace, p1.getFile().getName() + STDOUT);
}
@Override
public File stderr() {
return new File(workspace, p1.getFile().getName() + STDERR);
}
@Override
public String getName() {
return p1.getFile().toString();
}
@Override
public void setArguments(Object... args) {
List<Object> l = new ArrayList<>();
Object[] o = p1.getArguments();
if (o.length > 0 && o[0] instanceof String && p1.getFile().getName().contains("wine")) {
// keep the first argument, if execution is wine.
l.add(o[0]);
}
l.addAll(Arrays.asList(args));
p1.setArguments(l);
}
@Override
public void addArguments(Object... args) {
List<Object> l = new ArrayList<>();
l.addAll(Arrays.asList(p1.getArguments()));
l.addAll(Arrays.asList(args));
p1.setArguments(l);
}
@Override
public Object[] getArguments() {
return p1.getArguments();
}
@Override
public Map<String, String> environment() {
return p1.environment();
}
@Override
public int exec() throws IOException {
int ret = p1.exec();
if (ret != 0 && LOG.isLoggable(Level.INFO) && stderr().exists()) {
String s = FileUtils.readFileToString(stderr());
LOG.severe(s);
}
return ret;
}
@Override
public void redirectDefaults() throws IOException {
p1.redirectOutput(new FileWriter(stdout(), true));
p1.redirectError(new FileWriter(stderr(), true));
}
@Override
public void redirectOutput(String file) throws IOException {
if (file == null) {
throw new IOException("missing file name for redirect.");
}
p1.redirectOutput(new FileWriter(new File(workspace, file)));
}
@Override
public void redirectError(String file) throws IOException {
if (file == null) {
throw new IOException("missing file name for redirect.");
}
p1.redirectError(new FileWriter(new File(workspace, file)));
}
@Override
public void redirectOutput(StringWriter w) throws IOException {
p1.redirectOutput(w);
}
@Override
public void redirectError(StringWriter w) throws IOException {
p1.redirectError(w);
}
};
}
/**
* Get a file resources.
* @param c
* @param id
* @return
*/
public static File getResourceFile(Class<?> c, String id) throws ServiceException {
Resource resource = getResourceById(c, id);
if (resource == null) {
throw new ServiceException("No such resource: '" + id + "'");
}
String file = resource.file();
if (file == null || file.isEmpty()) {
return null;
}
File csip_home = new File(Config.getString("csip.dir"));
file = replaceArch(file);
if (file.endsWith("zip") && resource.type() == ResourceType.ARCHIVE) {
return new File(csip_home, file.substring(0, file.indexOf('.')));
}
File f = new File(csip_home, file);
if (!f.exists() || !f.canRead()) {
throw new ServiceException("Cannot find file: '" + file + "'");
}
return f;
}
/**
* Get the resource as java file.
*
* @param c
* @param id
* @param workspace
* @param jars
* @param LOG
* @return
* @throws ServiceException
*/
public static Executable getResourceJava(Class<?> c, String id, final File workspace,
List<File> jars, SessionLogger LOG) throws ServiceException {
Resource r = getResourceById(c, id);
if (r == null || r.type() != ResourceType.CLASSNAME) {
throw new ServiceException("Not found: " + id);
}
String oms_java_home = Config.getString("oms.java.home", "/opt/jdk1.7.0_45");
Execution p = new Execution(new File(oms_java_home, "/bin/java"));
p.setArguments(//r[0].args().split("\\s+"), // no jvmoptions
"-cp", Binaries.asClassPath(jars),
r.file(),
r.args().split("\\s+")
);
if (r.env().length > 0) {
p.environment().putAll(parseEnv(r.env()));
}
return asExecutable(p, workspace, LOG);
}
/**
*
* @param c
* @param id
* @param workspace
* @param jars
* @param LOG
* @return
* @throws ServiceException
*/
public static Executable getResourceOMSDSL(Class<?> c, String id,
final File workspace, List<File> jars, SessionLogger LOG) throws ServiceException {
Resource r = getResourceById(c, id);
if (r == null || r.type() != ResourceType.OMS_DSL) {
throw new ServiceException("Not found: " + id);
}
String oms_java_home = Config.getString("oms.java.home", "/opt/jdk1.7.0_45");
Execution p = new Execution(new File(oms_java_home, "/bin/java"));
p.setArguments(r.args().split("\\s+"), // jvmoptions
"-cp", asClassPath(jars),
"oms3.CLI",
"-r", r.file()
);
if (r.env().length > 0) {
p.environment().putAll(parseEnv(r.env()));
}
return asExecutable(p, workspace, LOG);
}
/**
* Get the JDBC connection from a resource definition.
*
* @param c
* @param id
* @param LOG
* @return
* @throws ServiceException
*/
public static Connection getRessourceJDBC(Class<?> c, String id, SessionLogger LOG) throws ServiceException {
synchronized (jdbcPools) {
JDBCPool p = jdbcPools.get(id);
if (p == null) {
Resource r = getResourceById(c, id);
if (r != null) {
if (r.type() == ResourceType.JDBC) {
String url = r.file();
if (url != null && !url.isEmpty()) {
Map<String, String> env = parseEnv(r.env());
p = new JDBCPool(url, env);
jdbcPools.put(id, p);
} else {
throw new ServiceException("Not url connection string in 'file' " + id);
}
} else {
throw new ServiceException("Not a JDBC resource: " + id);
}
} else {
throw new ServiceException("No such resource: " + id);
}
}
return p.getConnection(LOG);
}
}
public static void addToJDBCPool(String id, String url) {
Map<String, String> env = new HashMap<>();
jdbcPools.put(id, new JDBCPool(url, env));
}
public static Connection getConnection(String id, SessionLogger logger) throws ServiceException {
JDBCPool p = jdbcPools.get(id);
if (p == null) {
throw new ServiceException("No such resource: " + id);
}
return p.getConnection(logger);
}
/**
* Get a file resources.
* @param c
* @param id
* @param workspace
* @param LOG
* @return
*/
public static Executable getResourceExe0(Class<?> c, String id,
final File workspace, SessionLogger LOG) throws ServiceException {
Resource resource = getResourceById(c, id);
if (resource == null) {
throw new ServiceException("Not found: " + id);
}
String file = resource.file();
if (file == null || file.isEmpty()) {
return null;
}
file = replaceArch(file);
File executable;
if (resource.type() == ResourceType.REFERENCE) {
executable = new File(file);
// check only if there is a reference to an absolute file.
// relative commads could use the PATH.
if (executable.isAbsolute() && !executable.exists()) {
throw new ServiceException("Referenced executable not found: " + file);
}
if (executable.isAbsolute() && !executable.canExecute()) {
throw new ServiceException("Referenced executable not executable: " + file);
}
} else if (resource.type() == ResourceType.EXECUTABLE) {
try {
File csip_home = new File(Config.getString("csip.dir", "/tmp/csip"));
executable = Binaries.unpackResource(file, csip_home);
} catch (IOException ex) {
throw new ServiceException(ex);
}
} else {
throw new ServiceException("Illegal resource type: " + resource.type());
}
Execution p = null;
List<Object> args = new ArrayList<>();
if (resource.wine() && File.pathSeparatorChar == ':') {
p = new Execution(new File(Config.getString("wine.path", "/usr/bin/wine")));
args.add(executable.toString());
} else {
p = new Execution(new File(executable.toString()));
}
if (!resource.args().isEmpty()) {
args.add(resource.args().split("\\s+"));
}
if (args.size() > 0) {
p.setArguments(args);
}
if (resource.env().length > 0) {
p.environment().putAll(parseEnv(resource.env()));
}
return asExecutable(p, workspace, LOG);
}
public static Resource[] getMergedResources(Class<?> c) {
Resource[] r = c.getAnnotationsByType(Resource.class);
if (r == null || r.length == 0) {
return NO_RES;
}
List<Resource> l = new ArrayList<>();
for (Resource res : r) {
if (res.from() != Object.class) {
Resource[] r1 = getMergedResources(res.from());
l.addAll(Arrays.asList(r1));
} else {
l.add(res);
}
}
return l.toArray(new Resource[l.size()]);
}
public static Resource[] getResourcesByType(Class<?> c, ResourceType type) {
Resource[] r = getMergedResources(c);
List<Resource> l = new ArrayList<>();
for (Resource resource : r) {
if (resource.type() == type) {
l.add(resource);
}
}
return l.toArray(new Resource[l.size()]);
}
public static Resource getResourceById(Class<?> c, String id) {
for (Resource resource : getMergedResources(c)) {
if (resource.id().equals(id)) {
return resource;
}
}
return null;
}
public static void extractAllResources(Class<?> c, SessionLogger LOG) throws ServiceException {
File csip_home = new File(Config.getString("csip.dir", "/tmp/csip"));
csip_home.mkdirs();
for (Resource resource : getMergedResources(c)) {
if (resource.type() == ResourceType.REFERENCE || resource.type() == ResourceType.JDBC || resource.id().equals("")) {
continue;
}
String f = resource.file();
if (f == null || f.isEmpty()) {
continue;
}
f = replaceArch(f);
try {
if (!new File(csip_home, f).exists()) {
File file = Binaries.unpackResource(f, csip_home);
if (file.getName().endsWith("zip") && resource.type() == ResourceType.ARCHIVE) {
ZipFiles.unzip(file);
}
}
} catch (IOException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
}
static String replaceArch(String s) {
return s.replace("${arch}", getArch());
}
static private final long date_at_startup = new Date().getTime();
/**
*
* @param name
* @param file_or_folder
* @return
* @throws IOException
*/
public static File unpackResource(String name, File file_or_folder) throws IOException {
name = replaceArch(name);
URL u = Binaries.class.getResource(name);
if (u == null) {
throw new IllegalArgumentException("No such resource " + name);
}
File outFile = null;
if (file_or_folder.isDirectory()) {
outFile = new File(file_or_folder, name);
} else {
outFile = file_or_folder;
}
URLConnection con = u.openConnection();
long date = con.getHeaderFieldDate("Last-Modified", date_at_startup);
// newer date?
if (outFile.exists() && (date < outFile.lastModified())) {
return outFile;
}
// different size?
if (outFile.exists() && (con.getContentLengthLong() == outFile.length())) {
return outFile;
}
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
InputStream in = new BufferedInputStream(u.openStream());
FileUtils.copyInputStreamToFile(in, outFile);
in.close();
outFile.setExecutable(true);
outFile.setReadable(true);
outFile.setLastModified(date);
return outFile;
}
/**
*
* @param name
* @param newName
* @return
* @throws IOException
*/
public static File unpackResourceAbsolute(String name, String newName) throws IOException {
name = replaceArch(name);
URL u = Binaries.class.getResource(name);
if (u == null) {
throw new IllegalArgumentException("No such resource " + name);
}
File outFile = new File(newName);
URLConnection con = u.openConnection();
if (outFile.exists() && con.getDate() < outFile.lastModified()) {
// the local file exists and is newer.
return outFile;
}
if (outFile.exists() && con.getContentLength() == outFile.length()) {
return outFile;
}
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
InputStream in = new BufferedInputStream(u.openStream());
FileUtils.copyInputStreamToFile(in, outFile);
in.close();
outFile.setExecutable(true);
outFile.setLastModified(con.getDate());
return outFile;
}
/**
* get the FS usage of that directory/file location in percent.
*
* @param file
* @return value 0.0 .. 1.0, 1.0 means 100% full.
*/
public static double getFSUsage(File file) {
return 1 - (double) file.getUsableSpace() / file.getTotalSpace();
}
/**
* get the library jars
*
* @param dir
* @return
*/
public static List<File> getJars(File dir) {
File[] f = dir.listFiles((FilenameFilter) new WildcardFileFilter("*.jar"));
if (f == null) {
return new ArrayList<>();
}
return Arrays.asList(f);
}
/**
* Convert the files to a classpath
*
* @param f
* @param jar file list
* @return
*/
public static String asClassPath(List<File> f) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < f.size(); i++) {
if (f.get(i).getName().endsWith(".jar")) {
b.append(f.get(i).toString());
if (i < f.size() - 1) {
b.append(File.pathSeparatorChar);
}
}
}
return b.toString();
}
/**
* Converts a map to system properties.
*
* @param m
* @return
*/
public static String[] asSysProps(Map<String, String> m) {
List<String> b = new ArrayList<>();
for (String key : m.keySet()) {
String val = m.get(key);
b.add("-D" + key + "=" + val);
}
return b.toArray(new String[b.size()]);
}
/**
*
* @param env
* @return
*/
public static Map<String, String> parseEnv(String[] env) {
Map<String, String> m = new HashMap<>();
if (env == null) {
return m;
}
for (String s : env) {
if (s == null || s.isEmpty()) {
continue;
}
String t[] = s.trim().split("\\s*=\\s*");
if (t.length == 2) {
m.put(t[0], resolve(t[1]));
}
}
return m;
}
/**
* Parses a string into a number of bytes. e.g. "1KB" -> 1024,
* @param in
* @return the size in bytes.
*/
public static long parseByteSize(String in) {
in = in.trim().replaceAll(",", ".");
try {
return Long.parseLong(in);
} catch (NumberFormatException e) {
}
Matcher m = Pattern.compile("([\\d.,]+)\\s*(\\w)").matcher(in);
m.find();
double scale = 1;
switch (m.group(2).charAt(0)) {
case 'G':
case 'g':
scale *= 1024;
case 'M':
case 'm':
scale *= 1024;
case 'K':
case 'k':
scale *= 1024;
break;
default:
throw new IllegalArgumentException(in);
}
return Math.round(Double.parseDouble(m.group(1)) * scale);
}
/**
* Resolve a string with system and CSIP properties.
*
* @param str the string to resolve
* @return the resolved string.
*/
public static String resolve(String str) {
if (!str.contains("${")) {
return str;
}
Properties p = new Properties(System.getProperties());
p.putAll(Config.properties());
return resolve0(str, p);
}
/**
* property substitution in a string.
*
* @param str
* @return
*/
private static String resolve0(String str, Properties p) {
int idx = 0;
while (idx < str.length()) {
int start = str.indexOf("${", idx);
int end = str.indexOf("}", idx);
if (start == -1 || end == -1 || end < start) {
break;
}
String key = str.substring(start + 2, end);
String val = p.getProperty(key);
int inc;
if (val != null) {
str = str.replace("${" + key + "}", val);
inc = val.length();
} else {
inc = key.length() + 3;
}
idx = start + inc;
}
return str;
}
// String a = "jdbc:sqlserver://129.82.20.242:1433;databaseName=lmod_ZedX;user=lmod-rw;password=managements";
// String b = a.replaceAll("password=.+[;]?", "password=****;");
//
// System.out.println(b);
// String a = resolve("${csip.arch} all to ${user.dir} is true. '${}'live in ${csip.dir}");
// System.out.println(a);
//
//// File dir = new File("/tmp");
////
//// String b = "odd/bcd/ab.txt";
// File f = new File("odd/bcd/ab.txt");
// f.getParentFile().mkdirs();
//
// FileOutputStream os = new FileOutputStream(f);
// os.write("olaf".getBytes());
// os.close();
//// IOUtils.writeStringToFile(f, "hi there");
}