openwebbeans-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rmannibu...@apache.org
Subject svn commit: r1861905 - in /openwebbeans/trunk/webbeans-impl/src: main/java/org/apache/webbeans/component/creation/ main/java/org/apache/webbeans/config/ main/java/org/apache/webbeans/container/ main/java/org/apache/webbeans/decorator/ main/java/org/apa...
Date Sun, 23 Jun 2019 09:10:31 GMT
Author: rmannibucau
Date: Sun Jun 23 09:10:31 2019
New Revision: 1861905

URL: http://svn.apache.org/viewvc?rev=1861905&view=rev
Log:
OWB-1292 exit satisfiesDependency when generics loop

Modified:
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/AbstractBeanBuilder.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/BaseProducerFactory.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanManagerImpl.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/InjectionResolver.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/decorator/DecoratorsManager.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/portable/EventProducer.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/GenericsUtil.java
    openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java
    openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/unittests/inject/parametrized/GenericClassTest.java
    openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/util/GenericsUtilTest.java

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/AbstractBeanBuilder.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/AbstractBeanBuilder.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/AbstractBeanBuilder.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/AbstractBeanBuilder.java Sun Jun 23 09:10:31 2019
@@ -27,6 +27,7 @@ import javax.enterprise.inject.spi.Annot
 import javax.enterprise.inject.spi.AnnotatedParameter;
 import javax.enterprise.inject.spi.AnnotatedType;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Set;
 
 import org.apache.webbeans.component.ProducerFieldBean;
@@ -59,7 +60,7 @@ public abstract class AbstractBeanBuilde
                     boolean found = false;
                     for (ProducerMethodBean<?> producer : producerBeans)
                     {
-                        if (GenericsUtil.satisfiesDependency(false, true, producer.getCreatorMethod().getGenericReturnType(), param.getBaseType()))
+                        if (GenericsUtil.satisfiesDependency(false, true, producer.getCreatorMethod().getGenericReturnType(), param.getBaseType(), new HashMap<>()))
                         {
                             found = true;
                             break;
@@ -69,7 +70,7 @@ public abstract class AbstractBeanBuilde
                     {
                         for (ProducerFieldBean<?> field : producerFields)
                         {
-                            if (GenericsUtil.satisfiesDependency(false, true, field.getCreatorField().getType(), param.getBaseType()))
+                            if (GenericsUtil.satisfiesDependency(false, true, field.getCreatorField().getType(), param.getBaseType(), new HashMap<>()))
                             {
                                 found = true;
                                 break;
@@ -81,7 +82,7 @@ public abstract class AbstractBeanBuilde
                             // see if @Disposes should just be ignored as well - no inheritance
                             for (AnnotatedMethod<?> producer : ignoredProducers)
                             {
-                                if (GenericsUtil.satisfiesDependency(false, true, producer.getJavaMember().getGenericReturnType(), param.getBaseType()))
+                                if (GenericsUtil.satisfiesDependency(false, true, producer.getJavaMember().getGenericReturnType(), param.getBaseType(), new HashMap<>()))
                                 {
                                     found = true;
                                     break;

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/BaseProducerFactory.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/BaseProducerFactory.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/BaseProducerFactory.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/component/creation/BaseProducerFactory.java Sun Jun 23 09:10:31 2019
@@ -43,6 +43,7 @@ import javax.inject.Named;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -102,7 +103,7 @@ public abstract class BaseProducerFactor
                 {
                     if (annotatedParameter.isAnnotationPresent(Disposes.class))
                     {
-                        if (!GenericsUtil.satisfiesDependency(false, true, producerBaseType, annotatedParameter.getBaseType()))
+                        if (!GenericsUtil.satisfiesDependency(false, true, producerBaseType, annotatedParameter.getBaseType(), new HashMap<>()))
                         {
                             continue;
                         }

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/config/BeansDeployer.java Sun Jun 23 09:10:31 2019
@@ -597,7 +597,7 @@ public class BeansDeployer
                         ParameterizedType pt2 = ParameterizedType.class.cast(t);
 
                         if (pt1.getRawType() == pt2.getRawType() &&
-                            !GenericsUtil.isAssignableFrom(true, false, pt1, pt2))
+                            !GenericsUtil.isAssignableFrom(true, false, pt1, pt2, new HashMap<>()))
                         {
                             throw new WebBeansConfigurationException("Generic error matching " + api + " and " + t);
                         }

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanManagerImpl.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanManagerImpl.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanManagerImpl.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/BeanManagerImpl.java Sun Jun 23 09:10:31 2019
@@ -755,8 +755,8 @@ public class BeanManagerImpl implements
             boolean isProducer = AbstractProducerBean.class.isInstance(bean);
             if(!isProducer && // we have different rules for producers
                !isBeanTypeAssignableToGivenType(bean.getTypes(), beanType, bean instanceof NewBean, isProducer) &&
-               !GenericsUtil.satisfiesDependency(false, isProducer, beanType, bean.getBeanClass()) &&
-               !GenericsUtil.satisfiesDependencyRaw(false, isProducer, beanType, bean.getBeanClass()))
+               !GenericsUtil.satisfiesDependency(false, isProducer, beanType, bean.getBeanClass(), new HashMap<>()) &&
+               !GenericsUtil.satisfiesDependencyRaw(false, isProducer, beanType, bean.getBeanClass(), new HashMap<>()))
             {
                 throw new IllegalArgumentException("Given bean type : " + beanType + " is not applicable for the bean instance : " + bean);
             }
@@ -934,7 +934,7 @@ public class BeanManagerImpl implements
     {
         for (Type beanApiType : beanTypes)
         {
-            if (GenericsUtil.satisfiesDependency(false, producer, givenType, beanApiType))
+            if (GenericsUtil.satisfiesDependency(false, producer, givenType, beanApiType, new HashMap<>()))
             {
                 return true;
             }

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/InjectionResolver.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/InjectionResolver.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/InjectionResolver.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/container/InjectionResolver.java Sun Jun 23 09:10:31 2019
@@ -54,6 +54,7 @@ import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -514,7 +515,7 @@ public class InjectionResolver
 
                         if (GenericsUtil.satisfiesDependency(
                                 isDelegate, AbstractProducerBean.class.isInstance(component),
-                                injectionPointType, componentApiType))
+                                injectionPointType, componentApiType, new HashMap<>()))
                         {
                             resolvedComponents.add(component);
                             break;
@@ -599,7 +600,7 @@ public class InjectionResolver
             boolean isProducer = AbstractProducerBean.class.isInstance(bean);
             for (Type type : bean.getTypes())
             {
-                if (GenericsUtil.satisfiesDependency(isDelegate, isProducer, injectionPointType, type))
+                if (GenericsUtil.satisfiesDependency(isDelegate, isProducer, injectionPointType, type, new HashMap<>()))
                 {
                     resolved.add(bean);
                 }
@@ -625,7 +626,7 @@ public class InjectionResolver
             for (Type componentApiType : component.getTypes())
             {
 
-                if (GenericsUtil.satisfiesDependency(isDelegate, isProducer, injectionPointType, componentApiType))
+                if (GenericsUtil.satisfiesDependency(isDelegate, isProducer, injectionPointType, componentApiType, new HashMap<>()))
                 {
                     resolvedComponents.add(component);
                     break;

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/decorator/DecoratorsManager.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/decorator/DecoratorsManager.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/decorator/DecoratorsManager.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/decorator/DecoratorsManager.java Sun Jun 23 09:10:31 2019
@@ -21,6 +21,7 @@ package org.apache.webbeans.decorator;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -238,7 +239,7 @@ public class DecoratorsManager
         boolean ok = false;
         for (Type apiType : apiTypes)
         {
-            if (GenericsUtil.satisfiesDependency(true, false, decorator.getDelegateType(), apiType))
+            if (GenericsUtil.satisfiesDependency(true, false, decorator.getDelegateType(), apiType, new HashMap<>()))
             {
                 ok = true;
                 break;

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/event/NotificationManager.java Sun Jun 23 09:10:31 2019
@@ -29,6 +29,7 @@ import java.lang.reflect.TypeVariable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -346,8 +347,8 @@ public final class NotificationManager
             for (Type eventType : eventTypes)
             {
                 if ((ParameterizedType.class.isInstance(eventType) && Class.class.isInstance(observedType)
-                        && GenericsUtil.isAssignableFrom(true, false, observedType, ParameterizedType.class.cast(eventType).getRawType()))
-                    || GenericsUtil.isAssignableFrom(true, false, observedType, eventType))
+                        && GenericsUtil.isAssignableFrom(true, false, observedType, ParameterizedType.class.cast(eventType).getRawType(), new HashMap<>()))
+                    || GenericsUtil.isAssignableFrom(true, false, observedType, eventType, new HashMap<>()))
                 {
                     Set<ObserverMethod<?>> observerMethods = observerEntry.getValue();
 
@@ -498,7 +499,7 @@ public final class NotificationManager
         }
         else if (observerTypeActualArg instanceof ParameterizedType)
         {
-            return GenericsUtil.isAssignableFrom(false, true, observerTypeActualArg, beanClass);
+            return GenericsUtil.isAssignableFrom(false, true, observerTypeActualArg, beanClass, new HashMap<>());
         }
         
         return false;
@@ -533,7 +534,7 @@ public final class NotificationManager
             if(checkEventTypeParameterForExtensions(beanClass, actualArgs[0])
                     && (secondParam == null || actualArgs.length == 1
                             || checkEventTypeParameterForExtensions(secondParam, actualArgs[1])
-                            || GenericsUtil.isAssignableFrom(true, false, actualArgs[1], secondParam)))
+                            || GenericsUtil.isAssignableFrom(true, false, actualArgs[1], secondParam, new HashMap<>())))
             {
                 addToMatching(type, matching);   
             }

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/portable/EventProducer.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/portable/EventProducer.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/portable/EventProducer.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/portable/EventProducer.java Sun Jun 23 09:10:31 2019
@@ -22,6 +22,7 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -64,7 +65,8 @@ public class EventProducer<T> extends Ab
             {
                 ParameterizedType arg = ParameterizedType.class.cast(event);
                 Type[] actualTypeArguments = arg.getActualTypeArguments();
-                if (actualTypeArguments.length > 0 && GenericsUtil.isAssignableFrom(true, false, actualTypeArguments[0], type))
+                if (actualTypeArguments.length > 0 && GenericsUtil.isAssignableFrom(
+                        true, false, actualTypeArguments[0], type, new HashMap<>()))
                 {
                     list.add(original);
                 }

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/GenericsUtil.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/GenericsUtil.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/GenericsUtil.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/GenericsUtil.java Sun Jun 23 09:10:31 2019
@@ -33,6 +33,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.webbeans.config.OwbGenericArrayTypeImpl;
@@ -45,11 +46,15 @@ import org.apache.webbeans.config.OwbWil
  */
 public final class GenericsUtil
 {
-    public static boolean satisfiesDependency(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType)
+
+    private static final int MAX_GENERIC_LOOPS = 4; // todo: config? it is already crazy :s
+
+    public static boolean satisfiesDependency(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType,
+                                              Map<Type, Integer> visited)
     {
         if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
         {
-            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType, visited);
         }
         else
         {
@@ -58,18 +63,19 @@ public final class GenericsUtil
             
             if  (ClassUtil.isSame(injectionPointRawType, beanRawType))
             {
-                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType, visited);
             }
         }
 
         return false;
     }
 
-    public static boolean satisfiesDependencyRaw(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType)
+    public static boolean satisfiesDependencyRaw(boolean isDelegateOrEvent, boolean isProducer, Type injectionPointType, Type beanType,
+                                                 Map<Type, Integer> visited)
     {
         if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
         {
-            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType);
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType, visited);
         }
         else
         {
@@ -78,7 +84,7 @@ public final class GenericsUtil
 
             if  (ClassUtil.isSame(injectionPointRawType, beanRawType))
             {
-                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointRawType, beanRawType);
+                return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointRawType, beanRawType, visited);
             }
             else
             {
@@ -110,36 +116,35 @@ public final class GenericsUtil
     /**
      * 5.2.3 and 5.2.4
      */
-    public static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, Type requiredType, Type beanType)
+    public static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, Type requiredType, Type beanType,
+                                           Map<Type, Integer> visited)
     {
         if (requiredType instanceof Class)
         {
-            return isAssignableFrom(isDelegateOrEvent, (Class<?>)requiredType, beanType);
+            return isAssignableFrom(isDelegateOrEvent, (Class<?>)requiredType, beanType, visited);
         }
         else if (requiredType instanceof ParameterizedType)
         {
-            return isAssignableFrom(isDelegateOrEvent, isProducer, (ParameterizedType)requiredType, beanType);
+            return isAssignableFrom(isDelegateOrEvent, isProducer, (ParameterizedType)requiredType, beanType, visited);
         }
         else if (requiredType instanceof TypeVariable)
         {
-            return isAssignableFrom(isDelegateOrEvent, (TypeVariable<?>)requiredType, beanType);
+            return isAssignableFrom(isDelegateOrEvent, (TypeVariable<?>)requiredType, beanType, visited);
         }
         else if (requiredType instanceof GenericArrayType)
         {
             return Class.class.isInstance(beanType) && Class.class.cast(beanType).isArray()
-                    && isAssignableFrom(isDelegateOrEvent, (GenericArrayType)requiredType, beanType);
+                    && isAssignableFrom(isDelegateOrEvent, (GenericArrayType)requiredType, beanType, visited);
         }
         else if (requiredType instanceof WildcardType)
         {
-            return isAssignableFrom(isDelegateOrEvent, (WildcardType)requiredType, beanType);
-        }
-        else
-        {
-            throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
+            return isAssignableFrom(isDelegateOrEvent, (WildcardType)requiredType, beanType, visited);
         }
+        throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, Type beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, Type beanType,
+                                            Map<Type, Integer> visited)
     {
         if (beanType instanceof Class)
         {
@@ -147,19 +152,19 @@ public final class GenericsUtil
         }
         else if (beanType instanceof TypeVariable)
         {
-            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>)beanType);
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>)beanType, visited);
         }
         else if (beanType instanceof ParameterizedType)
         {
-            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType);
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType, visited);
         }
         else if (beanType instanceof GenericArrayType)
         {
-            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (GenericArrayType)beanType);
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (GenericArrayType)beanType, visited);
         }
         else if (beanType instanceof WildcardType)
         {
-            return isAssignableFrom(isDelegateOrEvent, (Type)injectionPointType, (WildcardType)beanType);
+            return isAssignableFrom(isDelegateOrEvent, (Type)injectionPointType, (WildcardType)beanType, visited);
         }
         else
         {
@@ -172,11 +177,12 @@ public final class GenericsUtil
         return ClassUtil.isClassAssignableFrom(injectionPointType, beanType);
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, TypeVariable<?> beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, TypeVariable<?> beanType,
+                                            Map<Type, Integer> visited)
     {
         for (Type bounds: beanType.getBounds())
         {
-            if (isAssignableFrom(isDelegateOrEvent, injectionPointType, bounds))
+            if (isAssignableFrom(isDelegateOrEvent, injectionPointType, bounds, visited))
             {
                 return true;
             }
@@ -188,7 +194,8 @@ public final class GenericsUtil
      * CDI Spec. 5.2.4: "A parameterized bean type is considered assignable to a raw required type
      * if the raw types are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object." 
      */
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, ParameterizedType beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, ParameterizedType beanType,
+                                            Map<Type, Integer> visited)
     {
         if (beanType.getRawType() != injectionPointType)
         {
@@ -199,7 +206,7 @@ public final class GenericsUtil
         {
             // for delegate and events we match 'in reverse' kind off
             // @Observes ProcessInjectionPoint<?, Instance> does also match Instance<SomeBean>
-            return isAssignableFrom(true, injectionPointType, beanType.getRawType());
+            return isAssignableFrom(true, injectionPointType, beanType.getRawType(), visited);
         }
 
         for (Type typeArgument: beanType.getActualTypeArguments())
@@ -224,23 +231,29 @@ public final class GenericsUtil
         return true;
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, GenericArrayType beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Class<?> injectionPointType, GenericArrayType beanType,
+                                            Map<Type, Integer> visited)
     {
-        return injectionPointType.isArray() && isAssignableFrom(isDelegateOrEvent, injectionPointType.getComponentType(), beanType.getGenericComponentType());
+        return injectionPointType.isArray() && isAssignableFrom(isDelegateOrEvent, injectionPointType.getComponentType(), beanType.getGenericComponentType(), visited);
     }
     
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Type injectionPointType, WildcardType beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, Type injectionPointType, WildcardType beanType,
+                                            Map<Type, Integer> visited)
     {
+        if (isGenericLoop(beanType, visited))
+        {
+            return false;
+        }
         for (Type bounds: beanType.getLowerBounds())
         {
-            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, injectionPointType))
+            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, injectionPointType, visited))
             {
                 return false;
             }
         }
         for (Type bounds: beanType.getUpperBounds())
         {
-            if (isAssignableFrom(isDelegateOrEvent, false, injectionPointType, bounds))
+            if (isAssignableFrom(isDelegateOrEvent, false, injectionPointType, bounds, visited))
             {
                 return true;
             }
@@ -248,23 +261,29 @@ public final class GenericsUtil
         return false;
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Type beanType)
+    private static boolean isGenericLoop(Type beanType, Map<Type, Integer> visited)
+    {
+        return visited.compute(beanType, (type, integer) -> integer == null ? 1 : (integer + 1)) > MAX_GENERIC_LOOPS;
+    }
+
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Type beanType,
+                                            Map<Type, Integer> visited)
     {
         if (beanType instanceof Class)
         {
-            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (Class<?>)beanType);
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (Class<?>)beanType, visited);
         }
         else if (beanType instanceof TypeVariable)
         {
-            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (TypeVariable<?>)beanType);
+            return isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, (TypeVariable<?>)beanType, visited);
         }
         else if (beanType instanceof ParameterizedType)
         {
-            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType);
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (ParameterizedType)beanType, visited);
         }
         else if (beanType instanceof WildcardType)
         {
-            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (WildcardType)beanType);
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (WildcardType)beanType, visited);
         }
         else if (beanType instanceof GenericArrayType)
         {
@@ -276,7 +295,8 @@ public final class GenericsUtil
         }
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Class<?> beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, Class<?> beanType,
+                                            Map<Type, Integer> visited)
     {
         Class<?> rawInjectionPointType = getRawType(injectionPointType);
         if (rawInjectionPointType.equals(beanType))
@@ -300,13 +320,13 @@ public final class GenericsUtil
         {
             return false;
         }
-        if (beanType.getSuperclass() != null && isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType.getGenericSuperclass()))
+        if (beanType.getSuperclass() != null && isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, beanType.getGenericSuperclass(), visited))
         {
             return true;
         }
         for (Type genericInterface: beanType.getGenericInterfaces())
         {
-            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, genericInterface))
+            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, genericInterface, visited))
             {
                 return true;
             }
@@ -314,7 +334,8 @@ public final class GenericsUtil
         return false;
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, TypeVariable<?> beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, boolean isProducer, ParameterizedType injectionPointType, TypeVariable<?> beanType,
+                                            Map<Type, Integer> visited)
     {
         Type[] types = beanType.getBounds();
         if (isNotBound(types))
@@ -323,7 +344,7 @@ public final class GenericsUtil
         }
         for (Type bounds: types)
         {
-            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, bounds))
+            if (isAssignableFrom(isDelegateOrEvent, isProducer, injectionPointType, bounds, visited))
             {
                 return true;
             }
@@ -334,7 +355,8 @@ public final class GenericsUtil
     /**
      * CDI Spec. 5.2.4
      */
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, ParameterizedType injectionPointType, ParameterizedType beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, ParameterizedType injectionPointType, ParameterizedType beanType,
+                                            Map<Type, Integer> visited)
     {
         if (injectionPointType.getRawType() != beanType.getRawType())
         {
@@ -360,7 +382,7 @@ public final class GenericsUtil
                 {
                     for (Type upperBound : bounds)
                     {
-                        if (!isAssignableFrom(true, false, upperBound, injectionPointTypeArgument))
+                        if (!isAssignableFrom(true, false, upperBound, injectionPointTypeArgument, visited))
                         {
                             return false;
                         }
@@ -377,7 +399,7 @@ public final class GenericsUtil
                 return injectionPointTypeArgument.equals(beanTypeArgument);
 
             }
-            else if (!isAssignableFrom(isDelegateOrEvent, false, injectionPointTypeArgument, beanTypeArgument))
+            else if (!isAssignableFrom(isDelegateOrEvent, false, injectionPointTypeArgument, beanTypeArgument, visited))
             {
                 return false;
             }
@@ -390,11 +412,16 @@ public final class GenericsUtil
         return bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class == bounds[0]);
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, TypeVariable<?> injectionPointType, Type beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, TypeVariable<?> injectionPointType, Type beanType,
+                                            Map<Type, Integer> visited)
     {
+        if (isGenericLoop(beanType, visited))
+        {
+            return false; // looping type so not resolvable
+        }
         for (Type bounds: injectionPointType.getBounds())
         {
-            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, beanType))
+            if (!isAssignableFrom(isDelegateOrEvent, false, bounds, beanType, visited))
             {
                 return false;
             }
@@ -403,7 +430,8 @@ public final class GenericsUtil
     }
 
     // rules are a bit different when in an array so we handle ParameterizedType manually (not reusing isAssignableFrom)
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, GenericArrayType injectionPointType, Type beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, GenericArrayType injectionPointType, Type beanType,
+                                            Map<Type, Integer> visited)
     {
         Type genericComponentType = injectionPointType.getGenericComponentType();
         Class componentType = Class.class.cast(beanType).getComponentType();
@@ -413,20 +441,21 @@ public final class GenericsUtil
         }
         if (ParameterizedType.class.isInstance(genericComponentType))
         {
-            return isAssignableFrom(isDelegateOrEvent, false, ParameterizedType.class.cast(genericComponentType).getRawType(), componentType);
+            return isAssignableFrom(isDelegateOrEvent, false, ParameterizedType.class.cast(genericComponentType).getRawType(), componentType, visited);
         }
-        return isAssignableFrom(isDelegateOrEvent, false, genericComponentType, componentType);
+        return isAssignableFrom(isDelegateOrEvent, false, genericComponentType, componentType, visited);
     }
 
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, Type beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, Type beanType,
+                                            Map<Type, Integer> visited)
     {
         if (beanType instanceof TypeVariable)
         {
-            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>)beanType);
+            return isAssignableFrom(isDelegateOrEvent, injectionPointType, (TypeVariable<?>)beanType, visited);
         }
         for (Type bounds: injectionPointType.getLowerBounds())
         {
-            if (!isAssignableFrom(isDelegateOrEvent, false, beanType, bounds))
+            if (!isAssignableFrom(isDelegateOrEvent, false, beanType, bounds, visited))
             {
                 return false;
             }
@@ -437,7 +466,7 @@ public final class GenericsUtil
             boolean isAssignable = false;
             for (Type beanSupertype: beanTypeClosure)
             {
-                if (isAssignableFrom(isDelegateOrEvent, false, bounds, beanSupertype)
+                if (isAssignableFrom(isDelegateOrEvent, false, bounds, beanSupertype, visited)
                     || (Class.class.isInstance(bounds)
                         && ParameterizedType.class.isInstance(beanSupertype)
                         && bounds == ParameterizedType.class.cast(beanSupertype).getRawType()))
@@ -457,13 +486,14 @@ public final class GenericsUtil
     /**
      * CDI 1.1 Spec. 5.2.4, third bullet point
      */
-    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, TypeVariable<?> beanType)
+    private static boolean isAssignableFrom(boolean isDelegateOrEvent, WildcardType injectionPointType, TypeVariable<?> beanType,
+                                            Map<Type, Integer> visited)
     {
         for (Type upperBound: injectionPointType.getUpperBounds())
         {
             for (Type bound: beanType.getBounds())
             {
-                if (!isAssignableFrom(isDelegateOrEvent, false, upperBound, bound) && !isAssignableFrom(isDelegateOrEvent, false, bound, upperBound))
+                if (!isAssignableFrom(isDelegateOrEvent, false, upperBound, bound, visited) && !isAssignableFrom(isDelegateOrEvent, false, bound, upperBound, visited))
                 {
                     return false;
                 }
@@ -473,7 +503,7 @@ public final class GenericsUtil
         {
             for (Type bound: beanType.getBounds())
             {
-                if (!isAssignableFrom(isDelegateOrEvent, false, bound, lowerBound))
+                if (!isAssignableFrom(isDelegateOrEvent, false, bound, lowerBound, visited))
                 {
                     return false;
                 }

Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/util/WebBeansUtil.java Sun Jun 23 09:10:31 2019
@@ -151,6 +151,7 @@ import java.lang.reflect.WildcardType;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -1471,7 +1472,8 @@ public final class WebBeansUtil
                     rawType.equals(Interceptor.class))
                 {
                     Type[] types = ClassUtil.getActualTypeArguments(injectionPoint.getType());
-                    if (types.length != 1 || !GenericsUtil.isAssignableFrom(false, AbstractProducerBean.class.isInstance(bean), bean.getBeanClass(), types[0]))
+                    if (types.length != 1 || !GenericsUtil.isAssignableFrom(
+                            false, AbstractProducerBean.class.isInstance(bean), bean.getBeanClass(), types[0], new HashMap<>()))
                     {
                         throw new WebBeansConfigurationException("injected bean parameter must be " + rawType);
                     }
@@ -1720,7 +1722,8 @@ public final class WebBeansUtil
                     Class<?> beanClass = AbstractOwbBean.class.isInstance(injectionPointBean) ?
                             AbstractOwbBean.class.cast(injectionPointBean).getReturnType() : injectionPointBean.getBeanClass();
                     Type beanType = pt.getActualTypeArguments()[0];
-                    if (!GenericsUtil.isAssignableFrom(false, AbstractProducerBean.class.isInstance(bean), beanClass, beanType))
+                    if (!GenericsUtil.isAssignableFrom(
+                            false, AbstractProducerBean.class.isInstance(bean), beanClass, beanType, new HashMap<>()))
                     {
                         throw new WebBeansConfigurationException("@Inject Bean<X> can only be done in X, found " + beanType + " and " + beanClass);
                     }

Modified: openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/unittests/inject/parametrized/GenericClassTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/unittests/inject/parametrized/GenericClassTest.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/unittests/inject/parametrized/GenericClassTest.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/unittests/inject/parametrized/GenericClassTest.java Sun Jun 23 09:10:31 2019
@@ -20,6 +20,7 @@ package org.apache.webbeans.test.unittes
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Type;
+import java.util.HashMap;
 
 import org.apache.webbeans.test.injection.generics.zoo.Horse;
 import org.apache.webbeans.test.injection.generics.zoo.HorseStable;
@@ -44,10 +45,10 @@ public class GenericClassTest
         Field check22Bound = Dao.class.getField("check22WithBound");
         Field check4 = WithTypeVariable.class.getField("check4");
 
-        Assert.assertFalse(GenericsUtil.satisfiesDependency(false, false, raw.getGenericType(), t.getGenericType()));
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, check4.getGenericType(), t.getGenericType()));
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, check22.getGenericType(), t.getGenericType()));
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, check22Bound.getGenericType(), t.getGenericType()));
+        Assert.assertFalse(GenericsUtil.satisfiesDependency(false, false, raw.getGenericType(), t.getGenericType(), new HashMap<>()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, check4.getGenericType(), t.getGenericType(), new HashMap<>()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, check22.getGenericType(), t.getGenericType(), new HashMap<>()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, check22Bound.getGenericType(), t.getGenericType(), new HashMap<>()));
     }
     
     @Test
@@ -59,8 +60,8 @@ public class GenericClassTest
         Field f4 = UserDao.class.getField("field4");
 
 
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, f3.getGenericType(), f1.getGenericType()));
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, f4.getGenericType(), f1.getGenericType()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, f3.getGenericType(), f1.getGenericType(), new HashMap<>()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, f4.getGenericType(), f1.getGenericType(), new HashMap<>()));
     }
 
     @Test
@@ -74,8 +75,8 @@ public class GenericClassTest
         Type pigStableType = this.getClass().getDeclaredField("pigStable").getType().getGenericSuperclass();
         Type horseStableType = this.getClass().getDeclaredField("horseStable").getType().getGenericSuperclass();
 
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, horseStableType, parameterizedHorseStableType));
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, parameterizedPigStableType, pigStableType));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, horseStableType, parameterizedHorseStableType, new HashMap<>()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, parameterizedPigStableType, pigStableType, new HashMap<>()));
     }
     // fields for {@link #testStable}
     private Stable<Horse> parameterizedHorseStable;
@@ -91,8 +92,8 @@ public class GenericClassTest
         Type parameterizedHorseStableType = this.getClass().getDeclaredField("parameterizedHorseStable").getGenericType();
         Type stableProducerMethodType = this.getClass().getDeclaredMethod("stableProducer").getGenericReturnType();
 
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, parameterizedPigStableType, stableProducerMethodType));
-        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, parameterizedHorseStableType, stableProducerMethodType));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, parameterizedPigStableType, stableProducerMethodType, new HashMap<>()));
+        Assert.assertTrue(GenericsUtil.satisfiesDependency(false, false, parameterizedHorseStableType, stableProducerMethodType, new HashMap<>()));
     }
     // method and field for {@link #testGenericProducerType}
     private <T> Stable<T> stableProducer()

Modified: openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/util/GenericsUtilTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/util/GenericsUtilTest.java?rev=1861905&r1=1861904&r2=1861905&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/util/GenericsUtilTest.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/test/util/GenericsUtilTest.java Sun Jun 23 09:10:31 2019
@@ -20,8 +20,11 @@ package org.apache.webbeans.test.util;
 
 import static java.util.Arrays.asList;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.Field;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Method;
@@ -30,6 +33,8 @@ import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.webbeans.config.OwbParametrizedTypeImpl;
@@ -87,6 +92,100 @@ public class GenericsUtilTest {
         Assert.assertFalse(GenericsUtil.containsWildcardType(GenericObject.class.getMethod("getObject").getGenericReturnType()));
     }
 
+    @Test
+    public void genericsLoop()
+    {
+        final ParameterizedType injectionPointType = new OwbParametrizedTypeImpl(null, GenericFoo.class, Long.class);
+        final TypeVariable<Class<?>> t = new TypeVariable<Class<?>>() {
+            @Override
+            public Type[] getBounds() {
+                final TypeVariable<?> ref = this;
+                return new Type[]{
+                    new OwbParametrizedTypeImpl(null, Comparable.class, new TypeVariable<Class<?>>() {
+                        @Override
+                        public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+                        {
+                            return null;
+                        }
+
+                        @Override
+                        public Annotation[] getAnnotations()
+                        {
+                            return new Annotation[0];
+                        }
+
+                        @Override
+                        public Annotation[] getDeclaredAnnotations()
+                        {
+                            return new Annotation[0];
+                        }
+
+                        @Override
+                        public Type[] getBounds()
+                        {
+                            return new Type[] { new OwbParametrizedTypeImpl(null, Comparable.class, ref) };
+                        }
+
+                        @Override
+                        public Class<?> getGenericDeclaration() {
+                            return GenericFoo.class;
+                        }
+
+                        @Override
+                        public String getName()
+                        {
+                            return "T";
+                        }
+
+                        @Override
+                        public AnnotatedType[] getAnnotatedBounds()
+                        {
+                            return new AnnotatedType[0];
+                        }
+                    })
+                };
+            }
+
+            @Override
+            public Class<?> getGenericDeclaration()
+            {
+                return GenericFoo.class;
+            }
+
+            @Override
+            public String getName()
+            {
+                return "T";
+            }
+
+            @Override
+            public AnnotatedType[] getAnnotatedBounds()
+            {
+                return new AnnotatedType[0];
+            }
+
+            @Override
+            public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+            {
+                return null;
+            }
+
+            @Override
+            public Annotation[] getAnnotations()
+            {
+                return new Annotation[0];
+            }
+
+            @Override
+            public Annotation[] getDeclaredAnnotations()
+            {
+                return new Annotation[0];
+            }
+        };
+        final ParameterizedType beanType = new OwbParametrizedTypeImpl(null, GenericFoo.class, t);
+        assertFalse(GenericsUtil.satisfiesDependency(false, false, injectionPointType, beanType, new HashMap<>()));
+    }
+
     public static abstract class AbstractObject<V>
     {
     
@@ -180,4 +279,23 @@ public class GenericsUtilTest {
             return this;
         }
     }
+
+    public interface GenericFoo<T extends Comparable<T>>
+    {
+        T someMethod();
+    }
+
+    public static class FooImpl<T extends Comparable<T>> implements GenericFoo<T>
+    {
+        @Override
+        public T someMethod()
+        {
+            return null;
+        }
+    }
+
+    public static class Bar
+    {
+        GenericFoo<Long> foo;
+    }
 }



Mime
View raw message