From e509820542486d9c82eb4680e492663690f3369c Mon Sep 17 00:00:00 2001
From: TheoneFx <chenxilzx1@gmail.com>
Date: Mon, 20 Mar 2023 20:14:34 +0800
Subject: [PATCH] optmize classpath content loader (#60)

---
 README-zh.md                                  |  14 +-
 docs/faq-zh.md                                |   7 +
 .../loader/ClasspathTemplateLoader.java       | 171 ++++--------------
 initializer-page/pom.xml                      |   2 +-
 .../initializer/start/web/CommonPages.java    |  37 ----
 5 files changed, 49 insertions(+), 182 deletions(-)
 create mode 100644 docs/faq-zh.md
 delete mode 100644 initializer-start/src/main/java/com/alibaba/initializer/start/web/CommonPages.java

diff --git a/README-zh.md b/README-zh.md
index 2bc0700..97cb8a1 100644
--- a/README-zh.md
+++ b/README-zh.md
@@ -6,25 +6,23 @@
 ## 文档
 - [如何自定义内容](docs/howToCustom-zh.md)
 - [代码贡献](docs/CONTRIBUTING-zh.md)
-
+- [常见问题](docs/faq-zh.md)
+ 
 ## 代码结构
 这是一个源自于 Spring Initializr 构建的云原生应用脚手架项目,你可以直接体验该项目的功能通过 [start.aliyun.com](https://start.aliyun.com/) ,项目本身包含以下模块:
 * initializer-generator: 脚手架生成项目模块,在其中`io.spring.start.site`目录下引用了部分 [start.spring.io](https://start.spring.io/) 的基础代码。
 * initializer-page: 脚手架前端页面
+* initializer-start: 脚手架启动、打包入口模块
 
 ## 基于源代码运行
 请在本地 clone 该项目,并确保具备 Java 17 环境。
 
 ### 构建项目
-
-首先,确保 python 在您的环境中已经安装。在项目根目录,执行以下命令,安装 Node 和 Yarn:
+由于前端是以源码的形式存储与本项目中,需要使用yarn进行编译后,成为当前项目的资源文件,才能被正确访问:
 ```shell
-mvn compile -P install-yarn
-```
-在项目根目录,执行以下命令,将静态文件 Copy 到 `initializer-generator` 模块的 target 中:
-```shell 
-mvn prepare-package
+mvn process-sources
 ```
+此步骤执行后,编译后的前端文件,会被复制到 `initializer-page/target/classes/static` 目录下
 
 ### 启动项目
 进入`initializer-generator` 模块,执行以下命令启动应用:
diff --git a/docs/faq-zh.md b/docs/faq-zh.md
new file mode 100644
index 0000000..d8b3acf
--- /dev/null
+++ b/docs/faq-zh.md
@@ -0,0 +1,7 @@
+# 常见问题
+
+## MAC 系统前端无法编译
+对于使用了 Arm 架构(即使用了 M1、M2 处理器)的 Mac 电脑,由于 node 安装包没有适配 Arm 架构的 release 包,需要通过环境变量的方式让 Maven 认为在 x64 系统下运行:
+```bash
+mvn clean package -Dos.arch=x64
+```
\ No newline at end of file
diff --git a/initializer-generator/src/main/java/com/alibaba/initializer/core/template/loader/ClasspathTemplateLoader.java b/initializer-generator/src/main/java/com/alibaba/initializer/core/template/loader/ClasspathTemplateLoader.java
index 17fc2fe..803a8c4 100644
--- a/initializer-generator/src/main/java/com/alibaba/initializer/core/template/loader/ClasspathTemplateLoader.java
+++ b/initializer-generator/src/main/java/com/alibaba/initializer/core/template/loader/ClasspathTemplateLoader.java
@@ -16,45 +16,27 @@
 
 package com.alibaba.initializer.core.template.loader;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URI;
-import java.net.URL;
-import java.nio.file.FileVisitOption;
-import java.nio.file.FileVisitResult;
-import java.nio.file.FileVisitor;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Collectors;
-
 import com.alibaba.initializer.core.constants.ErrorCodeEnum;
 import com.alibaba.initializer.core.exception.BizRuntimeException;
 import com.alibaba.initializer.core.template.CodeTemplate;
 import com.alibaba.initializer.core.template.CodeTemplateRepo;
 import com.alibaba.initializer.core.template.CodeTemplateRepoLoader;
-import com.google.common.collect.Lists;
-import lombok.SneakyThrows;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.io.Resource;
-import org.springframework.util.ResourceUtils;
-
 import static org.springframework.util.ResourceUtils.JAR_URL_SEPARATOR;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
 /**
  * load template file from classpath
  *
@@ -72,12 +54,13 @@ public class ClasspathTemplateLoader implements CodeTemplateRepoLoader {
         try {
             URI uri = new URI(uriStr);
 
-            Resource[] resources = resourceLoader.getResources(CLASSPAHT_PREFIX + uri.getPath());
+            Path rootPath = Paths.get(uri.getPath());
+
+            Resource[] resources = resourceLoader.getResources(CLASSPAHT_PREFIX + rootPath + "/**");
 
             List<CodeTemplate> templates = Arrays.stream(resources)
-                    .filter(Resource::exists)
-                    .map(this::scanTemplte)
-                    .flatMap(Collection::stream)
+                    .filter(Resource::isReadable)
+                    .map(item -> toTemplate(item, rootPath))
                     .collect(Collectors.toList());
 
             return new CodeTemplateRepo(uri, templates);
@@ -86,99 +69,23 @@ public class ClasspathTemplateLoader implements CodeTemplateRepoLoader {
         }
     }
 
-    @SneakyThrows
-    private List<CodeTemplate> scanTemplte(Resource resource) {
-        List<CodeTemplate> templates = Lists.newArrayList();
+    private CodeTemplate toTemplate(Resource resource, Path scanRootPath) {
 
-        URL url = resource.getURL();
+        try {
+            URL url = resource.getURL();
 
-        if (ResourceUtils.isFileURL(url)) {
-            visitFileSystem(templates::add, resource);
-        } else if (ResourceUtils.isJarURL(url)) {
-            visitJarSystem(templates::add, resource);
-        }
+            String urlFile = url.getFile();
 
-        return templates;
-    }
+            int separatorIndex = urlFile.lastIndexOf(JAR_URL_SEPARATOR);
 
-    private void visitFileSystem(TempFileVisitor visitor, Resource resource)
-            throws IOException {
-        if (!resource.isFile()) {
-            return;
-        }
-        File rootFile = resource.getFile();
-        Path scanRoot = Paths.get(rootFile.getAbsolutePath());
-        Files.walkFileTree(scanRoot, EnumSet.of(FileVisitOption.FOLLOW_LINKS),
-                Integer.MAX_VALUE, new FileVisitor<Path>() {
-                    @Override
-                    public FileVisitResult preVisitDirectory(Path dir,
-                                                             BasicFileAttributes attrs) {
-                        return FileVisitResult.CONTINUE;
-                    }
-
-                    @Override
-                    public FileVisitResult visitFile(Path originPath,
-                                                     BasicFileAttributes attrs) {
-                        String fileName = originPath.getFileName().toString();
-
-                        Path relativePath = originPath.subpath(scanRoot.getNameCount(),
-                                originPath.getNameCount());
-                        Path relativeFolderPath = relativePath.getNameCount() == 1
-                                ? null
-                                : relativePath.subpath(0,
-                                relativePath.getNameCount() - 1);
-
-                        visitor.visit(new CodeTemplate(relativeFolderPath, fileName) {
-                                          @Override
-                                          public Reader getReader() {
-                                              try {
-                                                  return new FileReader(originPath.toFile());
-                                              } catch (FileNotFoundException e) {
-                                                  throw new BizRuntimeException(ErrorCodeEnum.SYSTEM_ERROR, "", e);
-                                              }
-                                          }
-                                      }
-                        );
-                        return FileVisitResult.CONTINUE;
-                    }
-
-                    @Override
-                    public FileVisitResult visitFileFailed(Path file, IOException exc) {
-                        return FileVisitResult.CONTINUE;
-                    }
-
-                    @Override
-                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
-                        return FileVisitResult.CONTINUE;
-                    }
-                });
-    }
-
-    private void visitJarSystem(TempFileVisitor visitor, Resource resource) throws IOException {
-        URL url = resource.getURL();
-
-        URL jarUrl = ResourceUtils.extractJarFileURL(url);
-
-        JarFile jarFile = new JarFile(jarUrl.getFile());
-
-        Enumeration<JarEntry> entries = jarFile.entries();
-
-        String resourcePath = url.getFile();
-
-        // the scan root from jar file
-        String scanRoot = resourcePath.substring(resourcePath.indexOf(JAR_URL_SEPARATOR) + JAR_URL_SEPARATOR.length());
-        Path scanRootPath = Paths.get(scanRoot);
-
-        while (entries.hasMoreElements()) {
-            JarEntry entry = entries.nextElement();
-
-            // the full name of jar entry
-            String entryName = entry.getName();
-            if (!entryName.startsWith(scanRoot) || entry.isDirectory()) {
-                continue;
+            if (separatorIndex > -1) {
+                urlFile = urlFile.substring(separatorIndex + 1);
+            } else {
+                String classpathPath = ClasspathTemplateLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+                urlFile = urlFile.startsWith(classpathPath) ? urlFile.replace(classpathPath, "/") : urlFile;
             }
 
-            Path entryPath = Paths.get(entryName);
+            Path entryPath = Paths.get(urlFile);
             String fileName = entryPath.getFileName().toString();
 
             Path relativePath = entryPath.subpath(scanRootPath.getNameCount(), entryPath.getNameCount());
@@ -187,25 +94,17 @@ public class ClasspathTemplateLoader implements CodeTemplateRepoLoader {
                     : relativePath.subpath(0,
                     relativePath.getNameCount() - 1);
 
-            visitor.visit(new CodeTemplate(relativeFolderPath, fileName) {
-                              @Override
-                              public Reader getReader() {
-                                  try {
-                                      return new InputStreamReader(resourceLoader.getResource("classpath:/" + entryPath.toString()).getInputStream());
-                                  } catch (IOException e) {
-                                      throw new BizRuntimeException(ErrorCodeEnum.SYSTEM_ERROR, "load resource error", e);
-                                  }
-                              }
-                          }
-            );
+            return new CodeTemplate(relativeFolderPath, fileName) {
+                @Override
+                public Reader getReader() throws IOException {
+                    return new InputStreamReader(resource.getInputStream());
+                }
+            };
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
-    @FunctionalInterface
-    public interface TempFileVisitor {
-        void visit(CodeTemplate template);
-    }
-
     @Override
     public String getProtocol() {
         return "classpath";
diff --git a/initializer-page/pom.xml b/initializer-page/pom.xml
index 145b38e..ffd372b 100644
--- a/initializer-page/pom.xml
+++ b/initializer-page/pom.xml
@@ -54,7 +54,7 @@
                 <executions>
                     <execution>
                         <id>copy-base-web-resource</id>
-                        <phase>prepare-package</phase>
+                        <phase>process-sources</phase>
                         <goals>
                             <goal>copy-resources</goal>
                         </goals>
diff --git a/initializer-start/src/main/java/com/alibaba/initializer/start/web/CommonPages.java b/initializer-start/src/main/java/com/alibaba/initializer/start/web/CommonPages.java
deleted file mode 100644
index b9e6278..0000000
--- a/initializer-start/src/main/java/com/alibaba/initializer/start/web/CommonPages.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2022-2023 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 com.alibaba.initializer.start.web;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-@Controller
-public class CommonPages {
-
-    @Autowired
-    private ResourceLoader resourceLoader;
-
-    @GetMapping({"/checkpreload.htm", "nginx_status"})
-    @ResponseBody
-    public String checkPreload() {
-        return "success";
-    }
-
-}