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 2020/11/16 12:15:37 UTC
[ignite] branch ignite-13618 updated (0793e09 -> 5cc7dd0)
This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a change to branch ignite-13618
in repository https://gitbox.apache.org/repos/asf/ignite.git.
discard 0793e09 WIP. Replace reflection with unsafe.
new 5cc7dd0 WIP. Replace reflection with unsafe.
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (0793e09)
\
N -- N -- N refs/heads/ignite-13618 (5cc7dd0)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../schema/marshaller/JavaSerializerTest.java | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
[ignite] 01/01: WIP. Replace reflection with unsafe.
Posted by am...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch ignite-13618
in repository https://gitbox.apache.org/repos/asf/ignite.git
commit 5cc7dd047f97fda8e978d864faa5b77a2235373f
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Mon Nov 16 15:02:40 2020 +0300
WIP. Replace reflection with unsafe.
---
.../ignite/internal/schema/marshaller/Factory.java | 31 +++
.../internal/schema/marshaller/FieldAccessor.java | 144 ++++++++------
.../internal/schema/marshaller/JavaSerializer.java | 77 ++------
.../internal/schema/marshaller/Marshaller.java | 9 +-
.../schema/marshaller/MarshallerFactory.java | 74 +++++++
.../internal/schema/marshaller/ObjectFactory.java | 49 ++---
.../ignite/internal/util/IgniteUnsafeUtils.java | 219 +++++++++++++++++++++
.../schema/marshaller/JavaSerializerTest.java | 19 +-
8 files changed, 461 insertions(+), 161 deletions(-)
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Factory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Factory.java
new file mode 100644
index 0000000..0ad31e4
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Factory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.schema.marshaller;
+
+/**
+ * Factory interface.
+ * @param <T> Object type.
+ */
+public interface Factory<T> {
+ /**
+ * Creates object.
+ *
+ * @return Object.
+ */
+ public T create();
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/FieldAccessor.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/FieldAccessor.java
index 6e5603d..713553d 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/FieldAccessor.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/FieldAccessor.java
@@ -23,13 +23,13 @@ import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.Columns;
import org.apache.ignite.internal.schema.Tuple;
import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.util.IgniteUnsafeUtils;
import org.jetbrains.annotations.Nullable;
/**
* Field accessor to speedup access.
*/
-//TODO: Rewrite to direct field access via Unsafe to bypass security checks.
-//TODO: Extract interface, move to java-8 profile and add implementation based on VarHandle for java9+.
+// TODO: Extract interface, move to java-8 profile and add Java9+ implementation using VarHandles.
public abstract class FieldAccessor {
/**
* TODO: implement sesitive information filtering.
@@ -40,12 +40,15 @@ public abstract class FieldAccessor {
return true;
}
- /** Field name */
- protected final Field field;
+ /** Offset. */
+ protected final long offset;
/** Mode. */
protected final BinaryMode mode;
+ /** Field name */
+ protected final String name;
+
/**
* Mapped column position in schema.
* <p>
@@ -61,12 +64,11 @@ public abstract class FieldAccessor {
* @param colIdx Column index in schema.
* @return Accessor.
*/
- //TODO: Extract a provider for this factory-method.
- public static FieldAccessor create(Class<?> type, Column col, int colIdx) {
+ static FieldAccessor create(Class<?> type, Column col, int colIdx) {
try {
final Field field = type.getDeclaredField(col.name());
- if (field.getType().isPrimitive() && col.nullable()) //TODO: convert to assert?
+ if (field.getType().isPrimitive() && col.nullable())
throw new IllegalArgumentException("Failed to map non-nullable field to nullable column [name=" + field.getName() + ']');
BinaryMode mode = JavaSerializer.mode(field.getType());
@@ -159,15 +161,32 @@ public abstract class FieldAccessor {
* @param mode Binary mode;
*/
protected FieldAccessor(Field field, int colIdx, BinaryMode mode) {
+ assert field != null;
assert colIdx >= 0;
assert mode != null;
- this.field = field;
this.colIdx = colIdx;
this.mode = mode;
+ offset = IgniteUnsafeUtils.objectFieldOffset(field);
+ name = field.getName();
+
+ field.setAccessible(true);
+ }
+
+ /**
+ * Protected constructor.
+ *
+ * @param colIdx Column index.
+ * @param mode Binary mode;
+ */
+ private FieldAccessor(int colIdx, BinaryMode mode) {
+ assert colIdx >= 0;
+ assert mode != null;
- if (field != null)
- field.setAccessible(true);
+ this.colIdx = colIdx;
+ this.mode = mode;
+ offset = 0;
+ name = null;
}
/**
@@ -191,8 +210,8 @@ public abstract class FieldAccessor {
write0(Objects.requireNonNull(obj), writer);
}
catch (Exception ex) {
- if (includeSensitive() && field != null)
- throw new SerializationException("Failed to write field [name=" + field.getName() + ']', ex);
+ if (includeSensitive() && name != null)
+ throw new SerializationException("Failed to write field [name=" + name + ']', ex);
else
throw new SerializationException("Failed to write field [id=" + colIdx + ']', ex);
}
@@ -219,8 +238,8 @@ public abstract class FieldAccessor {
read0(Objects.requireNonNull(obj), reader);
}
catch (Exception ex) {
- if (includeSensitive())
- throw new SerializationException("Failed to read field [name=" + field.getName() + ']', ex);
+ if (includeSensitive() && name != null)
+ throw new SerializationException("Failed to read field [name=" + name + ']', ex);
else
throw new SerializationException("Failed to read field [id=" + colIdx + ']', ex);
}
@@ -250,18 +269,9 @@ public abstract class FieldAccessor {
*
* @param obj Object.
* @return Field value of given object.
- * @throws SerializationException If failed.
*/
- @Nullable Object value(Object obj) throws SerializationException {
- try {
- return field.get(Objects.requireNonNull(obj));
- }
- catch (IllegalAccessException ex) {
- if (includeSensitive())
- throw new SerializationException("Failed to read field [name=" + field.getName() + ']', ex);
- else
- throw new SerializationException("Failed to read field [id=" + colIdx + ']', ex);
- }
+ @Nullable Object value(Object obj) {
+ return IgniteUnsafeUtils.getObjectField(Objects.requireNonNull(obj), offset);
}
/**
@@ -275,7 +285,7 @@ public abstract class FieldAccessor {
* @param mode Binary mode.
*/
public IdentityAccessor(int colIdx, BinaryMode mode) {
- super(null, colIdx, mode);
+ super(colIdx, mode);
}
/** {@inheritDoc} */
@@ -314,13 +324,17 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
- writer.appendByte(field.getByte(obj));
+ @Override protected void write0(Object obj, TupleAssembler writer) {
+ final byte val = IgniteUnsafeUtils.getByteField(obj, offset);
+
+ writer.appendByte(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) throws IllegalAccessException {
- field.setByte(obj, reader.byteValue(colIdx));
+ @Override protected void read0(Object obj, Tuple reader) {
+ final byte val = reader.byteValue(colIdx);
+
+ IgniteUnsafeUtils.putByteField(obj, offset, val);
}
}
@@ -339,13 +353,17 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
- writer.appendShort(field.getShort(obj));
+ @Override protected void write0(Object obj, TupleAssembler writer) {
+ final short val = IgniteUnsafeUtils.getShortField(obj, offset);
+
+ writer.appendShort(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) throws IllegalAccessException {
- field.setShort(obj, reader.shortValue(colIdx));
+ @Override protected void read0(Object obj, Tuple reader) {
+ final short val = reader.shortValue(colIdx);
+
+ IgniteUnsafeUtils.putShortField(obj, offset, val);
}
}
@@ -364,13 +382,17 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
- writer.appendInt(field.getInt(obj));
+ @Override protected void write0(Object obj, TupleAssembler writer) {
+ final int val = IgniteUnsafeUtils.getIntField(obj, offset);
+
+ writer.appendInt(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) throws IllegalAccessException {
- field.setInt(obj, reader.intValue(colIdx));
+ @Override protected void read0(Object obj, Tuple reader) {
+ final int val = reader.intValue(colIdx);
+
+ IgniteUnsafeUtils.putIntField(obj, offset, val);
}
}
@@ -389,13 +411,17 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
- writer.appendLong(field.getLong(obj));
+ @Override protected void write0(Object obj, TupleAssembler writer) {
+ final long val = IgniteUnsafeUtils.getLongField(obj, offset);
+
+ writer.appendLong(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) throws IllegalAccessException {
- field.setLong(obj, reader.longValue(colIdx));
+ @Override protected void read0(Object obj, Tuple reader) {
+ final long val = reader.longValue(colIdx);
+
+ IgniteUnsafeUtils.putLongField(obj, offset, val);
}
}
@@ -414,13 +440,17 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
- writer.appendFloat(field.getFloat(obj));
+ @Override protected void write0(Object obj, TupleAssembler writer) {
+ final float val = IgniteUnsafeUtils.getFloatField(obj, offset);
+
+ writer.appendFloat(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) throws IllegalAccessException {
- field.setFloat(obj, reader.floatValue(colIdx));
+ @Override protected void read0(Object obj, Tuple reader) {
+ final float val = reader.floatValue(colIdx);
+
+ IgniteUnsafeUtils.putFloatField(obj, offset, val);
}
}
@@ -439,13 +469,17 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
- writer.appendDouble(field.getDouble(obj));
+ @Override protected void write0(Object obj, TupleAssembler writer) {
+ final double val = IgniteUnsafeUtils.getDoubleField(obj, offset);
+
+ writer.appendDouble(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) throws IllegalAccessException {
- field.setDouble(obj, reader.doubleValue(colIdx));
+ @Override protected void read0(Object obj, Tuple reader) {
+ final double val = reader.doubleValue(colIdx);
+
+ IgniteUnsafeUtils.putDoubleField(obj, offset, val);
}
}
@@ -465,13 +499,13 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) throws IllegalAccessException {
+ @Override protected void write0(Object obj, TupleAssembler writer) {
assert obj != null;
assert writer != null;
Object val;
- val = field.get(obj);
+ val = IgniteUnsafeUtils.getObjectField(obj, offset);
if (val == null) {
writer.appendNull();
@@ -483,12 +517,10 @@ public abstract class FieldAccessor {
}
/** {@inheritDoc} */
- @Override public void read0(Object obj, Tuple reader) throws IllegalAccessException {
+ @Override public void read0(Object obj, Tuple reader) {
Object val = JavaSerializer.readRefValue(reader, colIdx, mode);
- assert !field.getType().isPrimitive();
-
- field.set(obj, val);
+ IgniteUnsafeUtils.putObjectField(obj, offset, val);
}
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/JavaSerializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/JavaSerializer.java
index 1687ccc..9294ce9 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/JavaSerializer.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/JavaSerializer.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.schema.marshaller;
import java.util.BitSet;
import java.util.UUID;
import org.apache.ignite.internal.schema.ByteBufferTuple;
-import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.Columns;
import org.apache.ignite.internal.schema.NativeType;
import org.apache.ignite.internal.schema.SchemaDescriptor;
@@ -254,42 +253,8 @@ public class JavaSerializer {
this.keyClass = keyClass;
this.valClass = valClass;
- keyMarsh = createMarshaller(schema.keyColumns(), 0, keyClass);
- valMarsh = createMarshaller(schema.valueColumns(), schema.keyColumns().length(), valClass);
- }
-
- /**
- * Creates marshaller for class.
- *
- * @param cols Columns.
- * @param firstColId First column position in schema.
- * @param aClass Type.
- * @return Marshaller.
- */
- private static Marshaller createMarshaller(Columns cols, int firstColId, Class<?> aClass) {
- final BinaryMode mode = mode(aClass);
-
- if (mode != null) {
- final Column col = cols.column(0);
-
- assert cols.length() == 1;
- assert mode.typeSpec() == col.type().spec() : "Target type is not compatible.";
- assert !aClass.isPrimitive() : "Non-nullable types are not allowed.";
-
- return new Marshaller(FieldAccessor.createIdentityAccessor(col, firstColId, mode));
- }
-
- FieldAccessor[] fieldAccessors = new FieldAccessor[cols.length()];
-
- // Build accessors
- for (int i = 0; i < cols.length(); i++) {
- final Column col = cols.column(i);
-
- final int colIdx = firstColId + i; /* Absolute column idx in schema. */
- fieldAccessors[i] = FieldAccessor.create(aClass, col, colIdx);
- }
-
- return new Marshaller(ObjectFactory.classFactory(aClass), fieldAccessors);
+ keyMarsh = MarshallerFactory.createMarshaller(schema.keyColumns(), 0, keyClass);
+ valMarsh = MarshallerFactory.createMarshaller(schema.valueColumns(), schema.keyColumns().length(), valClass);
}
/**
@@ -335,33 +300,14 @@ public class JavaSerializer {
}
/**
- * Object statistic.
- */
- private static class ObjectStatistic {
- /** Non-null fields of varlen type. */
- int nonNullFields;
-
- /** Length of all non-null fields of varlen types. */
- int nonNullFieldsSize;
-
- /** Constructor. */
- public ObjectStatistic(int nonNullFields, int nonNullFieldsSize) {
- this.nonNullFields = nonNullFields;
- this.nonNullFieldsSize = nonNullFieldsSize;
- }
- }
-
- /**
* Reads object fields and gather statistic.
*
* @param cols Schema columns.
* @param marsh Marshaller.
* @param obj Object.
* @return Object statistic.
- * @throws SerializationException If failed.
*/
- private ObjectStatistic collectObjectStats(Columns cols, Marshaller marsh, Object obj)
- throws SerializationException {
+ private ObjectStatistic collectObjectStats(Columns cols, Marshaller marsh, Object obj) {
if (obj == null || cols.firstVarlengthColumn() < 0 /* No varlen columns */)
return new ObjectStatistic(0, 0);
@@ -428,4 +374,21 @@ public class JavaSerializer {
return o;
}
+
+ /**
+ * Object statistic.
+ */
+ private static class ObjectStatistic {
+ /** Non-null fields of varlen type. */
+ int nonNullFields;
+
+ /** Length of all non-null fields of varlen types. */
+ int nonNullFieldsSize;
+
+ /** Constructor. */
+ public ObjectStatistic(int nonNullFields, int nonNullFieldsSize) {
+ this.nonNullFields = nonNullFields;
+ this.nonNullFieldsSize = nonNullFieldsSize;
+ }
+ }
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Marshaller.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Marshaller.java
index f907c16..3b73a3e 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Marshaller.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Marshaller.java
@@ -52,7 +52,7 @@ public class Marshaller {
/**
* Object factory for complex types or {@code null} for basic type.
*/
- private final ObjectFactory<?> factory;
+ private final Factory<?> factory;
/**
* Constructor.
@@ -62,7 +62,7 @@ public class Marshaller {
* @param fieldAccessors Object field accessors for mapped columns.
*/
@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
- public Marshaller(ObjectFactory<?> factory, FieldAccessor[] fieldAccessors) {
+ public Marshaller(Factory<?> factory, FieldAccessor[] fieldAccessors) {
this.fieldAccessors = fieldAccessors;
this.factory = Objects.requireNonNull(factory);
}
@@ -84,9 +84,8 @@ public class Marshaller {
* @param obj Object.
* @param fldIdx Field index.
* @return Field value.
- * @throws SerializationException If failed.
*/
- public @Nullable Object value(Object obj, int fldIdx) throws SerializationException {
+ public @Nullable Object value(Object obj, int fldIdx) {
return fieldAccessors[fldIdx].value(obj);
}
@@ -101,7 +100,7 @@ public class Marshaller {
if (isBasicTypeMarshaller())
return fieldAccessors[0].read(reader);
- final Object obj = factory.newInstance();
+ final Object obj = factory.create();
for (int fldIdx = 0; fldIdx < fieldAccessors.length; fldIdx++)
fieldAccessors[fldIdx].read(obj, reader);
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerFactory.java
new file mode 100644
index 0000000..3e642da
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerFactory.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.schema.marshaller;
+
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.Columns;
+
+public abstract class MarshallerFactory {
+ /**
+ * Creates marshaller for class.
+ *
+ * @param cols Columns.
+ * @param firstColId First column position in schema.
+ * @param aClass Type.
+ * @return Marshaller.
+ */
+ public static Marshaller createMarshaller(Columns cols, int firstColId, Class<? extends Object> aClass) {
+ final BinaryMode mode = JavaSerializer.mode(aClass);
+
+ if (mode != null) {
+ final Column col = cols.column(0);
+
+ assert cols.length() == 1;
+ assert mode.typeSpec() == col.type().spec() : "Target type is not compatible.";
+ assert !aClass.isPrimitive() : "Non-nullable types are not allowed.";
+
+ return new Marshaller(FieldAccessor.createIdentityAccessor(col, firstColId, mode));
+ }
+
+ FieldAccessor[] fieldAccessors = new FieldAccessor[cols.length()];
+
+ // Build accessors
+ for (int i = 0; i < cols.length(); i++) {
+ final Column col = cols.column(i);
+
+ final int colIdx = firstColId + i; /* Absolute column idx in schema. */
+ fieldAccessors[i] = FieldAccessor.create(aClass, col, colIdx);
+ }
+
+ return new Marshaller(new ObjectFactory<>(aClass), fieldAccessors);
+ }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/ObjectFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/ObjectFactory.java
index 954b656..d98ca69 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/ObjectFactory.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/ObjectFactory.java
@@ -17,56 +17,31 @@
package org.apache.ignite.internal.schema.marshaller;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
+import org.apache.ignite.internal.util.IgniteUnsafeUtils;
/**
* Object factory.
*/
-// TODO: Rewrite to direct field access via Unsafe to bypass security checks.
-// TODO: Extract interface, move to java-8 profile and add Java9+ implementation using VarHandles.
-class ObjectFactory<T> {
- /**
- * Creates factory for class.
- *
- * @param tClass Class.
- * @return Object factory.
- */
- static <T> ObjectFactory<T> classFactory(Class<T> tClass) {
- try {
- return new ObjectFactory<>(tClass.getDeclaredConstructor());
- }
- catch (NoSuchMethodException e) {
- throw new IllegalStateException("No default constructor found for class: " + tClass.getSimpleName());
- }
- }
-
- /** Constructor for class. */
- private final Constructor<T> ctor;
+class ObjectFactory<T> implements Factory<T> {
+ /** Class. */
+ private final Class<T> tClass;
/**
* Constructor.
*
- * @param ctor Class constructor for factory.
+ * @param tClass Class.
*/
- private ObjectFactory(Constructor<T> ctor) {
- this.ctor = ctor;
-
- ctor.setAccessible(true);
+ ObjectFactory(Class<T> tClass) {
+ this.tClass = tClass;
}
- /**
- * Creates new class instance using default constructor.
- *
- * @return New instance of class.
- * @throws IllegalStateException If failed.
- */
- public T newInstance() throws IllegalStateException {
+ /** {@inheritDoc} */
+ @Override public T create() throws IllegalStateException {
try {
- return ctor.newInstance();
+ return (T)IgniteUnsafeUtils.allocateInstance(tClass);
}
- catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
- throw new IllegalStateException("Failed to instantiate class: " + ctor.getDeclaringClass(), e);
+ catch (InstantiationException e) {
+ throw new IllegalStateException("Failed to instantiate class: " + tClass.getSimpleName(), e);
}
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
index 3bce3df..db07a16 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
@@ -29,6 +29,7 @@ import sun.misc.Unsafe;
public class IgniteUnsafeUtils {
/** Unsafe. */
private static final Unsafe UNSAFE = unsafe();
+
/**
* @return Instance of Unsafe class.
*/
@@ -54,4 +55,222 @@ public class IgniteUnsafeUtils {
}
}
}
+
+ /**
+ * Returns object field offset.
+ *
+ * @param field Field.
+ * @return Object field offset.
+ */
+ public static long objectFieldOffset(Field field) {
+ return UNSAFE.objectFieldOffset(field);
+ }
+
+ /**
+ * Gets boolean value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Boolean value from object field.
+ */
+ public static boolean getBooleanField(Object obj, long fieldOff) {
+ return UNSAFE.getBoolean(obj, fieldOff);
+ }
+
+ /**
+ * Stores boolean value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putBooleanField(Object obj, long fieldOff, boolean val) {
+ UNSAFE.putBoolean(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets byte value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Byte value from object field.
+ */
+ public static byte getByteField(Object obj, long fieldOff) {
+ return UNSAFE.getByte(obj, fieldOff);
+ }
+
+ /**
+ * Stores byte value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putByteField(Object obj, long fieldOff, byte val) {
+ UNSAFE.putByte(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets short value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Short value from object field.
+ */
+ public static short getShortField(Object obj, long fieldOff) {
+ return UNSAFE.getShort(obj, fieldOff);
+ }
+
+ /**
+ * Stores short value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putShortField(Object obj, long fieldOff, short val) {
+ UNSAFE.putShort(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets char value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Char value from object field.
+ */
+ public static char getCharField(Object obj, long fieldOff) {
+ return UNSAFE.getChar(obj, fieldOff);
+ }
+
+ /**
+ * Stores char value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putCharField(Object obj, long fieldOff, char val) {
+ UNSAFE.putChar(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets integer value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Integer value from object field.
+ */
+ public static int getIntField(Object obj, long fieldOff) {
+ return UNSAFE.getInt(obj, fieldOff);
+ }
+
+ /**
+ * Stores integer value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putIntField(Object obj, long fieldOff, int val) {
+ UNSAFE.putInt(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets long value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Long value from object field.
+ */
+ public static long getLongField(Object obj, long fieldOff) {
+ return UNSAFE.getLong(obj, fieldOff);
+ }
+
+ /**
+ * Stores long value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putLongField(Object obj, long fieldOff, long val) {
+ UNSAFE.putLong(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets float value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Float value from object field.
+ */
+ public static float getFloatField(Object obj, long fieldOff) {
+ return UNSAFE.getFloat(obj, fieldOff);
+ }
+
+ /**
+ * Stores float value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putFloatField(Object obj, long fieldOff, float val) {
+ UNSAFE.putFloat(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets double value from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Double value from object field.
+ */
+ public static double getDoubleField(Object obj, long fieldOff) {
+ return UNSAFE.getDouble(obj, fieldOff);
+ }
+
+ /**
+ * Stores double value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putDoubleField(Object obj, long fieldOff, double val) {
+ UNSAFE.putDouble(obj, fieldOff, val);
+ }
+
+ /**
+ * Gets reference from object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @return Reference from object field.
+ */
+ public static Object getObjectField(Object obj, long fieldOff) {
+ return UNSAFE.getObject(obj, fieldOff);
+ }
+
+ /**
+ * Stores reference value into object field.
+ *
+ * @param obj Object.
+ * @param fieldOff Field offset.
+ * @param val Value.
+ */
+ public static void putObjectField(Object obj, long fieldOff, Object val) {
+ UNSAFE.putObject(obj, fieldOff, val);
+ }
+
+ /**
+ * Allocates instance of given class.
+ *
+ * @param cls Class.
+ * @return Allocated instance.
+ */
+ public static Object allocateInstance(Class cls) throws InstantiationException {
+ return UNSAFE.allocateInstance(cls);
+ }
}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
index 372c27e..3cfb0da 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
@@ -215,9 +215,8 @@ public class JavaSerializerTest {
/**
*
*/
- @SuppressWarnings("ResultOfObjectAllocationIgnored")
@Test
- public void testClassWithNoDefaultConstructor() {
+ public void testClassWithNoDefaultConstructor() throws SerializationException {
Column[] cols = new Column[] {
new Column("pLongCol", LONG, false),
};
@@ -227,10 +226,18 @@ public class JavaSerializerTest {
final Object key = WrongTestObject.randomObject(rnd);
final Object val = WrongTestObject.randomObject(rnd);
- assertThrows(IllegalStateException.class,
- () -> new JavaSerializer(schema, key.getClass(), val.getClass()),
- "No default constructor found for class: WrongTestObject"
- );
+ final JavaSerializer serializer = new JavaSerializer(schema, key.getClass(), val.getClass());
+
+ final byte[] bytes = serializer.serialize(key, val);
+
+ Object key1 = serializer.deserializeKey(bytes);
+ Object val1 = serializer.deserializeValue(bytes);
+
+ assertTrue(key.getClass().isInstance(key1));
+ assertTrue(val.getClass().isInstance(val1));
+
+ assertEquals(key, key);
+ assertEquals(val, val1);
}
/**