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/12/16 21:41:13 UTC
[ignite-3] branch ignite-13618 updated: Rewrite to VarHandles.
Improve serializer API.
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-3.git
The following commit(s) were added to refs/heads/ignite-13618 by this push:
new db6342b Rewrite to VarHandles. Improve serializer API.
db6342b is described below
commit db6342ba4b29a9fcc46d082bee1d8ee61db42ce6
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Wed Dec 16 23:34:08 2020 +0300
Rewrite to VarHandles.
Improve serializer API.
---
modules/commons/pom.xml | 12 +-
.../apache/ignite/internal/schema/NativeType.java | 8 +
.../schema/marshaller/AbstractSerializer.java | 108 ++++++
.../internal/schema/marshaller/BinaryMode.java | 20 +-
.../internal/schema/marshaller/CompilerUtils.java | 344 ++++++++++++++++++
.../internal/schema/marshaller/MarshallerUtil.java | 16 +-
.../schema/marshaller/SerializationException.java | 1 -
.../internal/schema/marshaller/Serializer.java | 8 +-
.../schema/marshaller/SerializerFactory.java | 9 +-
.../generator/FieldAccessExprGenerator.java | 360 -------------------
.../IdentityObjectMarshallerExprGenerator.java | 39 ++-
.../generator/JaninoSerializerGenerator.java | 346 -------------------
.../generator/MarshallerCodeGenerator.java | 47 +++
.../generator/MarshallerExprGenerator.java | 94 -----
.../generator/ObjectMarshallerCodeGenerator.java | 124 +++++++
.../marshaller/generator/SerializerGenerator.java | 361 +++++++++++++++++++
.../generator/TupleColumnAccessCodeGenerator.java | 257 ++++++++++++++
...UnsafeFieldAccessor.java => FieldAccessor.java} | 383 ++++++++++++++-------
.../marshaller/reflection/JavaSerializer.java | 186 +---------
.../schema/marshaller/reflection/Marshaller.java | 28 +-
.../ignite/internal/util/IgniteUnsafeUtils.java | 273 ---------------
.../apache/ignite/internal/util/ObjectFactory.java | 8 -
.../org/apache/ignite/lang/IgniteExperimental.java | 38 ++
.../benchmarks/SerializerBenchmarkTest.java | 51 +--
.../schema/marshaller/JavaSerializerTest.java | 96 +++++-
.../marshaller/reflection/FieldAccessorTest.java | 26 +-
pom.xml | 9 +-
27 files changed, 1771 insertions(+), 1481 deletions(-)
diff --git a/modules/commons/pom.xml b/modules/commons/pom.xml
index fc78231..a5b64e8 100644
--- a/modules/commons/pom.xml
+++ b/modules/commons/pom.xml
@@ -29,7 +29,7 @@
<artifactId>apache-ignite</artifactId>
<groupId>org.apache.ignite</groupId>
<version>3.0.0-SNAPSHOT</version>
- <relativePath>../..</relativePath>
+ <relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>ignite-commons</artifactId>
@@ -43,12 +43,11 @@
<version>${jetbrains.annotations.version}</version>
</dependency>
<dependency>
- <groupId>org.codehaus.janino</groupId>
- <artifactId>janino</artifactId>
- <version>${janino.version}</version>
+ <groupId>com.squareup</groupId>
+ <artifactId>javapoet</artifactId>
+ <version>${javapoet.version}</version>
</dependency>
-
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
@@ -56,11 +55,10 @@
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
-
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>${mockito.version}</version>
+ <version>${mockito.core.version}</version>
<scope>test</scope>
</dependency>
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/NativeType.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/NativeType.java
index 211a1e1..e5d40c7 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/NativeType.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/NativeType.java
@@ -133,4 +133,12 @@ public class NativeType implements Comparable<NativeType> {
return typeSpec.name().compareTo(o.typeSpec.name());
}
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return "NativeType{" +
+ "typeSpec=" + typeSpec +
+ ", len=" + len +
+ '}';
+ }
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/AbstractSerializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/AbstractSerializer.java
new file mode 100644
index 0000000..5ac127c
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/AbstractSerializer.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.util.Objects;
+import org.apache.ignite.internal.schema.ByteBufferTuple;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.util.Pair;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Base serializer class.
+ */
+public abstract class AbstractSerializer implements Serializer {
+ /** Schema descriptor. */
+ protected final SchemaDescriptor schema;
+
+ /**
+ * Constructor.
+ *
+ * @param schema Schema descriptor.
+ */
+ protected AbstractSerializer(SchemaDescriptor schema) {
+ this.schema = schema;
+ }
+
+ /** {@inheritDoc} */
+ @Override public byte[] serialize(Object key, Object val) throws SerializationException {
+ final TupleAssembler assembler = createAssembler(Objects.requireNonNull(key), val);
+
+ return serialize0(assembler, key, val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K> K deserializeKey(byte[] data) throws SerializationException {
+ final Tuple tuple = new ByteBufferTuple(schema, data);
+
+ return (K)deserializeKey0(tuple);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <V> V deserializeValue(byte[] data) throws SerializationException {
+ final Tuple tuple = new ByteBufferTuple(schema, data);
+
+ return (V)deserializeValue0(tuple);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> Pair<K, V> deserialize(byte[] data) throws SerializationException {
+ final Tuple tuple = new ByteBufferTuple(schema, data);
+
+ return new Pair<>((K)deserializeKey0(tuple), (V)deserializeValue0(tuple));
+ }
+
+ /**
+ * Tuple assembler factory method.
+ *
+ * @param key Key object.
+ * @param val Value object.
+ */
+ protected abstract TupleAssembler createAssembler(Object key, @Nullable Object val);
+
+ /**
+ * Internal serialization method.
+ *
+ * @param asm Tuple assembler.
+ * @param key Key object.
+ * @param val Value object.
+ * @return Serialized pair.
+ * @throws SerializationException If failed.
+ */
+ protected abstract byte[] serialize0(TupleAssembler asm, Object key, Object val) throws SerializationException;
+
+ /**
+ * Extract key object from tuple.
+ *
+ * @param tuple Tuple.
+ * @return Deserialized key object.
+ * @throws SerializationException If failed.
+ */
+ protected abstract Object deserializeKey0(Tuple tuple) throws SerializationException;
+
+ /**
+ * Extract value object from tuple.
+ *
+ * @param tuple Tuple.
+ * @return Deserialized value object.
+ * @throws SerializationException If failed.
+ */
+ protected abstract Object deserializeValue0(Tuple tuple) throws SerializationException;
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
index db5a77f..6a0c6ab 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
@@ -41,34 +41,34 @@ public enum BinaryMode {
/** Primitive int. */
P_DOUBLE(NativeTypeSpec.DOUBLE),
- /** */
+ /** Boxed byte. */
BYTE(NativeTypeSpec.BYTE),
- /** */
+ /** Boxed short. */
SHORT(NativeTypeSpec.SHORT),
- /** */
+ /** Boxed int. */
INT(NativeTypeSpec.INTEGER),
- /** */
+ /** Boxed long. */
LONG(NativeTypeSpec.LONG),
- /** */
+ /** Boxed float. */
FLOAT(NativeTypeSpec.FLOAT),
- /** */
+ /** Boxed double. */
DOUBLE(NativeTypeSpec.DOUBLE),
- /** */
+ /** String. */
STRING(NativeTypeSpec.STRING),
- /** */
+ /** Uuid. */
UUID(NativeTypeSpec.UUID),
- /** */
+ /** Raw byte array. */
BYTE_ARR(NativeTypeSpec.BYTES),
- /** */
+ /** BitSet.*/
BITSET(NativeTypeSpec.BITMASK);
/** Natove type spec. */
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/CompilerUtils.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/CompilerUtils.java
new file mode 100644
index 0000000..0edaf68
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/CompilerUtils.java
@@ -0,0 +1,344 @@
+/*
+ * 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 com.squareup.javapoet.JavaFile;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Compiler utility class.
+ */
+public final class CompilerUtils {
+ /**
+ * @return Creates classloader for dynamically loaded in-memory classes.
+ */
+ public static ClassLoader dynamicClassLoader() {
+ return AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override public ClassLoader run() {
+ return new MemoryClassLoader(new ConcurrentHashMap<>(), ClassLoader.getSystemClassLoader());
+ }
+ });
+ }
+
+ /**
+ * Compiles code and load compiled classes.
+ *
+ * @param javafile Java file representation.
+ * @return Classloader with compiled classes.
+ */
+ public static ClassLoader compileCode(JavaFile javafile) {
+ final JavaCompiler cmp = ToolProvider.getSystemJavaCompiler();
+
+ final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+ ConcurrentHashMap<String, byte[]> classes = classLoader instanceof MemoryClassLoader ?
+ ((MemoryClassLoader)classLoader).classBytes : new ConcurrentHashMap<>();
+
+ try (final MemoryJavaFileManager fileManager = new MemoryJavaFileManager(cmp.getStandardFileManager(null, null, null), classes)) {
+ DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
+
+ JavaCompiler.CompilationTask task = cmp.getTask(null, fileManager, diagnostics, null, null, Collections.singletonList(javafile.toJavaFileObject()));
+
+ if (task.call())
+ return classLoader instanceof MemoryClassLoader ? classLoader :
+ new MemoryClassLoader(classes, ClassLoader.getSystemClassLoader());
+
+ // TODO: write to log.
+ for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
+ System.out.println(diagnostic.getCode());
+ System.out.println(diagnostic.getKind());
+ System.out.println(diagnostic.getPosition());
+ System.out.println(diagnostic.getStartPosition());
+ System.out.println(diagnostic.getEndPosition());
+ System.out.println(diagnostic.getSource());
+ System.out.println(diagnostic.getMessage(null));
+ }
+
+ throw new IllegalStateException("Failed to compile code:\n" + javafile.toString());
+ }
+ catch (IOException ex) {
+ throw new IllegalStateException("Failed to compile code.", ex);
+ }
+ }
+
+ /**
+ * @param iterables Iterables.
+ * @return Concated iterable.
+ */
+ private static <E> Iterable<E> concat(Iterable<? extends E>... iterables) {
+ return new Iterable<>() {
+ private final Iterator<Iterable<? extends E>> it = Arrays.asList(iterables).iterator();
+
+ /** {@inheritDoc} */
+ @Override public Iterator<E> iterator() {
+ return new Iterator<>() {
+ private Iterator<? extends E> curIt = Collections.emptyIterator();
+
+ /** {@inheritDoc} */
+ @Override public boolean hasNext() {
+ if (curIt == null || !curIt.hasNext())
+ advance();
+
+ return curIt.hasNext();
+ }
+
+ /** Switches to next iterable. */
+ private void advance() {
+ while (it.hasNext() && !curIt.hasNext())
+ curIt = it.next().iterator();
+ }
+
+ /** {@inheritDoc} */
+ @Override public E next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ return curIt.next();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * In-memory java file manager.
+ */
+ private static class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
+ /** Classes code. */
+ private final Map<String, byte[]> classesBytes;
+
+ /**
+ * Constructor.
+ *
+ * @param fileManager Java file manager.
+ * @param classesBytes Classes code.
+ */
+ public MemoryJavaFileManager(JavaFileManager fileManager, Map<String, byte[]> classesBytes) {
+ super(fileManager);
+ this.classesBytes = classesBytes;
+ }
+
+ /**
+ * Java '.class' in-memory file.
+ */
+ private static class MemoryJavaFileObject extends SimpleJavaFileObject {
+ /** Class code. */
+ private final byte[] classBytes;
+
+ /**
+ * Constructor.
+ *
+ * @param className Class name.
+ * @param classBytes Class code.
+ */
+ MemoryJavaFileObject(String className, byte[] classBytes) {
+ super(URI.create(className + Kind.CLASS.extension), Kind.CLASS);
+ this.classBytes = classBytes;
+ }
+
+ /** {@inheritDoc} */
+ @Override public InputStream openInputStream() {
+ return new ByteArrayInputStream(classBytes);
+ }
+ }
+
+ /**
+ * A file object that stores Java bytecode into the classBytes map.
+ */
+ private class JavaClassOutputFile extends SimpleJavaFileObject {
+ /** Class name. */
+ private final String classname;
+
+ /**
+ * Constructor.
+ *
+ * @param classname Class name.
+ */
+ JavaClassOutputFile(String classname) {
+ super(URI.create(classname), Kind.CLASS);
+ this.classname = classname;
+ }
+
+ /** {@inheritDoc} */
+ @Override public OutputStream openOutputStream() {
+ return new ByteArrayOutputStream() {
+ @Override public void close() throws IOException {
+ super.close();
+
+ classesBytes.put(classname, toByteArray());
+ }
+ };
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Iterable<JavaFileObject> list(Location location, String packageName,
+ Set<JavaFileObject.Kind> kinds,
+ boolean recurse) throws IOException {
+ final Iterable<JavaFileObject> it = super.list(location, packageName, kinds, recurse);
+
+ if (location == StandardLocation.CLASS_PATH) {
+ assert kinds.contains(JavaFileObject.Kind.CLASS);
+
+ Iterable<JavaFileObject> localClasses = new Iterable<>() {
+ @Override public Iterator<JavaFileObject> iterator() {
+ return classesBytes.keySet().stream()
+ .filter(cn -> cn.startsWith(packageName))
+ .map(cn -> getJavaFileObjectByName(cn))
+ .filter(Objects::nonNull).iterator();
+ }
+ };
+
+ return concat(localClasses, it);
+ }
+ else
+ return it;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String inferBinaryName(Location location, JavaFileObject jfo) {
+ if (!(jfo instanceof MemoryJavaFileObject)) {
+ String result = super.inferBinaryName(location, jfo);
+ assert result != null;
+ return result;
+ }
+
+ // A [Java]FileObject's "name" looks like this: "/orc/codehaus/commons/compiler/Foo.java".
+ // A [Java]FileObject's "binary name" looks like "java.lang.annotation.Retention".
+
+ String bn = jfo.getName();
+ if (bn.startsWith("/"))
+ bn = bn.substring(1);
+
+ if (!bn.endsWith(jfo.getKind().extension)) {
+ throw new AssertionError(
+ "Name \"" + jfo.getName() + "\" does not match kind \"" + jfo.getKind() + "\""
+ );
+ }
+ bn = bn.substring(0, bn.length() - jfo.getKind().extension.length());
+
+ bn = bn.replace('/', '.');
+
+ return bn;
+ }
+
+ /** {@inheritDoc} */
+ @Override public JavaFileObject getJavaFileForInput(Location location, String className,
+ JavaFileObject.Kind kind) throws IOException {
+ if (location == StandardLocation.CLASS_OUTPUT) {
+ JavaFileObject javaFileObject = getJavaFileObjectByName(className);
+
+ if (javaFileObject != null)
+ return javaFileObject;
+ }
+
+ return fileManager.getJavaFileForInput(location, className, kind);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable private JavaFileObject getJavaFileObjectByName(String className) {
+ final byte[] bytes = classesBytes.get(className);
+
+ if (bytes != null)
+ return new MemoryJavaFileObject(className, bytes);
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public JavaFileObject getJavaFileForOutput(Location location,
+ String className,
+ JavaFileObject.Kind kind,
+ FileObject sibling) throws IOException {
+ if (kind == JavaFileObject.Kind.CLASS)
+ return new JavaClassOutputFile(className);
+ else
+ return super.getJavaFileForOutput(location, className, kind, sibling);
+ }
+ }
+
+ /**
+ * Classloader for runtime compiled classes.
+ */
+ private static final class MemoryClassLoader extends URLClassLoader {
+ /** Empty array. */
+ private static final URL[] EMPTY_URLS = new URL[0];
+
+ /** Classes code. */
+ private final ConcurrentHashMap<String, byte[]> classBytes;
+
+ /**
+ * Constructor.
+ *
+ * @param classBytes Classes code holder.
+ * @param parent Parent classloader.
+ */
+ MemoryClassLoader(ConcurrentHashMap<String, byte[]> classBytes, ClassLoader parent) {
+ super(EMPTY_URLS, parent);
+
+ this.classBytes = classBytes;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Class<?> findClass(String className) throws ClassNotFoundException {
+ byte[] buf = classBytes.get(className); // clear the bytes in map -- we don't need it anymore
+
+ if (buf != null)
+ return defineClass(className, buf, 0, buf.length);
+ else
+ return super.findClass(className);
+ }
+ }
+
+ /** Stub. */
+ private CompilerUtils() {
+ }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
index c111ba8..9361fa0 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
@@ -21,6 +21,7 @@ import java.util.BitSet;
import java.util.UUID;
import org.apache.ignite.internal.schema.NativeType;
import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.util.ObjectFactory;
/**
* Marshaller utility class.
@@ -97,7 +98,18 @@ public final class MarshallerUtil {
}
/**
- * Stub.
+ * Creates object factory for class.
+ * @param tClass Target type.
+ * @return Object factory.
*/
- private MarshallerUtil() {}
+ public static <T> ObjectFactory<T> factoryForClass(Class<T> tClass) {
+ if (mode(tClass) == null)
+ return new ObjectFactory<>(tClass);
+ else
+ return null;
+ }
+
+ /** Stub. */
+ private MarshallerUtil() {
+ }
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
index e690710..f87f91a 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
@@ -27,7 +27,6 @@ public class SerializationException extends Exception {
* @param cause Cause.
*/
public SerializationException(Throwable cause) {
- // Used by serializers generated with Janino.
super(cause);
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
index 21a1560..f809036 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema.marshaller;
+import org.apache.ignite.internal.util.Pair;
+
/**
* Key-value objects (de)serializer.
*/
@@ -33,10 +35,12 @@ public interface Serializer {
/**
* @return Key object.
*/
- Object deserializeKey(byte[] data) throws SerializationException;
+ <K> K deserializeKey(byte[] data) throws SerializationException;
/**
* @return Value object.
*/
- Object deserializeValue(byte[] data) throws SerializationException;
+ <V> V deserializeValue(byte[] data) throws SerializationException;
+
+ <K, V> Pair<K,V> deserialize(byte[] data) throws SerializationException;
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
index d9a8295..cf006c4 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
@@ -18,8 +18,10 @@
package org.apache.ignite.internal.schema.marshaller;
import org.apache.ignite.internal.schema.SchemaDescriptor;
-import org.apache.ignite.internal.schema.marshaller.generator.JaninoSerializerGenerator;
+import org.apache.ignite.internal.schema.marshaller.generator.SerializerGenerator;
import org.apache.ignite.internal.schema.marshaller.reflection.JavaSerializerFactory;
+import org.apache.ignite.lang.IgniteExperimental;
+import org.jetbrains.annotations.ApiStatus;
/**
* (De)Serializer factory interface.
@@ -29,13 +31,14 @@ public interface SerializerFactory {
/**
* @return Serializer factory back by code generator.
*/
- public static SerializerFactory createJaninoSerializerFactory() {
- return new JaninoSerializerGenerator();
+ public static SerializerFactory createGeneratedSerializerFactory() {
+ return new SerializerGenerator();
}
/**
* @return Reflection-based serializer factory.
*/
+ @IgniteExperimental
public static SerializerFactory createJavaSerializerFactory() {
return new JavaSerializerFactory();
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/FieldAccessExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/FieldAccessExprGenerator.java
deleted file mode 100644
index fb3736a..0000000
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/FieldAccessExprGenerator.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * 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.generator;
-
-import org.apache.ignite.internal.schema.marshaller.BinaryMode;
-import org.jetbrains.annotations.Nullable;
-
-import static org.apache.ignite.internal.schema.marshaller.generator.JaninoSerializerGenerator.LF;
-
-/**
- * Object field access expression generators.
- */
-class FieldAccessExprGenerator {
- /** Append null expression. */
- private static final String WRITE_NULL_EXPR = "asm.appendNull();";
-
- /**
- * Created object access expressions generator.
- *
- * @param mode Field access binary mode.
- * @param colIdx Column absolute index in schema.
- * @return Object field access expressions generator.
- */
- static FieldAccessExprGenerator createIdentityAccessor(BinaryMode mode, int colIdx) {
- return createAccessor(mode, colIdx, -1L);
- }
-
- /**
- * Created object field access expressions generator.
- *
- * @param mode Field access binary mode.
- * @param colIdx Column absolute index in schema.
- * @param offset Object field offset.
- * @return Object field access expressions generator.
- */
- static FieldAccessExprGenerator createAccessor(BinaryMode mode, int colIdx, long offset) {
- switch (mode) {
- case BYTE:
- return new FieldAccessExprGenerator(
- colIdx,
- "Byte",
- "tuple.byteValueBoxed",
- "asm.appendByte",
- offset);
-
- case P_BYTE:
- return new FieldAccessExprGenerator(
- colIdx,
- "tuple.byteValue",
- "asm.appendByte",
- offset,
- "IgniteUnsafeUtils.getByteField",
- "IgniteUnsafeUtils.putByteField"
- );
-
- case SHORT:
- return new FieldAccessExprGenerator(
- colIdx,
- "Short",
- "tuple.shortValueBoxed",
- "asm.appendShort",
- offset);
-
- case P_SHORT:
- return new FieldAccessExprGenerator(
- colIdx,
- "tuple.shortValue",
- "asm.appendShort",
- offset,
- "IgniteUnsafeUtils.getShortField",
- "IgniteUnsafeUtils.putShortField"
- );
-
- case INT:
- return new FieldAccessExprGenerator(
- colIdx,
- "Integer",
- "tuple.intValueBoxed",
- "asm.appendInt",
- offset);
-
- case P_INT:
- return new FieldAccessExprGenerator(
- colIdx,
- "tuple.intValue",
- "asm.appendInt",
- offset,
- "IgniteUnsafeUtils.getIntField",
- "IgniteUnsafeUtils.putIntField"
- );
-
- case LONG:
- return new FieldAccessExprGenerator(
- colIdx,
- "Long",
- "tuple.longValueBoxed",
- "asm.appendLong",
- offset);
-
- case P_LONG:
- return new FieldAccessExprGenerator(
- colIdx,
- "tuple.longValue",
- "asm.appendLong",
- offset,
- "IgniteUnsafeUtils.getLongField",
- "IgniteUnsafeUtils.putLongField"
- );
-
- case FLOAT:
- return new FieldAccessExprGenerator(
- colIdx,
- "Float",
- "tuple.floatValueBoxed",
- "asm.appendFloat",
- offset);
-
- case P_FLOAT:
- return new FieldAccessExprGenerator(
- colIdx,
- "tuple.floatValue",
- "asm.appendFloat",
- offset,
- "IgniteUnsafeUtils.getFloatField",
- "IgniteUnsafeUtils.putFloatField"
- );
-
- case DOUBLE:
- return new FieldAccessExprGenerator(
- colIdx,
- "Double",
- "tuple.doubleValueBoxed",
- "asm.appendDouble",
- offset);
-
- case P_DOUBLE:
- return new FieldAccessExprGenerator(
- colIdx,
- "tuple.doubleValue",
- "asm.appendDouble",
- offset,
- "IgniteUnsafeUtils.getDoubleField",
- "IgniteUnsafeUtils.putDoubleField"
- );
-
- case UUID:
- return new FieldAccessExprGenerator(
- colIdx,
- "UUID",
- "tuple.uuidValue", "asm.appendUuid",
- offset);
-
- case BITSET:
- return new FieldAccessExprGenerator(
- colIdx,
- "BitSet",
- "tuple.bitmaskValue", "asm.appendBitmask",
- offset);
-
- case STRING:
- return new FieldAccessExprGenerator(
- colIdx,
- "String",
- "tuple.stringValue", "asm.appendString",
- offset);
-
- case BYTE_ARR:
- return new FieldAccessExprGenerator(
- colIdx,
- "byte[]",
- "tuple.bytesValue", "asm.appendBytes",
- offset);
- default:
- throw new IllegalStateException("Unsupportd binary mode");
- }
- }
-
- /** Object field offset or {@code -1} for identity accessor. */
- private final long offset;
-
- /** Absolute schema index. */
- private final int colIdx;
-
- /** Class cast expression. */
- private final String classExpr;
-
- /** Write column value expression. */
- private final String writeColMethod;
-
- /** Read column value expression. */
- private final String readColMethod;
-
- /** Read object field expression. */
- private final String getFieldMethod;
-
- /** Write object field expression. */
- private final String putFieldMethod;
-
- /**
- * Constructor.
- *
- * @param colIdx Absolute schema index in schema.
- * @param castClassExpr Class cast expression
- * @param readColMethod Read column value expression.
- * @param writeColMethod Write column value expression.
- * @param offset Field offset or {@code -1} for identity accessor.
- */
- private FieldAccessExprGenerator(
- int colIdx,
- String castClassExpr,
- String readColMethod,
- String writeColMethod,
- long offset
- ) {
- this(colIdx, castClassExpr, readColMethod, writeColMethod, offset,
- "IgniteUnsafeUtils.getObjectField", "IgniteUnsafeUtils.putObjectField");
- }
-
- /**
- * Constructor.
- *
- * @param colIdx Absolute schema index in schema.
- * @param readColMethod Read column value expression.
- * @param writeColMethod Write column value expression.
- * @param offset Field offset or {@code -1} for identity accessor.
- * @param getFieldMethod Read object field expression.
- * @param putFieldMethod Read object field expression.
- */
- public FieldAccessExprGenerator(
- int colIdx,
- String readColMethod,
- String writeColMethod,
- long offset,
- String getFieldMethod,
- String putFieldMethod
- ) {
- this(colIdx, null /* primitive type */, readColMethod, writeColMethod, offset, getFieldMethod, putFieldMethod);
- }
-
- /**
- * Constructor.
- *
- * @param colIdx Absolute schema index in schema.
- * @param castClassExpr Class cast expression or {@code null} if not applicable.
- * @param readColMethod Read column value expression.
- * @param writeColMethod Write column value expression.
- * @param offset Field offset or {@code -1} for identity accessor.
- * @param getFieldMethod Read object field expression.
- * @param putFieldMethod Read object field expression.
- */
- private FieldAccessExprGenerator(
- int colIdx,
- @Nullable String castClassExpr,
- String readColMethod,
- String writeColMethod,
- long offset,
- String getFieldMethod,
- String putFieldMethod
- ) {
- this.offset = offset;
- this.colIdx = colIdx;
- this.classExpr = castClassExpr;
- this.putFieldMethod = putFieldMethod;
- this.getFieldMethod = getFieldMethod;
- this.writeColMethod = writeColMethod;
- this.readColMethod = readColMethod;
- }
-
- /**
- * @return {@code true} if it is primitive typed field accessor, {@code false} otherwise.
- */
- private boolean isPrimitive() {
- return classExpr == null;
- }
-
- /**
- * @return {@code true} if is identity accessor, {@code false} otherwise.
- */
- private boolean isIdentityAccessor() {
- return offset == -1;
- }
-
- /**
- * @return Object field value access expression or object value expression for simple types.
- */
- public String getFieldExpr() {
- if (isIdentityAccessor())
- return "obj"; // Identity accessor.
-
- return getFieldMethod + "(obj, " + offset + ')';
- }
-
- /**
- * Appends write value to field expression.
- *
- * @param sb String bulder.
- * @param valueExpression Value expression.
- * @param indent Line indentation.
- */
- public final void appendPutFieldExpr(StringBuilder sb, String valueExpression, String indent) {
- sb.append(indent).append(putFieldMethod).append("(obj, ").append(offset).append(", ").append(valueExpression).append(')');
- sb.append(";" + LF);
- }
-
- /**
- * Appends write value to column expression.
- *
- * @param sb String bulder.
- * @param valueExpr Value expression.
- * @param indent Line indentation.
- */
- public final void appendWriteColumnExpr(StringBuilder sb, String valueExpr, String indent) {
- if (isPrimitive() || isIdentityAccessor()) {
- // Translate to:
- // asm.appendX((T) %value%);
- // or for primitive value:
- // asm.appendX(%value%);
- sb.append(indent).append(writeColMethod).append('(');
-
- if (classExpr != null)
- sb.append("(").append(classExpr).append(")");
-
- sb.append(valueExpr).append(");" + LF);
-
- return;
- }
-
- assert classExpr != null;
-
- // Translate to:
- // { T fVal = (T)%value%;
- // if (fVal == null) asm.appendNull() else asm.appendX(fVal); }
- sb.append(indent).append("{ ").append(classExpr).append(" fVal = (").append(classExpr).append(')').append(valueExpr).append(";" + LF);
- sb.append(indent).append("if (fVal == null) " + WRITE_NULL_EXPR + LF);
- sb.append(indent).append("else ").append(writeColMethod).append("(fVal); }" + LF);
-
- }
-
- /**
- * @return Column value read expression.
- */
- public String readColumnExpr() {
- return readColMethod + "(" + colIdx + ")";
- }
-}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
index e574e61..5778a5a 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
@@ -17,29 +17,50 @@
package org.apache.ignite.internal.schema.marshaller.generator;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeSpec;
import org.apache.ignite.internal.schema.marshaller.Serializer;
/**
* Generate {@link Serializer} method's bodies for simple types.
*/
-class IdentityObjectMarshallerExprGenerator extends MarshallerExprGenerator {
+class IdentityObjectMarshallerExprGenerator implements MarshallerCodeGenerator {
+ /** Tuple column accessor. */
+ private final TupleColumnAccessCodeGenerator columnAccessor;
+
/**
* Constructor.
*
- * @param accessor Object field access expression generators.
+ * @param columnAccessor Tuple column code generator.
*/
- IdentityObjectMarshallerExprGenerator(FieldAccessExprGenerator accessor) {
- super(null /* no instantiation needed */, new FieldAccessExprGenerator[] {accessor});
+ IdentityObjectMarshallerExprGenerator(TupleColumnAccessCodeGenerator columnAccessor) {
+ this.columnAccessor = columnAccessor;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isSimpleType() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public CodeBlock unmarshallObjectCode(String tupleExpr) {
+ return CodeBlock.builder()
+ .addStatement("return $L", columnAccessor.read(tupleExpr))
+ .build();
+ }
+
+ /** {@inheritDoc} */
+ @Override public CodeBlock marshallObjectCode(String asm, String objVar) {
+ return columnAccessor.write(asm, objVar);
}
/** {@inheritDoc} */
- @Override public void appendMarshallObjectExpr(StringBuilder sb, String indent) {
- for (int i = 0; i < accessors.length; i++)
- accessors[i].appendWriteColumnExpr(sb, "obj", indent);
+ @Override public CodeBlock getValueCode(String objVar, int colIdx) {
+ return CodeBlock.of(objVar);
}
/** {@inheritDoc} */
- @Override public void appendUnmarshallObjectExpr(StringBuilder sb, String indent) {
- sb.append(indent).append("Object obj = ").append(accessors[0].readColumnExpr()).append(";" + JaninoSerializerGenerator.LF);
+ @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+ throw new UnsupportedOperationException("Static handlers are not applicable to simple types.");
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/JaninoSerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/JaninoSerializerGenerator.java
deleted file mode 100644
index 654bf97..0000000
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/JaninoSerializerGenerator.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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.generator;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import org.apache.ignite.internal.schema.Columns;
-import org.apache.ignite.internal.schema.SchemaDescriptor;
-import org.apache.ignite.internal.schema.marshaller.BinaryMode;
-import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
-import org.apache.ignite.internal.schema.marshaller.Serializer;
-import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
-import org.apache.ignite.internal.util.IgniteUnsafeUtils;
-import org.codehaus.commons.compiler.CompilerFactoryFactory;
-import org.codehaus.commons.compiler.IClassBodyEvaluator;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * {@link Serializer} code generator backed with Janino.
- */
-public class JaninoSerializerGenerator implements SerializerFactory {
- /** Tabulate. */
- static final String TAB = " ";
-
- /** Line feed. */
- static final char LF = '\n';
-
- /** String buffer initial size. */
- public static final int INITIAL_BUFFER_SIZE = 8 * 1024;
-
- /** Debug flag. */
- private static final boolean enabledDebug = true;
-
- /** {@inheritDoc} */
- @Override public Serializer create(
- SchemaDescriptor schema,
- Class<?> keyClass,
- Class<?> valClass
- ) {
- try {
- final IClassBodyEvaluator ce = CompilerFactoryFactory.getDefaultCompilerFactory().newClassBodyEvaluator();
-
- // Generate Serializer code.
- String code = generateSerializerClassCode(ce, schema, keyClass, valClass);
-
- //TODO: pass code to logger on trace level.
-
- if (enabledDebug) {
- ce.setDebuggingInformation(true, true, true);
-
- //TODO: dump code to log.
-// System.out.println(code);
- }
-
- try { // Compile and load class.
- ce.setParentClassLoader(getClass().getClassLoader());
- ce.cook(code);
-
- // Create and return Serializer instance.
- final Constructor<Serializer> ctor = (Constructor<Serializer>)ce.getClazz()
- .getDeclaredConstructor(schema.getClass(), Class.class, Class.class);
-
- return ctor.newInstance(schema, keyClass, valClass);
- }
- catch (Exception ex) {
- if (enabledDebug)
- throw new IllegalStateException("Failed to compile/instantiate generated Serializer: code=" +
- LF + code + LF, ex);
- else
- throw new IllegalStateException("Failed to compile/instantiate generated Serializer.", ex);
- }
- }
- catch (Exception ex) {
- //TODO: fallback to java serializer?
- throw new IllegalStateException(ex);
- }
- }
-
- /**
- * Generates serializer code.
- *
- * @param ce Class body evaluator.
- * @param schema Schema descriptor.
- * @param keyClass Key class.
- * @param valClass Value class.
- * @return Generated class code.
- */
- private String generateSerializerClassCode(
- IClassBodyEvaluator ce,
- SchemaDescriptor schema,
- Class<?> keyClass,
- Class<?> valClass
- ) {
- final String packageName = "org.apache.ignite.internal.schema.marshaller.";
- final String className = "JaninoSerializerForSchema_" + schema.version();
-
- // Prerequisites.
- ce.setClassName(packageName + className);
- ce.setImplementedInterfaces(new Class[] {Serializer.class});
- ce.setDefaultImports(
- "java.util.UUID",
- "java.util.BitSet",
-
- "org.apache.ignite.internal.schema.ByteBufferTuple",
- "org.apache.ignite.internal.schema.Columns",
- "org.apache.ignite.internal.schema.SchemaDescriptor",
- "org.apache.ignite.internal.schema.Tuple",
- "org.apache.ignite.internal.schema.TupleAssembler",
- "org.apache.ignite.internal.util.IgniteUnsafeUtils",
- "org.apache.ignite.internal.util.ObjectFactory"
- );
-
- // Build field accessor generators.
- final MarshallerExprGenerator keyMarsh = createObjectMarshaller(keyClass, "keyFactory", schema.keyColumns(), 0);
- final MarshallerExprGenerator valMarsh = createObjectMarshaller(valClass, "valFactory", schema.valueColumns(), schema.keyColumns().length());
-
- // Create buffer.
- final StringBuilder sb = new StringBuilder(INITIAL_BUFFER_SIZE);
-
- // Append class fields desctiption.
- sb.append("private final SchemaDescriptor schema;" + LF);
-
- if (!keyMarsh.isSimpleTypeMarshaller())
- sb.append("private final ObjectFactory keyFactory;" + LF);
- if (!valMarsh.isSimpleTypeMarshaller())
- sb.append("private final ObjectFactory valFactory;" + LF);
-
- // Append constructor code.
- sb.append(LF + "public ").append(className).append("(SchemaDescriptor schema, Class kClass, Class vClass) {" + LF);
- sb.append(TAB + "this.schema = schema; " + LF);
- if (!keyMarsh.isSimpleTypeMarshaller())
- sb.append(TAB + "keyFactory = new ObjectFactory(kClass);" + LF);
- if (!valMarsh.isSimpleTypeMarshaller())
- sb.append(TAB + "valFactory = new ObjectFactory(vClass);" + LF);
- sb.append("}" + LF);
-
- // Generate and append helper-methods.
- generateTupleFactoryMethod(sb, schema, keyMarsh, valMarsh);
-
- // Generate and append Serializer interface methods.
- appendSerializeMethod(sb, keyMarsh, valMarsh);
- writeDeserializeKeyMethod(sb, keyMarsh);
- writeDeserializeValueMethod(sb, valMarsh);
-
- return sb.toString();
- }
-
- /**
- * Creates marshal/unmarshall expressions generator for object.
- *
- * @param aClass Object class.
- * @param factoryRefExpr Factory reference expression.
- * @param columns Columns that aClass mapped to.
- * @param firstColIdx First column absolute index in schema.
- * @return Marshal/unmarshall expression generator.
- */
- private MarshallerExprGenerator createObjectMarshaller(
- Class<?> aClass,
- @Nullable String factoryRefExpr,
- Columns columns,
- int firstColIdx
- ) {
- BinaryMode mode = MarshallerUtil.mode(aClass);
-
- if (mode != null)
- return new IdentityObjectMarshallerExprGenerator(FieldAccessExprGenerator.createIdentityAccessor(mode, firstColIdx));
-
- FieldAccessExprGenerator[] accessors = new FieldAccessExprGenerator[columns.length()];
- try {
- for (int i = 0; i < columns.length(); i++) {
- final Field field = aClass.getDeclaredField(columns.column(i).name());
-
- accessors[i] = FieldAccessExprGenerator.createAccessor(
- MarshallerUtil.mode(field.getType()),
- firstColIdx + i /* schma absolute index. */,
- IgniteUnsafeUtils.objectFieldOffset(field));
- }
- }
- catch (NoSuchFieldException ex) {
- throw new IllegalStateException(ex);
- }
-
- return new MarshallerExprGenerator(factoryRefExpr, accessors);
- }
-
- /**
- * Appends {@link Serializer#serialize(Object, Object)} method code.
- *
- * @param sb String buffer to append to.
- * @param keyMarsh Marshall expression generator for key.
- * @param valMarsh Marshall expression generator for value.
- */
- private void appendSerializeMethod(
- StringBuilder sb,
- MarshallerExprGenerator keyMarsh,
- MarshallerExprGenerator valMarsh
- ) {
- // Mehtod signature.
- sb.append(LF + "@Override public byte[] serialize(Object key, Object val) throws SerializationException {" + LF);
- sb.append(TAB + "TupleAssembler asm = createAssembler(key, val);" + LF);
-
- // Key marshal script.
- sb.append(TAB + "{" + LF);
- sb.append(TAB + TAB + "Object obj = key;" + LF);
- keyMarsh.appendMarshallObjectExpr(sb, TAB + TAB);
- sb.append(TAB + "}" + LF);
-
- // Value marshal script.
- sb.append(TAB + " {" + LF);
- sb.append(TAB + TAB + "Object obj = val;" + LF);
- valMarsh.appendMarshallObjectExpr(sb, TAB + TAB);
- sb.append(TAB + "}" + LF);
-
- // Return statement.
- sb.append(TAB + "return asm.build();" + LF);
- sb.append("}" + LF);
- }
-
- /**
- * Appends {@link Serializer#deserializeKey(byte[])} method code.
- *
- * @param sb String buffer to append to.
- * @param keyMarsh Unmarshall expression generator for key.
- */
- private void writeDeserializeKeyMethod(StringBuilder sb, MarshallerExprGenerator keyMarsh) {
- // Mehtod signature.
- sb.append(LF + "@Override public Object deserializeKey(byte[] data) throws SerializationException {" + LF);
- sb.append(TAB + "Tuple tuple = new ByteBufferTuple(schema, data);" + LF);
-
- // Key unmarshal script.
- keyMarsh.appendUnmarshallObjectExpr(sb, TAB);
-
- // Return statement.
- sb.append(TAB + "return obj;" + LF);
- sb.append("}" + LF);
- }
-
- /**
- * Appends {@link Serializer#deserializeValue(byte[])} method code.
- *
- * @param sb String buffer to append to.
- * @param valMarsh Unmarshall expression generator for value.
- */
- private void writeDeserializeValueMethod(StringBuilder sb, MarshallerExprGenerator valMarsh) {
- // Mehtod signature.
- sb.append(LF + "@Override public Object deserializeValue(byte[] data) throws SerializationException {" + LF);
- sb.append(TAB + "Tuple tuple = new ByteBufferTuple(schema, data);" + LF);
-
- // Key unmarshal script.
- valMarsh.appendUnmarshallObjectExpr(sb, TAB);
-
- // Return statement.
- sb.append(TAB + "return obj;" + LF);
- sb.append("}" + LF);
- }
-
- /**
- * Appends helper methods code.
- *
- * @param sb String buffer to append to.
- * @param schema Schema descriptor.
- * @param keyMarsh Marshall expression generator for key.
- * @param valMarsh Marshall expression generator for value.
- */
- private void generateTupleFactoryMethod(
- StringBuilder sb,
- SchemaDescriptor schema,
- MarshallerExprGenerator keyMarsh,
- MarshallerExprGenerator valMarsh
- ) {
- // Method signature.
- sb.append(LF + "TupleAssembler createAssembler(Object key, Object val) {" + LF);
- // Local variables.
- sb.append(TAB + "int nonNullVarlenKeys = 0; int nonNullVarlenValues = 0;" + LF);
- sb.append(TAB + "int nonNullVarlenKeysSize = 0; int nonNullVarlenValuesSize = 0;" + LF);
- sb.append(LF);
- sb.append(TAB + "Columns keyCols = schema.keyColumns();" + LF);
- sb.append(TAB + "Columns valCols = schema.valueColumns();" + LF);
- sb.append(LF);
-
- Columns keyCols = schema.keyColumns();
- if (keyCols.firstVarlengthColumn() >= 0) {
- // Appends key analyzer code-block.
- sb.append(TAB + "{" + LF);
- sb.append(TAB + TAB + "Object fVal, obj = key;" + LF); // Temporary vars.
-
- for (int i = keyCols.firstVarlengthColumn(); i < keyCols.length(); i++) {
- assert !keyCols.column(i).type().spec().fixedLength();
-
- sb.append(TAB + TAB + "assert !keyCols.column(").append(i).append(").type().spec().fixedLength();" + LF);
- sb.append(TAB + TAB + "fVal = ").append(keyMarsh.accessors[i].getFieldExpr()).append(";" + LF);
- sb.append(TAB + TAB + "if (fVal != null) {" + LF);
- sb.append(TAB + TAB + TAB + "nonNullVarlenKeysSize += MarshallerUtil.getValueSize(fVal, keyCols.column(").append(i).append(").type());").append(LF);
- sb.append(TAB + TAB + TAB + "nonNullVarlenKeys++;" + LF);
- sb.append(TAB + TAB + "}" + LF);
- }
-
- sb.append(TAB + "}" + LF);
- }
-
- Columns valCols = schema.valueColumns();
- if (valCols.firstVarlengthColumn() >= 0) {
- // Appends value analyzer code-block.
- sb.append(TAB + "{" + LF);
- sb.append(TAB + TAB + "Object fVal, obj = val;" + LF); // Temporary vars.
-
- for (int i = valCols.firstVarlengthColumn(); i < valCols.length(); i++) {
- assert !valCols.column(i).type().spec().fixedLength();
-
- sb.append(TAB + TAB + "assert !valCols.column(").append(i).append(").type().spec().fixedLength();" + LF);
- sb.append(TAB + TAB + "fVal = ").append(valMarsh.accessors[i].getFieldExpr()).append(";" + LF);
- sb.append(TAB + TAB + "if (fVal != null) {" + LF);
- sb.append(TAB + TAB + TAB + "nonNullVarlenValuesSize += MarshallerUtil.getValueSize(fVal, valCols.column(").append(i).append(").type());" + LF);
- sb.append(TAB + TAB + TAB + "nonNullVarlenValues++;" + LF);
- sb.append(TAB + TAB + "}" + LF);
- }
- sb.append(TAB + "}" + LF);
- }
-
- // Calculate tuple size.
- sb.append(LF);
- sb.append(TAB + "int size = TupleAssembler.tupleSize(" + LF);
- sb.append(TAB + TAB + "keyCols, nonNullVarlenKeys, nonNullVarlenKeysSize, " + LF);
- sb.append(TAB + TAB + "valCols, nonNullVarlenValues, nonNullVarlenValuesSize); " + LF);
- sb.append(LF);
-
- // Return statement.
- sb.append(TAB + "return new TupleAssembler(schema, size, nonNullVarlenKeys, nonNullVarlenValues);" + LF);
- sb.append("}" + LF);
- }
-}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerCodeGenerator.java
new file mode 100644
index 0000000..bca3256
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerCodeGenerator.java
@@ -0,0 +1,47 @@
+package org.apache.ignite.internal.schema.marshaller.generator;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeSpec;
+
+/**
+ * Marshaller code generator.
+ */
+interface MarshallerCodeGenerator {
+ /**
+ * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
+ */
+ boolean isSimpleType();
+
+ /**
+ * @param tupleExpr Tuple to read from.
+ * @return Unmarshall object code.
+ */
+ CodeBlock unmarshallObjectCode(String tupleExpr);
+
+ /**
+ * @param asm Tuple assembler to write to.
+ * @param objVar Object to serialize.
+ * @return Marshall object code.
+ */
+ CodeBlock marshallObjectCode(String asm, String objVar);
+
+ /**
+ * @param objVar Object var.
+ * @param colIdx Column index.
+ * @return Object field value for given column.
+ */
+ CodeBlock getValueCode(String objVar, int colIdx);
+
+ /**
+ * @param classBuilder Class builder.
+ * @param staticInitBuilder Static initializer builder.
+ */
+ void initStaticHandlers(TypeSpec.Builder classBuilder, CodeBlock.Builder staticInitBuilder);
+
+ /**
+ * @return Marshaller target class.
+ */
+ default Class<?> getClazz() {
+ return null;
+ }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java
deleted file mode 100644
index 433b567..0000000
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.generator;
-
-import org.apache.ignite.internal.schema.marshaller.Serializer;
-
-/**
- * Generate {@link Serializer} method's bodies.
- */
-class MarshallerExprGenerator {
- /** Object factory regerence expression. */
- private final String factoryRefExpr;
-
- /** Object field access expression generators. */
- protected FieldAccessExprGenerator[] accessors;
-
- /**
- * Constructor.
- *
- * @param factoryRefExpr Object factory regerence expression.
- * @param accessors Object field access expression generators.
- */
- public MarshallerExprGenerator(String factoryRefExpr, FieldAccessExprGenerator[] accessors) {
- this.accessors = accessors;
- this.factoryRefExpr = factoryRefExpr;
- }
-
- /**
- * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
- */
- boolean isSimpleTypeMarshaller() {
- return factoryRefExpr == null;
- }
-
- /**
- * Appends unmashall object code to string builder.
- *
- * @param sb String builder.
- * @param indent Line indentation.
- */
- public void appendUnmarshallObjectExpr(StringBuilder sb, String indent) {
- assert factoryRefExpr != null;
-
- sb.append(indent).append("Object obj;" + JaninoSerializerGenerator.LF);
- // Try.
- sb.append(indent).append("try {" + JaninoSerializerGenerator.LF);
- sb.append(indent).append(JaninoSerializerGenerator.TAB + "obj = ").append(factoryRefExpr).append(".create();" + JaninoSerializerGenerator.LF);
-
- // Read column from tuple to object field.
- for (int i = 0; i < accessors.length; i++)
- accessors[i].appendPutFieldExpr(sb, accessors[i].readColumnExpr(), indent + JaninoSerializerGenerator.TAB);
-
- // Catch and rethrow wrapped exeption.
- sb.append(indent).append("} catch (Exception ex) {" + JaninoSerializerGenerator.LF);
- sb.append(indent).append(JaninoSerializerGenerator.TAB + "throw new SerializationException(\"Failed to instantiate object: \" + ")
- .append(factoryRefExpr).append(".getClazz().getName(), ex);").append(JaninoSerializerGenerator.LF);
- sb.append(indent).append("}" + JaninoSerializerGenerator.LF);
- }
-
- /**
- * Appends mashall object code to string builder.
- *
- * @param sb String builder.
- * @param indent Line indentation.
- */
- public void appendMarshallObjectExpr(StringBuilder sb, String indent) {
- // Try.
- sb.append(indent).append("try {" + JaninoSerializerGenerator.LF);
-
- // Write object field to tuple assembler.
- for (int i = 0; i < accessors.length; i++)
- accessors[i].appendWriteColumnExpr(sb, accessors[i].getFieldExpr(), indent + JaninoSerializerGenerator.TAB);
-
- // Catch and rethrow wrapped exeption.
- sb.append(indent).append("} catch (Exception ex) {" + JaninoSerializerGenerator.LF);
- sb.append(indent).append(JaninoSerializerGenerator.TAB + "throw new SerializationException(ex);").append(JaninoSerializerGenerator.LF);
- sb.append(indent).append("}" + JaninoSerializerGenerator.LF);
- }
-}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/ObjectMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/ObjectMarshallerCodeGenerator.java
new file mode 100644
index 0000000..fc57a82
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/ObjectMarshallerCodeGenerator.java
@@ -0,0 +1,124 @@
+/*
+ * 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.generator;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeSpec;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Field;
+import javax.lang.model.element.Modifier;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+
+/**
+ * Generates {@link Serializer} methods code.
+ */
+class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
+ /** Target object factory var. */
+ private final String objectFactoryVar;
+
+ /** Target class. */
+ private final Class<?> tClass;
+
+ /** Mapped columns. */
+ private final Columns columns;
+
+ /** Object field access expression generators. */
+ private final TupleColumnAccessCodeGenerator[] columnAccessessors;
+
+ /**
+ * Constructor.
+ *
+ * @param tClass Target object class.
+ * @param objectFactoryVar Target object factory var.
+ * @param columns Column object is mapped to.
+ * @param firstColIdx Column index offset.
+ */
+ public ObjectMarshallerCodeGenerator(Class<?> tClass, String objectFactoryVar, Columns columns, int firstColIdx) {
+ this.objectFactoryVar = objectFactoryVar;
+ this.tClass = tClass;
+
+ this.columns = columns;
+ columnAccessessors = new TupleColumnAccessCodeGenerator[this.columns.length()];
+ try {
+ for (int i = 0; i < columns.length(); i++) {
+ final Field field = tClass.getDeclaredField(columns.column(i).name());
+
+ columnAccessessors[i] = TupleColumnAccessCodeGenerator.createAccessor(MarshallerUtil.mode(field.getType()), i + firstColIdx);
+ }
+ }
+ catch (NoSuchFieldException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Class<?> getClazz() {
+ return tClass;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isSimpleType() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public CodeBlock unmarshallObjectCode(String tupleExpr) {
+ final CodeBlock.Builder builder = CodeBlock.builder()
+ .addStatement("$T obj = ($T)$L.create()", tClass, tClass, objectFactoryVar);
+
+ for (int i = 0; i < columnAccessessors.length; i++)
+ builder.addStatement("FIELD_HANDLE_$L.set(obj, $L)", columnAccessessors[i].columnIdx(), columnAccessessors[i].read(tupleExpr));
+
+ builder.addStatement("return obj");
+ return builder.build();
+ }
+
+ /** {@inheritDoc} */
+ @Override public CodeBlock getValueCode(String objVar, int i) {
+ return CodeBlock.of("FIELD_HANDLE_$L.get($L)", columnAccessessors[i].columnIdx(), objVar);
+ }
+
+ /** {@inheritDoc} */
+ @Override public CodeBlock marshallObjectCode(String asm, String objVar) {
+ final CodeBlock.Builder builder = CodeBlock.builder();
+
+ for (int i = 0; i < columnAccessessors.length; i++)
+ builder.add(columnAccessessors[i].write(asm, getValueCode(objVar, i).toString()));
+
+ return builder.build();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+ for (int i = 0; i < columnAccessessors.length; i++) {
+ builder.addField(FieldSpec.builder(
+ VarHandle.class,
+ CodeBlock.of("FIELD_HANDLE_$L", columnAccessessors[i].columnIdx()).toString(),
+ Modifier.PRIVATE,
+ Modifier.FINAL,
+ Modifier.STATIC)
+ .build());
+
+ staticBuilder.addStatement("FIELD_HANDLE_$L = lookup.unreflectVarHandle($T.class.getDeclaredField($S))",
+ columnAccessessors[i].columnIdx(), tClass, columns.column(i).name());
+ }
+ }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java
new file mode 100644
index 0000000..8a7d01b
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/SerializerGenerator.java
@@ -0,0 +1,361 @@
+/*
+ * 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.generator;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ArrayTypeName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+import javax.annotation.processing.Generated;
+import javax.lang.model.element.Modifier;
+import jdk.jfr.Experimental;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.schema.marshaller.AbstractSerializer;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.CompilerUtils;
+import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
+import org.apache.ignite.internal.schema.marshaller.SerializationException;
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
+import org.apache.ignite.internal.util.ObjectFactory;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * {@link Serializer} code generator.
+ */
+@Experimental
+public class SerializerGenerator implements SerializerFactory {
+ /** Serializer package name. */
+ public static final String SERIALIZER_PACKAGE_NAME = "org.apache.ignite.internal.schema.marshaller";
+
+ /** Serializer package name prefix. */
+ public static final String SERIALIZER_CLASS_NAME_PREFIX = "SerializerForSchema_";
+
+ /** {@inheritDoc} */
+ @Override public Serializer create(
+ SchemaDescriptor schema,
+ Class<?> keyClass,
+ Class<?> valClass
+ ) {
+ final String className = SERIALIZER_CLASS_NAME_PREFIX + schema.version();
+ try {
+ // Generate Serializer code.
+ JavaFile javaFile = generateSerializerClassCode(className, schema, keyClass, valClass);
+
+ //TODO: pass code to logger on trace level.
+// System.out.println(javaFile.toString());
+
+ // Compile.
+ ClassLoader loader = CompilerUtils.compileCode(javaFile);
+
+ // Instantiate serializer.
+ return (Serializer)loader.loadClass(javaFile.packageName + '.' + className)
+ .getDeclaredConstructor(SchemaDescriptor.class, Class.class, Class.class)
+ .newInstance(schema, keyClass, valClass);
+
+ }
+ catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalStateException("Failed to create serializer for key-value pair: schemaVer=" + schema.version() +
+ ", keyClass=" + keyClass.getSimpleName() + ", valueClass=" + valClass.getSimpleName(), e);
+ }
+ }
+
+ /**
+ * Generates serializer code.
+ *
+ * @param className Serializer class name.
+ * @param schema Schema descriptor.
+ * @param keyClass Key class.
+ * @param valClass Value class.
+ * @return Generated java file representation.
+ */
+ private JavaFile generateSerializerClassCode(String className, SchemaDescriptor schema, Class<?> keyClass,
+ Class<?> valClass) {
+ try {
+ // Build code generators.
+ final MarshallerCodeGenerator keyMarsh = createObjectMarshaller(keyClass, "keyFactory", schema.keyColumns(), 0);
+ final MarshallerCodeGenerator valMarsh = createObjectMarshaller(valClass, "valFactory", schema.valueColumns(), schema.keyColumns().length());
+
+ final TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className)
+ .superclass(AbstractSerializer.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", getClass().getCanonicalName()).build());
+
+ initFieldHandlers(keyMarsh, valMarsh, classBuilder);
+
+ classBuilder
+ .addField(ParameterizedTypeName.get(ObjectFactory.class, keyClass), "keyFactory", Modifier.PRIVATE, Modifier.FINAL)
+ .addField(ParameterizedTypeName.get(ObjectFactory.class, valClass), "valFactory", Modifier.PRIVATE, Modifier.FINAL)
+ .addMethod(
+ // Constructor.
+ MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(SchemaDescriptor.class, "schema")
+ .addParameter(Class.class, "keyClass")
+ .addParameter(Class.class, "valClass")
+ .addStatement("super(schema)")
+ .addStatement("this.keyFactory = $T.factoryForClass(keyClass)", MarshallerUtil.class)
+ .addStatement("this.valFactory = $T.factoryForClass(valClass)", MarshallerUtil.class)
+ .build()
+ )
+ .addMethod(generateTupleAsseblerFactoryMethod(schema, keyMarsh, valMarsh))
+ .addMethod(generateSerializeMethod(keyMarsh, valMarsh))
+ .addMethod(generateDeserializeKeyMethod(keyMarsh))
+ .addMethod(generateDeserializeValueMethod(valMarsh));
+
+ return JavaFile
+ .builder(SERIALIZER_PACKAGE_NAME, classBuilder.build())
+ .addStaticImport(MethodHandles.class, "Lookup")
+ .skipJavaLangImports(true)
+ .indent(" ")
+ .build();
+ }
+ catch (Exception ex) {
+ //TODO: fallback to java serializer?
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ /**
+ * @param keyMarsh Key marshaller code generator.
+ * @param valMarsh Value marshaller code generator.
+ * @param classBuilder Serializer class builder.
+ */
+ private void initFieldHandlers(
+ MarshallerCodeGenerator keyMarsh,
+ MarshallerCodeGenerator valMarsh,
+ TypeSpec.Builder classBuilder
+ ) {
+ if (keyMarsh.isSimpleType() && valMarsh.isSimpleType())
+ return; // No field hanlders needed for simple types.
+
+ final CodeBlock.Builder staticInitBuilder = CodeBlock.builder()
+ .addStatement("$T.Lookup lookup", MethodHandles.class)
+ .beginControlFlow("try");
+
+ if (!keyMarsh.isSimpleType()) {
+ staticInitBuilder.addStatement(
+ "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+ MethodHandles.class,
+ Objects.requireNonNull(keyMarsh.getClazz()),
+ MethodHandles.class
+ );
+
+ keyMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
+ }
+
+ if (!valMarsh.isSimpleType()) {
+ staticInitBuilder.addStatement(
+ "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+ MethodHandles.class,
+ Objects.requireNonNull(valMarsh.getClazz()),
+ MethodHandles.class
+ );
+
+ valMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
+ }
+
+ staticInitBuilder
+ .nextControlFlow(
+ "catch ($T | $T ex)",
+ ReflectiveOperationException.class,
+ SecurityException.class
+ )
+ .addStatement("throw new $T(ex)", IllegalStateException.class)
+ .endControlFlow();
+
+ classBuilder.addStaticBlock(staticInitBuilder.build());
+ }
+
+ /**
+ * @param schema Schema descriptor.
+ * @param keyMarsh Key marshaller code generator.
+ * @param valMarsh Value marshaller code generator.
+ * @return Tuple accembler factory method spec.
+ */
+ private MethodSpec generateTupleAsseblerFactoryMethod(SchemaDescriptor schema, MarshallerCodeGenerator keyMarsh,
+ MarshallerCodeGenerator valMarsh) {
+ final MethodSpec.Builder builder = MethodSpec
+ .methodBuilder("createAssembler")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .addParameter(Object.class, "key", Modifier.FINAL)
+ .addParameter(Object.class, "val", Modifier.FINAL)
+ .returns(TupleAssembler.class)
+
+ .addStatement("int varlenKeyCols = 0; int varlenValueCols = 0")
+ .addStatement("int varlenKeyColsSize = 0; int varlenValueColsSize = 0")
+ .addStatement("$T keyCols = schema.keyColumns()", Columns.class)
+ .addStatement("$T valCols = schema.valueColumns()", Columns.class);
+
+ Columns keyCols = schema.keyColumns();
+ if (keyCols.firstVarlengthColumn() >= 0) {
+ final CodeBlock.Builder block = CodeBlock.builder().indent()
+ .addStatement("$T fVal", Object.class);// Temporary vars.
+
+ for (int i = keyCols.firstVarlengthColumn(); i < keyCols.length(); i++) {
+ assert !keyCols.column(i).type().spec().fixedLength();
+
+ block.addStatement("fVal = $L", keyMarsh.getValueCode("key", i).toString());
+
+ block.beginControlFlow("if (fVal != null)")
+ .addStatement("varlenKeyColsSize += $T.getValueSize(fVal, keyCols.column($L).type())", MarshallerUtil.class, i)
+ .addStatement("varlenKeyCols++")
+ .endControlFlow();
+ }
+ block.unindent();
+
+ builder
+ .addCode("{\n")
+ .addCode(block.build())
+ .addCode("}\n");
+ }
+
+ Columns valCols = schema.valueColumns();
+ if (valCols.firstVarlengthColumn() >= 0) {
+ final CodeBlock.Builder block = CodeBlock.builder().indent()
+ .addStatement("$T fVal", Object.class);// Temporary vars.
+
+ for (int i = valCols.firstVarlengthColumn(); i < valCols.length(); i++) {
+ assert !valCols.column(i).type().spec().fixedLength();
+
+ block.addStatement("fVal = $L", valMarsh.getValueCode("val", i).toString());
+
+ block.beginControlFlow("if (fVal != null)")
+ .addStatement("varlenValueColsSize += $T.getValueSize(fVal, valCols.column($L).type())", MarshallerUtil.class, i)
+ .addStatement("varlenValueCols++")
+ .endControlFlow();
+ }
+ block.unindent();
+
+ builder
+ .addCode("{\n")
+ .addCode(block.build())
+ .addCode("}\n");
+ }
+
+ builder.addStatement("int size = $T.tupleSize(keyCols, varlenKeyCols, varlenKeyColsSize," +
+ "valCols, varlenValueCols, varlenValueColsSize)", TupleAssembler.class);
+
+ builder.addStatement("return new $T(schema, size, varlenKeyCols, varlenValueCols)", TupleAssembler.class);
+
+ return builder.build();
+ }
+
+ /**
+ * @param valMarsh Value marshaller code generator.
+ * @return Deserialize value method spec.
+ */
+ private MethodSpec generateDeserializeValueMethod(MarshallerCodeGenerator valMarsh) {
+ return MethodSpec
+ .methodBuilder("deserializeValue0")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .addParameter(Tuple.class, "tuple", Modifier.FINAL)
+ .addException(SerializationException.class)
+ .returns(TypeName.OBJECT)
+
+ .beginControlFlow("try")
+ .addCode(valMarsh.unmarshallObjectCode("tuple"))
+ .nextControlFlow("catch($T th)", Throwable.class)
+ .addStatement("throw new $T(th)", SerializationException.class)
+ .endControlFlow()
+ .build();
+ }
+
+ /**
+ * @param keyMarsh Key marshaller code generator.
+ * @return Deserialize key method spec.
+ */
+ private MethodSpec generateDeserializeKeyMethod(MarshallerCodeGenerator keyMarsh) {
+ return MethodSpec
+ .methodBuilder("deserializeKey0")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .addParameter(Tuple.class, "tuple", Modifier.FINAL)
+ .addException(SerializationException.class)
+ .returns(TypeName.OBJECT)
+
+ .beginControlFlow("try")
+ .addCode(keyMarsh.unmarshallObjectCode("tuple"))
+ .nextControlFlow("catch($T th)", Throwable.class)
+ .addStatement("throw new $T(th)", SerializationException.class)
+ .endControlFlow()
+ .build();
+ }
+
+ /**
+ * @param keyMarsh Key marshaller code generator.
+ * @param valMarsh Value marshaller code generator.
+ * @return Serialize method spec.
+ */
+ private MethodSpec generateSerializeMethod(MarshallerCodeGenerator keyMarsh, MarshallerCodeGenerator valMarsh) {
+ return MethodSpec.
+ methodBuilder("serialize0")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .addParameter(TupleAssembler.class, "asm", Modifier.FINAL)
+ .addParameter(TypeName.OBJECT, "key", Modifier.FINAL)
+ .addParameter(TypeName.OBJECT, "val", Modifier.FINAL)
+ .addException(SerializationException.class)
+ .returns(ArrayTypeName.of(TypeName.BYTE))
+
+ .beginControlFlow("try")
+ .addCode(keyMarsh.marshallObjectCode("asm", "key"))
+ .addCode(valMarsh.marshallObjectCode("asm", "val"))
+ .addStatement("return asm.build()")
+
+ .nextControlFlow("catch($T th)", Throwable.class)
+ .addStatement("throw new $T(th)", SerializationException.class)
+ .endControlFlow()
+ .build();
+ }
+
+ /**
+ * Creates marshaller code generator for given class.
+ *
+ * @param tClass Target class.
+ * @param factoryRefVar Object factory variable.
+ * @param columns Columns that tClass mapped to.
+ * @param firstColIdx First column absolute index in schema.
+ * @return Marshaller code generator.
+ */
+ private MarshallerCodeGenerator createObjectMarshaller(
+ Class<?> tClass,
+ @Nullable String factoryRefVar,
+ Columns columns,
+ int firstColIdx
+ ) {
+ BinaryMode mode = MarshallerUtil.mode(tClass);
+
+ if (mode != null) // Simple type.
+ return new IdentityObjectMarshallerExprGenerator(TupleColumnAccessCodeGenerator.createAccessor(mode, firstColIdx));
+ else
+ return new ObjectMarshallerCodeGenerator(tClass, factoryRefVar, columns, firstColIdx);
+ }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleColumnAccessCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleColumnAccessCodeGenerator.java
new file mode 100644
index 0000000..0641094
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/TupleColumnAccessCodeGenerator.java
@@ -0,0 +1,257 @@
+/*
+ * 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.generator;
+
+import com.squareup.javapoet.CodeBlock;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.BitSet;
+import java.util.UUID;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+
+/**
+ * Tuple access code generator.
+ */
+public class TupleColumnAccessCodeGenerator {
+ /** Tuple method handler. */
+ public static final MethodHandle READ_BYTE;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_SHORT;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_INT;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_LONG;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_FLOAT;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_DOUBLE;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_BYTE_BOXED;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_SHORT_BOXED;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_INT_BOXED;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_LONG_BOXED;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_FLOAT_BOXED;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_DOUBLE_BOXED;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_UUID;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_BITSET;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_STRING;
+ /** Tuple method handler. */
+ public static final MethodHandle READ_BYTE_ARR;
+
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_NULL;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_BYTE;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_SHORT;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_INT;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_LONG;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_FLOAT;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_DOUBLE;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_UUID;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_BITSET;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_STRING;
+ /** Tuple assembler method handler. */
+ public static final MethodHandle WRITE_BYTE_ARR;
+
+ /**
+ * Initializes static handlers.
+ */
+ static {
+ try {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ READ_BYTE = lookup.findVirtual(Tuple.class, "byteValue", MethodType.methodType(byte.class, int.class));
+ READ_SHORT = lookup.findVirtual(Tuple.class, "shortValue", MethodType.methodType(short.class, int.class));
+ READ_INT = lookup.findVirtual(Tuple.class, "intValue", MethodType.methodType(int.class, int.class));
+ READ_LONG = lookup.findVirtual(Tuple.class, "longValue", MethodType.methodType(long.class, int.class));
+ READ_FLOAT = lookup.findVirtual(Tuple.class, "floatValue", MethodType.methodType(float.class, int.class));
+ READ_DOUBLE = lookup.findVirtual(Tuple.class, "doubleValue", MethodType.methodType(double.class, int.class));
+ READ_BYTE_BOXED = lookup.findVirtual(Tuple.class, "byteValueBoxed", MethodType.methodType(Byte.class, int.class));
+ READ_SHORT_BOXED = lookup.findVirtual(Tuple.class, "shortValueBoxed", MethodType.methodType(Short.class, int.class));
+ READ_INT_BOXED = lookup.findVirtual(Tuple.class, "intValueBoxed", MethodType.methodType(Integer.class, int.class));
+ READ_LONG_BOXED = lookup.findVirtual(Tuple.class, "longValueBoxed", MethodType.methodType(Long.class, int.class));
+ READ_FLOAT_BOXED = lookup.findVirtual(Tuple.class, "floatValueBoxed", MethodType.methodType(Float.class, int.class));
+ READ_DOUBLE_BOXED = lookup.findVirtual(Tuple.class, "doubleValueBoxed", MethodType.methodType(Double.class, int.class));
+ READ_UUID = lookup.findVirtual(Tuple.class, "uuidValue", MethodType.methodType(UUID.class, int.class));
+ READ_BITSET = lookup.findVirtual(Tuple.class, "bitmaskValue", MethodType.methodType(BitSet.class, int.class));
+ READ_STRING = lookup.findVirtual(Tuple.class, "stringValue", MethodType.methodType(String.class, int.class));
+ READ_BYTE_ARR = lookup.findVirtual(Tuple.class, "bytesValue", MethodType.methodType(byte[].class, int.class));
+
+ WRITE_NULL = lookup.findVirtual(TupleAssembler.class, "appendNull", MethodType.methodType(void.class));
+ WRITE_BYTE = lookup.findVirtual(TupleAssembler.class, "appendByte", MethodType.methodType(void.class, byte.class));
+ WRITE_SHORT = lookup.findVirtual(TupleAssembler.class, "appendShort", MethodType.methodType(void.class, short.class));
+ WRITE_INT = lookup.findVirtual(TupleAssembler.class, "appendInt", MethodType.methodType(void.class, int.class));
+ WRITE_LONG = lookup.findVirtual(TupleAssembler.class, "appendLong", MethodType.methodType(void.class, long.class));
+ WRITE_FLOAT = lookup.findVirtual(TupleAssembler.class, "appendFloat", MethodType.methodType(void.class, float.class));
+ WRITE_DOUBLE = lookup.findVirtual(TupleAssembler.class, "appendDouble", MethodType.methodType(void.class, double.class));
+ WRITE_UUID = lookup.findVirtual(TupleAssembler.class, "appendUuid", MethodType.methodType(void.class, UUID.class));
+ WRITE_BITSET = lookup.findVirtual(TupleAssembler.class, "appendBitmask", MethodType.methodType(void.class, BitSet.class));
+ WRITE_STRING = lookup.findVirtual(TupleAssembler.class, "appendString", MethodType.methodType(void.class, String.class));
+ WRITE_BYTE_ARR = lookup.findVirtual(TupleAssembler.class, "appendBytes", MethodType.methodType(void.class, byte[].class));
+ }
+ catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * @param mode Binary mode.
+ * @param colIdx Column index in schema.
+ * @return Tuple column access code generator.
+ */
+ static TupleColumnAccessCodeGenerator createAccessor(BinaryMode mode, int colIdx) {
+ switch (mode) {
+ case P_BYTE:
+ return new TupleColumnAccessCodeGenerator("READ_BYTE", "WRITE_BYTE", byte.class, colIdx);
+ case P_SHORT:
+ return new TupleColumnAccessCodeGenerator("READ_SHORT", "WRITE_SHORT", short.class, colIdx);
+ case P_INT:
+ return new TupleColumnAccessCodeGenerator("READ_INT", "WRITE_INT", int.class, colIdx);
+ case P_LONG:
+ return new TupleColumnAccessCodeGenerator("READ_LONG", "WRITE_LONG", long.class, colIdx);
+ case P_FLOAT:
+ return new TupleColumnAccessCodeGenerator("READ_FLOAT", "WRITE_FLOAT", float.class, colIdx);
+ case P_DOUBLE:
+ return new TupleColumnAccessCodeGenerator("READ_DOUBLE", "WRITE_DOUBLE", double.class, colIdx);
+ case BYTE:
+ return new TupleColumnAccessCodeGenerator("READ_BYTE_BOXED", "WRITE_BYTE", Byte.class, byte.class, colIdx);
+ case SHORT:
+ return new TupleColumnAccessCodeGenerator("READ_SHORT_BOXED", "WRITE_SHORT", Short.class, short.class, colIdx);
+ case INT:
+ return new TupleColumnAccessCodeGenerator("READ_INT_BOXED", "WRITE_INT", Integer.class, int.class, colIdx);
+ case LONG:
+ return new TupleColumnAccessCodeGenerator("READ_LONG_BOXED", "WRITE_LONG", Long.class, long.class, colIdx);
+ case FLOAT:
+ return new TupleColumnAccessCodeGenerator("READ_FLOAT_BOXED", "WRITE_FLOAT", Float.class, float.class, colIdx);
+ case DOUBLE:
+ return new TupleColumnAccessCodeGenerator("READ_DOUBLE_BOXED", "WRITE_DOUBLE", Double.class, double.class, colIdx);
+ case STRING:
+ return new TupleColumnAccessCodeGenerator("READ_STRING", "WRITE_STRING", String.class, colIdx);
+ case UUID:
+ return new TupleColumnAccessCodeGenerator("READ_UUID", "WRITE_UUID", UUID.class, colIdx);
+ case BYTE_ARR:
+ return new TupleColumnAccessCodeGenerator("READ_BYTE_ARR", "WRITE_BYTE_ARR", byte[].class, colIdx);
+ case BITSET:
+ return new TupleColumnAccessCodeGenerator("READ_BITSET", "WRITE_BITSET", BitSet.class, colIdx);
+ }
+
+ throw new IllegalStateException("Unsupported binary mode: " + mode);
+ }
+
+ /** Reader handle name. */
+ private final String readHandleName;
+
+ /** Writer handle name. */
+ private final String writeHandleName;
+
+ /** Mapped value type. */
+ private final Class<?> mappedType;
+
+ /** Write method argument type. */
+ private final Class<?> writeArgType;
+
+ /** Column index in schema. */
+ private final int colIdx;
+
+ /**
+ * Constructor.
+ *
+ * @param readHandleName Reader handle name.
+ * @param writeHandleName Writer handle name.
+ * @param mappedType Mapped value type.
+ * @param colIdx Column index in schema.
+ */
+ TupleColumnAccessCodeGenerator(String readHandleName, String writeHandleName, Class<?> mappedType, int colIdx) {
+ this(readHandleName, writeHandleName, mappedType, mappedType, colIdx);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param readHandleName Reader handle name.
+ * @param writeHandleName Writer handle name.
+ * @param mappedType Mapped value type.
+ * @param writeArgType Write method argument type.
+ * @param colIdx Column index in schema.
+ */
+ TupleColumnAccessCodeGenerator(String readHandleName, String writeHandleName, Class<?> mappedType,
+ Class<?> writeArgType, int colIdx) {
+ this.readHandleName = readHandleName;
+ this.writeHandleName = writeHandleName;
+ this.colIdx = colIdx;
+ this.mappedType = mappedType;
+ this.writeArgType = writeArgType;
+ }
+
+ /**
+ * @return Column index in schema.
+ */
+ public int columnIdx() {
+ return colIdx;
+ }
+
+ /**
+ * @param tuple Tuple.
+ * @return Code that reads column value from tuple.
+ */
+ public CodeBlock read(String tuple) {
+ return CodeBlock.of("($T)$T.$L.invoke($L, $L)", mappedType, TupleColumnAccessCodeGenerator.class, readHandleName, tuple, colIdx);
+ }
+
+ /**
+ * @param asmVar Tuple assembler var.
+ * @param valExpr Value expression.
+ * @return Code that writes value to tuple column.
+ */
+ public CodeBlock write(String asmVar, String valExpr) {
+ if (mappedType.isPrimitive())
+ return CodeBlock.builder().addStatement("$T.$L.invokeExact($L, ($T)$L)", TupleColumnAccessCodeGenerator.class, writeHandleName, asmVar, writeArgType, valExpr).build();
+ else {
+ return CodeBlock.builder()
+ .add("{\n").indent()
+ .addStatement("Object fVal")
+ .beginControlFlow("if((fVal = $L) == null)", valExpr)
+ .addStatement("$T.WRITE_NULL.invokeExact($L)", TupleColumnAccessCodeGenerator.class, asmVar)
+ .nextControlFlow("else")
+ .addStatement("$T.$L.invokeExact($L, ($T)fVal)", TupleColumnAccessCodeGenerator.class, writeHandleName, asmVar, writeArgType)
+ .endControlFlow()
+ .unindent()
+ .add("}\n")
+ .build();
+ }
+ }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/UnsafeFieldAccessor.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java
similarity index 50%
rename from modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/UnsafeFieldAccessor.java
rename to modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java
index 72b2491..e8023fb 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/UnsafeFieldAccessor.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessor.java
@@ -17,8 +17,12 @@
package org.apache.ignite.internal.schema.marshaller.reflection;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
+import java.util.BitSet;
import java.util.Objects;
+import java.util.UUID;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.Columns;
import org.apache.ignite.internal.schema.Tuple;
@@ -26,32 +30,18 @@ import org.apache.ignite.internal.schema.TupleAssembler;
import org.apache.ignite.internal.schema.marshaller.BinaryMode;
import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
import org.apache.ignite.internal.schema.marshaller.SerializationException;
-import org.apache.ignite.internal.util.IgniteUnsafeUtils;
import org.jetbrains.annotations.Nullable;
/**
* Field accessor to speedup access.
*/
-// TODO: Extract interface, move to java-8 profile and add Java9+ implementation using VarHandles.
-public abstract class UnsafeFieldAccessor {
- /**
- * TODO: implement sesitive information filtering.
- *
- * @return {@code False} if sensitive information exoising is prohibited, {@code false} otherwise.
- */
- private static boolean includeSensitive() {
- return true;
- }
-
- /** Offset. */
- protected final long offset;
+public abstract class FieldAccessor {
+ /** VarHandle. */
+ protected final VarHandle varHandle;
/** Mode. */
protected final BinaryMode mode;
- /** Field name */
- protected final String name;
-
/**
* Mapped column position in schema.
* <p>
@@ -67,7 +57,7 @@ public abstract class UnsafeFieldAccessor {
* @param colIdx Column index in schema.
* @return Accessor.
*/
- static UnsafeFieldAccessor create(Class<?> type, Column col, int colIdx) {
+ static FieldAccessor create(Class<?> type, Column col, int colIdx) {
try {
final Field field = type.getDeclaredField(col.name());
@@ -75,25 +65,28 @@ public abstract class UnsafeFieldAccessor {
throw new IllegalArgumentException("Failed to map non-nullable field to nullable column [name=" + field.getName() + ']');
BinaryMode mode = MarshallerUtil.mode(field.getType());
+ final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(type, MethodHandles.lookup());
+
+ VarHandle varHandle = lookup.unreflectVarHandle(field);
switch (mode) {
case P_BYTE:
- return new BytePrimitiveAccessor(field, colIdx);
+ return new BytePrimitiveAccessor(varHandle, colIdx);
case P_SHORT:
- return new ShortPrimitiveAccessor(field, colIdx);
+ return new ShortPrimitiveAccessor(varHandle, colIdx);
case P_INT:
- return new IntPrimitiveAccessor(field, colIdx);
+ return new IntPrimitiveAccessor(varHandle, colIdx);
case P_LONG:
- return new LongPrimitiveAccessor(field, colIdx);
+ return new LongPrimitiveAccessor(varHandle, colIdx);
case P_FLOAT:
- return new FloatPrimitiveAccessor(field, colIdx);
+ return new FloatPrimitiveAccessor(varHandle, colIdx);
case P_DOUBLE:
- return new DoublePrimitiveAccessor(field, colIdx);
+ return new DoublePrimitiveAccessor(varHandle, colIdx);
case BYTE:
case SHORT:
@@ -105,7 +98,7 @@ public abstract class UnsafeFieldAccessor {
case UUID:
case BYTE_ARR:
case BITSET:
- return new ReferenceFieldAccessor(field, colIdx, mode);
+ return new ReferenceFieldAccessor(varHandle, colIdx, mode);
default:
assert false : "Invalid mode " + mode;
@@ -113,7 +106,7 @@ public abstract class UnsafeFieldAccessor {
throw new IllegalArgumentException("Failed to create accessor for field [name=" + field.getName() + ']');
}
- catch (NoSuchFieldException | SecurityException ex) {
+ catch (NoSuchFieldException | SecurityException | IllegalAccessException ex) {
throw new IllegalArgumentException(ex);
}
}
@@ -126,7 +119,7 @@ public abstract class UnsafeFieldAccessor {
* @param mode Binary mode.
* @return Accessor.
*/
- static UnsafeFieldAccessor createIdentityAccessor(Column col, int colIdx, BinaryMode mode) {
+ static FieldAccessor createIdentityAccessor(Column col, int colIdx, BinaryMode mode) {
switch (mode) {
// Marshaller read/write object contract methods allowed boxed types only.
case P_BYTE:
@@ -157,103 +150,229 @@ public abstract class UnsafeFieldAccessor {
}
/**
- * Protected constructor.
+ * Reads value object from tuple.
*
- * @param field Field.
+ * @param reader Reader.
* @param colIdx Column index.
- * @param mode Binary mode;
+ * @param mode Binary read mode.
+ * @return Read value object.
*/
- protected UnsafeFieldAccessor(Field field, int colIdx, BinaryMode mode) {
- assert field != null;
+ private static Object readRefValue(Tuple reader, int colIdx, BinaryMode mode) {
+ assert reader != null;
assert colIdx >= 0;
- assert mode != null;
- this.colIdx = colIdx;
- this.mode = mode;
- offset = IgniteUnsafeUtils.objectFieldOffset(field);
- name = field.getName();
+ Object val = null;
+
+ switch (mode) {
+ case BYTE:
+ val = reader.byteValueBoxed(colIdx);
+
+ break;
+
+ case SHORT:
+ val = reader.shortValueBoxed(colIdx);
+
+ break;
+
+ case INT:
+ val = reader.intValueBoxed(colIdx);
+
+ break;
+
+ case LONG:
+ val = reader.longValueBoxed(colIdx);
+
+ break;
+
+ case FLOAT:
+ val = reader.floatValueBoxed(colIdx);
+
+ break;
+
+ case DOUBLE:
+ val = reader.doubleValueBoxed(colIdx);
+
+ break;
+
+ case STRING:
+ val = reader.stringValue(colIdx);
+
+ break;
+
+ case UUID:
+ val = reader.uuidValue(colIdx);
+
+ break;
+
+ case BYTE_ARR:
+ val = reader.bytesValue(colIdx);
+
+ break;
+
+ case BITSET:
+ val = reader.bitmaskValue(colIdx);
+
+ break;
+
+ default:
+ assert false : "Invalid mode: " + mode;
+ }
+
+ return val;
+ }
+
+ /**
+ * Writes reference value to tuple.
+ *
+ * @param val Value object.
+ * @param writer Writer.
+ * @param mode Write binary mode.
+ */
+ private static void writeRefObject(Object val, TupleAssembler writer, BinaryMode mode) {
+ assert writer != null;
+
+ if (val == null) {
+ writer.appendNull();
+
+ return;
+ }
+
+ switch (mode) {
+ case BYTE:
+ writer.appendByte((Byte)val);
+
+ break;
+
+ case SHORT:
+ writer.appendShort((Short)val);
+
+ break;
+
+ case INT:
+ writer.appendInt((Integer)val);
+
+ break;
+
+ case LONG:
+ writer.appendLong((Long)val);
+
+ break;
+
+ case FLOAT:
+ writer.appendFloat((Float)val);
+
+ break;
+
+ case DOUBLE:
+ writer.appendDouble((Double)val);
+
+ break;
+
+ case STRING:
+ writer.appendString((String)val);
+
+ break;
+
+ case UUID:
+ writer.appendUuid((UUID)val);
+
+ break;
+
+ case BYTE_ARR:
+ writer.appendBytes((byte[])val);
+
+ break;
+
+ case BITSET:
+ writer.appendBitmask((BitSet)val);
+
+ break;
+
+ default:
+ assert false : "Invalid mode: " + mode;
+ }
}
/**
* Protected constructor.
*
+ * @param varHandle Field.
* @param colIdx Column index.
* @param mode Binary mode;
*/
- private UnsafeFieldAccessor(int colIdx, BinaryMode mode) {
+ protected FieldAccessor(VarHandle varHandle, int colIdx, BinaryMode mode) {
+ assert varHandle != null;
assert colIdx >= 0;
assert mode != null;
this.colIdx = colIdx;
this.mode = mode;
- offset = 0;
- name = null;
+ this.varHandle = varHandle;
}
/**
- * Get binary read/write mode.
+ * Protected constructor.
*
- * @return Binary mode.
+ * @param colIdx Column index.
+ * @param mode Binary mode;
*/
- public BinaryMode mode() {
- return mode;
+ private FieldAccessor(int colIdx, BinaryMode mode) {
+ assert colIdx >= 0;
+ assert mode != null;
+
+ this.colIdx = colIdx;
+ this.mode = mode;
+ varHandle = null;
}
/**
* Write object field value to tuple.
*
- * @param obj Source object.
* @param writer Tuple writer.
+ * @param obj Source object.
* @throws SerializationException If failed.
*/
- public void write(Object obj, TupleAssembler writer) throws SerializationException {
+ public void write(TupleAssembler writer, Object obj) throws SerializationException {
try {
- write0(Objects.requireNonNull(obj), writer);
+ write0(writer, obj);
}
catch (Exception ex) {
- if (includeSensitive() && name != null)
- throw new SerializationException("Failed to read field [id=" + colIdx + ']', ex);
- else
- throw new SerializationException("Failed to write field [id=" + colIdx + ']', ex);
+ throw new SerializationException("Failed to write field [id=" + colIdx + ']', ex);
}
}
/**
* Write object field value to tuple.
*
- * @param obj Source object.
* @param writer Tuple writer.
- * @throws IllegalAccessException If failed.
+ * @param obj Source object.
*/
- protected abstract void write0(Object obj, TupleAssembler writer) throws IllegalAccessException;
+ protected abstract void write0(TupleAssembler writer, Object obj) throws Exception;
/**
* Reads value fom tuple to object field.
*
- * @param obj Target object.
* @param reader Tuple reader.
+ * @param obj Target object.
* @throws SerializationException If failed.
*/
- public void read(Object obj, Tuple reader) throws SerializationException {
+ public void read(Tuple reader, Object obj) throws SerializationException {
try {
- read0(Objects.requireNonNull(obj), reader);
+ read0(reader, Objects.requireNonNull(obj));
}
catch (Exception 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);
+ throw new SerializationException("Failed to read field [id=" + colIdx + ']', ex);
}
}
/**
* Reads value fom tuple to object field.
*
- * @param obj Target object.
* @param reader Tuple reader.
- * @throws IllegalAccessException If failed.
+ * @param obj Target object.
+ * @throws Exception If failed.
*/
- protected abstract void read0(Object obj, Tuple reader) throws IllegalAccessException;
+ protected abstract void read0(Tuple reader, Object obj) throws Exception;
/**
* Read value.
@@ -261,7 +380,7 @@ public abstract class UnsafeFieldAccessor {
* @param reader Tuple reader.
* @return Object.
*/
- public Object read(Tuple reader) {
+ public Object read(Tuple reader) throws SerializationException {
throw new UnsupportedOperationException();
}
@@ -271,14 +390,14 @@ public abstract class UnsafeFieldAccessor {
* @param obj Object.
* @return Field value of given object.
*/
- @Nullable Object value(Object obj) {
- return IgniteUnsafeUtils.getObjectField(Objects.requireNonNull(obj), offset);
+ Object value(Object obj) {
+ return varHandle.get(Objects.requireNonNull(obj));
}
/**
* Accessor for field of primitive {@code byte} type.
*/
- private static class IdentityAccessor extends UnsafeFieldAccessor {
+ private static class IdentityAccessor extends FieldAccessor {
/**
* Constructor.
*
@@ -290,22 +409,22 @@ public abstract class UnsafeFieldAccessor {
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- JavaSerializer.writeRefObject(Objects.requireNonNull(obj, "Null values are not supported."), writer, mode);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ writeRefObject(obj, writer, mode);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
throw new UnsupportedOperationException("Called identity accessor for object field.");
}
/** {@inheritDoc} */
@Override public Object read(Tuple reader) {
- return JavaSerializer.readRefValue(reader, colIdx, mode);
+ return readRefValue(reader, colIdx, mode);
}
/** {@inheritDoc} */
- @Override @Nullable Object value(Object obj) {
+ @Override Object value(Object obj) {
return obj;
}
}
@@ -313,200 +432,198 @@ public abstract class UnsafeFieldAccessor {
/**
* Accessor for field of primitive {@code byte} type.
*/
- private static class BytePrimitiveAccessor extends UnsafeFieldAccessor {
+ private static class BytePrimitiveAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
*/
- public BytePrimitiveAccessor(Field field, int colIdx) {
- super(field, colIdx, BinaryMode.P_BYTE);
+ public BytePrimitiveAccessor(VarHandle varHandle, int colIdx) {
+ super(varHandle, colIdx, BinaryMode.P_BYTE);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- final byte val = IgniteUnsafeUtils.getByteField(obj, offset);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ final byte val = (byte)varHandle.get(obj);
writer.appendByte(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
final byte val = reader.byteValue(colIdx);
- IgniteUnsafeUtils.putByteField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
/**
* Accessor for field of primitive {@code short} type.
*/
- private static class ShortPrimitiveAccessor extends UnsafeFieldAccessor {
+ private static class ShortPrimitiveAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
*/
- public ShortPrimitiveAccessor(Field field, int colIdx) {
- super(field, colIdx, BinaryMode.P_SHORT);
+ public ShortPrimitiveAccessor(VarHandle varHandle, int colIdx) {
+ super(varHandle, colIdx, BinaryMode.P_SHORT);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- final short val = IgniteUnsafeUtils.getShortField(obj, offset);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ final short val = (short)varHandle.get(obj);
writer.appendShort(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
final short val = reader.shortValue(colIdx);
- IgniteUnsafeUtils.putShortField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
/**
* Accessor for field of primitive {@code int} type.
*/
- private static class IntPrimitiveAccessor extends UnsafeFieldAccessor {
+ private static class IntPrimitiveAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
*/
- public IntPrimitiveAccessor(Field field, int colIdx) {
- super(field, colIdx, BinaryMode.P_INT);
+ public IntPrimitiveAccessor(VarHandle varHandle, int colIdx) {
+ super(varHandle, colIdx, BinaryMode.P_INT);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- final int val = IgniteUnsafeUtils.getIntField(obj, offset);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ final int val = (int)varHandle.get(obj);
writer.appendInt(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
final int val = reader.intValue(colIdx);
- IgniteUnsafeUtils.putIntField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
/**
* Accessor for field of primitive {@code long} type.
*/
- private static class LongPrimitiveAccessor extends UnsafeFieldAccessor {
+ private static class LongPrimitiveAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
*/
- public LongPrimitiveAccessor(Field field, int colIdx) {
- super(field, colIdx, BinaryMode.P_LONG);
+ public LongPrimitiveAccessor(VarHandle varHandle, int colIdx) {
+ super(varHandle, colIdx, BinaryMode.P_LONG);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- final long val = IgniteUnsafeUtils.getLongField(obj, offset);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ final long val = (long)varHandle.get(obj);
writer.appendLong(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
final long val = reader.longValue(colIdx);
- IgniteUnsafeUtils.putLongField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
/**
* Accessor for field of primitive {@code float} type.
*/
- private static class FloatPrimitiveAccessor extends UnsafeFieldAccessor {
+ private static class FloatPrimitiveAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
*/
- public FloatPrimitiveAccessor(Field field, int colIdx) {
- super(field, colIdx, BinaryMode.P_FLOAT);
+ public FloatPrimitiveAccessor(VarHandle varHandle, int colIdx) {
+ super(varHandle, colIdx, BinaryMode.P_FLOAT);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- final float val = IgniteUnsafeUtils.getFloatField(obj, offset);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ final float val = (float)varHandle.get(obj);
writer.appendFloat(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
final float val = reader.floatValue(colIdx);
- IgniteUnsafeUtils.putFloatField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
/**
* Accessor for field of primitive {@code double} type.
*/
- private static class DoublePrimitiveAccessor extends UnsafeFieldAccessor {
+ private static class DoublePrimitiveAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
*/
- public DoublePrimitiveAccessor(Field field, int colIdx) {
- super(field, colIdx, BinaryMode.P_DOUBLE);
+ public DoublePrimitiveAccessor(VarHandle varHandle, int colIdx) {
+ super(varHandle, colIdx, BinaryMode.P_DOUBLE);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
- final double val = IgniteUnsafeUtils.getDoubleField(obj, offset);
+ @Override protected void write0(TupleAssembler writer, Object obj) {
+ final double val = (double)varHandle.get(obj);
writer.appendDouble(val);
}
/** {@inheritDoc} */
- @Override protected void read0(Object obj, Tuple reader) {
+ @Override protected void read0(Tuple reader, Object obj) {
final double val = reader.doubleValue(colIdx);
- IgniteUnsafeUtils.putDoubleField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
/**
* Accessor for field of reference type.
*/
- private static class ReferenceFieldAccessor extends UnsafeFieldAccessor {
+ private static class ReferenceFieldAccessor extends FieldAccessor {
/**
* Constructor.
*
- * @param field Field.
+ * @param varHandle VarHandle.
* @param colIdx Column index.
* @param mode Binary mode.
*/
- ReferenceFieldAccessor(Field field, int colIdx, BinaryMode mode) {
- super(field, colIdx, mode);
+ ReferenceFieldAccessor(VarHandle varHandle, int colIdx, BinaryMode mode) {
+ super(varHandle, colIdx, mode);
}
/** {@inheritDoc} */
- @Override protected void write0(Object obj, TupleAssembler writer) {
+ @Override protected void write0(TupleAssembler writer, Object obj) {
assert obj != null;
assert writer != null;
- Object val;
-
- val = IgniteUnsafeUtils.getObjectField(obj, offset);
+ Object val = varHandle.get(obj);
if (val == null) {
writer.appendNull();
@@ -514,14 +631,14 @@ public abstract class UnsafeFieldAccessor {
return;
}
- JavaSerializer.writeRefObject(val, writer, mode);
+ writeRefObject(val, writer, mode);
}
/** {@inheritDoc} */
- @Override public void read0(Object obj, Tuple reader) {
- Object val = JavaSerializer.readRefValue(reader, colIdx, mode);
+ @Override public void read0(Tuple reader, Object obj) {
+ Object val = readRefValue(reader, colIdx, mode);
- IgniteUnsafeUtils.putObjectField(obj, offset, val);
+ varHandle.set(obj, val);
}
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
index 4bbb630..d0f6532 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
@@ -17,171 +17,20 @@
package org.apache.ignite.internal.schema.marshaller.reflection;
-import java.util.BitSet;
-import java.util.UUID;
-import org.apache.ignite.internal.schema.ByteBufferTuple;
import org.apache.ignite.internal.schema.Columns;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.Tuple;
import org.apache.ignite.internal.schema.TupleAssembler;
-import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.AbstractSerializer;
import org.apache.ignite.internal.schema.marshaller.SerializationException;
-import org.apache.ignite.internal.schema.marshaller.Serializer;
+import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.schema.marshaller.MarshallerUtil.getValueSize;
/**
* Reflection based (de)serializer.
*/
-public class JavaSerializer implements Serializer {
-
- /**
- * Reads value object from tuple.
- *
- * @param reader Reader.
- * @param colIdx Column index.
- * @param mode Binary read mode.
- * @return Read value object.
- */
- static Object readRefValue(Tuple reader, int colIdx, BinaryMode mode) {
- assert reader != null;
- assert colIdx >= 0;
-
- Object val = null;
-
- switch (mode) {
- case BYTE:
- val = reader.byteValueBoxed(colIdx);
-
- break;
-
- case SHORT:
- val = reader.shortValueBoxed(colIdx);
-
- break;
-
- case INT:
- val = reader.intValueBoxed(colIdx);
-
- break;
-
- case LONG:
- val = reader.longValueBoxed(colIdx);
-
- break;
-
- case FLOAT:
- val = reader.floatValueBoxed(colIdx);
-
- break;
-
- case DOUBLE:
- val = reader.doubleValueBoxed(colIdx);
-
- break;
-
- case STRING:
- val = reader.stringValue(colIdx);
-
- break;
-
- case UUID:
- val = reader.uuidValue(colIdx);
-
- break;
-
- case BYTE_ARR:
- val = reader.bytesValue(colIdx);
-
- break;
-
- case BITSET:
- val = reader.bitmaskValue(colIdx);
-
- break;
-
- default:
- assert false : "Invalid mode: " + mode;
- }
-
- return val;
- }
-
- /**
- * Writes reference value to tuple.
- *
- * @param val Value object.
- * @param writer Writer.
- * @param mode Write binary mode.
- */
- static void writeRefObject(Object val, TupleAssembler writer, BinaryMode mode) {
- assert writer != null;
-
- if (val == null) {
- writer.appendNull();
-
- return;
- }
-
- switch (mode) {
- case BYTE:
- writer.appendByte((Byte)val);
-
- break;
-
- case SHORT:
- writer.appendShort((Short)val);
-
- break;
-
- case INT:
- writer.appendInt((Integer)val);
-
- break;
-
- case LONG:
- writer.appendLong((Long)val);
-
- break;
-
- case FLOAT:
- writer.appendFloat((Float)val);
-
- break;
-
- case DOUBLE:
- writer.appendDouble((Double)val);
-
- break;
-
- case STRING:
- writer.appendString((String)val);
-
- break;
-
- case UUID:
- writer.appendUuid((UUID)val);
-
- break;
-
- case BYTE_ARR:
- writer.appendBytes((byte[])val);
-
- break;
-
- case BITSET:
- writer.appendBitmask((BitSet)val);
-
- break;
-
- default:
- assert false : "Invalid mode: " + mode;
- }
- }
-
- /** Schema. */
- private final SchemaDescriptor schema;
-
+public class JavaSerializer extends AbstractSerializer {
/** Key class. */
private final Class<?> keyClass;
@@ -202,7 +51,7 @@ public class JavaSerializer implements Serializer {
* @param valClass Value type.
*/
public JavaSerializer(SchemaDescriptor schema, Class<?> keyClass, Class<?> valClass) {
- this.schema = schema;
+ super(schema);
this.keyClass = keyClass;
this.valClass = valClass;
@@ -211,18 +60,13 @@ public class JavaSerializer implements Serializer {
}
/** {@inheritDoc} */
- @Override public byte[] serialize(Object key, Object val) throws SerializationException {
+ @Override protected byte[] serialize0(TupleAssembler asm, Object key, @Nullable Object val)
+ throws SerializationException {
assert keyClass.isInstance(key);
assert val == null || valClass.isInstance(val);
- final TupleAssembler asm = createAssembler(key, val);
-
keyMarsh.writeObject(key, asm);
-
- if (val != null)
- valMarsh.writeObject(val, asm);
- else
- assert false; // TODO: add tomstone support and remove assertion.
+ valMarsh.writeObject(val, asm);
return asm.build();
}
@@ -234,7 +78,7 @@ public class JavaSerializer implements Serializer {
* @param val Value object.
* @return Tuple assembler.
*/
- private TupleAssembler createAssembler(Object key, Object val) {
+ @Override protected TupleAssembler createAssembler(Object key, Object val) {
ObjectStatistic keyStat = collectObjectStats(schema.keyColumns(), keyMarsh, key);
ObjectStatistic valStat = collectObjectStats(schema.valueColumns(), valMarsh, val);
@@ -274,9 +118,7 @@ public class JavaSerializer implements Serializer {
}
/** {@inheritDoc} */
- @Override public Object deserializeKey(byte[] data) throws SerializationException {
- final Tuple tuple = new ByteBufferTuple(schema, data);
-
+ @Override protected Object deserializeKey0(Tuple tuple) throws SerializationException {
final Object o = keyMarsh.readObject(tuple);
assert keyClass.isInstance(o);
@@ -285,14 +127,10 @@ public class JavaSerializer implements Serializer {
}
/** {@inheritDoc} */
- @Override public Object deserializeValue(byte[] data) throws SerializationException {
- final Tuple tuple = new ByteBufferTuple(schema, data);
-
- // TODO: add tomstone support.
-
+ @Override protected Object deserializeValue0(Tuple tuple) throws SerializationException {
final Object o = valMarsh.readObject(tuple);
- assert valClass.isInstance(o);
+ assert o == null || valClass.isInstance(o);
return o;
}
@@ -308,7 +146,7 @@ public class JavaSerializer implements Serializer {
int nonNullFieldsSize;
/** Constructor. */
- public ObjectStatistic(int nonNullFields, int nonNullFieldsSize) {
+ 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/reflection/Marshaller.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
index 506c3a3..49d3ee4 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
@@ -32,7 +32,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Marshaller.
*/
-public class Marshaller {
+class Marshaller {
/**
* Creates marshaller for class.
*
@@ -41,7 +41,7 @@ public class Marshaller {
* @param aClass Type.
* @return Marshaller.
*/
- public static Marshaller createMarshaller(Columns cols, int firstColId, Class<? extends Object> aClass) {
+ static Marshaller createMarshaller(Columns cols, int firstColId, Class<? extends Object> aClass) {
final BinaryMode mode = MarshallerUtil.mode(aClass);
if (mode != null) {
@@ -51,17 +51,17 @@ public class Marshaller {
assert mode.typeSpec() == col.type().spec() : "Target type is not compatible.";
assert !aClass.isPrimitive() : "Non-nullable types are not allowed.";
- return new Marshaller(UnsafeFieldAccessor.createIdentityAccessor(col, firstColId, mode));
+ return new Marshaller(FieldAccessor.createIdentityAccessor(col, firstColId, mode));
}
- UnsafeFieldAccessor[] fieldAccessors = new UnsafeFieldAccessor[cols.length()];
+ 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] = UnsafeFieldAccessor.create(aClass, col, colIdx);
+ fieldAccessors[i] = FieldAccessor.create(aClass, col, colIdx);
}
return new Marshaller(new ObjectFactory<>(aClass), fieldAccessors);
@@ -71,7 +71,7 @@ public class Marshaller {
* Field accessors for mapped columns.
* Array has same size and order as columns.
*/
- private final UnsafeFieldAccessor[] fieldAccessors;
+ private final FieldAccessor[] fieldAccessors;
/**
* Object factory for complex types or {@code null} for basic type.
@@ -86,7 +86,7 @@ public class Marshaller {
* @param fieldAccessors Object field accessors for mapped columns.
*/
@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
- public Marshaller(Factory<?> factory, UnsafeFieldAccessor[] fieldAccessors) {
+ Marshaller(Factory<?> factory, FieldAccessor[] fieldAccessors) {
this.fieldAccessors = fieldAccessors;
this.factory = Objects.requireNonNull(factory);
}
@@ -97,8 +97,8 @@ public class Marshaller {
*
* @param fieldAccessor Identity field accessor for object of basic type.
*/
- public Marshaller(UnsafeFieldAccessor fieldAccessor) {
- fieldAccessors = new UnsafeFieldAccessor[] {fieldAccessor};
+ public Marshaller(FieldAccessor fieldAccessor) {
+ fieldAccessors = new FieldAccessor[] {fieldAccessor};
factory = null;
}
@@ -121,13 +121,13 @@ public class Marshaller {
* @throws SerializationException If failed.
*/
public Object readObject(Tuple reader) throws SerializationException {
- if (isBasicTypeMarshaller())
+ if (isSimpleTypeMarshaller())
return fieldAccessors[0].read(reader);
final Object obj = factory.create();
for (int fldIdx = 0; fldIdx < fieldAccessors.length; fldIdx++)
- fieldAccessors[fldIdx].read(obj, reader);
+ fieldAccessors[fldIdx].read(reader, obj);
return obj;
}
@@ -141,13 +141,13 @@ public class Marshaller {
*/
public void writeObject(Object obj, TupleAssembler writer) throws SerializationException {
for (int fldIdx = 0; fldIdx < fieldAccessors.length; fldIdx++)
- fieldAccessors[fldIdx].write(obj, writer);
+ fieldAccessors[fldIdx].write(writer, obj);
}
/**
- * @return {@code true} if it is marshaller for basic type, {@code false} otherwise.
+ * @return {@code true} if it is marshaller for simple type, {@code false} otherwise.
*/
- private boolean isBasicTypeMarshaller() {
+ private boolean isSimpleTypeMarshaller() {
return factory == null;
}
}
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
deleted file mode 100644
index 74ea3a3..0000000
--- a/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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.util;
-
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import sun.misc.Unsafe;
-
-/**
- * Unsafe utility.
- */
-//TODO Move class to 'java-8' profile. Java9+ should use varhandles instead.
-public final class IgniteUnsafeUtils {
- /** Unsafe. */
- private static final Unsafe UNSAFE = unsafe();
-
- /**
- * @return Instance of Unsafe class.
- */
- private static Unsafe unsafe() {
- try {
- return Unsafe.getUnsafe();
- }
- catch (SecurityException ignored) {
- try {
- return AccessController.doPrivileged(
- new PrivilegedExceptionAction<Unsafe>() {
- @Override public Unsafe run() throws Exception {
- Field f = Unsafe.class.getDeclaredField("theUnsafe");
-
- f.setAccessible(true);
-
- return (Unsafe)f.get(null);
- }
- });
- }
- catch (PrivilegedActionException e) {
- throw new RuntimeException("Could not initialize intrinsics.", e.getCause());
- }
- }
- }
-
- /**
- * 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);
- }
-
- /**
- * Stub.
- */
- private IgniteUnsafeUtils() {
- }
-}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java
index f31965f..30f9365 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java
@@ -35,7 +35,6 @@ public class ObjectFactory<T> implements Factory<T> {
public ObjectFactory(Class<T> tClass) {
try {
cnstr = tClass.getDeclaredConstructor();
-
cnstr.setAccessible(true);
}
catch (NoSuchMethodException e) {
@@ -52,11 +51,4 @@ public class ObjectFactory<T> implements Factory<T> {
throw new IllegalStateException("Failed to instantiate class: " + cnstr.getDeclaringClass().getName(), e);
}
}
-
- /**
- * @return Class of object created by the factory.
- */
- public Class<T> getClazz() {
- return cnstr.getDeclaringClass();
- }
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/lang/IgniteExperimental.java b/modules/commons/src/main/java/org/apache/ignite/lang/IgniteExperimental.java
new file mode 100644
index 0000000..c68fe14
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/lang/IgniteExperimental.java
@@ -0,0 +1,38 @@
+/*
+ * 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.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+
+/**
+ * This annotation marks API elements (such as interfaces, methods, annotations and whole API packages) as experimental
+ * meaning that the API is not finalized yet and may be changed or replaced in future Ignite releases.
+ * <p>
+ * Such APIs are exposed so that users can make use of a feature before the API has been stabilized. The expectation is
+ * that an API element should be "eventually" stabilized. Incompatible changes are allowed for such APIs: API may be
+ * removed, changed or stabilized in future Ignite releases (both minor and maintenance).
+ */
+@Target(value = {TYPE, METHOD, ANNOTATION_TYPE, PACKAGE, FIELD})
+public @interface IgniteExperimental {
+}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
index 23193ba..b030a4f 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
@@ -17,18 +17,21 @@
package org.apache.ignite.internal.benchmarks;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
import java.lang.reflect.Field;
import java.util.Random;
import java.util.concurrent.TimeUnit;
+import javax.lang.model.element.Modifier;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.Columns;
import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.marshaller.CompilerUtils;
import org.apache.ignite.internal.schema.marshaller.Serializer;
import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
import org.apache.ignite.internal.util.Factory;
import org.apache.ignite.internal.util.ObjectFactory;
-import org.codehaus.commons.compiler.CompilerFactoryFactory;
-import org.codehaus.commons.compiler.IClassBodyEvaluator;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@@ -56,7 +59,7 @@ import static org.apache.ignite.internal.schema.NativeType.LONG;
@Measurement(time = 10, iterations = 5, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
@OutputTimeUnit(TimeUnit.MICROSECONDS)
-@Fork(1)
+@Fork(jvmArgs = "-Djava.lang.invoke.stringConcat=BC_SB" /* Workaround for Java 9+ */, value = 1)
public class SerializerBenchmarkTest {
/** Random. */
private Random rnd = new Random();
@@ -72,7 +75,7 @@ public class SerializerBenchmarkTest {
public int fieldsCount;
/** Serializer. */
- @Param({"Janino", "Java"})
+ @Param({"Generated"/*, "Java"*/})
public String serializerName;
/**
@@ -91,6 +94,8 @@ public class SerializerBenchmarkTest {
*/
@Setup
public void init() throws Exception {
+ Thread.currentThread().setContextClassLoader(CompilerUtils.dynamicClassLoader());
+
long seed = System.currentTimeMillis();
rnd = new Random(seed);
@@ -105,7 +110,7 @@ public class SerializerBenchmarkTest {
if ("Java".equals(serializerName))
serializer = SerializerFactory.createJavaSerializerFactory().create(schema, Long.class, valClass);
else
- serializer = SerializerFactory.createJaninoSerializerFactory().create(schema, Long.class, valClass);
+ serializer = SerializerFactory.createGeneratedSerializerFactory().create(schema, Long.class, valClass);
}
/**
@@ -155,28 +160,32 @@ public class SerializerBenchmarkTest {
* @return Generated test object class.
* @throws Exception If failed.
*/
- private Class<?> createGeneratedObjectClass(int maxFields, Class<?> fieldType) throws Exception {
- final IClassBodyEvaluator ce = CompilerFactoryFactory.getDefaultCompilerFactory().newClassBodyEvaluator();
+ private Class<?> createGeneratedObjectClass(int maxFields, Class<?> fieldType) {
+ final String packageName = "org.apache.ignite.internal.benchmarks";
+ final String className = "TestObject";
- ce.setClassName("TestObject");
- ce.setDefaultImports("java.util.Random");
+ final TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
- final StringBuilder sb = new StringBuilder();
for (int i = 0; i < maxFields; i++)
- sb.append(fieldType.getName()).append(" col").append(i).append(";\n");
+ classBuilder.addField(fieldType, "col" + i, Modifier.PRIVATE);
- // Constructor.
- sb.append("public TestObject() {\n");
- sb.append(" Random rnd = new Random();\n");
- for (int i = 0; i < maxFields; i++)
- sb.append(" col").append(i).append(" = rnd.nextLong();\n");
- sb.append("}\n");
+ { // Build constructor.
+ final MethodSpec.Builder builder = MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("$T rnd = new $T()", Random.class, Random.class);
- try {
- ce.setParentClassLoader(getClass().getClassLoader());
- ce.cook(sb.toString());
+ for (int i = 0; i < maxFields; i++)
+ builder.addStatement("col$L = rnd.nextLong()", i);
+
+ classBuilder.addMethod(builder.build());
+ }
+
+ final JavaFile javaFile = JavaFile.builder(packageName, classBuilder.build()).build();
- return ce.getClazz();
+ final ClassLoader loader = CompilerUtils.compileCode(javaFile);
+
+ try {
+ return loader.loadClass(packageName + '.' + className);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to compile/instantiate generated Serializer.", ex);
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 49ef554..7b41412 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
@@ -17,6 +17,9 @@
package org.apache.ignite.internal.schema.marshaller;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;
@@ -24,6 +27,7 @@ import java.util.Objects;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Stream;
+import javax.lang.model.element.Modifier;
import org.apache.ignite.internal.schema.Bitmask;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.Columns;
@@ -31,8 +35,9 @@ import org.apache.ignite.internal.schema.NativeType;
import org.apache.ignite.internal.schema.NativeTypeSpec;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.TestUtils;
-import org.apache.ignite.internal.schema.marshaller.generator.JaninoSerializerGenerator;
+import org.apache.ignite.internal.schema.marshaller.generator.SerializerGenerator;
import org.apache.ignite.internal.schema.marshaller.reflection.JavaSerializerFactory;
+import org.apache.ignite.internal.util.ObjectFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.TestFactory;
@@ -64,7 +69,7 @@ public class JavaSerializerTest {
*/
private static List<SerializerFactory> serializerFactoryProvider() {
return Arrays.asList(
- new JaninoSerializerGenerator(),
+ new SerializerGenerator(),
new JavaSerializerFactory()
);
}
@@ -245,7 +250,7 @@ public class JavaSerializerTest {
*/
@ParameterizedTest
@MethodSource("serializerFactoryProvider")
- public void testClassWithNoDefaultConstructor(SerializerFactory factory) throws SerializationException {
+ public void testClassWithNoDefaultConstructor(SerializerFactory factory) {
Column[] cols = new Column[] {
new Column("pLongCol", LONG, false),
};
@@ -258,7 +263,49 @@ public class JavaSerializerTest {
assertThrows(IllegalStateException.class,
() -> factory.create(schema, key.getClass(), val.getClass()),
"Class has no default constructor: class=org.apache.ignite.internal.schema.marshaller.JavaSerializerTest$WrongTestObject"
- );
+ );
+ }
+
+ /**
+ *
+ */
+ @ParameterizedTest
+ @MethodSource("serializerFactoryProvider")
+ public void testClassLoader(SerializerFactory factory) throws SerializationException {
+ final ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(CompilerUtils.dynamicClassLoader());
+
+ Column[] keyCols = new Column[] {
+ new Column("key", LONG, false)
+ };
+
+ Column[] valCols = new Column[] {
+ new Column("col0", LONG, false),
+ new Column("col1", LONG, false),
+ new Column("col2", LONG, false),
+ };
+
+ SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(keyCols), new Columns(valCols));
+
+ final Class<?> valClass = createGeneratedObjectClass(Long.class);
+ final ObjectFactory<?> objFactory = new ObjectFactory<>(valClass);
+
+ final Long key = rnd.nextLong();
+
+ Serializer serializer = factory.create(schema, key.getClass(), valClass);
+
+ byte[] bytes = serializer.serialize(key, objFactory.create());
+
+ Object key1 = serializer.deserializeKey(bytes);
+ Object val1 = serializer.deserializeValue(bytes);
+
+ assertTrue(key.getClass().isInstance(key1));
+ assertTrue(valClass.isInstance(val1));
+ }
+ finally {
+ Thread.currentThread().setContextClassLoader(prevClassLoader);
+ }
}
/**
@@ -318,6 +365,42 @@ public class JavaSerializerTest {
}
/**
+ * Generate class for test objects.
+ *
+ * @param fieldType Field type.
+ * @return Generated test object class.
+ */
+ private Class<?> createGeneratedObjectClass(Class<?> fieldType) {
+ final String packageName = getClass().getPackageName();
+ final String className = "GeneratedTestObject";
+
+ final TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
+
+ for (int i = 0; i < 3; i++)
+ classBuilder.addField(fieldType, "col" + i, Modifier.PRIVATE);
+
+ { // Build constructor.
+ final MethodSpec.Builder builder = MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("$T rnd = new $T()", Random.class, Random.class);
+
+ for (int i = 0; i < 3; i++)
+ builder.addStatement("col$L = rnd.nextLong()", i);
+
+ classBuilder.addMethod(builder.build());
+ }
+
+ final JavaFile javaFile = JavaFile.builder(packageName, classBuilder.build()).build();
+
+ try {
+ return CompilerUtils.compileCode(javaFile).loadClass(packageName + '.' + className);
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException("Failed to compile/instantiate generated Serializer.", ex);
+ }
+ }
+
+ /**
* Test object.
*/
public static class TestObject {
@@ -340,7 +423,9 @@ public class JavaSerializerTest {
obj.longCol = rnd.nextLong();
obj.floatCol = rnd.nextFloat();
obj.doubleCol = rnd.nextDouble();
+ obj.nullLongCol = null;
+ obj.nullBytesCol = null;
obj.uuidCol = new UUID(rnd.nextLong(), rnd.nextLong());
obj.bitmaskCol = TestUtils.randomBitSet(rnd, 42);
obj.stringCol = TestUtils.randomString(rnd, rnd.nextInt(255));
@@ -411,7 +496,7 @@ public class JavaSerializerTest {
/**
* Test object with private constructor.
*/
- private static class PrivateTestObject {
+ public static class PrivateTestObject {
/**
* @return Random TestObject.
*/
@@ -429,7 +514,6 @@ public class JavaSerializerTest {
/**
* Private constructor.
*/
- @SuppressWarnings("RedundantNoArgConstructor")
private PrivateTestObject() {
}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java
index 8e864d6..00a05f5 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java
@@ -103,17 +103,17 @@ public class FieldAccessorTest {
final TestObject obj = TestObject.randomObject(rnd);
for (int i = 0; i < cols.length; i++) {
- UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestObject.class, cols[i], i);
+ FieldAccessor accessor = FieldAccessor.create(TestObject.class, cols[i], i);
- accessor.write(obj, tupleAssembler);
+ accessor.write(tupleAssembler, obj);
}
final TestObject restoredObj = new TestObject();
for (int i = 0; i < cols.length; i++) {
- UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestObject.class, cols[i], i);
+ FieldAccessor accessor = FieldAccessor.create(TestObject.class, cols[i], i);
- accessor.read(restoredObj, tuple);
+ accessor.read(tuple, restoredObj);
}
assertEquals(obj.pByteCol, restoredObj.pByteCol);
@@ -159,17 +159,17 @@ public class FieldAccessorTest {
obj.stringCol = TestUtils.randomString(rnd, 255);
for (int i = 0; i < cols.length; i++) {
- UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestSimpleObject.class, cols[i], i);
+ FieldAccessor accessor = FieldAccessor.create(TestSimpleObject.class, cols[i], i);
- accessor.write(obj, tupleAssembler);
+ accessor.write(tupleAssembler, obj);
}
final TestSimpleObject restoredObj = new TestSimpleObject();
for (int i = 0; i < cols.length; i++) {
- UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestSimpleObject.class, cols[i], i);
+ FieldAccessor accessor = FieldAccessor.create(TestSimpleObject.class, cols[i], i);
- accessor.read(restoredObj, tuple);
+ accessor.read(tuple, restoredObj);
}
assertEquals(obj.intCol, restoredObj.intCol);
@@ -184,7 +184,7 @@ public class FieldAccessorTest {
*/
@Test
public void testIdentityAccessor() throws Exception {
- final UnsafeFieldAccessor accessor = UnsafeFieldAccessor.createIdentityAccessor(
+ final FieldAccessor accessor = FieldAccessor.createIdentityAccessor(
new Column("col0", STRING, true),
0,
BinaryMode.STRING);
@@ -193,7 +193,7 @@ public class FieldAccessorTest {
final Pair<TupleAssembler, Tuple> mocks = createMocks();
- accessor.write("Other string", mocks.getFirst());
+ accessor.write(mocks.getFirst(), "Other string");
assertEquals("Other string", accessor.read(mocks.getSecond()));
}
@@ -202,7 +202,7 @@ public class FieldAccessorTest {
*/
@Test
public void testWrongIdentityAccessor() throws Exception {
- final UnsafeFieldAccessor accessor = UnsafeFieldAccessor.createIdentityAccessor(
+ final FieldAccessor accessor = FieldAccessor.createIdentityAccessor(
new Column("col0", STRING, true),
42,
BinaryMode.UUID);
@@ -213,7 +213,7 @@ public class FieldAccessorTest {
assertThrows(
SerializationException.class,
- () -> accessor.write("Other string", mocks.getFirst()),
+ () -> accessor.write(mocks.getFirst(), "Other string"),
"Failed to write field [id=42]"
);
}
@@ -242,7 +242,7 @@ public class FieldAccessorTest {
final Answer<Object> tupleAnswer = new Answer<Object>() {
@Override public Object answer(InvocationOnMock invocation) {
- final int idx = invocation.getArgumentAt(0, Integer.class);
+ final int idx = invocation.getArgument(0, Integer.class);
return vals.get(idx);
}
diff --git a/pom.xml b/pom.xml
index 2d460d4..73e6a1c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,14 +49,16 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Dependencies versions. -->
- <janino.version>3.0.11</janino.version>
+ <javapoet.version>1.13.0</javapoet.version>
<javax.annotation.api.version>1.3.2</javax.annotation.api.version>
<jetbrains.annotations.version>20.1.0</jetbrains.annotations.version>
<jmh.framework.verion>1.9.3</jmh.framework.verion>
<junit.jupiter.version>5.7.0</junit.jupiter.version>
- <mockito.version>1.10.19</mockito.version>
+ <mockito.core.version>3.6.28</mockito.core.version>
<!-- Maven plugins -->
+ <maven.compiler.release>11</maven.compiler.release>
+
<maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.0.0-M4</maven.surefire.plugin.version>
<apache.rat.plugin.version>0.13</apache.rat.plugin.version>
@@ -141,9 +143,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
- <configuration>
- <release>11</release>
- </configuration>
</plugin>
<plugin>