diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java index b1d5ff77c9..6629f0d4c5 100644 --- a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java @@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.IdentityHashMap; import javax.annotation.Priority; import javax.enterprise.context.ApplicationScoped; @@ -99,7 +100,8 @@ import org.jboss.weld.injection.producer.BeanInjectionTarget; /** - * CDI extension that handles CDI bootstrap events and registers Jersey's internally used components and components registered + * CDI extension that handles CDI bootstrap events and registers Jersey's + * internally used components and components registered * using {@link Application}. */ class BinderRegisterExtension implements Extension { @@ -108,7 +110,8 @@ class BinderRegisterExtension implements Extension { private Supplier beanManagerSupplier; private Ref serverInjectionManager = Refs.emptyRef(); - private BootstrapInjectionManager clientBootstrapInjectionManager = new BootstrapInjectionManager(RuntimeType.CLIENT); + private BootstrapInjectionManager clientBootstrapInjectionManager = new BootstrapInjectionManager( + RuntimeType.CLIENT); private WrappingInjectionManager serverBootstrapInjectionManager = new WrappingInjectionManager() .setInjectionManager(new BootstrapInjectionManager(RuntimeType.SERVER)); private BootstrapBag bootstrapBag = new BootstrapBag(); @@ -118,7 +121,8 @@ class BinderRegisterExtension implements Extension { @Override protected void configure() { install(new ContextInjectionResolverImpl.Binder(beanManagerSupplier)); - bind(InitializableInstanceBinding.from(Bindings.service(serverInjectionManager.get()).to(InjectionManager.class))); + bind(InitializableInstanceBinding + .from(Bindings.service(serverInjectionManager.get()).to(InjectionManager.class))); } }; private final CachingBinder annotatedBeansBinder = new CachingBinder(serverInjectionManager); @@ -135,10 +139,12 @@ protected void configure() { final Set> managedBeans = new HashSet<>(); /** - * Ignores the classes which are manually added using bindings (through {@link Application} class) and scanned by CDI. + * Ignores the classes which are manually added using bindings (through + * {@link Application} class) and scanned by CDI. * The manual adding is privileged and the beans scanned using CDI are ignored. *

- * TODO: The method counts with the condition that the all bindings are known before the CDI scanning has been started, + * TODO: The method counts with the condition that the all bindings are known + * before the CDI scanning has been started, * can be changed during the migration from CDI SE to JAVA EE environment. * * @param pat processed type. @@ -177,7 +183,6 @@ void ignoreManuallyRegisteredComponents( } } - } void registerJerseyRequestScopedResources( @@ -196,7 +201,7 @@ void processRegistrars(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanMa } void handleRequestScoped( - @Observes @WithAnnotations({javax.enterprise.context.RequestScoped.class}) ProcessAnnotatedType pat) { + @Observes @WithAnnotations({ javax.enterprise.context.RequestScoped.class }) ProcessAnnotatedType pat) { final Class javaClass = pat.getAnnotatedType().getJavaClass(); if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); @@ -205,7 +210,7 @@ void handleRequestScoped( } void handleApplicationScoped( - @Observes @WithAnnotations({ApplicationScoped.class}) ProcessAnnotatedType pat) { + @Observes @WithAnnotations({ ApplicationScoped.class }) ProcessAnnotatedType pat) { final Class javaClass = pat.getAnnotatedType().getJavaClass(); if (Application.class.isAssignableFrom(javaClass)) { pat.veto(); @@ -216,7 +221,7 @@ void handleApplicationScoped( } } - void handleDependent(@Observes @WithAnnotations({Dependent.class}) ProcessAnnotatedType pat) { + void handleDependent(@Observes @WithAnnotations({ Dependent.class }) ProcessAnnotatedType pat) { final Class javaClass = pat.getAnnotatedType().getJavaClass(); if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); @@ -224,7 +229,7 @@ void handleDependent(@Observes @WithAnnotations({Dependent.class}) ProcessAnnota } } - void handleSessionScoped(@Observes @WithAnnotations({SessionScoped.class}) ProcessAnnotatedType pat) { + void handleSessionScoped(@Observes @WithAnnotations({ SessionScoped.class }) ProcessAnnotatedType pat) { final Class javaClass = pat.getAnnotatedType().getJavaClass(); if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); @@ -232,7 +237,7 @@ void handleSessionScoped(@Observes @WithAnnotations({SessionScoped.class}) Proce } } - void registerSingletonSubResources(@Observes @WithAnnotations({Singleton.class}) ProcessAnnotatedType pat){ + void registerSingletonSubResources(@Observes @WithAnnotations({ Singleton.class }) ProcessAnnotatedType pat) { final Class resourceClass = pat.getAnnotatedType().getJavaClass(); if (resourceClass.getAnnotation(Path.class) != null) { annotatedBeans.put(resourceClass, Singleton.class); @@ -241,10 +246,10 @@ void registerSingletonSubResources(@Observes @WithAnnotations({Singleton.class}) } } - void registerJerseyProviders(@Observes @WithAnnotations({Priority.class}) ProcessAnnotatedType pat) { + void registerJerseyProviders(@Observes @WithAnnotations({ Priority.class }) ProcessAnnotatedType pat) { final Class javaClass = pat.getAnnotatedType().getJavaClass(); if (!isNotJerseyInternal(javaClass)) { - pat.veto(); //veto Jersey internal + pat.veto(); // veto Jersey internal } annotatedBeans.put(javaClass, Priority.class); } @@ -260,8 +265,8 @@ public void observeInjectionTarget(@Observes ProcessInjectionTarget pit) return; } BasicInjectionTarget it = (BasicInjectionTarget) pit.getInjectionTarget(); - JerseyInjectionTarget jerseyInjectionTarget = - new JerseyInjectionTarget<>(it, pit.getAnnotatedType().getJavaClass()); + JerseyInjectionTarget jerseyInjectionTarget = new JerseyInjectionTarget<>(it, + pit.getAnnotatedType().getJavaClass()); jerseyInjectionTargets.add(jerseyInjectionTarget); pit.setInjectionTarget(jerseyInjectionTarget); } @@ -269,8 +274,10 @@ public void observeInjectionTarget(@Observes ProcessInjectionTarget pit) /** * Takes all registered bindings and registers them in {@link BeanManager}. *

- * Method should register only Jersey internal components and class/instances registered using {@link Application}. Registered - * classes/instances have priority therefore CDI scanning should veto these classes/instances during { + * Method should register only Jersey internal components and class/instances + * registered using {@link Application}. Registered + * classes/instances have priority therefore CDI scanning should veto these + * classes/instances during { * * @param abd {@code AfterBeanDiscovery} event. * @param beanManager current {@code BeanManager}. @@ -295,7 +302,8 @@ void registerBeans(@Observes AfterBeanDiscovery abd, BeanManager beanManager) { injectionResolvers.addAll(contextInjectionResolvers); /* - * Provide registered InjectionResolvers to Jersey's components which has been discovered by CDI in + * Provide registered InjectionResolvers to Jersey's components which has been + * discovered by CDI in * ProcessInjectionTarget bootstrap phase. */ jerseyInjectionTargets.forEach(injectionTarget -> injectionTarget.setInjectionResolvers(injectionResolvers)); @@ -312,58 +320,98 @@ void registerBeans(@Observes AfterBeanDiscovery abd, BeanManager beanManager) { } private void registerBeans(RuntimeType runtimeType, CachingBinder binder, AfterBeanDiscovery abd, - BeanManager beanManager) { + BeanManager beanManager) { final Collection bindings = binder.getBindings(); binder.setReadOnly(); - allBindingsLabel: - for (Binding binding : bindings) { + // Reuse the same BindingBeanPair for the same binding object. + final IdentityHashMap, BindingBeanPair> classPairCache = new IdentityHashMap<>(); + final IdentityHashMap, BindingBeanPair> supplierPairCache = new IdentityHashMap<>(); + + allBindingsLabel: for (Binding binding : bindings) { + + // ---------- ClassBinding ---------- if (ClassBinding.class.isAssignableFrom(binding.getClass())) { + final ClassBinding cb = (ClassBinding) binding; + + // Keep existing "update" path for CLIENT if (RuntimeType.CLIENT == runtimeType) { - for (Type contract : ((ClassBinding) binding).getContracts()) { - final List preregistered = classBindings.get(contract); - if (preregistered != null && preregistered.size() == 1) { - BeanHelper.updateBean( - (ClassBinding) binding, preregistered.get(0), injectionResolvers, beanManager); + for (Type contract : cb.getContracts()) { + final java.util.List pre = classBindings.get(contract); + if (pre != null && pre.size() == 1) { + BeanHelper.updateBean(cb, pre.get(0), injectionResolvers, beanManager); continue allBindingsLabel; } } } - BindingBeanPair pair = BeanHelper.registerBean( - runtimeType, (ClassBinding) binding, abd, injectionResolvers, beanManager); - for (Type contract : ((ClassBinding) binding).getContracts()) { - classBindings.add(contract, pair); + + // Register once per ClassBinding instance and reuse the pair + BindingBeanPair pair = classPairCache.get(cb); + if (pair == null) { + pair = BeanHelper.registerBean( + runtimeType, cb, abd, injectionResolvers, beanManager); + classPairCache.put(cb, pair); } + + // Write contracts in deterministic order + final java.util.List contracts = new java.util.ArrayList<>(cb.getContracts()); + contracts.sort(java.util.Comparator.comparing(Type::getTypeName)); + for (Type c : contracts) { + classBindings.add(c, pair); + } + + // ---------- SupplierClassBinding ---------- } else if (SupplierClassBinding.class.isAssignableFrom(binding.getClass())) { + final SupplierClassBinding scb = (SupplierClassBinding) binding; + + // Keep existing "update" path for CLIENT if (RuntimeType.CLIENT == runtimeType) { - for (Type contract : ((SupplierClassBinding) binding).getContracts()) { - final List preregistered = supplierClassBindings.get(contract); - if (preregistered != null && preregistered.size() == 1) { + for (Type contract : scb.getContracts()) { + final java.util.List pre = supplierClassBindings.get(contract); + if (pre != null && pre.size() == 1) { BeanHelper.updateSupplierBean( - (SupplierClassBinding) binding, preregistered.get(0), injectionResolvers, beanManager); + scb, pre.get(0), injectionResolvers, beanManager); continue allBindingsLabel; } } } - BindingBeanPair pair = BeanHelper.registerSupplier( - runtimeType, (SupplierClassBinding) binding, abd, injectionResolvers, beanManager); + + // Register once per SupplierClassBinding instance and reuse the pair + BindingBeanPair pair = supplierPairCache.get(scb); + if (pair == null) { + pair = BeanHelper.registerSupplier( + runtimeType, scb, abd, injectionResolvers, beanManager); + supplierPairCache.put(scb, pair); + } + if (pair != null) { - for (Type contract : ((SupplierClassBinding) binding).getContracts()) { - supplierClassBindings.add(contract, pair); + // Write contracts in deterministic order + final java.util.List contracts = new java.util.ArrayList<>(scb.getContracts()); + contracts.sort(java.util.Comparator.comparing(Type::getTypeName)); + for (Type c : contracts) { + supplierClassBindings.add(c, pair); } } + + // ---------- Instance bindings (unchanged) ---------- } else if (InitializableInstanceBinding.class.isAssignableFrom(binding.getClass())) { if (RuntimeType.SERVER == runtimeType || !matchInitializableInstanceBinding((InitializableInstanceBinding) binding)) { initializableInstanceBindings.add((InitializableInstanceBinding) binding); BeanHelper.registerBean( - runtimeType, (InitializableInstanceBinding) binding, abd, injectionResolvers, beanManager); + runtimeType, (InitializableInstanceBinding) binding, + abd, injectionResolvers, beanManager); } + } else if (InitializableSupplierInstanceBinding.class.isInstance(binding)) { if (RuntimeType.SERVER == runtimeType - || !matchInitializableSupplierInstanceBinding((InitializableSupplierInstanceBinding) binding)) { - initializableSupplierInstanceBindings.add((InitializableSupplierInstanceBinding) binding); - BeanHelper.registerSupplier(runtimeType, (InitializableSupplierInstanceBinding) binding, abd, beanManager); + || !matchInitializableSupplierInstanceBinding( + (InitializableSupplierInstanceBinding) binding)) { + initializableSupplierInstanceBindings.add( + (InitializableSupplierInstanceBinding) binding); + BeanHelper.registerSupplier( + runtimeType, (InitializableSupplierInstanceBinding) binding, + abd, beanManager); } } } @@ -408,7 +456,8 @@ private void registerApplicationHandler(BeanManager beanManager) { new ApplicationHandler(resourceConfig); } - private void bindApplication(Class applicationClass, ResourceConfig resourceConfig, BeanManager beanManager) { + private void bindApplication(Class applicationClass, ResourceConfig resourceConfig, + BeanManager beanManager) { final Application application = new Unmanaged<>(applicationClass).newInstance().produce().get(); for (Class clazz : application.getClasses()) { @@ -424,8 +473,8 @@ private void bindApplication(Class applicationClass, ResourceConfig final Class clazz = singleton.getClass(); if (beanManager.getBeans(clazz).isEmpty()) { // prevent double registration of a class -// final InstanceBinding binding = binder.bind(singleton); -// toSuper(clazz, binding); + // final InstanceBinding binding = binder.bind(singleton); + // toSuper(clazz, binding); resourceConfig.register(singleton); } else if (!annotatedBeans.containsKey(clazz)) { annotatedBeans.put(clazz, Provider.class); @@ -433,31 +482,34 @@ private void bindApplication(Class applicationClass, ResourceConfig } } -// private void bindApplication(Class applicationClass, AbstractBinder binder, BeanManager beanManager) { -// final Application application = new Unmanaged<>(applicationClass).newInstance().produce().get(); -// final CommonConfig commonConfig = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL); -// -// for (Class clazz : application.getClasses()) { -// if (beanManager.getBeans(clazz).isEmpty()) { -// // prevent double registration of a class -// // bind(clazz, binder); -// commonConfig.register(clazz); -// } else if (!annotatedBeans.containsKey(clazz)) { -// annotatedBeans.put(clazz, Provider.class); -// } -// } -// for (Object singleton : application.getSingletons()) { -// final Class clazz = singleton.getClass(); -// if (beanManager.getBeans(clazz).isEmpty()) { -// // prevent double registration of a class -//// final InstanceBinding binding = binder.bind(singleton); -//// toSuper(clazz, binding); -// commonConfig.register(singleton); -// } else if (!annotatedBeans.containsKey(clazz)) { -// annotatedBeans.put(clazz, Provider.class); -// } -// } -// } + // private void bindApplication(Class applicationClass, + // AbstractBinder binder, BeanManager beanManager) { + // final Application application = new + // Unmanaged<>(applicationClass).newInstance().produce().get(); + // final CommonConfig commonConfig = new CommonConfig(RuntimeType.SERVER, + // ComponentBag.INCLUDE_ALL); + // + // for (Class clazz : application.getClasses()) { + // if (beanManager.getBeans(clazz).isEmpty()) { + // // prevent double registration of a class + // // bind(clazz, binder); + // commonConfig.register(clazz); + // } else if (!annotatedBeans.containsKey(clazz)) { + // annotatedBeans.put(clazz, Provider.class); + // } + // } + // for (Object singleton : application.getSingletons()) { + // final Class clazz = singleton.getClass(); + // if (beanManager.getBeans(clazz).isEmpty()) { + // // prevent double registration of a class + //// final InstanceBinding binding = binder.bind(singleton); + //// toSuper(clazz, binding); + // commonConfig.register(singleton); + // } else if (!annotatedBeans.containsKey(clazz)) { + // annotatedBeans.put(clazz, Provider.class); + // } + // } + // } private static ClassBinding bind(Class clazz, AbstractBinder binder) { final ClassBinding binding = binder.bindAsContract(clazz); @@ -472,20 +524,21 @@ private static T toSuper(Class clazz, T binding) { binding.to(superClass); } } - for (Class intf : clazz.getInterfaces()){ + for (Class intf : clazz.getInterfaces()) { binding.to(intf); } return binding; } -// // Check first if a class is a JAX-RS resource, and only if so check with validation. -// // This prevents unnecessary warnings being logged for pure CDI beans. -// private final Cache, Boolean> jaxRsResourceCache = new Cache<>( -// clazz -> Resource.from(clazz, true) != null && Resource.from(clazz) != null); -// -// public boolean isJaxRsResource(Class resource) { -// return jaxRsResourceCache.apply(resource); -// } + // // Check first if a class is a JAX-RS resource, and only if so check with + // validation. + // // This prevents unnecessary warnings being logged for pure CDI beans. + // private final Cache, Boolean> jaxRsResourceCache = new Cache<>( + // clazz -> Resource.from(clazz, true) != null && Resource.from(clazz) != null); + // + // public boolean isJaxRsResource(Class resource) { + // return jaxRsResourceCache.apply(resource); + // } private boolean isJaxrs(Class clazz) { return Providers.isJaxRsProvider(clazz) || BeanHelper.isResourceClass(clazz) || isJerseyRegistrable(clazz); @@ -525,7 +578,6 @@ private boolean matchInitializableSupplierInstanceBinding(InitializableSupplierI return false; } - /** To be used by the tests only */ public void register(BeforeBeanDiscovery beforeBeanDiscovery, Binding binding) { register(RuntimeType.SERVER, binding); @@ -578,8 +630,10 @@ InjectionManager getInjectionManager(RuntimeType runtimeType) { } /** - * Injection manager used during the bootstrap. It is used to create the actual beans in the beans manager. - * Other InjectionManagers sets the beans (beans binding) by a value provided in runtime. + * Injection manager used during the bootstrap. It is used to create the actual + * beans in the beans manager. + * Other InjectionManagers sets the beans (beans binding) by a value provided in + * runtime. */ private class BootstrapInjectionManager implements InjectionManager { @@ -591,12 +645,12 @@ private BootstrapInjectionManager(RuntimeType runtimeType) { @Override public void completeRegistration() { - //noop + // noop } @Override public void shutdown() { - //noop + // noop } @Override @@ -611,9 +665,9 @@ public void register(Binding binding) { @Override public void register(Iterable descriptors) { - for (Binding binding : descriptors) { - register(binding); - } + for (Binding binding : descriptors) { + register(binding); + } } @Override @@ -642,7 +696,8 @@ public T createAndInitialize(Class createMe) { try { Constructor constructor = createMe.getConstructor(); return constructor.newInstance(); - } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException + | InvocationTargetException e) { return null; } } @@ -695,7 +750,7 @@ public List getAllInstances(Type contractOrImpl) { @Override public void inject(Object injectMe) { - // noop; + // noop; } @Override @@ -705,14 +760,17 @@ public void inject(Object injectMe, String classAnalyzer) { @Override public void preDestroy(Object preDestroyMe) { - //noop + // noop } } /** - * AbstractBinder that supports calling {@link #getBindings()} multiple times by caching the result. - * Each additional binding is added to the cache by the next call of {@link #getBindings()}. - * When {@link #setReadOnly()} is called, no additional binding is added to the cache. + * AbstractBinder that supports calling {@link #getBindings()} multiple times by + * caching the result. + * Each additional binding is added to the cache by the next call of + * {@link #getBindings()}. + * When {@link #setReadOnly()} is called, no additional binding is added to the + * cache. */ private class CachingBinder extends AbstractBinder { private final Ref injectionManager; @@ -818,7 +876,6 @@ private static class MergedBindings implements Binder { private final AbstractBinder first; private final AbstractBinder second; - private MergedBindings(AbstractBinder first, AbstractBinder second) { this.first = first; this.second = second;