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

io.virtualan.core.util.OpenApiGeneratorUtil Maven / Gradle / Ivy

package io.virtualan.core.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.virtualan.annotation.VirtualService;
import io.virtualan.autoconfig.ApplicationContextProvider;
import io.virtualan.core.VirtualServiceInfo;
import io.virtualan.core.VirtualServiceUtil;
import io.virtualan.core.model.VirtualServiceRequest;
import java.beans.IntrospectionException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.apache.commons.io.FileUtils;
import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.languages.SpringCodegen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo.Builder;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * The type Open api generator util.
 */
@Component
public class OpenApiGeneratorUtil {


  @Value("${virtualan.script.enabled:false}")
  private boolean scriptEnabled;

  private static Logger logger = LoggerFactory.getLogger(OpenApiGeneratorUtil.class);
  private File srcFolder = VirtualanConfiguration.getSrcPath();
  private File dependencyFolder = VirtualanConfiguration.getDependencyPath();
  private File yamlFolder = VirtualanConfiguration.getYamlPath();
  @Autowired
  private ApplicationContextProvider applicationContext;

  @Autowired
  private ApplicationContext  appContext;

  @Autowired
  private VirtualServiceUtil virtualServiceUtil;
  @Autowired
  private RequestMappingHandlerMapping requestMappingHandlerMapping;

  private static void deleteFolder(File file) {
    if (file != null && file.listFiles().length > 0) {
      for (File subFile : file.listFiles()) {
        if (subFile.isDirectory()) {
          deleteFolder(subFile);
          subFile.delete();
        } else {
          subFile.delete();
        }
      }
    }
  }

  /**
   * Load initial yaml files.
   */
  @EventListener(ApplicationReadyEvent.class)
  public void loadInitialYamlFiles()
          throws MalformedURLException, IntrospectionException {
    //Helper.addURLToClassLoader(VirtualanConfiguration.getPath().toURI().toURL(), applicationContext.getClassLoader());
    File file = VirtualanConfiguration.getYamlPath();
    getYaml( file);
  }

  /**
   * Gets yaml.
   *
   * @param file the file
   */
  public void getYaml( File file) {
    if (file != null && file.listFiles() != null && file.listFiles().length > 0) {
      for (File subFile : file.listFiles()) {
        if (subFile.isDirectory()) {
          getYaml(subFile);
        } else {
          try {
            generateRestApi(scriptEnabled, subFile.getName(), null, appContext.getClassLoader());
          }catch (Exception e){
            logger.warn("Unable to process : " + e.getMessage());
          }
        }
      }
    }
  }

  private void compile(File src, File dest) throws IOException {
    StringBuilder sb = new StringBuilder();
    File[] listOfFiles = dependencyFolder.listFiles();
    for (File file : listOfFiles) {
      if (file.isFile()) {
        sb.append(file.getAbsoluteFile()).append(":");
      }
    }
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    DiagnosticCollector diagnostics = new DiagnosticCollector<>();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null,
        StandardCharsets.UTF_8);
    Iterable fileList = Arrays.asList(dest);
    fileManager.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(listOfFiles));

    fileManager.setLocation(StandardLocation.CLASS_OUTPUT, fileList);
    Collection allFiles = FileUtils.listFiles(src, new String[]{"java"}, true);
    Iterable compilationUnits = fileManager
        .getJavaFileObjectsFromFiles(allFiles);
    JavaCompiler.CompilationTask task = compiler
        .getTask(null, fileManager, diagnostics, null, null, compilationUnits);
    boolean status = task.call();
    if (!status) {
      /*Iterate through each compilation problem and print it*/
      for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
        logger.warn("Error on line {} in {}" + diagnostic.getLineNumber() + diagnostic);
      }
    }
    fileManager.close();
    return;
  }

  private void collectFiles(File file, List list, String fileName) {
    if (file.isDirectory()) {
      for (File child : file.listFiles()) {
        collectFiles(child, list, fileName);
      }
    } else {
      if (file.getName().endsWith(fileName)) {
        list.add(file.getAbsolutePath());
      }
    }
  }

  private void openApiGenerator(String apiFile, VirtualServiceRequest request) throws IOException {
    OpenAPI openAPI = null;
    String fileName = null;
    if (apiFile != null) {
      fileName = apiFile.substring(0, apiFile.lastIndexOf("."));
      openAPI = new OpenAPIParser()
          .readLocation(
              yamlFolder.getAbsolutePath() + File.separator + fileName + File.separator + apiFile,
              null,
              new ParseOptions()).getOpenAPI();
     } else {
      openAPI = OpenApiGenerator.generateAPI(request);
      fileName = OpenApiGenerator.getResource(request);
    }
    File newFile = new File(srcFolder.getAbsolutePath() + File.separator + fileName);
    if (!newFile.exists()) {
      newFile.mkdir();
    } else {
      deleteFolder(newFile);
    }
    SpringCodegen codegen = new SpringCodegen();
    codegen.setOutputDir(newFile.getAbsolutePath());
    ClientOptInput input = new ClientOptInput();
    DefaultGenerator generator = new DefaultGenerator();
    generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
    generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
    generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
    generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
    generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true");
    Random rand = new Random(); //instance of random class
    int upperbound = 5000;
    int int_random = rand.nextInt(upperbound);
    codegen.setApiPackage("io.virtualan.api." + fileName + int_random);
    codegen.setModelPackage("io.virtualan.model." + fileName + int_random);
    codegen.additionalProperties().put(SpringCodegen.VIRTUAL_SERVICE, true);
    codegen.additionalProperties().put(SpringCodegen.JAVA_8, true);
    codegen.additionalProperties().put(SpringCodegen.SKIP_DEFAULT_INTERFACE, false);
    GlobalSettings.setProperty(CodegenConstants.SUPPORTING_FILES, "ApiUtil.java");
    input.openAPI(openAPI);
    input.config(codegen);
    generator.opts(input).generate();
  }

  /**
   * Remove rest api map.
   *
   * @param scriptEnabled the script enabled
   * @param yamlFile      the yaml file
   * @param request       the request
   * @param contextLoader the context loader
   * @return the map
   * @throws ClassNotFoundException the class not found exception
   * @throws InstantiationException the instantiation exception
   * @throws IllegalAccessException the illegal access exception
   * @throws IOException            the io exception
   * @throws IntrospectionException the introspection exception
   */
  public  Map> removeRestApi(boolean scriptEnabled, String yamlFile, VirtualServiceRequest request, ClassLoader contextLoader)
          throws Exception {
    List beans = new ArrayList<>();
    try {
      List fileNames = new ArrayList();
      List fileNameAll = new ArrayList();
      File destFile = null;
      destFile = new File(VirtualanConfiguration.getDestPath().getAbsolutePath() + File.separator + yamlFile);
      MyClassloader myClassloader = new MyClassloader(new java.net.URL[]{destFile.toURI().toURL()}, applicationContext.getClassLoader().getParent());
      applicationContext.classLoader(myClassloader);
      if (destFile.exists()) {
        collectFiles(destFile, fileNameAll, ".class");
        collectFiles(VirtualanConfiguration.getDestPath(), fileNames, ".class");
        for (String classNameRaw : fileNames) {
          String className = classNameRaw.replace(VirtualanConfiguration.getDestPath().getAbsolutePath(), "");
          className = className.substring(className.indexOf(File.separator, 1) + 1);
          className = className.replaceAll("/", ".");
          className = className.replaceAll("\\\\", ".");
          className = className.substring(0, className.lastIndexOf("."));
          String name = className;
          name = name.substring(name.lastIndexOf('.') + 1);
          name = name.substring(0, 1).toLowerCase() + name.substring(1);
          Class myController = applicationContext.getClassLoader().loadClass(className);
          if (fileNameAll.contains(classNameRaw) && name.endsWith("Controller")) {
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(myController);
            beanDefinition.setAutowireCandidate(true);
            beanDefinition.setLazyInit(false);
            beanDefinition.setAbstract(false);
            for (Class classssss : myController.getInterfaces()) {
              if (classssss.isAnnotationPresent(VirtualService.class)) {
                if (applicationContext.containsBean(name)) {
                  removeMapping(classssss);
                  applicationContext.removeBean(name);
                  beans.add(name);
                }
              }
            }
          }
        }
        deleteFolder(destFile);
        destFile.delete();
        File srcFile = new File(VirtualanConfiguration.getSrcPath().getAbsolutePath() + File.separator + yamlFile);
        deleteFolder(srcFile);
        srcFile.delete();
        File yaml = new File(VirtualanConfiguration.getYamlPath().getAbsolutePath() + File.separator + yamlFile);
        deleteFolder(yaml);
        yaml.delete();
        return getActiveServices(scriptEnabled, beans);
      } else {
        throw new Exception("This resource does not exists");
      }
    } catch (Exception e) {
      logger.warn("Unable to process :" + e.getMessage());
      getActiveServices(scriptEnabled, beans);
      if(e.getMessage() != null) {
        throw new Exception(e.getMessage());
      } else {
        throw new Exception("Unexpected error Check the logs");
      }
    }
  }

  private Map> getActiveServices(boolean scriptEnabled, List list) throws ClassNotFoundException, JsonProcessingException, InstantiationException, IllegalAccessException {
    Map> map = getVirtualServiceInfo().loadVirtualServices(scriptEnabled, applicationContext.getClassLoader().getParent());
    getVirtualServiceInfo().setResourceParent(getVirtualServiceInfo().loadMapper());
    Map> response = new HashMap<>();
    for(String beans : list){
      String bean  = beans.replace("ApiController", "");
      if(map.containsKey(bean)){
        response.put(bean, map.get(bean));
      }
    }
    map.entrySet().removeIf(entry -> !applicationContext.containsBean(entry.getKey()+"ApiController"));
    return response;
  }

  private Resource[] getCatalogList(String path) throws IOException {
    final PathMatchingResourcePatternResolver resolver =
            new PathMatchingResourcePatternResolver(applicationContext.getClassLoader().getParent().getParent());
    return resolver.getResources(path);
  }

//  /**
//   * Add url to class loader.
//   *
//   * @param url         the url
//   * @param classLoader the class loader
//   * @throws IntrospectionException the introspection exception
//   */
//  public void addURLToClassLoader(URL url, ClassLoader classLoader) throws IntrospectionException {
//    URLClassLoader systemClassLoader = null;
//    if(classLoader instanceof  VirtualanClassLoader) {
//      systemClassLoader
//              = new URLClassLoader(new URL[]{url}, classLoader);
//    } else  {
//      systemClassLoader = (URLClassLoader) classLoader;
//    }    Class classLoaderClass = URLClassLoader.class;
//    try {
//      Method method = classLoaderClass.getDeclaredMethod("addURL", new Class[]{URL.class});
//      method.setAccessible(true);
//      method.invoke(systemClassLoader, new Object[]{url});
//    } catch (Throwable t) {
//      throw new IntrospectionException("Error when adding url to system ClassLoader ");
//    }
//  }

  /**
   * Generate rest api map.
   *
   * @param scriptEnabled the script enabled
   * @param yamlFile      the yaml file
   * @param request       the request
   * @param contextLoader the context loader
   * @return the map
   * @throws ClassNotFoundException  the class not found exception
   * @throws InstantiationException  the instantiation exception
   * @throws IllegalAccessException  the illegal access exception
   * @throws JsonProcessingException the json processing exception
   */
  public Map> generateRestApi(boolean scriptEnabled, String yamlFile, VirtualServiceRequest request, ClassLoader contextLoader)
          throws ClassNotFoundException, InstantiationException, IllegalAccessException, JsonProcessingException {
    List addedBeans = new ArrayList();
    try {
      openApiGenerator(yamlFile, request);
      List fileNames = new ArrayList();
      List fileNameAll = new ArrayList();
      File destFile = null;
      File srcFile = null;
      if (yamlFile != null) {
        destFile = new File(VirtualanConfiguration.getDestPath().getAbsolutePath() + File.separator + yamlFile
                .substring(0, yamlFile.lastIndexOf(".")));
        srcFile = new File(VirtualanConfiguration.getSrcPath().getAbsolutePath() + File.separator + yamlFile
                .substring(0, yamlFile.lastIndexOf(".")));
      } else {
        destFile = new File(
                VirtualanConfiguration.getDestPath().getAbsolutePath() + File.separator + OpenApiGenerator.getResource(request));
        srcFile = new File(
                VirtualanConfiguration.getSrcPath().getAbsolutePath() + File.separator + OpenApiGenerator.getResource(request));
      }
      if (!destFile.exists()) {
        destFile.mkdir();
      } else {
        deleteFolder(destFile);
      }
      compile(srcFile, destFile);
      collectFiles(destFile, fileNameAll, ".class");
      collectFiles(destFile, fileNames, ".class");
       MyClassloader myClassloader = new MyClassloader(new java.net.URL[]{destFile.toURI().toURL()}, applicationContext.getClassLoader().getParent());
       applicationContext.classLoader(myClassloader);
       for (String classNameRaw : fileNames) {
        String className = classNameRaw.replace(VirtualanConfiguration.getDestPath().getAbsolutePath(), "");
        className = className.substring(className.indexOf(File.separator, 1) + 1);
        className = className.replaceAll("/", ".");
        className = className.replaceAll("\\\\", ".");
        className = className.substring(0, className.lastIndexOf("."));
        String name = className;
        name = name.substring(name.lastIndexOf('.') + 1);
        name = name.substring(0, 1).toLowerCase() + name.substring(1);
        Class myController = applicationContext.getClassLoader().loadClass(className);
        if (fileNameAll.contains(classNameRaw) && name.endsWith("Controller")) {
          GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
          beanDefinition.setBeanClass(myController);
          beanDefinition.setAutowireCandidate(true);
          beanDefinition.setLazyInit(false);
          beanDefinition.setAbstract(false);
          for (Class classssss : myController.getInterfaces()) {
            if (classssss.isAnnotationPresent(VirtualService.class)) {
              if (applicationContext.containsBean(name)) {
                removeMapping(classssss);
                applicationContext.removeBean(name);
              }
              applicationContext.addBean(name, beanDefinition);
              addMapping(name, classssss);
              addedBeans.add(name);
            }
          }
        }
      }

    } catch (Exception e) {
      logger.warn("Unable to process :" + e.getMessage());
    }
    return getActiveServices(scriptEnabled, addedBeans);
  }


  private void removeMapping(Class myControllerClzzz) throws IOException, ClassNotFoundException {
    for (Method method : myControllerClzzz.getDeclaredMethods()) {
      boolean isPost = method.isAnnotationPresent(PostMapping.class);
      if (isPost) {
        PostMapping annotation = method.getAnnotation(PostMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.POST);
        if (annotation.consumes().length > 0) {
          requestMappingInfo = requestMappingInfo.consumes(annotation.consumes()[0]);
        }

        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        requestMappingHandlerMapping.unregisterMapping(requestMappingInfo.build());
        logger.info("Unregistered: " + requestMappingInfo.build());
      }
      boolean isGet = method.isAnnotationPresent(GetMapping.class);
      if (isGet) {
        GetMapping annotation = method.getAnnotation(GetMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.GET);
        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        requestMappingHandlerMapping.unregisterMapping(requestMappingInfo.build());
        logger.info("Unregistered: " + requestMappingInfo.build());
      }

      boolean isPut = method.isAnnotationPresent(PutMapping.class);
      if (isPut) {
        PutMapping annotation = method.getAnnotation(PutMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.PUT);
        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        if (annotation.consumes().length > 0) {
          requestMappingInfo = requestMappingInfo.consumes(annotation.consumes()[0]);
        }
        requestMappingHandlerMapping.unregisterMapping(requestMappingInfo.build());
        logger.info("Unregistered: " + requestMappingInfo.build());
      }

      boolean isDelete = method.isAnnotationPresent(DeleteMapping.class);
      if (isDelete) {
        DeleteMapping annotation = method.getAnnotation(DeleteMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.DELETE);
        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        requestMappingHandlerMapping.unregisterMapping(requestMappingInfo.build());
        logger.info("Unregistered: " + requestMappingInfo.build());
      }
    }
  }


  private void addMapping(String name, Class myControllerClzzz)
      throws IOException, ClassNotFoundException {
    for (Method method : myControllerClzzz.getDeclaredMethods()) {
      boolean isPost = method.isAnnotationPresent(PostMapping.class);
      if (isPost) {
        PostMapping annotation = method.getAnnotation(PostMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.POST);
        if (annotation.consumes().length > 0) {
          requestMappingInfo = requestMappingInfo.consumes(annotation.consumes()[0]);
        }

        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        requestMappingHandlerMapping.registerMapping(requestMappingInfo.build(), name, method);
        logger.info("Registered: " + requestMappingInfo.build().toString());
      }
      boolean isGet = method.isAnnotationPresent(GetMapping.class);
      if (isGet) {
        GetMapping annotation = method.getAnnotation(GetMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.GET);
        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        requestMappingHandlerMapping.registerMapping(requestMappingInfo.build(), name, method);
        logger.info("Registered: " + requestMappingInfo.build().toString());
      }

      boolean isPut = method.isAnnotationPresent(PutMapping.class);
      if (isPut) {
        PutMapping annotation = method.getAnnotation(PutMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.PUT);
        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        if (annotation.consumes().length > 0) {
          requestMappingInfo = requestMappingInfo.consumes(annotation.consumes()[0]);
        }
        requestMappingHandlerMapping.registerMapping(requestMappingInfo.build(), name, method);
        logger.info("Registered: " + requestMappingInfo.build().toString());
      }

      boolean isDelete = method.isAnnotationPresent(DeleteMapping.class);
      if (isDelete) {
        DeleteMapping annotation = method.getAnnotation(DeleteMapping.class);
        Builder requestMappingInfo = RequestMappingInfo
            .paths(annotation.value()[0])
            .methods(RequestMethod.DELETE);
        if (annotation.produces().length > 0) {
          requestMappingInfo = requestMappingInfo.produces(annotation.produces()[0]);
        }
        requestMappingHandlerMapping.registerMapping(requestMappingInfo.build(), name, method);
        logger.info("Registered: " + requestMappingInfo.build().toString());
      }
    }
  }

  private String getName(Class classzz) {
    String interfaceName = classzz.getTypeName();
    interfaceName = interfaceName.substring(interfaceName.lastIndexOf('.') + 1);
    interfaceName = interfaceName.toLowerCase();
    return interfaceName;
  }

  /**
   * Gets virtual service info.
   *
   * @return the virtual service info
   */
  private VirtualServiceInfo getVirtualServiceInfo() {
    return virtualServiceUtil.getVirtualServiceInfo();
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy