You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2022/03/04 03:02:17 UTC

[tapestry-5] 05/05: TAP5-2700: changes to run tests on Java 17

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit f4b3e7a4979dc84e8486614ab2f3a6baa12a735a
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Tue Jan 18 23:48:53 2022 -0300

    TAP5-2700: changes to run tests on Java 17
    
    Adding JDK 17 testing in Travis
    
    TAP5-2700: allowing module classes written in Groovy again
    
    This fixes GridSymbolDemoTests
    
    TAP5-2700: fixing two broken tests
    
    GridSymbolDemoTests and XMLTokenStreamTests. testStreamEncoding
    
    TAP5-2700: trying to fix XMLTokenStreamTests.testStreamEncoding()
    
    TAP5-2700: fixing bad JavaVersion enum value for Java 9
    
    TAP5-2700: Tests covering new Java features from versions 9 to 17
    
    TAP5-2700: fixing failing tests in CookiesImplTest
    
    Fixing TapestryBeanValidationIntegrationTests > beaneditform_validation
    
    Trying to get ParameterTests.null_bound_to_primitive_field_is_an_error
    
    to pass in Jenkins
---
 build.gradle                                       |   5 +-
 tapestry-beanvalidator/build.gradle                |   6 +
 .../TapestryBeanValidationIntegrationTests.java    |   8 +
 tapestry-core/build.gradle                         |   3 +-
 .../integration/app1/ParameterTests.groovy         |   3 +
 .../symbolparam/GridSymbolDemoTests.groovy         |   4 +-
 .../internal/services/CookiesImplTest.java         |  24 ++-
 .../ioc/internal/DefaultModuleDefImpl.java         |  67 ++++++--
 tapestry-latest-java-tests/build.gradle            |  16 +-
 .../tapestry5/ioc/services/AndExpression.java      |  32 ++++
 .../apache/tapestry5/ioc/services/Appliance.java   |  33 ++++
 .../tapestry5/ioc/services/BinaryExpression.java   |  20 +++
 .../ioc/services/ConsolePortableHybrid.java        |  24 +++
 .../ioc/services/ConstantBinaryExpression.java     |  31 ++++
 .../ioc/services/ConstantIntExpression.java        |  24 +++
 .../tapestry5/ioc/services/CoolingMachine.java     |  23 +++
 .../org/apache/tapestry5/ioc/services/Freezer.java |  23 +++
 .../apache/tapestry5/ioc/services/HomeConsole.java |  24 +++
 .../tapestry5/ioc/services/IntExpression.java      |  20 +++
 .../apache/tapestry5/ioc/services/IntTuple.java    |  20 +++
 .../ioc/services/Java10And11ConcreteService.java   |  68 +++++++++
 .../ioc/services/Java10And11NewFeatureTests.java   |  54 +++++++
 .../tapestry5/ioc/services/Java10And11Service.java |  17 +++
 .../ioc/services/Java10And11ServiceImpl.java       |  69 +++++++++
 .../ioc/services/Java12And13ConcreteService.java   |  71 +++++++++
 .../ioc/services/Java12And13NewFeatureTests.java   |  69 +++++++++
 .../tapestry5/ioc/services/Java12And13Service.java |  28 ++++
 .../ioc/services/Java12And13ServiceImpl.java       |  69 +++++++++
 .../ioc/services/Java14ConcreteService.java        |  73 +++++++++
 .../ioc/services/Java14NewFeatureTests.java        | 101 ++++++++++++
 .../tapestry5/ioc/services/Java14Service.java      |  37 +++++
 .../tapestry5/ioc/services/Java14ServiceImpl.java  |  71 +++++++++
 .../ioc/services/Java15To17ConcreteService.java    |  63 ++++++++
 .../ioc/services/Java15To17NewFeatureTests.java    | 169 +++++++++++++++++++++
 .../tapestry5/ioc/services/Java15To17Service.java  |  34 +++++
 .../ioc/services/Java15To17ServiceImpl.java        |  66 ++++++++
 .../ioc/services/Java9ConcreteService.java         |  69 +++++++++
 .../ioc/services/Java9NewFeatureTests.java         |  60 ++++++++
 .../tapestry5/ioc/services/Java9Service.java       |  33 ++++
 .../tapestry5/ioc/services/Java9ServiceImpl.java   |  70 +++++++++
 .../tapestry5/ioc/services/OrExpression.java       |  32 ++++
 .../tapestry5/ioc/services/PlusExpression.java     |  24 +++
 .../tapestry5/ioc/services/Refrigerator.java       |  23 +++
 .../tapestry5/ioc/services/SquareExpression.java   |  24 +++
 .../apache/tapestry5/ioc/services/Videogame.java   |  19 +++
 .../tapestry5/ioc/services/WashingMachine.java     |  23 +++
 46 files changed, 1819 insertions(+), 27 deletions(-)

diff --git a/build.gradle b/build.gradle
index c888bdb..d037de0 100755
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ apply from: "sha256.gradle"
 project.ext.versions = [
     jetty: "8.1.19.v20160209",
     tomcat: "7.0.70",
-    testng: "6.8.21",
+    testng: "6.14.3",
     easymock: "3.3.1",
     servletapi: "3.0.1",
     spock: "2.1-M2-groovy-3.0",
@@ -165,7 +165,7 @@ subprojects {
     apply plugin: "jacoco"
     
     jacoco {
-        toolVersion = "0.8.6"
+        toolVersion = "0.8.7"
     }
     
     sourceCompatibility = "1.8"
@@ -221,7 +221,6 @@ subprojects {
     
         // Needed to have XMLTokenStreamTests.testStreamEncoding() passing on Java 9+
         if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_1_9)) {
-            println "Java 9+ JVM arg: --add-opens=java.base/java.nio.charset=ALL-UNNAMED"
             jvmArgs("--add-opens=java.base/java.nio.charset=ALL-UNNAMED");
         }
     
diff --git a/tapestry-beanvalidator/build.gradle b/tapestry-beanvalidator/build.gradle
index 4db1bba..9c22ede 100644
--- a/tapestry-beanvalidator/build.gradle
+++ b/tapestry-beanvalidator/build.gradle
@@ -9,6 +9,12 @@ dependencies {
   implementation "org.hibernate:hibernate-validator:4.3.2.Final"
 
   testImplementation project(':tapestry-test')
+  implementation "org.seleniumhq.selenium:selenium-leg-rc:${versions.selenium}", {
+      exclude group: "org.seleniumhq.selenium", module: "jetty-repacked"
+      exclude group: "org.testng", module: "testng"
+      exclude group: "javax.servlet", module: "javax.servlet-api"
+  }
+  
 }
 
 task compileCoffeeScript(type: CompileCoffeeScript) {
diff --git a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
index bd0c889..6e945fc 100644
--- a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
+++ b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
@@ -15,6 +15,8 @@ package org.apache.tapestry5.beanvalidator.integration;
 
 import org.apache.tapestry5.test.SeleniumTestCase;
 import org.apache.tapestry5.test.TapestryTestConfiguration;
+import org.openqa.selenium.By;
+import org.openqa.selenium.support.ui.ExpectedConditions;
 import org.testng.annotations.Test;
 
 @Test(sequential = true, groups = "integration")
@@ -108,6 +110,12 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
     public void beaneditform_validation() throws Exception
     {
         openLinks("BeanEditForm Validation Demo");
+
+        // Ugly hack to fix the "Unable to locate element: //input[@type='submit']" error.
+        // I have no idea why it's failing here but not in other tests and pages.
+        // I have no idea why it's falling to begin with.
+        // waitForCondition(ExpectedConditions.presenceOfElementLocated(By.xpath(SUBMIT)));
+        Thread.sleep(5000);
         
         clickAndWait(SUBMIT);
 
diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle
index 60d6d9c..35f5289 100644
--- a/tapestry-core/build.gradle
+++ b/tapestry-core/build.gradle
@@ -72,10 +72,9 @@ jar {
     }
 }
 
-// Needed to have XMLTokenStreamTests.testStreamEncoding() passing on Java 17
+// Needed to have XMLTokenStreamTests.testStreamEncoding() passing on Java 9+
 test {
     if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_1_9)) {
-        println "Java 9+ JVM arg: --add-opens=java.base/java.nio.charset=ALL-UNNAMED"
         jvmArgs("--add-opens=java.base/java.nio.charset=ALL-UNNAMED");
     }
 }
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/ParameterTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/ParameterTests.groovy
index 6f35e60..f7a401b 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/ParameterTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/app1/ParameterTests.groovy
@@ -29,6 +29,9 @@ class ParameterTests extends App1TestCase
     void null_bound_to_primitive_field_is_an_error()
     {
         openLinks "Null Bound to Primitive Demo"
+        
+        // Hack to try to avoid test to fail in Jenkins even though it passes locally.
+        Thread.sleep(5000);
 
         assertTextPresent "Parameter 'value' of component NullBindingToPrimitive:showint is bound to null. This parameter is not allowed to be null."
     }
diff --git a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/symbolparam/GridSymbolDemoTests.groovy b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/symbolparam/GridSymbolDemoTests.groovy
index c351e46..600ab6d 100644
--- a/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/symbolparam/GridSymbolDemoTests.groovy
+++ b/tapestry-core/src/test/groovy/org/apache/tapestry5/integration/symbolparam/GridSymbolDemoTests.groovy
@@ -1,4 +1,4 @@
-// Copyright 2011-2013 The Apache Software Foundation
+// Copyright 2011-2021 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
 package org.apache.tapestry5.integration.symbolparam
 
 import org.apache.tapestry5.integration.TapestryCoreTestCase
+import org.apache.tapestry5.test.TapestryTestConfiguration
 import org.testng.annotations.Test
 
 /**
  * Tests for the Grid's symbol parameter support added in 5.3.
  */
+@TapestryTestConfiguration(webAppFolder = "src/test/symbolparam")
 class GridSymbolDemoTests extends TapestryCoreTestCase
 {
     @Test
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java
index a24ec20..1e4c677 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/CookiesImplTest.java
@@ -31,6 +31,11 @@ public class CookiesImplTest extends Assert
 {
     private static class ComparableCookie extends Cookie
     {
+        public ComparableCookie(Cookie cookie) 
+        {
+            this(cookie.getName(), cookie.getValue(), cookie.getMaxAge());
+        }
+        
         public ComparableCookie(String name, String value, int maxAge)
         {
             super(name, value);
@@ -118,7 +123,7 @@ public class CookiesImplTest extends Assert
         expectedCookie.setPath("/context/");
         expectedCookie.setDomain("fobar.com");
         assertEquals(cookies.size(), 1);
-        assertEquals(cookies.get(0), expectedCookie);
+        assertFirstCookieEquals(cookies, expectedCookie);
     }
 
     private CookiesImpl createCookiesFixture(String contextPath, final List<Cookie> cookies)
@@ -141,7 +146,7 @@ public class CookiesImplTest extends Assert
         Cookie expectedCookie = new ComparableCookie("foo", "bar", -1);
         expectedCookie.setPath("/ctx/");
         assertEquals(cookies.size(), 1);
-        assertEquals(cookies.get(0), expectedCookie);
+        assertFirstCookieEquals(cookies, expectedCookie);
     }
 
     public void test_Write_Cookie()
@@ -153,7 +158,7 @@ public class CookiesImplTest extends Assert
         Cookie expectedCookie = new ComparableCookie("foo", "bar", 1000);
         expectedCookie.setPath("/ctx/");
         assertEquals(cookies.size(), 1);
-        assertEquals(cookies.get(0), expectedCookie);
+        assertFirstCookieEquals(cookies, expectedCookie);
     }
 
     public void test_Remove_Cookie()
@@ -165,7 +170,7 @@ public class CookiesImplTest extends Assert
         Cookie expectedCookie = new ComparableCookie("foo", null, 0);
         expectedCookie.setPath("/ctx/");
         assertEquals(cookies.size(), 1);
-        assertEquals(cookies.get(0), expectedCookie);
+        assertFirstCookieEquals(cookies, expectedCookie);
     }
     
     public void test_remove_cookie_with_nondefault_path()
@@ -177,6 +182,15 @@ public class CookiesImplTest extends Assert
         Cookie expectedCookie = new ComparableCookie("foo", null, 0);
         expectedCookie.setPath("/nondefault/");
         assertEquals(cookies.size(), 1);
-        assertEquals(cookies.get(0), expectedCookie);
+        assertFirstCookieEquals(cookies, expectedCookie);
     }
+    
+    // Needed since assertEquals() tests both arg1.equals(arg2) and arg2.equals(arg1),
+    // something it apparently didn't in the past, and Cookie from Servlet API doesn't
+    // implements equals()
+    private void assertFirstCookieEquals(final List<Cookie> cookies, Cookie expectedCookie) {
+        assertEquals(new ComparableCookie(cookies.get(0)), 
+                new ComparableCookie(expectedCookie));
+    }
+
 }
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
index adbd8e0..b444a69 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
@@ -14,7 +14,22 @@
 
 package org.apache.tapestry5.ioc.internal;
 
-import org.apache.tapestry5.commons.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.commons.Configuration;
+import org.apache.tapestry5.commons.MappedConfiguration;
+import org.apache.tapestry5.commons.ObjectCreator;
+import org.apache.tapestry5.commons.OrderedConfiguration;
 import org.apache.tapestry5.commons.internal.util.TapestryException;
 import org.apache.tapestry5.commons.services.PlasticProxyFactory;
 import org.apache.tapestry5.commons.util.CollectionFactory;
@@ -26,23 +41,26 @@ import org.apache.tapestry5.ioc.MethodAdviceReceiver;
 import org.apache.tapestry5.ioc.ScopeConstants;
 import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.ServiceBuilderResources;
-import org.apache.tapestry5.ioc.annotations.*;
-import org.apache.tapestry5.ioc.def.*;
+import org.apache.tapestry5.ioc.annotations.Advise;
+import org.apache.tapestry5.ioc.annotations.Contribute;
+import org.apache.tapestry5.ioc.annotations.Decorate;
+import org.apache.tapestry5.ioc.annotations.EagerLoad;
+import org.apache.tapestry5.ioc.annotations.Marker;
+import org.apache.tapestry5.ioc.annotations.Match;
+import org.apache.tapestry5.ioc.annotations.Optional;
+import org.apache.tapestry5.ioc.annotations.Order;
+import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
+import org.apache.tapestry5.ioc.annotations.Scope;
+import org.apache.tapestry5.ioc.annotations.Startup;
+import org.apache.tapestry5.ioc.def.ContributionDef;
+import org.apache.tapestry5.ioc.def.ContributionDef3;
+import org.apache.tapestry5.ioc.def.DecoratorDef;
+import org.apache.tapestry5.ioc.def.ModuleDef2;
+import org.apache.tapestry5.ioc.def.ServiceDef;
+import org.apache.tapestry5.ioc.def.StartupDef;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.slf4j.Logger;
 
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * Starting from the Class for a module, identifies all the services (service builder methods),
  * decorators (service
@@ -157,6 +175,7 @@ public class DefaultModuleDefImpl implements ModuleDef2, ServiceDefAccumulator
         }
 
         removeSyntheticMethods(methods);
+        removeGroovyObjectMethods(methods);
 
         boolean modulePreventsServiceDecoration = moduleClass.getAnnotation(PreventServiceDecoration.class) != null;
 
@@ -215,6 +234,24 @@ public class DefaultModuleDefImpl implements ModuleDef2, ServiceDefAccumulator
     {
         return serviceDefs.get(serviceId);
     }
+    
+    private void removeGroovyObjectMethods(Set<Method> methods)
+    {
+        Iterator<Method> iterator = methods.iterator();
+
+        while (iterator.hasNext())
+        {
+            Method m = iterator.next();
+            final String name = m.getName();
+
+            if (m.getDeclaringClass().getName().equals("groovy.lang.GroovyObject")
+                    || (name.equals("getMetaClass") && m.getReturnType().getName().equals("groovy.lang.MetaClass"))
+                || (m.getParameterCount() == 1 && m.getParameterTypes()[0].getName().equals("groovy.lang.MetaClass")))
+            {
+                iterator.remove();
+            }
+        }
+    }
 
     private void removeSyntheticMethods(Set<Method> methods)
     {
diff --git a/tapestry-latest-java-tests/build.gradle b/tapestry-latest-java-tests/build.gradle
index 54c9223..92267af 100644
--- a/tapestry-latest-java-tests/build.gradle
+++ b/tapestry-latest-java-tests/build.gradle
@@ -1,5 +1,19 @@
 description = "Test suite for making sure Tapestry runs on latest Java"
 
+sourceCompatibility = "17"
+targetCompatibility = "17"
+
+tasks.withType(JavaCompile).each { 
+    it.options.compilerArgs.add("--enable-preview")
+}
+
+test {
+    useTestNG()
+    jvmArgs(["--enable-preview"])
+}
+
 dependencies {
-    api project(':tapestry-ioc')
+    testImplementation project(':tapestry-ioc')
+    testImplementation "org.slf4j:slf4j-api:${versions.slf4j}"
+    testImplementation "org.testng:testng:${versions.testng}"
 }
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/AndExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/AndExpression.java
new file mode 100644
index 0000000..3696898
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/AndExpression.java
@@ -0,0 +1,32 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public final class AndExpression implements BinaryExpression
+{
+    private final BinaryExpression operand1, operand2;
+    
+    public AndExpression(BinaryExpression operand1, BinaryExpression operand2) {
+        super();
+        this.operand1 = operand1;
+        this.operand2 = operand2;
+    }
+
+    @Override
+    public boolean evaluate()
+    {
+        return operand1.evaluate() && operand2.evaluate();
+    }
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Appliance.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Appliance.java
new file mode 100644
index 0000000..8e25feea
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Appliance.java
@@ -0,0 +1,33 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public sealed abstract class Appliance permits WashingMachine, CoolingMachine
+{
+
+    final String name;
+
+    public Appliance(String name) 
+    {
+        super();
+        this.name = name;
+    }
+    
+    public String getName()
+    {
+        return name;
+    }
+    
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/BinaryExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/BinaryExpression.java
new file mode 100644
index 0000000..1202bf9
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/BinaryExpression.java
@@ -0,0 +1,20 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public sealed interface BinaryExpression permits ConstantBinaryExpression, AndExpression, OrExpression
+{
+    boolean evaluate();
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConsolePortableHybrid.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConsolePortableHybrid.java
new file mode 100644
index 0000000..3e0559b
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConsolePortableHybrid.java
@@ -0,0 +1,24 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public class ConsolePortableHybrid extends HomeConsole
+{
+    void turnEmbeddedScreenOn()
+    {
+        
+    }
+
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConstantBinaryExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConstantBinaryExpression.java
new file mode 100644
index 0000000..6bc19ec
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConstantBinaryExpression.java
@@ -0,0 +1,31 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public final class ConstantBinaryExpression implements BinaryExpression
+{
+    private final boolean value;
+    
+    public ConstantBinaryExpression(boolean value) 
+    {
+        this.value = value;
+    }
+
+    @Override
+    public boolean evaluate()
+    {
+        return value;
+    }
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConstantIntExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConstantIntExpression.java
new file mode 100644
index 0000000..ca0cad1
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/ConstantIntExpression.java
@@ -0,0 +1,24 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public final record ConstantIntExpression(int value) implements IntExpression
+{
+    @Override
+    public int evaluate()
+    {
+        return value;
+    }
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/CoolingMachine.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/CoolingMachine.java
new file mode 100644
index 0000000..6df55ae
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/CoolingMachine.java
@@ -0,0 +1,23 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public non-sealed abstract class CoolingMachine extends Appliance 
+{
+    public CoolingMachine(String name)
+    {
+        super(name);
+    }
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Freezer.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Freezer.java
new file mode 100644
index 0000000..eae1a6b
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Freezer.java
@@ -0,0 +1,23 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public final class Freezer extends CoolingMachine
+{
+    public Freezer()
+    {
+        super("Freezer");
+    }
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/HomeConsole.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/HomeConsole.java
new file mode 100644
index 0000000..6179de3
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/HomeConsole.java
@@ -0,0 +1,24 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public class HomeConsole implements Videogame
+{
+    void turnConnectedTVOn()
+    {
+        
+    }
+
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/IntExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/IntExpression.java
new file mode 100644
index 0000000..5538728
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/IntExpression.java
@@ -0,0 +1,20 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public sealed interface IntExpression permits ConstantIntExpression, PlusExpression, SquareExpression
+{
+    int evaluate();
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/IntTuple.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/IntTuple.java
new file mode 100644
index 0000000..69ef256
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/IntTuple.java
@@ -0,0 +1,20 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just used for tests
+ */
+public record IntTuple(int x, int y) 
+{
+
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11ConcreteService.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11ConcreteService.java
new file mode 100644
index 0000000..206dd88
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11ConcreteService.java
@@ -0,0 +1,68 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiFunction;
+
+public class Java10And11ConcreteService
+{
+    
+    public Java10And11ConcreteService() throws Exception 
+    {
+        localVariableTypeInference();
+    }
+
+    /**
+     * Tests Local Variable Type Inference.
+     * Examples taken from https://docs.oracle.com/en/java/javase/17/language/local-variable-type-inference.html.
+     */
+    @SuppressWarnings("unused")
+    public void localVariableTypeInference() throws Exception 
+    {
+        
+        var url = new URL("http://www.oracle.com/"); 
+        var conn = url.openConnection(); 
+        var reader = new BufferedReader(
+            new InputStreamReader(conn.getInputStream()));
+        
+        reader.close();
+        
+        
+        var fileName = "LICENSE.txt";
+        var list = new ArrayList<String>();    // infers ArrayList<String>
+        var stream = list.stream();            // infers Stream<String>
+        var path = Paths.get(fileName);        // infers Path
+        var bytes = Files.readAllBytes(path);  // infers bytes[]
+
+        List<String> myList = Arrays.asList("a", "b", "c");
+        for (var element : myList) { element += " " ;}  // infers String
+        
+        for (var counter = 0; counter < 10; counter++) { Math.max(counter, counter); }   // infers int
+        
+        try (var input = 
+                new FileInputStream(fileName)) {}   // infers FileInputStream
+        
+        BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
+        biFunction = (var a, var b) -> a + b;
+        
+    }
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11NewFeatureTests.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11NewFeatureTests.java
new file mode 100644
index 0000000..f91db00
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11NewFeatureTests.java
@@ -0,0 +1,54 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.RegistryBuilder;
+import org.apache.tapestry5.ioc.ServiceBinder;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for Tapestry-IoC and the only Java language feature introduced in Java SE 10 and 11,
+ * local variable type inference (introduced in 10 and improved in 11).
+ */
+public class Java10And11NewFeatureTests 
+{
+    
+    public static class Java10And11Module 
+    {
+        public static void bind(ServiceBinder binder)
+        {
+            binder.bind(Java10And11Service.class, Java10And11ServiceImpl.class);
+            binder.bind(Java10And11ConcreteService.class);
+        }
+    }
+    
+    private Java10And11Service java10And11Service;
+    
+    private Java10And11ConcreteService java10And11ConcreteService;
+    
+    @BeforeSuite
+    public void setup() 
+    {
+        var registry = RegistryBuilder.buildAndStartupRegistry(Java10And11Module.class);
+        java10And11Service = registry.getService(Java10And11Service.class);
+        java10And11ConcreteService = registry.getService(Java10And11ConcreteService.class);
+    }
+
+    @Test
+    public void localVariableTypeInference() throws Exception 
+    {
+        java10And11Service.localVariableTypeInference();
+        java10And11ConcreteService.localVariableTypeInference();;
+    }
+    
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11Service.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11Service.java
new file mode 100644
index 0000000..b68e29e
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11Service.java
@@ -0,0 +1,17 @@
+// Licensed 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.tapestry5.ioc.services;
+
+public interface Java10And11Service 
+{
+    void localVariableTypeInference() throws Exception;
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11ServiceImpl.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11ServiceImpl.java
new file mode 100644
index 0000000..1fdccfd
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java10And11ServiceImpl.java
@@ -0,0 +1,69 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiFunction;
+
+public class Java10And11ServiceImpl implements Java10And11Service 
+{
+    
+    public Java10And11ServiceImpl() throws Exception 
+    {
+        localVariableTypeInference();
+    }
+
+    /**
+     * Tests Local Variable Type Inference.
+     * Examples taken from https://docs.oracle.com/en/java/javase/17/language/local-variable-type-inference.html.
+     */
+    @SuppressWarnings("unused")
+    @Override
+    public void localVariableTypeInference() throws Exception 
+    {
+        
+        var url = new URL("http://www.oracle.com/"); 
+        var conn = url.openConnection(); 
+        var reader = new BufferedReader(
+            new InputStreamReader(conn.getInputStream()));
+        
+        reader.close();
+        
+        
+        var fileName = "LICENSE.txt";
+        var list = new ArrayList<String>();    // infers ArrayList<String>
+        var stream = list.stream();            // infers Stream<String>
+        var path = Paths.get(fileName);        // infers Path
+        var bytes = Files.readAllBytes(path);  // infers bytes[]
+
+        List<String> myList = Arrays.asList("a", "b", "c");
+        for (var element : myList) { element += " " ;}  // infers String
+        
+        for (var counter = 0; counter < 10; counter++) { Math.max(counter, counter); }   // infers int
+        
+        try (var input = 
+                new FileInputStream(fileName)) {}   // infers FileInputStream
+        
+        BiFunction<Integer, Integer, Integer> biFunction = (a, b) -> a + b;
+        biFunction = (var a, var b) -> a + b;
+        
+    }
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13ConcreteService.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13ConcreteService.java
new file mode 100644
index 0000000..38eb3c5
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13ConcreteService.java
@@ -0,0 +1,71 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import java.time.LocalDate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Java12And13ConcreteService
+{
+    
+    private static final String DAY_OF_WEEK_STRING = "Work!";
+    private static final String WEEKEND_STRING = "Rest!";
+    private static final Logger LOGGER = LoggerFactory.getLogger(Java12And13ServiceImpl.class);
+    
+    public Java12And13ConcreteService()
+    {
+        textBlocks();
+        switchExpressions();
+    }
+
+    /**
+     * Tests Text Blocks
+     * https://docs.oracle.com/en/java/javase/17/language/text-blocks.html
+     */
+    public void textBlocks() 
+    {
+        String string = """
+                <html>
+                    <body>
+                        <p>Hello, Tapestry world!>/p>
+                    </body>
+                </html>
+                """;
+        LOGGER.info("Text block: " + string);
+    }
+
+    /**
+     * Tests Switch Expressions
+     * https://docs.oracle.com/en/java/javase/17/language/switch-expressions.html
+     */
+    public void switchExpressions() 
+    {
+        LocalDate date = LocalDate.now();
+        String string = switch(date.getDayOfWeek())
+        {
+            case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> 
+            {
+                LOGGER.info(DAY_OF_WEEK_STRING);
+                yield DAY_OF_WEEK_STRING;
+            }
+            default -> 
+            {
+                LOGGER.info(WEEKEND_STRING);
+                yield WEEKEND_STRING;
+            }
+        };
+        LOGGER.info("Result: " + string);
+    }
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13NewFeatureTests.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13NewFeatureTests.java
new file mode 100644
index 0000000..051b586
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13NewFeatureTests.java
@@ -0,0 +1,69 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.RegistryBuilder;
+import org.apache.tapestry5.ioc.ServiceBinder;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for Tapestry-IoC and the Java language features introduced in Java SE 12 and 13,
+ * switch expressiosn and text blocks.
+ */
+public class Java12And13NewFeatureTests 
+{
+    
+    public static class Java12And13Module 
+    {
+        public static void bind(ServiceBinder binder)
+        {
+            binder.bind(Java12And13Service.class, Java12And13ServiceImpl.class);
+            binder.bind(Java12And13ConcreteService.class);
+        }
+    }
+    
+    private Java12And13Service java12And13Service;
+    
+    private Java12And13ConcreteService java12And13ConcreteService;
+    
+    @BeforeSuite
+    public void setup() 
+    {
+        var registry = RegistryBuilder.buildAndStartupRegistry(Java12And13Module.class);
+        java12And13Service = registry.getService(Java12And13Service.class);
+        java12And13ConcreteService = registry.getService(Java12And13ConcreteService.class);
+    }
+
+    /**
+     * Tests Text Blocks
+     * https://docs.oracle.com/en/java/javase/17/language/text-blocks.html
+     */
+    @Test
+    public void textBlocks() throws Exception 
+    {
+        java12And13Service.textBlocks();
+        java12And13ConcreteService.textBlocks();
+    }
+
+    /**
+     * Tests Switch Expressions
+     * https://docs.oracle.com/en/java/javase/17/language/switch-expressions.html
+     */
+    @Test
+    public void switchExpressions() throws Exception 
+    {
+        java12And13Service.switchExpressions();
+        java12And13ConcreteService.switchExpressions();
+    }
+
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13Service.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13Service.java
new file mode 100644
index 0000000..0c97386
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13Service.java
@@ -0,0 +1,28 @@
+// Licensed 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.tapestry5.ioc.services;
+
+public interface Java12And13Service 
+{
+
+    /**
+     * Tests Text Blocks
+     * https://docs.oracle.com/en/java/javase/17/language/text-blocks.html
+     */
+    void textBlocks();
+    
+    /**
+     * Tests Switch Expressions
+     * https://docs.oracle.com/en/java/javase/17/language/switch-expressions.html
+     */
+    void switchExpressions();
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13ServiceImpl.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13ServiceImpl.java
new file mode 100644
index 0000000..76f22cd
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java12And13ServiceImpl.java
@@ -0,0 +1,69 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import java.time.LocalDate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Java12And13ServiceImpl implements Java12And13Service 
+{
+    
+    private static final String DAY_OF_WEEK_STRING = "Work!";
+    private static final String WEEKEND_STRING = "Rest!";
+    private static final Logger LOGGER = LoggerFactory.getLogger(Java12And13ServiceImpl.class);
+    
+    public Java12And13ServiceImpl()
+    {
+        textBlocks();
+        switchExpressions();
+    }
+
+    /**
+     * Tests Text Blocks
+     * https://docs.oracle.com/en/java/javase/17/language/text-blocks.html
+     */
+    @Override
+    public void textBlocks() 
+    {
+        String string = """
+                <html>
+                    <body>
+                        <p>Hello, Tapestry world!>/p>
+                    </body>
+                </html>
+                """;
+        LOGGER.info("Text block: " + string);
+    }
+
+    @Override
+    public void switchExpressions() 
+    {
+        LocalDate date = LocalDate.now();
+        String string = switch(date.getDayOfWeek())
+        {
+            case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> 
+            {
+                LOGGER.info(DAY_OF_WEEK_STRING);
+                yield DAY_OF_WEEK_STRING;
+            }
+            default -> 
+            {
+                LOGGER.info(WEEKEND_STRING);
+                yield WEEKEND_STRING;
+            }
+        };
+        LOGGER.info("Result: " + string);
+    }
+
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14ConcreteService.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14ConcreteService.java
new file mode 100644
index 0000000..8c99405
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14ConcreteService.java
@@ -0,0 +1,73 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.slf4j.LoggerFactory;
+
+public class Java14ConcreteService
+{
+    
+    public Java14ConcreteService()
+    {
+        patternMatchingForTheInstanceOfOperator();
+        records();
+        localRecord();  
+    }
+
+    /**
+     * For testing Pattern Matching for the instanceof Operator
+     * http://www.oracle.com/pls/topic/lookup?ctx=javase14&id=GUID-843060B5-240C-4F47-A7B0-95C42E5B08A7
+     */
+    public void patternMatchingForTheInstanceOfOperator() 
+    {
+        handleVideogame(new HomeConsole());
+        handleVideogame(new ConsolePortableHybrid());
+    }
+    
+    @SuppressWarnings("preview")
+    private void handleVideogame(Videogame videogame)
+    {
+        if (videogame instanceof HomeConsole homeConsole)
+        {
+            homeConsole.turnConnectedTVOn();
+        }
+        if (videogame instanceof ConsolePortableHybrid hybrid)
+        {
+            hybrid.turnEmbeddedScreenOn();
+        }
+    }
+
+    /**
+     * For testing Records
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public IntTuple records() 
+    {
+        return new IntTuple(1, 1);
+    }
+    
+    /**
+     * For testing Local Record Classes
+     * GUID-6699E26F-4A9B-4393-A08B-1E47D4B2D263__GUID-FB8EDC85-2C6A-4591-8E00-248DA900723A
+     */
+    public void localRecord() 
+    {
+        
+        record Local(String name)
+        {
+            
+        }
+        
+        LoggerFactory.getLogger(this.getClass()).info("Local record " + new Local("test"));
+    }
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14NewFeatureTests.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14NewFeatureTests.java
new file mode 100644
index 0000000..35746c2
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14NewFeatureTests.java
@@ -0,0 +1,101 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.Registry;
+import org.apache.tapestry5.ioc.RegistryBuilder;
+import org.apache.tapestry5.ioc.ServiceBinder;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for Tapestry-IoC and Java language features introduced in Java SE 14: pattern matching for the
+ * <code>instanceof</code> operator and records.
+ */
+public class Java14NewFeatureTests 
+{
+    
+    private Registry registry;
+    
+    public static class Java14Module 
+    {
+        public static void bind(ServiceBinder binder)
+        {
+            binder.bind(Java14Service.class, Java14ServiceImpl.class);
+            binder.bind(Java14ConcreteService.class);
+        }
+        public static IntTuple buildIntTuple()
+        {
+            return new IntTuple(10, 15);
+        }
+    }
+    
+    private Java14Service java14Service;
+    
+    private Java14ConcreteService java14ConcreteService;
+    
+    @BeforeSuite
+    public void setup() 
+    {
+        registry = RegistryBuilder.buildAndStartupRegistry(Java14Module.class);
+        java14Service = registry.getService(Java14Service.class);
+        java14ConcreteService = registry.getService(Java14ConcreteService.class);
+    }
+
+    /**
+     * For testing Pattern Matching for the instanceof Operator
+     * http://www.oracle.com/pls/topic/lookup?ctx=javase14&id=GUID-843060B5-240C-4F47-A7B0-95C42E5B08A7
+     */
+    @Test
+    public void patternMatchingForTheInstanceOfOperator() 
+    {
+        java14Service.patternMatchingForTheInstanceOfOperator();
+        java14ConcreteService.patternMatchingForTheInstanceOfOperator();
+    }
+
+    /**
+     * For testing Records
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    @Test
+    public void records() 
+    {
+        process(java14Service.records());
+        process(java14ConcreteService.records());
+    }
+    
+    /**
+     * For testing services whose type is a record.
+     */
+    @Test
+    public void recordsAsServices()
+    {
+        process(registry.getService(IntTuple.class));
+    }
+
+    private void process(IntTuple record) {
+        LoggerFactory.getLogger(this.getClass()).info("Record: " + record);
+    }
+    
+    /**
+     * For testing Local Record Classes
+     * GUID-6699E26F-4A9B-4393-A08B-1E47D4B2D263__GUID-FB8EDC85-2C6A-4591-8E00-248DA900723A
+     */
+    @Test
+    public void localRecord() 
+    {
+        java14Service.localRecord();
+        java14ConcreteService.localRecord();
+    }
+
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14Service.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14Service.java
new file mode 100644
index 0000000..a0096c2
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14Service.java
@@ -0,0 +1,37 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.slf4j.LoggerFactory;
+
+public interface Java14Service 
+{
+    
+    /**
+     * For testing Pattern Matching for the instanceof Operator
+     * http://www.oracle.com/pls/topic/lookup?ctx=javase14&id=GUID-843060B5-240C-4F47-A7B0-95C42E5B08A7
+     */
+    void patternMatchingForTheInstanceOfOperator();
+    
+    /**
+     * For testing Records
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    IntTuple records();
+    
+    /**
+     * For testing Local Record Classes
+     * GUID-6699E26F-4A9B-4393-A08B-1E47D4B2D263__GUID-FB8EDC85-2C6A-4591-8E00-248DA900723A
+     */
+    void localRecord();    
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14ServiceImpl.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14ServiceImpl.java
new file mode 100644
index 0000000..a35b10f
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java14ServiceImpl.java
@@ -0,0 +1,71 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.slf4j.LoggerFactory;
+
+public class Java14ServiceImpl implements Java14Service 
+{
+    
+    public Java14ServiceImpl() 
+    {
+        patternMatchingForTheInstanceOfOperator();
+        records();
+    }
+
+    /**
+     * For testing Pattern Matching for the instanceof Operator
+     * http://www.oracle.com/pls/topic/lookup?ctx=javase14&id=GUID-843060B5-240C-4F47-A7B0-95C42E5B08A7
+     */
+    public void patternMatchingForTheInstanceOfOperator() 
+    {
+        handleVideogame(new HomeConsole());
+        handleVideogame(new ConsolePortableHybrid());
+    }
+    
+    @SuppressWarnings("preview")
+    private void handleVideogame(Videogame videogame)
+    {
+        if (videogame instanceof HomeConsole homeConsole)
+        {
+            homeConsole.turnConnectedTVOn();
+        }
+        if (videogame instanceof ConsolePortableHybrid hybrid)
+        {
+            hybrid.turnEmbeddedScreenOn();
+        }
+    }
+
+    /**
+     * For testing Records
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public IntTuple records() 
+    {
+        return new IntTuple(1, 1);
+    }
+    
+    /**
+     * For testing Local Record Classes
+     * GUID-6699E26F-4A9B-4393-A08B-1E47D4B2D263__GUID-FB8EDC85-2C6A-4591-8E00-248DA900723A
+     */
+    public void localRecord() {
+        
+        record Local(String name)
+        {
+            
+        }
+        LoggerFactory.getLogger(this.getClass()).info("Local record " + new Local("test"));
+    }
+
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17ConcreteService.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17ConcreteService.java
new file mode 100644
index 0000000..f76a77f
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17ConcreteService.java
@@ -0,0 +1,63 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Java15To17ConcreteService
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(Java15To17ConcreteService.class);
+    
+    public Java15To17ConcreteService() 
+    {
+        recordsWithoutInterfacesNorRecords();
+    }
+
+    /**
+     * Tests Record Classes, regular ones (no interfaces, no records).
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithoutInterfacesNorRecords() 
+    {
+        Appliance appliance = new WashingMachine();
+        CoolingMachine coolingMachine = new Freezer();
+        coolingMachine = new Refrigerator();
+        LOGGER.info("Appliance: " + appliance.getName());
+        LOGGER.info("Cooling machine: " + coolingMachine.getName());
+    }
+    
+    /**
+     * Tests Record Classes with interfaces but no records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithInterfacesButNotRecords() 
+    {
+        BinaryExpression expression = new AndExpression(
+                new OrExpression(new ConstantBinaryExpression(false), new ConstantBinaryExpression(true)),
+                new AndExpression(new ConstantBinaryExpression(false), new ConstantBinaryExpression(true))
+        );
+        LOGGER.info("Binary expression: " + expression.evaluate());
+    }
+
+    /**
+     * Tests Record Classes with interfaces and records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithInterfacesAndRecords() 
+    {
+        IntExpression expression = new SquareExpression(
+                new PlusExpression(new ConstantIntExpression(2), new ConstantIntExpression(3)));
+        LOGGER.info("Int expression: " + expression.evaluate());
+    }
+
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17NewFeatureTests.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17NewFeatureTests.java
new file mode 100644
index 0000000..f8fb60d
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17NewFeatureTests.java
@@ -0,0 +1,169 @@
+// Licensed 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 To
+// limitations under the License.
+package org.apache.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.Registry;
+import org.apache.tapestry5.ioc.RegistryBuilder;
+import org.apache.tapestry5.ioc.ServiceBinder;
+import org.apache.tapestry5.ioc.annotations.Marker;
+import org.apache.tapestry5.ioc.annotations.Primary;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for Tapestry-IoC To the only Java language feature introduced in Java SE 10 To 11,
+ * local variable type inference (introduced in 10 To improved in 11).
+ */
+public class Java15To17NewFeatureTests 
+{
+    
+    // TODO: test ChainBuilder and company
+    
+    private static final Logger LOGGER = LoggerFactory.getLogger(Java15To17NewFeatureTests.class);
+    
+    public static class Java15To17Module 
+    {
+        
+        public static void bind(ServiceBinder binder)
+        {
+            binder.bind(Java15To17Service.class, Java15To17ServiceImpl.class);
+            binder.bind(Java15To17ConcreteService.class);
+        }
+        
+        // Two service for each case of sealed classes (no record and no interface, interface 
+        // and no record, interface and record): one for the sealed class itself,
+        // another for the subclasses.
+        // 
+        @Marker(Primary.class)        
+        public static Appliance buildAppliance()
+        {
+            return new Freezer();
+        }
+
+        public static Refrigerator buildRefridgerator()
+        {
+            return new Refrigerator();
+        }
+
+        @Marker(Primary.class)
+        public static BinaryExpression buildBinaryExpression()
+        {
+            return new OrExpression(new ConstantBinaryExpression(false), new ConstantBinaryExpression(true));
+        }
+        
+        public static ConstantBinaryExpression buildConstantBinaryExpression()
+        {
+            return new ConstantBinaryExpression(true);
+        }
+
+        @Marker(Primary.class)
+        public static IntExpression buildIntExpression()
+        {
+            return new SquareExpression(new ConstantIntExpression(42));
+        }
+        
+        public static ConstantIntExpression buildConstantIntExpression()
+        {
+            return new ConstantIntExpression(3);
+        }
+
+    }
+    
+    private Java15To17Service java15To17Service;
+    
+    private Java15To17ConcreteService java15To17ConcreteService;
+
+    private Registry registry;
+    
+    @BeforeSuite
+    public void setup() 
+    {
+        registry = RegistryBuilder.buildAndStartupRegistry(Java15To17Module.class);
+        java15To17Service = registry.getService(Java15To17Service.class);
+        java15To17ConcreteService = registry.getService(Java15To17ConcreteService.class);
+    }
+
+    /**
+     * Tests Record Classes, regular ones (no interfaces, no records).
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    @Test
+    public void recordsWithoutInterfacesNorRecords()
+    {
+        java15To17Service.recordsWithoutInterfacesNorRecords();
+        java15To17ConcreteService.recordsWithoutInterfacesNorRecords();
+    }
+    
+    /**
+     * Tests Record Classes with interfaces but no records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    @Test
+    public void recordsWithInterfacesButNotRecords()
+    {
+        java15To17Service.recordsWithInterfacesButNotRecords();
+        java15To17ConcreteService.recordsWithInterfacesButNotRecords();
+    }
+
+    /**
+     * Tests Record Classes with interfaces and records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    @Test
+    public void recordsWithInterfacesAndRecords()
+    {
+        java15To17Service.recordsWithInterfacesAndRecords();
+        java15To17ConcreteService.recordsWithInterfacesAndRecords();
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void recordsWithoutInterfacesNorRecords_service_sealedClass()
+    {
+        LOGGER.info(registry.getService(Appliance.class, Primary.class).getName());
+    }
+    
+    @Test
+    public void recordsWithoutInterfacesNorRecords_service_subclass()
+    {
+        LOGGER.info(registry.getService(Refrigerator.class).getName());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void recordsWithInterfacesButNotRecords_service_sealedClass()
+    {
+        LOGGER.info(String.valueOf(registry.getService(BinaryExpression.class, Primary.class).evaluate()));
+    }
+    
+    @Test
+    public void recordsWithInterfacesButNotRecords_service_subclass()
+    {
+        LOGGER.info(String.valueOf(registry.getService(ConstantBinaryExpression.class).evaluate()));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void recordsWithInterfacesAndRecords_service_sealedClass()
+    {
+        LOGGER.info(String.valueOf(registry.getService(IntExpression.class, Primary.class).evaluate()));
+    }
+    
+    @Test
+    public void recordsWithInterfacesAndRecords_service_subclass()
+    {
+        LOGGER.info(String.valueOf(registry.getService(ConstantIntExpression.class).evaluate()));
+    }
+
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17Service.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17Service.java
new file mode 100644
index 0000000..d18f8b3
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17Service.java
@@ -0,0 +1,34 @@
+// Licensed 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.tapestry5.ioc.services;
+
+public interface Java15To17Service 
+{
+    /**
+     * Tests Record Classes, regular ones (no interfaces, no records).
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithoutInterfacesNorRecords();  
+    
+    /**
+     * Tests Record Classes with interfaces but no records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithInterfacesButNotRecords();
+
+    /**
+     * Tests Record Classes with interfaces and records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithInterfacesAndRecords();
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17ServiceImpl.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17ServiceImpl.java
new file mode 100644
index 0000000..942aecd
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java15To17ServiceImpl.java
@@ -0,0 +1,66 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Java15To17ServiceImpl implements Java15To17Service
+{
+    
+    private static final Logger LOGGER = LoggerFactory.getLogger(Java15To17ServiceImpl.class);
+    
+    public Java15To17ServiceImpl() 
+    {
+        recordsWithInterfacesAndRecords();
+        recordsWithInterfacesButNotRecords();
+        recordsWithoutInterfacesNorRecords();
+    }
+    
+    /**
+     * Tests Record Classes, regular ones (no interfaces, no records).
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithoutInterfacesNorRecords()
+    {
+        Appliance appliance = new WashingMachine();
+        CoolingMachine coolingMachine = new Freezer();
+        coolingMachine = new Refrigerator();
+        LOGGER.info("Appliance: " + appliance.getName());
+        LOGGER.info("Cooling machine: " + coolingMachine.getName());
+    }
+    
+    /**
+     * Tests Record Classes with interfaces but no records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithInterfacesButNotRecords()
+    {
+        BinaryExpression expression = new AndExpression(
+                new OrExpression(new ConstantBinaryExpression(false), new ConstantBinaryExpression(true)),
+                new AndExpression(new ConstantBinaryExpression(false), new ConstantBinaryExpression(true))
+        );
+        LOGGER.info("Binary expression: " + expression.evaluate());
+    }
+
+    /**
+     * Tests Record Classes with interfaces and records.
+     * https://docs.oracle.com/en/java/javase/17/language/records.html
+     */
+    public void recordsWithInterfacesAndRecords()
+    {
+        IntExpression expression = new SquareExpression(
+                new PlusExpression(new ConstantIntExpression(2), new ConstantIntExpression(3)));
+        LOGGER.info("Int expression: " + expression.evaluate());
+    }
+    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9ConcreteService.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9ConcreteService.java
new file mode 100644
index 0000000..921a60e
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9ConcreteService.java
@@ -0,0 +1,69 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.List;
+
+public class Java9ConcreteService
+{
+    
+    public Java9ConcreteService()
+    {
+        tryWithResources();
+        callSafeVarArgs();
+        diamondSyntaxAndAnonymousInnerClasses();
+    }
+    
+    /**
+     * For resting More Concise try-with-resources Statements
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-A920DB06-0FD1-4F9C-8A9A-15FC979D5DA3
+     */
+    public void tryWithResources()
+    {
+        Formatter formatter = new Formatter();
+        try (formatter) 
+        {
+            formatter.format("nothing");
+        }
+    }
+    
+    public void callSafeVarArgs()
+    {
+        safeVarags("1");
+    }
+    
+    /**
+     * For testing @SafeVarargs Annotation Allowed on Proviate Instance Methods.
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-015392DB-F5C4-4A8E-B190-E797707E7BFB
+     */
+    @SafeVarargs
+    private void safeVarags( String...strings)
+    {
+        
+    }
+    
+    /**
+     * For testing Diamond Syntax and Anonymous Inner Classes
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-0DF89F53-9232-44E3-80A4-48DD0C2CF359
+     */
+    private class Inner
+    {
+        
+    }
+    
+    private void diamondSyntaxAndAnonymousInnerClasses() {
+        List<Inner> list = new ArrayList<>();
+        list.add(new Inner());
+    }    
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9NewFeatureTests.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9NewFeatureTests.java
new file mode 100644
index 0000000..f2ddcc6
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9NewFeatureTests.java
@@ -0,0 +1,60 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import org.apache.tapestry5.ioc.RegistryBuilder;
+import org.apache.tapestry5.ioc.ServiceBinder;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for Tapestry-IoC and Java language features introduced in Java SE 9.
+ */
+public class Java9NewFeatureTests 
+{
+    
+    public static class Java9Module 
+    {
+        public static void bind(ServiceBinder binder)
+        {
+            binder.bind(Java9Service.class, Java9ServiceImpl.class);
+            binder.bind(Java9ConcreteService.class);
+        }
+    }
+    
+    private Java9Service java9Service;
+    
+    private Java9ConcreteService java9ConcreteService;
+    
+    @BeforeSuite
+    public void setup() 
+    {
+        var registry = RegistryBuilder.buildAndStartupRegistry(Java9Module.class);
+        java9Service = registry.getService(Java9Service.class);
+        java9ConcreteService = registry.getService(Java9ConcreteService.class);
+    }
+
+    @Test
+    public void safevarargs() 
+    {
+        java9Service.callSafeVarArgs();
+        java9ConcreteService.callSafeVarArgs();
+    }
+    
+    @Test
+    public void tryWithResources() 
+    {
+        java9Service.tryWithResources();
+        java9ConcreteService.tryWithResources();
+    }
+    
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9Service.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9Service.java
new file mode 100644
index 0000000..15b3f77
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9Service.java
@@ -0,0 +1,33 @@
+// Licensed 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.tapestry5.ioc.services;
+
+public interface Java9Service 
+{
+    
+    void tryWithResources();
+    
+    void callSafeVarArgs();
+
+    default void defaultMethod()
+    {
+        privateMethodInInterface();
+    }
+    
+    /**
+     * For testing Support for Private Interface Methods
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-015392DB-F5C4-4A8E-B190-E797707E7BFB
+     */
+    private void privateMethodInInterface()
+    {
+    }
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9ServiceImpl.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9ServiceImpl.java
new file mode 100644
index 0000000..3fd2117
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Java9ServiceImpl.java
@@ -0,0 +1,70 @@
+// Licensed 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.tapestry5.ioc.services;
+
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.List;
+
+public class Java9ServiceImpl implements Java9Service 
+{
+    
+    public Java9ServiceImpl() 
+    {
+        diamondSyntaxAndAnonymousInnerClasses();
+        tryWithResources();
+        callSafeVarArgs();
+    }
+    
+    /**
+     * For testing More Concise try-with-resources Statements
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-A920DB06-0FD1-4F9C-8A9A-15FC979D5DA3
+     */
+    public void tryWithResources()
+    {
+        Formatter formatter = new Formatter();
+        try (formatter) 
+        {
+            formatter.format("nothing");
+        }
+    }
+    
+    public void callSafeVarArgs()
+    {
+        safeVarags("1");
+    }
+    
+    /**
+     * For testing @SafeVarargs Annotation Allowed on Proviate Instance Methods.
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-015392DB-F5C4-4A8E-B190-E797707E7BFB
+     */
+    @SafeVarargs
+    private void safeVarags(String...strings)
+    {
+        
+    }
+    
+    /**
+     * For testing Diamond Syntax and Anonymous Inner Classes
+     * https://docs.oracle.com/en/java/javase/17/language/java-language-changes.html#GUID-0DF89F53-9232-44E3-80A4-48DD0C2CF359
+     */
+    private class Inner
+    {
+        
+    }
+    
+    private void diamondSyntaxAndAnonymousInnerClasses() {
+        List<Inner> list = new ArrayList<>();
+        list.add(new Inner());
+    }
+
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/OrExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/OrExpression.java
new file mode 100644
index 0000000..632136b
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/OrExpression.java
@@ -0,0 +1,32 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public final class OrExpression implements BinaryExpression
+{
+    private final BinaryExpression operand1, operand2;
+    
+    public OrExpression(BinaryExpression operand1, BinaryExpression operand2) {
+        super();
+        this.operand1 = operand1;
+        this.operand2 = operand2;
+    }
+
+    @Override
+    public boolean evaluate()
+    {
+        return operand1.evaluate() || operand2.evaluate();
+    }
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/PlusExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/PlusExpression.java
new file mode 100644
index 0000000..7538231
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/PlusExpression.java
@@ -0,0 +1,24 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public final record PlusExpression(IntExpression operand1, IntExpression operand2) implements IntExpression
+{
+    @Override
+    public int evaluate()
+    {
+        return operand1.evaluate() + operand2.evaluate();
+    }
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Refrigerator.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Refrigerator.java
new file mode 100644
index 0000000..1f02a6b
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Refrigerator.java
@@ -0,0 +1,23 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public final class Refrigerator extends CoolingMachine
+{
+    public Refrigerator()
+    {
+        super("Refrigerator");
+    }
+}
\ No newline at end of file
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/SquareExpression.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/SquareExpression.java
new file mode 100644
index 0000000..94a5787
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/SquareExpression.java
@@ -0,0 +1,24 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Record Classes tests.
+ */
+public final record SquareExpression (IntExpression expression) implements IntExpression
+{
+    @Override
+    public int evaluate()
+    {
+        return expression.evaluate() * expression.evaluate();
+    }
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Videogame.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Videogame.java
new file mode 100644
index 0000000..c0a4e1d
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/Videogame.java
@@ -0,0 +1,19 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public interface Videogame 
+{
+}
diff --git a/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/WashingMachine.java b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/WashingMachine.java
new file mode 100644
index 0000000..df6dd5f
--- /dev/null
+++ b/tapestry-latest-java-tests/src/test/java/org/apache/tapestry5/ioc/services/WashingMachine.java
@@ -0,0 +1,23 @@
+// Licensed 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.tapestry5.ioc.services;
+
+/**
+ * Just for use in Pattern Matching for the instanceof Operator tests.
+ */
+public final class WashingMachine extends Appliance 
+{
+    public WashingMachine()
+    {
+        super("Washing machine");
+    }
+}
\ No newline at end of file