Util.java
// Copyright (c) ZeroC, Inc.
package com.zeroc.Ice;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadFactory;
/** Utility methods for the Ice runtime. */
public final class Util {
/**
* Creates a new empty property set.
*
* @return A new empty property set.
* @deprecated Use {@link Properties#Properties()} instead.
*/
@Deprecated
public static Properties createProperties() {
return new Properties();
}
/**
* Creates a property set initialized from an argument vector.
*
* @param args A command-line argument vector, possibly containing options to set properties. If
* the command-line options include a <code>--Ice.Config</code> option, the corresponding
* configuration files are parsed. If the same property is set in a configuration file and
* in the argument vector, the argument vector takes precedence.
* @return A new property set initialized with the property settings that were removed from the
* argument vector.
* @deprecated Use {@link Properties#Properties(String[])} instead.
*/
@Deprecated
public static Properties createProperties(String[] args) {
return new Properties(args, null, null);
}
/**
* Creates a property set initialized from an argument vector and returns the remaining
* arguments.
*
* @param args A command-line argument vector, possibly containing options to set properties. If
* the command-line options include a {@code --Ice.Config} option, the corresponding
* configuration files are parsed. If the same property is set in a configuration file and
* in the argument vector, the argument vector takes precedence.
* @param remainingArgs If non null, the given list will contain on return the command-line
* arguments that were not used to set properties.
* @return A new property set initialized with the property settings that were removed from the
* argument vector.
* @deprecated Use {@link Properties#Properties(String[], java.util.List)} instead.
*/
@Deprecated
public static Properties createProperties(String[] args, List<String> remainingArgs) {
return new Properties(args, null, remainingArgs);
}
/**
* Creates a property set initialized from an argument vector.
*
* @param args A command-line argument vector, possibly containing options to set properties. If
* the command-line options include a {@code --Ice.Config} option, the corresponding
* configuration files are parsed. If the same property is set in a configuration file and
* in the argument vector, the argument vector takes precedence.
* @param defaults Default values for the property set. Settings in configuration files and
* {@code args} override these defaults.
* @return A new property set initialized with the property settings that were removed from the
* argument vector.
* @deprecated Use {@link Properties#Properties(String[], Properties)} instead.
*/
@Deprecated
public static Properties createProperties(String[] args, Properties defaults) {
return new Properties(args, defaults, null);
}
/**
* Creates a property set initialized from an argument vector and returns the remaining
* arguments.
*
* @param args A command-line argument vector, possibly containing options to set properties. If
* the command-line options include a {@code --Ice.Config} option, the corresponding
* configuration files are parsed. If the same property is set in a configuration file and
* in the argument vector, the argument vector takes precedence.
* @param defaults Default values for the property set. Settings in configuration files and
* {@code args} override these defaults.
* @param remainingArgs If non null, the given list will contain on return the command-line
* arguments that were not used to set properties.
* @return A new property set initialized with the property settings that were removed from the
* argument vector.
* @deprecated Use {@link Properties#Properties(String[], Properties, java.util.List)} instead.
*/
@Deprecated
public static Properties createProperties(
String[] args, Properties defaults, List<String> remainingArgs) {
return new Properties(args, defaults, remainingArgs);
}
/**
* Creates a new communicator.
*
* @param initData Options for the new communicator.
* @return The new communicator.
* @see InitializationData
*/
public static Communicator initialize(InitializationData initData) {
if (initData == null) {
initData = new InitializationData();
} else {
initData = initData.clone(); // shallow clone
}
var communicator = new Communicator(initData);
communicator.finishSetup();
return communicator;
}
/**
* Creates a new communicator with the default options.
*
* @return The new communicator.
*/
public static Communicator initialize() {
return initialize((InitializationData) null);
}
/**
* Creates a new communicator, using Ice properties parsed from command-line arguments.
*
* @param args A command-line argument vector. This method parses arguments starting with `--` and one of the
* reserved prefixes (Ice, IceSSL, etc.) as properties for the new communicator. If there is an argument
* starting with `--Ice.Config`, this method loads the specified configuration file. When the same property is
* set in a configuration file and through a command-line argument, the command-line setting takes precedence.
* @param remainingArgs If non-null, the given list will contain on return the command-line arguments that were
* not used to set properties.
* @return The new communicator.
*/
public static Communicator initialize(String[] args, List<String> remainingArgs) {
var initData = new InitializationData();
initData.properties = new Properties(args, remainingArgs);
return initialize(initData);
}
/**
* Creates a new communicator, using Ice properties parsed from command-line arguments.
*
* @param args A command-line argument vector. This method parses arguments starting with `--` and one of the
* reserved prefixes (Ice, IceSSL, etc.) as properties for the new communicator. If there is an argument
* starting with `--Ice.Config`, this method loads the specified configuration file. When the same property is
* set in a configuration file and through a command-line argument, the command-line setting takes precedence.
* @return The new communicator.
*/
public static Communicator initialize(String[] args) {
return initialize(args, null);
}
/**
* Converts a stringified identity into an Identity.
*
* @param s The string to convert.
* @return The converted object identity.
*/
public static Identity stringToIdentity(String s) {
Identity ident = new Identity();
//
// Find unescaped separator; note that the string may contain an escaped
// backslash before the separator.
//
int slash = -1, pos = 0;
while ((pos = s.indexOf('/', pos)) != -1) {
int escapes = 0;
while (pos - escapes > 0 && s.charAt(pos - escapes - 1) == '\\') {
escapes++;
}
//
// We ignore escaped escapes
//
if (escapes % 2 == 0) {
if (slash == -1) {
slash = pos;
} else {
//
// Extra unescaped slash found.
//
throw new ParseException("unescaped backslash in identity string '" + s + "'");
}
}
pos++;
}
if (slash == -1) {
ident.category = "";
try {
ident.name = StringUtil.unescapeString(s, 0, s.length(), "/");
} catch (IllegalArgumentException ex) {
throw new ParseException("invalid name in identity string '" + s + "'", ex);
}
} else {
try {
ident.category = StringUtil.unescapeString(s, 0, slash, "/");
} catch (IllegalArgumentException ex) {
throw new ParseException("invalid category in identity string '" + s + "'", ex);
}
if (slash + 1 < s.length()) {
try {
ident.name = StringUtil.unescapeString(s, slash + 1, s.length(), "/");
} catch (IllegalArgumentException ex) {
throw new ParseException("invalid name in identity string '" + s + "'", ex);
}
} else {
ident.name = "";
}
}
return ident;
}
/**
* Converts an Identity into a string using the specified mode.
*
* @param ident The object identity to convert.
* @param toStringMode Specifies how to handle non-ASCII characters and non-printable ASCII characters.
* @return the stringified identifier
*/
public static String identityToString(Identity ident, ToStringMode toStringMode) {
if (ident.category == null || ident.category.isEmpty()) {
return StringUtil.escapeString(ident.name, "/", toStringMode);
} else {
return StringUtil.escapeString(ident.category, "/", toStringMode)
+ '/'
+ StringUtil.escapeString(ident.name, "/", toStringMode);
}
}
/**
* Converts an object identity to a string.
*
* @param ident The object identity to convert.
* @return The string representation of the object identity using the default mode (Unicode)
*/
public static String identityToString(Identity ident) {
return identityToString(ident, ToStringMode.Unicode);
}
/**
* Compares the object identities of two proxies.
*
* @param lhs A proxy.
* @param rhs A proxy.
* @return -1 if the identity in {@code lhs} compares less than the identity in {@code rhs};
* 0 if the identities compare equal; 1, otherwise.
* @see ProxyIdentityKey
* @see ProxyIdentityFacetKey
* @see #proxyIdentityAndFacetCompare
*/
public static int proxyIdentityCompare(ObjectPrx lhs, ObjectPrx rhs) {
if (lhs == null && rhs == null) {
return 0;
} else if (lhs == null && rhs != null) {
return -1;
} else if (lhs != null && rhs == null) {
return 1;
} else {
Identity lhsIdentity = lhs.ice_getIdentity();
Identity rhsIdentity = rhs.ice_getIdentity();
int n;
if ((n = lhsIdentity.name.compareTo(rhsIdentity.name)) != 0) {
return n;
}
return lhsIdentity.category.compareTo(rhsIdentity.category);
}
}
/**
* Compares the object identities and facets of two proxies.
*
* @param lhs A proxy.
* @param rhs A proxy.
* @return -1 if the identity and facet in {@code lhs} compare less than the identity and
* facet in {@code rhs}; 0 if the identities and facets compare equal; 1, otherwise.
* @see ProxyIdentityFacetKey
* @see ProxyIdentityKey
* @see #proxyIdentityCompare
*/
public static int proxyIdentityAndFacetCompare(ObjectPrx lhs, ObjectPrx rhs) {
if (lhs == null && rhs == null) {
return 0;
} else if (lhs == null && rhs != null) {
return -1;
} else if (lhs != null && rhs == null) {
return 1;
} else {
Identity lhsIdentity = lhs.ice_getIdentity();
Identity rhsIdentity = rhs.ice_getIdentity();
int n;
if ((n = lhsIdentity.name.compareTo(rhsIdentity.name)) != 0) {
return n;
}
if ((n = lhsIdentity.category.compareTo(rhsIdentity.category)) != 0) {
return n;
}
String lhsFacet = lhs.ice_getFacet();
String rhsFacet = rhs.ice_getFacet();
if (lhsFacet == null && rhsFacet == null) {
return 0;
} else if (lhsFacet == null) {
return -1;
} else if (rhsFacet == null) {
return 1;
}
return lhsFacet.compareTo(rhsFacet);
}
}
/**
* Gets the per-process logger. This logger is used by all communicators that do not have their own specific logger
* configured at the time the communicator is created.
*
* @return The current per-process logger instance.
*/
public static Logger getProcessLogger() {
synchronized (_processLoggerMutex) {
if (_processLogger == null) {
//
// TODO: Would be nice to be able to use process name as prefix by default.
//
_processLogger = new LoggerI("");
}
return _processLogger;
}
}
/**
* Sets the per-process logger. This logger is used by all communicators that do not have their own specific logger
* configured at the time the communicator is created.
*
* @param logger The new per-process logger instance.
*/
public static void setProcessLogger(Logger logger) {
synchronized (_processLoggerMutex) {
_processLogger = logger;
}
}
/**
* Returns the Ice version in the form {@code A.B.C}, where {@code A} indicates the
* major version, {@code B} indicates the minor version, and {@code C} indicates the
* patch level.
*
* @return The Ice version.
*/
public static String stringVersion() {
return "3.8.0-alpha.0"; // "A.B.C", with A=major, B=minor, C=patch
}
/**
* Returns the Ice version as an integer in the form {@code AABBCC}, where {@code AA}
* indicates the major version, {@code BB} indicates the minor version, and {@code CC}
* indicates the patch level. For example, for Ice 3.8.1, the returned value is 30801.
*
* @return The Ice version.
*/
public static int intVersion() {
return 30850; // AABBCC, with AA=major, BB=minor, CC=patch
}
/**
* Converts a string to a protocol version.
*
* @param version The string to convert.
* @return The converted protocol version.
*/
public static ProtocolVersion stringToProtocolVersion(String version) {
return new ProtocolVersion(stringToMajor(version), stringToMinor(version));
}
/**
* Converts a string to an encoding version.
*
* @param version The string to convert.
* @return The converted encoding version.
*/
public static EncodingVersion stringToEncodingVersion(String version) {
return new EncodingVersion(stringToMajor(version), stringToMinor(version));
}
/**
* Converts a protocol version to a string.
*
* @param v The protocol version to convert.
* @return The converted string.
*/
public static String protocolVersionToString(ProtocolVersion v) {
return majorMinorToString(v.major, v.minor);
}
/**
* Converts an encoding version to a string.
*
* @param v The encoding version to convert.
* @return The converted string.
*/
public static String encodingVersionToString(EncodingVersion v) {
return majorMinorToString(v.major, v.minor);
}
/**
* Returns the supported Ice protocol version.
*
* @return The Ice protocol version.
*/
public static ProtocolVersion currentProtocol() {
return Protocol.currentProtocol.clone();
}
/**
* Returns the supported Slice encoding version.
*
* @return The Slice encoding version.
*/
public static EncodingVersion currentEncoding() {
return Protocol.currentEncoding.clone();
}
/**
* Downcasts a {@code CompletableFuture<T>} to an {@code InvocationFuture<T>} object.
*
* @param f The CompletableFuture returned by an asynchronous invocation.
* @param <T> The result type.
* @return f casted to an {@code InvocationFuture<T>}.
* @throws ClassCastException if f is not an {@code InvocationFuture<T>}.
*/
public static <T> InvocationFuture<T> getInvocationFuture(CompletableFuture<T> f) {
return (InvocationFuture<T>) f;
}
/**
* Translates a Slice type id to a Java class name.
*
* @param id The Slice type id, such as {@code ::Module::Type}.
* @return The equivalent Java class name, or null if the type id is malformed.
*/
public static String typeIdToClass(String id) {
if (!id.startsWith("::")) {
return null;
}
StringBuilder buf = new StringBuilder(id.length());
int start = 2;
boolean done = false;
while (!done) {
int end = id.indexOf(':', start);
String s;
if (end != -1) {
s = id.substring(start, end);
start = end + 2;
} else {
s = id.substring(start);
done = true;
}
if (buf.length() > 0) {
buf.append('.');
}
buf.append(s);
}
return buf.toString();
}
static String createThreadName(final Properties properties, final String name) {
String threadName = properties.getIceProperty("Ice.ProgramName");
if (!threadName.isEmpty()) {
threadName += '-';
}
threadName = threadName + name;
return threadName;
}
static ThreadFactory createThreadFactory(final Properties properties, final String name) {
return new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName(name);
if (properties.getIceProperty("Ice.ThreadPriority").length() > 0) {
t.setPriority(Util.getThreadPriorityProperty(properties, "Ice"));
}
return t;
}
};
}
/**
* Given a path name, first try to open it as a class path resource (the path is treated as
* absolute). If that fails, fall back to the file system. Returns null if the file does not
* exist and raises IOException if an error occurs.
*
* @param cl the class loader to use
* @param path the path to the resource
* @return an InputStream, or null if the resource could not be found
* @throws IOException if an I/O error occurs while trying to open the resource
* @hidden Public because it's used by SSL.
*/
public static InputStream openResource(ClassLoader cl, String path)
throws IOException {
//
// Calling getResourceAsStream on the class loader means all paths are absolute,
// whereas calling it on the class means all paths are relative to the class
// unless the path has a leading forward slash. We call it on the class loader.
//
// getResourceAsStream returns null if the resource can't be found.
//
InputStream stream = null;
try {
stream = cl.getResourceAsStream(path);
} catch (IllegalArgumentException ex) {
//
// With JDK-7 this can happen if the result url (base url + path) produces a
// malformed url for an URLClassLoader. For example the code in following
// comment will produce this exception under Windows.
//
// URLClassLoader cl = new URLClassLoader(new URL[] {new
// URL("http://localhost:8080/")});
// java.io.InputStream in = Util.openResource(cl, "c:\\foo.txt");
//
}
if (stream == null) {
try {
File f = new File(path);
if (f.exists()) {
stream = new FileInputStream(f);
}
} catch (java.lang.SecurityException ex) {
// Ignore - a security manager may forbid access to the local file system.
}
}
return stream;
}
/**
* Finds a class by name using the specified class loader.
*
* @param className the name of the class to find
* @param cl the class loader to use, or null to use the system class loader
* @return the class object for the specified class, or null if the class could not be found
* @hidden Public because it's used by IceBox and IceGridGUI.
*/
public static Class<?> findClass(String className, ClassLoader cl) throws LinkageError {
//
// Try to load the class using the given class loader (if any). If that fails (or
// none is provided), we try to load the class a few more ways before giving up.
//
// Calling Class.forName() doesn't always work. For example, if Ice.jar is installed
// as an extension (in $JAVA_HOME/jre/lib/ext), calling Class.forName(name) uses the
// extension class loader, which will not look in CLASSPATH for the target class.
//
Class<?> c = null;
if (cl != null) {
c = loadClass(className, cl);
}
//
// Try using the current thread's class loader.
//
if (c == null) {
try {
cl = Thread.currentThread().getContextClassLoader();
if (cl != null) {
c = loadClass(className, cl);
}
} catch (java.lang.SecurityException ex) {}
}
//
// Try using Class.forName().
//
try {
if (c == null) {
c = Class.forName(className);
}
} catch (ClassNotFoundException ex) {
// Ignore
}
//
// Fall back to the system class loader (which knows about CLASSPATH).
//
if (c == null) {
try {
cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
c = loadClass(className, cl);
}
} catch (java.lang.SecurityException ex) {}
}
return c;
}
private static Class<?> loadClass(String className, ClassLoader cl) {
if (cl != null) {
try {
return cl.loadClass(className);
} catch (ClassNotFoundException ex) {
// Ignore
}
}
return null;
}
private static int getThreadPriorityProperty(Properties properties, String prefix) {
String pri = properties.getProperty(prefix + ".ThreadPriority");
if ("MIN_PRIORITY".equals(pri) || "java.lang.Thread.MIN_PRIORITY".equals(pri)) {
return Thread.MIN_PRIORITY;
} else if ("NORM_PRIORITY".equals(pri) || "java.lang.Thread.NORM_PRIORITY".equals(pri)) {
return Thread.NORM_PRIORITY;
} else if ("MAX_PRIORITY".equals(pri) || "java.lang.Thread.MAX_PRIORITY".equals(pri)) {
return Thread.MAX_PRIORITY;
}
try {
return Integer.parseInt(pri);
} catch (NumberFormatException ex) {}
return Thread.NORM_PRIORITY;
}
// TODO can we just move this to TestHelper?
/**
* Returns true if we're running on Android.
*
* @return true if running on Android, false otherwise
* @hidden Public because it's used by the tests.
*/
public static boolean isAndroid() {
return System.getProperty("java.vm.name").startsWith("Dalvik");
}
private static byte stringToMajor(String str) {
int pos = str.indexOf('.');
if (pos == -1) {
throw new ParseException("malformed version value in '" + str + "'");
}
String majStr = str.substring(0, pos);
int majVersion;
try {
majVersion = Integer.parseInt(majStr);
} catch (NumberFormatException ex) {
throw new ParseException("invalid version value in '" + str + "'", ex);
}
if (majVersion < 1 || majVersion > 255) {
throw new ParseException("range error in version '" + str + "'");
}
return (byte) majVersion;
}
private static byte stringToMinor(String str) {
int pos = str.indexOf('.');
if (pos == -1) {
throw new ParseException("malformed version value in '" + str + "'");
}
String minStr = str.substring(pos + 1, str.length());
int minVersion;
try {
minVersion = Integer.parseInt(minStr);
} catch (NumberFormatException ex) {
throw new ParseException("invalid version value in '" + str + "'", ex);
}
if (minVersion < 0 || minVersion > 255) {
throw new ParseException("range error in version '" + str + "'");
}
return (byte) minVersion;
}
private static String majorMinorToString(byte major, byte minor) {
StringBuilder str = new StringBuilder();
str.append(major < 0 ? major + 255 : (int) major);
str.append(".");
str.append(minor < 0 ? minor + 255 : (int) minor);
return str.toString();
}
/** The protocol version 1.0. */
public static final ProtocolVersion Protocol_1_0 = new ProtocolVersion((byte) 1, (byte) 0);
/** The encoding version 1.0. */
public static final EncodingVersion Encoding_1_0 = new EncodingVersion((byte) 1, (byte) 0);
/** The encoding version 1.1. */
public static final EncodingVersion Encoding_1_1 = new EncodingVersion((byte) 1, (byte) 1);
private static final java.lang.Object _processLoggerMutex = new java.lang.Object();
private static Logger _processLogger;
private Util() {}
}