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

org.sam.server.context.BeanClassLoader Maven / Gradle / Ivy

package org.sam.server.context;

import org.sam.server.annotation.component.Component;
import org.sam.server.annotation.ComponentScan;
import org.sam.server.annotation.component.Handler;
import org.sam.server.annotation.handle.RequestMapping;
import org.sam.server.exception.ComponentScanNotFoundException;
import org.sam.server.http.Interceptor;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 루트 패키지로 부터 클래스 파일을 읽어 클래스 정보를 저장하는 클래스입니다.
 *
 * @author hypernova1
 */
public class BeanClassLoader {

    private static String rootPackageName;

    private static final List> handlerClasses = new ArrayList<>();

    private static final List> componentClasses = new ArrayList<>();

    private static final List> interceptorClasses = new ArrayList<>();

    static {
        findRootPackageName();
        loadClasses();
    }

    /**
     * 루트 패키지부터 경로를 탐색하며 핸들러, 컴포넌트, 인터셉터 클래스를 저장합니다.
     * */
    private static void loadClasses() {
        String path = rootPackageName.replace(".", "/");
        try {
            Enumeration resources = Thread.currentThread().getContextClassLoader().getResources(path);
            List> classes = new ArrayList<>();
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                File directory = new File(resource.getFile());
                classes.addAll(createClasses(directory, rootPackageName));
            }
            loadHandlerClasses(classes);
            loadComponentClasses(classes);
            loadInterceptorClasses(classes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 핸들러 클래스를 저장합니다.
     *
     * @param classes 클래스 목록
     * @see RequestMapping
     * */
    private static void loadHandlerClasses(List> classes) {
        handlerClasses.addAll(classes.stream()
                .filter(BeanClassLoader::isHandlerClass)
                .collect(Collectors.toList()));
    }

    /**
     * 컴포넌트 클래스를 저장합니다.
     *
     * @param classes 클래스 목록
     * @see org.sam.server.annotation.component.Component
     * */
    private static void loadComponentClasses(List> classes) {
        for (Class clazz : classes) {
            Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
            addComponentClass(clazz, declaredAnnotations);
        }
    }

    private static void addComponentClass(Class clazz, Annotation[] declaredAnnotations) {
        for (Annotation declaredAnnotation : declaredAnnotations) {
            if (!isComponentClass(declaredAnnotation)) continue;
            componentClasses.add(clazz);
        }
    }

    /**
     * 인터셉터를 상속받은 클래스를 저장합니다.
     *
     * @param classes 클래스 목록
     * @see org.sam.server.http.Interceptor
     * */
    private static void loadInterceptorClasses(List> classes) {
        for (Class clazz : classes) {
            if (!isInterceptorClass(clazz)) continue;
            interceptorClasses.add(clazz);
        }
    }

    /**
     * 디렉토리를 탐색하며 클래스를 찾아 목록을 반환합니다.
     *
     * @param directory 디렉토리
     * @param packageName 패키지명
     * @return 해당 디렉토리의 클래스 목록
     * */
    private static Collection> createClasses(File directory, String packageName) {
        List> classes = new ArrayList<>();
        if (!directory.exists()) return classes;
        File[] files = directory.listFiles();
        assert files != null;
        for (File file : files) {
            if (file.isDirectory()) {
                Collection> foundClasses = createClasses(file, packageName + "." + file.getName());
                classes.addAll(foundClasses);
                continue;
            }
            Class clazz = createClass(packageName, file);
            if (clazz == null) continue;
            classes.add(clazz);
        }
        return classes;
    }

    /**
     * 패키지 안에 있는 클래스를 반환합니다.
     *
     * @param packageName 패키지 이름
     * @param file 패키지 안의 파일
     * @return 클래스
     * */
    private static Class createClass(String packageName, File file) {
        if (!isClassFile(file)) return null;
        try {
            String fullClassName = getFullClassName(packageName, file);
            return Class.forName(fullClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 루트 패키지를 찾습니다.
     * */
    private static void findRootPackageName() {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        try {
            Enumeration resources = classLoader.getResources("");
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                retrieveFile(new File(resource.getFile()), "");
                if (rootPackageName != null) return;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new ComponentScanNotFoundException();
    }

    /**
     * 모든 디렉토리를 탐색하여 루트 패키지를 찾아 저장합니다.
     *
     * @param directory 디렉토리
     * @param packageName 패키지 이름
     * */
    private static void retrieveFile(File directory, String packageName) {
        if (!directory.exists()) return;
        File[] files = directory.listFiles();
        if (files == null) return;
        for (File file : files) {
            appendPackageName(file, packageName);
        }
    }

    /**
     * 패키지 이름을 세팅합니다.
     *
     * @param file 파일
     * @param packageName 현재까지 만들어진 패키지명
     * */
    private static void appendPackageName(File file, String packageName) {
        StringBuilder packageNameBuilder = new StringBuilder(packageName);

        if (!packageNameBuilder.toString().isEmpty()) {
            packageNameBuilder.append(".");
        }

        if (isClassFile(file)) {
            try {
                Class clazz = Class.forName(getClassName(packageNameBuilder.toString() + file.getName()));
                if (!isComponentScanClass(clazz)) return;
                rootPackageName = packageName;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        retrieveFile(file, packageNameBuilder + file.getName());
    }

    /**
     * 핸들러 클래스 목록을 반환합니다.
     *
     * @return 핸들러 클래스 목록
     * */
    static List> getHandlerClasses() {
        return handlerClasses;
    }

    /**
     * 컴포넌트 클래스 목록을 반환합니다.
     *
     * @return 컴포넌트 클래스 목록
     * */
    static List> getComponentClasses() {
        return componentClasses;
    }

    /**
     * 인터셉터 구현 클래스 목록을 반환합니다.
     *
     * @return 인터셉터 구현 클래스 목록
     * */
    static List> getInterceptorClasses() {
        return interceptorClasses;
    }

    /**
     * 클래스의 이름을 반환합니다.
     *
     * @param packageName 패키지명
     * @param file 클래스 파일
     * @return 클래스 이름
     * */
    private static String getFullClassName(String packageName, File file) {
        return packageName + "." + getClassName(file.getName());
    }

    /**
     * 클래스의 이름을 반환합니다.
     *
     * @param fileName 패키지명
     * @return 클래스 이름
     * */
    private static String getClassName(String fileName) {
        return fileName.substring(0, fileName.length() - 6);
    }

    /**
     * 해당 클래스가 핸들러 클래스인지 확인합니다.
     *
     * @param clazz 클래스 타입
     * @return 핸들러 클래스 여부
     * */
    private static boolean isHandlerClass(Class clazz) {
        return clazz.getDeclaredAnnotation(Handler.class) != null;
    }

    /**
     * 해당 클래스에 ComponentScan 어노테이션이 붙어 있는지 확인합니다.
     *
     * @param clazz 클래스 타입
     * @return ComponentScan 클래스 여부
     * @see org.sam.server.annotation.ComponentScan
     * */
    private static boolean isComponentScanClass(Class clazz) {
        return clazz.getDeclaredAnnotation(ComponentScan.class) != null;
    }

    /**
     * 해당 파일이 클래스 파일인지 확인합니다.
     *
     * @param file 파일
     * @return 클래스 파일 여부
     * */
    private static boolean isClassFile(File file) {
        return file.getName().endsWith(".class");
    }

    /**
     * Interceptor를 구현한 클래스인지 확인합니다.
     *
     * @param clazz 클래스 타입
     * @return Interceptor 구현 여부
     * */
    private static boolean isInterceptorClass(Class clazz) {
        Class[] interfaces = clazz.getInterfaces();
        return Arrays.asList(interfaces).contains(Interceptor.class);
    }

    /**
     * Component 관련 어노테이션인지 확인합니다.
     *
     * @param annotation 어노테이션
     * @return Component 어노테이션 여부
     * */
    private static boolean isComponentClass(Annotation annotation) {
        return annotation.annotationType().getDeclaredAnnotation(Component.class) != null || annotation.annotationType().equals(Component.class);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy