Fixed #23616 by refactoring

- cleanup around leaky api of generateAndLoad which relied on consistency of
  it's parameters
- the anchor class was moved to the generator to keep JDK rules
- removeRedundantMethods was and is slow -> TODO: optimize + test
- ServiceInterfaceGenerator changed output class name and package, because
  original addition of jaxws.internal is forbidden in JDK11+
- tests were enhanced to be able to compare loading of cached classes.
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EJBUtils.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EJBUtils.java
index ec4daf1..1ba97fa 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EJBUtils.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/EJBUtils.java
@@ -54,6 +54,7 @@
 
 import org.glassfish.pfl.dynamic.codegen.spi.Wrapper;
 
+import static com.sun.ejb.codegen.GenericHomeGenerator.GENERIC_HOME_CLASSNAME;
 import static java.util.logging.Level.FINE;
 import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.DUMP_AFTER_SETUP_VISITOR;
 import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.TRACE_BYTE_CODE_GENERATION;
@@ -177,48 +178,61 @@
 
     private static String getClassPackageName(String intf) {
         int dot = intf.lastIndexOf('.');
-        return (dot == -1) ? null : intf.substring(0, dot);
+        return dot == -1 ? null : intf.substring(0, dot);
     }
 
+    /** @return the simple name of the class. For included classes returns includes dollar and wrapper class */
     private static String getClassSimpleName(String intf) {
         int dot = intf.lastIndexOf('.');
-        return (dot == -1) ? intf : intf.substring(dot+1);
+        return dot == -1 ? intf : intf.substring(dot + 1);
     }
 
+    /**
+     * Prepends __EJB31_Generated__ and adds _Intf__ to the simple class name.
+     *
+     * @param ejbClassName full class name
+     */
     public static String getGeneratedOptionalInterfaceName(String ejbClassName) {
         String packageName = getClassPackageName(ejbClassName);
         String simpleName = getClassSimpleName(ejbClassName);
         String optionalIntfName = "__EJB31_Generated__" + simpleName + "__Intf__";
-        return (packageName != null) ?
-            packageName + "." + optionalIntfName : optionalIntfName;
+        return packageName == null ? optionalIntfName : packageName + "." + optionalIntfName;
     }
 
+    /**
+     * Adds _Serializable to the original name.
+     *
+     * @param beanClass full class name
+     */
     public static String getGeneratedSerializableClassName(String beanClass) {
         String packageName = getClassPackageName(beanClass);
         String simpleName = getClassSimpleName(beanClass);
         String generatedSimpleName = "_" + simpleName + "_Serializable";
-        return (packageName != null) ?
-            packageName + "." + generatedSimpleName : generatedSimpleName;
+        return packageName == null ? generatedSimpleName : packageName + "." + generatedSimpleName;
     }
 
+    /**
+     * Adds _Remote to the original name.
+     *
+     * @param businessIntf full class name
+     */
     public static String getGeneratedRemoteIntfName(String businessIntf) {
         String packageName = getClassPackageName(businessIntf);
         String simpleName = getClassSimpleName(businessIntf);
         String generatedSimpleName = "_" + simpleName + "_Remote";
-        return (packageName != null) ?
-            packageName + "." + generatedSimpleName : generatedSimpleName;
+        return packageName == null ? generatedSimpleName : packageName + "." + generatedSimpleName;
     }
 
+    /**
+     * Adds _Wrapper to the original name.
+     *
+     * @param businessIntf full class name
+     */
     public static String getGeneratedRemoteWrapperName(String businessIntf) {
         String packageName = getClassPackageName(businessIntf);
         String simpleName = getClassSimpleName(businessIntf);
         String generatedSimpleName = "_" + simpleName + "_Wrapper";
-        return (packageName != null) ?
-            packageName + "." + generatedSimpleName : generatedSimpleName;
-    }
-
-    public static String getGenericEJBHomeClassName() {
-        return "com.sun.ejb.codegen.GenericEJBHome_Generated";
+        return packageName == null ? generatedSimpleName : packageName + "." + generatedSimpleName;
     }
 
     /**
@@ -340,100 +354,49 @@
     }
 
 
-    public static Object resolveEjbRefObject(EjbReferenceDescriptor refDesc,
-                                             Object jndiObj)
-        throws NamingException {
-
-        Object returnObject = jndiObj;
-
-        if( refDesc.isLocal() ) {
-
+    public static Object resolveEjbRefObject(EjbReferenceDescriptor refDesc, Object jndiObj) throws NamingException {
+        if (refDesc.isLocal()) {
             EjbDescriptor target = refDesc.getEjbDescriptor();
-
             BaseContainer container = EjbContainerUtilImpl.getInstance().getContainer(target.getUniqueId());
-
-            if( refDesc.isEJB30ClientView() ) {
-                GenericEJBLocalHome genericLocalHome =
-                    container.getEJBLocalBusinessHome(refDesc.getEjbInterface());
-                returnObject = genericLocalHome.create(refDesc.getEjbInterface());
-            } else {
-                returnObject = container.getEJBLocalHome();
+            if (refDesc.isEJB30ClientView()) {
+                GenericEJBLocalHome genericLocalHome = container.getEJBLocalBusinessHome(refDesc.getEjbInterface());
+                return genericLocalHome.create(refDesc.getEjbInterface());
             }
-
-        } else {
-
-            // For the Remote case, the only time we have to do
-            // something extra with the given jndiObj is if the lookup
-            // is for a Remote 3.0 object and it was made through a
-            // corba interoperable name.  In that case,
-            // the jndiObj refers to the internal Remote 3.0 Home so we
-            // still need to create a remote 30 client wrapper object.
-
-            if ( refDesc.isEJB30ClientView() &&
-                 !(jndiObj instanceof RemoteBusinessWrapperBase) ) {
-                returnObject = EJBUtils.lookupRemote30BusinessObject
-                    (jndiObj, refDesc.getEjbInterface());
-            }
-
+            return container.getEJBLocalHome();
         }
 
-        return returnObject;
-
+        if (refDesc.isEJB30ClientView() && !(jndiObj instanceof RemoteBusinessWrapperBase)) {
+            return EJBUtils.lookupRemote30BusinessObject(jndiObj, refDesc.getEjbInterface());
+        }
+        return jndiObj;
     }
 
-    public static Object lookupRemote30BusinessObject(Object jndiObj,
-                                                      String businessInterface)
-        throws NamingException
 
-    {
-        Object returnObject = null;
-
+    public static Object lookupRemote30BusinessObject(Object jndiObj, String businessInterface) throws NamingException {
         try {
-
             ClassLoader loader = Thread.currentThread().getContextClassLoader();
-
-            Class genericEJBHome = loadGeneratedGenericEJBHomeClass
-                (loader);
-
-            final Object genericHomeObj =
-                PortableRemoteObject.narrow(jndiObj, genericEJBHome);
+            Class<?> genericEJBHome = loadGeneratedGenericEJBHomeClass(loader);
+            final Object genericHomeObj = PortableRemoteObject.narrow(jndiObj, genericEJBHome);
 
             // The generated remote business interface and the
             // client wrapper for the business interface are produced
-            // dynamically.  The following call must be made before
+            // dynamically. The following call must be made before
             // any EJB 3.0 Remote business interface runtime behavior
             // is needed in a given JVM.
             loadGeneratedRemoteBusinessClasses(businessInterface);
 
-            String generatedRemoteIntfName = EJBUtils.
-                getGeneratedRemoteIntfName(businessInterface);
+            String generatedRemoteIntfName = EJBUtils.getGeneratedRemoteIntfName(businessInterface);
+            Method createMethod = genericEJBHome.getMethod("create", String.class);
+            java.rmi.Remote delegate = (java.rmi.Remote) createMethod.invoke(genericHomeObj, generatedRemoteIntfName);
 
-            Method createMethod = genericEJBHome.getMethod
-                ("create", String.class);
-
-            java.rmi.Remote delegate = (java.rmi.Remote)
-                createMethod.invoke(genericHomeObj,
-                                    generatedRemoteIntfName);
-
-
-            returnObject = createRemoteBusinessObject
-                (loader, businessInterface, delegate);
-
-
-            // TODO Bring over appclient security exception retry logic  CR 6620388
-
-        } catch(Exception e) {
-            NamingException ne = new NamingException
-                ("ejb ref resolution error for remote business interface"
-                 + businessInterface);
-
-            ne.initCause(e instanceof InvocationTargetException ?
-                         e.getCause() : e);
+            // TODO Bring over appclient security exception retry logic CR 6620388
+            return createRemoteBusinessObject(loader, businessInterface, delegate);
+        } catch (Exception e) {
+            NamingException ne = new NamingException(
+                "ejb ref resolution error for remote business interface" + businessInterface);
+            ne.initCause(e instanceof InvocationTargetException ? e.getCause() : e);
             throw ne;
         }
-
-        return returnObject;
-
     }
 
 
@@ -452,6 +415,10 @@
     }
 
 
+    /**
+     * @param appClassLoader - used to verify existence of classes and for generating too.
+     * @param businessInterfaceName - this class must exist
+     */
     public static void loadGeneratedRemoteBusinessClasses(ClassLoader appClassLoader, String businessInterfaceName)
         throws Exception {
         String generatedRemoteIntfName = EJBUtils.getGeneratedRemoteIntfName(businessInterfaceName);
@@ -465,17 +432,13 @@
         Wrapper._setClassLoader(appClassLoader);
         try {
             if (generatedRemoteIntf == null) {
-                RemoteGenerator gen = new RemoteGenerator(appClassLoader, businessInterfaceName);
-                Class<?> developerClass = appClassLoader.loadClass(businessInterfaceName);
-                generateAndLoad(gen, generatedRemoteIntfName, appClassLoader, developerClass);
-
+                RemoteGenerator generator = new RemoteGenerator(appClassLoader, businessInterfaceName);
+                generateAndLoad(generator, appClassLoader);
             }
             if (generatedRemoteWrapper == null) {
-                Remote30WrapperGenerator gen = new Remote30WrapperGenerator(appClassLoader, businessInterfaceName,
-                    generatedRemoteIntfName);
-
-                Class<?> developerClass = appClassLoader.loadClass(businessInterfaceName);
-                generateAndLoad(gen, wrapperClassName, appClassLoader, developerClass);
+                Remote30WrapperGenerator generator
+                    = new Remote30WrapperGenerator(appClassLoader, businessInterfaceName, generatedRemoteIntfName);
+                generateAndLoad(generator, appClassLoader);
             }
         } finally {
             // Make sure no classloader is bound to threadlocal: avoid possible classloader leak.
@@ -484,36 +447,38 @@
     }
 
 
-    public static Class<?> loadGeneratedGenericEJBHomeClass(ClassLoader appClassLoader) throws Exception {
-        String className = getGenericEJBHomeClassName();
-        Class<?> generatedGenericEJBHomeClass = loadClassIgnoringExceptions(appClassLoader, className);
+    public static Class<?> loadGeneratedGenericEJBHomeClass(final ClassLoader appClassLoader) throws Exception {
+        final Class<?> generatedGenericEJBHomeClass = loadClassIgnoringExceptions(appClassLoader, GENERIC_HOME_CLASSNAME);
         if (generatedGenericEJBHomeClass != null) {
             return generatedGenericEJBHomeClass;
         }
-        GenericHomeGenerator gen = new GenericHomeGenerator(appClassLoader);
-        return generateAndLoad(gen, className, appClassLoader, GenericHomeGenerator.class);
+        final GenericHomeGenerator generator = new GenericHomeGenerator();
+        return generateAndLoad(generator, appClassLoader);
     }
 
 
-    public static Class generateSEI(ClassGeneratorFactory cgf, ClassLoader loader, Class beanClass) {
-        Class<?> clazz = loadClassIgnoringExceptions(loader, cgf.className());
+    public static Class<?> generateSEI(ClassGeneratorFactory cgf, ClassLoader loader) {
+        Class<?> clazz = loadClassIgnoringExceptions(loader, cgf.getGeneratedClassName());
         if (clazz != null) {
             return clazz;
         }
-        return generateAndLoad(cgf, cgf.className(), loader, beanClass);
+        return generateAndLoad(cgf, loader);
     }
 
 
-    private synchronized static Class<?> generateAndLoad(
-        final ClassGeneratorFactory factory, final String requestedClassName,
-        final ClassLoader loader, final Class<?> anchorClass) {
-
-        Class<?> clazz = loadClassIgnoringExceptions(loader, requestedClassName);
+    /**
+     * Checks if the class wasn't already generated by another thread and if not, generates it.
+     * The class name is retrieved from {@link ClassGeneratorFactory#getGeneratedClassName()}
+     * and if it wasn't found, generator knows it's definition.
+     */
+    // made package visible just for tests
+    static synchronized Class<?> generateAndLoad(final ClassGeneratorFactory generator, final ClassLoader loader) {
+        Class<?> clazz = loadClassIgnoringExceptions(loader, generator.getGeneratedClassName());
         if (clazz != null) {
             return clazz;
         }
 
-        factory.evaluate();
+        generator.evaluate();
 
         final Properties props = new Properties();
         if (_logger.isLoggable(Level.FINEST)) {
@@ -531,9 +496,9 @@
         }
 
         if (System.getSecurityManager() == null) {
-            return Wrapper._generate(anchorClass, props);
+            return Wrapper._generate(generator.getAnchorClass(), props);
         }
-        PrivilegedAction<Class<?>> action = () -> Wrapper._generate(anchorClass, props);
+        PrivilegedAction<Class<?>> action = () -> Wrapper._generate(generator.getAnchorClass(), props);
         return AccessController.doPrivileged(action);
     }
 
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ClassGeneratorFactory.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ClassGeneratorFactory.java
index 22eba45..84a1263 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ClassGeneratorFactory.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ClassGeneratorFactory.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,14 +17,26 @@
 
 package com.sun.ejb.codegen;
 
+import org.glassfish.pfl.dynamic.codegen.spi.Wrapper;
 
-/** Convenience interface that defines a factory for ClassGenerator instances.
+/**
+ * Convenience interface that defines a factory for ClassGenerator instances.
  * It puts the class name of the generated class in a single place.
- * It must always be the case that evaluate().name().equals( className() ).
  */
-public interface ClassGeneratorFactory
-{
-    String className() ;
+public interface ClassGeneratorFactory {
 
+    /**
+     * @return name of the generated class or interface
+     */
+    String getGeneratedClassName();
+
+    /**
+     * @return loadable class of the same package as {@link #getGeneratedClassName()}
+     */
+    Class<?> getAnchorClass();
+
+    /**
+     * Calls {@link Wrapper} methods to configure the class definition.
+     */
     void evaluate();
 }
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Generator.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Generator.java
index 7ea2959..84fdca6 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Generator.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Generator.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,246 +18,102 @@
 package com.sun.ejb.codegen;
 
 import java.lang.reflect.Method;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.Vector;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.sun.enterprise.deployment.MethodDescriptor;
-import com.sun.logging.LogDomains;
-import org.glassfish.api.deployment.DeploymentContext;
-import org.glassfish.ejb.deployment.descriptor.ContainerTransaction;
-import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
-import org.glassfish.ejb.deployment.descriptor.EjbSessionDescriptor;
+import java.util.ArrayList;
+import java.util.List;
 
 
 /**
  * The base class for all code generators.
  */
-public abstract class Generator {
-
-    protected static final Logger _logger = LogDomains.getLogger(Generator.class, LogDomains.EJB_LOGGER);
-
-    protected String ejbClassSymbol;
-
-    public abstract String getGeneratedClass();
-
+public abstract class Generator implements ClassGeneratorFactory {
 
     /**
      * Get the package name from the class name.
+     *
      * @param className class name.
      * @return the package name.
      */
     protected String getPackageName(String className) {
         int dot = className.lastIndexOf('.');
-        if (dot == -1)
+        if (dot == -1) {
             return null;
+        }
         return className.substring(0, dot);
     }
 
-    protected String getBaseName(String className) {
-        int dot = className.lastIndexOf('.');
-        if (dot == -1)
-            return className;
-        return className.substring(dot+1);
-    }
-
-    protected String printType(Class cls) {
-        if (cls.isArray()) {
-            return printType(cls.getComponentType()) + "[]";
-        } else {
-            return cls.getName();
-        }
-    }
-
-    protected Method[] removeDups(Method[] orig) {
-        // Remove duplicates from method array.
-        // Duplicates will arise if a class/intf and super-class/intf
-        // define methods with the same signature. Potentially the
-        // throws clauses of the methods may be different (note Java
-        // requires that the superclass/intf method have a superset of the
-        // exceptions in the derived method).
-        Vector nodups = new Vector();
-        for ( int i=0; i<orig.length; i++ ) {
-            Method m1 = orig[i];
-            boolean dup = false;
-            for ( Enumeration e=nodups.elements(); e.hasMoreElements(); ) {
-                Method m2 = (Method)e.nextElement();
-
-                // m1 and m2 are duplicates if they have the same signature
-                // (name and same parameters).
-                if ( !m1.getName().equals(m2.getName()) )
-                    continue;
-
-                Class[] m1parms = m1.getParameterTypes();
-                Class[] m2parms = m2.getParameterTypes();
-                if ( m1parms.length != m2parms.length )
-                    continue;
-
-                boolean parmsDup = true;
-                for ( int j=0; j<m2parms.length; j++ ) {
-                    if ( m1parms[j] != m2parms[j] ) {
-                        parmsDup = false;
-                        break;
-                    }
-                }
-                if ( parmsDup ) {
-                    dup = true;
-                    // Select which of the duplicate methods to generate
-                    // code for: choose the one that is lower in the
-                    // inheritance hierarchy: this ensures that the generated
-                    // method will compile.
-                    if ( m2.getDeclaringClass().isAssignableFrom(
-                                                    m1.getDeclaringClass()) ) {
-                        // m2 is a superclass/intf of m1, so replace m2 with m1
-                        nodups.remove(m2);
-                        nodups.add(m1);
-                    }
-                    break;
-                }
-            }
-
-            if ( !dup )
-                nodups.add(m1);
-        }
-        return (Method[])nodups.toArray(new Method[nodups.size()]);
-    }
 
     /**
-     * Return true if method is on a jakarta.ejb.EJBObject/EJBLocalObject/
-     * jakarta.ejb.EJBHome,jakarta.ejb.EJBLocalHome interface.
+     * @param className
+     * @return simple class name (including wrapper class and dollar sign if it is internal class)
      */
-    protected boolean isEJBIntfMethod(Class ejbIntfClz, Method methodToCheck) {
-        boolean isEJBIntfMethod = false;
-
-        Method[] ejbIntfMethods = ejbIntfClz.getMethods();
-        for(int i = 0; i < ejbIntfMethods.length; i++) {
-            Method next = ejbIntfMethods[i];
-            if(methodCompare(methodToCheck, next)) {
-                isEJBIntfMethod = true;
-
-                String ejbIntfClzName  = ejbIntfClz.getName();
-                Class methodToCheckClz = methodToCheck.getDeclaringClass();
-                if( !methodToCheckClz.getName().equals(ejbIntfClzName) ) {
-                    String[] logParams = {next.toString(), methodToCheck.toString()};
-                    _logger.log(Level.WARNING,
-                                "ejb.illegal_ejb_interface_override",
-                                logParams);
-                }
-
-                break;
-            }
+    protected String getBaseName(String className) {
+        int dot = className.lastIndexOf('.');
+        if (dot == -1) {
+            return className;
         }
-
-        return isEJBIntfMethod;
+        return className.substring(dot + 1);
     }
 
 
-    private boolean methodCompare(Method factoryMethod, Method homeMethod) {
+    /**
+     * Remove duplicates from method array.
+     * <p>
+     * Duplicates will arise if a class/intf and super-class/intf
+     * define methods with the same signature. Potentially the
+     * throws clauses of the methods may be different (note Java
+     * requires that the superclass/intf method have a superset of the
+     * exceptions in the derived method).
+     *
+     * @param methods
+     * @return methods which can be generated in an interface
+     */
+    protected Method[] removeRedundantMethods(Method[] methods) {
+        final List<Method> nodups = new ArrayList<>();
+        for (Method method : methods) {
+            boolean duplicationDetected = false;
+            final List<Method> previousResult = new ArrayList<>(nodups);
+            for (Method alreadyProcessed : previousResult) {
+                // m1 and m2 are duplicates if they have the same signature
+                // (name and same parameters).
+                if (!method.getName().equals(alreadyProcessed.getName())) {
+                    continue;
+                }
+                if (!haveSameParams(method, alreadyProcessed)) {
+                    continue;
+                }
+                duplicationDetected = true;
+                // Select which of the duplicate methods to generate
+                // code for: choose the one that is lower in the
+                // inheritance hierarchy: this ensures that the generated
+                // method will compile.
+                if (alreadyProcessed.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
+                    // alreadyProcessedMethod is a superclass/intf of method,
+                    // so replace it with more concrete method
+                    nodups.remove(alreadyProcessed);
+                    nodups.add(method);
+                }
+                break;
+            }
 
-        if (!factoryMethod.getName().equals(homeMethod.getName())) {
+            if (!duplicationDetected) {
+                nodups.add(method);
+            }
+        }
+        return nodups.toArray(new Method[nodups.size()]);
+    }
+
+
+    private boolean haveSameParams(final Method method1, final Method method2) {
+        Class<?>[] m1parms = method1.getParameterTypes();
+        Class<?>[] m2parms = method2.getParameterTypes();
+        if (m1parms.length != m2parms.length) {
             return false;
         }
-
-        Class[] factoryParamTypes = factoryMethod.getParameterTypes();
-        Class[] beanParamTypes = homeMethod.getParameterTypes();
-        if (factoryParamTypes.length != beanParamTypes.length) {
-            return false;
-        }
-        for(int i = 0; i < factoryParamTypes.length; i++) {
-            if (factoryParamTypes[i] != beanParamTypes[i]) {
+        for (int i = 0; i < m2parms.length; i++) {
+            if (m1parms[i] != m2parms[i]) {
                 return false;
             }
         }
-
-        // NOTE : Exceptions and return types are not part of equality check
         return true;
     }
-
-
-    protected String getUniqueClassName(DeploymentContext context, String origName, String origSuffix,
-        Vector existingClassNames) {
-        String newClassName = null;
-        boolean foundUniqueName = false;
-        int count = 0;
-        while (!foundUniqueName) {
-            String suffix = origSuffix;
-            if (count > 0) {
-                suffix = origSuffix + count;
-            }
-            newClassName = origName + suffix;
-            if (!existingClassNames.contains(newClassName)) {
-                foundUniqueName = true;
-                existingClassNames.add(newClassName);
-            } else {
-                count++;
-            }
-        }
-        return newClassName;
-    }
-
-
-    protected String getTxAttribute(EjbDescriptor dd, Method method) {
-        // The TX_* strings returned MUST match the TX_* constants in
-        // com.sun.ejb.Container.
-        if (dd instanceof EjbSessionDescriptor && ((EjbSessionDescriptor) dd).getTransactionType().equals("Bean")) {
-            return "TX_BEAN_MANAGED";
-        }
-
-        String txAttr = null;
-        MethodDescriptor mdesc = new MethodDescriptor(method, ejbClassSymbol);
-        ContainerTransaction ct = dd.getContainerTransactionFor(mdesc);
-        if (ct != null) {
-            String attr = ct.getTransactionAttribute();
-            if (attr.equals(ContainerTransaction.NOT_SUPPORTED)) {
-                txAttr = "TX_NOT_SUPPORTED";
-            } else if (attr.equals(ContainerTransaction.SUPPORTS)) {
-                txAttr = "TX_SUPPORTS";
-            } else if (attr.equals(ContainerTransaction.REQUIRED)) {
-                txAttr = "TX_REQUIRED";
-            } else if (attr.equals(ContainerTransaction.REQUIRES_NEW)) {
-                txAttr = "TX_REQUIRES_NEW";
-            } else if (attr.equals(ContainerTransaction.MANDATORY)) {
-                txAttr = "TX_MANDATORY";
-            } else if (attr.equals(ContainerTransaction.NEVER)) {
-                txAttr = "TX_NEVER";
-            }
-        }
-
-        if (txAttr == null) {
-            throw new RuntimeException("Transaction Attribute not found for method " + method);
-        }
-        return txAttr;
-    }
-
-    protected String getSecurityAttribute(EjbDescriptor dd, Method m) {
-        // The SEC_* strings returned MUST match the SEC_* constants in
-        // com.sun.ejb.Container.
-        MethodDescriptor thisMethodDesc = new MethodDescriptor(m, ejbClassSymbol);
-        Set unchecked = dd.getUncheckedMethodDescriptors();
-        if (unchecked != null) {
-            Iterator i = unchecked.iterator();
-            while (i.hasNext()) {
-                MethodDescriptor md = (MethodDescriptor) i.next();
-                if (thisMethodDesc.equals(md)) {
-                    return "SEC_UNCHECKED";
-                }
-            }
-        }
-
-        Set excluded = dd.getExcludedMethodDescriptors();
-        if (excluded != null) {
-            Iterator i = excluded.iterator();
-            while (i.hasNext()) {
-                MethodDescriptor md = (MethodDescriptor) i.next();
-                if (thisMethodDesc.equals(md)) {
-                    return "SEC_EXCLUDED";
-                }
-            }
-        }
-
-        return "SEC_CHECKED";
-    }
 }
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/GenericHomeGenerator.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/GenericHomeGenerator.java
index 256b8dd..789dc91 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/GenericHomeGenerator.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/GenericHomeGenerator.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,74 +17,70 @@
 
 package com.sun.ejb.codegen;
 
+import com.sun.ejb.containers.GenericEJBHome;
 
-import com.sun.ejb.EJBUtils;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
 
-import com.sun.enterprise.util.LocalStringManagerImpl;
-
-import static java.lang.reflect.Modifier.*;
-
-import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.*;
+import static java.lang.reflect.Modifier.ABSTRACT;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._String;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._arg;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._classGenerator;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._clear;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._end;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._interface;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._method;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._package;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._t;
 
 /**
  * This class is used to generate a sub-interface of the
  * GenericEJBHome interface that will be loaded within each
  * application.
  */
+public class GenericHomeGenerator extends Generator {
 
-public class GenericHomeGenerator extends Generator
-    implements ClassGeneratorFactory {
-
-    private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(GenericHomeGenerator.class);
-
-    private String genericEJBHomeClassName;
-    private ClassLoader loader;
+    /**
+     * This class name is relative to the classloader used, but has always the same name.
+     */
+    public static final String GENERIC_HOME_CLASSNAME = GenericHomeGenerator.class.getPackageName()
+        + ".GenericEJBHome_Generated";
 
     /**
      * Get the fully qualified name of the generated class.
      * @return the name of the generated class.
      */
-    public String getGeneratedClass() {
-        return genericEJBHomeClassName;
-    }
-
-    // For corba codegen infrastructure
-    public String className() {
-        return getGeneratedClass();
+    @Override
+    public final String getGeneratedClassName() {
+        return GENERIC_HOME_CLASSNAME;
     }
 
 
-    public GenericHomeGenerator(ClassLoader cl) throws GeneratorException {
-        super();
-
-        genericEJBHomeClassName = EJBUtils.getGenericEJBHomeClassName();
-        loader = cl;
+    @Override
+    public Class<?> getAnchorClass() {
+        return GenericHomeGenerator.class;
     }
 
 
+    @Override
     public void evaluate() {
-
         _clear();
 
-        String packageName = getPackageName(genericEJBHomeClassName);
-        String simpleName = getBaseName (genericEJBHomeClassName);
+        String packageName = getPackageName(getGeneratedClassName());
+        String simpleName = getBaseName(getGeneratedClassName());
 
         _package(packageName);
 
-        _interface(PUBLIC, simpleName,
-                   _t("com.sun.ejb.containers.GenericEJBHome"));
+        _interface(PUBLIC, simpleName, _t(GenericEJBHome.class.getName()));
 
-        // Create method
-        _method(PUBLIC | ABSTRACT, _t("java.rmi.Remote"),
-                "create", _t("java.rmi.RemoteException"));
+        _method(PUBLIC | ABSTRACT, _t(Remote.class.getName()), "create", _t(RemoteException.class.getName()));
 
         _arg(_String(), "generatedBusinessIntf");
 
         _end();
 
         _classGenerator() ;
-
-        return;
     }
 
 }
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Remote30WrapperGenerator.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Remote30WrapperGenerator.java
index 66b1ce8..d288bc7 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Remote30WrapperGenerator.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/Remote30WrapperGenerator.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,85 +17,104 @@
 
 package com.sun.ejb.codegen;
 
-import java.lang.reflect.Method;
-import java.util.*;
-
-
-import static java.lang.reflect.Modifier.*;
-
-import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.*;
-import org.glassfish.pfl.dynamic.codegen.spi.Type ;
-import org.glassfish.pfl.dynamic.codegen.spi.Expression ;
-
 import com.sun.ejb.EJBUtils;
+import com.sun.ejb.containers.InternalEJBContainerException;
+import com.sun.ejb.containers.InternalRemoteException;
+import com.sun.ejb.containers.RemoteBusinessWrapperBase;
 import com.sun.enterprise.util.LocalStringManagerImpl;
 
-public class Remote30WrapperGenerator extends Generator
-    implements ClassGeneratorFactory {
+import java.lang.reflect.Method;
+import java.rmi.AccessException;
+import java.rmi.NoSuchObjectException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
-    private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(Remote30WrapperGenerator.class);
+import org.glassfish.pfl.dynamic.codegen.spi.Expression;
+import org.glassfish.pfl.dynamic.codegen.spi.Type;
+import org.omg.CORBA.SystemException;
 
-    private String remoteInterfaceName;
-    private Class businessInterface;
-    private String remoteClientClassName;
-    private String remoteClientPackageName;
-    private String remoteClientSimpleName;
-    private Method[] bizMethods;
+import jakarta.ejb.EJBAccessException;
+import jakarta.ejb.EJBException;
+import jakarta.ejb.EJBTransactionRequiredException;
+import jakarta.ejb.EJBTransactionRolledbackException;
+import jakarta.ejb.NoSuchEJBException;
+import jakarta.transaction.TransactionRequiredException;
+import jakarta.transaction.TransactionRolledbackException;
 
-    private ClassLoader loader;
+import static java.lang.reflect.Modifier.PRIVATE;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.*;
 
-    public String getGeneratedClass() {
-        return remoteClientClassName;
-    }
+public final class Remote30WrapperGenerator extends Generator {
 
-    // For corba codegen infrastructure
-    public String className() {
-        return getGeneratedClass();
-    }
+    private static final LocalStringManagerImpl localStrings = new LocalStringManagerImpl(Remote30WrapperGenerator.class);
+    private static final Logger LOG = Logger.getLogger(Remote30WrapperGenerator.class.getName());
+
+    private final String remoteInterfaceName;
+    private final Class<?> businessInterface;
+    private final String remoteClientClassName;
+    private final String remoteClientPackageName;
+    private final String remoteClientSimpleName;
+    private final Method[] methodsToGenerate;
+
+    private final ClassLoader loader;
 
 
     /**
      * Construct the Wrapper generator with the specified deployment
      * descriptor and class loader.
      *
-     * @exception GeneratorException
+     * @param loader
+     * @param businessIntfName must already exist and be loadable by the loader
+     * @param remoteInterfaceName generated class will implement this
+     *
+     * @throws GeneratorException
      */
-    public Remote30WrapperGenerator(ClassLoader cl, String businessIntfName, String remoteIntfName)
+    public Remote30WrapperGenerator(ClassLoader loader, String businessIntfName, String remoteInterfaceName)
         throws GeneratorException {
-        super();
 
-        remoteInterfaceName = remoteIntfName;
-        loader = cl;
+        this.loader = loader;
+        this.remoteInterfaceName = remoteInterfaceName;
 
         try {
-            this.businessInterface = cl.loadClass(businessIntfName);
+            businessInterface = loader.loadClass(businessIntfName);
         } catch (ClassNotFoundException ex) {
-            throw new InvalidBean(
-            localStrings.getLocalString(
-            "generator.remote_interface_not_found",
-            "Business interface " + businessInterface + " not found "));
+            throw new InvalidBean(localStrings.getLocalString(
+                "generator.remote_interface_not_found",
+                "Business interface " + businessIntfName + " not found "));
         }
 
         if (jakarta.ejb.EJBObject.class.isAssignableFrom(businessInterface)) {
-            throw new GeneratorException("Invalid Remote Business Interface " +
-                 businessInterface + ". A Remote Business interface MUST " +
-                 "not extend jakarta.ejb.EJBObject.");
+            throw new GeneratorException("Invalid Remote Business Interface " + businessInterface
+                + ". A Remote Business interface MUST not extend jakarta.ejb.EJBObject.");
         }
 
-        remoteClientClassName = EJBUtils.
-            getGeneratedRemoteWrapperName(businessInterface.getName());
-
+        remoteClientClassName = EJBUtils.getGeneratedRemoteWrapperName(businessIntfName);
         remoteClientPackageName = getPackageName(remoteClientClassName);
         remoteClientSimpleName = getBaseName(remoteClientClassName);
 
-        bizMethods = removeDups(businessInterface.getMethods());
+        methodsToGenerate = removeRedundantMethods(businessInterface.getMethods());
 
         // NOTE : no need to remove ejb object methods because EJBObject
         // is only visible through the RemoteHome view.
-
     }
 
+    @Override
+    public String getGeneratedClassName() {
+        return remoteClientClassName;
+    }
 
+    @Override
+    public Class<?> getAnchorClass() {
+        return businessInterface;
+    }
+
+    @Override
     public void evaluate() {
 
         _clear();
@@ -107,7 +127,7 @@
         }
 
         _class(PUBLIC, remoteClientSimpleName,
-               _t("com.sun.ejb.containers.RemoteBusinessWrapperBase"),
+               _t(RemoteBusinessWrapperBase.class.getName()),
                _t(businessInterface.getName()));
 
         _data(PRIVATE, _t(remoteInterfaceName), "delegate_");
@@ -117,45 +137,42 @@
         _arg(_String(), "busIntf");
 
         _body();
-        _expr(_super(_s(_void(), _t("java.rmi.Remote"), _String()), _v("stub"), _v("busIntf")));
+        _expr(_super(_s(_void(), _t(Remote.class.getName()), _String()), _v("stub"), _v("busIntf")));
         _assign(_v("delegate_"), _v("stub"));
         _end();
 
-        for(int i = 0; i < bizMethods.length; i++) {
-            printMethodImpl(bizMethods[i]);
+        for (Method method : methodsToGenerate) {
+            printMethodImpl(method);
         }
 
         _end();
 
         try {
-            java.util.Properties p = new java.util.Properties();
+            Properties p = new Properties();
             p.put("Wrapper.DUMP_AFTER_SETUP_VISITOR", "true");
             p.put("Wrapper.TRACE_BYTE_CODE_GENERATION", "true");
             p.put("Wrapper.USE_ASM_VERIFIER", "true");
             _byteCode(loader, p);
         } catch(Exception e) {
-            System.out.println("Got exception when generating byte code");
-            e.printStackTrace();
+            LOG.log(Level.WARNING, "Got exception when generating byte code", e);
         }
 
         _classGenerator() ;
-
-        return;
     }
 
 
     private void printMethodImpl(Method m) {
-        List<Type> exceptionList = new LinkedList<Type>();
-        for (Class exception : m.getExceptionTypes()) {
+        List<Type> exceptionList = new LinkedList<>();
+        for (Class<?> exception : m.getExceptionTypes()) {
             exceptionList.add(Type.type(exception));
         }
 
         _method(PUBLIC, Type.type(m.getReturnType()), m.getName(), exceptionList);
 
         int i = 0;
-        List<Type> expressionListTypes = new LinkedList<Type>();
-        List<Expression> expressionList = new LinkedList<Expression>();
-        for (Class param : m.getParameterTypes()) {
+        List<Type> expressionListTypes = new LinkedList<>();
+        List<Expression> expressionList = new LinkedList<>();
+        for (Class<?> param : m.getParameterTypes()) {
             String paramName = "param" + i;
             _arg(Type.type(param), paramName);
             i++;
@@ -167,7 +184,7 @@
 
         _try();
 
-        Class returnType = m.getReturnType();
+        Class<?> returnType = m.getReturnType();
 
         if (returnType == void.class) {
             _expr(
@@ -177,76 +194,93 @@
                 _call(_v("delegate_"), m.getName(), _s(Type.type(returnType), expressionListTypes), expressionList));
         }
 
-        boolean doExceptionTranslation = !java.rmi.Remote.class.isAssignableFrom(businessInterface);
+        boolean doExceptionTranslation = !Remote.class.isAssignableFrom(businessInterface);
         if (doExceptionTranslation) {
+            _catch(_t(TransactionRolledbackException.class.getName()), "trex");
 
-            _catch(_t("jakarta.transaction.TransactionRolledbackException"), "trex");
-
-            _define( _t("java.lang.RuntimeException"), "r",
-                _new( _t("jakarta.ejb.EJBTransactionRolledbackException"), _s(_void())));
+            _define(_t(RuntimeException.class.getName()), "r",
+                _new(_t(EJBTransactionRolledbackException.class.getName()), _s(_void())));
             _expr(
-                _call( _v("r"), "initCause", _s(_t("java.lang.Throwable"), _t("java.lang.Throwable")), _v("trex"))
+                _call(
+                    _v("r"), "initCause",
+                    _s(_t(Throwable.class.getName()), _t(Throwable.class.getName())),
+                    _v("trex")
+                )
             );
             _throw(_v("r"));
 
-            _catch(_t("jakarta.transaction.TransactionRequiredException"), "treqex");
+            _catch(_t(TransactionRequiredException.class.getName()), "treqex");
 
-            _define( _t("java.lang.RuntimeException"), "r",
-                _new( _t("jakarta.ejb.EJBTransactionRequiredException"), _s(_void())));
+            _define(_t(RuntimeException.class.getName()), "r",
+                _new(_t(EJBTransactionRequiredException.class.getName()), _s(_void())));
+
             _expr(
-                _call( _v("r"), "initCause", _s(_t("java.lang.Throwable"), _t("java.lang.Throwable")), _v("treqex"))
+                _call(
+                    _v("r"), "initCause",
+                    _s(_t(Throwable.class.getName()), _t(Throwable.class.getName())),
+                    _v("treqex")
+                )
             );
             _throw(_v("r"));
 
-            _catch(_t("java.rmi.NoSuchObjectException"), "nsoe");
+            _catch(_t(NoSuchObjectException.class.getName()), "nsoe");
 
-            _define( _t("java.lang.RuntimeException"), "r",
-                _new( _t("jakarta.ejb.NoSuchEJBException"), _s(_void())));
+            _define(_t(RuntimeException.class.getName()), "r",
+                _new(_t(NoSuchEJBException.class.getName()), _s(_void())));
             _expr(
-                _call( _v("r"), "initCause", _s(_t("java.lang.Throwable"), _t("java.lang.Throwable")), _v("nsoe"))
+                _call(
+                    _v("r"), "initCause",
+                    _s(_t(Throwable.class.getName()), _t(Throwable.class.getName())),
+                    _v("nsoe")
+                )
             );
             _throw(_v("r"));
 
-            _catch(_t("java.rmi.AccessException"), "accex");
+            _catch(_t(AccessException.class.getName()), "accex");
 
-            _define( _t("java.lang.RuntimeException"), "r",
-                _new( _t("jakarta.ejb.EJBAccessException"), _s(_void())));
+            _define(_t(RuntimeException.class.getName()), "r",
+                _new(_t(EJBAccessException.class.getName()), _s(_void())));
             _expr(
-                _call( _v("r"), "initCause", _s(_t("java.lang.Throwable"), _t("java.lang.Throwable")), _v("accex"))
+                _call(
+                    _v("r"), "initCause",
+                    _s(_t(Throwable.class.getName()), _t(Throwable.class.getName())),
+                    _v("accex")
+                )
             );
             _throw(_v("r"));
 
-            _catch(_t("com.sun.ejb.containers.InternalEJBContainerException"), "iejbcEx");
+            _catch(_t(InternalEJBContainerException.class.getName()), "iejbcEx");
 
-            // This wraps an EJBException. Pull out the cause and throw
-            // it as is.
-            //_define( _t("java.lang.Throwable"), "r", _null());
-
-
-            // _throw(_cast(_t("jakarta.ejb.EJBException"), _v("r")));
             _throw(
-                _cast(_t("jakarta.ejb.EJBException"), _call( _v("iejbcEx"), "getCause", _s(_t("java.lang.Throwable"))))
+                _cast(
+                    _t(EJBException.class.getName()),
+                    _call(_v("iejbcEx"), "getCause", _s(_t(Throwable.class.getName())))
+                )
             );
 
 
-            _catch(_t("java.rmi.RemoteException"), "re");
+            _catch(_t(RemoteException.class.getName()), "re");
 
-            _throw( _new( _t("jakarta.ejb.EJBException"), _s(_void(), _t("java.lang.Exception")), _v("re")));
+            _throw( _new( _t(EJBException.class.getName()), _s(_void(), _t(Exception.class.getName())), _v("re")));
 
-            _catch( _t("org.omg.CORBA.SystemException"), "corbaSysEx");
-            _define( _t("java.lang.RuntimeException"), "r", _new( _t("jakarta.ejb.EJBException"), _s(_void())));
+            _catch( _t(SystemException.class.getName()), "corbaSysEx");
+            _define( _t(RuntimeException.class.getName()), "r", _new( _t(EJBException.class.getName()), _s(_void())));
             _expr(
-                _call( _v("r"), "initCause", _s(_t("java.lang.Throwable"), _t("java.lang.Throwable")), _v("corbaSysEx"))
+                _call(
+                    _v("r"), "initCause",
+                    _s(_t(Throwable.class.getName()), _t(Throwable.class.getName())),
+                    _v("corbaSysEx")
+                )
             );
             _throw(_v("r"));
 
             _end();
 
         } else {
-            _catch(_t("com.sun.ejb.containers.InternalEJBContainerException"), "iejbcEx");
+            _catch(_t(InternalEJBContainerException.class.getName()), "iejbcEx");
             _throw(
-                _new( _t("com.sun.ejb.containers.InternalRemoteException"),
-                _s(_void(), _t("com.sun.ejb.containers.InternalEJBContainerException")),
+                _new( _t(InternalRemoteException.class.getName()),
+                _s(_void(), _t(InternalEJBContainerException.class.getName())),
                 _v("iejbcEx"))
             );
             _end();
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/RemoteGenerator.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/RemoteGenerator.java
index 2bb8271..9de52f8 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/RemoteGenerator.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/RemoteGenerator.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,80 +17,88 @@
 
 package com.sun.ejb.codegen;
 
-import java.lang.reflect.Method;
-import java.io.*;
-import java.util.*;
 import com.sun.ejb.EJBUtils;
+import com.sun.ejb.containers.InternalEJBContainerException;
+import com.sun.ejb.containers.RemoteBusinessObject;
+import com.sun.enterprise.util.LocalStringManagerImpl;
 
-import static java.lang.reflect.Modifier.*;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
 
-import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.*;
 import org.glassfish.pfl.dynamic.codegen.spi.Type ;
 
-import com.sun.enterprise.util.LocalStringManagerImpl;
+import static java.lang.reflect.Modifier.ABSTRACT;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._arg;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._classGenerator;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._clear;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._end;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._interface;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._method;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._package;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._t;
 
 /**
  * This class is used to generate the RMI-IIOP version of a
  * remote business interface.
  */
-
-public class RemoteGenerator extends Generator
-    implements ClassGeneratorFactory {
+public final class RemoteGenerator extends Generator {
 
     private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(RemoteGenerator.class);
 
+    private Class<?> businessInterface;
+    private final Method[] methodsToGenerate;
+    private final String remoteInterfacePackageName;
+    private final String remoteInterfaceSimpleName;
+    private final String remoteInterfaceName;
 
-    private Class businessInterface;
-    private Method[] bizMethods;
-    private String remoteInterfacePackageName;
-    private String remoteInterfaceSimpleName;
-    private String remoteInterfaceName;
-
-    /**
-     * Get the fully qualified name of the generated class.
-     * Note: the remote/local implementation class is in the same package
-     * as the bean class, NOT the remote/local interface.
-     * @return the name of the generated class.
-     */
-    public String getGeneratedClass() {
-        return remoteInterfaceName;
-    }
-
-    // For corba codegen infrastructure
-    public String className() {
-        return getGeneratedClass();
-    }
 
     /**
      * Construct the Wrapper generator with the specified deployment
      * descriptor and class loader.
-     * @exception GeneratorException
+     *
+     * @param classLoader
+     * @param businessIntf
+     * @throws InvalidBean if the businessInterface doesn't exist.
      */
-    public RemoteGenerator(ClassLoader cl, String businessIntf) throws GeneratorException {
-        super();
-
+    public RemoteGenerator(ClassLoader classLoader, String businessIntf) throws InvalidBean {
         try {
-            businessInterface = cl.loadClass(businessIntf);
+            businessInterface = classLoader.loadClass(businessIntf);
         } catch (ClassNotFoundException ex) {
             throw new InvalidBean(
-                localStrings.getLocalString(
-                    "generator.remote_interface_not_found",
-                    "Remote interface not found "));
+                localStrings.getLocalString("generator.remote_interface_not_found", "Remote interface not found "));
         }
 
-        remoteInterfaceName = EJBUtils.getGeneratedRemoteIntfName
-            (businessInterface.getName());
-
+        remoteInterfaceName = EJBUtils.getGeneratedRemoteIntfName(businessInterface.getName());
         remoteInterfacePackageName = getPackageName(remoteInterfaceName);
         remoteInterfaceSimpleName = getBaseName(remoteInterfaceName);
 
-        bizMethods = removeDups(businessInterface.getMethods());
+        methodsToGenerate = removeRedundantMethods(businessInterface.getMethods());
 
         // NOTE : no need to remove ejb object methods because EJBObject
         // is only visible through the RemoteHome view.
     }
 
+    /**
+     * Get the fully qualified name of the generated class.
+     * <p>
+     * Note: the remote/local implementation class is in the same package
+     * as the bean class, NOT the remote/local interface.
+     *
+     * @return the name of the generated class.
+     */
+    @Override
+    public String getGeneratedClassName() {
+        return remoteInterfaceName;
+    }
 
+    @Override
+    public Class<?> getAnchorClass() {
+        return businessInterface;
+    }
+
+    @Override
     public void evaluate() {
 
         _clear();
@@ -97,51 +106,46 @@
         if (remoteInterfacePackageName != null) {
             _package(remoteInterfacePackageName);
         } else {
-            // no-arg _package() call is required for default package
             _package();
         }
 
         _interface(PUBLIC, remoteInterfaceSimpleName,
-                   _t("java.rmi.Remote"),
-                   _t("com.sun.ejb.containers.RemoteBusinessObject"));
+            _t(java.rmi.Remote.class.getName()),
+            _t(RemoteBusinessObject.class.getName())
+        );
 
-        for(int i = 0; i < bizMethods.length; i++) {
-            printMethod(bizMethods[i]);
+        for (Method method : methodsToGenerate) {
+            printMethod(method);
         }
 
         _end();
 
         _classGenerator() ;
-
-        return;
-
     }
 
 
     private void printMethod(Method m) {
         boolean throwsRemoteException = false;
-        List<Type> exceptionList = new LinkedList<Type>();
-        for(Class exception : m.getExceptionTypes()) {
+        List<Type> exceptionList = new LinkedList<>();
+        for (Class<?> exception : m.getExceptionTypes()) {
             exceptionList.add(Type.type(exception));
-            if( exception.getName().equals("java.rmi.RemoteException") ) {
+            if (exception.getName().equals("java.rmi.RemoteException")) {
                 throwsRemoteException = true;
             }
         }
-        if( !throwsRemoteException ) {
+        if (!throwsRemoteException) {
             exceptionList.add(_t("java.rmi.RemoteException"));
         }
 
-        exceptionList.add(_t("com.sun.ejb.containers.InternalEJBContainerException"));
+        exceptionList.add(_t(InternalEJBContainerException.class.getName()));
         _method(PUBLIC | ABSTRACT, Type.type(m.getReturnType()), m.getName(), exceptionList);
 
         int i = 0;
-        for(Class param : m.getParameterTypes()) {
+        for (Class<?> param : m.getParameterTypes()) {
             _arg(Type.type(param), "param" + i);
             i++;
         }
 
         _end();
     }
-
-
 }
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ServiceInterfaceGenerator.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ServiceInterfaceGenerator.java
index d639f2a..ed9abd8 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ServiceInterfaceGenerator.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/codegen/ServiceInterfaceGenerator.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 Contributors to the Eclipse Foundation
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,21 +19,23 @@
 
 
 import java.lang.reflect.Method;
-import java.util.List;
+import java.rmi.RemoteException;
 import java.util.ArrayList;
 import java.util.LinkedList;
-
-import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper.*;
+import java.util.List;
 import org.glassfish.pfl.dynamic.codegen.spi.Type ;
 
-import java.util.logging.Logger;
-
 import jakarta.jws.WebMethod;
 
-import static java.lang.reflect.Modifier.*;
-
-import com.sun.enterprise.util.LocalStringManagerImpl;
-import com.sun.logging.LogDomains;
+import static java.lang.reflect.Modifier.ABSTRACT;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._arg;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._clear;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._end;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._interface;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._method;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._package;
+import static org.glassfish.pfl.dynamic.codegen.spi.Wrapper._t;
 
 /**
  * This class is responsible for generating the SEI when it is not packaged
@@ -40,98 +43,52 @@
  *
  * @author Jerome Dochez
  */
-public class ServiceInterfaceGenerator extends Generator
-    implements ClassGeneratorFactory {
+public class ServiceInterfaceGenerator extends Generator {
 
-    private static LocalStringManagerImpl localStrings =
-        new LocalStringManagerImpl(ServiceInterfaceGenerator.class);
-    private static Logger _logger=null;
-    static{
-       _logger=LogDomains.getLogger(ServiceInterfaceGenerator.class, LogDomains.DPL_LOGGER);
-    }
-
-    Class sib=null;
-    String serviceIntfName;
-    String packageName;
-    String serviceIntfSimpleName;
-    Method[] intfMethods;
+    private final Class<?> ejbClass;
+    private final String packageName;
+    private final String serviceIntfName;
+    private final String serviceIntfSimpleName;
+    private final Method[] intfMethods;
 
    /**
      * Construct the Wrapper generator with the specified deployment
      * descriptor and class loader.
-     * @exception GeneratorException.
      */
-    public ServiceInterfaceGenerator(ClassLoader cl, Class sib)
-        throws GeneratorException, ClassNotFoundException
-    {
-        super();
-
-        this.sib = sib;
-        serviceIntfSimpleName = getServiceIntfName();
-
-        packageName = getPackageName();
-        serviceIntfName = packageName + "." + serviceIntfSimpleName;
-
-        intfMethods = calculateMethods(sib, removeDups(sib.getMethods()));
+    public ServiceInterfaceGenerator(Class<?> ejbClass) {
+        this.ejbClass = ejbClass;
+        packageName = getPackageName(ejbClass.getName());
+        serviceIntfSimpleName = getServiceIntfName(ejbClass);
+        serviceIntfName = (packageName == null ? "" : packageName + ".") + serviceIntfSimpleName;
+        intfMethods = calculateMethods(ejbClass, removeRedundantMethods(ejbClass.getMethods()));
 
         // NOTE : no need to remove ejb object methods because EJBObject
         // is only visible through the RemoteHome view.
     }
 
-    public String getServiceIntfName() {
-        String serviceIntfSimpleName = sib.getSimpleName();
-        if (serviceIntfSimpleName.endsWith("EJB")) {
-            return serviceIntfSimpleName.substring(0, serviceIntfSimpleName.length()-3);
-        } else {
-            return serviceIntfSimpleName+"SEI";
-        }
-    }
-
-    public String getPackageName() {
-        return sib.getPackage().getName()+".internal.jaxws";
-    }
 
     /**
      * Get the fully qualified name of the generated class.
+     * <p>
      * Note: the remote/local implementation class is in the same package
      * as the bean class, NOT the remote/local interface.
+     *
      * @return the name of the generated class.
      */
-    public String getGeneratedClass() {
+    @Override
+    public String getGeneratedClassName() {
         return serviceIntfName;
     }
 
-    // For corba codegen infrastructure
-    public String className() {
-        return getGeneratedClass();
+
+    @Override
+    public Class<?> getAnchorClass() {
+        return ejbClass;
     }
 
-    private Method[] calculateMethods(Class sib, Method[] initialList) {
 
-        // we start by assuming the @WebMethod was NOT used on this class
-        boolean webMethodAnnotationUsed = false;
-        List<Method> list = new ArrayList<Method>();
-
-        for (Method m : initialList) {
-            WebMethod wm = m.getAnnotation(WebMethod.class);
-            if ( (wm != null) && !webMethodAnnotationUsed) {
-                webMethodAnnotationUsed=true;
-                // reset the list, this is the first annotated method we find
-                list.clear();
-            }
-            if (wm!=null) {
-                list.add(m);
-            } else {
-                if (!webMethodAnnotationUsed && !m.getDeclaringClass().equals(java.lang.Object.class)) {
-                    list.add(m);
-                }
-            }
-        }
-        return list.toArray(new Method[list.size()]);
-    }
-
+    @Override
     public void evaluate() {
-
         _clear();
 
         if (packageName != null) {
@@ -140,38 +97,31 @@
 
         _interface(PUBLIC, serviceIntfSimpleName);
 
-        for(int i = 0; i < intfMethods.length; i++) {
-            printMethod(intfMethods[i]);
+        for (Method intfMethod : intfMethods) {
+            printMethod(intfMethod);
         }
 
         _end();
-
-        return;
-
     }
 
 
-    private void printMethod(Method m)
-    {
-
+    private void printMethod(Method m) {
         boolean throwsRemoteException = false;
-        List<Type> exceptionList = new LinkedList<Type>();
-        for(Class exception : m.getExceptionTypes()) {
+        List<Type> exceptionList = new LinkedList<>();
+        for (Class<?> exception : m.getExceptionTypes()) {
             exceptionList.add(Type.type(exception));
-            if( exception.getName().equals("java.rmi.RemoteException") ) {
+            if (exception.getName().equals(RemoteException.class.getName())) {
                 throwsRemoteException = true;
             }
-    }
-        if( !throwsRemoteException ) {
-            exceptionList.add(_t("java.rmi.RemoteException"));
+        }
+        if (!throwsRemoteException) {
+            exceptionList.add(_t(RemoteException.class.getName()));
         }
 
-        _method( PUBLIC | ABSTRACT, Type.type(m.getReturnType()),
-                 m.getName(), exceptionList);
+        _method(PUBLIC | ABSTRACT, Type.type(m.getReturnType()), m.getName(), exceptionList);
 
         int i = 0;
-
-        for(Class param : m.getParameterTypes()) {
+        for (Class<?> param : m.getParameterTypes()) {
             _arg(Type.type(param), "param" + i);
             i++;
         }
@@ -179,4 +129,36 @@
         _end();
     }
 
+
+    private static String getServiceIntfName(Class<?> ejbClass) {
+        String serviceIntfSimpleName = ejbClass.getSimpleName();
+        if (serviceIntfSimpleName.endsWith("EJB")) {
+            return serviceIntfSimpleName.substring(0, serviceIntfSimpleName.length() - 3) + "_GeneratedSEI";
+        }
+        return serviceIntfSimpleName + "_GeneratedSEI";
+    }
+
+
+    private static Method[] calculateMethods(Class sib, Method[] initialList) {
+        // we start by assuming the @WebMethod was NOT used on this class
+        boolean webMethodAnnotationUsed = false;
+        List<Method> list = new ArrayList<>();
+
+        for (Method m : initialList) {
+            WebMethod wm = m.getAnnotation(WebMethod.class);
+            if (wm != null && !webMethodAnnotationUsed) {
+                webMethodAnnotationUsed = true;
+                // reset the list, this is the first annotated method we find
+                list.clear();
+            }
+            if (wm != null) {
+                list.add(m);
+            } else {
+                if (!webMethodAnnotationUsed && !m.getDeclaringClass().equals(Object.class)) {
+                    list.add(m);
+                }
+            }
+        }
+        return list.toArray(new Method[list.size()]);
+    }
 }
diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java
index 48b787a..8c89bea 100644
--- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java
+++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/BaseContainer.java
@@ -1103,8 +1103,8 @@
             Class serviceEndpointIntfClass = loader.loadClass(webServiceEndpoint.getServiceEndpointInterface());
 
             if (!serviceEndpointIntfClass.isInterface()) {
-                ServiceInterfaceGenerator generator = new ServiceInterfaceGenerator(loader, ejbClass);
-                serviceEndpointIntfClass = EJBUtils.generateSEI(generator, loader, this.ejbClass);
+                ServiceInterfaceGenerator generator = new ServiceInterfaceGenerator(ejbClass);
+                serviceEndpointIntfClass = EJBUtils.generateSEI(generator, loader);
                 if (serviceEndpointIntfClass == null) {
                     throw new RuntimeException(localStrings.getLocalString("ejb.error_generating_sei",
                         "Error in generating service endpoint interface class for EJB class {0}", this.ejbClass));
diff --git a/appserver/ejb/ejb-container/src/test/java/com/sun/ejb/EJBUtilsTest.java b/appserver/ejb/ejb-container/src/test/java/com/sun/ejb/EJBUtilsTest.java
index ab949d8..b81f13d 100644
--- a/appserver/ejb/ejb-container/src/test/java/com/sun/ejb/EJBUtilsTest.java
+++ b/appserver/ejb/ejb-container/src/test/java/com/sun/ejb/EJBUtilsTest.java
@@ -18,13 +18,16 @@
 
 import com.sun.ejb.codegen.ClassGeneratorFactory;
 import com.sun.ejb.codegen.Generator;
+import com.sun.ejb.codegen.Remote30WrapperGenerator;
 import com.sun.ejb.codegen.ServiceInterfaceGenerator;
 
 import java.util.Collection;
 import java.util.concurrent.TimeUnit;
 
-import org.glassfish.pfl.dynamic.codegen.spi.Wrapper;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.Mode;
 import org.openjdk.jmh.results.Result;
@@ -34,7 +37,6 @@
 import org.openjdk.jmh.runner.options.OptionsBuilder;
 import org.openjdk.jmh.runner.options.TimeValue;
 
-import static java.lang.reflect.Modifier.PUBLIC;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.lessThan;
@@ -47,53 +49,71 @@
 /**
  * @author David Matejcek
  */
+@TestMethodOrder(OrderAnnotation.class)
 public class EJBUtilsTest {
 
+    /**
+     * The value shall be high enough to pass on all standard environments,
+     * but lower than when we are generating classes. See warmup results in logs.
+     */
+    private static final double MAX_TIME_PER_OPERATION = 1_000_000d;
     private static final ClassLoader loader = EJBUtilsTest.class.getClassLoader();
+    private static double firstRunScore;
 
     @Test
-    public void generateSEI_Benchmark() throws Exception {
+    @Order(1)
+    public void generateAndLoad_firstRun() throws Exception {
         Options options = new OptionsBuilder()
             .include(getClass().getName() + ".*")
-            .mode(Mode.AverageTime)
             .warmupIterations(0)
-            .measurementIterations(2)
-            .measurementTime(TimeValue.seconds(1L))
-            .forks(1)
-            .threads(20)
-            .shouldFailOnError(true)
-            .shouldDoGC(true)
-            .timeout(TimeValue.seconds(5L))
-            .timeUnit(TimeUnit.NANOSECONDS)
+            .measurementIterations(1).forks(1).measurementTime(TimeValue.milliseconds(50L))
+            // should be able to detect deadlocks and race conditions when generating classes
+            .threads(100).timeout(TimeValue.seconds(5L))
+            .timeUnit(TimeUnit.MICROSECONDS)
+            .mode(Mode.SingleShotTime).shouldFailOnError(true)
             .build();
 
         Collection<RunResult> results = new Runner(options).run();
         assertThat(results, hasSize(1));
-        Result<?> result = results.iterator().next().getAggregatedResult().getPrimaryResult();
-        assertThat(result.getScore(), lessThan(100000d));
+        Result<?> primaryResult = results.iterator().next().getPrimaryResult();
+        firstRunScore = primaryResult.getScore();
+        assertThat(primaryResult.getScore(), lessThan(MAX_TIME_PER_OPERATION));
+    }
+
+
+    @Test
+    @Order(2)
+    public void generateAndLoad_Benchmark() throws Exception {
+        Options options = new OptionsBuilder()
+            .include(getClass().getName() + ".*")
+            .warmupBatchSize(1).warmupForks(0).warmupIterations(1).warmupTime(TimeValue.milliseconds(50L))
+            .measurementIterations(1).forks(1).measurementTime(TimeValue.milliseconds(200L))
+            // should be able to detect race conditions when loading classes generated in previous test
+            .threads(100).timeout(TimeValue.seconds(5L))
+            .timeUnit(TimeUnit.MICROSECONDS)
+            .mode(Mode.AverageTime).shouldFailOnError(true)
+            .build();
+
+        Collection<RunResult> results = new Runner(options).run();
+        assertThat(results, hasSize(1));
+        Result<?> primaryResult = results.iterator().next().getPrimaryResult();
+        assertThat(primaryResult.getScore(), lessThan(firstRunScore / 4));
     }
 
 
     @Benchmark
-    public void generateSei_Benchmark() throws Exception {
-        ClassGeneratorFactory generator = new CustomGenerator();
-        Class<?> newClass = EJBUtils.generateSEI(generator, loader, EJbUtilsEjbTestClass.class);
+    public void generateAndLoad() throws Exception {
+        // random interface for the test
+        String interfaceName = ClassGeneratorFactory.class.getName();
+        Generator generator = new Remote30WrapperGenerator(loader, interfaceName, interfaceName);
+        Class<?> newClass = EJBUtils.generateAndLoad(generator, loader);
         assertNotNull(newClass);
-        assertEquals(generator.className(), newClass.getName());
+        assertEquals(generator.getGeneratedClassName(), newClass.getName());
     }
 
 
     @Test
-    public void generateSei_ServiceInterfaceGenerator() throws Exception {
-        ServiceInterfaceGenerator generator = new ServiceInterfaceGenerator(loader, EJbUtilsEjbTestClass.class);
-        // FIXME: com.sun.ejb.EJBUtilsTest$EJbUtilsEjbTestClass doesn't have expected package com.sun.ejb.internal.jaxws
-        Class<?> newClass = EJBUtils.generateSEI(generator, loader, EJbUtilsEjbTestClass.class);
-        assertNotNull(newClass);
-        assertEquals(generator.className(), newClass.getName());
-    }
-
-
-    @Test
+    @Order(10)
     public void loadGeneratedRemoteBusinessClasses() throws Exception {
         EJBUtils.loadGeneratedRemoteBusinessClasses(EjbUtilsTestInterface.class.getName());
         Class<?> ifaceRemote = loader.loadClass("com.sun.ejb._EJBUtilsTest$EjbUtilsTestInterface_Remote");
@@ -105,6 +125,7 @@
 
 
     @Test
+    @Order(20)
     public void loadGeneratedGenericEJBHomeClass() throws Exception {
         Class<?> newClass = EJBUtils.loadGeneratedGenericEJBHomeClass(loader);
         assertNotNull(newClass);
@@ -113,34 +134,18 @@
         assertSame(newClass, EJBUtils.loadGeneratedGenericEJBHomeClass(loader));
     }
 
-    private static class CustomGenerator extends Generator implements ClassGeneratorFactory {
 
-        @Override
-        public String getGeneratedClass() {
-            return "com.sun.ejb.EJBUtilsTestImplFromCustomGenerator";
-        }
-
-        @Override
-        public String className() {
-            return getGeneratedClass();
-        }
-
-        @Override
-        public void evaluate() {
-            Wrapper._clear();
-            Wrapper._package(getPackageName(className()));
-            Wrapper._interface(PUBLIC, getBaseName(className()));
-            Wrapper._classGenerator();
-        }
+    @Test
+    @Order(30)
+    public void generateSEI() throws Exception {
+        Generator generator = new ServiceInterfaceGenerator(ClassGeneratorFactory.class);
+        Class<?> newClass = EJBUtils.generateSEI(generator, loader);
+        assertNotNull(newClass);
+        assertEquals(generator.getGeneratedClassName(), newClass.getName());
     }
 
 
     interface EjbUtilsTestInterface {
         void doSomething();
     }
-
-
-    public static class EJbUtilsEjbTestClass {
-
-    }
 }