├── .github ├── dependabot.yml └── workflows │ ├── deploy.yml │ └── maven.yml ├── .gitignore ├── .whitesource ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── com └── ghostchu └── simplereloadlib ├── ReloadManager.java ├── ReloadResult.java ├── ReloadStatus.java ├── Reloadable.java └── ReloadableContainer.java /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | # This file copied from EasySQL Project 5 | # see: https://github.com/CarmJos/EasySQL 6 | 7 | name: "Project Deploy" 8 | 9 | on: 10 | # 支持手动触发构建 11 | workflow_dispatch: 12 | release: 13 | # 创建release的时候触发 14 | types: [ published ] 15 | 16 | jobs: 17 | central-deploy: 18 | name: "Deploy Project (Central Repository)" 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: "Set up JDK" 24 | uses: actions/setup-java@v2 25 | with: 26 | java-version: '11' 27 | distribution: 'adopt' 28 | cache: maven 29 | server-id: ossrh 30 | server-username: MAVEN_USERNAME 31 | server-password: MAVEN_PASSWORD 32 | gpg-private-key: ${{ secrets.GPG_SECRET }} # Value of the GPG private key to import 33 | gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase 34 | 35 | - name: "Central Deploy" 36 | run: mvn -B deploy nexus-staging:release --file pom.xml -DskipTests 37 | env: 38 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 39 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 40 | MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSWORD }} 41 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Project Build & Tests 5 | 6 | on: 7 | # 支持手动触发构建 8 | workflow_dispatch: 9 | push: 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: "Set up JDK" 19 | uses: actions/setup-java@v2 20 | with: 21 | java-version: '11' 22 | distribution: 'adopt' 23 | - name: "Package" 24 | run: mvn -B package --file pom.xml -Dmaven.javadoc.skip=true 25 | - name: "Target Stage" 26 | run: mkdir staging && cp target/*.jar staging 27 | - name: "Upload artifact" 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: Artifact 31 | path: staging 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Maven template 3 | target/ 4 | pom.xml.tag 5 | pom.xml.releaseBackup 6 | pom.xml.versionsBackup 7 | pom.xml.next 8 | release.properties 9 | dependency-reduced-pom.xml 10 | buildNumber.properties 11 | .mvn/timing.properties 12 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 13 | .mvn/wrapper/maven-wrapper.jar 14 | 15 | ### Java template 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Mobile Tools for Java (J2ME) 26 | .mtj.tmp/ 27 | 28 | # Package Files # 29 | *.jar 30 | !lib/*.jar 31 | *.war 32 | *.nar 33 | *.ear 34 | *.zip 35 | *.tar.gz 36 | *.rar 37 | 38 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 39 | hs_err_pid* 40 | 41 | .idea 42 | *.iml 43 | 44 | #Eclipse 45 | .settings/ 46 | .classpath 47 | .project 48 | /cache/* 49 | /unit-test.yml 50 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff" 8 | }, 9 | "issueSettings": { 10 | "minSeverityLevel": "LOW" 11 | } 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2022 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleReloadLib 2 | Simple Java generic reload library. 3 | 4 | ## Introduce 5 | SimpleReloadLib isvused to be a part of QuickShop-Reremake. But it is really easy to use and powerful, so I made it to a standalone lib. 6 | You can insert this to any Java project including Bukkit, Paper, BungeeCord, Forge, Fabric etc. 7 | This lib is already used in QuickShop-Reremake over 1 year and no errors were found. 8 | 9 | ## Features 10 | * Reloading mechanism based on registration order, no need to worry about reload order。 11 | * Simple and easy to use, just need to register with eyes closed. 12 | * Lightweight, powered by 5 java classes. You can package it into any project. 13 | * WeakReference to save your memory and prevent memory leaking. 14 | * Support both object and static method. 15 | 16 | ## How to use 17 | 18 | 1. Create a Reload Manager 19 | ```java 20 | ReloadManager reloadManager = new ReloadManager(); 21 | ``` 22 | 23 | 2. Implement Reloadable and register it into ReloadManager (or static method if register for static util) 24 | 25 | ```java 26 | public class Example implements Reloadable { 27 | public Example(){ 28 | Instance.getReloadManager().register(this); 29 | } 30 | 31 | @Override 32 | public ReloadResult reloadModule() throws Exception { 33 | try{ 34 | // Reload code here 35 | return ReloadResult.builder().status(ReloadStatus.SUCCESS).build(); 36 | } catch (IllegalStateException scheduleException) { 37 | return ReloadResult.builder().status(ReloadStatus.SCHEDULED).reason("Resource in use").build(); 38 | } catch (RuntimeException requireRestartException) { 39 | return ReloadResult.builder().status(ReloadStatus.REQUIRE_RESTART).reason("Restart required").build(); 40 | } catch (Exception otherException){ 41 | return ReloadResult.builder().status(ReloadStatus.EXCEPTION).exception(otherException).reason("Unkown error raised").build(); 42 | } 43 | // If there have any Exception not be catched, Manager will catch it and report with ReloadStatus.EXCEPTION 44 | } 45 | } 46 | ``` 47 | 48 | 3. Reload it! 49 | 50 | ```java 51 | Map results = reloadManager.reload(); 52 | Map results = reloadManager.reload(Example.class); 53 | ``` 54 | 55 | ## Registerable reloadables 56 | 57 | * Any classes that implement Reloadable 58 | * Any no args and returns ReloadResult static Method 59 | 60 | ## Get summary 61 | 62 | SimpleReloadLib offers a Map contains ReloadContainer and ReloadResults: 63 | 64 | * SUCCESS - Successfully to reloading. 65 | * OUTDATED - WeakReferenced object already invalid and will be removed in next reload. 66 | * REQUIRE_RESTART - Reload is impossible, restart required. 67 | * SCHEDULED - Cannot reload in this time but already scheduled reloading if possible. 68 | * EXCEPTION - Something just exploded 69 | 70 | ## Maven 71 | 72 | We're in central. 73 | 74 | ```xml 75 | 76 | 77 | com.ghostchu 78 | simplereloadlib 79 | 1.1.2 80 | 81 | 82 | ``` 83 | 84 | ## License 85 | 86 | MIT 87 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | 1.8 9 | ${java.version} 10 | ${java.version} 11 | UTF-8 12 | 13 | 14 | 15 | com.ghostchu 16 | simplereloadlib 17 | 1.1.2 18 | jar 19 | 20 | SimpleReloadLib 21 | 简单但是实用的通用重载库。 22 | https://github.com/Ghost-chu/SimpleReloadLib 23 | 24 | 25 | 26 | Ghost_chu 27 | Ghost chu 28 | 2908803755@qq.com 29 | https://www.ghostchu.com 30 | Asia/Shanghai 31 | 32 | 33 | 34 | 35 | scm:git:git@github.com:Ghost-chu/SimpleReloadLib.git 36 | scm:git:git@github.com:Ghost-chu/SimpleReloadLib.git 37 | https://github.com/Ghost-chu/SimpleReloadLib 38 | HEAD 39 | 40 | 41 | 42 | 43 | The MIT License 44 | https://github.com/Ghost-chu/SimpleReloadLib/blob/master/LICENSE 45 | 46 | 47 | 48 | 49 | GitHub Actions 50 | https://github.com/Ghost-chu/SimpleReloadLib/actions/workflows/maven.yml 51 | 52 | 53 | 54 | 55 | 56 | ossrh 57 | https://s01.oss.sonatype.org/content/repositories/snapshots 58 | 59 | 60 | ossrh 61 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 62 | 63 | 64 | 65 | 66 | 67 | sonatype 68 | https://oss.sonatype.org/content/groups/public/ 69 | 70 | 71 | 72 | 73 | 74 | 75 | org.projectlombok 76 | lombok 77 | 1.18.24 78 | provided 79 | 80 | 81 | 82 | org.jetbrains 83 | annotations 84 | 23.0.0 85 | provided 86 | 87 | 88 | 89 | 90 | 91 | 92 | clean package 93 | 94 | 95 | org.sonatype.plugins 96 | nexus-staging-maven-plugin 97 | 1.6.13 98 | true 99 | 100 | ossrh 101 | https://s01.oss.sonatype.org/ 102 | false 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-compiler-plugin 108 | 3.10.1 109 | 110 | ${java.version} 111 | ${java.version} 112 | UTF-8 113 | -parameters 114 | 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-javadoc-plugin 119 | 3.4.0 120 | 121 | javadoc 122 | false 123 | UTF-8 124 | UTF-8 125 | UTF-8 126 | 127 | 128 | 129 | attach-javadocs 130 | 131 | jar 132 | 133 | 134 | 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-jar-plugin 139 | 3.2.2 140 | 141 | 142 | org.apache.maven.plugins 143 | maven-source-plugin 144 | 3.2.1 145 | 146 | 147 | package 148 | 149 | jar-no-fork 150 | 151 | 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-shade-plugin 157 | 3.3.0 158 | 159 | 160 | package 161 | 162 | shade 163 | 164 | 165 | false 166 | 167 | 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-surefire-plugin 173 | 2.22.2 174 | 175 | false 176 | 177 | 178 | 179 | org.apache.maven.plugins 180 | maven-gpg-plugin 181 | 3.0.1 182 | 183 | 184 | sign-artifacts 185 | verify 186 | 187 | sign 188 | 189 | 190 | 191 | --pinentry-mode 192 | loopback 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | src/main/resources 202 | true 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /src/main/java/com/ghostchu/simplereloadlib/ReloadManager.java: -------------------------------------------------------------------------------- 1 | package com.ghostchu.simplereloadlib; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.lang.ref.WeakReference; 7 | import java.lang.reflect.Method; 8 | import java.util.*; 9 | 10 | /** 11 | * ReloadManager controls modules to reloading while needed 12 | *

13 | * Register order is reloading order preventing unexpected behavior. 14 | */ 15 | public class ReloadManager { 16 | private final List registry = Collections.synchronizedList(new LinkedList<>()); 17 | 18 | /** 19 | * Register a reloadable module into reloading registery 20 | * 21 | * @param reloadable Reloadable module 22 | */ 23 | public synchronized void register(@NotNull Reloadable reloadable) { 24 | unregister(reloadable); 25 | this.registry.add(new ReloadableContainer(new WeakReference<>(reloadable), null)); 26 | } 27 | 28 | /** 29 | * Register a reloadable module into reloading registery 30 | * 31 | * @param reloadMethod Reloadable module 32 | */ 33 | public synchronized void register(@NotNull Method reloadMethod) { 34 | unregister(reloadMethod); 35 | this.registry.add(new ReloadableContainer(null, reloadMethod)); 36 | } 37 | 38 | /** 39 | * Register a reloadable module into reloading registery 40 | * 41 | * @param reloadMethod Reloadable module 42 | */ 43 | public synchronized void unregister(@NotNull Method reloadMethod) { 44 | this.registry.removeIf(reloadableContainer -> { 45 | if (reloadableContainer.getReloadableMethod() != null) { 46 | Method method = reloadableContainer.getReloadableMethod(); 47 | return reloadMethod.equals(method); 48 | } else { 49 | return false; 50 | } 51 | }); 52 | } 53 | 54 | 55 | /** 56 | * Unregister a reloadable module from reloading registry 57 | * 58 | * @param reloadable Reloadable module 59 | */ 60 | public synchronized void unregister(@NotNull Reloadable reloadable) { 61 | this.registry.removeIf(reloadableContainer -> { 62 | if (reloadableContainer != null) { 63 | if (reloadableContainer.getReloadable() != null) { 64 | return Objects.equals(reloadableContainer.getReloadable().get(), reloadable); 65 | } 66 | } 67 | return false; 68 | }); 69 | } 70 | 71 | /** 72 | * Unregister all reloadable modules that same with specific class from reloading registry 73 | * 74 | * @param clazz Class that impl reloadable 75 | */ 76 | public synchronized void unregister(@NotNull Class clazz) { 77 | this.registry.removeIf(reloadable -> { 78 | if (reloadable.getReloadable() != null) { 79 | Reloadable rable = reloadable.getReloadable().get(); 80 | if (rable != null) { 81 | return clazz.equals(rable.getClass()); 82 | } 83 | return false; 84 | } 85 | if (reloadable.getReloadableMethod() != null) { 86 | Method method = reloadable.getReloadableMethod(); 87 | if (method != null) { 88 | return clazz.equals(method.getDeclaringClass()); 89 | } 90 | } 91 | return false; 92 | }); 93 | } 94 | 95 | /** 96 | * Reload all reloadable modules 97 | * 98 | * @return Reloading results 99 | */ 100 | @NotNull 101 | public synchronized Map reload() { 102 | return reload(null); 103 | } 104 | 105 | /** 106 | * Reload all reloadable modules that equals specific class 107 | * 108 | * @param clazz The class that impl reloadable 109 | * @return Reloading results 110 | */ 111 | @NotNull 112 | public synchronized Map reload(@Nullable Class clazz) { 113 | Map reloadResultMap = new LinkedHashMap<>(); 114 | for (ReloadableContainer reloadContainer : new ArrayList<>(this.registry)) { 115 | Reloadable reloadable = null; 116 | Method reloadMethod = null; 117 | if (reloadContainer.getReloadable() != null) 118 | reloadable = reloadContainer.getReloadable().get(); 119 | if (reloadContainer.getReloadableMethod() != null) 120 | reloadMethod = reloadContainer.getReloadableMethod(); 121 | if (reloadable == null && reloadMethod == null) { 122 | reloadResultMap.put(reloadContainer, new ReloadResult(ReloadStatus.OUTDATED, "Container object has been outdated", null)); 123 | this.registry.remove(reloadContainer); 124 | continue; 125 | } 126 | ReloadResult reloadResult = new ReloadResult(ReloadStatus.EXCEPTION, "Incorrect reload object", null); 127 | try { 128 | if (reloadable != null) { 129 | if (clazz != null && !clazz.equals(reloadable.getClass())) { 130 | continue; 131 | } 132 | reloadResult = reloadable.reloadModule(); 133 | } 134 | if (reloadMethod != null) { 135 | if (clazz != null && !clazz.equals(reloadMethod.getDeclaringClass())) { 136 | continue; 137 | } 138 | reloadResult = (ReloadResult) reloadMethod.invoke(null); 139 | } 140 | reloadResultMap.put(reloadContainer, reloadResult); 141 | } catch (Exception e) { 142 | reloadResultMap.put(reloadContainer, new ReloadResult(ReloadStatus.EXCEPTION, "Reloading failed", e)); 143 | } 144 | } 145 | return reloadResultMap; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/ghostchu/simplereloadlib/ReloadResult.java: -------------------------------------------------------------------------------- 1 | package com.ghostchu.simplereloadlib; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | /** 10 | * ReloadResult that contains the result of reloading a plugin. 11 | */ 12 | @Data 13 | @Builder 14 | @AllArgsConstructor 15 | public class ReloadResult { 16 | /** 17 | * Reload status that module returned. 18 | */ 19 | @NotNull 20 | private ReloadStatus status; 21 | /** 22 | * The reason of reloading. (Optional) 23 | */ 24 | @Nullable 25 | private String reason; 26 | /** 27 | * The exception that occurred during reloading. (Optional) 28 | */ 29 | @Nullable 30 | private Throwable exception; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/ghostchu/simplereloadlib/ReloadStatus.java: -------------------------------------------------------------------------------- 1 | package com.ghostchu.simplereloadlib; 2 | 3 | /** 4 | * The reloading status. 5 | */ 6 | public enum ReloadStatus { 7 | /** 8 | * Reload successes 9 | */ 10 | SUCCESS, 11 | /** 12 | * The object that registered now no reference and has been GC by Java. 13 | */ 14 | OUTDATED, 15 | /** 16 | * Reload require the application restart 17 | */ 18 | REQUIRE_RESTART, 19 | /** 20 | * Reload has been scheduled, waiting next call to affect 21 | */ 22 | SCHEDULED, 23 | /** 24 | * Oof, reloading exploded, wtf 25 | */ 26 | EXCEPTION, 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/ghostchu/simplereloadlib/Reloadable.java: -------------------------------------------------------------------------------- 1 | package com.ghostchu.simplereloadlib; 2 | 3 | /** 4 | * Interface that allow class register to ReloadManager 5 | */ 6 | public interface Reloadable { 7 | /** 8 | * Callback for reloading 9 | * 10 | * @return Reloading success 11 | * @throws Exception Throws error if module failed to process reloading (this will turn ReloadStatus to ReloadStatus.EXCEPTION) 12 | */ 13 | default ReloadResult reloadModule() throws Exception { 14 | return ReloadResult.builder().status(ReloadStatus.SUCCESS).build(); 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/ghostchu/simplereloadlib/ReloadableContainer.java: -------------------------------------------------------------------------------- 1 | package com.ghostchu.simplereloadlib; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.lang.ref.WeakReference; 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * This class is used to store the information of a reloadable class. 13 | */ 14 | @Data 15 | @Builder 16 | @AllArgsConstructor 17 | public class ReloadableContainer { 18 | /** 19 | * The class that is reloadable. (Optional) 20 | * While this is null, reloadableMethod must be set. 21 | */ 22 | @Nullable 23 | private WeakReference reloadable; 24 | /** 25 | * The static method with no args and returns a ReloadResult value. (Optional) 26 | * While this is null, reloadable must be set. 27 | */ 28 | @Nullable 29 | private Method reloadableMethod; 30 | 31 | /** 32 | * The reloadable is an object 33 | * @return true = reloadable shouldn't be null, false = reloadableMethod shouldn't be null 34 | */ 35 | public boolean isObject() { 36 | return reloadable != null; 37 | } 38 | } 39 | --------------------------------------------------------------------------------