Revert optimization removal (#1352)

* Revert optimization removal javaee/jaxb-v2#1157

  * Keep optimization for Java versions older then 9 with multi-release jar
  * Add noOptimization flag for possible compatibility issues

Signed-off-by: Daniel Kec <daniel.kec@oracle.com>
diff --git a/jaxb-ri/pom.xml b/jaxb-ri/pom.xml
index 6ca9fdc..eee74e5 100644
--- a/jaxb-ri/pom.xml
+++ b/jaxb-ri/pom.xml
@@ -314,6 +314,11 @@
                         </dependency>
                     </dependencies>
                 </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>3.0.0-M3</version>
+                </plugin>
             </plugins>
         </pluginManagement>
 
diff --git a/jaxb-ri/runtime/impl/pom.xml b/jaxb-ri/runtime/impl/pom.xml
index 024cb95..3ec3bf7 100644
--- a/jaxb-ri/runtime/impl/pom.xml
+++ b/jaxb-ri/runtime/impl/pom.xml
@@ -80,6 +80,80 @@
     <build>
         <plugins>
             <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>add-mr-resource</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>add-resource</goal>
+                        </goals>
+                        <configuration>
+                            <resources>
+                                <resource>
+                                    <directory>${mrjar.sourceDirectory}</directory>
+                                    <targetPath>META-INF/versions</targetPath>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>default-compile-mr</id>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <compileSourceRoots>
+                                <compileSourceRoot>${mrjar.sourceDirectory}/${upper.java.level}</compileSourceRoot>
+                            </compileSourceRoots>
+                            <outputDirectory>${project.build.outputDirectory}/META-INF/versions/${upper.java.level}</outputDirectory>
+                            <source>${upper.java.level}</source>
+                            <target>${upper.java.level}</target>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Implementation-Vendor>${vendor.name}</Implementation-Vendor>
+                        <Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id>
+                        <Implementation-Build-Id>${project.version} - ${scmBranch}-${buildNumber}, ${timestamp}</Implementation-Build-Id>
+                        <Multi-Release>true</Multi-Release>
+                        <Import-Package>
+                            javax.activation;version=!,
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
                 <configuration>
@@ -90,9 +164,43 @@
                         --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2=java.xml.bind
                         --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2.schemagen=java.xml.bind
                         --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2.schemagen.xmlidref=java.xml.bind
+                        --add-opens java.base/java.lang=org.glassfish.jaxb.runtime
+                        --add-opens java.base/java.lang.reflect=org.glassfish.jaxb.runtime
+                        --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2.runtime.reflect.opt=java.xml.bind
                     </argLine>
                 </configuration>
             </plugin>
+
+            <!-- Run tests on multi-release jar -->
+            <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>
+                    <systemProperties>
+                        <property>
+                            <name>java.util.logging.config.file</name>
+                            <value>logging.properties</value>
+                        </property>
+                    </systemProperties>
+                    <argLine>
+                        --add-opens java.base/java.lang=com.sun.xml.bind
+                        --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2=java.xml.bind
+                        --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2.schemagen=java.xml.bind
+                        --add-opens org.glassfish.jaxb.runtime/com.sun.xml.bind.v2.schemagen.xmlidref=java.xml.bind
+                    </argLine>
+                    <includes>
+                        <include>**/*TestMultiRelease.java</include>
+                    </includes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java
new file mode 100644
index 0000000..7e15a18
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.sun.xml.bind.Utils;
+import com.sun.xml.bind.v2.bytecode.ClassTailor;
+
+/**
+ * Stub version of {@link AccessorInjector} for java versions >= 9
+ *
+ * @author Daniel Kec
+ */
+class AccessorInjector {
+
+    protected static final boolean noOptimize = true;
+
+    public static Class<?> prepare(
+            Class beanClass, String templateClassName, String newClassName, String... replacements) {
+        return null;
+    }
+
+}
diff --git a/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/Injector.java b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/Injector.java
new file mode 100644
index 0000000..9ac324e
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/Injector.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+/**
+ * Stub version of {@link Injector} for java versions >= 9
+ *
+ * @author Daniel Kec
+ */
+final class Injector {
+
+    /**
+     * Injects a new class into the given class loader.
+     *
+     * @return null
+     * if it fails to inject.
+     */
+    static Class inject(ClassLoader cl, String className, byte[] image) {
+        return null;
+    }
+
+    /**
+     * Returns the already injected class, or null.
+     */
+    static Class find(ClassLoader cl, String className) {
+        return null;
+    }
+}
diff --git a/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedAccessorFactory.java b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedAccessorFactory.java
new file mode 100644
index 0000000..a389e2f
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedAccessorFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import com.sun.xml.bind.v2.runtime.reflect.Accessor;
+
+import java.util.logging.Logger;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+
+import java.util.logging.Logger;
+
+/**
+ * Stub version of {@link OptimizedAccessorFactory} for java versions >= 9
+ *
+ * @author Daniel Kec
+ */
+public abstract class OptimizedAccessorFactory {
+    private OptimizedAccessorFactory() {} // no instantiation please
+
+    private static final Logger logger = Logger.getLogger(OptimizedAccessorFactory.class.getName());
+
+
+    public static <B, V> Accessor<B, V> get(Method param1, Method param2) {
+        return getStub();
+    }
+
+    public static <B, V> Accessor<B, V> get(Field param) {
+        return getStub();
+    }
+
+    private static <B, V> Accessor<B, V> getStub(){
+        logger.finer("Optimization is not available since java 9");
+        return null;
+    }
+
+}
diff --git a/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedTransducedAccessorFactory.java b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedTransducedAccessorFactory.java
new file mode 100644
index 0000000..88696b1
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java-mr/9/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedTransducedAccessorFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
+import com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor;
+
+import java.util.logging.Logger;
+
+/**
+ * Stub version of {@link OptimizedTransducedAccessorFactory} for java versions >= 9
+ *
+ * @author Daniel Kec
+ */
+public abstract class OptimizedTransducedAccessorFactory {
+    private OptimizedTransducedAccessorFactory() {} // no instantiation please
+
+    private static final Logger logger = Logger.getLogger(OptimizedTransducedAccessorFactory.class.getName());
+
+    public static final TransducedAccessor get(RuntimePropertyInfo prop) {
+        logger.finer("Optimization is not available since java 9");
+        return null;
+    }
+}
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeBuiltinLeafInfoImpl.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeBuiltinLeafInfoImpl.java
index 46177e5..fc412ee 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeBuiltinLeafInfoImpl.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeBuiltinLeafInfoImpl.java
@@ -113,6 +113,10 @@
         return false;
     }
 
+    public final boolean isDefault() {
+        return true;
+    }
+
     public void declareNamespace(T o, XMLSerializer w) throws AccessorException {
     }
 
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeClassInfoImpl.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeClassInfoImpl.java
index ceb3921..50f0085 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeClassInfoImpl.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeClassInfoImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -332,6 +332,10 @@
             return xacc.useNamespace();
         }
 
+        public boolean isDefault() {
+            return false;
+        }
+
         public void declareNamespace(BeanT bean, XMLSerializer w) throws AccessorException {
             try {
                 xacc.declareNamespace(bean,w);
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeEnumLeafInfoImpl.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeEnumLeafInfoImpl.java
index e1874d1..2bd2cb0 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeEnumLeafInfoImpl.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/model/impl/RuntimeEnumLeafInfoImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -95,6 +95,10 @@
         return new QName[]{getTypeName()};
     }
 
+    public boolean isDefault() {
+        return false;
+    }
+
     @Override
     public Class getClazz() {
         return clazz;
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/FilterTransducer.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/FilterTransducer.java
index deb321f..e68cad7 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/FilterTransducer.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/FilterTransducer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -32,6 +32,12 @@
         this.core = core;
     }
 
+    public final boolean isDefault() {
+        // this must be used as a decorating transducer,
+        // so it may never be default.
+        return false;
+    }
+
     public boolean useNamespace() {
         return core.useNamespace();
     }
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/Transducer.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/Transducer.java
index f088a3e..d95d3d2 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/Transducer.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/Transducer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -20,6 +20,7 @@
 import com.sun.istack.NotNull;
 import com.sun.xml.bind.api.AccessorException;
 import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
+import com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedTransducedAccessorFactory;
 
 import org.xml.sax.SAXException;
 
@@ -39,6 +40,14 @@
 public interface Transducer<ValueT> {
 
     /**
+     * If this {@link Transducer} is the default transducer for the <code>ValueT</code>,
+     * this method returns true.
+     *
+     * Used exclusively by {@link OptimizedTransducedAccessorFactory#get(RuntimePropertyInfo)}
+     */
+    boolean isDefault();
+
+    /**
      * If true, this {@link Transducer} doesn't declare any namespace,
      * and therefore {@link #declareNamespace(Object, XMLSerializer)} is no-op.
      *
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/Accessor.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/Accessor.java
index 2649993..410ace8 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/Accessor.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/Accessor.java
@@ -31,6 +31,7 @@
 import com.sun.xml.bind.v2.model.core.Adapter;
 import com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder;
 import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
+import com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory;
 import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
 import com.sun.xml.bind.v2.runtime.unmarshaller.Receiver;
 import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
@@ -247,6 +248,14 @@
 
         @Override
         public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) {
+            if (context != null && context.fastBoot) {
+                // let's not waste time on doing this for the sake of faster boot.
+                return this;
+            }
+            Accessor<BeanT, ValueT> acc = OptimizedAccessorFactory.get(f);
+            if (acc != null) {
+                return acc;
+            }
             return this;
         }
     }
@@ -353,6 +362,20 @@
 
         @Override
         public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) {
+            if (getter == null || setter == null) {
+                // if we aren't complete, OptimizedAccessor won't always work
+                return this;
+            }
+            if (context != null && context.fastBoot) {
+                // let's not waste time on doing this for the sake of faster boot.
+                return this;
+            }
+
+            Accessor<BeanT, ValueT> acc = OptimizedAccessorFactory.get(getter, setter);
+            if (acc != null) {
+                return acc;
+            }
+
             return this;
         }
     }
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/TransducedAccessor.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/TransducedAccessor.java
index abc7557..29746be 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/TransducedAccessor.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/TransducedAccessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0, which is available at
@@ -30,6 +30,7 @@
 import com.sun.xml.bind.v2.runtime.Transducer;
 import com.sun.xml.bind.v2.runtime.XMLSerializer;
 import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
+import com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedTransducedAccessorFactory;
 import com.sun.xml.bind.v2.runtime.unmarshaller.Patcher;
 import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
 import com.sun.xml.bind.v2.runtime.unmarshaller.LocatorEx;
@@ -133,6 +134,13 @@
         if(prop.id()==ID.IDREF)
             return new IDREFTransducedAccessorImpl(prop.getAccessor());
 
+        if (xducer.isDefault() && context != null && !context.fastBoot) {
+            TransducedAccessor xa = OptimizedTransducedAccessorFactory.get(prop);
+            if (xa != null) {
+                return xa;
+            }
+        }
+
         if(xducer.useNamespace())
             return new CompositeContextDependentTransducedAccessorImpl( context, xducer, prop.getAccessor() );
         else
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java
index d57489b..62b2c43 100644
--- a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/AccessorInjector.java
@@ -11,6 +11,7 @@
 package com.sun.xml.bind.v2.runtime.reflect.opt;
 
 import java.io.InputStream;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import com.sun.xml.bind.Utils;
@@ -32,6 +33,41 @@
     }
 
     /**
+     * Loads the optimized class and returns it.
+     *
+     * @return null
+     *      if it fails for some reason.
+     */
+    public static Class<?> prepare(
+            Class beanClass, String templateClassName, String newClassName, String... replacements ) {
+
+        if(noOptimize)
+            return null;
+
+        try {
+            ClassLoader cl = SecureLoader.getClassClassLoader(beanClass);
+            if(cl==null)    return null;    // how do I inject classes to this "null" class loader? for now, back off.
+
+            Class c = Injector.find(cl,newClassName);
+            if (c==null) {
+                byte[] image = tailor(templateClassName,newClassName,replacements);
+                if (image==null) {
+                    return null;
+                }
+                c = Injector.inject(cl,newClassName,image);
+                if (c == null) {
+                    Injector.find(cl, newClassName);
+                }
+            }
+            return c;
+        } catch(SecurityException e) {
+            // we don't have enough permission to do this
+            logger.log(Level.INFO,"Unable to create an optimized TransducedAccessor ",e);
+            return null;
+        }
+    }
+
+    /**
      * Customizes a class file by replacing constant pools.
      *
      * @param templateClassName
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/Injector.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/Injector.java
new file mode 100644
index 0000000..746fc99
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/Injector.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.ref.WeakReference;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.sun.xml.bind.v2.runtime.reflect.Accessor;
+import java.lang.reflect.Field;
+import java.security.CodeSource;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+
+/**
+ * A {@link ClassLoader} used to "inject" optimized accessor classes
+ * into the VM.
+ *
+ * <p>
+ * Its parent class loader needs to be set to the one that can see the user
+ * class.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+final class Injector {
+
+    /**
+     * {@link Injector}s keyed by their parent {@link ClassLoader}.
+     *
+     * We only need one injector per one user class loader.
+     */
+    private static final ReentrantReadWriteLock irwl = new ReentrantReadWriteLock();
+    private static final Lock ir = irwl.readLock();
+    private static final Lock iw = irwl.writeLock();
+    private static final Map<ClassLoader, WeakReference<Injector>> injectors =
+            new WeakHashMap<ClassLoader, WeakReference<Injector>>();
+    private static final Logger logger = Logger.getLogger(Injector.class.getName());
+    /**
+     * Injects a new class into the given class loader.
+     *
+     * @return null
+     *      if it fails to inject.
+     */
+    static Class inject(ClassLoader cl, String className, byte[] image) {
+        Injector injector = get(cl);
+        if (injector != null) {
+            return injector.inject(className, image);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the already injected class, or null.
+     */
+    static Class find(ClassLoader cl, String className) {
+        Injector injector = get(cl);
+        if (injector != null) {
+            return injector.find(className);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Gets or creates an {@link Injector} for the given class loader.
+     *
+     * @return null
+     *      if it fails.
+     */
+    private static Injector get(ClassLoader cl) {
+        Injector injector = null;
+        WeakReference<Injector> wr;
+        ir.lock();
+        try {
+            wr = injectors.get(cl);
+        } finally {
+            ir.unlock();
+        }
+        if (wr != null) {
+            injector = wr.get();
+        }
+        if (injector == null) {
+            try {
+                wr = new WeakReference<Injector>(injector = new Injector(cl));
+                iw.lock();
+                try {
+                    if (!injectors.containsKey(cl)) {
+                        injectors.put(cl, wr);
+                    }
+                } finally {
+                    iw.unlock();
+                }
+            } catch (SecurityException e) {
+                logger.log(Level.FINE, "Unable to set up a back-door for the injector", e);
+                return null;
+            }
+        }
+        return injector;
+    }
+    /**
+     * Injected classes keyed by their names.
+     */
+    private final Map<String, Class> classes = new HashMap<>();
+    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+    private final Lock r = rwl.readLock();
+    private final Lock w = rwl.writeLock();
+    private final ClassLoader parent;
+    /**
+     * True if this injector is capable of injecting accessors.
+     * False otherwise, which happens if this classloader can't see {@link Accessor}.
+     */
+    private final boolean loadable;
+    private static Method defineClass;
+    private static Method resolveClass;
+    private static Method findLoadedClass;
+    private static Object U;
+
+    static {
+        try {
+            Method[] m = AccessController.doPrivileged(
+                    new PrivilegedAction<Method[]>() {
+                @Override
+                public Method[] run() {
+                    return new Method[]{
+                        getMethod(ClassLoader.class, "defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE),
+                        getMethod(ClassLoader.class, "resolveClass", Class.class),
+                        getMethod(ClassLoader.class, "findLoadedClass", String.class)
+                    };
+                }
+            }
+            );
+            defineClass = m[0];
+            resolveClass = m[1];
+            findLoadedClass = m[2];
+        } catch (Throwable t) {
+            try {
+                U = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    @Override
+                    public Object run() throws Exception {
+                        Class u = classForNames("sun.misc.Unsafe", "jdk.internal.misc.Unsafe");
+                        Field theUnsafe = u.getDeclaredField("theUnsafe");
+                        theUnsafe.setAccessible(true);
+                        return theUnsafe.get(null);
+                    }
+                });
+                defineClass = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
+                    @Override
+                    public Method run() throws Exception {
+                        try {
+                            return U.getClass().getMethod("defineClass",
+                                    new Class[]{String.class,
+                                        byte[].class,
+                                        Integer.TYPE,
+                                        Integer.TYPE,
+                                        ClassLoader.class,
+                                        ProtectionDomain.class});
+                        } catch (NoSuchMethodException | SecurityException ex) {
+                            throw ex;
+                        }
+                    }
+                });
+            } catch (SecurityException | PrivilegedActionException ex) {
+                Logger.getLogger(Injector.class.getName()).log(Level.SEVERE, null, ex);
+            }
+        }
+    }
+
+    private static Class classForNames(String... names) throws ClassNotFoundException {
+        for (String name : names) {
+            try {
+                return Class.forName(name);
+            } catch (ClassNotFoundException e) {
+                continue;
+            }
+        }
+
+        throw new ClassNotFoundException(
+                String.format("No class found for supplied FQDNs %s",
+                        Arrays.toString(names)));
+    }
+
+    private static Method getMethod(final Class<?> c, final String methodname, final Class<?>... params) {
+        try {
+            Method m = c.getDeclaredMethod(methodname, params);
+            m.setAccessible(true);
+            return m;
+        } catch (NoSuchMethodException e) {
+            throw new NoSuchMethodError(e.getMessage());
+        }
+    }
+
+    private Injector(ClassLoader parent) {
+        this.parent = parent;
+        assert parent != null;
+
+        boolean loadableCheck = false;
+
+        try {
+            loadableCheck = parent.loadClass(Accessor.class.getName()) == Accessor.class;
+        } catch (ClassNotFoundException e) {
+            // not loadable
+        }
+
+        this.loadable = loadableCheck;
+    }
+
+    @SuppressWarnings("LockAcquiredButNotSafelyReleased")
+    private Class inject(String className, byte[] image) {
+        if (!loadable) // this injector cannot inject anything
+        {
+            return null;
+        }
+
+        boolean wlocked = false;
+        boolean rlocked = false;
+        try {
+
+            r.lock();
+            rlocked = true;
+
+            Class c = classes.get(className);
+
+            // Unlock now during the findLoadedClass process to avoid
+            // deadlocks
+            r.unlock();
+            rlocked = false;
+
+            //find loaded class from classloader
+            if (c == null && findLoadedClass != null) {
+
+                try {
+                    c = (Class) findLoadedClass.invoke(parent, className.replace('/', '.'));
+                } catch (IllegalArgumentException | IllegalAccessException e) {
+                    logger.log(Level.FINE, "Unable to find " + className, e);
+                } catch (InvocationTargetException e) {
+                    Throwable t = e.getTargetException();
+                    logger.log(Level.FINE, "Unable to find " + className, t);
+                }
+
+                if (c != null) {
+
+                    w.lock();
+                    wlocked = true;
+
+                    classes.put(className, c);
+
+                    w.unlock();
+                    wlocked = false;
+
+                    return c;
+                }
+            }
+
+            if (c == null) {
+
+                r.lock();
+                rlocked = true;
+
+                c = classes.get(className);
+
+                // Unlock now during the define/resolve process to avoid
+                // deadlocks
+                r.unlock();
+                rlocked = false;
+
+                if (c == null) {
+
+                    // we need to inject a class into the
+                    try {
+                        if (resolveClass != null) {
+                            c = (Class) defineClass.invoke(parent, className.replace('/', '.'), image, 0, image.length);
+                            resolveClass.invoke(parent, c);
+                        } else {
+                            c = (Class) defineClass.invoke(U, className.replace('/', '.'), image, 0, image.length, parent, Injector.class.getProtectionDomain());
+                        }
+                    } catch (IllegalAccessException  e) {
+                        logger.log(Level.FINE, "Unable to inject " + className, e);
+                        return null;
+                    } catch (InvocationTargetException e) {
+                        Throwable t = e.getTargetException();
+                        if (t instanceof LinkageError) {
+                            logger.log(Level.FINE, "duplicate class definition bug occured? Please report this : " + className, t);
+                        } else {
+                            logger.log(Level.FINE, "Unable to inject " + className, t);
+                        }
+                        return null;
+                    } catch (SecurityException e) {
+                        logger.log(Level.FINE, "Unable to inject " + className, e);
+                        return null;
+                    } catch (LinkageError e) {
+                        logger.log(Level.FINE, "Unable to inject " + className, e);
+                        return null;
+                    }
+
+                    w.lock();
+                    wlocked = true;
+
+                    // During the time we were unlocked, we could have tried to
+                    // load the class from more than one thread. Check now to see
+                    // if someone else beat us to registering this class
+                    if (!classes.containsKey(className)) {
+                        classes.put(className, c);
+                    }
+
+                    w.unlock();
+                    wlocked = false;
+                }
+            }
+            return c;
+        } finally {
+            if (rlocked) {
+                r.unlock();
+            }
+            if (wlocked) {
+                w.unlock();
+            }
+        }
+    }
+
+    private Class find(String className) {
+        r.lock();
+        try {
+            return classes.get(className);
+        } finally {
+            r.unlock();
+        }
+    }
+}
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedAccessorFactory.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedAccessorFactory.java
new file mode 100644
index 0000000..733bfca
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedAccessorFactory.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.sun.xml.bind.v2.runtime.reflect.Accessor;
+import com.sun.xml.bind.v2.runtime.RuntimeUtil;
+
+import static com.sun.xml.bind.v2.bytecode.ClassTailor.toVMClassName;
+import static com.sun.xml.bind.v2.bytecode.ClassTailor.toVMTypeName;
+
+/**
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class OptimizedAccessorFactory {
+    private OptimizedAccessorFactory() {} // no instanciation please
+
+    public static final boolean noOptimization = Boolean.getBoolean(OptimizedAccessorFactory.class.getName()+".noOptimization");
+
+    private static final Logger logger = Logger.getLogger(OptimizedAccessorFactory.class.getName());
+
+    private static final String fieldTemplateName;
+    private static final String methodTemplateName;
+
+    static {
+        String s = FieldAccessor_Byte.class.getName();
+        fieldTemplateName = s.substring(0,s.length()-"Byte".length()).replace('.','/');
+
+        s = MethodAccessor_Byte.class.getName();
+        methodTemplateName = s.substring(0,s.length()-"Byte".length()).replace('.','/');
+    }
+
+    /**
+     * Gets the optimized {@link Accessor} that accesses the given getter/setter.
+     *
+     * @return null
+     *      if for some reason it fails to create an optimized version.
+     */
+    public static final <B,V> Accessor<B,V> get(Method getter, Method setter) {
+        if(noOptimization){
+            return null;
+        }
+
+        // make sure the method signatures are what we expect
+        if(getter.getParameterTypes().length!=0)
+            return null;
+        Class<?>[] sparams = setter.getParameterTypes();
+        if(sparams.length!=1)
+            return null;
+        if(sparams[0]!=getter.getReturnType())
+            return null;
+        if(setter.getReturnType()!=Void.TYPE)
+            return null;
+        if(getter.getDeclaringClass()!=setter.getDeclaringClass())
+            return null;
+        if(Modifier.isPrivate(getter.getModifiers()) || Modifier.isPrivate(setter.getModifiers()))
+            // we can't access private fields
+            return null;
+
+        Class t = sparams[0];
+        String typeName = t.getName().replace('.','_');
+        if (t.isArray()) {
+            typeName = "AOf_";
+            String compName = t.getComponentType().getName().replace('.','_');
+            while (compName.startsWith("[L")) {
+                compName = compName.substring(2);
+                typeName += "AOf_";
+            }
+            typeName = typeName + compName;
+        }
+
+        String newClassName = toVMClassName(getter.getDeclaringClass())+"$JaxbAccessorM_"+getter.getName()+'_'+setter.getName()+'_'+typeName;
+        Class opt;
+
+        if(t.isPrimitive())
+            opt = AccessorInjector.prepare( getter.getDeclaringClass(),
+                    methodTemplateName+RuntimeUtil.primitiveToBox.get(t).getSimpleName(),
+                    newClassName,
+                    toVMClassName(Bean.class),
+                    toVMClassName(getter.getDeclaringClass()),
+                    "get_"+t.getName(),
+                    getter.getName(),
+                    "set_"+t.getName(),
+                    setter.getName());
+        else
+            opt = AccessorInjector.prepare( getter.getDeclaringClass(),
+                    methodTemplateName+"Ref",
+                    newClassName,
+                    toVMClassName(Bean.class),
+                    toVMClassName(getter.getDeclaringClass()),
+                    toVMClassName(Ref.class),
+                    toVMClassName(t),
+                    "()"+toVMTypeName(Ref.class),
+                    "()"+toVMTypeName(t),
+                    '('+toVMTypeName(Ref.class)+")V",
+                    '('+toVMTypeName(t)+")V",
+                    "get_ref",
+                    getter.getName(),
+                    "set_ref",
+                    setter.getName());
+
+        if(opt==null)
+            return null;
+
+        Accessor<B,V> acc = instanciate(opt);
+        if (acc!=null) {
+            if (logger.isLoggable(Level.FINE)) {
+                logger.log(Level.FINE, "Using optimized Accessor for {0} and {1}", new Object[]{getter, setter});
+            }
+        }
+        return acc;
+    }
+
+
+    /**
+     * Gets the optimized {@link Accessor} that accesses the given field.
+     *
+     * @return null
+     *      if for some reason it fails to create an optimized version.
+     */
+    public static final <B,V> Accessor<B,V> get(Field field) {
+        if(noOptimization){
+            return null;
+        }
+
+        int mods = field.getModifiers();
+        if(Modifier.isPrivate(mods) || Modifier.isFinal(mods))
+            // we can't access private fields
+            return null;
+
+        String newClassName = toVMClassName(field.getDeclaringClass())+"$JaxbAccessorF_"+field.getName();
+
+        Class opt;
+
+        if(field.getType().isPrimitive())
+            opt = AccessorInjector.prepare( field.getDeclaringClass(),
+                    fieldTemplateName+RuntimeUtil.primitiveToBox.get(field.getType()).getSimpleName(),
+                    newClassName,
+                    toVMClassName(Bean.class),
+                    toVMClassName(field.getDeclaringClass()),
+                    "f_"+field.getType().getName(),
+                    field.getName() );
+        else
+            opt = AccessorInjector.prepare( field.getDeclaringClass(),
+                    fieldTemplateName+"Ref",
+                    newClassName,
+                    toVMClassName(Bean.class),
+                    toVMClassName(field.getDeclaringClass()),
+                    toVMClassName(Ref.class),
+                    toVMClassName(field.getType()),
+                    toVMTypeName(Ref.class),
+                    toVMTypeName(field.getType()),
+                    "f_ref",
+                    field.getName() );
+
+        if(opt==null)
+            return null;
+
+        Accessor<B,V> acc = instanciate(opt);
+        if (acc!=null) {
+            if (logger.isLoggable(Level.FINE)) {
+                logger.log(Level.FINE, "Using optimized Accessor for {0}", field);
+            }
+        }
+        return acc;
+    }
+
+    private static <B,V> Accessor<B,V> instanciate(Class opt) {
+        try {
+            return (Accessor<B,V>)opt.newInstance();
+        } catch (InstantiationException e) {
+            logger.log(Level.INFO,"failed to load an optimized Accessor",e);
+        } catch (IllegalAccessException e) {
+            logger.log(Level.INFO,"failed to load an optimized Accessor",e);
+        } catch (SecurityException e) {
+            logger.log(Level.INFO,"failed to load an optimized Accessor",e);
+        }
+        return null;
+    }
+}
diff --git a/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedTransducedAccessorFactory.java b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedTransducedAccessorFactory.java
new file mode 100644
index 0000000..689012e
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/main/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizedTransducedAccessorFactory.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.sun.xml.bind.v2.model.core.TypeInfo;
+import com.sun.xml.bind.v2.model.runtime.RuntimeClassInfo;
+import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
+import com.sun.xml.bind.v2.runtime.reflect.Accessor;
+import com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor;
+
+import static com.sun.xml.bind.v2.bytecode.ClassTailor.toVMClassName;
+
+/**
+ * Prepares optimized {@link TransducedAccessor} from templates.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class OptimizedTransducedAccessorFactory {
+    private OptimizedTransducedAccessorFactory() {} // no instantiation please
+
+    // http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#75929
+    // "same runtime package"
+
+    private static final Logger logger = Logger.getLogger(OptimizedTransducedAccessorFactory.class.getName());
+
+    private static final String fieldTemplateName;
+    private static final String methodTemplateName;
+
+    static {
+        String s = TransducedAccessor_field_Byte.class.getName();
+        fieldTemplateName = s.substring(0,s.length()-"Byte".length()).replace('.','/');
+
+        s = TransducedAccessor_method_Byte.class.getName();
+        methodTemplateName = s.substring(0,s.length()-"Byte".length()).replace('.','/');
+    }
+
+    /**
+     * Gets the optimized {@link TransducedAccessor} if possible.
+     *
+     * @return null
+     *      if for some reason it fails to create an optimized version.
+     */
+    public static final TransducedAccessor get(RuntimePropertyInfo prop) {
+        if(OptimizedAccessorFactory.noOptimization){
+            return null;
+        }
+
+        Accessor acc = prop.getAccessor();
+
+        // consider using an optimized TransducedAccessor implementations.
+        Class opt=null;
+
+        TypeInfo<Type,Class> parent = prop.parent();
+        if(!(parent instanceof RuntimeClassInfo))
+            return null;
+        
+        Class dc = ((RuntimeClassInfo)parent).getClazz();
+        String newClassName = toVMClassName(dc)+"_JaxbXducedAccessor_"+prop.getName();
+
+
+        if(acc instanceof Accessor.FieldReflection) {
+            // TODO: we also need to make sure that the default xducer is used.
+            Accessor.FieldReflection racc = (Accessor.FieldReflection) acc;
+            Field field = racc.f;
+
+            int mods = field.getModifiers();
+            if(Modifier.isPrivate(mods) || Modifier.isFinal(mods))
+                // we can't access private fields.
+                // TODO: think about how to improve this case
+                return null;
+
+            Class<?> t = field.getType();
+            if(t.isPrimitive())
+                opt = AccessorInjector.prepare( dc,
+                    fieldTemplateName+suffixMap.get(t),
+                    newClassName,
+                    toVMClassName(Bean.class),
+                    toVMClassName(dc),
+                    "f_"+t.getName(),
+                    field.getName() );
+        }
+
+        if(acc.getClass()==Accessor.GetterSetterReflection.class) {
+            Accessor.GetterSetterReflection gacc = (Accessor.GetterSetterReflection) acc;
+
+            if(gacc.getter==null || gacc.setter==null)
+                return null;    // incomplete
+
+            Class<?> t = gacc.getter.getReturnType();
+
+            if(Modifier.isPrivate(gacc.getter.getModifiers())
+            || Modifier.isPrivate(gacc.setter.getModifiers()))
+                // we can't access private methods.
+                return null;
+
+
+            if(t.isPrimitive())
+                opt = AccessorInjector.prepare( dc,
+                    methodTemplateName+suffixMap.get(t),
+                    newClassName,
+                    toVMClassName(Bean.class),
+                    toVMClassName(dc),
+                    "get_"+t.getName(),
+                    gacc.getter.getName(),
+                    "set_"+t.getName(),
+                    gacc.setter.getName());
+        }
+
+        if(opt==null)
+            return null;
+
+        logger.log(Level.FINE,"Using optimized TransducedAccessor for "+prop.displayName());
+
+
+        try {
+            return (TransducedAccessor)opt.newInstance();
+        } catch (InstantiationException e) {
+            logger.log(Level.INFO,"failed to load an optimized TransducedAccessor",e);
+        } catch (IllegalAccessException e) {
+            logger.log(Level.INFO,"failed to load an optimized TransducedAccessor",e);
+        } catch (SecurityException e) {
+            logger.log(Level.INFO,"failed to load an optimized TransducedAccessor",e);
+        }
+        return null;
+    }
+
+    private static final Map<Class,String> suffixMap = new HashMap<Class, String>();
+
+    static {
+        suffixMap.put(Byte.TYPE,"Byte");
+        suffixMap.put(Short.TYPE,"Short");
+        suffixMap.put(Integer.TYPE,"Integer");
+        suffixMap.put(Long.TYPE,"Long");
+        suffixMap.put(Boolean.TYPE,"Boolean");
+        suffixMap.put(Float.TYPE,"Float");
+        suffixMap.put(Double.TYPE,"Double");
+    }
+
+}
diff --git a/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/test/AbstractTestMultiRelease.java b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/test/AbstractTestMultiRelease.java
new file mode 100644
index 0000000..d84c973
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/test/AbstractTestMultiRelease.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package com.sun.xml.bind.test;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/**
+ * Tools for testing multi-release jar, makes possible to skip tests marked for java version above current one.
+ *
+ * @author Daniel Kec
+ * @see SinceJava9
+ */
+public abstract class AbstractTestMultiRelease {
+
+    @Rule
+    public TestWatcher javaVersionWatcher = new TestWatcher() {
+        @Override
+        protected void starting(Description description) {
+            if (description.getAnnotation(SinceJava9.class) != null) {
+                if (!isAboveOrEqualJava9()) {
+                    Assume.assumeTrue("Skipping, test is applicable since java 9 and higher.", false);
+                }
+            }
+            super.starting(description);
+        }
+    };
+
+    protected boolean isAboveOrEqualJava9() {
+        try {
+            //Runtime#version is available since java 9
+            Runtime.class.getMethod("version");
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/test/SinceJava9.java b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/test/SinceJava9.java
new file mode 100644
index 0000000..039f337
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/test/SinceJava9.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package com.sun.xml.bind.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker for test methods which are assumed to be tested only on Java 9 and higher
+ *
+ * @see AbstractTestMultiRelease
+ * @author Daniel Kec
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface SinceJava9 {
+
+}
diff --git a/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizationTest.java b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizationTest.java
new file mode 100644
index 0000000..1546244
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizationTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import java.io.StringReader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * Test flag for disabling optimization
+ * com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory.noOptimization=true/false
+ *
+ * @author Daniel Kec
+ */
+public class OptimizationTest {
+
+    private static final String EXPECTED_ACCESSOR_CLASS_REF = Book.class.getName() + "$JaxbAccessorM_getAuthor_setAuthor_java_lang_String";
+    private static final String OPTIMIZATION_ON = "AUTHOR_EXPECTING_OPTIMIZATION_ON";
+    private static final String OPTIMIZATION_OFF = "AUTHOR_EXPECTING_OPTIMIZATION_OFF";
+
+    @Test
+    public void checkNoOptimizationFlagFalse() throws JAXBException {
+        Book book = unmarshal(OPTIMIZATION_ON);
+        Assert.assertTrue(book.errorMessage, book.errorMessage == null);
+        Assert.assertEquals(OPTIMIZATION_ON, book.getAuthor());
+    }
+
+    @Test
+    public void checkNoOptimizationFlagTrue() throws JAXBException, NoSuchFieldException, IllegalAccessException {
+        try {
+            setNoOptimizationFlag(true);
+            Book book = unmarshal(OPTIMIZATION_OFF);
+            Assert.assertTrue(book.errorMessage, book.errorMessage == null);
+            Assert.assertEquals(OPTIMIZATION_OFF, book.getAuthor());
+        } finally {
+            setNoOptimizationFlag(false);
+        }
+    }
+
+    private Book unmarshal(String autor) throws JAXBException {
+        JAXBContext context = JAXBContext.newInstance(Book.class);
+        Unmarshaller unmarshaller = context.createUnmarshaller();
+        return (Book) unmarshaller.unmarshal(new StringReader(
+                String.format("<book><author>%s</author></book>", autor)));
+    }
+
+    private void setNoOptimizationFlag(boolean flag) throws NoSuchFieldException, IllegalAccessException {
+        Field noOptimizationField = OptimizedAccessorFactory.class.getField("noOptimization");
+        Field modifiersField = Field.class.getDeclaredField("modifiers");
+        modifiersField.setAccessible(true);
+        noOptimizationField.setAccessible(true);
+        modifiersField.setInt(noOptimizationField, noOptimizationField.getModifiers() & ~Modifier.FINAL);
+        noOptimizationField.setBoolean(OptimizedAccessorFactory.class, true);
+    }
+
+    @XmlRootElement
+    static class Book {
+        private String author;
+
+        @XmlTransient
+        String errorMessage = null;
+
+        public String getAuthor() {
+            return author;
+        }
+
+        public void setAuthor(String author) {
+            String artificialInnerClass = null;
+            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+            for (StackTraceElement el : stackTrace) {
+                if (el.getClassName().equals(EXPECTED_ACCESSOR_CLASS_REF)) {
+                    artificialInnerClass = el.getClassName();
+                    break;
+                }
+            }
+
+            if (author.equals(OPTIMIZATION_OFF) && artificialInnerClass != null) {
+                this.errorMessage = String.format("Artificial accessor class detected %s", artificialInnerClass);
+            } else if (author.equals(OPTIMIZATION_ON) && artificialInnerClass == null) {
+                this.errorMessage = String.format("Expected artificial accessor class not found: %s", EXPECTED_ACCESSOR_CLASS_REF);
+            }
+            this.author = author;
+        }
+    }
+}
diff --git a/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizationTestMultiRelease.java b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizationTestMultiRelease.java
new file mode 100644
index 0000000..8ed8485
--- /dev/null
+++ b/jaxb-ri/runtime/impl/src/test/java/com/sun/xml/bind/v2/runtime/reflect/opt/OptimizationTestMultiRelease.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package com.sun.xml.bind.v2.runtime.reflect.opt;
+
+import com.sun.xml.bind.test.AbstractTestMultiRelease;
+import com.sun.xml.bind.test.SinceJava9;
+import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
+import com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+
+/**
+ * Test multi release for optimization feature,
+ * Feature needs to be disabled since java 9.
+ *
+ * @author Daniel Kec
+ */
+public class OptimizationTestMultiRelease extends AbstractTestMultiRelease {
+
+    @SuppressWarnings("WeakerAccess")
+    public static String TEST_FIELD = "TEST_FIELD";
+
+    @Test
+    @SinceJava9
+    public void testStubbedAccessorInjector() {
+        //Stubbed version doesn't mind null param
+        Assert.assertNull("Injector should always return null since Java 9",
+                AccessorInjector.prepare(null, null, null, null));
+    }
+
+    @Test
+    @SinceJava9
+    public void testStubbedInjector() {
+        //Stubbed version doesn't mind null param
+        Assert.assertNull("Injector should always return null since Java 9",
+                Injector.find(null, null));
+    }
+
+    @Test
+    @SinceJava9
+    public void testStubbedOptimizedAccessorFactory() throws NoSuchFieldException {
+        Field field = this.getClass().getDeclaredField(TEST_FIELD);
+        Assert.assertNull("OptimizedAccessorFactory should always return null since Java 9", OptimizedAccessorFactory.get(field));
+    }
+
+    @Test
+    @SinceJava9
+    @SuppressWarnings("ConstantConditions")
+    public void testStubbedOptimizedTransducedAccessorFactory() throws NoSuchFieldException {
+        final String errMessage = "OptimizedTransducedAccessorFactory should always return null since Java 9";
+        try {
+            //Stubbed version doesn't mind null param
+            TransducedAccessor accessor = OptimizedTransducedAccessorFactory.get((RuntimePropertyInfo) null);
+            Assert.assertNull(errMessage, accessor);
+        } catch (NullPointerException npe) {
+            Assert.fail(errMessage);
+        }
+    }
+
+}
diff --git a/jaxb-ri/tools/config/copyright-exclude b/jaxb-ri/tools/config/copyright-exclude
index eed5c68..79c07a7 100644
--- a/jaxb-ri/tools/config/copyright-exclude
+++ b/jaxb-ri/tools/config/copyright-exclude
@@ -1,6 +1,7 @@
 /MANIFEST.MF
 /META-INF/services/
 /README
+.iml
 .gif
 .jpg
 .png