mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-05-31 17:31:55 +00:00
Shrinking Jar File Size Again (#64)
* Adding back dynamic library support and adding some metrics, velocity first * Removing guava, using caffiene instead * Merge cleanup * Maybe this will get caches working properly now? * Refactored to be more clean and reliable * Fixing bungee compile --------- Co-authored-by: Dawson <dawson@funkemunky.cc>
This commit is contained in:
@@ -9,6 +9,9 @@ import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.depends.LibraryLoader;
|
||||
import dev.brighten.antivpn.depends.MavenLibrary;
|
||||
import dev.brighten.antivpn.depends.Relocate;
|
||||
import dev.brighten.antivpn.message.MessageHandler;
|
||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
@@ -29,6 +32,21 @@ import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.220", relocations = {
|
||||
@Relocate(from ="org" + ".\\h2", to ="dev.brighten.antivpn.shaded.org.h2")})
|
||||
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14", relocations = {
|
||||
@Relocate(from = "com." + "\\mongodb", to = "dev.brighten.antivpn.shaded.com.mongodb"),
|
||||
@Relocate(from = "org" + "\\.bson", to = "dev.brighten.antivpn.shaded.org.bson")
|
||||
})
|
||||
@MavenLibrary(
|
||||
groupId = "com.mysql",
|
||||
artifactId = "mysql-connector-j",
|
||||
version = "9.1.0",
|
||||
relocations = {
|
||||
@Relocate(from = "com.my\\" + "sql.cj", to = "dev.brighten.antivpn.shaded.com.mysql.cj"),
|
||||
@Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc")
|
||||
}
|
||||
)
|
||||
public class AntiVPN {
|
||||
|
||||
private static AntiVPN INSTANCE;
|
||||
@@ -51,6 +69,8 @@ public class AntiVPN {
|
||||
INSTANCE.executor = executor;
|
||||
INSTANCE.playerExecutor = playerExecutor;
|
||||
|
||||
LibraryLoader.loadAll(INSTANCE);
|
||||
|
||||
try {
|
||||
File configFile = new File(pluginFolder, "config.yml");
|
||||
if(!configFile.exists()){
|
||||
@@ -143,7 +163,20 @@ public class AntiVPN {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
executor.onShutdown();
|
||||
if (database instanceof H2VPN) {
|
||||
database.shutdown();
|
||||
|
||||
// Try to deregister driver
|
||||
try {
|
||||
java.sql.Driver driver = java.sql.DriverManager.getDriver("jdbc:h2:");
|
||||
if (driver != null) {
|
||||
java.sql.DriverManager.deregisterDriver(driver);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log but don't throw
|
||||
executor.log("Failed to deregister H2 driver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
VPNExecutor.threadExecutor.shutdown();
|
||||
if(database != null) database.shutdown();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||
@@ -10,11 +8,9 @@ import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class VPNExecutor {
|
||||
@@ -24,23 +20,15 @@ public abstract class VPNExecutor {
|
||||
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
|
||||
@Getter
|
||||
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private final Cache<String, VPNResponse> responseCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
public abstract void registerListeners();
|
||||
|
||||
public abstract void onShutdown();
|
||||
|
||||
public abstract void log(Level level, String log, Object... objects);
|
||||
|
||||
public abstract void log(String log, Object... objects);
|
||||
|
||||
public abstract void logException(String message, Exception ex);
|
||||
public abstract void logException(String message, Throwable ex);
|
||||
|
||||
public void logException(Exception ex) {
|
||||
public void logException(Throwable ex) {
|
||||
logException("An exception occurred: " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
@@ -58,44 +46,31 @@ public abstract class VPNExecutor {
|
||||
return whitelistedIps.contains(ip);
|
||||
}
|
||||
|
||||
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
|
||||
threadExecutor.execute(() -> {
|
||||
if(cachedResults) {
|
||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
|
||||
if(cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
result.accept(responseCache.get(ip, () -> checkIp(ip)));
|
||||
} catch (ExecutionException e) {
|
||||
log("Failed to process checkIp() method! Reason: " + e.getMessage());
|
||||
result.accept(VPNResponse.FAILED_RESPONSE);
|
||||
VPNResponse response = FunkemunkyAPI
|
||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
}
|
||||
} else {
|
||||
result.accept(checkIp(ip));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public VPNResponse checkIp(String ip) {
|
||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
|
||||
if(cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
VPNResponse response = FunkemunkyAPI
|
||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
}
|
||||
}
|
||||
}, threadExecutor);
|
||||
}
|
||||
|
||||
public abstract void disablePlugin();
|
||||
|
||||
@@ -55,28 +55,31 @@ public class LookupCommand extends Command {
|
||||
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[0]);
|
||||
|
||||
if(!player.isPresent()) {
|
||||
if(player.isEmpty()) {
|
||||
return String.format("&cNo player found with the name \"%s\"", args[0]);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().checkIp(player.get().getIp().getHostAddress(),
|
||||
false, result -> {
|
||||
if(!result.isSuccess()) {
|
||||
executor.sendMessage("&cThere was an error trying to find the information of this player.");
|
||||
} else {
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy()
|
||||
? "&a" + result.getMethod() : "&cNo");
|
||||
executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
|
||||
executor.sendMessage("&e%s&8: &f%s", "City", result.getCity());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude()
|
||||
+ "&7/&f" + result.getLongitude());
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
}
|
||||
});
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.checkIp(player.get().getIp().getHostAddress())
|
||||
.thenAccept(result -> {
|
||||
if(!result.isSuccess()) {
|
||||
executor.sendMessage("&cThere was an error trying to find the " +
|
||||
"information of this player.");
|
||||
return;
|
||||
}
|
||||
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy()
|
||||
? "&a" + result.getMethod() : "&cNo");
|
||||
executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
|
||||
executor.sendMessage("&e%s&8: &f%s", "City", result.getCity());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude()
|
||||
+ "&7/&f" + result.getLongitude());
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
});
|
||||
|
||||
|
||||
return "&7Looking up the IP information for player " + player.get().getName() + "...";
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package dev.brighten.antivpn.database.local;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
@@ -20,6 +22,12 @@ import java.util.function.Consumer;
|
||||
|
||||
public class H2VPN implements VPNDatabase {
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
|
||||
public H2VPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
@@ -41,29 +49,26 @@ public class H2VPN implements VPNDatabase {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed())
|
||||
return Optional.empty();
|
||||
|
||||
ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery();
|
||||
|
||||
try {
|
||||
if (rs != null && rs.next()) {
|
||||
VPNResponse response = new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
|
||||
if(System.currentTimeMillis() - response.getLastAccess() > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return Optional.empty();
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
try(ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).
|
||||
executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
return new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
}
|
||||
return Optional.of(response);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting a response for "
|
||||
+ ip, e);
|
||||
}
|
||||
} catch (SQLException throwables) {
|
||||
throwables.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Optional.empty();
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -80,6 +85,8 @@ public class H2VPN implements VPNDatabase {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
||||
@@ -198,13 +205,12 @@ public class H2VPN implements VPNDatabase {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery();
|
||||
|
||||
try {
|
||||
try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
@@ -271,6 +277,7 @@ public class H2VPN implements VPNDatabase {
|
||||
public void shutdown() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
|
||||
MySQL.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package dev.brighten.antivpn.database.mongo;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.mongodb.*;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
@@ -24,6 +26,11 @@ public class MongoVPN implements VPNDatabase {
|
||||
private MongoCollection<Document> settingsDocument, cacheDocument;
|
||||
private MongoClient client;
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
public MongoVPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
@@ -41,32 +48,37 @@ public class MongoVPN implements VPNDatabase {
|
||||
}
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
||||
|
||||
if(rdoc != null) {
|
||||
long lastUpdate = rdoc.get("lastAccess", 0L);
|
||||
if(rdoc != null) {
|
||||
long lastUpdate = rdoc.get("lastAccess", 0L);
|
||||
|
||||
if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return Optional.empty();
|
||||
if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
return VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
|
||||
.countryName(rdoc.getString("countryName"))
|
||||
.countryCode(rdoc.getString("countryCode"))
|
||||
.city(rdoc.getString("city"))
|
||||
.isp(rdoc.getString("isp"))
|
||||
.method(rdoc.getString("method"))
|
||||
.timeZone(rdoc.getString("timeZone"))
|
||||
.proxy(rdoc.getBoolean("proxy"))
|
||||
.cached(rdoc.getBoolean("cached"))
|
||||
.success(true)
|
||||
.latitude(rdoc.getDouble("latitude"))
|
||||
.longitude(rdoc.getDouble("longitude"))
|
||||
.lastAccess(rdoc.get("lastAccess", 0L))
|
||||
.build();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
|
||||
.countryName(rdoc.getString("countryName"))
|
||||
.countryCode(rdoc.getString("countryCode"))
|
||||
.city(rdoc.getString("city"))
|
||||
.isp(rdoc.getString("isp"))
|
||||
.method(rdoc.getString("method"))
|
||||
.timeZone(rdoc.getString("timeZone"))
|
||||
.proxy(rdoc.getBoolean("proxy"))
|
||||
.cached(rdoc.getBoolean("cached"))
|
||||
.success(true)
|
||||
.latitude(rdoc.getDouble("latitude"))
|
||||
.longitude(rdoc.getDouble("longitude"))
|
||||
.lastAccess(rdoc.get("lastAccess", 0L))
|
||||
.build());
|
||||
}
|
||||
return Optional.empty();
|
||||
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,6 +99,8 @@ public class MongoVPN implements VPNDatabase {
|
||||
rdoc.put("longitude", toCache.getLongitude());
|
||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
Bson update = new Document("$set", rdoc);
|
||||
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package dev.brighten.antivpn.database.sql;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
@@ -20,6 +22,12 @@ import java.util.function.Consumer;
|
||||
|
||||
public class MySqlVPN implements VPNDatabase {
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
|
||||
public MySqlVPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
@@ -41,30 +49,34 @@ public class MySqlVPN implements VPNDatabase {
|
||||
if (isDisabled())
|
||||
return Optional.empty();
|
||||
|
||||
ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery();
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
try(ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)
|
||||
.executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
VPNResponse responseFromDoc = new VPNResponse(rs.getString("asn"),
|
||||
rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
|
||||
try {
|
||||
if (rs != null && rs.next()) {
|
||||
VPNResponse response = new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
if(System.currentTimeMillis() - responseFromDoc.getLastAccess() > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
if(System.currentTimeMillis() - response.getLastAccess() > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return Optional.empty();
|
||||
return responseFromDoc;
|
||||
}
|
||||
|
||||
return Optional.of(response);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("Failed to get response from cache due to SQL error for: " + ip, e);
|
||||
}
|
||||
} catch (SQLException throwables) {
|
||||
throwables.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Optional.empty();
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -81,6 +93,8 @@ public class MySqlVPN implements VPNDatabase {
|
||||
if (isDisabled())
|
||||
return;
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
||||
@@ -103,10 +117,10 @@ public class MySqlVPN implements VPNDatabase {
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (isDisabled())
|
||||
return false;
|
||||
ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery();
|
||||
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
try(ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery()) {
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -114,11 +128,10 @@ public class MySqlVPN implements VPNDatabase {
|
||||
public boolean isWhitelisted(String ip) {
|
||||
if (isDisabled())
|
||||
return false;
|
||||
ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1")
|
||||
.append(ip).executeQuery();
|
||||
|
||||
|
||||
return set != null && set.next() && set.getString("ip") != null;
|
||||
try(ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1")
|
||||
.append(ip).executeQuery()) {
|
||||
return set != null && set.next() && set.getString("ip") != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,13 +212,13 @@ public class MySqlVPN implements VPNDatabase {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery();
|
||||
|
||||
try {
|
||||
try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("Failed to get alerts state from database for: " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
@@ -247,18 +260,16 @@ public class MySqlVPN implements VPNDatabase {
|
||||
AntiVPN.getInstance().getExecutor().log("Creating tables...");
|
||||
|
||||
//Running check for old table types to update
|
||||
oldTableCheck: {
|
||||
Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';").execute(set -> {
|
||||
if(set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||
AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " +
|
||||
"Dropping table and creating a new one...");
|
||||
if(Query.prepare("drop table `responses`").execute() > 0) {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';").execute(set -> {
|
||||
if(set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||
AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " +
|
||||
"Dropping table and creating a new one...");
|
||||
if(Query.prepare("drop table `responses`").execute() > 0) {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute();
|
||||
Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)").execute();
|
||||
@@ -277,15 +288,13 @@ public class MySqlVPN implements VPNDatabase {
|
||||
" AND table_name='whitelisted' AND index_name='uuid_1';";
|
||||
ResultSet rs = Query.prepare(query).executeQuery();
|
||||
int id = 0;
|
||||
whitelistedIndex: {
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute();
|
||||
}
|
||||
id = 0;
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute();
|
||||
}
|
||||
id = 0;
|
||||
responsesIndex: {
|
||||
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " +
|
||||
"AND table_name='responses' AND index_name='ip_1';";
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.NonnullByDefault;
|
||||
import dev.brighten.antivpn.utils.Supplier;
|
||||
import dev.brighten.antivpn.utils.Suppliers;
|
||||
import lombok.Getter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.commons.ClassRemapper;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
/**
|
||||
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
|
||||
* into the classloader.
|
||||
*/
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
@NonnullByDefault
|
||||
public final class LibraryLoader {
|
||||
|
||||
@SuppressWarnings("Guava")
|
||||
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = Suppliers.memoize(() ->
|
||||
URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()));
|
||||
|
||||
public static void loadAll(Object object) {
|
||||
loadAll(object.getClass());
|
||||
}
|
||||
|
||||
public static void loadAll(Class<?> clazz) {
|
||||
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
|
||||
|
||||
for (MavenLibrary lib : libs) {
|
||||
// Create relocations map if any are defined
|
||||
Map<String, String> relocations = new HashMap<>();
|
||||
for (Relocate relocate : lib.relocations()) {
|
||||
relocations.put(relocate.from().replace("\\", ""), relocate.to());
|
||||
}
|
||||
|
||||
load(lib.groupId().replace("\\", ""), lib.artifactId(), lib.version(), lib.repo().url(), relocations);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(String groupId, String artifactId, String version, String repoUrl,
|
||||
Map<String, String> relocations) {
|
||||
load(new Dependency(groupId, artifactId, version, repoUrl), relocations);
|
||||
}
|
||||
|
||||
public static void load(Dependency d, Map<String, String> relocations) {
|
||||
System.out.printf("Loading dependency %s:%s:%s from %s%n",
|
||||
d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl());
|
||||
String name = d.getArtifactId() + "-" + d.getVersion();
|
||||
|
||||
// If we have relocations, add a suffix to identify the relocated version
|
||||
String fileName = name + ".jar";
|
||||
if (!relocations.isEmpty()) {
|
||||
fileName = name + "-relocated.jar";
|
||||
}
|
||||
|
||||
File saveLocation = new File(getLibFolder(), fileName);
|
||||
File originalJar = new File(getLibFolder(), name + ".jar");
|
||||
|
||||
// Download the original jar if it doesn't exist
|
||||
if (!originalJar.exists()) {
|
||||
try {
|
||||
System.out.println("Dependency '" + name +
|
||||
"' is not already in the libraries folder. Attempting to download...");
|
||||
URL url = d.getUrl();
|
||||
|
||||
try (InputStream is = url.openStream()) {
|
||||
Files.copy(is, originalJar.toPath());
|
||||
}
|
||||
System.out.println("Dependency '" + name + "' successfully downloaded.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Unable to download dependency: " + d, e);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have relocations, create a relocated jar
|
||||
if (!relocations.isEmpty() && !saveLocation.exists()) {
|
||||
try {
|
||||
System.out.println("Relocating packages for " + name + "...");
|
||||
relocateJar(originalJar, saveLocation, relocations);
|
||||
System.out.println("Successfully relocated packages for " + name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Failed to relocate packages for dependency: " + d, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the appropriate jar (original or relocated)
|
||||
File jarToLoad = relocations.isEmpty() ? originalJar : saveLocation;
|
||||
|
||||
if (!jarToLoad.exists()) {
|
||||
throw new RuntimeException("Unable to find dependency jar: " + jarToLoad.getAbsolutePath());
|
||||
}
|
||||
|
||||
try {
|
||||
URL_INJECTOR.get().addURL(jarToLoad.toURI().toURL());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to load dependency: " + jarToLoad, e);
|
||||
}
|
||||
|
||||
System.out.println("Loaded dependency '" + name + "' successfully.");
|
||||
}
|
||||
|
||||
private static void relocateJar(File sourceJar, File targetJar, Map<String, String> relocations)
|
||||
throws IOException {
|
||||
// Track service files to avoid duplicates
|
||||
Map<String, StringBuilder> serviceFiles = new HashMap<>();
|
||||
|
||||
try (JarFile jar = new JarFile(sourceJar);
|
||||
JarOutputStream jos = new JarOutputStream(Files.newOutputStream(targetJar.toPath()))) {
|
||||
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
|
||||
// Skip directories
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
if (name.startsWith("META-INF/services/")) {
|
||||
// Process service files but don't write yet
|
||||
processServiceFile(name, is, serviceFiles, relocations);
|
||||
} else if (name.endsWith(".class")) {
|
||||
// Relocate class file path as well as content
|
||||
String relocatedPath = relocateClassPath(name, relocations);
|
||||
|
||||
JarEntry newEntry = new JarEntry(relocatedPath);
|
||||
jos.putNextEntry(newEntry);
|
||||
|
||||
byte[] classBytes = readAllBytes(is);
|
||||
byte[] relocatedBytes = relocateClass(classBytes, relocations);
|
||||
jos.write(relocatedBytes);
|
||||
jos.closeEntry();
|
||||
} else {
|
||||
// Copy other files as-is
|
||||
JarEntry newEntry = new JarEntry(name);
|
||||
jos.putNextEntry(newEntry);
|
||||
copyStream(is, jos);
|
||||
jos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now write all service files after processing
|
||||
for (Map.Entry<String, StringBuilder> entry : serviceFiles.entrySet()) {
|
||||
try {
|
||||
JarEntry serviceEntry = new JarEntry(entry.getKey());
|
||||
jos.putNextEntry(serviceEntry);
|
||||
jos.write(entry.getValue().toString().getBytes());
|
||||
jos.closeEntry();
|
||||
} catch (Exception e) {
|
||||
// Log but continue with other service files
|
||||
System.out.println("Warning: Could not write service file " +
|
||||
entry.getKey() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processServiceFile(String name, InputStream is,
|
||||
Map<String, StringBuilder> serviceFiles,
|
||||
Map<String, String> relocations) throws IOException {
|
||||
// Read service file content
|
||||
String content = new String(readAllBytes(is));
|
||||
StringBuilder contentBuilder = serviceFiles.computeIfAbsent(name, k -> new StringBuilder());
|
||||
|
||||
// Process and relocate service implementations
|
||||
for (String line : content.split("\n")) {
|
||||
String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
if (trimmed.startsWith(relocation.getKey())) {
|
||||
trimmed = relocation.getValue() +
|
||||
trimmed.substring(relocation.getKey().length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
contentBuilder.append(trimmed).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] relocateClass(byte[] classBytes, Map<String, String> relocations) {
|
||||
try {
|
||||
// Convert to slash notation for ASM
|
||||
Remapper prefixRemapper = getPrefixRemapper(relocations);
|
||||
|
||||
// Create custom ClassWriter to handle missing classes
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
protected String getCommonSuperClass(String type1, String type2) {
|
||||
try {
|
||||
return super.getCommonSuperClass(type1, type2);
|
||||
} catch (RuntimeException e) {
|
||||
// Fall back to Object when classes can't be loaded
|
||||
return "java/lang/Object";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ClassVisitor visitor = new ClassRemapper(writer, prefixRemapper);
|
||||
|
||||
// Process class with remapper
|
||||
reader.accept(visitor, ClassReader.EXPAND_FRAMES);
|
||||
|
||||
return writer.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return classBytes;
|
||||
}
|
||||
}
|
||||
|
||||
private static Remapper getPrefixRemapper(Map<String, String> relocations) {
|
||||
Map<String, String> slashMappings = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||
String fromSlash = entry.getKey().replace('.', '/');
|
||||
String toSlash = entry.getValue().replace('.', '/');
|
||||
slashMappings.put(fromSlash, toSlash);
|
||||
}
|
||||
|
||||
// Create customized remapper for package prefixes
|
||||
return new Remapper() {
|
||||
@Override
|
||||
public String map(String typeName) {
|
||||
if (typeName == null) return null;
|
||||
|
||||
for (Map.Entry<String, String> entry : slashMappings.entrySet()) {
|
||||
String from = entry.getKey();
|
||||
String to = entry.getValue();
|
||||
|
||||
if (typeName.startsWith(from)) {
|
||||
return to + typeName.substring(from.length());
|
||||
}
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String relocateClassPath(String path, Map<String, String> relocations) {
|
||||
// Convert path to package format (replacing / with .)
|
||||
String packagePath = path.substring(0, path.length() - 6).replace('/', '.');
|
||||
|
||||
// Apply relocations
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
if (packagePath.startsWith(relocation.getKey())) {
|
||||
packagePath = relocation.getValue() + packagePath.substring(relocation.getKey().length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to path format
|
||||
return packagePath.replace('.', '/') + ".class";
|
||||
}
|
||||
|
||||
private static byte[] readAllBytes(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int bytesRead;
|
||||
byte[] data = new byte[1024];
|
||||
while ((bytesRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, bytesRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
private static void copyStream(InputStream is, OutputStream os) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
private static File getLibFolder() {
|
||||
File pluginDataFolder = AntiVPN.getInstance().getPluginFolder();
|
||||
File libs = new File(pluginDataFolder, "libraries");
|
||||
if(libs.mkdirs()) {
|
||||
System.out.println("Created libraries folder!");
|
||||
}
|
||||
return libs;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@NonnullByDefault
|
||||
// Fix the Dependency class to preserve original groupId for downloading
|
||||
public static final class Dependency {
|
||||
private final String groupId;
|
||||
private final String artifactId;
|
||||
private final String version;
|
||||
private final String repoUrl;
|
||||
// Keep the original groupId/artifactId for Maven downloads
|
||||
private final String originalGroupId;
|
||||
private final String originalArtifactId;
|
||||
|
||||
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
|
||||
this.originalGroupId = Objects.requireNonNull(groupId, "groupId");
|
||||
this.originalArtifactId = Objects.requireNonNull(artifactId, "artifactId");
|
||||
this.groupId = this.originalGroupId;
|
||||
this.artifactId = this.originalArtifactId;
|
||||
this.version = Objects.requireNonNull(version, "version");
|
||||
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
|
||||
}
|
||||
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
String repo = this.repoUrl;
|
||||
if (!repo.endsWith("/")) {
|
||||
repo += "/";
|
||||
}
|
||||
repo += "%s/%s/%s/%s-%s.jar";
|
||||
|
||||
// Always use original groupId for Maven repository URL
|
||||
String url = String.format(repo, this.originalGroupId.replace(".", "/"),
|
||||
this.originalArtifactId, this.version, this.originalArtifactId, this.version);
|
||||
return new URL(url);
|
||||
}
|
||||
|
||||
// Rest of the class unchanged
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate the required libraries for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibraries {
|
||||
|
||||
MavenLibrary[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate a required library for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Repeatable(MavenLibraries.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibrary {
|
||||
|
||||
/**
|
||||
* The group id of the library
|
||||
*
|
||||
* @return the group id of the library
|
||||
*/
|
||||
String groupId();
|
||||
|
||||
/**
|
||||
* The artifact id of the library
|
||||
*
|
||||
* @return the artifact id of the library
|
||||
*/
|
||||
String artifactId();
|
||||
|
||||
/**
|
||||
* The version of the library
|
||||
*
|
||||
* @return the version of the library
|
||||
*/
|
||||
String version();
|
||||
|
||||
/**
|
||||
* The repo where the library can be obtained from
|
||||
*
|
||||
* @return the repo where the library can be obtained from
|
||||
*/
|
||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||
|
||||
Relocate[] relocations() default {}; // Add this line
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
public @interface Relocate {
|
||||
String from();
|
||||
String to();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Represents a maven repository.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.LOCAL_VARIABLE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Repository {
|
||||
|
||||
/**
|
||||
* Gets the base url of the repository.
|
||||
*
|
||||
* @return the base url of the repository
|
||||
*/
|
||||
String url();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provides access to {@link URLClassLoader}#addURL.
|
||||
*/
|
||||
public abstract class URLClassLoaderAccess {
|
||||
|
||||
/**
|
||||
* Creates a {@link URLClassLoaderAccess} for the given class loader.
|
||||
*
|
||||
* @param classLoader the class loader
|
||||
* @return the access object
|
||||
*/
|
||||
static URLClassLoaderAccess create(URLClassLoader classLoader) {
|
||||
if (Reflection.isSupported()) {
|
||||
return new Reflection(classLoader);
|
||||
} else if (Unsafe.isSupported()) {
|
||||
return new Unsafe(classLoader);
|
||||
} else {
|
||||
return Noop.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private final URLClassLoader classLoader;
|
||||
|
||||
protected URLClassLoaderAccess(URLClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the given URL to the class loader.
|
||||
*
|
||||
* @param url the URL to add
|
||||
*/
|
||||
public abstract void addURL(URL url);
|
||||
|
||||
/**
|
||||
* Accesses using reflection, not supported on Java 9+.
|
||||
*/
|
||||
private static class Reflection extends URLClassLoaderAccess {
|
||||
private static final Method ADD_URL_METHOD;
|
||||
|
||||
static {
|
||||
Method addUrlMethod;
|
||||
try {
|
||||
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
addUrlMethod.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
addUrlMethod = null;
|
||||
}
|
||||
ADD_URL_METHOD = addUrlMethod;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return ADD_URL_METHOD != null;
|
||||
}
|
||||
|
||||
Reflection(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
try {
|
||||
ADD_URL_METHOD.invoke(super.classLoader, url);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accesses using sun.misc.Unsafe, supported on Java 9+.
|
||||
*
|
||||
* @author Vaishnav Anil (<a href="https://github.com/slimjar/slimjar">...</a>)
|
||||
*/
|
||||
private static class Unsafe extends URLClassLoaderAccess {
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
|
||||
static {
|
||||
sun.misc.Unsafe unsafe;
|
||||
try {
|
||||
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
|
||||
} catch (Throwable t) {
|
||||
unsafe = null;
|
||||
}
|
||||
UNSAFE = unsafe;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return UNSAFE != null;
|
||||
}
|
||||
|
||||
private final Collection<URL> unopenedURLs;
|
||||
private final Collection<URL> pathURLs;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Unsafe(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
Collection<URL> unopenedURLs;
|
||||
Collection<URL> pathURLs;
|
||||
try {
|
||||
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
|
||||
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
|
||||
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
|
||||
} catch (Throwable e) {
|
||||
unopenedURLs = null;
|
||||
pathURLs = null;
|
||||
}
|
||||
this.unopenedURLs = unopenedURLs;
|
||||
this.pathURLs = pathURLs;
|
||||
}
|
||||
|
||||
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
return UNSAFE.getObject(object, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
this.unopenedURLs.add(url);
|
||||
this.pathURLs.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Noop extends URLClassLoaderAccess {
|
||||
private static final Noop INSTANCE = new Noop();
|
||||
|
||||
private Noop() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Guava 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
|
||||
*
|
||||
* http://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 dev.brighten.antivpn.utils;
|
||||
|
||||
/**
|
||||
* Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular
|
||||
* version.
|
||||
*/
|
||||
abstract class ExtraObjectsMethodsForWeb {}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NonnullByDefault {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Guava 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
|
||||
*
|
||||
* http://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 dev.brighten.antivpn.utils;
|
||||
|
||||
/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */
|
||||
final class NullnessCasts {
|
||||
/**
|
||||
* Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that
|
||||
* that conversion is safe.
|
||||
*
|
||||
* <p>This method is intended to help with usages of type parameters that have {
|
||||
* ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null
|
||||
* types (or if the type is a non-variable type, like {@code String}), then code should almost
|
||||
* never use this method, preferring instead to call {@code requireNonNull} so as to benefit from
|
||||
* its runtime check.
|
||||
*
|
||||
* <p>An example use case for this method is in implementing an {@code Iterator<T>} whose {@code
|
||||
* next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the
|
||||
* code would be responsible for populating a "real" {@code T} (which might still be the value
|
||||
* {@code null}!) before returning it to callers. Depending on how the code is structured, a
|
||||
* nullness analysis might not understand that the field has been populated. To avoid that problem
|
||||
* without having to add {@code @SuppressWarnings}, the code can call this method.
|
||||
*
|
||||
* <p>Why <i>not</i> just add {@code SuppressWarnings}? The problem is that this method is
|
||||
* typically useful for {@code return} statements. That leaves the code with two options: Either
|
||||
* add the suppression to the whole method (which turns off checking for a large section of code),
|
||||
* or extract a variable, and put the suppression on that. However, a local variable typically
|
||||
* doesn't work: Because nullness analyses typically infer the nullness of local variables,
|
||||
* there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the
|
||||
* analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}.
|
||||
* (Even if supported added {@code @NonNull}, that would not help, since the problem case
|
||||
* addressed by this method is the case in which {@code T} has parametric nullness -- and thus its
|
||||
* value may be legitimately {@code null}.)
|
||||
*/
|
||||
@SuppressWarnings("nullness")
|
||||
static <T> T uncheckedCastNullableTToT(T t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
private NullnessCasts() {}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public final class Preconditions {
|
||||
private Preconditions() {
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference, Object errorMessage) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(String.valueOf(errorMessage));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
static String format(String template, Object... args) {
|
||||
template = String.valueOf(template);
|
||||
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
|
||||
int templateStart = 0;
|
||||
|
||||
int i;
|
||||
int placeholderStart;
|
||||
for(i = 0; i < args.length; templateStart = placeholderStart + 2) {
|
||||
placeholderStart = template.indexOf("%s", templateStart);
|
||||
if (placeholderStart == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, placeholderStart);
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, template.length());
|
||||
if (i < args.length) {
|
||||
builder.append(" [");
|
||||
builder.append(args[i++]);
|
||||
|
||||
while(i < args.length) {
|
||||
builder.append(", ");
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(']');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Supplier<T> extends java.util.function.Supplier<T> {
|
||||
T get();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Guava 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
|
||||
*
|
||||
* http://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 dev.brighten.antivpn.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
|
||||
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Useful suppliers.
|
||||
*
|
||||
* <p>All methods return serializable suppliers as long as they're given serializable parameters.
|
||||
*
|
||||
* @author Laurence Gonsalves
|
||||
* @author Harry Heymann
|
||||
* @since 2.0
|
||||
*/
|
||||
public final class Suppliers {
|
||||
private Suppliers() {}
|
||||
|
||||
/**
|
||||
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
|
||||
* and returns that value on subsequent calls to {@code get()}. See: <a
|
||||
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||
*
|
||||
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
|
||||
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
|
||||
* form does not contain the cached value, which will be recalculated when {@code get()} is called
|
||||
* on the reserialized instance.
|
||||
*
|
||||
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
|
||||
* delegating calls until it returns valid data.
|
||||
*
|
||||
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
|
||||
* returned directly.
|
||||
*/
|
||||
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
|
||||
if (delegate instanceof NonSerializableMemoizingSupplier
|
||||
|| delegate instanceof MemoizingSupplier) {
|
||||
return delegate;
|
||||
}
|
||||
return delegate instanceof Serializable
|
||||
? new MemoizingSupplier<>(delegate)
|
||||
: new NonSerializableMemoizingSupplier<>(delegate);
|
||||
}
|
||||
|
||||
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> delegate;
|
||||
transient volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
transient T value;
|
||||
|
||||
MemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
T t = delegate.get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suppliers.memoize("
|
||||
+ (initialized ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
||||
volatile Supplier<T> delegate;
|
||||
volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
T value;
|
||||
|
||||
NonSerializableMemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
/*
|
||||
* requireNonNull is safe because we read and write `delegate` under synchronization.
|
||||
*
|
||||
* TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton
|
||||
* `Supplier` that always throws an exception.
|
||||
*/
|
||||
T t = requireNonNull(delegate).get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
// Release the delegate to GC.
|
||||
delegate = null;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Supplier<T> delegate = this.delegate;
|
||||
return "Suppliers.memoize("
|
||||
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user