org.freedesktop.dbus.utils.Util Maven / Gradle / Ivy
Show all versions of dbus-java-core Show documentation
package org.freedesktop.dbus.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class providing helper methods for handling strings, files and so on.
*
* @author hypfvieh
* @since v3.2.5 - 2020-12-28
*/
public final class Util {
private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);
/** Characters used for random strings */
private static final char[] SYMBOLS;
static {
StringBuilder tmp = new StringBuilder();
for (char ch = '0'; ch <= '9'; ++ch) {
tmp.append(ch);
}
for (char ch = 'a'; ch <= 'z'; ++ch) {
tmp.append(ch);
}
for (char ch = 'A'; ch <= 'Z'; ++ch) {
tmp.append(ch);
}
SYMBOLS = tmp.toString().toCharArray();
}
private Util() {}
/**
* Trys to read a properties file.
* Returns null if properties file could not be loaded
* @param _file property file to read
* @return Properties Object or null
*/
public static Properties readProperties(File _file) {
if (_file.exists()) {
try {
return readProperties(new FileInputStream(_file));
} catch (FileNotFoundException _ex) {
LOGGER.info("Could not load properties file: " + _file, _ex);
}
}
return null;
}
/**
* Tries to read a properties file from an inputstream.
* @param _stream input stream providing property file content
* @return properties object/null
*/
public static Properties readProperties(InputStream _stream) {
Properties props = new Properties();
if (_stream == null) {
return null;
}
try {
props.load(_stream);
return props;
} catch (IOException | NumberFormatException _ex) {
LOGGER.warn("Could not properties: ", _ex);
}
return null;
}
/**
* Checks if the given String is either null or blank.
* Blank means:
*
* " " - true
* "" - true
* null - true
* " xx" - false
*
* @param _str string to test
* @return true if string is blank or null, false otherwise
*/
public static boolean isBlank(String _str) {
if (_str == null) {
return true;
}
return _str.isBlank();
}
/**
* Null-safe equals for two strings.
*
* @param _str1 first string
* @param _str2 second string
* @return true if both are equal (also true if both are null)
*/
public static boolean strEquals(String _str1, String _str2) {
if (_str1 == _str2) {
return true;
} else if (_str1 == null || _str2 == null) {
return false;
} else if (_str1.length() != _str2.length()) {
return false;
}
return _str1.equals(_str2);
}
/**
* Checks if the given String is either null or empty.
* Blank means:
*
* " " - false
* "" - true
* null - true
* " xx" - false
*
* @param _str string to test
* @return true if string is empty or null, false otherwise
*/
public static boolean isEmpty(String _str) {
if (_str == null) {
return true;
}
return _str.isEmpty();
}
/**
* Generate a simple (cryptographic insecure) random string.
* @param _length length of random string
* @return random string or empty string if _length <= 0
*/
public static String randomString(int _length) {
if (_length <= 0) {
return "";
}
Random random = new Random();
char[] buf = new char[_length];
for (int idx = 0; idx < buf.length; ++idx) {
buf[idx] = SYMBOLS[random.nextInt(SYMBOLS.length)];
}
return new String(buf);
}
/**
* Upper case the first letter of the given string.
*
* @param _str string
* @return uppercased string
*/
public static String upperCaseFirstChar(String _str) {
if (_str == null) {
return null;
}
if (_str.isEmpty()) {
return _str;
}
return _str.substring(0, 1).toUpperCase() + _str.substring(1);
}
/**
* Converts a snake-case-string to camel case string.
*
* Eg. this_is_snake_case → thisIsSnakeCase
* @param _input string
* @return camel case string or input if nothing todo. Returns null if input was null.
*/
public static String snakeToCamelCase(String _input) {
if (isBlank(_input)) {
return _input;
}
Pattern compile = Pattern.compile("_[a-zA-Z]");
Matcher matcher = compile.matcher(_input);
String result = _input;
while (matcher.find()) {
String match = matcher.group();
String replacement = match.replace("_", "");
replacement = replacement.toUpperCase();
result = result.replaceFirst(match, replacement);
}
return result;
}
/**
* Abbreviates a String using ellipses.
*
* @param _str string to abbrivate
* @param _length max length
* @return abbreviated string, original string if string length is lower or equal then desired length or null if input was null
*/
public static String abbreviate(String _str, int _length) {
if (_str == null) {
return null;
}
if (_str.length() <= _length) {
return _str;
}
String abbr = _str.substring(0, _length - 3) + "...";
return abbr;
}
/**
* Check if the given value is a valid network port (1 - 65535).
* @param _port 'port' to check
* @param _allowWellKnown allow ports below 1024 (aka reserved well known ports)
* @return true if int is a valid network port, false otherwise
*/
public static boolean isValidNetworkPort(int _port, boolean _allowWellKnown) {
if (_allowWellKnown) {
return _port > 0 && _port < 65536;
}
return _port > 1024 && _port < 65536;
}
/**
* @see #isValidNetworkPort(int, boolean)
* @param _str string to check
* @param _allowWellKnown allow well known port
* @return true if valid port, false otherwise
*/
public static boolean isValidNetworkPort(String _str, boolean _allowWellKnown) {
if (isInteger(_str, false)) {
return isValidNetworkPort(Integer.parseInt(_str), _allowWellKnown);
}
return false;
}
/**
* Check if string is an either positive or negative integer.
*
* @param _str string to validate
* @param _allowNegative negative integer allowed
* @return true if integer, false otherwise
*/
public static boolean isInteger(String _str, boolean _allowNegative) {
if (_str == null) {
return false;
}
String regex = "[0-9]+$";
if (_allowNegative) {
regex = "^-?" + regex;
} else {
regex = "^" + regex;
}
return _str.matches(regex);
}
/**
* Reads a file to a List<String> (each line is one entry in list).
* Line endings (line feed/carriage return) are NOT removed!
*
* @param _fileName file to read
* @return list containing text
*/
public static List readFileToList(String _fileName) {
List localText = getTextfileFromUrl(_fileName, Charset.defaultCharset(), false);
return localText;
}
/**
* Reads a file to a String.
* Line endings (line feed/carriage return) are NOT removed!
*
* @param _file file to read
* @return String containing content, maybe null
*/
public static String readFileToString(File _file) {
return String.join(System.lineSeparator(), readFileToList(_file.getAbsolutePath()));
}
/**
* Reads a text file from the given URL using the provided charset.
* Using the _silent argument optionally disables all error logging.
*
* @param _url url providing the file to read
* @param _charset charset to use
* @param _silent true to not log exceptions, false otherwise
* @return list of string or null on error
*/
public static List getTextfileFromUrl(String _url, Charset _charset, boolean _silent) {
if (_url == null) {
return null;
}
String fileUrl = _url;
if (!fileUrl.contains("://")) {
fileUrl = "file://" + fileUrl;
}
try {
URL dlUrl;
if (fileUrl.startsWith("file:/")) {
dlUrl = new URL("file", "", fileUrl.replaceFirst("file:\\/{1,2}", ""));
} else {
dlUrl = new URL(fileUrl);
}
URLConnection urlConn = dlUrl.openConnection();
urlConn.setDoInput(true);
urlConn.setUseCaches(false);
return readTextFileFromStream(urlConn.getInputStream(), _charset, _silent);
} catch (IOException _ex) {
if (!_silent) {
LOGGER.warn("Error while reading file:", _ex);
}
}
return null;
}
/**
* Reads a text file from given {@link InputStream} using the given {@link Charset}.
* @param _input stream to read
* @param _charset charset to use
* @param _silent true to disable exception logging, false otherwise
* @return List of string or null on error
*/
public static List readTextFileFromStream(InputStream _input, Charset _charset, boolean _silent) {
if (_input == null) {
return null;
}
try {
List fileContent;
try (BufferedReader dis = new BufferedReader(new InputStreamReader(_input, _charset))) {
String s;
fileContent = new ArrayList<>();
while ((s = dis.readLine()) != null) {
fileContent.add(s);
}
}
return !fileContent.isEmpty() ? fileContent : null;
} catch (IOException _ex) {
if (!_silent) {
LOGGER.warn("Error while reading file:", _ex);
}
}
return null;
}
/**
* Write String to file with the given charset.
* Optionally appends the data to the file.
*
* @param _fileName the file to write
* @param _fileContent the content to write
* @param _charset the charset to use
* @param _append append content to file, if false file will be overwritten if existing
*
* @return true on successful write, false otherwise
*/
public static boolean writeTextFile(String _fileName, String _fileContent, Charset _charset, boolean _append) {
if (isBlank(_fileName)) {
return false;
}
String allText = "";
if (_append) {
File file = new File(_fileName);
if (file.exists()) {
allText = readFileToString(file);
}
}
allText += _fileContent;
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(_fileName), _charset)) {
writer.write(allText);
} catch (IOException _ex) {
LOGGER.error("Could not write file to '" + _fileName + "'", _ex);
return false;
}
return true;
}
/**
* Gets the host name of the local machine.
* @return host name
*/
public static String getHostName() {
try {
return java.net.InetAddress.getLocalHost().getHostName();
} catch (java.net.UnknownHostException _ex) {
return null;
}
}
/**
* Checks if any of the 'needle' values are found in 'haystack'.
*
* @param type
* @param _haystack collection to check
* @param _needles values to find
*
* @return true if any value found, false if any parameter null or no matching value found
*/
public static boolean collectionContainsAny(Collection _haystack, Collection _needles) {
if (_haystack == null || _needles == null) {
return false;
}
for (T t : _needles) {
if (_haystack.contains(t)) {
return true;
}
}
return false;
}
/**
* Determines the current logged on user.
* @return logged on user
*/
public static String getCurrentUser() {
String[] sysPropParms = new String[] {"user.name", "USER", "USERNAME"};
for (int i = 0; i < sysPropParms.length; i++) {
String val = System.getProperty(sysPropParms[i]);
if (!isEmpty(val)) {
return val;
}
}
return null;
}
/**
* Checks if the running OS is a MacOS/MacOS X.
* @return true if MacOS (or MacOS X), false otherwise
*/
public static boolean isMacOs() {
String osName = System.getProperty("os.name");
return osName != null && osName.toLowerCase(Locale.US).startsWith("mac");
}
public static boolean isFreeBsd() {
String osName = System.getProperty("os.name");
return osName != null && osName.toLowerCase(Locale.US).startsWith("freebsd");
}
/**
* Checks if the running OS is a MS Windows OS.
* @return true if Windows, false otherwise
*/
public static boolean isWindows() {
String osName = System.getProperty("os.name");
return osName != null && osName.toLowerCase(Locale.US).startsWith("windows");
}
/**
* Get the java version of the current running JRE.
*
* @return java major
*/
public static int getJavaVersion() {
String version = System.getProperty("java.version");
if (version.startsWith("1.")) {
version = version.substring(2, 3);
} else {
int dot = version.indexOf('.');
if (dot != -1) {
version = version.substring(0, dot);
}
}
return Integer.parseInt(version);
}
/**
* Create a random GUID (used for connection addresses).
*
* @return String
*/
public static String genGUID() {
Random r = new Random();
byte[] buf = new byte[16];
r.nextBytes(buf);
return Hexdump.toHex(buf, false);
}
/**
* Creates a unix socket address using.
*
* @param _listeningSocket true if the address should be used for a listing socket
* @param _abstract true to create an abstract socket
*
* @return address String
*/
public static String createDynamicSessionAddress(boolean _listeningSocket, boolean _abstract) {
String address = "unix:";
String path = new File(System.getProperty("java.io.tmpdir"), "dbus-XXXXXXXXXX").getAbsolutePath();
Random r = new Random();
do {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append((char) (Math.abs(r.nextInt()) % 26) + 65);
}
path = path.replaceAll("..........$", sb.toString());
LoggerFactory.getLogger(Util.class).trace("Trying path {}", path);
} while (new File(path).exists());
if (_abstract) {
address += "abstract=" + path;
} else {
address += "path=" + path;
}
if (_listeningSocket) {
address += ",listen=true";
}
address += ",guid=" + Util.genGUID();
LoggerFactory.getLogger(Util.class).debug("Created Session address: {}", address);
return address;
}
/**
* Checks if given value is greater or equal to the given minimum and less or equal to the given maximum.
*
* @param _check value to check
* @param _min minimum allowed value (including)
* @param _max maximum allowed value (including)
*
* @return given value if in range
* @throws IllegalArgumentException when given value is out of range
*
* @since 4.2.0 - 2022-07-13
*/
public static int checkIntInRange(int _check, int _min, int _max) {
if (_check >= _min && _check <= _max) {
return _check;
}
throw new IllegalArgumentException("Value " + _check + " is out ouf range (< " + _min + " && > " + _max + ")");
}
/**
* Setup the unix socket file permissions.
* User and group can always be set, file permissions are only set on non-windows OSes.
*
* @param _path path to file which where permissions should be set
* @param _fileOwner new owner for the file
* @param _fileGroup new group for the file
* @param _fileUnixPermissions unix permissions to set on file
*/
public static void setFilePermissions(Path _path, String _fileOwner, String _fileGroup, Set _fileUnixPermissions) {
Objects.requireNonNull(_path, "Path required");
UserPrincipalLookupService userPrincipalLookupService = _path.getFileSystem().getUserPrincipalLookupService();
if (userPrincipalLookupService == null) {
LOGGER.error("Unable to set user/group permissions on {}", _path);
}
if (!Util.isBlank(_fileOwner)) {
try {
UserPrincipal userPrincipal = userPrincipalLookupService.lookupPrincipalByName(_fileOwner);
if (userPrincipal != null) {
Files.getFileAttributeView(_path, PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).setOwner(userPrincipal);
}
} catch (IOException _ex) {
LOGGER.error("Could not change owner of {} to {}", _path, _fileOwner, _ex);
}
}
if (!Util.isBlank(_fileGroup)) {
try {
GroupPrincipal groupPrincipal = userPrincipalLookupService.lookupPrincipalByGroupName(_fileGroup);
if (groupPrincipal != null) {
Files.getFileAttributeView(_path, PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).setGroup(groupPrincipal);
}
} catch (IOException _ex) {
LOGGER.error("Could not change group of {} to {}", _path, _fileGroup, _ex);
}
}
if (!Util.isWindows() && _fileUnixPermissions != null) {
try {
Files.setPosixFilePermissions(_path, _fileUnixPermissions);
} catch (Exception _ex) {
LOGGER.error("Could not set file permissions of {} to {}", _path, _fileUnixPermissions, _ex);
}
}
}
/**
* Waits for the provided supplier to return true or throws an exception.
*
* This method will call the provided supplier every _sleepTime milliseconds to check
* if the supplier returns true.
* If supplier returns true, method will return.
* If no value is present after the defined _timeoutMs a {@link IllegalStateException} is thrown.
*
* @param _lockName name for the lock (used in exception text and logging)
* @param _wait supplier to wait for
* @param _timeoutMs timeout in milliseconds when wait will fail
* @param _sleepTime sleep time between each retries
*
* @param exception type which might be thrown
*
* @throws T when timeout is reached
*/
@SuppressWarnings("unchecked")
public static void waitFor(String _lockName, IThrowingSupplier _wait, long _timeoutMs, long _sleepTime) throws T {
long waited = 0;
boolean wait = false;
T lastEx = null;
do {
try {
lastEx = null;
// supplier may throw, if so assume that we should still wait and retry
wait = _wait.get();
} catch (Throwable _ex) {
lastEx = (T) _ex;
wait = false;
}
if (waited >= _timeoutMs) {
if (lastEx != null) { // we have a timeout and last retry has thrown a exception
throw lastEx;
}
// we have a timeout and no exception happened before
throw new IllegalStateException(_lockName + " not available in the specified time of " + _timeoutMs + " ms");
}
try {
Thread.sleep(_sleepTime);
} catch (InterruptedException _ex) {
LOGGER.debug("Interrupted while waiting for {}", _lockName);
break;
}
waited += _sleepTime;
LOGGER.debug("Waiting for {} to be available: {} of {} ms waited", _lockName, waited, _timeoutMs);
} while (!wait);
}
}