All Downloads are FREE. Search and download functionalities are using the official Maven repository.

top.osjf.sdk.http.HttpSdkSupport Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright 2024-? the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package top.osjf.sdk.http;

import top.osjf.sdk.core.process.Request;
import top.osjf.sdk.core.process.Response;
import top.osjf.sdk.core.support.SdkSupport;
import top.osjf.sdk.core.util.ArrayUtils;
import top.osjf.sdk.core.util.MapUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Support classes for http SDK.
 *
 * @author zhangpengfei
 * @since 1.0.0
 */
public abstract class HttpSdkSupport extends SdkSupport {

    /*** content type name */
    protected static final String named = "Content-Type";

    /*** default Json content type */
    protected static final String default_content_type = "application/json";

    /***The left angle bracket included in the generic.*/
    protected static final String LEFT_ANGLE_BRACKET = "<";

    /***The right angle bracket included in the generic.*/
    protected static final String RIGHT_ANGLE_BRACKET = ">";

    /*** cache for dynamically obtain the type of response class.*/
    protected static final Map, Object> rps_classes = new ConcurrentHashMap<>(16);

    /**
     * Check if the existing request headers contain {@link #named}, and
     * if no default JSON is provided.
     *
     * @param headers existing request headers.
     */
    public static void checkContentType(Map headers) {
        if (MapUtils.isEmpty(headers)) {
            return;
        }
        //if no Content-Type
        if (!headers.containsKey(named)) {
            //default to JSON Content-Type
            headers.put(named, default_content_type);
        }
    }

    /**
     * Retrieves the required response type for a given request.
     *
     * 

This method determines and returns the required response type based on the * passed request object and its inheritance hierarchy. * It first attempts to retrieve the type from a cache, and if not found, traverses * the class hierarchy (including interfaces and parent classes) * of the request object to find a matching response type. If a matching generic type * is found, it is returned; otherwise, * if no matching type is found, the default type is returned.

* * @param request The request object used to determine the required response type. * @param def The default type to return if no matching response type is found. * @return The required response type for the request, or the default type if none is found. * @see Class#getGenericInterfaces() * @see Class#getGenericSuperclass() */ public static Object getResponseRequiredType(Request request, Object def) { if (request == null) { return null; } //Loop stop marker. boolean goWhile = true; //The final return type. Object type = null; //The class object of the current request class. final Class inletClass = request.getClass(); //Try reading the cache first. Object inlet = rps_classes.get(inletClass); if (inlet != null) { return inlet; } //Cache temporary type class objects. Map> type_classes = new ConcurrentHashMap<>(16); //There is no cache to execute the fetch logic. Class clazz = inletClass; while (goWhile) { //Collect the type of the class for visual objects. //Including interfaces and classes. ClassLoader classLoader = clazz.getClassLoader(); List types = new ArrayList<>(); Type genericSuperclass = clazz.getGenericSuperclass(); if (genericSuperclass != null) { types.add(genericSuperclass); } Type[] genericInterfaces = clazz.getGenericInterfaces(); if (ArrayUtils.isNotEmpty(genericInterfaces)) { types.addAll(Arrays.asList(genericInterfaces)); } //Filter the main class of the request main class, taking the first one. Type typeFilter = types.stream().filter(linkType -> { Class typeClass = getTypeClass(linkType, classLoader); if (typeClass == null) { return false; } //Cache the class objects of the current classes for future use type_classes.putIfAbsent(linkType, typeClass); return request.isAssignableRequest(typeClass); }).findFirst().orElse(null); //If there is nothing available, simply exit the loop. if (typeFilter == null) { goWhile = false; continue; } //If it is a type that carries a generic, then obtain whether // it contains a generic that responds to the main class and obtain it. if (typeFilter instanceof ParameterizedType) { Object arguments = getActualResponseTypeArguments(typeFilter, clazz.getClassLoader()); if (arguments != null) { type = arguments; //After obtaining it, bind the response generic of the // main class and use it for subsequent caching. rps_classes.putIfAbsent(inletClass, type); goWhile = false; } else { //The paradigm does not carry response generics, and the class // object logic for filtering types is executed. // According to the specification of the idea, it will not go this far. clazz = type_classes.get(typeFilter); } } else { //Non generic classes, directly use class objects of filter types for subsequent logic. clazz = type_classes.get(typeFilter); } } //If the type is empty, cache a default type. if (type == null) { rps_classes.put(inletClass, def); } return rps_classes.get(inletClass); } private static Object getActualResponseTypeArguments(Type type, ClassLoader classLoader) { Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); // as Response and retrieve the first one. return Arrays.stream(actualTypeArguments).filter(argType -> { Class typeClass = getTypeClass(argType, classLoader); return typeClass != null && Response.class.isAssignableFrom(typeClass); }) .findFirst() .orElse(null); } private static Class getTypeClass(Type type, ClassLoader classLoader) { String typeName = type.getTypeName(); String className = typeName; //A generic type name that carries<...>, After removing such content, // the original name of the class is obtained. if (typeName.contains(LEFT_ANGLE_BRACKET) && typeName.contains(RIGHT_ANGLE_BRACKET)) { className = typeName.split(LEFT_ANGLE_BRACKET)[0]; } Class classObj; try { classObj = Class.forName(className, false, classLoader); } catch (ClassNotFoundException e) { classObj = null; } return classObj; } public static Object[] toLoggerArray(Object... args) { return args; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy