Record processing backport (#531)

Record processing backport

Signed-off-by: David Kral <david.k.kral@oracle.com>
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 0000000..6019b64
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+name: Yasson
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: Test on JDK ${{ matrix.java_version }}
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        java_version: [ 11, 17 ]
+
+    steps:
+      - name: Checkout for build
+        uses: actions/checkout@v2.3.4
+        with:
+          fetch-depth: 0
+      - name: Set up compile JDK
+        uses: actions/setup-java@v2
+        with: #Compile java needs to be the highest to ensure proper compilation of the multi-release jar
+          distribution: 'adopt'
+          java-version: 17
+      - name: Maven cache
+        uses: actions/cache@v2
+        env:
+          cache-name: maven-cache
+        with:
+          path:
+            ~/.m2
+          key: build-${{ env.cache-name }}
+      - name: Copyright
+        run: bash etc/copyright.sh
+      - name: Checkstyle
+        run: mvn -B -Pstaging checkstyle:checkstyle
+      - name: Yasson install
+        run: mvn -U -C -Pstaging clean install -DskipTests
+      - name: Set up JDK for tests
+        uses: actions/setup-java@v2
+        with:
+          distribution: 'adopt'
+          java-version: ${{ matrix.java_version }}
+      - name: Yasson tests
+        run: mvn -U -C -Dmaven.javadoc.skip=true -Pstaging verify
diff --git a/pom.xml b/pom.xml
index 385d30e..3baa8a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0 which is available at
@@ -36,6 +36,7 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <jakarta.json.version>1.1.6</jakarta.json.version>
         <jakarta.json.bind.version>1.0.2</jakarta.json.bind.version>
+        <jakarta.enterprise.cdi-api.version>2.0.2</jakarta.enterprise.cdi-api.version>
         <netbeans.hint.jdkPlatform>JDK_9</netbeans.hint.jdkPlatform>
     </properties>
 
@@ -62,7 +63,7 @@
         <dependency>
             <groupId>jakarta.enterprise</groupId>
             <artifactId>jakarta.enterprise.cdi-api</artifactId>
-            <version>2.0.2</version>
+            <version>${jakarta.enterprise.cdi-api.version}</version>
             <optional>true</optional>
             <scope>provided</scope>
         </dependency>
@@ -222,6 +223,50 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>jdk16</id>
+            <activation>
+                <jdk>[16,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <version>3.8.1</version>
+                        <executions>
+                            <execution>
+                                <id>default-testCompile</id>
+                                <configuration>
+                                    <release>16</release>
+                                    <compileSourceRoots>
+                                        <compileSourceRoot>${project.basedir}/src/test/java</compileSourceRoot>
+                                        <compileSourceRoot>${project.basedir}/src/test/java16</compileSourceRoot>
+                                    </compileSourceRoots>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <includes>
+                                <include>**/RecordTest.java</include>
+                            </includes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
 
     <build>
@@ -255,6 +300,11 @@
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>3.0.0-M3</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
                     <version>3.8.1</version>
                     <executions>
@@ -272,6 +322,19 @@
                             </configuration>
                         </execution>
                         <execution>
+                            <id>multi-release-compile-16</id>
+                            <goals>
+                                <goal>compile</goal>
+                            </goals>
+                            <configuration>
+                                <release>16</release>
+                                <compileSourceRoots>
+                                    <compileSourceRoot>${project.basedir}/src/main/java16</compileSourceRoot>
+                                </compileSourceRoots>
+                                <multiReleaseOutput>true</multiReleaseOutput>
+                            </configuration>
+                        </execution>
+                        <execution>
                             <id>base-compile</id>
                             <goals>
                                 <goal>compile</goal>
@@ -305,6 +368,9 @@
                     <configuration>
                         <archive>
                             <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                            <manifestEntries>
+                                <Multi-Release>true</Multi-Release>
+                            </manifestEntries>
                         </archive>
                     </configuration>
                 </plugin>
@@ -366,7 +432,7 @@
                 <plugin>
                     <groupId>org.apache.felix</groupId>
                     <artifactId>maven-bundle-plugin</artifactId>
-                    <version>3.5.0</version>
+                    <version>5.1.1</version>
                     <executions>
                         <execution>
                             <id>osgi-bundle</id>
@@ -375,13 +441,14 @@
                                 <goal>manifest</goal>
                             </goals>
                             <configuration>
+                                <niceManifest>true</niceManifest>
                                 <instructions>
-                                    <_failok>true</_failok>
                                     <Bundle-Name>${project.name}</Bundle-Name>
                                     <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
                                     <Bundle-Version>${project.version}</Bundle-Version>
                                     <Export-Package>org.eclipse.yasson;version=${project.version}</Export-Package>
                                     <Private-Package>org.eclipse.yasson.*;version=${project.version}</Private-Package>
+                                    <Multi-Release>true</Multi-Release>
                                     <Import-Package>
                                         javax.enterprise.context.spi;version=!;resolution:="optional",
                                         javax.enterprise.inject.spi;version=!;resolution:="optional",
@@ -396,6 +463,7 @@
                     </executions>
                 </plugin>
                 <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
                     <version>3.0.0-M3</version>
                     <executions>
@@ -410,12 +478,14 @@
                                 <excludes>
                                     <exclude>**/JavaxNamingExcludedTest.class</exclude>
                                     <exclude>**/AnnotationIntrospectorWithoutOptionalModulesTest.class</exclude>
+                                    <exclude>**/*Record*</exclude>
                                 </excludes>
                                 <argLine>
                                     <!--Remove when CDI is updated to support modules
                                     (Yasson does read CDI which is on CP, CDI access yasson's classes with reflection)
                                     -->
                                     --add-reads org.eclipse.yasson=ALL-UNNAMED --add-opens org.eclipse.yasson/org.eclipse.yasson.internal.cdi=ALL-UNNAMED
+                                    --add-opens=java.base/java.lang=ALL-UNNAMED
 
                                     --add-exports org.eclipse.yasson/org.eclipse.yasson.internal.cdi=java.naming
                                 </argLine>
diff --git a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java
index d10b5a1..a0ab13f 100644
--- a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java
+++ b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -175,12 +175,15 @@
             }
         }
         if (jsonbCreator == null) {
-            jsonbCreator = constructorPropertiesIntrospector.getCreator(declaredConstructors);
+            jsonbCreator = ClassMultiReleaseExtension.findCreator(clazz, declaredConstructors, this);
+            if (jsonbCreator == null) {
+                jsonbCreator = constructorPropertiesIntrospector.getCreator(declaredConstructors);
+            }
         }
         return jsonbCreator;
     }
 
-    private JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Class<?> clazz) {
+    JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Class<?> clazz) {
         if (existing != null) {
             throw new JsonbException(Messages.getMessage(MessageKeys.MULTIPLE_JSONB_CREATORS, clazz));
         }
diff --git a/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java b/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
new file mode 100644
index 0000000..c7adaa1
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.Property;
+
+/**
+ * Search for instance creator from other sources.
+ * Mainly intended to add extensibility for different java versions and new features.
+ */
+public class ClassMultiReleaseExtension {
+
+    private ClassMultiReleaseExtension() {
+        throw new IllegalStateException("This class cannot be instantiated");
+    }
+
+    static boolean shouldTransformToPropertyName(Method method) {
+        return true;
+    }
+
+    static boolean isSpecialAccessorMethod(Method method, Map<String, Property> classProperties) {
+        return false;
+    }
+
+    static JsonbCreator findCreator(Class<?> clazz,
+                                    Constructor<?>[] declaredConstructors,
+                                    AnnotationIntrospector introspector) {
+        return null;
+    }
+
+    public static Optional<JsonbException> exceptionToThrow(Class<?> clazz) {
+        return Optional.empty();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ClassParser.java b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
index 57c7617..8b0114d 100644
--- a/src/main/java/org/eclipse/yasson/internal/ClassParser.java
+++ b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -192,10 +192,14 @@
         for (Method method : declaredMethods) {
             String name = method.getName();
             //isBridge method filters out methods inherited from interfaces
-            if (!isPropertyMethod(method) || method.isBridge() || isSpecialCaseMethod(clazz, method)) {
+            boolean isAccessorMethod = ClassMultiReleaseExtension.isSpecialAccessorMethod(method, classProperties)
+                    || isPropertyMethod(method);
+            if (!isAccessorMethod || method.isBridge() || isSpecialCaseMethod(clazz, method)) {
                 continue;
             }
-            final String propertyName = toPropertyMethod(name);
+            final String propertyName = ClassMultiReleaseExtension.shouldTransformToPropertyName(method)
+                    ? toPropertyMethod(name)
+                    : name;
 
             registerMethod(propertyName, method, classElement, classProperties);
         }
diff --git a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
index df49725..ca78328 100644
--- a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
+++ b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -61,6 +61,10 @@
      */
     NO_DEFAULT_CONSTRUCTOR("noDefaultConstructor"),
     /**
+     * Class does not have default constructor.
+     */
+    RECORD_MULTIPLE_CONSTRUCTORS("recordMultipleConstructors"),
+    /**
      * There has been an error while invoking getter.
      */
     INVOKING_GETTER("invokingGetter"),
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java
index 8b155c8..38c7ddf 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectDeserializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -23,6 +23,7 @@
 import javax.json.bind.serializer.JsonbDeserializer;
 import javax.json.stream.JsonParser;
 
+import org.eclipse.yasson.internal.ClassMultiReleaseExtension;
 import org.eclipse.yasson.internal.JsonbContext;
 import org.eclipse.yasson.internal.JsonbParser;
 import org.eclipse.yasson.internal.JsonbRiParser;
@@ -98,7 +99,8 @@
         } else {
             Constructor<T> defaultConstructor = (Constructor<T>) getClassModel().getDefaultConstructor();
             if (defaultConstructor == null) {
-                throw new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, rawType));
+                throw ClassMultiReleaseExtension.exceptionToThrow(rawType)
+                        .orElse(new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, rawType)));
             }
             instance = ReflectionUtils.createNoArgConstructorInstance(defaultConstructor);
         }
diff --git a/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java b/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
new file mode 100644
index 0000000..d62e7fb
--- /dev/null
+++ b/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.Property;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Search for instance creator from other sources.
+ * Mainly intended to add extensibility for different java versions and new features.
+ */
+public class ClassMultiReleaseExtension {
+
+    private ClassMultiReleaseExtension() {
+        throw new IllegalStateException("This class cannot be instantiated");
+    }
+
+    static boolean shouldTransformToPropertyName(Method method) {
+        return !method.getDeclaringClass().isRecord();
+    }
+
+    static boolean isSpecialAccessorMethod(Method method, Map<String, Property> classProperties) {
+        return method.getDeclaringClass().isRecord()
+                && method.getParameterCount() == 0
+                && !void.class.equals(method.getReturnType())
+                && classProperties.containsKey(method.getName());
+    }
+
+    static JsonbCreator findCreator(Class<?> clazz,
+                                    Constructor<?>[] declaredConstructors,
+                                    AnnotationIntrospector introspector) {
+        if (clazz.isRecord()) {
+            if (declaredConstructors.length == 1) {
+                return introspector.createJsonbCreator(declaredConstructors[0], null, clazz);
+            }
+        }
+        return null;
+    }
+
+    public static Optional<JsonbException> exceptionToThrow(Class<?> clazz) {
+        if (clazz.isRecord()) {
+            if (clazz.getDeclaredConstructors().length > 1) {
+                return Optional.of(new JsonbException(Messages.getMessage(MessageKeys.RECORD_MULTIPLE_CONSTRUCTORS, clazz)));
+            }
+        }
+        return Optional.empty();
+    }
+
+}
diff --git a/src/main/resources/yasson-messages.properties b/src/main/resources/yasson-messages.properties
index aeca59a..6629b3f 100644
--- a/src/main/resources/yasson-messages.properties
+++ b/src/main/resources/yasson-messages.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2022 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0 which is available at
@@ -50,6 +50,7 @@
 errorParsingDate=Error parsing {1} from value: {0}. Check your @JsonbDateFormat has all time units for {1} type, \
   or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING.
 noDefaultConstructor=Cannot create instance of a class: {0}, No default constructor found.
+recordMultipleConstructors=Cannot create instance of a record: {0}, Multiple constructors found.
 offsetDateTimeFromMillis=Parsing {0} from epoch millisecond, UTC zone offset will be used.
 timeToEpochMillisError=Cannot convert {0} to/from epoch milliseconds.
 configPropertyInvalidType=JsonbConfig property [{0}] must be of type [{1}].
diff --git a/src/test/java16/org/eclipse/yasson/records/Car.java b/src/test/java16/org/eclipse/yasson/records/Car.java
new file mode 100644
index 0000000..16a18fe
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/Car.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import javax.json.bind.annotation.JsonbProperty;
+
+public record Car(@JsonbProperty("typeChanged") String type, @JsonbProperty("colorChanged") String color) {
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/CarWithCreator.java b/src/test/java16/org/eclipse/yasson/records/CarWithCreator.java
new file mode 100644
index 0000000..281fcfb
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/CarWithCreator.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import javax.json.bind.annotation.JsonbCreator;
+import javax.json.bind.annotation.JsonbProperty;
+
+public record CarWithCreator(String type, String color) {
+
+    @JsonbCreator
+    public static CarWithCreator create(@JsonbProperty("type") String type, @JsonbProperty("color") String color) {
+        return new CarWithCreator(type, color);
+    }
+
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/CarWithExtraMethod.java b/src/test/java16/org/eclipse/yasson/records/CarWithExtraMethod.java
new file mode 100644
index 0000000..685a237
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/CarWithExtraMethod.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithExtraMethod(String type, String color) {
+
+    public String type() {
+        return type;
+    }
+
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/CarWithMultipleConstructors.java b/src/test/java16/org/eclipse/yasson/records/CarWithMultipleConstructors.java
new file mode 100644
index 0000000..39f6ef4
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/CarWithMultipleConstructors.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithMultipleConstructors(String type, String color) {
+
+    public CarWithMultipleConstructors(String type) {
+        this(type, "red");
+    }
+
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java b/src/test/java16/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java
new file mode 100644
index 0000000..42bea50
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import javax.json.bind.annotation.JsonbCreator;
+import javax.json.bind.annotation.JsonbProperty;
+
+public record CarWithMultipleConstructorsAndCreator(String type, String color) {
+
+    @JsonbCreator
+    public CarWithMultipleConstructorsAndCreator(@JsonbProperty("type") String type) {
+        this(type, "red");
+    }
+
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/CarWithoutAnnotations.java b/src/test/java16/org/eclipse/yasson/records/CarWithoutAnnotations.java
new file mode 100644
index 0000000..4ded5f7
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/CarWithoutAnnotations.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithoutAnnotations(String type, String color) {
+}
diff --git a/src/test/java16/org/eclipse/yasson/records/RecordTest.java b/src/test/java16/org/eclipse/yasson/records/RecordTest.java
new file mode 100644
index 0000000..a7c3e97
--- /dev/null
+++ b/src/test/java16/org/eclipse/yasson/records/RecordTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import javax.json.bind.JsonbException;
+
+import org.eclipse.yasson.Jsonbs;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class RecordTest {
+
+    @Test
+    public void testRecordProcessing() {
+        Car car = new Car("skoda", "green");
+        String expected = "{\"colorChanged\":\"green\",\"typeChanged\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        Car deserialized = Jsonbs.defaultJsonb.fromJson(expected, Car.class);
+        assertEquals(car, deserialized);
+    }
+
+    @Test
+    public void testRecordProcessingWithoutJsonbProperties() {
+        CarWithoutAnnotations car = new CarWithoutAnnotations("skoda", "green");
+        String expected = "{\"color\":\"green\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        CarWithoutAnnotations deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithoutAnnotations.class);
+        assertEquals(car, deserialized);
+    }
+
+    @Test
+    public void testRecordProcessingWithExtraMethod() {
+        CarWithExtraMethod car = new CarWithExtraMethod("skoda", "green");
+        String expected = "{\"color\":\"green\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        CarWithExtraMethod deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithExtraMethod.class);
+        assertEquals(car, deserialized);
+    }
+
+    @Test
+    public void testRecordMultipleConstructors() {
+        CarWithMultipleConstructors car = new CarWithMultipleConstructors("skoda");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        JsonbException jsonbException = assertThrows(JsonbException.class,
+                                                     () -> Jsonbs.defaultJsonb.fromJson(expected,
+                                                                                        CarWithMultipleConstructors.class));
+        String expectedMessage = Messages.getMessage(MessageKeys.RECORD_MULTIPLE_CONSTRUCTORS, CarWithMultipleConstructors.class);
+        assertEquals(expectedMessage, jsonbException.getMessage());
+    }
+
+    @Test
+    public void testRecordMultipleConstructorsWithJsonbCreator() {
+        CarWithMultipleConstructorsAndCreator car = new CarWithMultipleConstructorsAndCreator("skoda");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        CarWithMultipleConstructorsAndCreator deserialized =  Jsonbs.defaultJsonb
+                .fromJson(expected, CarWithMultipleConstructorsAndCreator.class);
+        assertEquals(car, deserialized);
+    }
+
+    @Test
+    public void testRecordJsonbCreator() {
+        CarWithCreator car = new CarWithCreator("skoda", "red");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        CarWithCreator deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithCreator.class);
+        assertEquals(car, deserialized);
+    }
+
+}