From d78c34d18acc1cbfc33cdf59f126aa6787a45442 Mon Sep 17 00:00:00 2001 From: leosam1024 <127861447+leosam1024@users.noreply.github.com> Date: Mon, 12 Jun 2023 22:28:38 +0800 Subject: [PATCH] =?UTF-8?q?MOD:=E6=9B=B4=E6=8D=A2=E4=B8=BAVertx=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=20=E6=8F=90=E9=AB=98=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 143 ++++++- .../com/leosam/tvbox/mv/MainVerticle.java | 123 ++++++ .../leosam/tvbox/mv/TvboxMvApplication.java | 13 - .../tvbox/mv/controller/IndexController.java | 26 -- .../tvbox/mv/controller/VodController.java | 28 -- .../leosam/tvbox/mv/lucene/MvSearcher.java | 29 +- .../leosam/tvbox/mv/service/MvService.java | 45 +- .../tvbox/mv/utils/ClassPathReaderUtils.java | 78 +++- .../tvbox/mv/utils/CollectionUtils.java | 15 + .../com/leosam/tvbox/mv/utils/JsonUtils.java | 93 ----- .../leosam/tvbox/mv/utils/NumberUtils.java | 2 +- .../com/leosam/tvbox/mv/utils/StopWatch.java | 392 ++++++++++++++++++ .../leosam/tvbox/mv/utils/StringUtils.java | 16 + .../com/leosam/tvbox/mv/utils/VertxUtils.java | 20 + .../tvbox/mv/view/MyTimeInterceptor.java | 40 -- .../leosam/tvbox/mv/view/MyWebMvcConfig.java | 27 -- src/main/resources/application.yaml | 34 -- src/main/resources/logback.xml | 66 +++ src/main/resources/static/tvbox.json | 13 + .../com/leosam/tvbox/mv/TestMainVerticle.java | 22 + .../tvbox/mv/TvboxMvApplicationTests.java | 13 - 21 files changed, 913 insertions(+), 325 deletions(-) create mode 100644 src/main/java/com/leosam/tvbox/mv/MainVerticle.java delete mode 100644 src/main/java/com/leosam/tvbox/mv/TvboxMvApplication.java delete mode 100644 src/main/java/com/leosam/tvbox/mv/controller/IndexController.java delete mode 100644 src/main/java/com/leosam/tvbox/mv/controller/VodController.java create mode 100644 src/main/java/com/leosam/tvbox/mv/utils/CollectionUtils.java delete mode 100644 src/main/java/com/leosam/tvbox/mv/utils/JsonUtils.java create mode 100644 src/main/java/com/leosam/tvbox/mv/utils/StopWatch.java create mode 100644 src/main/java/com/leosam/tvbox/mv/utils/StringUtils.java create mode 100644 src/main/java/com/leosam/tvbox/mv/utils/VertxUtils.java delete mode 100644 src/main/java/com/leosam/tvbox/mv/view/MyTimeInterceptor.java delete mode 100644 src/main/java/com/leosam/tvbox/mv/view/MyWebMvcConfig.java delete mode 100644 src/main/resources/application.yaml create mode 100644 src/main/resources/logback.xml create mode 100644 src/main/resources/static/tvbox.json create mode 100644 src/test/java/com/leosam/tvbox/mv/TestMainVerticle.java delete mode 100644 src/test/java/com/leosam/tvbox/mv/TvboxMvApplicationTests.java diff --git a/pom.xml b/pom.xml index ade1590..49fd622 100644 --- a/pom.xml +++ b/pom.xml @@ -2,16 +2,11 @@ 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.1.0 - - com.leosam.tvbox tvbox-mv 0.0.1-SNAPSHOT tvbox-mv + Demo project for Spring Boot 17 @@ -20,46 +15,164 @@ UTF-8 true + 3.8.1 + 3.2.4 + 2.22.2 + 3.0.0 + + 4.4.3 + 5.9.2 + + com.leosam.tvbox.mv.MainVerticle + io.vertx.core.Launcher + + - org.springframework.boot - spring-boot-starter-web + io.vertx + vertx-web + + + com.fasterxml.jackson.core + jackson-databind - org.springframework.boot - spring-boot-starter-test + io.vertx + vertx-junit5 test + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + + org.slf4j + slf4j-api + 2.0.5 + + + ch.qos.logback + logback-classic + 1.4.7 + + + org.codehaus.janino + janino + 3.1.9 + + + org.fusesource.jansi + jansi + 2.4.0 + + org.apache.lucene lucene-core - 8.5.2 + 9.5.0 org.apache.lucene lucene-queryparser - 8.5.2 + 9.5.0 org.apache.lucene lucene-analyzers-smartcn - 8.5.2 + 8.11.2 + + + + io.vertx + vertx-stack-depchain + ${vertx.version} + pom + import + + + + - org.springframework.boot - spring-boot-maven-plugin + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 17 + + + maven-shade-plugin + ${maven-shade-plugin.version} + + + package + + shade + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + ${launcher.class} + ${main.verticle} + + + + + ${project.artifactId}-${project.version} + + + + + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + io.vertx.core.Launcher + + run + ${main.verticle} + + + + diff --git a/src/main/java/com/leosam/tvbox/mv/MainVerticle.java b/src/main/java/com/leosam/tvbox/mv/MainVerticle.java new file mode 100644 index 0000000..28c9953 --- /dev/null +++ b/src/main/java/com/leosam/tvbox/mv/MainVerticle.java @@ -0,0 +1,123 @@ +package com.leosam.tvbox.mv; + +import com.leosam.tvbox.mv.data.MvResult; +import com.leosam.tvbox.mv.data.VodResult; +import com.leosam.tvbox.mv.service.MvService; +import com.leosam.tvbox.mv.utils.NumberUtils; +import com.leosam.tvbox.mv.utils.StringUtils; +import com.leosam.tvbox.mv.utils.VertxUtils; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpServer; +import io.vertx.core.json.Json; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; + +public class MainVerticle extends AbstractVerticle { + private static final Logger logger = LoggerFactory.getLogger(MainVerticle.class); + + private static MvService mvService; + private static int port = 0; + + static { + CompletableFuture.runAsync(MainVerticle::initIndex) + .thenAcceptAsync((a) -> { + if (port > 0) { + logger.info("索引完成,可以请求数据了, HTTP server port " + port); + } else { + logger.info("索引完成,可以请求数据了"); + } + }); + } + + private static void initIndex() { + try { + MvService mvService = new MvService(); + mvService.initIndex(); + MainVerticle.mvService = mvService; + } catch (Exception e) { + logger.error("初始化索引失败", e); + } + } + + @Override + public void start(Promise startPromise) throws Exception { + HttpServer httpServer = vertx.createHttpServer(); + + Router router = Router.router(vertx); + router.route("/mv/search").handler(this::searchMv); + router.route("/mv/vod").handler(this::searchMvVod); + router.route().handler(req -> req.response().putHeader("content-type", "text/plain").end("Hello from Vert.x!")); + httpServer.requestHandler(router); + + httpServer.listen(7777, http -> { + if (http.succeeded()) { + startPromise.complete(); + logger.info("HTTP server started on port 7777"); + } else { + startPromise.fail(http.cause()); + } + }); + port = httpServer.actualPort(); + } + + private void searchMvVod(RoutingContext req) { + // 获取参数 + String wd = VertxUtils.queryParam(req, "wd"); + String ac = VertxUtils.queryParam(req, "ac"); + String ids = VertxUtils.queryParam(req, "ids"); + String maxCount = VertxUtils.queryParam(req, "maxCount"); + int max = Math.min(Math.max(NumberUtils.toInt(maxCount, 100), 10), 1000); + String query = StringUtils.isNotEmpty(wd) ? wd : ids; + + // 处理结果 + VodResult vodResult = null; + try { + if (mvService != null) { + vodResult = mvService.searchVod(query, max); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + // 返回数据 + String jsonString = Json.encode(vodResult); + req.response() + .putHeader("content-type", "application/json") + .end(jsonString); + } + + private void searchMv(RoutingContext req) { + // 获取参数 + String query = VertxUtils.queryParam(req, "query"); + String maxCount = VertxUtils.queryParam(req, "maxCount"); + int max = Math.min(Math.max(NumberUtils.toInt(maxCount, 100), 10), 1000); + + // 处理结果 + MvResult search = null; + try { + if (mvService != null) { + search = mvService.search(query, max); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + // 返回数据 + String jsonString = Json.encode(search); + req.response() + .putHeader("content-type", "application/json") + .end(jsonString); + } + + + public static void main(String[] args) throws Exception { + Vertx vertx = Vertx.vertx(); + vertx.deployVerticle(new MainVerticle()); + } +} diff --git a/src/main/java/com/leosam/tvbox/mv/TvboxMvApplication.java b/src/main/java/com/leosam/tvbox/mv/TvboxMvApplication.java deleted file mode 100644 index a0c5fda..0000000 --- a/src/main/java/com/leosam/tvbox/mv/TvboxMvApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.leosam.tvbox.mv; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class TvboxMvApplication { - - public static void main(String[] args) { - SpringApplication.run(TvboxMvApplication.class, args); - } - -} diff --git a/src/main/java/com/leosam/tvbox/mv/controller/IndexController.java b/src/main/java/com/leosam/tvbox/mv/controller/IndexController.java deleted file mode 100644 index 0befa31..0000000 --- a/src/main/java/com/leosam/tvbox/mv/controller/IndexController.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.leosam.tvbox.mv.controller; - -import com.leosam.tvbox.mv.data.MvResult; -import com.leosam.tvbox.mv.service.MvService; -import com.leosam.tvbox.mv.utils.NumberUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author admin - * @since 2023/6/10 17:34 - */ -@RestController -public class IndexController { - - @Autowired - private MvService mvService; - - @RequestMapping(value = {"/mv/search"}) - public MvResult searchMv(String query, String maxCount) throws Exception { - int max = Math.min(Math.max(NumberUtils.toInt(maxCount, 100), 10), 1000); - return mvService.search(query, max); - } - -} diff --git a/src/main/java/com/leosam/tvbox/mv/controller/VodController.java b/src/main/java/com/leosam/tvbox/mv/controller/VodController.java deleted file mode 100644 index 31bff81..0000000 --- a/src/main/java/com/leosam/tvbox/mv/controller/VodController.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.leosam.tvbox.mv.controller; - -import com.leosam.tvbox.mv.data.VodResult; -import com.leosam.tvbox.mv.service.MvService; -import com.leosam.tvbox.mv.utils.NumberUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * @author admin - * @since 2023/6/11 18:54 - */ -@RestController -public class VodController { - - @Autowired - private MvService mvService; - - @RequestMapping(value = {"/mv/vod"}) - public VodResult searchVod(String wd, String ac, String ids, String maxCount) throws Exception { - int max = Math.min(Math.max(NumberUtils.toInt(maxCount, 100), 10), 1000); - String query = StringUtils.hasText(wd) ? wd : ids; - return mvService.searchVod(query, max); - } - -} diff --git a/src/main/java/com/leosam/tvbox/mv/lucene/MvSearcher.java b/src/main/java/com/leosam/tvbox/mv/lucene/MvSearcher.java index 5374a51..189a644 100644 --- a/src/main/java/com/leosam/tvbox/mv/lucene/MvSearcher.java +++ b/src/main/java/com/leosam/tvbox/mv/lucene/MvSearcher.java @@ -1,10 +1,12 @@ package com.leosam.tvbox.mv.lucene; +import io.vertx.ext.web.impl.LRUCache; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.StoredFields; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; @@ -24,39 +26,58 @@ import java.nio.file.Paths; public class MvSearcher { private IndexReader reader; private IndexSearcher searcher; + private StoredFields storedFields; + private final LRUCache documentLruCache = new LRUCache<>(1000); + private final LRUCache topDocsLruCache = new LRUCache<>(100); public MvSearcher(String indexDirectoryPath) throws IOException { Path indexPath = Paths.get(indexDirectoryPath); Directory directory = FSDirectory.open(indexPath); reader = DirectoryReader.open(directory); searcher = new IndexSearcher(reader); + storedFields = reader.storedFields(); } public TopDocs searchTopDocs(String field, String queryStr, int maxHit) throws Exception { + String cacheKey = field + "_" + queryStr + "_" + maxHit; + TopDocs topDocs = topDocsLruCache.get(cacheKey); + if (topDocs != null) { + return topDocs; + } + Analyzer analyzer = new SmartChineseAnalyzer(); // Analyzer analyzer = new StandardAnalyzer(); QueryParser parser = new QueryParser(field, analyzer); Query query = parser.parse(queryStr); TopDocs docs = searcher.search(query, maxHit); + + topDocsLruCache.put(cacheKey, docs); return docs; } public void search(String field, String queryStr) throws Exception { Analyzer analyzer = new SmartChineseAnalyzer(); -// Analyzer analyzer = new StandardAnalyzer(); + // Analyzer analyzer = new StandardAnalyzer(); QueryParser parser = new QueryParser(field, analyzer); Query query = parser.parse(queryStr); TopDocs docs = searcher.search(query, 200); // 处理搜索结果 for (ScoreDoc scoreDoc : docs.scoreDocs) { - Document document = searcher.doc(scoreDoc.doc); + Document document = getDocument(scoreDoc.doc); System.out.println(document.get("name") + "," + document.get("url") + " score=" + scoreDoc.score); } } - public Document getDocument(int docID) throws IOException { - Document document = searcher.doc(docID); + public Document getDocument(int docId) throws IOException { + Document document = documentLruCache.get(docId); + if (document != null) { + return document; + } + document = storedFields.document(docId); + if (document != null) { + documentLruCache.put(docId, document); + } return document; } diff --git a/src/main/java/com/leosam/tvbox/mv/service/MvService.java b/src/main/java/com/leosam/tvbox/mv/service/MvService.java index 921180d..c03d5d7 100644 --- a/src/main/java/com/leosam/tvbox/mv/service/MvService.java +++ b/src/main/java/com/leosam/tvbox/mv/service/MvService.java @@ -7,6 +7,9 @@ import com.leosam.tvbox.mv.data.VodResult; import com.leosam.tvbox.mv.lucene.MvIndex; import com.leosam.tvbox.mv.lucene.MvSearcher; import com.leosam.tvbox.mv.utils.ClassPathReaderUtils; +import com.leosam.tvbox.mv.utils.CollectionUtils; +import com.leosam.tvbox.mv.utils.StopWatch; +import com.leosam.tvbox.mv.utils.StringUtils; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.StringField; @@ -15,14 +18,10 @@ import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StopWatch; -import org.springframework.util.StringUtils; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -34,16 +33,16 @@ import java.util.concurrent.atomic.AtomicInteger; * @author admin * @since 2023/6/10 17:35 */ -@Service -public class MvService implements InitializingBean { +public class MvService { private static final Logger logger = LoggerFactory.getLogger(MvService.class); private static final String indexDirectoryPath = "index"; + private static final String MV_FILE = "tvbox/16wMV.txt"; private MvSearcher mvSearcher; public MvResult search(String query, int max) throws Exception { - if (!StringUtils.hasText(query)) { + if (StringUtils.isEmpty(query) || mvSearcher == null) { return new MvResult(); } @@ -55,7 +54,7 @@ public class MvService implements InitializingBean { MvResult result = new MvResult(); result.setQuery(query); result.setTotalHits(topDocs.totalHits.value); - result.setList(new LinkedList<>()); + result.setList(new ArrayList<>(topDocs.scoreDocs.length)); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { Document document = mvSearcher.getDocument(scoreDoc.doc); if (document == null) { @@ -66,8 +65,9 @@ public class MvService implements InitializingBean { String songName = name; String songUser = null; if (name.contains("-")) { - songUser = name.split("-", 2)[0]; - songName = name.split("-", 2)[1]; + String[] split = name.split("-", 2); + songUser = split[0]; + songName = split[1]; } String url = document.get("url"); if (!url.startsWith("http")) { @@ -96,12 +96,13 @@ public class MvService implements InitializingBean { vod.setVodId(wd); vod.setVodName(wd); vod.setVodPlayFrom("mv"); - vod.setVodPic("http://m4.auto.itc.cn/auto/content/20230611/45d65d8a001f0c5aa008030f41c98666.jpeg"); + vod.setVodPic("http://yanxuan.nosdn.127.net/b6fb987ce79f308949e44f5129a4b51c.jpeg"); + // vod.setVodPic("http://m4.auto.itc.cn/auto/content/20230611/45d65d8a001f0c5aa008030f41c98666.jpeg"); List playUrlList = new LinkedList<>(); Set vodActorList = new LinkedHashSet<>(); for (MvContent content : search.getList()) { playUrlList.add(content.getName() + "$" + content.getUrl()); - if (vodActorList.size() < 5 && StringUtils.hasText(content.getSongUser())) { + if (vodActorList.size() < 5 && StringUtils.isNotEmpty(content.getSongUser())) { vodActorList.add(content.getSongUser()); } } @@ -115,8 +116,8 @@ public class MvService implements InitializingBean { return vodResult; } - @Override - public void afterPropertiesSet() throws Exception { + public void initIndex() throws Exception { + // 重建索引 String indexAbsolutePath = new File(indexDirectoryPath).getAbsoluteFile().getAbsolutePath(); reBuildIndex(indexAbsolutePath); @@ -130,13 +131,22 @@ public class MvService implements InitializingBean { private static void reBuildIndex(String indexDirectoryPath) throws IOException { + File file = new File(indexDirectoryPath).getAbsoluteFile(); + int mvFileSize = ClassPathReaderUtils.getSize(MV_FILE); + File mvFileSizeTxt = Path.of(file.getAbsolutePath(), "" + mvFileSize + ".txt").toFile(); + if (mvFileSizeTxt.exists()) { + logger.info("{}已经索引, 跳过索引", MV_FILE); + return; + } + + StopWatch stopWatch = new StopWatch(); logger.info("重建索引中...."); // 清空以前的索引 stopWatch.start("清空以前索引"); logger.info("清空历史索引...."); - File file = new File(indexDirectoryPath).getAbsoluteFile(); + if (file.isDirectory()) { File[] files = file.listFiles(); for (File file1 : files) { @@ -152,7 +162,7 @@ public class MvService implements InitializingBean { logger.info("创建索引中...."); AtomicInteger line = new AtomicInteger(); MvIndex mvIndex = new MvIndex(indexDirectoryPath); - ClassPathReaderUtils.getBufferedReader("tvbox/16wMV.txt").lines() + ClassPathReaderUtils.getBufferedReader(MV_FILE).lines() .filter(l -> l.contains(",h")) .filter(l -> l.contains("-")) .forEach(l -> { @@ -182,6 +192,7 @@ public class MvService implements InitializingBean { } }); mvIndex.close(); + mvFileSizeTxt.createNewFile(); logger.info("创建索引中....完成, 总共{}条", line.get()); stopWatch.stop(); logger.info("重建索引中....完成,耗时 {} 毫秒, 索引位置:{}", stopWatch.getTotalTimeMillis(), file.getPath()); diff --git a/src/main/java/com/leosam/tvbox/mv/utils/ClassPathReaderUtils.java b/src/main/java/com/leosam/tvbox/mv/utils/ClassPathReaderUtils.java index 4c4e4a8..a3e8882 100644 --- a/src/main/java/com/leosam/tvbox/mv/utils/ClassPathReaderUtils.java +++ b/src/main/java/com/leosam/tvbox/mv/utils/ClassPathReaderUtils.java @@ -1,12 +1,12 @@ package com.leosam.tvbox.mv.utils; -import org.springframework.core.io.ClassPathResource; import java.io.BufferedReader; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.util.Properties; /** * @author admin @@ -14,19 +14,33 @@ import java.util.Properties; */ public class ClassPathReaderUtils { + public static int getSize(String path) { + try { + ClassPathResource resource = new ClassPathResource(path); + InputStream inputStream = resource.getInputStream(); + int available = inputStream.available(); + return available; + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + public static InputStreamReader getInputStreamReader(String path) { try { ClassPathResource resource = new ClassPathResource(path); - InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream(),StandardCharsets.UTF_8); + InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8); return inputStreamReader; } catch (IOException ex) { throw new RuntimeException(ex); } } + public static BufferedReader getBufferedReader(String path) { try { ClassPathResource resource = new ClassPathResource(path); + + InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream(),StandardCharsets.UTF_8); BufferedReader reader = new BufferedReader(inputStreamReader); return reader; @@ -35,17 +49,6 @@ public class ClassPathReaderUtils { } } - public static Properties getProperties(String path) { - Properties properties = new Properties(); - try { - ClassPathResource resource = new ClassPathResource(path); - InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream()); - properties.load(new BufferedReader(inputStreamReader)); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - return properties; - } public static String getContent(String path) { StringBuilder content = new StringBuilder(); @@ -70,5 +73,52 @@ public class ClassPathReaderUtils { System.out.println(json); } + public static class ClassPathResource { + private final String path; + + private final ClassLoader classLoader; + + public ClassPathResource(String path) { + this.path = path; + this.classLoader = getDefaultClassLoader(); + } + + public InputStream getInputStream() throws IOException { + InputStream is; + if (this.classLoader != null) { + is = this.classLoader.getResourceAsStream(this.path); + } else { + is = ClassLoader.getSystemResourceAsStream(this.path); + } + if (is == null) { + throw new FileNotFoundException(path + " cannot be opened because it does not exist"); + } + return is; + } + + + public static ClassLoader getDefaultClassLoader() { + ClassLoader cl = null; + try { + cl = Thread.currentThread().getContextClassLoader(); + } catch (Throwable ex) { + // Cannot access thread context ClassLoader - falling back... + } + if (cl == null) { + // No thread context class loader -> use class loader of this class. + cl = ClassPathResource.class.getClassLoader(); + if (cl == null) { + // getClassLoader() returning null indicates the bootstrap ClassLoader + try { + cl = ClassLoader.getSystemClassLoader(); + } catch (Throwable ex) { + // Cannot access system ClassLoader - oh well, maybe the caller can live with null... + } + } + } + return cl; + } + } + } diff --git a/src/main/java/com/leosam/tvbox/mv/utils/CollectionUtils.java b/src/main/java/com/leosam/tvbox/mv/utils/CollectionUtils.java new file mode 100644 index 0000000..cb77e8a --- /dev/null +++ b/src/main/java/com/leosam/tvbox/mv/utils/CollectionUtils.java @@ -0,0 +1,15 @@ +package com.leosam.tvbox.mv.utils; + +import java.util.Collection; + +/** + * @author admin + * @since 2023/6/12 21:15 + */ +public class CollectionUtils { + + public static boolean isEmpty(Collection collection) { + return (collection == null || collection.isEmpty()); + } + +} diff --git a/src/main/java/com/leosam/tvbox/mv/utils/JsonUtils.java b/src/main/java/com/leosam/tvbox/mv/utils/JsonUtils.java deleted file mode 100644 index abe6181..0000000 --- a/src/main/java/com/leosam/tvbox/mv/utils/JsonUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.leosam.tvbox.mv.utils; - - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.HashMap; - -/** - * - */ -public class JsonUtils { - - private static final Logger LOG = LoggerFactory.getLogger(JsonUtils.class); - private static final TypeReference> TYPE_MAP = - new TypeReference>() { - }; - - public static ObjectMapper objectMapper = new ObjectMapper(); - - static { - objectMapper.registerModule(new Jdk8Module()); - objectMapper.enable(JsonParser.Feature.ALLOW_COMMENTS); - } - - public static String writeValue(Object o) { - try { - return writeValueThrowException(o); - } catch (Exception e) { - return ""; - } - } - - public static String writeValuePrettyPrinter(Object o) { - try { - return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(o); - } catch (Exception e) { - LOG.error("exception occur when Json serializePretty", e); - return ""; - } - } - - - public static String writeValueThrowException(Object o) throws JsonProcessingException { - try { - return objectMapper.writeValueAsString(o); - } catch (Exception e) { - LOG.error("exception occur when Json serialize", e); - throw e; - } - } - - public static T readValue(String json, Class clazz) { - try { - return readValueThrowException(json, clazz); - } catch (IOException e) { - return null; - } - } - - public static T readValueThrowException(String json, Class clazz) throws IOException { - try { - return objectMapper.readValue(json, clazz); - } catch (IOException e) { - LOG.warn("Failed to deserialize from JSON string", e); - throw e; - } - } - - public static T readValue(String json, TypeReference typeReference) { - try { - return readValueThrowException(json, typeReference); - } catch (IOException e) { - return null; - } - } - - public static T readValueThrowException(String json, TypeReference typeReference) throws IOException { - try { - return objectMapper.readValue(json, typeReference); - } catch (IOException e) { - LOG.warn("Failed to deserialize from JSON string", e); - throw e; - } - } - -} diff --git a/src/main/java/com/leosam/tvbox/mv/utils/NumberUtils.java b/src/main/java/com/leosam/tvbox/mv/utils/NumberUtils.java index e2b8ddc..d94dda7 100644 --- a/src/main/java/com/leosam/tvbox/mv/utils/NumberUtils.java +++ b/src/main/java/com/leosam/tvbox/mv/utils/NumberUtils.java @@ -7,7 +7,7 @@ package com.leosam.tvbox.mv.utils; public class NumberUtils { public static int toInt(final String str, final int defaultValue) { - if (str == null) { + if (str == null || str.length() == 0) { return defaultValue; } try { diff --git a/src/main/java/com/leosam/tvbox/mv/utils/StopWatch.java b/src/main/java/com/leosam/tvbox/mv/utils/StopWatch.java new file mode 100644 index 0000000..376184a --- /dev/null +++ b/src/main/java/com/leosam/tvbox/mv/utils/StopWatch.java @@ -0,0 +1,392 @@ +package com.leosam.tvbox.mv.utils; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + + +/** + * Simple stop watch, allowing for timing of a number of tasks, exposing total + * running time and running time for each named task. + * + *

Conceals use of {@link System#nanoTime()}, improving the readability of + * application code and reducing the likelihood of calculation errors. + * + *

Note that this object is not designed to be thread-safe and does not use + * synchronization. + * + *

This class is normally used to verify performance during proof-of-concept + * work and in development, rather than as part of production applications. + * + *

As of Spring Framework 5.2, running time is tracked and reported in + * nanoseconds. + * + * @author Rod Johnson + * @author Juergen Hoeller + * @author Sam Brannen + * @since May 2, 2001 + */ +public class StopWatch { + + /** + * Identifier of this {@code StopWatch}. + *

Handy when we have output from multiple stop watches and need to + * distinguish between them in log or console output. + */ + private final String id; + + private boolean keepTaskList = true; + + private final List taskList = new ArrayList<>(1); + + /** + * Start time of the current task. + */ + private long startTimeNanos; + + /** + * Name of the current task. + */ + private String currentTaskName; + + private TaskInfo lastTaskInfo; + + private int taskCount; + + /** + * Total running time. + */ + private long totalTimeNanos; + + + /** + * Construct a new {@code StopWatch}. + *

Does not start any task. + */ + public StopWatch() { + this(""); + } + + /** + * Construct a new {@code StopWatch} with the given ID. + *

The ID is handy when we have output from multiple stop watches and need + * to distinguish between them. + *

Does not start any task. + * + * @param id identifier for this stop watch + */ + public StopWatch(String id) { + this.id = id; + } + + + /** + * Get the ID of this {@code StopWatch}, as specified on construction. + * + * @return the ID (empty String by default) + * @see #StopWatch(String) + * @since 4.2.2 + */ + public String getId() { + return this.id; + } + + /** + * Configure whether the {@link StopWatch.TaskInfo} array is built over time. + *

Set this to {@code false} when using a {@code StopWatch} for millions + * of intervals; otherwise, the {@code TaskInfo} structure will consume + * excessive memory. + *

Default is {@code true}. + */ + public void setKeepTaskList(boolean keepTaskList) { + this.keepTaskList = keepTaskList; + } + + + /** + * Start an unnamed task. + *

The results are undefined if {@link #stop()} or timing methods are + * called without invoking this method first. + * + * @see #start(String) + * @see #stop() + */ + public void start() throws IllegalStateException { + start(""); + } + + /** + * Start a named task. + *

The results are undefined if {@link #stop()} or timing methods are + * called without invoking this method first. + * + * @param taskName the name of the task to start + * @see #start() + * @see #stop() + */ + public void start(String taskName) throws IllegalStateException { + if (this.currentTaskName != null) { + throw new IllegalStateException("Can't start StopWatch: it's already running"); + } + this.currentTaskName = taskName; + this.startTimeNanos = System.nanoTime(); + } + + /** + * Stop the current task. + *

The results are undefined if timing methods are called without invoking + * at least one pair of {@code start()} / {@code stop()} methods. + * + * @see #start() + * @see #start(String) + */ + public void stop() throws IllegalStateException { + if (this.currentTaskName == null) { + throw new IllegalStateException("Can't stop StopWatch: it's not running"); + } + long lastTime = System.nanoTime() - this.startTimeNanos; + this.totalTimeNanos += lastTime; + this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime); + if (this.keepTaskList) { + this.taskList.add(this.lastTaskInfo); + } + ++this.taskCount; + this.currentTaskName = null; + } + + /** + * Determine whether this {@code StopWatch} is currently running. + * + * @see #currentTaskName() + */ + public boolean isRunning() { + return (this.currentTaskName != null); + } + + /** + * Get the name of the currently running task, if any. + * + * @see #isRunning() + * @since 4.2.2 + */ + public String currentTaskName() { + return this.currentTaskName; + } + + /** + * Get the time taken by the last task in nanoseconds. + * + * @see #getLastTaskTimeMillis() + * @since 5.2 + */ + public long getLastTaskTimeNanos() throws IllegalStateException { + if (this.lastTaskInfo == null) { + throw new IllegalStateException("No tasks run: can't get last task interval"); + } + return this.lastTaskInfo.getTimeNanos(); + } + + /** + * Get the time taken by the last task in milliseconds. + * + * @see #getLastTaskTimeNanos() + */ + public long getLastTaskTimeMillis() throws IllegalStateException { + if (this.lastTaskInfo == null) { + throw new IllegalStateException("No tasks run: can't get last task interval"); + } + return this.lastTaskInfo.getTimeMillis(); + } + + /** + * Get the name of the last task. + */ + public String getLastTaskName() throws IllegalStateException { + if (this.lastTaskInfo == null) { + throw new IllegalStateException("No tasks run: can't get last task name"); + } + return this.lastTaskInfo.getTaskName(); + } + + /** + * Get the last task as a {@link StopWatch.TaskInfo} object. + */ + public StopWatch.TaskInfo getLastTaskInfo() throws IllegalStateException { + if (this.lastTaskInfo == null) { + throw new IllegalStateException("No tasks run: can't get last task info"); + } + return this.lastTaskInfo; + } + + + /** + * Get the total time in nanoseconds for all tasks. + * + * @see #getTotalTimeMillis() + * @see #getTotalTimeSeconds() + * @since 5.2 + */ + public long getTotalTimeNanos() { + return this.totalTimeNanos; + } + + /** + * Get the total time in milliseconds for all tasks. + * + * @see #getTotalTimeNanos() + * @see #getTotalTimeSeconds() + */ + public long getTotalTimeMillis() { + return nanosToMillis(this.totalTimeNanos); + } + + /** + * Get the total time in seconds for all tasks. + * + * @see #getTotalTimeNanos() + * @see #getTotalTimeMillis() + */ + public double getTotalTimeSeconds() { + return nanosToSeconds(this.totalTimeNanos); + } + + /** + * Get the number of tasks timed. + */ + public int getTaskCount() { + return this.taskCount; + } + + /** + * Get an array of the data for tasks performed. + */ + public StopWatch.TaskInfo[] getTaskInfo() { + if (!this.keepTaskList) { + throw new UnsupportedOperationException("Task info is not being kept!"); + } + return this.taskList.toArray(new StopWatch.TaskInfo[0]); + } + + + /** + * Get a short description of the total running time. + */ + public String shortSummary() { + return "StopWatch '" + getId() + "': running time = " + getTotalTimeNanos() + " ns"; + } + + /** + * Generate a string with a table describing all tasks performed. + *

For custom reporting, call {@link #getTaskInfo()} and use the task info + * directly. + */ + public String prettyPrint() { + StringBuilder sb = new StringBuilder(shortSummary()); + sb.append('\n'); + if (!this.keepTaskList) { + sb.append("No task info kept"); + } else { + sb.append("---------------------------------------------\n"); + sb.append("ns % Task name\n"); + sb.append("---------------------------------------------\n"); + NumberFormat nf = NumberFormat.getNumberInstance(); + nf.setMinimumIntegerDigits(9); + nf.setGroupingUsed(false); + NumberFormat pf = NumberFormat.getPercentInstance(); + pf.setMinimumIntegerDigits(3); + pf.setGroupingUsed(false); + for (StopWatch.TaskInfo task : getTaskInfo()) { + sb.append(nf.format(task.getTimeNanos())).append(" "); + sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" "); + sb.append(task.getTaskName()).append('\n'); + } + } + return sb.toString(); + } + + /** + * Generate an informative string describing all tasks performed + *

For custom reporting, call {@link #getTaskInfo()} and use the task info + * directly. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(shortSummary()); + if (this.keepTaskList) { + for (StopWatch.TaskInfo task : getTaskInfo()) { + sb.append("; [").append(task.getTaskName()).append("] took ").append(task.getTimeNanos()).append(" ns"); + long percent = Math.round(100.0 * task.getTimeNanos() / getTotalTimeNanos()); + sb.append(" = ").append(percent).append('%'); + } + } else { + sb.append("; no task info kept"); + } + return sb.toString(); + } + + + private static long nanosToMillis(long duration) { + return TimeUnit.NANOSECONDS.toMillis(duration); + } + + private static double nanosToSeconds(long duration) { + return duration / 1_000_000_000.0; + } + + + /** + * Nested class to hold data about one task executed within the {@code StopWatch}. + */ + public static final class TaskInfo { + + private final String taskName; + + private final long timeNanos; + + TaskInfo(String taskName, long timeNanos) { + this.taskName = taskName; + this.timeNanos = timeNanos; + } + + /** + * Get the name of this task. + */ + public String getTaskName() { + return this.taskName; + } + + /** + * Get the time in nanoseconds this task took. + * + * @see #getTimeMillis() + * @see #getTimeSeconds() + * @since 5.2 + */ + public long getTimeNanos() { + return this.timeNanos; + } + + /** + * Get the time in milliseconds this task took. + * + * @see #getTimeNanos() + * @see #getTimeSeconds() + */ + public long getTimeMillis() { + return nanosToMillis(this.timeNanos); + } + + /** + * Get the time in seconds this task took. + * + * @see #getTimeMillis() + * @see #getTimeNanos() + */ + public double getTimeSeconds() { + return nanosToSeconds(this.timeNanos); + } + + } + +} diff --git a/src/main/java/com/leosam/tvbox/mv/utils/StringUtils.java b/src/main/java/com/leosam/tvbox/mv/utils/StringUtils.java new file mode 100644 index 0000000..3488d44 --- /dev/null +++ b/src/main/java/com/leosam/tvbox/mv/utils/StringUtils.java @@ -0,0 +1,16 @@ +package com.leosam.tvbox.mv.utils; + +/** + * @author admin + * @since 2023/6/12 21:32 + */ +public class StringUtils { + + public static boolean isNotEmpty(final CharSequence cs) { + return !isEmpty(cs); + } + + public static boolean isEmpty(final CharSequence cs) { + return cs == null || cs.length() == 0; + } +} diff --git a/src/main/java/com/leosam/tvbox/mv/utils/VertxUtils.java b/src/main/java/com/leosam/tvbox/mv/utils/VertxUtils.java new file mode 100644 index 0000000..bb1ce7b --- /dev/null +++ b/src/main/java/com/leosam/tvbox/mv/utils/VertxUtils.java @@ -0,0 +1,20 @@ +package com.leosam.tvbox.mv.utils; + +import io.vertx.ext.web.RoutingContext; + +import java.util.List; + +/** + * @author admin + * @since 2023/6/12 21:16 + */ +public class VertxUtils { + + public static String queryParam(RoutingContext context, String wd) { + List param = context.queryParam(wd); + if (param != null && param.size() > 0) { + return param.get(0); + } + return ""; + } +} diff --git a/src/main/java/com/leosam/tvbox/mv/view/MyTimeInterceptor.java b/src/main/java/com/leosam/tvbox/mv/view/MyTimeInterceptor.java deleted file mode 100644 index 3632e31..0000000 --- a/src/main/java/com/leosam/tvbox/mv/view/MyTimeInterceptor.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.leosam.tvbox.mv.view; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.servlet.HandlerInterceptor; - -/** - * @author admin - * @since 2023/6/11 20:03 - */ -public class MyTimeInterceptor implements HandlerInterceptor { - - private static final Logger logger = LoggerFactory.getLogger(MyTimeInterceptor.class); - /** - * 线程变量 - 开始执行时间 - */ - private final ThreadLocal startTime = new ThreadLocal<>(); - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - startTime.set(System.nanoTime()); - return true; - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { - if (startTime.get() == null) { - return; - } - long processTime = System.nanoTime() - startTime.get(); - if (logger.isInfoEnabled()) { - logger.info("RequestURL[{}], Referer[{}], TIME[{} ms]", request.getRequestURL().toString(), request.getHeader("Referer"), String.format("%.3f", (processTime / 1_000_000.0))); - } - startTime.remove(); - } - -} - diff --git a/src/main/java/com/leosam/tvbox/mv/view/MyWebMvcConfig.java b/src/main/java/com/leosam/tvbox/mv/view/MyWebMvcConfig.java deleted file mode 100644 index e208f7b..0000000 --- a/src/main/java/com/leosam/tvbox/mv/view/MyWebMvcConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.leosam.tvbox.mv.view; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - * @author admin - */ -@Configuration -public class MyWebMvcConfig implements WebMvcConfigurer { - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - // 页面运行时间统计 - registry.addInterceptor(new MyTimeInterceptor()) - .addPathPatterns("/**") - .excludePathPatterns("/static/**") - .excludePathPatterns("/favicon.ico"); - } -} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml deleted file mode 100644 index 69087e0..0000000 --- a/src/main/resources/application.yaml +++ /dev/null @@ -1,34 +0,0 @@ -server: - port: 7777 -spring: - profiles: - active: test -logging: - file: - name: ./logs/tvbox-mv.log - level: - .: error - org.spring.framework: debug - com.leosam: debug - ---- -server: - port: 7777 -spring: - config: - activate: - on-profile: test -logging: - file: - name: ./logs/tvbox-mv.log - ---- -server: - port: 7777 -spring: - config: - activate: - on-profile: linux -logging: - file: - name: /opt/logs/tvbox-mv.log diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..189020b --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + ${PATTERN} + + + + + + + + + ${ENCODING} + + + + + + ${PATTERN-COLOR} + true + + + + + + ${PATTERN} + true + + + + + + + ./logs/run.log + + + + 7 + ./logs/run.%d{yyyy-MM-dd}.%i.log + + 100MB + + + + + ${PATTERN} + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/tvbox.json b/src/main/resources/static/tvbox.json new file mode 100644 index 0000000..a644872 --- /dev/null +++ b/src/main/resources/static/tvbox.json @@ -0,0 +1,13 @@ +{ + "sites": [ + { + "key": "MV_vod", + "name": "👀┃MV┃视频", + "type": 1, + "api": "http://192.168.31.253:7777/mv/vod", + "searchable": 1, + "quickSearch": 1, + "filterable": 1 + } + ] +} \ No newline at end of file diff --git a/src/test/java/com/leosam/tvbox/mv/TestMainVerticle.java b/src/test/java/com/leosam/tvbox/mv/TestMainVerticle.java new file mode 100644 index 0000000..22cc576 --- /dev/null +++ b/src/test/java/com/leosam/tvbox/mv/TestMainVerticle.java @@ -0,0 +1,22 @@ +package com.leosam.tvbox.mv; + +import io.vertx.core.Vertx; +import io.vertx.junit5.VertxExtension; +import io.vertx.junit5.VertxTestContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(VertxExtension.class) +public class TestMainVerticle { + + @BeforeEach + void deploy_verticle(Vertx vertx, VertxTestContext testContext) { + vertx.deployVerticle(new MainVerticle(), testContext.succeeding(id -> testContext.completeNow())); + } + + @Test + void verticle_deployed(Vertx vertx, VertxTestContext testContext) throws Throwable { + testContext.completeNow(); + } +} diff --git a/src/test/java/com/leosam/tvbox/mv/TvboxMvApplicationTests.java b/src/test/java/com/leosam/tvbox/mv/TvboxMvApplicationTests.java deleted file mode 100644 index 23da0ca..0000000 --- a/src/test/java/com/leosam/tvbox/mv/TvboxMvApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.leosam.tvbox.mv; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class TvboxMvApplicationTests { - - @Test - void contextLoads() { - } - -}