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

io.github.jhipster.loaded.JHipsterReloaderThread Maven / Gradle / Ivy

package io.github.jhipster.loaded;

import io.github.jhipster.loaded.reloader.Reloader;
import io.github.jhipster.loaded.reloader.type.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.data.mongodb.core.mapping.Document;

import javax.persistence.Entity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * This thread stores classes to reload, to reload them all in one batch.
 */
public class JHipsterReloaderThread implements Runnable {

    private static final boolean jpaPresent =
            ClassUtils.isPresent("javax.persistence.Entity", JHipsterReloaderThread.class.getClassLoader());

    private static final boolean mongoDBPresent =
            ClassUtils.isPresent("com.mongodb.Mongo", JHipsterReloaderThread.class.getClassLoader());

    private static Logger log = LoggerFactory.getLogger(JHipsterReloaderThread.class);

    private static final Object lock = new Object();

    public static boolean isStarted;

    private static boolean hotReloadTriggered = false;

    private static boolean isWaitingForNewClasses = false;

    private String domainPackageName;
    private String dtoPackageName;

    /**
     * How long does the thread wait until running a new batch.
     */
    private static final int BATCH_DELAY = 250;

    /**
     * The list of reloaders called when a spring class has been compiled
     * and needs to be reloaded
     */
    private Collection reloaders;

    /**
     * Stores the Spring controllers reloaded in the batch.
     */
    private List controllers = new ArrayList<>();

    /**
     * Stores the Spring services reloaded in the batch.
     */
    private List services = new ArrayList<>();

    /**
     * Stores the Spring repositories reloaded in the batch.
     */
    private List repositories = new ArrayList<>();

    /**
     * Stores the Spring components reloaded in the batch.
     */
    private List components = new ArrayList<>();

    /**
     * Stores the JPA entities reloaded in the batch.
     */
    private List entities = new ArrayList<>();

    /**
     * Stores the DTOs reloaded in the batch.
     */
    private List dtos = new ArrayList<>();

    public JHipsterReloaderThread(ConfigurableApplicationContext applicationContext, Collection reloaders) {
        this.reloaders = reloaders;
        domainPackageName = applicationContext.getEnvironment().getProperty("hotReload.package.domain");
        dtoPackageName = applicationContext.getEnvironment().getProperty("hotReload.package.restdto");
        isStarted = true;
    }

    public void reloadEvent(String typename, Class clazz) {
        synchronized (lock) {
            log.trace("Hot reloading - checking if this is a Spring bean: {}", typename);

            boolean startReloading = false;
            if (AnnotationUtils.findAnnotation(clazz, Repository.class) != null ||
                    AnnotationUtils.findAnnotation(clazz, NoRepositoryBean.class) != null ||
                    ClassUtils.isAssignable(clazz, org.springframework.data.repository.Repository.class)) {
                log.trace("{} is a Spring Repository", typename);
                repositories.add(clazz);
                startReloading = true;
            } else if (AnnotationUtils.findAnnotation(clazz, Service.class) != null) {
                log.trace("{} is a Spring Service", typename);
                services.add(clazz);
                startReloading = true;
            } else if (AnnotationUtils.findAnnotation(clazz, Controller.class) != null ||
                    AnnotationUtils.findAnnotation(clazz, RestController.class) != null) {
                log.trace("{} is a Spring Controller", typename);
                controllers.add(clazz);
                startReloading = true;
            } else if (AnnotationUtils.findAnnotation(clazz, Component.class) != null) {
                log.trace("{} is a Spring Component", typename);
                components.add(clazz);
                startReloading = true;
            } else if (typename.startsWith(domainPackageName)) {
                log.trace("{} is in the JPA package, checking if it is an entity", typename);
                if (jpaPresent && AnnotationUtils.findAnnotation(clazz, Entity.class) != null) {
                    log.trace("{} is a JPA Entity", typename);
                    entities.add(clazz);
                    startReloading = true;
                }
                if (mongoDBPresent && AnnotationUtils.findAnnotation(clazz, Document.class) != null) {
                    log.trace("{} is a MongoDB Entity", typename);
                    entities.add(clazz);
                    startReloading = true;
                }
            } else if (typename.startsWith(dtoPackageName)) {
                log.debug("{}  is a REST DTO", typename);
                dtos.add(clazz);
                startReloading = true;
            }

            if (startReloading) {
                hotReloadTriggered = true;
                isWaitingForNewClasses = true;
            }
        }
    }

    public void run() {
        while (isStarted) {
            try {
                Thread.sleep(BATCH_DELAY);
                if (hotReloadTriggered) {
                    if (isWaitingForNewClasses) {
                        log.info("Batch reload has been triggered, waiting for new classes for {} ms", BATCH_DELAY);
                        isWaitingForNewClasses = false;
                    } else {
                        hotReloadTriggered = batchReload();
                    }
                } else {
                    log.trace("Waiting for batch reload");
                }
            } catch (InterruptedException e) {
                log.error("JHipsterReloaderThread was awaken", e);
            }
        }
    }

    private boolean batchReload() {
        boolean hasBeansToReload = false;
        synchronized (lock) {
            log.info("Batch reload in progress...");

            for (Reloader reloader : reloaders) {
                boolean reload = false;
                reloader.prepare();

                // reload entities
                if (reloader.supports(EntityReloaderType.class) && !entities.isEmpty()) {
                    reload = true;
                    addSpringBeans(reloader, EntityReloaderType.instance, entities);
                }
                // reload dtos
                if (reloader.supports(RestDtoReloaderType.class) && !dtos.isEmpty()) {
                    reload = true;
                    addSpringBeans(reloader, RestDtoReloaderType.instance, dtos);
                }
                // reload repositories
                if (reloader.supports(RepositoryReloaderType.class) && !repositories.isEmpty()) {
                    reload = true;
                    addSpringBeans(reloader, RepositoryReloaderType.instance, repositories);
                }
                // reload services
                if (reloader.supports(ServiceReloaderType.class) && !services.isEmpty()) {
                    reload = true;
                    addSpringBeans(reloader, ServiceReloaderType.instance, services);
                }
                // reload components
                if (reloader.supports(ComponentReloaderType.class) && !components.isEmpty()) {
                    reload = true;
                    addSpringBeans(reloader, ComponentReloaderType.instance, components);
                }
                // reload controllers
                if (reloader.supports(ControllerReloaderType.class) && !controllers.isEmpty()) {
                    reload = true;
                    addSpringBeans(reloader, ControllerReloaderType.instance, controllers);
                }

                // Reload the spring beans
                if (reload || reloader.hasBeansToReload()) {
                    reloader.reload();
                }

                if (reloader.hasBeansToReload()) {
                    hasBeansToReload = true;
                }
            }

            // clear all lists
            entities.clear();
            dtos.clear();
            repositories.clear();
            services.clear();
            components.clear();
            controllers.clear();
        }

        return hasBeansToReload;
    }

    private void addSpringBeans(Reloader reloader, ReloaderType type, Collection classes) {
        if (classes.size() > 0) {
            log.debug("There are {} Spring {} updated, adding them to be reloaded", classes.size(), type.getName());
            reloader.addBeansToReload(classes, type.getClass());
        }
    }

    /**
     * Register the thread and starts it.
     */
    public static void register(JHipsterReloaderThread jHipsterReloaderThread) {
        try {
            final Thread thread = new Thread(jHipsterReloaderThread);
            thread.setDaemon(true);
            thread.start();

            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    JHipsterReloaderThread.isStarted = false;
                    try {
                        thread.join();
                    } catch (InterruptedException e) {
                        log.error("Failed during the JVM shutdown", e);
                    }
                }
            });
        } catch (Exception e) {
            log.error("Failed to start the reloader thread. Classes will not be reloaded correctly.", e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy