You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2021/04/14 10:06:31 UTC
[ignite-3] branch main updated: IGNITE-14501: Ignite 3: Fix
toString implementations. (#88)
This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 28f0927 IGNITE-14501: Ignite 3: Fix toString implementations. (#88)
28f0927 is described below
commit 28f0927e7dd242fdb2c9c341dbac6e3939c65fe1
Author: Andrew V. Mashenkov <AM...@users.noreply.github.com>
AuthorDate: Wed Apr 14 13:06:17 2021 +0300
IGNITE-14501: Ignite 3: Fix toString implementations. (#88)
---
.../presto/bytecode/AnnotationDefinition.java | 4 +-
modules/cli/pom.xml | 6 +
.../ignite/cli/builtins/module/ModuleRegistry.java | 23 +-
.../builtins/module/StandardModuleDefinition.java | 8 +-
.../configuration/validation/ValidationIssue.java | 8 +-
.../internal/tostring/CircularStringBuilder.java | 277 +++
.../ignite/internal/tostring/ClassDescriptor.java | 86 +
.../ignite/internal/tostring/FieldDescriptor.java | 142 ++
.../internal/tostring/IgniteToStringBuilder.java | 2110 ++++++++++++++++++++
.../internal/tostring/IgniteToStringExclude.java} | 33 +-
.../internal/tostring/IgniteToStringInclude.java | 45 +
.../internal/tostring/IgniteToStringOrder.java} | 42 +-
.../org/apache/ignite/internal/tostring/S.java} | 31 +-
.../ignite/internal/tostring/SBLimitedLength.java | 287 +++
.../tostring/SensitiveDataLoggingPolicy.java} | 34 +-
.../ignite/internal/tostring/package-info.java} | 25 +-
.../apache/ignite/internal/util/IgniteUtils.java | 54 +
.../apache/ignite/lang/IgniteStringBuilder.java | 504 +++++
.../apache/ignite/lang/IgniteSystemProperties.java | 307 +++
.../internal/testframework/IgniteAbstractTest.java | 54 +
.../testframework/SystemPropertiesExtension.java | 198 ++
.../testframework/SystemPropertiesList.java} | 32 +-
.../internal/testframework/WithSystemProperty.java | 114 ++
.../internal/testframework/package-info.java} | 25 +-
.../tostring/CircularStringBuilderSelfTest.java | 72 +
.../tostring/IgniteToStringBuilderSelfTest.java | 994 +++++++++
.../tostring/SensitiveDataToStringTest.java | 180 ++
modules/network/pom.xml | 13 +-
.../org/apache/ignite/network/TestMessage.java | 17 +-
.../org/apache/ignite/network/ClusterNode.java | 7 +-
.../java/org/apache/ignite/raft/client/Peer.java | 6 +-
.../internal/schema/AbstractSchemaObject.java | 8 +-
.../org/apache/ignite/internal/schema/Bitmask.java | 7 +
.../org/apache/ignite/internal/schema/Column.java | 4 +-
.../apache/ignite/internal/schema/ColumnImpl.java | 10 +-
.../org/apache/ignite/internal/schema/Columns.java | 6 +
.../ignite/internal/schema/HashIndexImpl.java | 11 +-
.../ignite/internal/schema/IndexColumnImpl.java | 5 +-
.../apache/ignite/internal/schema/NativeType.java | 9 +-
.../ignite/internal/schema/NativeTypeSpec.java | 6 +-
.../ignite/internal/schema/PartialIndexImpl.java | 14 +-
.../ignite/internal/schema/PrimaryIndexImpl.java | 17 +
.../ignite/internal/schema/SchemaDescriptor.java | 6 +
.../ignite/internal/schema/SchemaTableImpl.java | 9 +-
.../internal/schema/SortedIndexColumnImpl.java | 6 +-
.../ignite/internal/schema/SortedIndexImpl.java | 15 +-
46 files changed, 5640 insertions(+), 231 deletions(-)
diff --git a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java
index 6a1d41d..4d35fc5 100644
--- a/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java
+++ b/modules/bytecode/src/main/java/com/facebook/presto/bytecode/AnnotationDefinition.java
@@ -13,6 +13,7 @@
*/
package com.facebook.presto.bytecode;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -126,8 +127,7 @@ public class AnnotationDefinition {
}
public Map<String, Object> getValues() {
- // todo we need an unmodifiable view
- return values;
+ return Collections.unmodifiableMap(values);
}
@SuppressWarnings("OverlyStrongTypeCast")
diff --git a/modules/cli/pom.xml b/modules/cli/pom.xml
index fed7c40..1506e54 100644
--- a/modules/cli/pom.xml
+++ b/modules/cli/pom.xml
@@ -41,6 +41,12 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<!-- 3-rd party dependencies. -->
<dependency>
<groupId>ch.qos.logback</groupId>
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ModuleRegistry.java b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ModuleRegistry.java
index d271ecd..b7bc791 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ModuleRegistry.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/ModuleRegistry.java
@@ -17,6 +17,10 @@
package org.apache.ignite.cli.builtins.module;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -24,12 +28,10 @@ import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonGetter;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ignite.cli.CliPathsConfigLoader;
import org.apache.ignite.cli.IgniteCLIException;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
/**
* The registry of installed CLI or Ignite server modules.
@@ -152,15 +154,19 @@ public class ModuleRegistry {
*/
public static class ModuleDefinition {
/** Module's name. */
+ @IgniteToStringInclude
public final String name;
/** Module's server artifacts. */
+ @IgniteToStringInclude
public final List<Path> artifacts;
/** Module's CLI artifacts. */
+ @IgniteToStringInclude
public final List<Path> cliArtifacts;
/** Type of module source. */
+ @IgniteToStringInclude
public final SourceType type;
/**
@@ -210,15 +216,8 @@ public class ModuleRegistry {
/** {@inheritDoc} */
@Override public String toString() {
- return "ModuleDefinition{" +
- "name='" + name + '\'' +
- ", artifacts=" + artifacts +
- ", cliArtifacts=" + cliArtifacts +
- ", type=" + type +
- ", source='" + src + '\'' +
- '}';
+ return S.toString(ModuleDefinition.class, this);
}
-
}
/**
diff --git a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/StandardModuleDefinition.java b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/StandardModuleDefinition.java
index 3050cfb..cfd38f3 100644
--- a/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/StandardModuleDefinition.java
+++ b/modules/cli/src/main/java/org/apache/ignite/cli/builtins/module/StandardModuleDefinition.java
@@ -19,6 +19,8 @@ package org.apache.ignite.cli.builtins.module;
import java.util.Collections;
import java.util.List;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
/**
* Definition of Ignite standard module.
@@ -27,15 +29,19 @@ import java.util.List;
*/
public class StandardModuleDefinition {
/** Module name. **/
+ @IgniteToStringInclude
public final String name;
/** Module description. */
+ @IgniteToStringInclude
public final String desc;
/** List of server artifacts. */
+ @IgniteToStringInclude
public final List<String> artifacts;
/** List of CLI tool artifacts. */
+ @IgniteToStringInclude
public final List<String> cliArtifacts;
/**
@@ -55,6 +61,6 @@ public class StandardModuleDefinition {
/** {@inheritDoc} */
@Override public String toString() {
- return name + ":\t" + desc;
+ return S.toString(StandardModuleDefinition.class, this);
}
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java b/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
index 28dd677..a91af05 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
@@ -16,10 +16,12 @@
*/
package org.apache.ignite.configuration.validation;
+import org.apache.ignite.internal.tostring.S;
+
/** */
public class ValidationIssue {
/** */
- private String message;
+ private final String message;
/** */
public ValidationIssue(String message) {
@@ -31,8 +33,8 @@ public class ValidationIssue {
return message;
}
- /** */
+ /** {@inheritDoc} */
@Override public String toString() {
- return "ValidationIssue [message=" + message + ']';
+ return S.toString(ValidationIssue.class, this);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/tostring/CircularStringBuilder.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/CircularStringBuilder.java
new file mode 100644
index 0000000..08a344d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/CircularStringBuilder.java
@@ -0,0 +1,277 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.util.Arrays;
+
+/**
+ * Basic string builder over circular buffer.
+ */
+class CircularStringBuilder {
+ /** Value */
+ private final char[] buf;
+
+ /** Writer position (0 if empty). */
+ private int pos;
+
+ /** Value is full flag. */
+ private boolean full;
+
+ /** Number of skipped characters */
+ private int skipped;
+
+ /**
+ * Creates an CircularStringBuilder of the specified capacity.
+ *
+ * @param capacity Buffer capacity.
+ */
+ CircularStringBuilder(int capacity) {
+ assert capacity > 0 : "Can't allocate CircularStringBuilder with capacity: " + capacity;
+
+ buf = new char[capacity];
+ pos = 0;
+ skipped = 0;
+ full = false;
+ }
+
+ /**
+ * Reset internal builder state
+ */
+ public void reset() {
+ Arrays.fill(buf, (char)0);
+
+ pos = 0;
+ full = false;
+ skipped = 0;
+ }
+
+ /**
+ * Returns the length (character count).
+ *
+ * @return the length of the sequence of characters currently
+ * represented by this object
+ */
+ public int length() {
+ return full ? buf.length : pos;
+ }
+
+ /**
+ * Returns the current capacity.
+ *
+ * @return the current capacity
+ */
+ public int capacity() {
+ return buf.length;
+ }
+
+ /**
+ * Appends the string representation of the {@code Object} argument.
+ *
+ * @param obj an {@code Object}.
+ * @return {@code this} for chaining.
+ */
+ public CircularStringBuilder append(Object obj) {
+ return append(String.valueOf(obj));
+ }
+
+ /**
+ * Appends the specified string to this character sequence.
+ *
+ * @param str a string.
+ * @return {@code this} for chaining.
+ */
+ public CircularStringBuilder append(String str) {
+ if (str == null)
+ return appendNull();
+
+ int strLen = str.length();
+
+ if (strLen == 0)
+ return this;
+ else if (strLen >= buf.length) {
+ // String bigger or equal to value length
+ str.getChars(strLen - buf.length, strLen, buf, 0);
+
+ skipped += strLen - buf.length + pos;
+
+ pos = buf.length;
+
+ full = true;
+ }
+ // String is shorter value length
+ else if (buf.length - pos < strLen) {
+ // String doesn't fit into remaining part of value array
+ int firstPart = buf.length - pos;
+
+ if (firstPart > 0)
+ str.getChars(0, firstPart, buf, pos);
+
+ str.getChars(firstPart, strLen, buf, 0);
+
+ skipped += full ? strLen : strLen - firstPart;
+
+ pos = pos + strLen - buf.length;
+
+ full = true;
+ }
+ else {
+ // Whole string fin into remaining part of value array
+ str.getChars(0, strLen, buf, pos);
+
+ skipped += full ? strLen : 0;
+
+ pos += strLen;
+ }
+
+ return this;
+ }
+
+ /**
+ * Append StringBuffer.
+ *
+ * @param sb StringBuffer to append.
+ * @return {@code this} for chaining.
+ */
+ public CircularStringBuilder append(StringBuffer sb) {
+ if (sb == null)
+ return appendNull();
+
+ int strLen = sb.length();
+
+ if (strLen == 0)
+ return this;
+ if (strLen >= buf.length) {
+ // String bigger or equal to value length
+ sb.getChars(strLen - buf.length, strLen, buf, 0);
+
+ skipped += strLen - buf.length + pos;
+
+ pos = buf.length;
+
+ full = true;
+ }
+ // String is shorter value length
+ else if (buf.length - pos < strLen) {
+ // String doesn't fit into remaining part of value array
+ int firstPart = buf.length - pos;
+
+ if (firstPart > 0)
+ sb.getChars(0, firstPart, buf, pos);
+
+ sb.getChars(firstPart, strLen, buf, 0);
+
+ skipped += full ? strLen : strLen - firstPart;
+
+ pos = pos + strLen - buf.length;
+
+ full = true;
+ }
+ else {
+ // Whole string fin into remaining part of value array
+ sb.getChars(0, strLen, buf, pos);
+
+ skipped += full ? strLen : 0;
+
+ pos += strLen;
+ }
+
+ return this;
+ }
+
+ /**
+ * Append StringBuilder.
+ *
+ * @param sb StringBuilder to append.
+ * @return {@code this} for chaining.
+ */
+ public CircularStringBuilder append(StringBuilder sb) {
+ if (sb == null)
+ return appendNull();
+
+ int strLen = sb.length();
+
+ if (strLen == 0)
+ return this;
+ if (strLen >= buf.length) {
+ // String bigger or equal to value length
+ sb.getChars(strLen - buf.length, strLen, buf, 0);
+
+ skipped += strLen - buf.length + pos;
+
+ pos = buf.length;
+
+ full = true;
+ }
+ // String is shorter value length
+ else if (buf.length - pos < strLen) {
+ // String doesn't fit into remaining part of value array
+ int firstPart = buf.length - pos;
+
+ if (firstPart > 0)
+ sb.getChars(0, firstPart, buf, pos);
+
+ sb.getChars(firstPart, strLen, buf, 0);
+
+ skipped += full ? strLen : strLen - firstPart;
+
+ pos = pos + strLen - buf.length;
+
+ full = true;
+ }
+ else {
+ // Whole string fin into remaining part of value array
+ sb.getChars(0, strLen, buf, pos);
+
+ skipped += full ? strLen : 0;
+
+ pos += strLen;
+ }
+
+ return this;
+ }
+
+ /**
+ * @return {@code this} for chaining.
+ */
+ private CircularStringBuilder appendNull() {
+ return append("null");
+ }
+
+ /**
+ * @return Count of skipped elements.
+ */
+ public int getSkipped() {
+ return skipped;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ // Create a copy, don't share the array
+ if (full && pos < buf.length) {
+ char[] tmpBuf = new char[buf.length];
+ int tailLen = buf.length - pos;
+
+ System.arraycopy(buf, pos, tmpBuf, 0, tailLen);
+ System.arraycopy(buf, 0, tmpBuf, tailLen, buf.length - tailLen);
+
+ return new String(tmpBuf, 0, tmpBuf.length);
+ }
+ else
+ return new String(buf, 0, pos);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/tostring/ClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/ClassDescriptor.java
new file mode 100644
index 0000000..d61ede9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/ClassDescriptor.java
@@ -0,0 +1,86 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Simple class descriptor containing simple and fully qualified class names as well as
+ * the list of class fields.
+ */
+class ClassDescriptor {
+ /** Class simple name. */
+ private final String sqn;
+
+ /** Class FQN. */
+ private final String fqn;
+
+ /** Class field descriptors. */
+ private final ArrayList<FieldDescriptor> fields = new ArrayList<>();
+
+ /**
+ * @param cls Class.
+ */
+ ClassDescriptor(Class<?> cls) {
+ assert cls != null;
+
+ fqn = cls.getName();
+ sqn = cls.getSimpleName();
+ }
+
+ /**
+ * @param field Field descriptor to be added.
+ */
+ void addField(FieldDescriptor field) {
+ assert field != null;
+
+ fields.add(field);
+ }
+
+ /**
+ *
+ */
+ void sortFields() {
+ fields.trimToSize();
+
+ fields.sort(Comparator.comparingInt(FieldDescriptor::getOrder));
+ }
+
+ /**
+ * @return Simple class name.
+ */
+ String getSimpleClassName() {
+ return sqn;
+ }
+
+ /**
+ * @return Fully qualified class name.
+ */
+ String getFullyQualifiedClassName() {
+ return fqn;
+ }
+
+ /**
+ * @return List of fields.
+ */
+ List<FieldDescriptor> getFields() {
+ return fields;
+ }
+}
\ No newline at end of file
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/tostring/FieldDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/FieldDescriptor.java
new file mode 100644
index 0000000..683fae5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/FieldDescriptor.java
@@ -0,0 +1,142 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import org.intellij.lang.annotations.MagicConstant;
+
+/**
+ * Simple field descriptor containing field name and its order in the class descriptor.
+ */
+class FieldDescriptor {
+ /** */
+ public static final int FIELD_TYPE_OBJECT = 0;
+
+ /** */
+ public static final int FIELD_TYPE_BYTE = 1;
+
+ /** */
+ public static final int FIELD_TYPE_BOOLEAN = 2;
+
+ /** */
+ public static final int FIELD_TYPE_CHAR = 3;
+
+ /** */
+ public static final int FIELD_TYPE_SHORT = 4;
+
+ /** */
+ public static final int FIELD_TYPE_INT = 5;
+
+ /** */
+ public static final int FIELD_TYPE_FLOAT = 6;
+
+ /** */
+ public static final int FIELD_TYPE_LONG = 7;
+
+ /** */
+ public static final int FIELD_TYPE_DOUBLE = 8;
+
+ /** Field name. */
+ private final String name;
+
+ /** */
+ private int order = Integer.MAX_VALUE;
+
+ /** Field VarHandle. */
+ private final VarHandle varHandle;
+
+ /** Numeric constant for the field's type. One of {@code FIELD_TYPE_*} constants of current class. */
+ private final int type;
+
+ /** Class of the field. Upper bound in case of generic field types. */
+ private final Class<?> cls;
+
+ /**
+ * @param field Field descriptor.
+ * @param varHandle Field VarHandle.
+ */
+ FieldDescriptor(Field field, VarHandle varHandle) {
+ assert (field.getModifiers() & Modifier.STATIC) == 0 : "Static fields are not allowed here: " + field;
+
+ this.varHandle = varHandle;
+
+ cls = field.getType();
+
+ name = field.getName();
+
+ if (!cls.isPrimitive())
+ type = FIELD_TYPE_OBJECT;
+ else {
+ if (cls == byte.class)
+ type = FIELD_TYPE_BYTE;
+ else if (cls == boolean.class)
+ type = FIELD_TYPE_BOOLEAN;
+ else if (cls == char.class)
+ type = FIELD_TYPE_CHAR;
+ else if (cls == short.class)
+ type = FIELD_TYPE_SHORT;
+ else if (cls == int.class)
+ type = FIELD_TYPE_INT;
+ else if (cls == float.class)
+ type = FIELD_TYPE_FLOAT;
+ else if (cls == long.class)
+ type = FIELD_TYPE_LONG;
+ else if (cls == double.class)
+ type = FIELD_TYPE_DOUBLE;
+ else
+ throw new IllegalArgumentException("Unexpected primitive type: " + cls);
+ }
+ }
+
+ /**
+ * @return Field order.
+ */
+ int getOrder() { return order; }
+
+ /**
+ * @param order Field order.
+ */
+ void setOrder(int order) { this.order = order; }
+
+ /**
+ * @return Field VarHandle.
+ */
+ public VarHandle varHandle() {
+ return varHandle;
+ }
+
+ /**
+ * @return Numeric constant for the field's type. One of {@code FIELD_TYPE_*} constants of current class.
+ */
+ @MagicConstant(valuesFromClass = FieldDescriptor.class)
+ public int type() {
+ return type;
+ }
+
+ /** */
+ public Class<?> fieldClass() {
+ return cls;
+ }
+
+ /**
+ * @return Field name.
+ */
+ String getName() { return name; }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringBuilder.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringBuilder.java
new file mode 100644
index 0000000..a990d2e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringBuilder.java
@@ -0,0 +1,2110 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.io.Externalizable;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.EventListener;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.apache.ignite.lang.IgniteInternalException;
+import org.apache.ignite.lang.IgniteStringBuilder;
+import org.apache.ignite.lang.IgniteSystemProperties;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static java.util.Objects.nonNull;
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_SENSITIVE_DATA_LOGGING;
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_TO_STRING_COLLECTION_LIMIT;
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_TO_STRING_IGNORE_RUNTIME_EXCEPTION;
+import static org.apache.ignite.lang.IgniteSystemProperties.getBoolean;
+import static org.apache.ignite.lang.IgniteSystemProperties.getInteger;
+import static org.apache.ignite.lang.IgniteSystemProperties.getString;
+
+/**
+ * Provides auto-generation framework for {@code toString()} output.
+ * <p>
+ * In case of recursion, object fields will be printed only for the first entry to prevent recursion,
+ * and all the next repeated entrances will be shown as "ClassName@hash".
+ * <p>
+ * Default exclusion policy (can be overridden with {@link IgniteToStringInclude} annotation):
+ * <ul>
+ * <li>fields with {@link IgniteToStringExclude} annotation
+ * <li>classes with {@link IgniteToStringExclude} annotation
+ * <li>static fields
+ * <li>non-private fields
+ * <li>arrays
+ * <li>fields of type {@link Object}
+ * <li>fields of type {@link Thread}
+ * <li>fields of type {@link Runnable}
+ * <li>fields of type {@link Serializable}
+ * <li>fields of type {@link Externalizable}
+ * <li>{@link InputStream} implementations
+ * <li>{@link OutputStream} implementations
+ * <li>{@link EventListener} implementations
+ * <li>{@link Lock} implementations
+ * <li>{@link ReadWriteLock} implementations
+ * <li>{@link Condition} implementations
+ * <li>{@link Map} implementations
+ * <li>{@link Collection} implementations
+ * </ul>
+ */
+@SuppressWarnings({"BooleanParameter", "NonFinalUtilityClass"})
+public class IgniteToStringBuilder {
+ /** Empty array instance. */
+ private static final Object[] EMPTY_ARRAY = new Object[0];
+
+ /** Max collection elements to be written. */
+ private static final int COLLECTION_LIMIT = getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
+
+ /** Ignore flag for runtime exceptions while building string. */
+ private static final boolean IGNORE_RUNTIME_EXCEPTION = !getBoolean(IGNITE_TO_STRING_IGNORE_RUNTIME_EXCEPTION, false);
+
+ /** Supplier for {@link #includeSensitive} with default behavior. */
+ private static final AtomicReference<Supplier<SensitiveDataLoggingPolicy>> SENS_DATA_LOG_SUP_REF =
+ new AtomicReference<>(new Supplier<>() {
+ /** Sensitive data logging policy. */
+ final SensitiveDataLoggingPolicy sensitiveDataLoggingPolicy =
+ SensitiveDataLoggingPolicy.valueOf(getString(IGNITE_SENSITIVE_DATA_LOGGING, "hash").toUpperCase());
+
+ /** {@inheritDoc} */
+ @Override public SensitiveDataLoggingPolicy get() {
+ return sensitiveDataLoggingPolicy;
+ }
+ });
+
+ /** Every thread has its own string builder. */
+ private static final ThreadLocal<SBLimitedLength> threadLocSB = ThreadLocal.withInitial(() ->
+ new SBLimitedLength(256));
+
+ /**
+ * Tracks the objects currently printing in the string builder.
+ * <p>
+ * Since {@code toString()} methods can be chain-called from the same thread we
+ * have to keep a map of this objects pointed to the position of previous occurrence
+ * and remove/add them in each {@code toString(...)} apply.
+ */
+ private static final ThreadLocal<IdentityHashMap<Object, EntryReference>> savedObjects = ThreadLocal.withInitial(IdentityHashMap::new);
+
+ /**
+ *
+ */
+ private static final Map<String, ClassDescriptor> classCache = new ConcurrentHashMap<>();
+
+ /**
+ * Initialization-on-demand holder.
+ *
+ * @see <a href= "https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom">
+ * "Initialization-on-demand holder idiom"</a>.
+ */
+ private static final class Holder {
+ /** Supplier holder for {@link #includeSensitive} and {@link #getSensitiveDataLogging}. */
+ static final Supplier<SensitiveDataLoggingPolicy> SENS_DATA_LOG_SUP = SENS_DATA_LOG_SUP_REF.get();
+ }
+
+ /**
+ * @return {@link SensitiveDataLoggingPolicy} Log levels for sensitive data
+ */
+ public static SensitiveDataLoggingPolicy getSensitiveDataLogging() {
+ return Holder.SENS_DATA_LOG_SUP.get();
+ }
+
+ /**
+ * Setting the logic of the {@link #includeSensitive} and {@link #getSensitiveDataLogging} methods.
+ * <p>
+ * Overrides default supplier that uses {@link IgniteSystemProperties#IGNITE_SENSITIVE_DATA_LOGGING}
+ * system property.
+ *
+ * <b>Important!</b> Changing the logic is possible only until the first
+ * call of {@link #includeSensitive} or {@link #getSensitiveDataLogging} methods.
+ *
+ * @param sup {@link SensitiveDataLoggingPolicy} supplier.
+ */
+ public static void setSensitiveDataLoggingPolicySupplier(Supplier<SensitiveDataLoggingPolicy> sup) {
+ assert nonNull(sup);
+
+ SENS_DATA_LOG_SUP_REF.set(sup);
+ }
+
+ /**
+ * Return include sensitive data flag.
+ *
+ * @return {@code true} if need to include sensitive data, {@code false} otherwise.
+ * @see IgniteToStringBuilder#setSensitiveDataLoggingPolicySupplier(Supplier)
+ */
+ public static boolean includeSensitive() {
+ return Holder.SENS_DATA_LOG_SUP.get() == SensitiveDataLoggingPolicy.PLAIN;
+ }
+
+ /**
+ * @param obj Object.
+ * @return Hexed identity hashcode.
+ */
+ public static String identity(Object obj) {
+ return '@' + Integer.toHexString(System.identityHashCode(obj));
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param name4 Additional parameter name.
+ * @param val4 Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0,
+ String name1, Object val1,
+ String name2, Object val2,
+ String name3, Object val3,
+ String name4, Object val4) {
+ return toString(cls,
+ obj,
+ name0, val0, false,
+ name1, val1, false,
+ name2, val2, false,
+ name3, val3, false,
+ name4, val4, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param name4 Additional parameter name.
+ * @param val4 Additional parameter value.
+ * @param name5 Additional parameter name.
+ * @param val5 Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0,
+ String name1, Object val1,
+ String name2, Object val2,
+ String name3, Object val3,
+ String name4, Object val4,
+ String name5, Object val5) {
+ return toString(cls,
+ obj,
+ name0, val0, false,
+ name1, val1, false,
+ name2, val2, false,
+ name3, val3, false,
+ name4, val4, false,
+ name5, val5, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param name4 Additional parameter name.
+ * @param val4 Additional parameter value.
+ * @param name5 Additional parameter name.
+ * @param val5 Additional parameter value.
+ * @param name6 Additional parameter name.
+ * @param val6 Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0,
+ String name1, Object val1,
+ String name2, Object val2,
+ String name3, Object val3,
+ String name4, Object val4,
+ String name5, Object val5,
+ String name6, Object val6) {
+ return toString(cls,
+ obj,
+ name0, val0, false,
+ name1, val1, false,
+ name2, val2, false,
+ name3, val3, false,
+ name4, val4, false,
+ name5, val5, false,
+ name6, val6, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param sens3 Property sensitive flag.
+ * @param name4 Additional parameter name.
+ * @param val4 Additional parameter value.
+ * @param sens4 Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0, boolean sens0,
+ String name1, Object val1, boolean sens1,
+ String name2, Object val2, boolean sens2,
+ String name3, Object val3, boolean sens3,
+ String name4, Object val4, boolean sens4) {
+ assert cls != null;
+ assert obj != null;
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+ assert name4 != null;
+
+ Object[] addNames = new Object[5];
+ Object[] addVals = new Object[5];
+ boolean[] addSens = new boolean[5];
+
+ addNames[0] = name0;
+ addVals[0] = val0;
+ addSens[0] = sens0;
+ addNames[1] = name1;
+ addVals[1] = val1;
+ addSens[1] = sens1;
+ addNames[2] = name2;
+ addVals[2] = val2;
+ addSens[2] = sens2;
+ addNames[3] = name3;
+ addVals[3] = val3;
+ addSens[3] = sens3;
+ addNames[4] = name4;
+ addVals[4] = val4;
+ addSens[4] = sens4;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 5);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param sens3 Property sensitive flag.
+ * @param name4 Additional parameter name.
+ * @param val4 Additional parameter value.
+ * @param sens4 Property sensitive flag.
+ * @param name5 Additional parameter name.
+ * @param val5 Additional parameter value.
+ * @param sens5 Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0, boolean sens0,
+ String name1, Object val1, boolean sens1,
+ String name2, Object val2, boolean sens2,
+ String name3, Object val3, boolean sens3,
+ String name4, Object val4, boolean sens4,
+ String name5, Object val5, boolean sens5) {
+ assert cls != null;
+ assert obj != null;
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+ assert name4 != null;
+ assert name5 != null;
+
+ Object[] addNames = new Object[6];
+ Object[] addVals = new Object[6];
+ boolean[] addSens = new boolean[6];
+
+ addNames[0] = name0;
+ addVals[0] = val0;
+ addSens[0] = sens0;
+ addNames[1] = name1;
+ addVals[1] = val1;
+ addSens[1] = sens1;
+ addNames[2] = name2;
+ addVals[2] = val2;
+ addSens[2] = sens2;
+ addNames[3] = name3;
+ addVals[3] = val3;
+ addSens[3] = sens3;
+ addNames[4] = name4;
+ addVals[4] = val4;
+ addSens[4] = sens4;
+ addNames[5] = name5;
+ addVals[5] = val5;
+ addSens[5] = sens5;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 6);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param sens3 Property sensitive flag.
+ * @param name4 Additional parameter name.
+ * @param val4 Additional parameter value.
+ * @param sens4 Property sensitive flag.
+ * @param name5 Additional parameter name.
+ * @param val5 Additional parameter value.
+ * @param sens5 Property sensitive flag.
+ * @param name6 Additional parameter name.
+ * @param val6 Additional parameter value.
+ * @param sens6 Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0, boolean sens0,
+ String name1, Object val1, boolean sens1,
+ String name2, Object val2, boolean sens2,
+ String name3, Object val3, boolean sens3,
+ String name4, Object val4, boolean sens4,
+ String name5, Object val5, boolean sens5,
+ String name6, Object val6, boolean sens6) {
+ assert cls != null;
+ assert obj != null;
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+ assert name4 != null;
+ assert name5 != null;
+ assert name6 != null;
+
+ Object[] addNames = new Object[7];
+ Object[] addVals = new Object[7];
+ boolean[] addSens = new boolean[7];
+
+ addNames[0] = name0;
+ addVals[0] = val0;
+ addSens[0] = sens0;
+ addNames[1] = name1;
+ addVals[1] = val1;
+ addSens[1] = sens1;
+ addNames[2] = name2;
+ addVals[2] = val2;
+ addSens[2] = sens2;
+ addNames[3] = name3;
+ addVals[3] = val3;
+ addSens[3] = sens3;
+ addNames[4] = name4;
+ addVals[4] = val4;
+ addSens[4] = sens4;
+ addNames[5] = name5;
+ addVals[5] = val5;
+ addSens[5] = sens5;
+ addNames[6] = name6;
+ addVals[6] = val6;
+ addSens[6] = sens6;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 7);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0,
+ String name1, Object val1,
+ String name2, Object val2,
+ String name3, Object val3) {
+ return toString(cls, obj,
+ name0, val0, false,
+ name1, val1, false,
+ name2, val2, false,
+ name3, val3, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Additional parameter name.
+ * @param val3 Additional parameter value.
+ * @param sens3 Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0, boolean sens0,
+ String name1, Object val1, boolean sens1,
+ String name2, Object val2, boolean sens2,
+ String name3, Object val3, boolean sens3) {
+ assert cls != null;
+ assert obj != null;
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+
+ Object[] addNames = new Object[4];
+ Object[] addVals = new Object[4];
+ boolean[] addSens = new boolean[4];
+
+ addNames[0] = name0;
+ addVals[0] = val0;
+ addSens[0] = sens0;
+ addNames[1] = name1;
+ addVals[1] = val1;
+ addSens[1] = sens1;
+ addNames[2] = name2;
+ addVals[2] = val2;
+ addSens[2] = sens2;
+ addNames[3] = name3;
+ addVals[3] = val3;
+ addSens[3] = sens3;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 4);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0,
+ String name1, Object val1,
+ String name2, Object val2) {
+ return toString(cls,
+ obj,
+ name0, val0, false,
+ name1, val1, false,
+ name2, val2, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Additional parameter name.
+ * @param val2 Additional parameter value.
+ * @param sens2 Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0, boolean sens0,
+ String name1, Object val1, boolean sens1,
+ String name2, Object val2, boolean sens2) {
+ assert cls != null;
+ assert obj != null;
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+
+ Object[] addNames = new Object[3];
+ Object[] addVals = new Object[3];
+ boolean[] addSens = new boolean[3];
+
+ addNames[0] = name0;
+ addVals[0] = val0;
+ addSens[0] = sens0;
+ addNames[1] = name1;
+ addVals[1] = val1;
+ addSens[1] = sens1;
+ addNames[2] = name2;
+ addVals[2] = val2;
+ addSens[2] = sens2;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 3);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0,
+ String name1, Object val1) {
+ return toString(cls, obj, name0, val0, false, name1, val1, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name0 Additional parameter name.
+ * @param val0 Additional parameter value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Additional parameter name.
+ * @param val1 Additional parameter value.
+ * @param sens1 Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj,
+ String name0, Object val0, boolean sens0,
+ String name1, Object val1, boolean sens1) {
+ assert cls != null;
+ assert obj != null;
+ assert name0 != null;
+ assert name1 != null;
+
+ Object[] addNames = new Object[2];
+ Object[] addVals = new Object[2];
+ boolean[] addSens = new boolean[2];
+
+ addNames[0] = name0;
+ addVals[0] = val0;
+ addSens[0] = sens0;
+ addNames[1] = name1;
+ addVals[1] = val1;
+ addSens[1] = sens1;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 2);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name Additional parameter name.
+ * @param val Additional parameter value.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj, String name, @Nullable Object val) {
+ return toString(cls, obj, name, val, false);
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param name Additional parameter name.
+ * @param val Additional parameter value.
+ * @param sens Property sensitive flag.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj, String name, @Nullable Object val, boolean sens) {
+ assert cls != null;
+ assert obj != null;
+ assert name != null;
+
+ Object[] addNames = new Object[1];
+ Object[] addVals = new Object[1];
+ boolean[] addSens = new boolean[1];
+
+ addNames[0] = name;
+ addVals[0] = val;
+ addSens[0] = sens;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 1);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj) {
+ assert cls != null;
+ assert obj != null;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(cls, sb, obj, EMPTY_ARRAY, EMPTY_ARRAY, null, 0);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces auto-generated output of string presentation for given object and its declaration class.
+ *
+ * @param <T> Type of the object.
+ * @param cls Declaration class of the object. Note that this should not be a runtime class.
+ * @param obj Object to get a string presentation for.
+ * @param parent String representation of parent.
+ * @return String presentation of the given object.
+ */
+ public static <T> String toString(Class<T> cls, T obj, String parent) {
+ return parent != null ? toString(cls, obj, "super", parent) : toString(cls, obj);
+ }
+
+ /**
+ * Print value with length limitation.
+ *
+ * @param buf buffer to print to.
+ * @param val value to print, can be {@code null}.
+ */
+ private static void toString(SBLimitedLength buf, Object val) {
+ toString(buf, null, val);
+ }
+
+ /**
+ * Print value with length limitation.
+ *
+ * @param buf buffer to print to.
+ * @param cls value class.
+ * @param val value to print.
+ */
+ private static void toString(SBLimitedLength buf, Class<?> cls, Object val) {
+ if (val == null) {
+ buf.a("null");
+
+ return;
+ }
+
+ if (cls == null)
+ cls = val.getClass();
+
+ if (cls.isPrimitive()) {
+ buf.a(val);
+
+ return;
+ }
+
+ IdentityHashMap<Object, EntryReference> svdObjs = savedObjects.get();
+
+ if (handleRecursion(buf, val, cls, svdObjs))
+ return;
+
+ svdObjs.put(val, new EntryReference(buf.length()));
+
+ try {
+ if (cls.isArray())
+ addArray(buf, cls, val);
+ else if (val instanceof Collection)
+ addCollection(buf, (Collection<?>)val);
+ else if (val instanceof Map)
+ addMap(buf, (Map<?, ?>)val);
+ else
+ buf.a(val);
+ }
+ finally {
+ svdObjs.remove(val);
+ }
+ }
+
+ /**
+ * Writes array to buffer.
+ *
+ * @param buf String builder buffer.
+ * @param arrType Type of the array.
+ * @param obj Array object.
+ */
+ private static void addArray(SBLimitedLength buf, Class arrType, Object obj) {
+ if (arrType.getComponentType().isPrimitive()) {
+ buf.a(arrayToString(obj));
+
+ return;
+ }
+
+ Object[] arr = (Object[])obj;
+
+ buf.a(arrType.getSimpleName()).a(" [");
+
+ for (int i = 0; i < arr.length; i++) {
+ toString(buf, arr[i]);
+
+ if (i == COLLECTION_LIMIT - 1 || i == arr.length - 1)
+ break;
+
+ buf.a(", ");
+ }
+
+ handleOverflow(buf, arr.length);
+
+ buf.a(']');
+ }
+
+ /**
+ * Writes collection to buffer.
+ *
+ * @param buf String builder buffer.
+ * @param col Collection object.
+ */
+ private static void addCollection(SBLimitedLength buf, Collection<?> col) {
+ buf.a(col.getClass().getSimpleName()).a(" [");
+
+ int cnt = 0;
+ boolean needHandleOverflow = true;
+
+ Iterator<?> iter = col.iterator();
+ int colSize = col.size();
+
+ while (iter.hasNext()) {
+ Object obj;
+
+ try {
+ obj = iter.next();
+ }
+ catch (ConcurrentModificationException e) {
+ handleConcurrentModification(buf, cnt, colSize);
+
+ needHandleOverflow = false;
+ break;
+ }
+
+ toString(buf, obj);
+
+ if (++cnt == COLLECTION_LIMIT || cnt == colSize)
+ break;
+
+ buf.a(", ");
+ }
+
+ if (needHandleOverflow)
+ handleOverflow(buf, colSize);
+
+ buf.a(']');
+ }
+
+ /**
+ * Writes map to buffer.
+ *
+ * @param buf String builder buffer.
+ * @param map Map object.
+ */
+ private static <K, V> void addMap(SBLimitedLength buf, Map<K, V> map) {
+ buf.a(map.getClass().getSimpleName()).a(" {");
+
+ int cnt = 0;
+ boolean needHandleOverflow = true;
+
+ Iterator<Map.Entry<K, V>> iter = map.entrySet().iterator();
+ int mapSize = map.size();
+
+ while (iter.hasNext()) {
+ Object key;
+ Object value;
+
+ try {
+ Map.Entry<K, V> entry = iter.next();
+
+ key = entry.getKey();
+ value = entry.getValue();
+ }
+ catch (ConcurrentModificationException e) {
+ handleConcurrentModification(buf, cnt, mapSize);
+
+ needHandleOverflow = false;
+ break;
+ }
+
+ toString(buf, key);
+
+ buf.a('=');
+
+ toString(buf, value);
+
+ if (++cnt == COLLECTION_LIMIT || cnt == mapSize)
+ break;
+
+ buf.a(", ");
+ }
+
+ if (needHandleOverflow)
+ handleOverflow(buf, mapSize);
+
+ buf.a('}');
+ }
+
+ /**
+ * Writes overflow message to buffer if needed.
+ *
+ * @param buf String builder buffer.
+ * @param size Size to compare with limit.
+ */
+ private static void handleOverflow(SBLimitedLength buf, int size) {
+ int overflow = size - COLLECTION_LIMIT;
+
+ if (overflow > 0)
+ buf.a("... and ").a(overflow).a(" more");
+ }
+
+ /**
+ * Writes message about situation of ConcurrentModificationException caught when iterating over collection.
+ *
+ * @param buf String builder buffer.
+ * @param writtenElements Number of elements successfully written to output.
+ * @param size Overall size of collection.
+ */
+ private static void handleConcurrentModification(SBLimitedLength buf, int writtenElements, int size) {
+ buf.a("... concurrent modification was detected, ").a(writtenElements).a(" out of ").a(size)
+ .a(" were written");
+ }
+
+ /**
+ * Creates an uniformed string presentation for the given object.
+ *
+ * @param <T> Type of object.
+ * @param cls Class of the object.
+ * @param buf String builder buffer.
+ * @param obj Object for which to get string presentation.
+ * @param addNames Names of additional values to be included.
+ * @param addVals Additional values to be included.
+ * @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
+ * @param addLen How many additional values will be included.
+ * @return String presentation of the given object.
+ */
+ private static <T> String toStringImpl(
+ Class<T> cls,
+ SBLimitedLength buf,
+ T obj,
+ Object[] addNames,
+ Object[] addVals,
+ @Nullable boolean[] addSens,
+ int addLen) {
+ assert cls != null;
+ assert buf != null;
+ assert obj != null;
+ assert addNames != null;
+ assert addVals != null;
+ assert addNames.length == addVals.length;
+ assert addLen <= addNames.length;
+
+ boolean newStr = buf.length() == 0;
+
+ IdentityHashMap<Object, EntryReference> svdObjs = savedObjects.get();
+
+ if (newStr)
+ svdObjs.put(obj, new EntryReference(buf.length()));
+
+ try {
+ int len = buf.length();
+
+ String s = toStringImpl0(cls, buf, obj, addNames, addVals, addSens, addLen);
+
+ if (newStr)
+ return s;
+
+ buf.setLength(len);
+
+ return s.substring(len);
+ }
+ finally {
+ if (newStr)
+ svdObjs.remove(obj);
+ }
+ }
+
+ /**
+ * Creates an uniformed string presentation for the given object.
+ *
+ * @param cls Class of the object.
+ * @param buf String builder buffer.
+ * @param obj Object for which to get string presentation.
+ * @param addNames Names of additional values to be included.
+ * @param addVals Additional values to be included.
+ * @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
+ * @param addLen How many additional values will be included.
+ * @param <T> Type of object.
+ * @return String presentation of the given object.
+ */
+ private static <T> String toStringImpl0(
+ Class<T> cls,
+ SBLimitedLength buf,
+ T obj,
+ Object[] addNames,
+ Object[] addVals,
+ @Nullable boolean[] addSens,
+ int addLen
+ ) {
+ try {
+ ClassDescriptor cd = getClassDescriptor(cls);
+
+ assert cd != null;
+
+ buf.a(cd.getSimpleClassName());
+
+ EntryReference ref = savedObjects.get().get(obj);
+
+ if (ref != null && ref.hashNeeded) {
+ buf.a(identity(obj));
+
+ ref.hashNeeded = false;
+ }
+
+ buf.a(" [");
+
+ boolean first = true;
+
+ for (FieldDescriptor fd : cd.getFields()) {
+ if (!first)
+ buf.a(", ");
+ else
+ first = false;
+
+ buf.a(fd.getName()).a('=');
+
+ final VarHandle fH = fd.varHandle();
+
+ switch (fd.type()) {
+ case FieldDescriptor.FIELD_TYPE_OBJECT:
+ try {
+ toString(buf, fd.fieldClass(), fH.get(obj));
+ }
+ catch (RuntimeException e) {
+ if (IGNORE_RUNTIME_EXCEPTION) {
+ buf.a("Runtime exception was caught when building string representation: " +
+ e.getMessage());
+ }
+ else
+ throw e;
+ }
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_BYTE:
+ buf.a((byte)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_BOOLEAN:
+ buf.a((boolean)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_CHAR:
+ buf.a((char)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_SHORT:
+ buf.a((short)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_INT:
+ buf.a((int)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_FLOAT:
+ buf.a((float)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_LONG:
+ buf.a((long)fH.get(obj));
+
+ break;
+ case FieldDescriptor.FIELD_TYPE_DOUBLE:
+ buf.a((double)fH.get(obj));
+
+ break;
+ }
+ }
+
+ appendVals(buf, first, addNames, addVals, addSens, addLen);
+
+ buf.a(']');
+
+ return buf.toString();
+ }
+ // Specifically catching all exceptions.
+ catch (Exception e) {
+ // Remove entry from cache to avoid potential memory leak
+ // in case new class loader got loaded under the same identity hash.
+ classCache.remove(cls.getName() + System.identityHashCode(cls.getClassLoader()));
+
+ // No other option here.
+ throw new IgniteInternalException(e);
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name Property name.
+ * @param val Property value.
+ * @return String presentation.
+ */
+ public static String toString(String str, String name, @Nullable Object val) {
+ return toString(str, name, val, false);
+ }
+
+ /**
+ * Returns limited string representation of array.
+ *
+ * @param arr Array object. Each value is automatically wrapped if it has a primitive type.
+ * @return String representation of an array.
+ */
+ public static String arrayToString(Object arr) {
+ if (arr == null)
+ return "null";
+
+ String res;
+
+ int arrLen;
+
+ if (arr instanceof Object[]) {
+ Object[] objArr = (Object[])arr;
+
+ arrLen = objArr.length;
+
+ if (arrLen > COLLECTION_LIMIT)
+ objArr = Arrays.copyOf(objArr, COLLECTION_LIMIT);
+
+ res = Arrays.toString(objArr);
+ }
+ else {
+ res = toStringWithLimit(arr, COLLECTION_LIMIT);
+
+ arrLen = Array.getLength(arr);
+ }
+
+ if (arrLen > COLLECTION_LIMIT) {
+ StringBuilder resSB = new StringBuilder(res);
+
+ resSB.deleteCharAt(resSB.length() - 1);
+
+ resSB.append("... and ").append(arrLen - COLLECTION_LIMIT).append(" more]");
+
+ res = resSB.toString();
+ }
+
+ return res;
+ }
+
+ /**
+ * Returns limited string representation of array.
+ *
+ * @param arr Input array. Each value is automatically wrapped if it has a primitive type.
+ * @param limit max array items to string limit.
+ * @return String representation of an array.
+ */
+ private static String toStringWithLimit(Object arr, int limit) {
+ int arrIdxMax = Array.getLength(arr) - 1;
+
+ if (arrIdxMax == -1)
+ return "[]";
+
+ int idxMax = Math.min(arrIdxMax, limit);
+
+ StringBuilder b = new StringBuilder();
+
+ b.append('[');
+
+ for (int i = 0; i <= idxMax; ++i) {
+ b.append(Array.get(arr, i));
+
+ if (i == idxMax)
+ return b.append(']').toString();
+
+ b.append(", ");
+ }
+
+ return b.toString();
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name Property name.
+ * @param val Property value.
+ * @param sens Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str, String name, @Nullable Object val, boolean sens) {
+ assert name != null;
+
+ Object[] propNames = new Object[1];
+ Object[] propVals = new Object[1];
+ boolean[] propSens = new boolean[1];
+
+ propNames[0] = name;
+ propVals[0] = val;
+ propSens[0] = sens;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 1);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @return String presentation.
+ */
+ public static String toString(String str, String name0, @Nullable Object val0, String name1,
+ @Nullable Object val1) {
+ return toString(str, name0, val0, false, name1, val1, false);
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param name2 Property name.
+ * @param val2 Property value.
+ * @return String presentation.
+ */
+ public static String toString(String str, String name0, @Nullable Object val0, String name1,
+ @Nullable Object val1, String name2, @Nullable Object val2) {
+ return toString(str, name0, val0, false, name1, val1, false, name2, val2, false);
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param sens1 Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str,
+ String name0, @Nullable Object val0, boolean sens0,
+ String name1, @Nullable Object val1, boolean sens1) {
+ assert name0 != null;
+ assert name1 != null;
+
+ Object[] propNames = new Object[2];
+ Object[] propVals = new Object[2];
+ boolean[] propSens = new boolean[2];
+
+ propNames[0] = name0;
+ propVals[0] = val0;
+ propSens[0] = sens0;
+ propNames[1] = name1;
+ propVals[1] = val1;
+ propSens[1] = sens1;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 2);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Property name.
+ * @param val2 Property value.
+ * @param sens2 Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str,
+ String name0, @Nullable Object val0, boolean sens0,
+ String name1, @Nullable Object val1, boolean sens1,
+ String name2, @Nullable Object val2, boolean sens2) {
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+
+ Object[] propNames = new Object[3];
+ Object[] propVals = new Object[3];
+ boolean[] propSens = new boolean[3];
+
+ propNames[0] = name0;
+ propVals[0] = val0;
+ propSens[0] = sens0;
+ propNames[1] = name1;
+ propVals[1] = val1;
+ propSens[1] = sens1;
+ propNames[2] = name2;
+ propVals[2] = val2;
+ propSens[2] = sens2;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 3);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Property name.
+ * @param val2 Property value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Property name.
+ * @param val3 Property value.
+ * @param sens3 Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str,
+ String name0, @Nullable Object val0, boolean sens0,
+ String name1, @Nullable Object val1, boolean sens1,
+ String name2, @Nullable Object val2, boolean sens2,
+ String name3, @Nullable Object val3, boolean sens3) {
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+
+ Object[] propNames = new Object[4];
+ Object[] propVals = new Object[4];
+ boolean[] propSens = new boolean[4];
+
+ propNames[0] = name0;
+ propVals[0] = val0;
+ propSens[0] = sens0;
+ propNames[1] = name1;
+ propVals[1] = val1;
+ propSens[1] = sens1;
+ propNames[2] = name2;
+ propVals[2] = val2;
+ propSens[2] = sens2;
+ propNames[3] = name3;
+ propVals[3] = val3;
+ propSens[3] = sens3;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 4);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Property name.
+ * @param val2 Property value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Property name.
+ * @param val3 Property value.
+ * @param sens3 Property sensitive flag.
+ * @param name4 Property name.
+ * @param val4 Property value.
+ * @param sens4 Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str,
+ String name0, @Nullable Object val0, boolean sens0,
+ String name1, @Nullable Object val1, boolean sens1,
+ String name2, @Nullable Object val2, boolean sens2,
+ String name3, @Nullable Object val3, boolean sens3,
+ String name4, @Nullable Object val4, boolean sens4) {
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+ assert name4 != null;
+
+ Object[] propNames = new Object[5];
+ Object[] propVals = new Object[5];
+ boolean[] propSens = new boolean[5];
+
+ propNames[0] = name0;
+ propVals[0] = val0;
+ propSens[0] = sens0;
+ propNames[1] = name1;
+ propVals[1] = val1;
+ propSens[1] = sens1;
+ propNames[2] = name2;
+ propVals[2] = val2;
+ propSens[2] = sens2;
+ propNames[3] = name3;
+ propVals[3] = val3;
+ propSens[3] = sens3;
+ propNames[4] = name4;
+ propVals[4] = val4;
+ propSens[4] = sens4;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 5);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Property name.
+ * @param val2 Property value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Property name.
+ * @param val3 Property value.
+ * @param sens3 Property sensitive flag.
+ * @param name4 Property name.
+ * @param val4 Property value.
+ * @param sens4 Property sensitive flag.
+ * @param name5 Property name.
+ * @param val5 Property value.
+ * @param sens5 Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str,
+ String name0, @Nullable Object val0, boolean sens0,
+ String name1, @Nullable Object val1, boolean sens1,
+ String name2, @Nullable Object val2, boolean sens2,
+ String name3, @Nullable Object val3, boolean sens3,
+ String name4, @Nullable Object val4, boolean sens4,
+ String name5, @Nullable Object val5, boolean sens5) {
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+ assert name4 != null;
+ assert name5 != null;
+
+ Object[] propNames = new Object[6];
+ Object[] propVals = new Object[6];
+ boolean[] propSens = new boolean[6];
+
+ propNames[0] = name0;
+ propVals[0] = val0;
+ propSens[0] = sens0;
+ propNames[1] = name1;
+ propVals[1] = val1;
+ propSens[1] = sens1;
+ propNames[2] = name2;
+ propVals[2] = val2;
+ propSens[2] = sens2;
+ propNames[3] = name3;
+ propVals[3] = val3;
+ propSens[3] = sens3;
+ propNames[4] = name4;
+ propVals[4] = val4;
+ propSens[4] = sens4;
+ propNames[5] = name5;
+ propVals[5] = val5;
+ propSens[5] = sens5;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 6);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param name0 Property name.
+ * @param val0 Property value.
+ * @param sens0 Property sensitive flag.
+ * @param name1 Property name.
+ * @param val1 Property value.
+ * @param sens1 Property sensitive flag.
+ * @param name2 Property name.
+ * @param val2 Property value.
+ * @param sens2 Property sensitive flag.
+ * @param name3 Property name.
+ * @param val3 Property value.
+ * @param sens3 Property sensitive flag.
+ * @param name4 Property name.
+ * @param val4 Property value.
+ * @param sens4 Property sensitive flag.
+ * @param name5 Property name.
+ * @param val5 Property value.
+ * @param sens5 Property sensitive flag.
+ * @param name6 Property name.
+ * @param val6 Property value.
+ * @param sens6 Property sensitive flag.
+ * @return String presentation.
+ */
+ public static String toString(String str,
+ String name0, @Nullable Object val0, boolean sens0,
+ String name1, @Nullable Object val1, boolean sens1,
+ String name2, @Nullable Object val2, boolean sens2,
+ String name3, @Nullable Object val3, boolean sens3,
+ String name4, @Nullable Object val4, boolean sens4,
+ String name5, @Nullable Object val5, boolean sens5,
+ String name6, @Nullable Object val6, boolean sens6) {
+ assert name0 != null;
+ assert name1 != null;
+ assert name2 != null;
+ assert name3 != null;
+ assert name4 != null;
+ assert name5 != null;
+ assert name6 != null;
+
+ Object[] propNames = new Object[7];
+ Object[] propVals = new Object[7];
+ boolean[] propSens = new boolean[7];
+
+ propNames[0] = name0;
+ propVals[0] = val0;
+ propSens[0] = sens0;
+ propNames[1] = name1;
+ propVals[1] = val1;
+ propSens[1] = sens1;
+ propNames[2] = name2;
+ propVals[2] = val2;
+ propSens[2] = sens2;
+ propNames[3] = name3;
+ propVals[3] = val3;
+ propSens[3] = sens3;
+ propNames[4] = name4;
+ propVals[4] = val4;
+ propSens[4] = sens4;
+ propNames[5] = name5;
+ propVals[5] = val5;
+ propSens[5] = sens5;
+ propNames[6] = name6;
+ propVals[6] = val6;
+ propSens[6] = sens6;
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, 7);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Produces uniformed output of string with context properties
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param triplets Triplets {@code {name, value, sensitivity}}.
+ * @return String presentation.
+ */
+ public static String toString(String str, Object... triplets) {
+ if (triplets.length % 3 != 0)
+ throw new IllegalArgumentException("Array length must be a multiple of 3");
+
+ int propCnt = triplets.length / 3;
+
+ Object[] propNames = new Object[propCnt];
+ Object[] propVals = new Object[propCnt];
+ boolean[] propSens = new boolean[propCnt];
+
+ for (int i = 0; i < propCnt; i++) {
+ Object name = triplets[i * 3];
+
+ assert name != null;
+
+ propNames[i] = name;
+
+ propVals[i] = triplets[i * 3 + 1];
+
+ Object sens = triplets[i * 3 + 2];
+
+ assert sens instanceof Boolean;
+
+ propSens[i] = (Boolean)sens;
+ }
+
+ SBLimitedLength sb = threadLocSB.get();
+
+ boolean newStr = sb.length() == 0;
+
+ try {
+ return toStringImpl(str, sb, propNames, propVals, propSens, propCnt);
+ }
+ finally {
+ if (newStr)
+ sb.reset();
+ }
+ }
+
+ /**
+ * Creates an uniformed string presentation for the binary-like object.
+ *
+ * @param str Output prefix or {@code null} if empty.
+ * @param buf String builder buffer.
+ * @param propNames Names of object properties.
+ * @param propVals Property values.
+ * @param propSens Sensitive flag of values or {@code null} if all values is not sensitive.
+ * @param propCnt Properties count.
+ * @return String presentation of the object.
+ */
+ private static String toStringImpl(String str, SBLimitedLength buf, Object[] propNames, Object[] propVals,
+ boolean[] propSens, int propCnt) {
+
+ boolean newStr = buf.length() == 0;
+
+ if (str != null)
+ buf.a(str).a(" ");
+
+ buf.a("[");
+
+ appendVals(buf, true, propNames, propVals, propSens, propCnt);
+
+ buf.a(']');
+
+ if (newStr)
+ return buf.toString();
+
+ // Called from another ITSB.toString(), so this string is already in the buffer and shouldn't be returned.
+ return "";
+ }
+
+ /**
+ * Append additional values to the buffer.
+ *
+ * @param buf Buffer.
+ * @param first First value flag.
+ * @param addNames Names of additional values to be included.
+ * @param addVals Additional values to be included.
+ * @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
+ * @param addLen How many additional values will be included.
+ */
+ private static void appendVals(SBLimitedLength buf,
+ boolean first,
+ Object[] addNames,
+ Object[] addVals,
+ boolean[] addSens,
+ int addLen) {
+ if (addLen > 0) {
+ for (int i = 0; i < addLen; i++) {
+ Object addVal = addVals[i];
+
+ if (addVal != null) {
+ if (addSens != null && addSens[i] && !includeSensitive())
+ continue;
+
+ IgniteToStringInclude incAnn = addVal.getClass().getAnnotation(IgniteToStringInclude.class);
+
+ if (incAnn != null && incAnn.sensitive() && !includeSensitive())
+ continue;
+ }
+
+ if (!first)
+ buf.a(", ");
+ else
+ first = false;
+
+ buf.a(addNames[i]).a('=');
+
+ toString(buf, addVal);
+ }
+ }
+ }
+
+ /**
+ * @param cls Class.
+ * @param <T> Type of the object.
+ * @return Descriptor for the class.
+ * @throws IllegalAccessException If failed.
+ */
+ @SuppressWarnings({"TooBroadScope"})
+ private static <T> ClassDescriptor getClassDescriptor(Class<T> cls) throws IllegalAccessException {
+ assert cls != null;
+
+ String key = cls.getName() + System.identityHashCode(cls.getClassLoader());
+
+ ClassDescriptor cd = classCache.get(key);
+
+ if (cd != null)
+ return cd;
+
+ cd = new ClassDescriptor(cls);
+
+ final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(cls, MethodHandles.lookup());
+
+ for (Field f : cls.getDeclaredFields()) {
+ boolean add = false;
+
+ Class<?> type = f.getType();
+
+ final IgniteToStringInclude incFld = f.getAnnotation(IgniteToStringInclude.class);
+ final IgniteToStringInclude incType = type.getAnnotation(IgniteToStringInclude.class);
+
+ if (incFld != null || incType != null) {
+ // Information is not sensitive when both the field and the field type are not sensitive.
+ // When @IgniteToStringInclude is not present then the flag is false by default for that attribute.
+ final boolean notSens = (incFld == null || !incFld.sensitive()) && (incType == null || !incType.sensitive());
+ add = notSens || includeSensitive();
+ }
+ else if (!f.isAnnotationPresent(IgniteToStringExclude.class) &&
+ !type.isAnnotationPresent(IgniteToStringExclude.class)
+ ) {
+ if (
+ // Include only private non-static
+ Modifier.isPrivate(f.getModifiers()) && !Modifier.isStatic(f.getModifiers()) &&
+
+ // No direct objects & serializable.
+ Object.class != type &&
+ Serializable.class != type &&
+ Externalizable.class != type &&
+
+ // No arrays.
+ !type.isArray() &&
+
+ // Exclude collections, IO, etc.
+ !EventListener.class.isAssignableFrom(type) &&
+ !Map.class.isAssignableFrom(type) &&
+ !Collection.class.isAssignableFrom(type) &&
+ !InputStream.class.isAssignableFrom(type) &&
+ !OutputStream.class.isAssignableFrom(type) &&
+ !Thread.class.isAssignableFrom(type) &&
+ !Runnable.class.isAssignableFrom(type) &&
+ !Lock.class.isAssignableFrom(type) &&
+ !ReadWriteLock.class.isAssignableFrom(type) &&
+ !Condition.class.isAssignableFrom(type)
+ )
+ add = true;
+ }
+
+ if (add) {
+ FieldDescriptor fd = new FieldDescriptor(f, lookup.unreflectVarHandle(f));
+
+ // Get order, if any.
+ final IgniteToStringOrder annOrder = f.getAnnotation(IgniteToStringOrder.class);
+ if (annOrder != null)
+ fd.setOrder(annOrder.value());
+
+ cd.addField(fd);
+ }
+ }
+
+ cd.sortFields();
+
+ classCache.putIfAbsent(key, cd);
+
+ return cd;
+ }
+
+ /**
+ * Returns sorted and compacted string representation of given {@code col}.
+ * Two nearby numbers with difference at most 1 are compacted to one continuous segment.
+ * E.g. collection of [1, 2, 3, 5, 6, 7, 10] will be compacted to [1-3, 5-7, 10].
+ *
+ * @param col Collection of integers.
+ * @return Compacted string representation of given collections.
+ */
+ public static String compact(Collection<Integer> col) {
+ return compact(col, i -> i + 1);
+ }
+
+ /**
+ * Returns sorted and compacted string representation of given {@code col}.
+ * Two nearby numbers are compacted to one continuous segment.
+ * E.g. collection of [1, 2, 3, 5, 6, 7, 10] with
+ * {@code nextValFun = i -> i + 1} will be compacted to [1-3, 5-7, 10].
+ *
+ * @param col Collection of numbers.
+ * @param nextValFun Function to get nearby number.
+ * @return Compacted string representation of given collections.
+ */
+ public static <T extends Number & Comparable<? super T>> String compact(
+ Collection<T> col,
+ Function<T, T> nextValFun
+ ) {
+ assert nonNull(col);
+ assert nonNull(nextValFun);
+
+ if (col.isEmpty())
+ return "[]";
+
+ IgniteStringBuilder sb = new IgniteStringBuilder();
+ sb.a('[');
+
+ List<T> l = new ArrayList<>(col);
+ Collections.sort(l);
+
+ T left = l.get(0), right = left;
+ for (int i = 1; i < l.size(); i++) {
+ T val = l.get(i);
+
+ if (right.compareTo(val) == 0 || nextValFun.apply(right).compareTo(val) == 0) {
+ right = val;
+ continue;
+ }
+
+ if (left.compareTo(right) == 0)
+ sb.a(left);
+ else
+ sb.a(left).a('-').a(right);
+
+ sb.a(',').a(' ');
+
+ left = right = val;
+ }
+
+ if (left.compareTo(right) == 0)
+ sb.a(left);
+ else
+ sb.a(left).a('-').a(right);
+
+ sb.a(']');
+
+ return sb.toString();
+ }
+
+ /**
+ * Checks that object is already saved.
+ * In positive case this method inserts hash to the saved object entry (if needed) and name@hash for current entry.
+ * Further toString operations are not needed for current object.
+ *
+ * @param buf String builder buffer.
+ * @param obj Object.
+ * @param cls Class.
+ * @param svdObjs Map with saved objects to handle recursion.
+ * @return {@code True} if object is already saved and name@hash was added to buffer.
+ * {@code False} if it wasn't saved previously and it should be saved.
+ */
+ private static boolean handleRecursion(
+ SBLimitedLength buf,
+ Object obj,
+ @NotNull Class<?> cls,
+ IdentityHashMap<Object, EntryReference> svdObjs
+ ) {
+ EntryReference ref = svdObjs.get(obj);
+
+ if (ref == null)
+ return false;
+
+ int pos = ref.pos;
+
+ String name = cls.getSimpleName();
+ String hash = identity(obj);
+ String savedName = name + hash;
+ String charsAtPos = buf.impl().substring(pos, pos + savedName.length());
+
+ if (!buf.isOverflowed() && !savedName.equals(charsAtPos)) {
+ if (charsAtPos.startsWith(cls.getSimpleName())) {
+ buf.i(pos + name.length(), hash);
+
+ incValues(svdObjs, obj, hash.length());
+ }
+ else
+ ref.hashNeeded = true;
+ }
+
+ buf.a(savedName);
+
+ return true;
+ }
+
+ /**
+ * Increment positions of already presented objects afterward given object.
+ *
+ * @param svdObjs Map with objects already presented in the buffer.
+ * @param obj Object.
+ * @param hashLen Length of the object's hash.
+ */
+ private static void incValues(IdentityHashMap<Object, EntryReference> svdObjs, Object obj, int hashLen) {
+ int baseline = svdObjs.get(obj).pos;
+
+ for (IdentityHashMap.Entry<Object, EntryReference> entry : svdObjs.entrySet()) {
+ EntryReference ref = entry.getValue();
+
+ int pos = ref.pos;
+
+ if (pos > baseline)
+ ref.pos = pos + hashLen;
+ }
+ }
+
+ /**
+ * Stub.
+ */
+ protected IgniteToStringBuilder() {
+ // No op.
+ }
+
+ /**
+ *
+ */
+ private static class EntryReference {
+ /** Position. */
+ int pos;
+
+ /** First object entry needs hash to be written. */
+ boolean hashNeeded;
+
+ /**
+ * @param pos Position.
+ */
+ private EntryReference(int pos) {
+ this.pos = pos;
+ hashNeeded = false;
+ }
+ }
+}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringExclude.java
similarity index 57%
copy from modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
copy to modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringExclude.java
index a7fe166..48348d5 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringExclude.java
@@ -15,27 +15,22 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.schema;
+package org.apache.ignite.internal.tostring;
-import org.apache.ignite.schema.IndexColumn;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * Non-ordered index column.
+ * Class or field annotated with IgniteToStringInclude claims the element <b>must be</b> excluded
+ * from {@code toString()} output.
+ * This annotation is used to override the default exclusion policy.
*/
-class IndexColumnImpl extends AbstractSchemaObject implements IndexColumn {
- /**
- * Constructor.
- *
- * @param name Column name.
- */
- IndexColumnImpl(String name) {
- super(name);
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return "Column[" +
- "name='" + name() + '\'' +
- ']';
- }
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.TYPE})
+public @interface IgniteToStringExclude {
+ // No-op.
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringInclude.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringInclude.java
new file mode 100644
index 0000000..850ab69
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringInclude.java
@@ -0,0 +1,45 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.ignite.lang.IgniteSystemProperties;
+
+/**
+ * Class or field annotated with IgniteToStringInclude claims the element <b>should</b> be included
+ * in {@code toString()} output.
+ * This annotation is used to override the default exclusion policy.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.TYPE})
+public @interface IgniteToStringInclude {
+ /**
+ * A flag indicating if sensitive information stored in the field or fields of the class.<br/>
+ * Such information will be included to {@code toString()} output according to
+ * {@link IgniteSystemProperties#IGNITE_SENSITIVE_DATA_LOGGING} policy.
+ *
+ * @return Attribute value.
+ * @see SensitiveDataLoggingPolicy}.
+ */
+ boolean sensitive() default false;
+}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringOrder.java
similarity index 53%
copy from modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java
copy to modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringOrder.java
index 9ad2d9f..db91ad1 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/IgniteToStringOrder.java
@@ -15,36 +15,26 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.schema;
+package org.apache.ignite.internal.tostring;
-import org.apache.ignite.schema.SchemaObject;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * Schema object base class.
+ * Indicates field order in {@code toString()} output if set.
+ * Fields with smaller order value will come earlier the others in {@code toString()} output.
+ * By default, the order is the same as the order of declaration in the class.
+ * If order is not specified the {@link Integer#MAX_VALUE} will be used.
*/
-public abstract class AbstractSchemaObject implements SchemaObject {
- /** Schema object name. */
- private final String name;
-
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface IgniteToStringOrder {
/**
- * Constructor.
- *
- * @param name Schema object name.
+ * Numeric order value.
*/
- protected AbstractSchemaObject(String name) {
- this.name = name;
- }
-
- /** {@inheritDoc} */
- @Override public String name() {
- return name;
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return "SchemaObject[" +
- "name='" + name + '\'' +
- "class=" + getClass().getName() +
- ']';
- }
+ int value();
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/S.java
similarity index 63%
copy from modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
copy to modules/core/src/main/java/org/apache/ignite/internal/tostring/S.java
index 28dd677..1ab2322 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/S.java
@@ -14,25 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.ignite.configuration.validation;
-/** */
-public class ValidationIssue {
- /** */
- private String message;
+package org.apache.ignite.internal.tostring;
- /** */
- public ValidationIssue(String message) {
- this.message = message;
- }
-
- /** */
- public String message() {
- return message;
- }
-
- /** */
- @Override public String toString() {
- return "ValidationIssue [message=" + message + ']';
- }
-}
+/**
+ * Defines a shortcut for {@link IgniteToStringBuilder}.
+ *
+ * Since Java doesn't provide type aliases (like Scala, for example) we resort to these types of measures.
+ * Intended for internal use only and meant to provide for more terse code when readability of code is not compromised.
+ */
+@SuppressWarnings({"ExtendsUtilityClass"})
+public final class S extends IgniteToStringBuilder {
+ /* No-op. */
+}
\ No newline at end of file
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/tostring/SBLimitedLength.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/SBLimitedLength.java
new file mode 100644
index 0000000..b09fc2e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/SBLimitedLength.java
@@ -0,0 +1,287 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.util.Arrays;
+import org.apache.ignite.lang.IgniteStringBuilder;
+import org.apache.ignite.lang.IgniteSystemProperties;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_TO_STRING_MAX_LENGTH;
+
+/**
+ * String builder with limited length.
+ * <p>
+ * Keeps head and tail for long strings that not fit to the limit cutting the middle of the string.
+ */
+class SBLimitedLength extends IgniteStringBuilder {
+ /** Max string length. */
+ private static final int MAX_STR_LEN = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_MAX_LENGTH, 10_000);
+
+ /** Length of tail part of message. */
+ private static final int TAIL_LEN = MAX_STR_LEN / 10 * 2;
+
+ /** Length of head part of message. */
+ private static final int HEAD_LEN = MAX_STR_LEN - TAIL_LEN;
+
+ /** Additional string builder to get tail of message. */
+ private CircularStringBuilder tail;
+
+ /**
+ * @param cap Capacity.
+ */
+ SBLimitedLength(int cap) {
+ super(cap);
+
+ tail = null;
+ }
+
+ /**
+ * Resets buffer.
+ */
+ public void reset() {
+ setLength(0);
+
+ if (tail != null)
+ tail.reset();
+ }
+
+ /**
+ * @return tail string builder.
+ */
+ public @Nullable CircularStringBuilder getTail() {
+ return tail;
+ }
+
+ /**
+ * @return This builder.
+ */
+ private SBLimitedLength onWrite() {
+ if (!isOverflowed())
+ return this;
+
+ if (tail == null)
+ tail = new CircularStringBuilder(TAIL_LEN);
+
+ if (tail.length() == 0) {
+ int newSbLen = Math.min(length(), HEAD_LEN + 1);
+ tail.append(impl().substring(newSbLen));
+
+ setLength(newSbLen);
+ }
+
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(Object obj) {
+ if (isOverflowed()) {
+ tail.append(obj);
+ return this;
+ }
+
+ super.a(obj);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(String str) {
+ if (isOverflowed()) {
+ tail.append(str);
+ return this;
+ }
+
+ super.a(str);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(StringBuffer sb) {
+ if (isOverflowed()) {
+ tail.append(sb);
+ return this;
+ }
+
+ super.a(sb);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(CharSequence s) {
+ if (isOverflowed()) {
+ tail.append(s);
+ return this;
+ }
+
+ super.a(s);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(CharSequence s, int start, int end) {
+ if (isOverflowed()) {
+ tail.append(s.subSequence(start, end));
+ return this;
+ }
+
+ super.a(s, start, end);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(char[] str) {
+ if (isOverflowed()) {
+ tail.append(str);
+ return this;
+ }
+
+ super.a(str);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(char[] str, int offset, int len) {
+ if (isOverflowed()) {
+ tail.append(Arrays.copyOfRange(str, offset, len));
+ return this;
+ }
+
+ super.a(str, offset, len);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("BooleanParameter")
+ @Override public SBLimitedLength a(boolean b) {
+ if (isOverflowed()) {
+ tail.append(b);
+ return this;
+ }
+
+ super.a(b);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(char c) {
+ if (isOverflowed()) {
+ tail.append(c);
+ return this;
+ }
+
+ super.a(c);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(int i) {
+ if (isOverflowed()) {
+ tail.append(i);
+ return this;
+ }
+
+ super.a(i);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(long lng) {
+ if (isOverflowed()) {
+ tail.append(lng);
+ return this;
+ }
+
+ super.a(lng);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(float f) {
+ if (isOverflowed()) {
+ tail.append(f);
+ return this;
+ }
+
+ super.a(f);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength a(double d) {
+ if (isOverflowed()) {
+ tail.append(d);
+ return this;
+ }
+
+ super.a(d);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public SBLimitedLength appendCodePoint(int codePoint) {
+ if (isOverflowed()) {
+ tail.append(codePoint);
+ return this;
+ }
+
+ super.appendCodePoint(codePoint);
+
+ return onWrite();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ if (tail == null)
+ return super.toString();
+ else {
+ int tailLen = tail.length();
+ StringBuilder res = new StringBuilder(impl().capacity() + tailLen + 100);
+
+ res.append(impl());
+
+ if (tail.getSkipped() > 0) {
+ res.append("... and ").append((tail.getSkipped() + tailLen))
+ .append(" skipped ...");
+ }
+
+ res.append(tail.toString());
+
+ return res.toString();
+ }
+ }
+
+ /**
+ * @return {@code True} - if buffer limit is reached.
+ */
+ public boolean isOverflowed() {
+ return impl().length() > HEAD_LEN;
+ }
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/SensitiveDataLoggingPolicy.java
similarity index 66%
copy from modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
copy to modules/core/src/main/java/org/apache/ignite/internal/tostring/SensitiveDataLoggingPolicy.java
index 28dd677..1cac58e 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/SensitiveDataLoggingPolicy.java
@@ -14,25 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.ignite.configuration.validation;
-/** */
-public class ValidationIssue {
- /** */
- private String message;
+package org.apache.ignite.internal.tostring;
- /** */
- public ValidationIssue(String message) {
- this.message = message;
- }
+/**
+ * Log levels for sensitive data
+ */
+public enum SensitiveDataLoggingPolicy {
+ /**
+ * Write sensitive information in {@code toString()} output.
+ */
+ PLAIN,
- /** */
- public String message() {
- return message;
- }
+ /**
+ * Write hash of sensitive information in {@code toString()} output.
+ */
+ HASH,
- /** */
- @Override public String toString() {
- return "ValidationIssue [message=" + message + ']';
- }
+ /**
+ * Don't write sensitive information in {@code toString()} output.
+ */
+ NONE;
}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java b/modules/core/src/main/java/org/apache/ignite/internal/tostring/package-info.java
similarity index 65%
copy from modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
copy to modules/core/src/main/java/org/apache/ignite/internal/tostring/package-info.java
index 28dd677..9cc76fb 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/tostring/package-info.java
@@ -14,25 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.ignite.configuration.validation;
-/** */
-public class ValidationIssue {
- /** */
- private String message;
-
- /** */
- public ValidationIssue(String message) {
- this.message = message;
- }
-
- /** */
- public String message() {
- return message;
- }
-
- /** */
- @Override public String toString() {
- return "ValidationIssue [message=" + message + ']';
- }
-}
+/**
+ * Common utility classes for strings.
+ */
+package org.apache.ignite.internal.tostring;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 283f433..58b7e02 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -113,4 +113,58 @@ public class IgniteUtils {
public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int expSize) {
return new LinkedHashMap<>(capacity(expSize));
}
+
+ /**
+ * Applies a supplemental hash function to a given hashCode, which
+ * defends against poor quality hash functions. This is critical
+ * because ConcurrentHashMap uses power-of-two length hash tables,
+ * that otherwise encounter collisions for hashCodes that do not
+ * differ in lower or upper bits.
+ * <p>
+ * This function has been taken from Java 8 ConcurrentHashMap with
+ * slightly modifications.
+ *
+ * @param h Value to hash.
+ * @return Hash value.
+ */
+ public static int hash(int h) {
+ // Spread bits to regularize both segment and index locations,
+ // using variant of single-word Wang/Jenkins hash.
+ h += (h << 15) ^ 0xffffcd7d;
+ h ^= (h >>> 10);
+ h += (h << 3);
+ h ^= (h >>> 6);
+ h += (h << 2) + (h << 14);
+
+ return h ^ (h >>> 16);
+ }
+
+ /**
+ * Applies a supplemental hash function to a given hashCode, which
+ * defends against poor quality hash functions. This is critical
+ * because ConcurrentHashMap uses power-of-two length hash tables,
+ * that otherwise encounter collisions for hashCodes that do not
+ * differ in lower or upper bits.
+ * <p>
+ * This function has been taken from Java 8 ConcurrentHashMap with
+ * slightly modifications.
+ *
+ * @param obj Value to hash.
+ * @return Hash value.
+ */
+ public static int hash(Object obj) {
+ return hash(obj.hashCode());
+ }
+
+ /**
+ * A primitive override of {@link #hash(Object)} to avoid unnecessary boxing.
+ *
+ * @param key Value to hash.
+ * @return Hash value.
+ */
+ public static int hash(long key) {
+ int val = (int)(key ^ (key >>> 32));
+
+ return hash(val);
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/IgniteStringBuilder.java b/modules/core/src/main/java/org/apache/ignite/lang/IgniteStringBuilder.java
new file mode 100644
index 0000000..d21997b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/lang/IgniteStringBuilder.java
@@ -0,0 +1,504 @@
+/*
+ * 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 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 org.apache.ignite.lang;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * Optimized string builder with better API.
+ */
+public class IgniteStringBuilder implements Serializable {
+ /** System line separator. */
+ private static final String NL = System.getProperty("line.separator");
+
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private StringBuilder impl;
+
+ /**
+ * @see StringBuilder#StringBuilder()
+ */
+ public IgniteStringBuilder() {
+ impl = new StringBuilder(16);
+ }
+
+ /**
+ *
+ * @param cap Initial capacity.
+ * @see StringBuilder#StringBuilder(int)
+ */
+ public IgniteStringBuilder(int cap) {
+ impl = new StringBuilder(cap);
+ }
+
+ /**
+ *
+ * @param str Initial string.
+ * @see StringBuilder#StringBuilder(String)
+ */
+ public IgniteStringBuilder(String str) {
+ impl = new StringBuilder(str);
+ }
+
+ /**
+ * @param seq Initial character sequence.
+ * @see StringBuilder#StringBuilder(CharSequence)
+ */
+ public IgniteStringBuilder(CharSequence seq) {
+ impl = new StringBuilder(seq);
+ }
+
+ /**
+ *
+ * @param len Length to set.
+ */
+ public void setLength(int len) {
+ impl.setLength(len);
+ }
+
+ /**
+ * Gets underlying implementation.
+ *
+ * @return Underlying implementation.
+ */
+ public StringBuilder impl() {
+ assert impl != null;
+
+ return impl;
+ }
+
+ /**
+ *
+ * @return Buffer length.
+ */
+ public int length() {
+ return impl.length();
+ }
+
+ /**
+ *
+ * @param obj Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(Object obj) {
+ impl.append(obj);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param str Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(String str) {
+ impl.append(str);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param sb Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(StringBuffer sb) {
+ impl.append(sb);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param s Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(CharSequence s) {
+ impl.append(s);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param s Element to add.
+ * @param start Start position.
+ * @param end End position.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(CharSequence s, int start, int end) {
+ impl.append(s, start, end);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param str Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(char[] str) {
+ impl.append(str);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param str Element to add.
+ * @param offset Start offset.
+ * @param len Length.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(char[] str, int offset, int len) {
+ impl.append(str, offset, len);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param b Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ @SuppressWarnings("BooleanParameter")
+ public IgniteStringBuilder a(boolean b) {
+ impl.append(b);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param c Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(char c) {
+ impl.append(c);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param i Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(int i) {
+ impl.append(i);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param lng Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(long lng) {
+ impl.append(lng);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param f Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(float f) {
+ impl.append(f);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param d Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder a(double d) {
+ impl.append(d);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param codePoint Element to add.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder appendCodePoint(int codePoint) {
+ impl.appendCodePoint(codePoint);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param start Start position to delete from.
+ * @param end End position.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder d(int start, int end) {
+ impl.delete(start, end);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param index Index to delete character at.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder d(int index) {
+ impl.deleteCharAt(index);
+
+ return this;
+ }
+
+ /**
+ * Adds a platform-dependent newline to this buffer.
+ *
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder nl() {
+ impl.append(NL);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param start Start position to replace from.
+ * @param end End position.
+ * @param str String to replace with.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder r(int start, int end, String str) {
+ impl.replace(start, end, str);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param index Start index to insert to.
+ * @param str String to insert.
+ * @param off Offset in the string to be inserted.
+ * @param len Length of the substring to be inserted.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int index, char[] str, int off, int len) {
+ impl.insert(index, str, off, len);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param off Offset to be inserted at.
+ * @param obj Object whose string representation to be inserted.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, Object obj) {
+ return i(off, String.valueOf(obj));
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param str String to be inserted.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, String str) {
+ impl.insert(off, str);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param str String to be inserted.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, char[] str) {
+ impl.insert(off, str);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param dstOff Offset to insert at.
+ * @param s String to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int dstOff, CharSequence s) {
+ impl.insert(dstOff, s);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param dstOff Offset to insert at.
+ * @param s String to insert.
+ * @param start Start index to insert from.
+ * @param end End index to insert up to.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int dstOff, CharSequence s, int start, int end) {
+ impl.insert(dstOff, s, start, end);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param b Element to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, boolean b) {
+ impl.insert(off, b);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param c Element to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, char c) {
+ impl.insert(off, c);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param i Element to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, int i) {
+ return i(off, String.valueOf(i));
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param l Element to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, long l) {
+ return i(off, String.valueOf(l));
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param f Element to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, float f) {
+ return i(off, String.valueOf(f));
+ }
+
+ /**
+ *
+ * @param off Offset to insert at.
+ * @param d Element to insert.
+ * @return This buffer for chaining method calls.
+ */
+ public IgniteStringBuilder i(int off, double d) {
+ return i(off, String.valueOf(d));
+ }
+
+ /**
+ * Appends given long value as a hex string to this string builder.
+ *
+ * @param val Value to append.
+ * @return This builder for chaining method calls.
+ */
+ public IgniteStringBuilder appendHex(long val) {
+ String hex = Long.toHexString(val);
+
+ int len = hex.length();
+
+ for (int i = 0; i < 16 - len; i++)
+ a('0');
+
+ a(hex);
+
+ return this;
+ }
+
+ /**
+ * Appends given long value as a hex string to this string builder.
+ *
+ * @param val Value to append.
+ * @return This builder for chaining method calls.
+ */
+ public IgniteStringBuilder appendHex(int val) {
+ String hex = Integer.toHexString(val);
+
+ int len = hex.length();
+
+ for (int i = 0; i < 8 - len; i++)
+ a('0');
+
+ a(hex);
+
+ return this;
+ }
+
+ /**
+ *
+ * @param s Stream to write to.
+ * @throws IOException Thrown in case of any IO errors.
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException {
+ s.writeObject(impl);
+ }
+
+ /**
+ *
+ * @param s Stream to read from.
+ * @throws IOException Thrown in case of any IO errors.
+ * @throws ClassNotFoundException Thrown if read class cannot be found.
+ */
+ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
+ impl = (StringBuilder) s.readObject();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return impl.toString();
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/lang/IgniteSystemProperties.java
new file mode 100644
index 0000000..6fae6b3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/lang/IgniteSystemProperties.java
@@ -0,0 +1,307 @@
+/*
+ * 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 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 org.apache.ignite.lang;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.ignite.internal.tostring.IgniteToStringBuilder;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Contains constants for all system properties and environmental variables in Ignite.
+ * These properties and variables can be used to affect the behavior of Ignite.
+ */
+public final class IgniteSystemProperties {
+ /**
+ * Setting to {@code PLAIN} enables writing sensitive information in {@code toString()} output.
+ * Setting to {@code HASH"} enables writing hash of sensitive information in {@code toString()} output.
+ * Setting to {@code NONE} disables writing sensitive information in {@code toString()} output.
+ *
+ * Default: {@code HASH}.
+ * @see IgniteToStringBuilder
+ */
+ public static final String IGNITE_SENSITIVE_DATA_LOGGING = "IGNITE_SENSITIVE_DATA_LOGGING";
+
+ /**
+ * Limit collection (map, array) elements number to output.
+ *
+ * Default: 100
+ * @see IgniteToStringBuilder
+ */
+ public static final String IGNITE_TO_STRING_COLLECTION_LIMIT = "IGNITE_TO_STRING_COLLECTION_LIMIT";
+
+ /**
+ * Boolean flag indicating whether {@link IgniteToStringBuilder} should ignore {@link RuntimeException}
+ * when building string representation of an object and just print information about exception into the log
+ * or rethrow.
+ *
+ * Default: {@code True}.
+ * @see IgniteToStringBuilder
+ */
+ public static final String IGNITE_TO_STRING_IGNORE_RUNTIME_EXCEPTION = "IGNITE_TO_STRING_IGNORE_RUNTIME_EXCEPTION";
+
+ /**
+ * Maximum length for {@code IgniteToStringBuilder.toString(...)} methods result.
+ *
+ * Default: 10_000.
+ * @see IgniteToStringBuilder
+ */
+ public static final String IGNITE_TO_STRING_MAX_LENGTH = "IGNITE_TO_STRING_MAX_LENGTH";
+
+ /**
+ * Enforces singleton.
+ */
+ private IgniteSystemProperties() {
+ // No-op.
+ }
+
+ /**
+ * @param enumCls Enum type.
+ * @param name Name of the system property or environment variable.
+ * @return Enum value or {@code null} if the property is not set.
+ */
+ public static <E extends Enum<E>> E getEnum(Class<E> enumCls, String name) {
+ return getEnum(enumCls, name, null);
+ }
+
+ /**
+ * @param name Name of the system property or environment variable.
+ * @return Enum value or the given default.
+ */
+ public static <E extends Enum<E>> E getEnum(String name, E dflt) {
+ return getEnum(dflt.getDeclaringClass(), name, dflt);
+ }
+
+ /**
+ * @param enumCls Enum type.
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Enum value or the given default.
+ */
+ private static <E extends Enum<E>> E getEnum(Class<E> enumCls, String name, E dflt) {
+ assert enumCls != null;
+
+ String val = getString(name);
+
+ if (val == null)
+ return dflt;
+
+ try {
+ return Enum.valueOf(enumCls, val);
+ }
+ catch (IllegalArgumentException ignore) {
+ return dflt;
+ }
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ *
+ * @param name Name of the system property or environment variable.
+ * @return Value of the system property or environment variable.
+ * Returns {@code null} if neither can be found for given name.
+ */
+ @Nullable public static String getString(String name) {
+ assert name != null;
+
+ String v = System.getProperty(name);
+
+ if (v == null)
+ v = System.getenv(name);
+
+ return v;
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ *
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Value of the system property or environment variable.
+ * Returns {@code null} if neither can be found for given name.
+ */
+ @Nullable public static String getString(String name, String dflt) {
+ String val = getString(name);
+
+ return val == null ? dflt : val;
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ * The result is transformed to {@code boolean} using {@code Boolean.valueOf()} method.
+ *
+ * @param name Name of the system property or environment variable.
+ * @return Boolean value of the system property or environment variable.
+ * Returns {@code False} in case neither system property
+ * nor environment variable with given name is found.
+ */
+ public static boolean getBoolean(String name) {
+ return getBoolean(name, false);
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ * The result is transformed to {@code boolean} using {@code Boolean.valueOf()} method.
+ *
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Boolean value of the system property or environment variable.
+ * Returns default value in case neither system property
+ * nor environment variable with given name is found.
+ */
+ public static boolean getBoolean(String name, boolean dflt) {
+ String val = getString(name);
+
+ return val == null ? dflt : Boolean.parseBoolean(val);
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ * The result is transformed to {@code int} using {@code Integer.parseInt()} method.
+ *
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Integer value of the system property or environment variable.
+ * Returns default value in case neither system property
+ * nor environment variable with given name is found.
+ */
+ public static int getInteger(String name, int dflt) {
+ String s = getString(name);
+
+ if (s == null)
+ return dflt;
+
+ int res;
+
+ try {
+ res = Integer.parseInt(s);
+ }
+ catch (NumberFormatException ignore) {
+ res = dflt;
+ }
+
+ return res;
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ * The result is transformed to {@code float} using {@code Float.parseFloat()} method.
+ *
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Float value of the system property or environment variable.
+ * Returns default value in case neither system property
+ * nor environment variable with given name is found.
+ */
+ public static float getFloat(String name, float dflt) {
+ String s = getString(name);
+
+ if (s == null)
+ return dflt;
+
+ float res;
+
+ try {
+ res = Float.parseFloat(s);
+ }
+ catch (NumberFormatException ignore) {
+ res = dflt;
+ }
+
+ return res;
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ * The result is transformed to {@code long} using {@code Long.parseLong()} method.
+ *
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Integer value of the system property or environment variable.
+ * Returns default value in case neither system property
+ * nor environment variable with given name is found.
+ */
+ public static long getLong(String name, long dflt) {
+ String s = getString(name);
+
+ if (s == null)
+ return dflt;
+
+ long res;
+
+ try {
+ res = Long.parseLong(s);
+ }
+ catch (NumberFormatException ignore) {
+ res = dflt;
+ }
+
+ return res;
+ }
+
+ /**
+ * Gets either system property or environment variable with given name.
+ * The result is transformed to {@code double} using {@code Double.parseDouble()} method.
+ *
+ * @param name Name of the system property or environment variable.
+ * @param dflt Default value.
+ * @return Integer value of the system property or environment variable.
+ * Returns default value in case neither system property
+ * nor environment variable with given name is found.
+ */
+ public static double getDouble(String name, double dflt) {
+ String s = getString(name);
+
+ if (s == null)
+ return dflt;
+
+ double res;
+
+ try {
+ res = Double.parseDouble(s);
+ }
+ catch (NumberFormatException ignore) {
+ res = dflt;
+ }
+
+ return res;
+ }
+
+ /**
+ * Gets snapshot of system properties.
+ * Snapshot could be used for thread safe iteration over system properties.
+ * Non-string properties are removed before return.
+ *
+ * @return Snapshot of system properties.
+ */
+ public static Properties snapshot() {
+ Properties sysProps = (Properties)System.getProperties().clone();
+
+ Iterator<Map.Entry<Object, Object>> iter = sysProps.entrySet().iterator();
+
+ while (iter.hasNext()) {
+ Map.Entry entry = iter.next();
+
+ if (!(entry.getValue() instanceof String) || !(entry.getKey() instanceof String))
+ iter.remove();
+ }
+
+ return sysProps;
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/IgniteAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/IgniteAbstractTest.java
new file mode 100644
index 0000000..50c10de
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/IgniteAbstractTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 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 org.apache.ignite.internal.testframework;
+
+import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.tostring.SensitiveDataLoggingPolicy;
+import org.apache.ignite.lang.IgniteLogger;
+
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_SENSITIVE_DATA_LOGGING;
+import static org.apache.ignite.lang.IgniteSystemProperties.getString;
+
+/**
+ * Ignite base test class.
+ */
+public abstract class IgniteAbstractTest {
+ /** Logger. */
+ protected static IgniteLogger log;
+
+ /** Init test env. */
+ static {
+ S.setSensitiveDataLoggingPolicySupplier(() ->
+ SensitiveDataLoggingPolicy.valueOf(getString(IGNITE_SENSITIVE_DATA_LOGGING, "hash").toUpperCase()));
+ }
+
+ /**
+ * Constructor.
+ */
+ @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
+ protected IgniteAbstractTest() {
+ log = IgniteLogger.forClass(getClass());
+ }
+
+ /**
+ * @return Logger.
+ */
+ protected IgniteLogger logger() {
+ return log;
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/SystemPropertiesExtension.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/SystemPropertiesExtension.java
new file mode 100644
index 0000000..a1c9caa
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/SystemPropertiesExtension.java
@@ -0,0 +1,198 @@
+/*
+ * 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 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 org.apache.ignite.internal.testframework;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+/**
+ * JUnit rule that manages usage of {@link WithSystemProperty} annotations.<br/>
+ * Should be used in {@link ExtendWith}.
+ *
+ * @see WithSystemProperty
+ * @see ExtendWith
+ */
+public class SystemPropertiesExtension implements
+ BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback {
+ /** Class properties. */
+ @SuppressWarnings("InstanceVariableMayNotBeInitialized")
+ private List<Prop<String, String>> testMethodSysProps;
+
+ /** Class properties. */
+ @SuppressWarnings("InstanceVariableMayNotBeInitialized")
+ private List<Prop<String, String>> testClassSysProps;
+
+ /** {@inheritDoc} */
+ @Override public void beforeAll(ExtensionContext ctx) {
+ testClassSysProps = extractSystemPropertiesBeforeClass(ctx.getRequiredTestClass());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void afterAll(ExtensionContext context) {
+ clearSystemProperties(testClassSysProps);
+
+ testClassSysProps = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void beforeEach(ExtensionContext ctx) {
+ testMethodSysProps = extractSystemPropertiesBeforeTestMethod(ctx.getRequiredTestMethod());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void afterEach(ExtensionContext context) {
+ clearSystemProperties(testMethodSysProps);
+
+ testMethodSysProps = null;
+ }
+
+ /**
+ * Set system properties before class.
+ *
+ * @param testCls Current test class.
+ * @return List of updated properties in reversed order.
+ */
+ private List<Prop<String, String>> extractSystemPropertiesBeforeClass(Class<?> testCls) {
+ List<WithSystemProperty[]> allProps = new ArrayList<>();
+
+ for (Class<?> cls = testCls; cls != null; cls = cls.getSuperclass()) {
+ SystemPropertiesList clsProps = cls.getAnnotation(SystemPropertiesList.class);
+
+ if (clsProps != null)
+ allProps.add(clsProps.value());
+ else {
+ WithSystemProperty clsProp = cls.getAnnotation(WithSystemProperty.class);
+
+ if (clsProp != null)
+ allProps.add(new WithSystemProperty[] {clsProp});
+ }
+ }
+
+ Collections.reverse(allProps);
+
+ // List of system properties to set when all tests in class are finished.
+ final List<Prop<String, String>> clsSysProps = new ArrayList<>();
+
+ for (WithSystemProperty[] props : allProps) {
+ for (WithSystemProperty prop : props) {
+ String oldVal = System.setProperty(prop.key(), prop.value());
+
+ clsSysProps.add(new Prop<>(prop.key(), oldVal));
+ }
+ }
+
+ Collections.reverse(clsSysProps);
+
+ return clsSysProps;
+ }
+
+ /**
+ * Set system properties before test method.
+ *
+ * @param testMtd Current test method.
+ * @return List of updated properties in reversed order.
+ */
+ public List<Prop<String, String>> extractSystemPropertiesBeforeTestMethod(Method testMtd) {
+ WithSystemProperty[] allProps = null;
+
+ SystemPropertiesList testProps = testMtd.getAnnotation(SystemPropertiesList.class);
+
+ if (testProps != null)
+ allProps = testProps.value();
+ else {
+ WithSystemProperty testProp = testMtd.getAnnotation(WithSystemProperty.class);
+
+ if (testProp != null)
+ allProps = new WithSystemProperty[] {testProp};
+ }
+
+ // List of system properties to set when test is finished.
+ List<Prop<String, String>> testSysProps = new ArrayList<>();
+
+ if (allProps != null) {
+ for (WithSystemProperty prop : allProps) {
+ String oldVal = System.setProperty(prop.key(), prop.value());
+
+ testSysProps.add(new Prop<>(prop.key(), oldVal));
+ }
+ }
+
+ Collections.reverse(testSysProps);
+
+ return testSysProps;
+ }
+
+ /**
+ * Return old values of updated properties.
+ *
+ * @param sysProps List of properties to clear.
+ */
+ private void clearSystemProperties(List<Prop<String, String>> sysProps) {
+ if (sysProps == null)
+ return; // Nothing to do.
+
+ for (Prop<String, String> prop : sysProps) {
+ if (prop.value() == null)
+ System.clearProperty(prop.key());
+ else
+ System.setProperty(prop.key(), prop.value());
+ }
+ }
+
+ /**
+ * Property.
+ */
+ public static class Prop<K, V> {
+ /** Property key. */
+ private final K key;
+
+ /** Property value. */
+ private final V val;
+
+ /**
+ * @param key Property key.
+ * @param val Property value.
+ */
+ Prop(K key, V val) {
+ this.key = key;
+ this.val = val;
+ }
+
+ /**
+ * @return Property key.
+ */
+ K key() {
+ return key;
+ }
+
+ /**
+ * @return Property value.
+ */
+ V value() {
+ return val;
+ }
+ }
+}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/SystemPropertiesList.java
similarity index 57%
copy from modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
copy to modules/core/src/test/java/org/apache/ignite/internal/testframework/SystemPropertiesList.java
index a7fe166..1b2835b 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/SystemPropertiesList.java
@@ -15,27 +15,21 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.schema;
+package org.apache.ignite.internal.testframework;
-import org.apache.ignite.schema.IndexColumn;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * Non-ordered index column.
+ * {@link Repeatable} for the {@link WithSystemProperty}.<br/>
+ * Not intended for direct usage. Use multiple {@link WithSystemProperty} annotation instead.
*/
-class IndexColumnImpl extends AbstractSchemaObject implements IndexColumn {
- /**
- * Constructor.
- *
- * @param name Column name.
- */
- IndexColumnImpl(String name) {
- super(name);
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return "Column[" +
- "name='" + name() + '\'' +
- ']';
- }
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface SystemPropertiesList {
+ /** Array of underlying annotations. */
+ WithSystemProperty[] value();
}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/testframework/WithSystemProperty.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/WithSystemProperty.java
new file mode 100644
index 0000000..24407ff
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/WithSystemProperty.java
@@ -0,0 +1,114 @@
+/*
+ * 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 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 org.apache.ignite.internal.testframework;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+/**
+ * Annotation that defines a scope with specific system property configured.<br/>
+ * <br/>
+ * Might be used on class level or on method level. Multiple annotations might be applied to the same class/method.<br/>
+ * <br/>
+ * In short, these two approaches are basically equivalent:<br/>
+ * <br/>
+ * Short:
+ * <pre>{@code @WithSystemProperty(key = "name", value = "val")
+ * public class SomeTest {
+ * }
+ * }</pre>
+ * Long:
+ * <pre>{@code public class SomeTest {
+ * private static Object oldVal;
+ *
+ * @BeforeClass
+ * public static void beforeClass() {
+ * oldVal = System.getProperty("name");
+ *
+ * System.setProperty("name", "val");
+ * }
+ *
+ * @AfterClass
+ * public static void afterClass() {
+ * if (oldVal == null)
+ * System.clearProperty("name");
+ * else
+ * System.setProperty("name", oldVal);
+ * }
+ * }
+ * }</pre>
+ * <p>
+ * Same applies to methods with the difference that annotation translates into something like {@link BeforeEach} and
+ * {@link AfterEach}.
+ * <br/><br/>
+ * <pre>{@code public class SomeTest {
+ * @Test
+ * @WithSystemProperty(key = "name", value = "val")
+ * public void test() {
+ * // ...
+ * }
+ * }
+ * }</pre>
+ * is equivalent to:
+ * <pre>{@code public class SomeTest {
+ * @Test
+ * public void test() {
+ * Object oldVal = System.getProperty("name");
+ *
+ * try {
+ * // ...
+ * }
+ * finally {
+ * if (oldVal == null)
+ * System.clearProperty("name");
+ * else
+ * System.setProperty("name", oldVal);
+ * }
+ * }
+ * }
+ * }</pre>
+ * For class level annotation it applies system properties for the whole class hierarchy (ignoring interfaces, there's
+ * no linearization implemented). More specific classes have higher priority and set their properties last. It all
+ * starts with {@link Object} which, of course, is not annotated.<br/>
+ * <br/>
+ * Test methods do not inherit their annotations from overridden methods of super class.<br/>
+ * <br/>
+ * If more than one annotation is presented on class/method then they will be applied in the same order as they
+ * appear in code. It is achieved with the help of {@link Repeatable} feature of Java annotations -
+ * {@link SystemPropertiesList} is automatically generated in such cases.
+ * For that reason it is not recommended using {@link SystemPropertiesList} directly.
+ *
+ * @see System#setProperty(String, String)
+ * @see SystemPropertiesExtension
+ * @see SystemPropertiesList
+ */
+@Repeatable(SystemPropertiesList.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface WithSystemProperty {
+ /** The name of the system property. */
+ String key();
+
+ /** The value of the system property. */
+ String value();
+}
diff --git a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java b/modules/core/src/test/java/org/apache/ignite/internal/testframework/package-info.java
similarity index 65%
copy from modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
copy to modules/core/src/test/java/org/apache/ignite/internal/testframework/package-info.java
index 28dd677..23ab2d4 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/configuration/validation/ValidationIssue.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/testframework/package-info.java
@@ -14,25 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.ignite.configuration.validation;
-/** */
-public class ValidationIssue {
- /** */
- private String message;
-
- /** */
- public ValidationIssue(String message) {
- this.message = message;
- }
-
- /** */
- public String message() {
- return message;
- }
-
- /** */
- @Override public String toString() {
- return "ValidationIssue [message=" + message + ']';
- }
-}
+/**
+ * Contains internal tests or test related classes and interfaces.
+ */
+package org.apache.ignite.internal.testframework;
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/tostring/CircularStringBuilderSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/tostring/CircularStringBuilderSelfTest.java
new file mode 100644
index 0000000..1a77252
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/tostring/CircularStringBuilderSelfTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import org.apache.ignite.internal.testframework.IgniteAbstractTest;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ *
+ */
+public class CircularStringBuilderSelfTest extends IgniteAbstractTest {
+ /**
+ *
+ */
+ @Test
+ public void testCSBPrimitive() {
+ CircularStringBuilder csb = new CircularStringBuilder(1);
+ csb.append((String)null);
+ assertEquals("l", csb.toString());
+ csb.append('1');
+ assertEquals("1", csb.toString());
+
+ CircularStringBuilder csb2 = new CircularStringBuilder(1);
+ csb2.append(1);
+ assertEquals("1", csb2.toString());
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testCSBOverflow() {
+ testSB(3, "1234", 2, "234");
+ testSB(4, "1234", 2, "1234");
+ testSB(5, "1234", 2, "41234");
+ testSB(6, "1234", 2, "341234");
+ testSB(7, "1234", 2, "2341234");
+ testSB(8, "1234", 2, "12341234");
+ }
+
+ /**
+ * @param capacity Capacity.
+ * @param pattern Pattern to add.
+ * @param num How many times pattern should be added.
+ * @param expected Expected string.
+ */
+ private void testSB(int capacity, String pattern, int num, String expected) {
+ CircularStringBuilder csb = new CircularStringBuilder(capacity);
+
+ for (int i = 0; i < num; i++)
+ csb.append(pattern);
+
+ assertEquals(expected, csb.toString());
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/tostring/IgniteToStringBuilderSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/tostring/IgniteToStringBuilderSelfTest.java
new file mode 100644
index 0000000..939edb7
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/tostring/IgniteToStringBuilderSelfTest.java
@@ -0,0 +1,994 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReadWriteLock;
+import org.apache.ignite.internal.testframework.IgniteAbstractTest;
+import org.apache.ignite.lang.IgniteInternalException;
+import org.apache.ignite.lang.IgniteSystemProperties;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.ignite.internal.tostring.IgniteToStringBuilder.identity;
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_TO_STRING_COLLECTION_LIMIT;
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_TO_STRING_MAX_LENGTH;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for {@link IgniteToStringBuilder}.
+ */
+public class IgniteToStringBuilderSelfTest extends IgniteAbstractTest {
+ /**
+ *
+ */
+ @Test
+ public void testToString() {
+ TestClass1 obj = new TestClass1();
+
+ assertEquals(obj.toStringManual(), obj.toStringAutomatic());
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringWithAdditions() {
+ TestClass1 obj = new TestClass1();
+
+ String manual = obj.toStringWithAdditionalManual();
+
+ String automatic = obj.toStringWithAdditionalAutomatic();
+
+ assertEquals(manual, automatic);
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringCheckSimpleListRecursionPrevention() {
+ ArrayList<Object> list1 = new ArrayList<>();
+ ArrayList<Object> list2 = new ArrayList<>();
+
+ list2.add(list1);
+ list1.add(list2);
+
+ assertEquals("ArrayList [size=1]", IgniteToStringBuilder.toString(ArrayList.class, list1));
+ assertEquals("ArrayList [size=1]", IgniteToStringBuilder.toString(ArrayList.class, list2));
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringCheckSimpleMapRecursionPrevention() {
+ HashMap<Object, Object> map1 = new HashMap<>();
+ HashMap<Object, Object> map2 = new HashMap<>();
+
+ map1.put("2", map2);
+ map2.put("1", map1);
+
+ assertEquals("HashMap []", IgniteToStringBuilder.toString(HashMap.class, map1));
+ assertEquals("HashMap []", IgniteToStringBuilder.toString(HashMap.class, map2));
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringCheckListAdvancedRecursionPrevention() {
+ ArrayList<Object> list1 = new ArrayList<>();
+ ArrayList<Object> list2 = new ArrayList<>();
+
+ list2.add(list1);
+ list1.add(list2);
+
+ final String hash1 = identity(list1);
+ final String hash2 = identity(list2);
+
+ assertEquals("ArrayList" + hash1 + " [size=1, name=ArrayList [ArrayList" + hash1 + "]]",
+ IgniteToStringBuilder.toString(ArrayList.class, list1, "name", list2));
+ assertEquals("ArrayList" + hash2 + " [size=1, name=ArrayList [ArrayList" + hash2 + "]]",
+ IgniteToStringBuilder.toString(ArrayList.class, list2, "name", list1));
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringCheckMapAdvancedRecursionPrevention() {
+ HashMap<Object, Object> map1 = new HashMap<>();
+ HashMap<Object, Object> map2 = new HashMap<>();
+
+ map1.put("2", map2);
+ map2.put("1", map1);
+
+ final String hash1 = identity(map1);
+ final String hash2 = identity(map2);
+
+ assertEquals("HashMap" + hash1 + " [name=HashMap {1=HashMap" + hash1 + "}]",
+ IgniteToStringBuilder.toString(HashMap.class, map1, "name", map2));
+ assertEquals("HashMap" + hash2 + " [name=HashMap {2=HashMap" + hash2 + "}]",
+ IgniteToStringBuilder.toString(HashMap.class, map2, "name", map1));
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testToStringCheckObjectRecursionPrevention() throws Exception {
+ Node n1 = new Node();
+ Node n2 = new Node();
+ Node n3 = new Node();
+ Node n4 = new Node();
+
+ n1.name = "n1";
+ n2.name = "n2";
+ n3.name = "n3";
+ n4.name = "n4";
+
+ n1.next = n2;
+ n2.next = n3;
+ n3.next = n4;
+ n4.next = n3;
+
+ n1.nodes = new Node[4];
+ n2.nodes = n1.nodes;
+ n3.nodes = n1.nodes;
+ n4.nodes = n1.nodes;
+
+ n1.nodes[0] = n1;
+ n1.nodes[1] = n2;
+ n1.nodes[2] = n3;
+ n1.nodes[3] = n4;
+
+ String expN1 = n1.toString();
+ String expN2 = n2.toString();
+ String expN3 = n3.toString();
+ String expN4 = n4.toString();
+
+ CyclicBarrier bar = new CyclicBarrier(4);
+ Executor pool = Executors.newFixedThreadPool(4);
+
+ CompletableFuture<String> fut1 = runAsync(new BarrierCallable(bar, n1, expN1), pool);
+ CompletableFuture<String> fut2 = runAsync(new BarrierCallable(bar, n2, expN2), pool);
+ CompletableFuture<String> fut3 = runAsync(new BarrierCallable(bar, n3, expN3), pool);
+ CompletableFuture<String> fut4 = runAsync(new BarrierCallable(bar, n4, expN4), pool);
+
+ fut1.get(3_000, TimeUnit.MILLISECONDS);
+ fut2.get(3_000, TimeUnit.MILLISECONDS);
+ fut3.get(3_000, TimeUnit.MILLISECONDS);
+ fut4.get(3_000, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * @param callable Callable.
+ * @return Completable future.
+ */
+ private static <U> CompletableFuture<U> runAsync(Callable<U> callable, Executor pool) {
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ return callable.call();
+ }
+ catch (Throwable th) {
+ throw new IgniteInternalException(th);
+ }
+ }, pool);
+ }
+
+ /**
+ * JUnit.
+ */
+ @Test
+ public void testToStringPerformance() {
+ TestClass1 obj = new TestClass1();
+
+ // Warm up.
+ obj.toStringAutomatic();
+
+ long start = System.currentTimeMillis();
+
+ for (int i = 0; i < 100000; i++)
+ obj.toStringManual();
+
+ logger().info("Manual toString() took: " + (System.currentTimeMillis() - start) + "ms");
+
+ start = System.currentTimeMillis();
+
+ for (int i = 0; i < 100000; i++)
+ obj.toStringAutomatic();
+
+ logger().info("Automatic toString() took: " + (System.currentTimeMillis() - start) + "ms");
+ }
+
+ /**
+ * Test array print.
+ *
+ * @param v value to get array class and fill array.
+ * @param limit value of IGNITE_TO_STRING_COLLECTION_LIMIT.
+ */
+ private <T, V> void testArr(V v, int limit) {
+ T[] arrOf = (T[])Array.newInstance(v.getClass(), limit + 1);
+ Arrays.fill(arrOf, v);
+ T[] arr = Arrays.copyOf(arrOf, limit);
+
+ checkArrayOverflow(arrOf, arr, limit);
+ }
+
+ /**
+ * Test array print.
+ */
+ @Test
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public void testArrLimitWithRecursion() {
+ int limit = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
+
+ ArrayList[] arrOf = new ArrayList[limit + 1];
+ Arrays.fill(arrOf, new ArrayList());
+ ArrayList[] arr = Arrays.copyOf(arrOf, limit);
+
+ arrOf[0].add(arrOf);
+ arr[0].add(arr);
+
+ checkArrayOverflow(arrOf, arr, limit);
+ }
+
+ /**
+ * @param arrOf Array.
+ * @param arr Array copy.
+ * @param limit Array limit.
+ */
+ private void checkArrayOverflow(Object[] arrOf, Object[] arr, int limit) {
+ String arrStr = IgniteToStringBuilder.arrayToString(arr);
+ String arrOfStr = IgniteToStringBuilder.arrayToString(arrOf);
+
+ // Simulate overflow
+ StringBuilder resultSB = new StringBuilder(arrStr);
+ resultSB.deleteCharAt(resultSB.length() - 1);
+ resultSB.append("... and ").append(arrOf.length - limit).append(" more]");
+
+ arrStr = resultSB.toString();
+
+ assertEquals(arrStr, arrOfStr, "Collection limit error in array of type " +
+ arrOf.getClass().getName() + " error, normal arr: <" + arrStr + ">, overflowed arr: <" + arrOfStr + ">");
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringCollectionLimits() {
+ int limit = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
+
+ Object[] vals = new Object[] {
+ Byte.MIN_VALUE, Boolean.TRUE, Short.MIN_VALUE, Integer.MIN_VALUE, Long.MIN_VALUE,
+ Float.MIN_VALUE, Double.MIN_VALUE, Character.MIN_VALUE, new TestClass1()};
+ for (Object val : vals)
+ testArr(val, limit);
+
+ //noinspection ZeroLengthArrayAllocation
+ int[] intArr1 = new int[0];
+
+ assertEquals("[]", IgniteToStringBuilder.arrayToString(intArr1));
+ assertEquals("null", IgniteToStringBuilder.arrayToString(null));
+
+ int[] intArr2 = {1, 2, 3};
+
+ assertEquals("[1, 2, 3]", IgniteToStringBuilder.arrayToString(intArr2));
+
+ Object[] intArr3 = {2, 3, 4};
+
+ assertEquals("[2, 3, 4]", IgniteToStringBuilder.arrayToString(intArr3));
+
+ byte[] byteArr = new byte[1];
+
+ byteArr[0] = 1;
+ assertEquals(Arrays.toString(byteArr), IgniteToStringBuilder.arrayToString(byteArr));
+ byteArr = Arrays.copyOf(byteArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(byteArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ boolean[] boolArr = new boolean[1];
+
+ boolArr[0] = true;
+ assertEquals(Arrays.toString(boolArr), IgniteToStringBuilder.arrayToString(boolArr));
+ boolArr = Arrays.copyOf(boolArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(boolArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ short[] shortArr = new short[1];
+
+ shortArr[0] = 100;
+ assertEquals(Arrays.toString(shortArr), IgniteToStringBuilder.arrayToString(shortArr));
+ shortArr = Arrays.copyOf(shortArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(shortArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ int[] intArr = new int[1];
+
+ intArr[0] = 10000;
+ assertEquals(Arrays.toString(intArr), IgniteToStringBuilder.arrayToString(intArr));
+ intArr = Arrays.copyOf(intArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(intArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ long[] longArr = new long[1];
+
+ longArr[0] = 10000000;
+ assertEquals(Arrays.toString(longArr), IgniteToStringBuilder.arrayToString(longArr));
+ longArr = Arrays.copyOf(longArr, 101);
+ assertTrue(
+ IgniteToStringBuilder.arrayToString(longArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ float[] floatArr = new float[1];
+
+ floatArr[0] = 1.f;
+ assertEquals(Arrays.toString(floatArr), IgniteToStringBuilder.arrayToString(floatArr));
+ floatArr = Arrays.copyOf(floatArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(floatArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ double[] doubleArr = new double[1];
+
+ doubleArr[0] = 1.;
+ assertEquals(Arrays.toString(doubleArr), IgniteToStringBuilder.arrayToString(doubleArr));
+ doubleArr = Arrays.copyOf(doubleArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(doubleArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ char[] cArr = new char[1];
+
+ cArr[0] = 'a';
+ assertEquals(Arrays.toString(cArr), IgniteToStringBuilder.arrayToString(cArr));
+ cArr = Arrays.copyOf(cArr, 101);
+ assertTrue(IgniteToStringBuilder.arrayToString(cArr).contains("... and 1 more"),
+ "Can't find \"... and 1 more\" in overflowed array string!");
+
+ Map<String, String> strMap = new TreeMap<>();
+ List<String> strList = new ArrayList<>(limit + 1);
+
+ TestClass1 testCls = new TestClass1();
+
+ testCls.strMap = strMap;
+ testCls.strListIncl = strList;
+
+ for (int i = 0; i < limit; i++) {
+ strMap.put("k" + i, "v");
+ strList.add("e");
+ }
+
+ checkColAndMap(testCls);
+ }
+
+ /**
+ *
+ */
+ @Test
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public void testToStringColAndMapLimitWithRecursion() {
+ int limit = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
+ Map strMap = new TreeMap<>();
+ List strList = new ArrayList<>(limit + 1);
+
+ TestClass1 testClass = new TestClass1();
+ testClass.strMap = strMap;
+ testClass.strListIncl = strList;
+
+ Map m = new TreeMap();
+ m.put("m", strMap);
+
+ List l = new ArrayList();
+ l.add(strList);
+
+ strMap.put("k0", m);
+ strList.add(l);
+
+ for (int i = 1; i < limit; i++) {
+ strMap.put("k" + i, "v");
+ strList.add("e");
+ }
+
+ checkColAndMap(testClass);
+ }
+
+ /**
+ * @param testCls Class with collection and map included in toString().
+ */
+ private void checkColAndMap(TestClass1 testCls) {
+ String testClsStr = IgniteToStringBuilder.toString(TestClass1.class, testCls);
+
+ testCls.strMap.put("kz", "v"); // important to add last element in TreeMap here
+ testCls.strListIncl.add("e");
+
+ String testClsStrOf = IgniteToStringBuilder.toString(TestClass1.class, testCls);
+
+ String testClsStrOfR = testClsStrOf.replaceAll("... and 1 more", "");
+
+ assertEquals(testClsStr.length(), testClsStrOfR.length(),
+ "Collection limit error in Map or List, normal: <" + testClsStr + ">, overflowed: <" + testClsStrOf + ">");
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testToStringSizeLimits() {
+ int limit = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_MAX_LENGTH, 10_000);
+ int tailLen = limit / 10 * 2;
+
+ StringBuilder sb = new StringBuilder(limit + 10);
+
+ sb.append("a".repeat(Math.max(0, limit - 100)));
+
+ String actual = IgniteToStringBuilder.toString(TestClass2.class, new TestClass2(sb.toString()));
+ String exp = "TestClass2 [str=" + sb + ", nullArr=null]";
+
+ assertEquals(exp, actual);
+
+ sb.append("b".repeat(110));
+
+ actual = IgniteToStringBuilder.toString(TestClass2.class, new TestClass2(sb.toString()));
+ exp = "TestClass2 [str=" + sb + ", nullArr=null]";
+
+ assertEquals(exp.substring(0, limit - tailLen), actual.substring(0, limit - tailLen));
+ assertEquals(exp.substring(exp.length() - tailLen), actual.substring(actual.length() - tailLen));
+
+ assertTrue(actual.contains("... and"));
+ assertTrue(actual.contains("skipped ..."));
+ }
+
+ /**
+ *
+ */
+ @Test
+ public void testHierarchy() {
+ Wrapper w = new Wrapper();
+ Parent p = w.p;
+ String hash = identity(p);
+
+ assertEquals("Wrapper [p=Child [b=0, pb=Parent[] [null], super=Parent [a=0, pa=Parent[] [null]]]]",
+ w.toString());
+
+ p.pa[0] = p;
+
+ assertEquals("Wrapper [p=Child" + hash + " [b=0, pb=Parent[] [null]," +
+ " super=Parent [a=0, pa=Parent[] [Child" + hash + "]]]]", w.toString());
+
+ ((Child)p).pb[0] = p;
+
+ assertEquals("Wrapper [p=Child" + hash + " [b=0, pb=Parent[] [Child" + hash
+ + "], super=Parent [a=0, pa=Parent[] [Child" + hash + "]]]]", w.toString());
+ }
+
+ /**
+ * Verifies that {@link IgniteToStringBuilder} doesn't fail while iterating over concurrently modified collection.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testToStringCheckConcurrentModificationExceptionFromList() throws Exception {
+ ClassWithList classWithList = new ClassWithList();
+
+ CountDownLatch modificationStartedLatch = new CountDownLatch(1);
+ AtomicBoolean finished = new AtomicBoolean(false);
+
+ final CompletableFuture<Void> finishFut = CompletableFuture.runAsync(() -> {
+ List<SlowToStringObject> list = classWithList.list;
+ for (int i = 0; i < 100; i++)
+ list.add(new SlowToStringObject());
+
+ Random rnd = new Random();
+
+ while (!finished.get()) {
+ if (rnd.nextBoolean() && list.size() > 1)
+ list.remove(list.size() / 2);
+ else
+ list.add(list.size() / 2, new SlowToStringObject());
+
+ if (modificationStartedLatch.getCount() > 0)
+ modificationStartedLatch.countDown();
+ }
+ });
+
+ modificationStartedLatch.await();
+
+ String s = null;
+
+ try {
+ s = classWithList.toString();
+ }
+ finally {
+ finished.set(true);
+
+ finishFut.get();
+
+ assertNotNull(s);
+ assertTrue(s.contains("concurrent modification"));
+ }
+ }
+
+ /**
+ * Verifies that {@link IgniteToStringBuilder} doesn't fail while iterating over concurrently modified map.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testToStringCheckConcurrentModificationExceptionFromMap() throws Exception {
+ ClassWithMap classWithMap = new ClassWithMap();
+
+ CountDownLatch modificationStartedLatch = new CountDownLatch(1);
+ AtomicBoolean finished = new AtomicBoolean(false);
+
+ CompletableFuture<Void> finishFut = CompletableFuture.runAsync(() -> {
+ Map<Integer, SlowToStringObject> map = classWithMap.map;
+ for (int i = 0; i < 100; i++)
+ map.put(i, new SlowToStringObject());
+
+ Random rnd = new Random();
+
+ while (!finished.get()) {
+ if (rnd.nextBoolean() && map.size() > 1)
+ map.remove(map.size() / 2);
+ else
+ map.put(map.size() / 2, new SlowToStringObject());
+
+ if (modificationStartedLatch.getCount() > 0)
+ modificationStartedLatch.countDown();
+ }
+ });
+
+ modificationStartedLatch.await();
+
+ String s = null;
+
+ try {
+ s = classWithMap.toString();
+ }
+ finally {
+ finished.set(true);
+
+ finishFut.get();
+
+ assertNotNull(s);
+ assertTrue(s.contains("concurrent modification"));
+ }
+ }
+
+ /**
+ * Test verifies that when RuntimeException is thrown from toString method of some class
+ * IgniteToString builder doesn't fail but finishes building toString representation.
+ */
+ @Test
+ public void testRuntimeExceptionCaught() {
+ WrapperForFaultyToStringClass wr = new WrapperForFaultyToStringClass(
+ new ClassWithFaultyToString[] {new ClassWithFaultyToString()});
+
+ String strRep = wr.toString();
+
+ //field before faulty field was written successfully to string representation
+ assertTrue(strRep.contains("id=12345"));
+
+ //message from RuntimeException was written to string representation
+ assertTrue(strRep.contains("toString failed"));
+
+ //field after faulty field was written successfully to string representation
+ assertTrue(strRep.contains("str=str"));
+ }
+
+ /**
+ * Test class.
+ */
+ @SuppressWarnings("InstanceVariableMayNotBeInitialized")
+ private static class Node {
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ String name;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ Node next;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ Node[] nodes;
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return IgniteToStringBuilder.toString(Node.class, this);
+ }
+ }
+
+ /**
+ * Test class.
+ */
+ private static class BarrierCallable implements Callable<String> {
+ /**
+ *
+ */
+ CyclicBarrier bar;
+
+ /**
+ *
+ */
+ Object obj;
+
+ /** Expected value of {@code toString()} method. */
+ String exp;
+
+ /**
+ *
+ */
+ private BarrierCallable(CyclicBarrier bar, Object obj, String exp) {
+ this.bar = bar;
+ this.obj = obj;
+ this.exp = exp;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String call() throws Exception {
+ for (int i = 0; i < 10; i++) {
+ bar.await();
+
+ assertEquals(exp, obj.toString());
+ }
+
+ return null;
+ }
+ }
+
+ /**
+ * Class containing another class with faulty toString implementation
+ * to force IgniteToStringBuilder to call faulty toString.
+ */
+ @SuppressWarnings({"FieldMayBeFinal", "unused"})
+ private static class WrapperForFaultyToStringClass {
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private int id = 12345;
+
+ /**
+ *
+ */
+ @SuppressWarnings("unused")
+ @IgniteToStringInclude
+ private ClassWithFaultyToString[] arr;
+
+ /**
+ *
+ */
+ @SuppressWarnings("unused")
+ @IgniteToStringInclude
+ private String str = "str";
+
+ /**
+ *
+ */
+ WrapperForFaultyToStringClass(ClassWithFaultyToString[] arr) {
+ this.arr = arr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(WrapperForFaultyToStringClass.class, this);
+ }
+ }
+
+ /**
+ * Class throwing a RuntimeException from a {@link ClassWithFaultyToString#toString()} method.
+ */
+ private static class ClassWithFaultyToString {
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ throw new RuntimeException("toString failed");
+ }
+ }
+
+ /**
+ * Test class.
+ */
+ @SuppressWarnings({"InstanceVariableMayNotBeInitialized", "FieldMayBeFinal", "unused", "FieldMayBeStatic"})
+ private static class TestClass1 {
+ /**
+ *
+ */
+ @IgniteToStringOrder(0)
+ private String id = "1234567890";
+
+ /**
+ *
+ */
+ private int intVar;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude(sensitive = true)
+ private long longVar;
+
+ /**
+ *
+ */
+ @IgniteToStringOrder(1)
+ private UUID uuidVar = UUID.randomUUID();
+
+ /**
+ *
+ */
+ private boolean boolVar;
+
+ /**
+ *
+ */
+ private byte byteVar;
+
+ /**
+ *
+ */
+ private String name = "qwertyuiopasdfghjklzxcvbnm";
+
+ /**
+ *
+ */
+ private final Integer finalInt = 2;
+
+ /**
+ *
+ */
+ private List<String> strList;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private Map<String, String> strMap;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private List<String> strListIncl;
+
+ /**
+ *
+ */
+ private Object obj = new Object();
+
+ /**
+ *
+ */
+ private ReadWriteLock lock;
+
+ /**
+ * @return Manual string.
+ */
+ String toStringManual() {
+ StringBuilder buf = new StringBuilder();
+
+ buf.append(getClass().getSimpleName()).append(" [");
+
+ buf.append("id=").append(id).append(", ");
+ buf.append("uuidVar=").append(uuidVar).append(", ");
+ buf.append("intVar=").append(intVar).append(", ");
+ if (S.includeSensitive())
+ buf.append("longVar=").append(longVar).append(", ");
+ buf.append("boolVar=").append(boolVar).append(", ");
+ buf.append("byteVar=").append(byteVar).append(", ");
+ buf.append("name=").append(name).append(", ");
+ buf.append("finalInt=").append(finalInt).append(", ");
+ buf.append("strMap=").append(strMap).append(", ");
+ buf.append("strListIncl=").append(strListIncl);
+
+ buf.append("]");
+
+ return buf.toString();
+ }
+
+ /**
+ * @return Automatic string.
+ */
+ String toStringAutomatic() {
+ return S.toString(TestClass1.class, this);
+ }
+
+ /**
+ * @return Automatic string with additional parameters.
+ */
+ String toStringWithAdditionalAutomatic() {
+ return S.toString(TestClass1.class, this, "newParam1", 1, false, "newParam2", 2, true);
+ }
+
+ /**
+ * @return Manual string with additional parameters.
+ */
+ String toStringWithAdditionalManual() {
+ StringBuilder s = new StringBuilder(toStringManual());
+ s.setLength(s.length() - 1);
+ s.append(", newParam1=").append(1);
+ if (S.includeSensitive())
+ s.append(", newParam2=").append(2);
+ s.append(']');
+ return s.toString();
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings({"InstanceVariableMayNotBeInitialized", "FieldMayBeFinal", "unused"})
+ private static class TestClass2 {
+ /**
+ *
+ */
+ @SuppressWarnings("unused")
+ @IgniteToStringInclude
+ private String str;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private Object[] nullArr;
+
+ /**
+ * @param str String.
+ */
+ TestClass2(String str) {
+ this.str = str;
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("FieldMayBeFinal")
+ private static class ClassWithList {
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private List<SlowToStringObject> list = new LinkedList<>();
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(ClassWithList.class, this);
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings("FieldMayBeFinal")
+ private static class ClassWithMap {
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private Map<Integer, SlowToStringObject> map = new HashMap<>();
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(ClassWithMap.class, this);
+ }
+ }
+
+ /**
+ * Class sleeps a short quanta of time to increase chances of data race
+ * between {@link IgniteToStringBuilder} iterating over collection user thread concurrently modifying it.
+ */
+ private static class SlowToStringObject {
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ try {
+ Thread.sleep(1);
+ }
+ catch (InterruptedException e) {
+ log.error(e.getMessage(), e);
+ }
+
+ return super.toString();
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings({"InstanceVariableMayNotBeInitialized", "MismatchedReadAndWriteOfArray", "unused", "FieldMayBeFinal"})
+ private static class Parent {
+ /**
+ *
+ */
+ private int a;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private Parent[] pa = new Parent[1];
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Parent.class, this);
+ }
+ }
+
+ /**
+ *
+ */
+ @SuppressWarnings({"InstanceVariableMayNotBeInitialized", "MismatchedReadAndWriteOfArray", "unused", "FieldMayBeFinal"})
+ private static class Child extends Parent {
+ /**
+ *
+ */
+ private int b;
+
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ private Parent[] pb = new Parent[1];
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Child.class, this, super.toString());
+ }
+ }
+
+ /**
+ *
+ */
+ private static class Wrapper {
+ /**
+ *
+ */
+ @IgniteToStringInclude
+ Parent p = new Child();
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Wrapper.class, this);
+ }
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/tostring/SensitiveDataToStringTest.java b/modules/core/src/test/java/org/apache/ignite/internal/tostring/SensitiveDataToStringTest.java
new file mode 100644
index 0000000..9017b73
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/tostring/SensitiveDataToStringTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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 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 org.apache.ignite.internal.tostring;
+
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import org.apache.ignite.internal.testframework.IgniteAbstractTest;
+import org.apache.ignite.internal.testframework.SystemPropertiesExtension;
+import org.apache.ignite.internal.testframework.WithSystemProperty;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.lang.IgniteSystemProperties;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import static org.apache.ignite.internal.tostring.SensitiveDataLoggingPolicy.HASH;
+import static org.apache.ignite.internal.tostring.SensitiveDataLoggingPolicy.NONE;
+import static org.apache.ignite.internal.tostring.SensitiveDataLoggingPolicy.PLAIN;
+import static org.apache.ignite.lang.IgniteSystemProperties.IGNITE_SENSITIVE_DATA_LOGGING;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for output of {@code toString()} depending on the value of
+ * {@link IgniteSystemProperties#IGNITE_SENSITIVE_DATA_LOGGING}
+ */
+@ExtendWith(SystemPropertiesExtension.class)
+public class SensitiveDataToStringTest extends IgniteAbstractTest {
+ /** Random int. */
+ private static final int rndInt0 = 54321;
+
+ /** Random string. */
+ private static final String rndString = "qwer";
+
+ /**
+ *
+ */
+ @Test
+ public void testSensitivePropertiesResolving0() {
+ assertSame(HASH, S.getSensitiveDataLogging(), S.getSensitiveDataLogging().toString());
+ }
+
+ /**
+ *
+ */
+ @Test
+ @WithSystemProperty(key = IGNITE_SENSITIVE_DATA_LOGGING, value = "plain")
+ public void testSensitivePropertiesResolving1() {
+ assertSame(PLAIN, S.getSensitiveDataLogging(), S.getSensitiveDataLogging().toString());
+ }
+
+ /**
+ *
+ */
+ @Test
+ @WithSystemProperty(key = IGNITE_SENSITIVE_DATA_LOGGING, value = "hash")
+ public void testSensitivePropertiesResolving2() {
+ assertSame(HASH, S.getSensitiveDataLogging(), S.getSensitiveDataLogging().toString());
+ }
+
+ /**
+ *
+ */
+ @Test
+ @WithSystemProperty(key = IGNITE_SENSITIVE_DATA_LOGGING, value = "none")
+ public void testSensitivePropertiesResolving3() {
+ assertSame(NONE, S.getSensitiveDataLogging(), S.getSensitiveDataLogging().toString());
+ }
+
+ /**
+ *
+ */
+ @Test
+ @WithSystemProperty(key = IGNITE_SENSITIVE_DATA_LOGGING, value = "plain")
+ public void testTableObjectImplWithSensitive() {
+ testTableObjectImpl((strToCheck, object) -> assertTrue(strToCheck.contains(object.toString()), strToCheck));
+ }
+
+ /**
+ *
+ */
+ @Test
+ @WithSystemProperty(key = IGNITE_SENSITIVE_DATA_LOGGING, value = "hash")
+ public void testTableObjectImplWithHashSensitive() {
+ testTableObjectImpl((strToCheck, object) -> assertTrue(strToCheck.contains(object.toString()), strToCheck));
+ }
+
+ /**
+ *
+ */
+ @Test
+ @WithSystemProperty(key = IGNITE_SENSITIVE_DATA_LOGGING, value = "none")
+ public void testTableObjectImplWithoutSensitive() {
+ testTableObjectImpl((strToCheck, object) -> assertEquals("TableObject", object.toString(), strToCheck));
+ }
+
+ /**
+ *
+ */
+ private void testTableObjectImpl(BiConsumer<String, Object> checker) {
+ Person person = new Person(rndInt0, rndString);
+
+ TableObject testObject = new TableObject(person);
+ checker.accept(testObject.toString(), testObject);
+ }
+
+ /**
+ *
+ */
+ static class TableObject {
+ @IgniteToStringInclude(sensitive = true)
+ Person person;
+
+ TableObject(Person person) {
+ this.person = person;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ switch (S.getSensitiveDataLogging()) {
+ case PLAIN:
+ return S.toString(getClass().getSimpleName(), "person", person, false);
+
+ case HASH:
+ return String.valueOf(person == null ? "null" : IgniteUtils.hash(person));
+
+ case NONE:
+ default:
+ return "TableObject";
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ static class Person {
+ /** Id organization. */
+ int orgId;
+
+ /** Person name. */
+ String name;
+
+ /**
+ * Constructor.
+ *
+ * @param orgId Id organization.
+ * @param name Person name.
+ */
+ Person(int orgId, String name) {
+ this.orgId = orgId;
+ this.name = name;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(orgId, name);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Person.class, this);
+ }
+ }
+}
diff --git a/modules/network/pom.xml b/modules/network/pom.xml
index 93547f1..3de7c6d 100644
--- a/modules/network/pom.xml
+++ b/modules/network/pom.xml
@@ -44,6 +44,12 @@
<dependency>
<groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-configuration-annotation-processor</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
<artifactId>ignite-core</artifactId>
<version>${project.version}</version>
</dependency>
@@ -66,13 +72,6 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
-
- <dependency>
- <groupId>org.apache.ignite</groupId>
- <artifactId>ignite-configuration-annotation-processor</artifactId>
- <version>${project.version}</version>
- <scope>compile</scope>
- </dependency>
</dependencies>
<build>
<resources>
diff --git a/modules/network/src/integrationTest/java/org/apache/ignite/network/TestMessage.java b/modules/network/src/integrationTest/java/org/apache/ignite/network/TestMessage.java
index d87715e..3ee3594 100644
--- a/modules/network/src/integrationTest/java/org/apache/ignite/network/TestMessage.java
+++ b/modules/network/src/integrationTest/java/org/apache/ignite/network/TestMessage.java
@@ -21,6 +21,8 @@ package org.apache.ignite.network;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.network.message.NetworkMessage;
/** */
@@ -31,6 +33,8 @@ public class TestMessage implements NetworkMessage, Serializable {
/** */
private final String msg;
+ /** */
+ @IgniteToStringInclude
private final Map<Integer, String> map;
/** */
@@ -48,14 +52,6 @@ public class TestMessage implements NetworkMessage, Serializable {
}
/** {@inheritDoc} */
- @Override public String toString() {
- return "TestMessage{" +
- "msg='" + msg + '\'' +
- ", map=" + map +
- '}';
- }
-
- /** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -72,4 +68,9 @@ public class TestMessage implements NetworkMessage, Serializable {
@Override public short directType() {
return TYPE;
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(TestMessage.class, this);
+ }
}
diff --git a/modules/network/src/main/java/org/apache/ignite/network/ClusterNode.java b/modules/network/src/main/java/org/apache/ignite/network/ClusterNode.java
index e0ad6a7..205c32f 100644
--- a/modules/network/src/main/java/org/apache/ignite/network/ClusterNode.java
+++ b/modules/network/src/main/java/org/apache/ignite/network/ClusterNode.java
@@ -18,6 +18,7 @@ package org.apache.ignite.network;
import java.io.Serializable;
import java.util.Objects;
+import org.apache.ignite.internal.tostring.S;
/**
* Representation of a node in a cluster.
@@ -79,10 +80,6 @@ public class ClusterNode implements Serializable {
/** {@inheritDoc} */
@Override public String toString() {
- return "ClusterNode{" +
- "name='" + name + '\'' +
- ", host='" + host + '\'' +
- ", port=" + port +
- '}';
+ return S.toString(ClusterNode.class, this);
}
}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/Peer.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/Peer.java
index 6bb66f2..53da9a3 100644
--- a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/Peer.java
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/Peer.java
@@ -18,6 +18,7 @@
package org.apache.ignite.raft.client;
import java.io.Serializable;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.network.ClusterNode;
/**
@@ -73,6 +74,7 @@ public final class Peer implements Serializable {
return priority;
}
+ /** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -85,13 +87,15 @@ public final class Peer implements Serializable {
return true;
}
+ /** {@inheritDoc} */
@Override public int hashCode() {
int result = node.hashCode();
result = 31 * result + priority;
return result;
}
+ /** {@inheritDoc} */
@Override public String toString() {
- return node.name() + ":" + priority;
+ return S.toString(Peer.class, this);
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java
index 9ad2d9f..2c1ecbb 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/AbstractSchemaObject.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.SchemaObject;
/**
@@ -42,9 +43,8 @@ public abstract class AbstractSchemaObject implements SchemaObject {
/** {@inheritDoc} */
@Override public String toString() {
- return "SchemaObject[" +
- "name='" + name + '\'' +
- "class=" + getClass().getName() +
- ']';
+ return S.toString("SchemaObject",
+ "name", name,
+ "class", getClass().getName());
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Bitmask.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Bitmask.java
index c3b1eed..172b195 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Bitmask.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Bitmask.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
+
/**
* A fixed-sized type representing a bitmask of <code>n</code> bits. The actual size of a bitmask will round up
* to the smallest number of bytes required to store <code>n</code> bits.
@@ -84,4 +86,9 @@ public class Bitmask extends NativeType {
else
return res;
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(Bitmask.class.getSimpleName(), "bits", bits, "typeSpec", spec(), "len", length());
+ }
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java
index 1758f77..12eaa9c 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
+
/**
* Column description for a type schema. Column contains a column name, a column type and a nullability flag.
* <p>
@@ -132,6 +134,6 @@ public class Column implements Comparable<Column> {
/** {@inheritDoc} */
@Override public String toString() {
- return "Column [idx=" + schemaIndex + ", name=" + name + ", type=" + type + ", nullable=" + nullable + ']';
+ return S.toString(Column.class, this);
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/ColumnImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/ColumnImpl.java
index 24dfc4b..84e75f0 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/ColumnImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/ColumnImpl.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.Column;
import org.apache.ignite.schema.ColumnType;
import org.jetbrains.annotations.Nullable;
@@ -32,6 +34,7 @@ public class ColumnImpl extends AbstractSchemaObject implements Column {
private final boolean nullable;
/** Default value. */
+ @IgniteToStringInclude(sensitive = true)
private final Object defVal;
/**
@@ -66,11 +69,6 @@ public class ColumnImpl extends AbstractSchemaObject implements Column {
/** {@inheritDoc} */
@Override public String toString() {
- return "ColumnImpl[" +
- "name='" + name() + '\'' +
- ", type=" + type +
- ", nullable=" + nullable +
- ", default=" + defVal +
- ']';
+ return S.toString(ColumnImpl.class, this);
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Columns.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Columns.java
index 669ed6d..9f7190a 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Columns.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Columns.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.schema;
import java.util.Arrays;
import java.util.NoSuchElementException;
+import org.apache.ignite.internal.tostring.S;
/**
* A set of columns representing a key or a value chunk in a row.
@@ -283,4 +284,9 @@ public class Columns {
throw new NoSuchElementException("No field '" + fieldName + "' defined");
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.arrayToString(cols);
+ }
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/HashIndexImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/HashIndexImpl.java
index cbac7bd..fde1522 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/HashIndexImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/HashIndexImpl.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.schema;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.HashIndex;
import org.apache.ignite.schema.IndexColumn;
@@ -28,6 +30,7 @@ import org.apache.ignite.schema.IndexColumn;
*/
public class HashIndexImpl extends AbstractSchemaObject implements HashIndex {
/** Index columns. */
+ @IgniteToStringInclude
private final List<IndexColumn> columns;
/**
@@ -50,11 +53,9 @@ public class HashIndexImpl extends AbstractSchemaObject implements HashIndex {
/** {@inheritDoc} */
@Override public String toString() {
- return "TableIndex[" +
- "type=HASH, " +
- "name='" + name() + '\'' +
- ", columns=[" + columns.stream().map(IndexColumn::name).collect(Collectors.joining()) + ']' +
- ']';
+ return S.toString(HashIndexImpl.class, this,
+ "type", type(),
+ "name", name());
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
index a7fe166..9948859 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/IndexColumnImpl.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.IndexColumn;
/**
@@ -34,8 +35,6 @@ class IndexColumnImpl extends AbstractSchemaObject implements IndexColumn {
/** {@inheritDoc} */
@Override public String toString() {
- return "Column[" +
- "name='" + name() + '\'' +
- ']';
+ return S.toString(IndexColumnImpl.class.getSimpleName(), "name", name());
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeType.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeType.java
index 6572a36..d580137 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeType.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeType.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
+
/**
* A thin wrapper over {@link NativeTypeSpec} to instantiate parameterized constrained types.
*/
@@ -55,6 +57,7 @@ public class NativeType implements Comparable<NativeType> {
private final int len;
/**
+ *
*/
protected NativeType(NativeTypeSpec typeSpec, int len) {
if (!typeSpec.fixedLength())
@@ -68,6 +71,7 @@ public class NativeType implements Comparable<NativeType> {
}
/**
+ *
*/
protected NativeType(NativeTypeSpec typeSpec) {
if (typeSpec.fixedLength())
@@ -137,6 +141,9 @@ public class NativeType implements Comparable<NativeType> {
/** {@inheritDoc} */
@Override public String toString() {
- return "NativeType [typeSpec=" + typeSpec + ", len=" + len + ']';
+ return S.toString(NativeType.class.getSimpleName(),
+ "name", typeSpec.name(),
+ "len", len,
+ "fixed", typeSpec.fixedLength());
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java
index a5ae386..cc470aa 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypeSpec.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
+
/**
* Base class for storage built-in data types definition. The class contains predefined values
* for fixed-sized types and some of the variable-sized types. Parameterized types, such as
@@ -173,6 +175,8 @@ public enum NativeTypeSpec {
/** {@inheritDoc} */
@Override public String toString() {
- return "NativeTypeSpec [desc=" + desc + ", fixedSize=" + fixedSize + ']';
+ return S.toString(NativeTypeSpec.class.getSimpleName(),
+ "name", name(),
+ "fixed", fixedLength());
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/PartialIndexImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/PartialIndexImpl.java
index 09c2295..3500497 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/PartialIndexImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/PartialIndexImpl.java
@@ -18,8 +18,7 @@
package org.apache.ignite.internal.schema;
import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.ignite.schema.IndexColumn;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.PartialIndex;
import org.apache.ignite.schema.SortedIndexColumn;
@@ -51,11 +50,10 @@ public class PartialIndexImpl extends SortedIndexImpl implements PartialIndex {
/** {@inheritDoc} */
@Override public String toString() {
- return "PartialIndex[" +
- "name='" + name() + '\'' +
- ", type=PARTIAL" +
- ", expr='" + expr + '\'' +
- ", columns=[" + columns().stream().map(IndexColumn::toString).collect(Collectors.joining(",")) + ']' +
- ']';
+ return S.toString(PartialIndex.class, this,
+ "type", type(),
+ "name", name(),
+ "uniq", unique(),
+ "cols", columns());
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/PrimaryIndexImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/PrimaryIndexImpl.java
index c99340d..f433531 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/PrimaryIndexImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/PrimaryIndexImpl.java
@@ -19,6 +19,8 @@ package org.apache.ignite.internal.schema;
import java.util.Collections;
import java.util.List;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.PrimaryIndex;
import org.apache.ignite.schema.SortedIndexColumn;
@@ -27,6 +29,7 @@ import org.apache.ignite.schema.SortedIndexColumn;
*/
public class PrimaryIndexImpl extends SortedIndexImpl implements PrimaryIndex {
/** Affinity columns. */
+ @IgniteToStringInclude
private final List<String> affCols;
/**
@@ -49,4 +52,18 @@ public class PrimaryIndexImpl extends SortedIndexImpl implements PrimaryIndex {
@Override public String type() {
return "PK";
}
+
+ /** {@inheritDoc} */
+ @Override public boolean unique() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(PrimaryIndexImpl.class, this,
+ "type", type(),
+ "name", name(),
+ "uniq", unique(),
+ "cols", columns());
+ }
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
index 15c0962..e0e0dfb 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.schema;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import org.apache.ignite.internal.tostring.S;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -106,4 +107,9 @@ public class SchemaDescriptor {
public @Nullable Column column(@NotNull String name) {
return colMap.get(name);
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(SchemaDescriptor.class, this);
+ }
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaTableImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaTableImpl.java
index c4180f9..97589ff 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaTableImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaTableImpl.java
@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.ignite.internal.schema.modification.TableModificationBuilderImpl;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.Column;
import org.apache.ignite.schema.IndexColumn;
import org.apache.ignite.schema.PrimaryIndex;
@@ -134,12 +135,6 @@ public class SchemaTableImpl extends AbstractSchemaObject implements SchemaTable
/** {@inheritDoc} */
@Override public String toString() {
- return "SchemaTable[" +
- "name='" + name() + '\'' +
- ", keyCols=" + keyCols.stream().map(Column::name).collect(Collectors.joining(",")) +
- ", affCols=" + affCols.stream().map(Column::name).collect(Collectors.joining(",")) +
- ", column=" + cols.values() +
- ", indices=" + indices.values() +
- ']';
+ return S.toString(SchemaTableImpl.class, this);
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexColumnImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexColumnImpl.java
index 8df6ae7..a0f7a37 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexColumnImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexColumnImpl.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.schema;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.SortedIndexColumn;
/**
@@ -45,9 +46,6 @@ public class SortedIndexColumnImpl extends AbstractSchemaObject implements Sorte
/** {@inheritDoc} */
@Override public String toString() {
- return "Column[" +
- "name='" + name() + '\'' +
- ", order=" + (asc ? "asc" : "desc") +
- ']';
+ return S.toString(SortedIndexColumnImpl.class, this);
}
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexImpl.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexImpl.java
index 115f46c..3b5a44a 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexImpl.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SortedIndexImpl.java
@@ -19,8 +19,8 @@ package org.apache.ignite.internal.schema;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.ignite.schema.IndexColumn;
+import org.apache.ignite.internal.tostring.IgniteToStringInclude;
+import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.schema.SortedIndex;
import org.apache.ignite.schema.SortedIndexColumn;
@@ -29,6 +29,7 @@ import org.apache.ignite.schema.SortedIndexColumn;
*/
public class SortedIndexImpl extends AbstractSchemaObject implements SortedIndex {
/** Columns. */
+ @IgniteToStringInclude
private final List<SortedIndexColumn> cols;
/** Unique flag. */
@@ -65,12 +66,8 @@ public class SortedIndexImpl extends AbstractSchemaObject implements SortedIndex
/** {@inheritDoc} */
@Override public String toString() {
- return "SortedIndex[" +
- "name='" + name() + '\'' +
- ", type=SORTED" +
- ", uniq=" + uniq +
- ", columns=[" + columns().stream().map(IndexColumn::toString).collect(Collectors.joining(",")) +
- "]]";
+ return S.toString(SortedIndex.class, this,
+ "type", type(),
+ "name", name());
}
-
}