Search in sources :

Example 1 with MethodDescriptor.ofMethod

use of io.quarkus.gizmo.MethodDescriptor.ofMethod in project quarkus by quarkusio.

the class BytecodeRecorderImpl method loadComplexObject.

/**
 * Created a {@link DeferredParameter} to load a complex object, such as a javabean or collection. This is basically
 * just an extension of {@link #loadObjectInstanceImpl(Object, Map, Class, boolean)} but it removes some of the more complex
 * code from that method.
 *
 * @param param The object to load
 * @param existing The existing object map
 * @param expectedType The expected type of the object
 * @param relaxedValidation
 * @return
 */
private DeferredParameter loadComplexObject(Object param, Map<Object, DeferredParameter> existing, Class<?> expectedType, boolean relaxedValidation) {
    // a list of steps that are performed on the object after it has been created
    // we need to create all these first, to ensure the required objects have already
    // been deserialized
    List<SerializationStep> setupSteps = new ArrayList<>();
    List<SerializationStep> ctorSetupSteps = new ArrayList<>();
    boolean relaxedOk = false;
    if (param instanceof Collection) {
        // if this is a collection we want to serialize every element
        for (Object i : (Collection) param) {
            DeferredParameter val = i != null ? loadObjectInstance(i, existing, i.getClass(), relaxedValidation) : loadObjectInstance(null, existing, Object.class, relaxedValidation);
            setupSteps.add(new SerializationStep() {

                @Override
                public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                    // each step can happen in a new method, so it is safe to do this
                    method.invokeInterfaceMethod(COLLECTION_ADD, context.loadDeferred(out), context.loadDeferred(val));
                }

                @Override
                public void prepare(MethodContext context) {
                    // handle the value serialization
                    val.prepare(context);
                }
            });
        }
        relaxedOk = true;
    }
    if (param instanceof Map) {
        // map works the same as collection
        for (Map.Entry<?, ?> i : ((Map<?, ?>) param).entrySet()) {
            DeferredParameter key = loadObjectInstance(i.getKey(), existing, i.getKey().getClass(), relaxedValidation);
            DeferredParameter val = i.getValue() != null ? loadObjectInstance(i.getValue(), existing, i.getValue().getClass(), relaxedValidation) : loadObjectInstance(null, existing, Object.class, relaxedValidation);
            setupSteps.add(new SerializationStep() {

                @Override
                public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                    method.invokeInterfaceMethod(MAP_PUT, context.loadDeferred(out), context.loadDeferred(key), context.loadDeferred(val));
                }

                @Override
                public void prepare(MethodContext context) {
                    key.prepare(context);
                    val.prepare(context);
                }
            });
        }
        relaxedOk = true;
    }
    // check how the object is constructed
    NonDefaultConstructorHolder nonDefaultConstructorHolder = null;
    DeferredParameter[] nonDefaultConstructorHandles = null;
    // used to resolve the parameter position for @RecordableConstructor
    Map<String, Integer> constructorParamNameMap = new HashMap<>();
    if (nonDefaultConstructors.containsKey(param.getClass())) {
        nonDefaultConstructorHolder = nonDefaultConstructors.get(param.getClass());
        List<Object> params = nonDefaultConstructorHolder.paramGenerator.apply(param);
        if (params.size() != nonDefaultConstructorHolder.constructor.getParameterCount()) {
            throw new RuntimeException("Unable to serialize " + param + " as the wrong number of parameters were generated for " + nonDefaultConstructorHolder.constructor);
        }
        int count = 0;
        nonDefaultConstructorHandles = new DeferredParameter[params.size()];
        Class<?>[] parameterTypes = nonDefaultConstructorHolder.constructor.getParameterTypes();
        for (int i = 0; i < params.size(); i++) {
            Object obj = params.get(i);
            nonDefaultConstructorHandles[i] = loadObjectInstance(obj, existing, parameterTypes[count++], relaxedValidation);
        }
    } else if (classesToUseRecorableConstructor.contains(param.getClass())) {
        Constructor<?> current = null;
        int count = 0;
        for (var c : param.getClass().getConstructors()) {
            if (current == null || current.getParameterCount() < c.getParameterCount()) {
                current = c;
                count = 0;
            } else if (current != null && current.getParameterCount() == c.getParameterCount()) {
                count++;
            }
        }
        if (current == null || count > 0) {
            throw new RuntimeException("Unable to determine the recordable constructor to use for " + param.getClass());
        }
        nonDefaultConstructorHolder = new NonDefaultConstructorHolder(current, null);
        nonDefaultConstructorHandles = new DeferredParameter[current.getParameterCount()];
        if (current.getParameterCount() > 0) {
            Parameter[] parameters = current.getParameters();
            for (int i = 0; i < current.getParameterCount(); ++i) {
                String name = parameters[i].getName();
                constructorParamNameMap.put(name, i);
            }
        }
    } else {
        for (Constructor<?> ctor : param.getClass().getConstructors()) {
            if (ctor.isAnnotationPresent(RecordableConstructor.class)) {
                nonDefaultConstructorHolder = new NonDefaultConstructorHolder(ctor, null);
                nonDefaultConstructorHandles = new DeferredParameter[ctor.getParameterCount()];
                if (ctor.getParameterCount() > 0) {
                    Parameter[] ctorParameters = ctor.getParameters();
                    for (int i = 0; i < ctor.getParameterCount(); ++i) {
                        String name = ctorParameters[i].getName();
                        constructorParamNameMap.put(name, i);
                    }
                }
                break;
            }
        }
    }
    Set<String> handledProperties = new HashSet<>();
    Property[] desc = PropertyUtils.getPropertyDescriptors(param);
    for (Property i : desc) {
        if (!i.getDeclaringClass().getPackageName().startsWith("java.")) {
            // check if the getter is ignored
            if ((i.getReadMethod() != null) && (i.getReadMethod().getAnnotation(IgnoreProperty.class) != null)) {
                continue;
            }
            // check if the matching field is ignored
            try {
                if (param.getClass().getDeclaredField(i.getName()).getAnnotation(IgnoreProperty.class) != null) {
                    continue;
                }
            } catch (NoSuchFieldException ignored) {
            }
        }
        Integer ctorParamIndex = constructorParamNameMap.remove(i.name);
        if (i.getReadMethod() != null && i.getWriteMethod() == null && ctorParamIndex == null) {
            try {
                // read only prop, we may still be able to do stuff with it if it is a collection
                if (Collection.class.isAssignableFrom(i.getPropertyType())) {
                    // special case, a collection with only a read method
                    // we assume we can just add to the connection
                    handledProperties.add(i.getName());
                    Collection propertyValue = (Collection) i.read(param);
                    if (propertyValue != null && !propertyValue.isEmpty()) {
                        List<DeferredParameter> params = new ArrayList<>();
                        for (Object c : propertyValue) {
                            DeferredParameter toAdd = loadObjectInstance(c, existing, Object.class, relaxedValidation);
                            params.add(toAdd);
                        }
                        setupSteps.add(new SerializationStep() {

                            @Override
                            public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                                // get the collection
                                ResultHandle prop = method.invokeVirtualMethod(MethodDescriptor.ofMethod(i.getReadMethod()), context.loadDeferred(out));
                                for (DeferredParameter i : params) {
                                    // add the parameter
                                    // TODO: this is not guareded against large collections, probably not an issue in practice
                                    method.invokeInterfaceMethod(COLLECTION_ADD, prop, context.loadDeferred(i));
                                }
                            }

                            @Override
                            public void prepare(MethodContext context) {
                                for (DeferredParameter i : params) {
                                    i.prepare(context);
                                }
                            }
                        });
                    }
                } else if (Map.class.isAssignableFrom(i.getPropertyType())) {
                    // special case, a map with only a read method
                    // we assume we can just add to the map
                    // similar to how collection works above
                    handledProperties.add(i.getName());
                    Map<Object, Object> propertyValue = (Map<Object, Object>) i.read(param);
                    if (propertyValue != null && !propertyValue.isEmpty()) {
                        Map<DeferredParameter, DeferredParameter> def = new LinkedHashMap<>();
                        for (Map.Entry<Object, Object> entry : propertyValue.entrySet()) {
                            DeferredParameter key = loadObjectInstance(entry.getKey(), existing, Object.class, relaxedValidation);
                            DeferredParameter val = loadObjectInstance(entry.getValue(), existing, Object.class, relaxedValidation);
                            def.put(key, val);
                        }
                        setupSteps.add(new SerializationStep() {

                            @Override
                            public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                                ResultHandle prop = method.invokeVirtualMethod(MethodDescriptor.ofMethod(i.getReadMethod()), context.loadDeferred(out));
                                for (Map.Entry<DeferredParameter, DeferredParameter> e : def.entrySet()) {
                                    method.invokeInterfaceMethod(MAP_PUT, prop, context.loadDeferred(e.getKey()), context.loadDeferred(e.getValue()));
                                }
                            }

                            @Override
                            public void prepare(MethodContext context) {
                                for (Map.Entry<DeferredParameter, DeferredParameter> e : def.entrySet()) {
                                    e.getKey().prepare(context);
                                    e.getValue().prepare(context);
                                }
                            }
                        });
                    }
                } else if (!relaxedValidation && !i.getName().equals("class") && !relaxedOk && nonDefaultConstructorHolder == null) {
                    // check if there is actually a field with the name
                    try {
                        i.getReadMethod().getDeclaringClass().getDeclaredField(i.getName());
                        throw new RuntimeException("Cannot serialise field '" + i.getName() + "' on object '" + param + "' as the property is read only");
                    } catch (NoSuchFieldException e) {
                    // if there is no underlying field then we ignore the property
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else if (i.getReadMethod() != null && (i.getWriteMethod() != null || ctorParamIndex != null)) {
            // normal javabean property
            try {
                handledProperties.add(i.getName());
                Object propertyValue = i.read(param);
                if (propertyValue == null && ctorParamIndex == null) {
                    // TODO: is this a valid assumption? Should we check this by creating an instance?
                    continue;
                }
                Class propertyType = i.getPropertyType();
                if (ctorParamIndex == null) {
                    Class<?> getterReturnType = i.getReadMethod().getReturnType();
                    Class<?> setterParameterType = i.getWriteMethod().getParameterTypes()[0];
                    if (getterReturnType != setterParameterType) {
                        if (relaxedValidation) {
                            for (Method m : param.getClass().getMethods()) {
                                if (m.getName().equals(i.getWriteMethod().getName())) {
                                    if (m.getParameterCount() > 0) {
                                        Class<?>[] parameterTypes = m.getParameterTypes();
                                        if (parameterTypes[0].isAssignableFrom(param.getClass())) {
                                            propertyType = parameterTypes[0];
                                            break;
                                        }
                                    }
                                }
                            }
                        } else {
                            throw new RuntimeException("Cannot serialise field '" + i.getName() + "' on object '" + param + "' of type '" + param.getClass().getName() + "' as getter and setter are of different types. Getter type is '" + getterReturnType.getName() + "' while setter type is '" + setterParameterType.getName() + "'.");
                        }
                    }
                }
                DeferredParameter val = loadObjectInstance(propertyValue, existing, i.getPropertyType(), relaxedValidation);
                if (ctorParamIndex != null) {
                    nonDefaultConstructorHandles[ctorParamIndex] = val;
                    ctorSetupSteps.add(new SerializationStep() {

                        @Override
                        public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                        }

                        @Override
                        public void prepare(MethodContext context) {
                            val.prepare(context);
                        }
                    });
                } else {
                    Class finalPropertyType = propertyType;
                    setupSteps.add(new SerializationStep() {

                        @Override
                        public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                            ResultHandle object = context.loadDeferred(out);
                            ResultHandle resultVal = context.loadDeferred(val);
                            method.invokeVirtualMethod(ofMethod(param.getClass(), i.getWriteMethod().getName(), i.getWriteMethod().getReturnType(), finalPropertyType), object, resultVal);
                        }

                        @Override
                        public void prepare(MethodContext context) {
                            val.prepare(context);
                        }
                    });
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    // now handle accessible fields
    for (Field field : param.getClass().getFields()) {
        // check if the field is ignored
        if (field.getAnnotation(IgnoreProperty.class) != null) {
            continue;
        }
        if (!handledProperties.contains(field.getName())) {
            Integer ctorParamIndex = constructorParamNameMap.remove(field.getName());
            if ((ctorParamIndex != null || !Modifier.isFinal(field.getModifiers())) && !Modifier.isStatic(field.getModifiers())) {
                try {
                    DeferredParameter val = loadObjectInstance(field.get(param), existing, field.getType(), relaxedValidation);
                    if (ctorParamIndex != null) {
                        nonDefaultConstructorHandles[ctorParamIndex] = val;
                        ctorSetupSteps.add(new SerializationStep() {

                            @Override
                            public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                            }

                            @Override
                            public void prepare(MethodContext context) {
                                val.prepare(context);
                            }
                        });
                    } else {
                        setupSteps.add(new SerializationStep() {

                            @Override
                            public void handle(MethodContext context, MethodCreator method, DeferredArrayStoreParameter out) {
                                method.writeInstanceField(FieldDescriptor.of(param.getClass(), field.getName(), field.getType()), context.loadDeferred(out), context.loadDeferred(val));
                            }

                            @Override
                            public void prepare(MethodContext context) {
                                val.prepare(context);
                            }
                        });
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
    if (!constructorParamNameMap.isEmpty()) {
        throw new RuntimeException("Could not find parameters for constructor " + nonDefaultConstructorHolder.constructor + " could not read field values " + constructorParamNameMap.keySet());
    }
    NonDefaultConstructorHolder finalNonDefaultConstructorHolder = nonDefaultConstructorHolder;
    DeferredParameter[] finalCtorHandles = nonDefaultConstructorHandles;
    // create a deferred value to represet the object itself. This allows the creation to be split
    // over multiple methods, which is important if this is a large object
    DeferredArrayStoreParameter objectValue = new DeferredArrayStoreParameter(param, expectedType) {

        @Override
        ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
            ResultHandle out;
            // do the creation
            if (finalNonDefaultConstructorHolder != null) {
                out = method.newInstance(ofConstructor(finalNonDefaultConstructorHolder.constructor.getDeclaringClass(), finalNonDefaultConstructorHolder.constructor.getParameterTypes()), Arrays.stream(finalCtorHandles).map(m -> context.loadDeferred(m)).toArray(ResultHandle[]::new));
            } else {
                if (List.class.isAssignableFrom(param.getClass()) && expectedType == List.class) {
                    // list is a common special case, so let's handle it
                    List listParam = (List) param;
                    if (listParam.isEmpty()) {
                        out = method.newInstance(ofConstructor(ArrayList.class));
                    } else {
                        out = method.newInstance(ofConstructor(ArrayList.class, int.class), method.load(listParam.size()));
                    }
                } else {
                    try {
                        param.getClass().getDeclaredConstructor();
                        out = method.newInstance(ofConstructor(param.getClass()));
                    } catch (NoSuchMethodException e) {
                        // fallback for collection types, such as unmodifiableMap
                        if (SortedMap.class.isAssignableFrom(expectedType)) {
                            out = method.newInstance(ofConstructor(TreeMap.class));
                        } else if (Map.class.isAssignableFrom(expectedType)) {
                            out = method.newInstance(ofConstructor(LinkedHashMap.class));
                        } else if (List.class.isAssignableFrom(expectedType)) {
                            out = method.newInstance(ofConstructor(ArrayList.class));
                        } else if (SortedSet.class.isAssignableFrom(expectedType)) {
                            out = method.newInstance(ofConstructor(TreeSet.class));
                        } else if (Set.class.isAssignableFrom(expectedType)) {
                            out = method.newInstance(ofConstructor(LinkedHashSet.class));
                        } else {
                            throw new RuntimeException("Unable to serialize objects of type " + param.getClass() + " to bytecode as it has no default constructor");
                        }
                    }
                }
            }
            return out;
        }
    };
    // now return the actual deferred parameter that represents the result of construction
    return new DeferredArrayStoreParameter(param, expectedType) {

        @Override
        void doPrepare(MethodContext context) {
            // first create the actial object
            for (SerializationStep i : ctorSetupSteps) {
                i.prepare(context);
            }
            objectValue.prepare(context);
            for (SerializationStep i : setupSteps) {
                // then prepare the steps (i.e. creating the values to be placed into this object)
                i.prepare(context);
                // now actually run the steps (i.e. actually stick the values into the object)
                context.writeInstruction(new InstructionGroup() {

                    @Override
                    public void write(MethodContext context, MethodCreator method, ResultHandle array) {
                        i.handle(context, method, objectValue);
                    }
                });
            }
            super.doPrepare(context);
        }

        @Override
        ResultHandle createValue(MethodContext context, MethodCreator method, ResultHandle array) {
            // just return the already created object
            return context.loadDeferred(objectValue);
        }
    };
}
Also used : Arrays(java.util.Arrays) Array(java.lang.reflect.Array) SortedSet(java.util.SortedSet) ListIterator(java.util.ListIterator) URL(java.net.URL) ClassOutput(io.quarkus.gizmo.ClassOutput) ClassInfo(org.jboss.jandex.ClassInfo) MethodInfo(org.jboss.jandex.MethodInfo) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) RecordableConstructor(io.quarkus.runtime.annotations.RecordableConstructor) Duration(java.time.Duration) Map(java.util.Map) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) StartupContext(io.quarkus.runtime.StartupContext) Method(java.lang.reflect.Method) AnnotationValue(org.jboss.jandex.AnnotationValue) Assert(org.wildfly.common.Assert) IdentityHashMap(java.util.IdentityHashMap) ProxyConfiguration(io.quarkus.deployment.proxy.ProxyConfiguration) Collection(java.util.Collection) Property(io.quarkus.deployment.recording.PropertyUtils.Property) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) NavigableSet(java.util.NavigableSet) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) Collectors(java.util.stream.Collectors) ProxyFactory(io.quarkus.deployment.proxy.ProxyFactory) List(java.util.List) RelaxedValidation(io.quarkus.runtime.annotations.RelaxedValidation) Modifier(java.lang.reflect.Modifier) Annotation(java.lang.annotation.Annotation) Optional(java.util.Optional) IgnoreProperty(io.quarkus.runtime.annotations.IgnoreProperty) ArrayType(org.jboss.jandex.ArrayType) ResultHandle(io.quarkus.gizmo.ResultHandle) SortedMap(java.util.SortedMap) TryBlock(io.quarkus.gizmo.TryBlock) MethodDescriptor.ofConstructor(io.quarkus.gizmo.MethodDescriptor.ofConstructor) CatchBlockCreator(io.quarkus.gizmo.CatchBlockCreator) Proxy(java.lang.reflect.Proxy) MethodCreator(io.quarkus.gizmo.MethodCreator) Type(org.jboss.jandex.Type) HashMap(java.util.HashMap) MethodDescriptor.ofMethod(io.quarkus.gizmo.MethodDescriptor.ofMethod) ClassCreator(io.quarkus.gizmo.ClassCreator) Constructor(java.lang.reflect.Constructor) Function(java.util.function.Function) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Inject(javax.inject.Inject) Parameter(java.lang.reflect.Parameter) RuntimeValue(io.quarkus.runtime.RuntimeValue) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) StartupTask(io.quarkus.runtime.StartupTask) LinkedHashSet(java.util.LinkedHashSet) MethodDescriptor(io.quarkus.gizmo.MethodDescriptor) MalformedURLException(java.net.MalformedURLException) Field(java.lang.reflect.Field) FieldDescriptor(io.quarkus.gizmo.FieldDescriptor) AbstractMap(java.util.AbstractMap) ParameterizedType(java.lang.reflect.ParameterizedType) TreeMap(java.util.TreeMap) Closeable(java.io.Closeable) InvocationHandler(java.lang.reflect.InvocationHandler) AnnotationProxy(io.quarkus.deployment.recording.AnnotationProxyProvider.AnnotationProxy) ObjectSubstitution(io.quarkus.runtime.ObjectSubstitution) Collections(java.util.Collections) LinkedHashSet(java.util.LinkedHashSet) IdentityHashMap(java.util.IdentityHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Method(java.lang.reflect.Method) MethodDescriptor.ofMethod(io.quarkus.gizmo.MethodDescriptor.ofMethod) IgnoreProperty(io.quarkus.runtime.annotations.IgnoreProperty) SortedMap(java.util.SortedMap) Collection(java.util.Collection) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) SortedMap(java.util.SortedMap) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) AbstractMap(java.util.AbstractMap) TreeMap(java.util.TreeMap) SortedSet(java.util.SortedSet) Set(java.util.Set) NavigableSet(java.util.NavigableSet) TreeSet(java.util.TreeSet) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Field(java.lang.reflect.Field) ResultHandle(io.quarkus.gizmo.ResultHandle) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) Property(io.quarkus.deployment.recording.PropertyUtils.Property) IgnoreProperty(io.quarkus.runtime.annotations.IgnoreProperty) RecordableConstructor(io.quarkus.runtime.annotations.RecordableConstructor) MethodDescriptor.ofConstructor(io.quarkus.gizmo.MethodDescriptor.ofConstructor) Constructor(java.lang.reflect.Constructor) TreeMap(java.util.TreeMap) MalformedURLException(java.net.MalformedURLException) RecordableConstructor(io.quarkus.runtime.annotations.RecordableConstructor) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) MethodCreator(io.quarkus.gizmo.MethodCreator)

Example 2 with MethodDescriptor.ofMethod

use of io.quarkus.gizmo.MethodDescriptor.ofMethod in project quarkus by quarkusio.

the class JaxrsClientReactiveProcessor method createMultipartForm.

/*
     * Translate the class to be sent as multipart to Vertx Web MultipartForm.
     */
private ResultHandle createMultipartForm(MethodCreator methodCreator, ResultHandle methodParam, Type formClassType, IndexView index) {
    AssignableResultHandle multipartForm = methodCreator.createVariable(QuarkusMultipartForm.class);
    methodCreator.assign(multipartForm, methodCreator.newInstance(MethodDescriptor.ofConstructor(QuarkusMultipartForm.class)));
    ClassInfo formClass = index.getClassByName(formClassType.name());
    for (FieldInfo field : formClass.fields()) {
        // go field by field, ignore static fields and fail on non-public fields, only public fields are supported ATM
        if (Modifier.isStatic(field.flags())) {
            continue;
        }
        String fieldName = field.name();
        ResultHandle fieldValue = null;
        String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        for (MethodInfo method : formClass.methods()) {
            if (method.name().equals(getterName) && method.returnType().name().equals(field.type().name()) && method.parameters().isEmpty() && Modifier.isPublic(method.flags()) && !Modifier.isStatic(method.flags())) {
                fieldValue = methodCreator.invokeVirtualMethod(method, methodParam);
                break;
            }
        }
        if ((fieldValue == null) && Modifier.isPublic(field.flags())) {
            fieldValue = methodCreator.readInstanceField(field, methodParam);
        }
        if (fieldValue == null) {
            throw new IllegalArgumentException("Non-public field '" + fieldName + "' without a getter, found in a multipart form data class '" + formClassType.name() + "'. Rest Client Reactive only supports multipart form classes with fields that are public or have public getters.");
        }
        String formParamName = formParamName(field);
        String partType = formPartType(field);
        String partFilename = formPartFilename(field);
        Type fieldType = field.type();
        BytecodeCreator ifValueNotNull = methodCreator.ifNotNull(fieldValue).trueBranch();
        switch(fieldType.kind()) {
            case CLASS:
                // we support string, and send it as an attribute
                ClassInfo fieldClass = index.getClassByName(fieldType.name());
                if (DotNames.STRING.equals(fieldClass.name())) {
                    addString(ifValueNotNull, multipartForm, formParamName, partFilename, fieldValue);
                } else if (is(FILE, fieldClass, index)) {
                    // file is sent as file :)
                    if (partType == null) {
                        throw new IllegalArgumentException("No @PartType annotation found on multipart form field of type File: " + formClass.name() + "." + field.name());
                    }
                    ResultHandle filePath = ifValueNotNull.invokeVirtualMethod(MethodDescriptor.ofMethod(File.class, "toPath", Path.class), fieldValue);
                    addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, filePath);
                } else if (is(PATH, fieldClass, index)) {
                    // and so is path
                    if (partType == null) {
                        throw new IllegalArgumentException("No @PartType annotation found on multipart form field of type Path: " + formClass.name() + "." + field.name());
                    }
                    addFile(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue);
                } else if (is(BUFFER, fieldClass, index)) {
                    // and buffer
                    addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, fieldValue, field);
                } else {
                    // assume POJO:
                    addPojo(ifValueNotNull, multipartForm, formParamName, partType, fieldValue, field);
                }
                break;
            case ARRAY:
                // byte[] can be sent as file too
                Type componentType = fieldType.asArrayType().component();
                if (componentType.kind() != Type.Kind.PRIMITIVE || !byte.class.getName().equals(componentType.name().toString())) {
                    throw new IllegalArgumentException("Array of unsupported type: " + componentType.name() + " on " + formClassType.name() + "." + field.name());
                }
                ResultHandle buffer = ifValueNotNull.invokeStaticInterfaceMethod(MethodDescriptor.ofMethod(Buffer.class, "buffer", Buffer.class, byte[].class), fieldValue);
                addBuffer(ifValueNotNull, multipartForm, formParamName, partType, partFilename, buffer, field);
                break;
            case PRIMITIVE:
                // primitives are converted to text and sent as attribute
                ResultHandle string = primitiveToString(ifValueNotNull, fieldValue, field);
                addString(ifValueNotNull, multipartForm, formParamName, partFilename, string);
                break;
            case PARAMETERIZED_TYPE:
                ParameterizedType parameterizedType = fieldType.asParameterizedType();
                List<Type> args = parameterizedType.arguments();
                if (parameterizedType.name().equals(MULTI) && args.size() == 1 && args.get(0).name().equals(BYTE)) {
                    addMultiAsFile(ifValueNotNull, multipartForm, formParamName, partType, field, fieldValue);
                    break;
                }
                throw new IllegalArgumentException("Unsupported multipart form field type: " + parameterizedType + "<" + args.stream().map(a -> a.name().toString()).collect(Collectors.joining(",")) + "> in field class " + formClassType.name());
            case VOID:
            case TYPE_VARIABLE:
            case UNRESOLVED_TYPE_VARIABLE:
            case WILDCARD_TYPE:
                throw new IllegalArgumentException("Unsupported multipart form field type: " + fieldType + " in " + "field class " + formClassType.name());
        }
    }
    return multipartForm;
}
Also used : Buffer(io.vertx.core.buffer.Buffer) DotNames(io.quarkus.arc.processor.DotNames) GeneratedClassGizmoAdaptor(io.quarkus.deployment.GeneratedClassGizmoAdaptor) ParameterType(org.jboss.resteasy.reactive.common.model.ParameterType) JandexUtil(io.quarkus.deployment.util.JandexUtil) FormParamItem(org.jboss.resteasy.reactive.client.processor.beanparam.FormParamItem) FieldInfo(org.jboss.jandex.FieldInfo) ClientBeanParamInfo(org.jboss.resteasy.reactive.client.processor.beanparam.ClientBeanParamInfo) PathParamItem(org.jboss.resteasy.reactive.client.processor.beanparam.PathParamItem) GenericTypeMapping(org.jboss.resteasy.reactive.common.core.GenericTypeMapping) RecorderContext(io.quarkus.deployment.recording.RecorderContext) AdditionalReaderWriter(org.jboss.resteasy.reactive.common.processor.AdditionalReaderWriter) MetricsCapabilityBuildItem(io.quarkus.deployment.metrics.MetricsCapabilityBuildItem) MediaType(javax.ws.rs.core.MediaType) Map(java.util.Map) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) CONSUMES(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.CONSUMES) ParameterizedType(org.jboss.jandex.ParameterizedType) AdditionalReaders(org.jboss.resteasy.reactive.common.processor.AdditionalReaders) ClientResponseBuilderFactory(io.quarkus.jaxrs.client.reactive.runtime.ClientResponseBuilderFactory) Path(java.nio.file.Path) MethodParameter(org.jboss.resteasy.reactive.common.model.MethodParameter) AnnotationValue(org.jboss.jandex.AnnotationValue) AdditionalWriters(org.jboss.resteasy.reactive.common.processor.AdditionalWriters) ReflectiveHierarchyBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem) ClientEndpointIndexer(org.jboss.resteasy.reactive.client.processor.scanning.ClientEndpointIndexer) RuntimeType(javax.ws.rs.RuntimeType) ContentType(org.apache.http.entity.ContentType) Set(java.util.Set) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) ARRAY(org.jboss.jandex.Type.Kind.ARRAY) GenericType(javax.ws.rs.core.GenericType) ResteasyReactiveDotNames(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames) RestClientInterface(org.jboss.resteasy.reactive.common.model.RestClientInterface) CompletionStage(java.util.concurrent.CompletionStage) RxInvoker(javax.ws.rs.client.RxInvoker) PRIMITIVE(org.jboss.jandex.Type.Kind.PRIMITIVE) ConfigProvider(org.eclipse.microprofile.config.ConfigProvider) AnnotationInstance(org.jboss.jandex.AnnotationInstance) QueryParamItem(org.jboss.resteasy.reactive.client.processor.beanparam.QueryParamItem) ProcessingException(javax.ws.rs.ProcessingException) ResultHandle(io.quarkus.gizmo.ResultHandle) TryBlock(io.quarkus.gizmo.TryBlock) Record(io.quarkus.deployment.annotations.Record) CatchBlockCreator(io.quarkus.gizmo.CatchBlockCreator) PART_TYPE_NAME(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.PART_TYPE_NAME) DotName(org.jboss.jandex.DotName) RestClientBase(io.quarkus.jaxrs.client.reactive.runtime.RestClientBase) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) AsyncInvoker(javax.ws.rs.client.AsyncInvoker) QuarkusFactoryCreator(io.quarkus.resteasy.reactive.common.deployment.QuarkusFactoryCreator) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) ResteasyReactiveConfig(io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveConfig) BeanArchiveIndexBuildItem(io.quarkus.arc.deployment.BeanArchiveIndexBuildItem) QuarkusResteasyReactiveDotNames(io.quarkus.resteasy.reactive.common.deployment.QuarkusResteasyReactiveDotNames) LinkedHashSet(java.util.LinkedHashSet) FieldFiller(org.jboss.resteasy.reactive.client.spi.FieldFiller) CLASS(org.jboss.jandex.Type.Kind.CLASS) PARAMETERIZED_TYPE(org.jboss.jandex.Type.Kind.PARAMETERIZED_TYPE) FORM_PARAM(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.FORM_PARAM) BranchResult(io.quarkus.gizmo.BranchResult) ClientObservabilityHandler(org.jboss.resteasy.reactive.client.handlers.ClientObservabilityHandler) CompletionStageRxInvoker(javax.ws.rs.client.CompletionStageRxInvoker) PrimitiveType(org.jboss.jandex.PrimitiveType) Serialisers(org.jboss.resteasy.reactive.common.core.Serialisers) ResourceReader(org.jboss.resteasy.reactive.common.model.ResourceReader) FieldDescriptor(io.quarkus.gizmo.FieldDescriptor) EndpointIndexer.extractProducesConsumesValues(org.jboss.resteasy.reactive.common.processor.EndpointIndexer.extractProducesConsumesValues) File(java.io.File) GeneratedClassBuildItem(io.quarkus.deployment.builditem.GeneratedClassBuildItem) Item(org.jboss.resteasy.reactive.client.processor.beanparam.Item) BytecodeTransformerBuildItem(io.quarkus.deployment.builditem.BytecodeTransformerBuildItem) ToObjectArray(io.quarkus.jaxrs.client.reactive.runtime.ToObjectArray) ResponseBuilderFactory(org.jboss.resteasy.reactive.common.core.ResponseBuilderFactory) HashUtil(org.jboss.resteasy.reactive.common.processor.HashUtil) ResourceWriter(org.jboss.resteasy.reactive.common.model.ResourceWriter) ApplicationIndexBuildItem(io.quarkus.deployment.builditem.ApplicationIndexBuildItem) BeanParamItem(org.jboss.resteasy.reactive.client.processor.beanparam.BeanParamItem) HeaderParamItem(org.jboss.resteasy.reactive.client.processor.beanparam.HeaderParamItem) BiFunction(java.util.function.BiFunction) ResourceScanningResult(org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult) REST_FORM_PARAM(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.REST_FORM_PARAM) MultipartResponseDataBase(io.quarkus.jaxrs.client.reactive.runtime.impl.MultipartResponseDataBase) ClassInfo(org.jboss.jandex.ClassInfo) CombinedIndexBuildItem(io.quarkus.deployment.builditem.CombinedIndexBuildItem) BuildProducer(io.quarkus.deployment.annotations.BuildProducer) ParamConverter(javax.ws.rs.ext.ParamConverter) Capabilities(io.quarkus.deployment.Capabilities) MethodInfo(org.jboss.jandex.MethodInfo) Types(io.quarkus.arc.processor.Types) MULTI(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.MULTI) MultipartResponseData(org.jboss.resteasy.reactive.client.spi.MultipartResponseData) Locale(java.util.Locale) ParamConverterProvider(javax.ws.rs.ext.ParamConverterProvider) AnnotationTarget(org.jboss.jandex.AnnotationTarget) MaybeRestClientInterface(org.jboss.resteasy.reactive.common.model.MaybeRestClientInterface) Collection(java.util.Collection) ExecutionTime(io.quarkus.deployment.annotations.ExecutionTime) ResourceScanningResultBuildItem(io.quarkus.resteasy.reactive.common.deployment.ResourceScanningResultBuildItem) Invocation(javax.ws.rs.client.Invocation) Config(org.eclipse.microprofile.config.Config) Collectors(java.util.stream.Collectors) Entity(javax.ws.rs.client.Entity) WebTargetImpl(org.jboss.resteasy.reactive.client.impl.WebTargetImpl) MethodDescriptors(io.quarkus.arc.processor.MethodDescriptors) List(java.util.List) AsyncInvokerImpl(org.jboss.resteasy.reactive.client.impl.AsyncInvokerImpl) Buffer(io.vertx.core.buffer.Buffer) Modifier(java.lang.reflect.Modifier) BeanContainerBuildItem(io.quarkus.arc.deployment.BeanContainerBuildItem) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) ServiceProviderBuildItem(io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem) AbstractRxInvoker(org.jboss.resteasy.reactive.client.impl.AbstractRxInvoker) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem) Logger(org.jboss.logging.Logger) MethodCreator(io.quarkus.gizmo.MethodCreator) Type(org.jboss.jandex.Type) HashMap(java.util.HashMap) MethodDescriptor.ofMethod(io.quarkus.gizmo.MethodDescriptor.ofMethod) CookieParamItem(org.jboss.resteasy.reactive.client.processor.beanparam.CookieParamItem) ResourceMethod(org.jboss.resteasy.reactive.common.model.ResourceMethod) ClassCreator(io.quarkus.gizmo.ClassCreator) Multi(io.smallrye.mutiny.Multi) MetricsFactory(io.quarkus.runtime.metrics.MetricsFactory) Uni(io.smallrye.mutiny.Uni) HashSet(java.util.HashSet) ClientBuilderImpl(org.jboss.resteasy.reactive.client.impl.ClientBuilderImpl) QuarkusMultipartForm(org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartForm) ClientBuilder(javax.ws.rs.client.ClientBuilder) BuildStep(io.quarkus.deployment.annotations.BuildStep) MessageBodyReaderOverrideBuildItem(io.quarkus.resteasy.reactive.spi.MessageBodyReaderOverrideBuildItem) RuntimeValue(io.quarkus.runtime.RuntimeValue) ApplicationResultBuildItem(io.quarkus.resteasy.reactive.common.deployment.ApplicationResultBuildItem) ClientRestHandler(org.jboss.resteasy.reactive.client.spi.ClientRestHandler) EndpointIndexer(org.jboss.resteasy.reactive.common.processor.EndpointIndexer) MessageBodyWriterOverrideBuildItem(io.quarkus.resteasy.reactive.spi.MessageBodyWriterOverrideBuildItem) JaxrsClientReactiveRecorder(io.quarkus.jaxrs.client.reactive.runtime.JaxrsClientReactiveRecorder) MessageBodyWriterBuildItem(io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem) IndexView(org.jboss.jandex.IndexView) RuntimeInitializedClassBuildItem(io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem) MethodDescriptor(io.quarkus.gizmo.MethodDescriptor) Iterator(java.util.Iterator) OBJECT(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.OBJECT) UniInvoker(org.jboss.resteasy.reactive.client.impl.UniInvoker) COMPLETION_STAGE(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.COMPLETION_STAGE) Capability(io.quarkus.deployment.Capability) MessageBodyReaderBuildItem(io.quarkus.resteasy.reactive.spi.MessageBodyReaderBuildItem) MultivaluedHashMap(javax.ws.rs.core.MultivaluedHashMap) MultiInvoker(org.jboss.resteasy.reactive.client.impl.MultiInvoker) Consumer(java.util.function.Consumer) MultivaluedMap(javax.ws.rs.core.MultivaluedMap) AbstractMap(java.util.AbstractMap) FileDownload(org.jboss.resteasy.reactive.multipart.FileDownload) SerializersUtil(io.quarkus.resteasy.reactive.common.deployment.SerializersUtil) Closeable(java.io.Closeable) WebTarget(javax.ws.rs.client.WebTarget) Comparator(java.util.Comparator) Collections(java.util.Collections) ClientImpl(org.jboss.resteasy.reactive.client.impl.ClientImpl) UNI(org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.UNI) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) ParameterizedType(org.jboss.jandex.ParameterizedType) ParameterType(org.jboss.resteasy.reactive.common.model.ParameterType) MediaType(javax.ws.rs.core.MediaType) ParameterizedType(org.jboss.jandex.ParameterizedType) RuntimeType(javax.ws.rs.RuntimeType) ContentType(org.apache.http.entity.ContentType) GenericType(javax.ws.rs.core.GenericType) PrimitiveType(org.jboss.jandex.PrimitiveType) Type(org.jboss.jandex.Type) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) ResultHandle(io.quarkus.gizmo.ResultHandle) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) MethodInfo(org.jboss.jandex.MethodInfo) FieldInfo(org.jboss.jandex.FieldInfo) ClassInfo(org.jboss.jandex.ClassInfo)

Aggregations

QuarkusClassLoader (io.quarkus.bootstrap.classloading.QuarkusClassLoader)2 AssignableResultHandle (io.quarkus.gizmo.AssignableResultHandle)2 BytecodeCreator (io.quarkus.gizmo.BytecodeCreator)2 CatchBlockCreator (io.quarkus.gizmo.CatchBlockCreator)2 ClassCreator (io.quarkus.gizmo.ClassCreator)2 FieldDescriptor (io.quarkus.gizmo.FieldDescriptor)2 MethodCreator (io.quarkus.gizmo.MethodCreator)2 MethodDescriptor (io.quarkus.gizmo.MethodDescriptor)2 MethodDescriptor.ofMethod (io.quarkus.gizmo.MethodDescriptor.ofMethod)2 ResultHandle (io.quarkus.gizmo.ResultHandle)2 TryBlock (io.quarkus.gizmo.TryBlock)2 BeanArchiveIndexBuildItem (io.quarkus.arc.deployment.BeanArchiveIndexBuildItem)1 BeanContainerBuildItem (io.quarkus.arc.deployment.BeanContainerBuildItem)1 DotNames (io.quarkus.arc.processor.DotNames)1 MethodDescriptors (io.quarkus.arc.processor.MethodDescriptors)1 Types (io.quarkus.arc.processor.Types)1 Capabilities (io.quarkus.deployment.Capabilities)1 Capability (io.quarkus.deployment.Capability)1 GeneratedClassGizmoAdaptor (io.quarkus.deployment.GeneratedClassGizmoAdaptor)1 BuildProducer (io.quarkus.deployment.annotations.BuildProducer)1