diff --git a/app/pom.xml b/app/pom.xml
index 8a3b0d0..0fcf3dc 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -23,6 +23,15 @@
0.1.0.0
compile
+
+
+
+
+
+ org.eclipse.jgit
+ org.eclipse.jgit
+ 7.3.0.202506031305-r
+
\ No newline at end of file
diff --git a/app/src/main/java/ru/kirillius/pf/sdn/App.java b/app/src/main/java/ru/kirillius/pf/sdn/App.java
index a1a465b..f2a34ed 100644
--- a/app/src/main/java/ru/kirillius/pf/sdn/App.java
+++ b/app/src/main/java/ru/kirillius/pf/sdn/App.java
@@ -1,62 +1,31 @@
package ru.kirillius.pf.sdn;
-import lombok.Getter;
-import org.eclipse.jetty.server.Server;
-import ru.kirillius.pf.sdn.External.API.HENetBGPInfoProvider;
-import ru.kirillius.pf.sdn.core.Auth.AuthManager;
-import ru.kirillius.pf.sdn.core.Config;
-import ru.kirillius.pf.sdn.core.Context;
-import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInformationService;
-import ru.kirillius.pf.sdn.core.Networking.NetworkManager;
+import ru.kirillius.utils.logging.SystemLogger;
import java.io.File;
import java.io.IOException;
+import java.util.Collections;
+import java.util.logging.Level;
-public class App implements Context {
- public static void main(String[] args) {
- new App();
- }
-
+public class App extends AppContext {
private final static File configFile = new File("config.json");
- public App() {
- try {
- config = Config.load(configFile);
- } catch (IOException e) {
- config = new Config();
- try {
- Config.store(config, configFile);
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- authManager = new AuthManager(this);
- server = new Server();
- autonomousSystemInformationService = new AutonomousSystemInformationService();
- autonomousSystemInformationService.setProvider(new HENetBGPInfoProvider());
- networkManager = new NetworkManager(this);
- var inputResources = networkManager.getInputResources();
- inputResources.add(config.getCustomResources());
- networkManager.triggerUpdate();
-
- while (networkManager.isUpdatingNow()) {
- Thread.yield();
- }
-
- return;
+ public App(File configFile) {
+ super(configFile);
}
- @Getter
- private final NetworkManager networkManager;
- @Getter
- private Config config;
- @Getter
- private final AuthManager authManager;
- @Getter
- private final Server server;
- @Getter
- private final AutonomousSystemInformationService autonomousSystemInformationService;
+ static {
+ SystemLogger.initializeLogging(Level.INFO, Collections.emptyList());
+ }
+
+ public static void main(String[] args) {
+
+ try (App app = new App(configFile)) {
+
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/app/src/main/java/ru/kirillius/pf/sdn/AppContext.java b/app/src/main/java/ru/kirillius/pf/sdn/AppContext.java
new file mode 100644
index 0000000..2b3ab90
--- /dev/null
+++ b/app/src/main/java/ru/kirillius/pf/sdn/AppContext.java
@@ -0,0 +1,66 @@
+package ru.kirillius.pf.sdn;
+
+import lombok.Getter;
+import org.eclipse.jetty.server.Server;
+import ru.kirillius.pf.sdn.External.API.HEInfoProvider;
+import ru.kirillius.pf.sdn.core.Auth.AuthManager;
+import ru.kirillius.pf.sdn.core.Config;
+import ru.kirillius.pf.sdn.core.Context;
+import ru.kirillius.pf.sdn.core.ContextEventsHandler;
+import ru.kirillius.pf.sdn.core.Networking.ASInfoService;
+import ru.kirillius.pf.sdn.core.Networking.NetworkManager;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+
+public class AppContext implements Context, Closeable {
+
+ public AppContext(File configFile) {
+ try {
+ config = Config.load(configFile);
+ } catch (IOException e) {
+ config = new Config();
+ try {
+ Config.store(config, configFile);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ authManager = new AuthManager(this);
+ server = new Server();
+ ASInfoService = new ASInfoService();
+ ASInfoService.setProvider(new HEInfoProvider(this));
+ networkManager = new NetworkManager(this);
+ networkManager.getInputResources().add(config.getCustomResources());
+ }
+
+
+ @Getter
+ private final NetworkManager networkManager;
+ @Getter
+ private Config config;
+ @Getter
+ private final AuthManager authManager;
+ @Getter
+ private final Server server;
+ @Getter
+ private final ASInfoService ASInfoService;
+
+ @Override
+ public void close() throws IOException {
+ ASInfoService.close();
+ networkManager.close();
+ try {
+ server.stop();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Getter
+ private final ContextEventsHandler EventsHandler = new ContextEventsHandler();
+
+
+}
diff --git a/app/src/main/java/ru/kirillius/pf/sdn/External/API/GitSubscription.java b/app/src/main/java/ru/kirillius/pf/sdn/External/API/GitSubscription.java
new file mode 100644
index 0000000..e29be84
--- /dev/null
+++ b/app/src/main/java/ru/kirillius/pf/sdn/External/API/GitSubscription.java
@@ -0,0 +1,119 @@
+package ru.kirillius.pf.sdn.External.API;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import ru.kirillius.pf.sdn.core.Context;
+import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
+import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig;
+import ru.kirillius.pf.sdn.core.Subscription.SubscriptionProvider;
+import ru.kirillius.pf.sdn.core.Util.HashUtil;
+import ru.kirillius.utils.logging.SystemLogger;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+public class GitSubscription implements SubscriptionProvider {
+ private final Context context;
+
+ public GitSubscription(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public Map getResources(RepositoryConfig config) {
+ try {
+ var repoDir = new File(context.getConfig().getCacheDirectory(), "git/" + HashUtil.md5(config.getName()));
+
+ if (!repoDir.exists()) {
+ if (!repoDir.mkdirs()) {
+ throw new IOException("Unable to create directory: " + repoDir.getAbsolutePath());
+ }
+ }
+
+ var repository = isGitRepository(repoDir) ? openRepository(repoDir) : cloneRepository(config.getSource(), repoDir);
+
+
+ SystemLogger.message("Fetching git repository " + config.getName(), CTX);
+ checkAndPullUpdates(repository);
+
+ repository.close();
+ return Map.of();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private static void checkAndPullUpdates(Git git) throws GitAPIException {
+
+
+
+
+
+ var fetchResult = git.fetch()
+ .setCheckFetchedObjects(true)
+ .call();
+
+ if (fetchResult.getTrackingRefUpdates() != null && !fetchResult.getTrackingRefUpdates().isEmpty()) {
+
+
+
+ SystemLogger.message("Downloading updates...", CTX);
+
+ // Выполняем pull чтобы получить изменения
+ var pullResult = git.pull().call();
+
+ if (pullResult.isSuccessful()) {
+ System.out.println("✅ Обновление успешно завершено!");
+
+ // Проверяем были ли обновлены файлы
+ if (pullResult.getFetchResult() != null &&
+ !pullResult.getFetchResult().getTrackingRefUpdates().isEmpty()) {
+
+ System.out.println("📁 Были обновлены файлы:");
+ pullResult.getFetchResult().getTrackingRefUpdates().forEach(refUpdate -> {
+ System.out.println(" - " + refUpdate.getLocalName() +
+ " : " + refUpdate.getOldObjectId().abbreviate(7).name() +
+ " -> " + refUpdate.getNewObjectId().abbreviate(7).name());
+ });
+ }
+ } else {
+ System.out.println("❌ Ошибка при обновлении репозитория");
+ }
+ } else {
+ System.out.println("✅ Репозиторий уже актуален, обновлений нет");
+ }
+
+ }
+
+ private final static String CTX = GitSubscription.class.getSimpleName();
+
+ private static Git cloneRepository(String REPO_URL, File path) throws GitAPIException {
+ SystemLogger.message("Cloning repository " + REPO_URL, CTX);
+ return Git.cloneRepository()
+ .setURI(REPO_URL)
+ .setDirectory(path)
+ .setCloneAllBranches(true)
+ .call();
+ }
+
+ private static Git openRepository(File repoDir) throws IOException {
+ var builder = new FileRepositoryBuilder();
+ var repository = builder.setGitDir(new File(repoDir, ".git"))
+ .readEnvironment()
+ .findGitDir()
+ .build();
+ return new Git(repository);
+ }
+
+ private static boolean isGitRepository(File directory) {
+ if (!directory.exists() || !directory.isDirectory()) {
+ return false;
+ }
+
+ var gitDir = new File(directory, ".git");
+ return gitDir.exists() && gitDir.isDirectory();
+ }
+}
diff --git a/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java b/app/src/main/java/ru/kirillius/pf/sdn/External/API/HEInfoProvider.java
similarity index 83%
rename from app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java
rename to app/src/main/java/ru/kirillius/pf/sdn/External/API/HEInfoProvider.java
index ec76931..65fc689 100644
--- a/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java
+++ b/app/src/main/java/ru/kirillius/pf/sdn/External/API/HEInfoProvider.java
@@ -4,7 +4,8 @@ import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import org.json.JSONTokener;
-import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInfoProvider;
+import ru.kirillius.pf.sdn.core.Context;
+import ru.kirillius.pf.sdn.core.Networking.ASInfoProvider;
import ru.kirillius.pf.sdn.core.Networking.IPv4Subnet;
import ru.kirillius.utils.logging.SystemLogger;
@@ -17,7 +18,14 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-public class HENetBGPInfoProvider implements AutonomousSystemInfoProvider {
+public class HEInfoProvider implements ASInfoProvider {
+
+ private final Context context;
+
+ public HEInfoProvider(Context context) {
+ this.context = context;
+ }
+
@Override
@SneakyThrows
public List getPrefixes(int as) {
@@ -52,5 +60,5 @@ public class HENetBGPInfoProvider implements AutonomousSystemInfoProvider {
return list;
}
- private final static String CTX = HENetBGPInfoProvider.class.getSimpleName();
+ private final static String CTX = HEInfoProvider.class.getSimpleName();
}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java
index 11c9579..3d5eb75 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java
@@ -11,6 +11,7 @@ import ru.kirillius.json.JSONSerializable;
import ru.kirillius.json.JSONUtility;
import ru.kirillius.pf.sdn.core.Auth.AuthToken;
import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
+import ru.kirillius.pf.sdn.core.Subscription.RepositoryConfig;
import java.io.*;
import java.util.Collections;
@@ -27,11 +28,21 @@ public class Config {
@JSONProperty
private String host = "0.0.0.0";
+ @Setter
+ @Getter
+ @JSONProperty
+ private File cacheDirectory = new File("./.cache");
+
@Setter
@Getter
@JSONArrayProperty(type = AuthToken.class)
private List tokens = Collections.emptyList();
+ @Setter
+ @Getter
+ @JSONArrayProperty(type = RepositoryConfig.class)
+ private List subscriptions = Collections.emptyList();
+
@Setter
@Getter
@JSONProperty
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java
index 4327a74..8a88472 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java
@@ -2,7 +2,7 @@ package ru.kirillius.pf.sdn.core;
import org.eclipse.jetty.server.Server;
import ru.kirillius.pf.sdn.core.Auth.AuthManager;
-import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInformationService;
+import ru.kirillius.pf.sdn.core.Networking.ASInfoService;
import ru.kirillius.pf.sdn.core.Networking.NetworkManager;
public interface Context {
@@ -12,8 +12,8 @@ public interface Context {
Server getServer();
- AutonomousSystemInformationService getAutonomousSystemInformationService();
+ ASInfoService getASInfoService();
NetworkManager getNetworkManager();
-
+ ContextEventsHandler getEventsHandler();
}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/ContextEventsHandler.java b/core/src/main/java/ru/kirillius/pf/sdn/core/ContextEventsHandler.java
new file mode 100644
index 0000000..3d5f917
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/ContextEventsHandler.java
@@ -0,0 +1,13 @@
+package ru.kirillius.pf.sdn.core;
+
+import lombok.Getter;
+import ru.kirillius.java.utils.events.ConcurrentEventHandler;
+import ru.kirillius.java.utils.events.EventHandler;
+import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
+
+public final class ContextEventsHandler {
+ @Getter
+ private final EventHandler networkManagerUpdateEvent = new ConcurrentEventHandler<>();
+ @Getter
+ private final EventHandler subscriptionsUpdateEvent = new ConcurrentEventHandler<>();
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoProvider.java
new file mode 100644
index 0000000..6a7c17b
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoProvider.java
@@ -0,0 +1,20 @@
+package ru.kirillius.pf.sdn.core.Networking;
+
+import ru.kirillius.pf.sdn.core.Context;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+public interface ASInfoProvider {
+ List getPrefixes(int as);
+
+ static ASInfoProvider instantiate(Class extends ASInfoProvider> providerClass, Context context) {
+ try {
+ var constructor = providerClass.getConstructor(Context.class);
+ return constructor.newInstance(context);
+ } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
+ IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoService.java
similarity index 84%
rename from core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java
rename to core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoService.java
index bb39c77..5a5bc2a 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/ASInfoService.java
@@ -12,13 +12,13 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@NoArgsConstructor
-public class AutonomousSystemInformationService implements Closeable {
+public class ASInfoService implements Closeable {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
@Getter
@Setter
- private AutonomousSystemInfoProvider provider = null;
+ private ASInfoProvider provider = null;
public Future> getPrefixes(int as) {
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java
deleted file mode 100644
index 3d1c3ce..0000000
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ru.kirillius.pf.sdn.core.Networking;
-
-import java.util.List;
-
-public interface AutonomousSystemInfoProvider {
- List getPrefixes(int as);
-}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java
index 214e451..f96083c 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java
@@ -72,6 +72,11 @@ public class NetworkManager implements Closeable {
outputResources.setSubnets(subnets.stream().toList());
outputResources.setDomains(domains.stream().toList());
+ try {
+ context.getEventsHandler().getNetworkManagerUpdateEvent().invoke(outputResources);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}));
}
@@ -81,7 +86,7 @@ public class NetworkManager implements Closeable {
private void fetchPrefixes(List systems) {
systems.forEach(as -> {
- var service = context.getAutonomousSystemInformationService();
+ var service = context.getASInfoService();
var future = service.getPrefixes(as);
while (!future.isDone() && !future.isCancelled()) {
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/RepositoryConfig.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/RepositoryConfig.java
new file mode 100644
index 0000000..37fd4dc
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/RepositoryConfig.java
@@ -0,0 +1,26 @@
+package ru.kirillius.pf.sdn.core.Subscription;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import ru.kirillius.json.JSONProperty;
+import ru.kirillius.json.JSONSerializable;
+
+@NoArgsConstructor
+@JSONSerializable
+public class RepositoryConfig {
+ @Setter
+ @Getter
+ @JSONProperty
+ private String name;
+
+ @Setter
+ @Getter
+ @JSONProperty
+ private Class> type;
+
+ @Setter
+ @Getter
+ @JSONProperty
+ private String source;
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionManager.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionManager.java
new file mode 100644
index 0000000..7f70070
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionManager.java
@@ -0,0 +1,57 @@
+package ru.kirillius.pf.sdn.core.Subscription;
+
+import lombok.Getter;
+import ru.kirillius.pf.sdn.core.Context;
+import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class SubscriptionManager implements Closeable {
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ private Context context;
+
+ public SubscriptionManager(Context context) {
+ this.context = context;
+ }
+
+ private final AtomicReference> updateProcess = new AtomicReference<>();
+
+ @Getter
+ private final NetworkResourceBundle outputResources = new NetworkResourceBundle();
+
+ public boolean isUpdatingNow() {
+ var future = updateProcess.get();
+ return future != null && !future.isDone() && !future.isCancelled();
+ }
+
+
+ public synchronized void triggerUpdate() {
+ if (isUpdatingNow()) {
+ return;
+ }
+ updateProcess.set(executor.submit(() -> {
+ var bundle = new NetworkResourceBundle();
+
+
+ outputResources.clear();
+ outputResources.add(bundle);
+ try {
+ context.getEventsHandler().getSubscriptionsUpdateEvent().invoke(outputResources);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ }
+
+
+ @Override
+ public void close() throws IOException {
+ executor.shutdown();
+ }
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionProvider.java
new file mode 100644
index 0000000..1a9e83f
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Subscription/SubscriptionProvider.java
@@ -0,0 +1,21 @@
+package ru.kirillius.pf.sdn.core.Subscription;
+
+import ru.kirillius.pf.sdn.core.Context;
+import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+public interface SubscriptionProvider {
+ Map getResources(RepositoryConfig config);
+
+ static SubscriptionProvider instantiate(Class extends SubscriptionProvider> providerClass, Context context) {
+ try {
+ var constructor = providerClass.getConstructor(Context.class);
+ return constructor.newInstance(context);
+ } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
+ IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java
index dd62def..5490fbc 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Util/HashUtil.java
@@ -8,6 +8,26 @@ public final class HashUtil {
private HashUtil() {
}
+ public static String md5(String input) {
+ try {
+ var md = MessageDigest.getInstance("MD5");
+ var hash = md.digest(input.getBytes());
+
+
+ var hexString = new StringBuilder();
+ for (var b : hash) {
+ var hex = Integer.toHexString(0xff & b);
+ if (hex.length() == 1) {
+ hexString.append('0');
+ }
+ hexString.append(hex);
+ }
+
+ return hexString.toString();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("MD5 algorithm not found", e);
+ }
+ }
public static String hash(String data, String salt) {
String generatedPassword = null;
diff --git a/pom.xml b/pom.xml
index 94bdf3d..80a6856 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,22 +63,8 @@
cron-utils
9.2.0
-
-
- org.snmp4j
- snmp4j
- 3.7.7
-
-
- ru.kirillius
- icmp4j
- 1.0.0.0
-
-
- ru.kirillius.util
- dynamic-types
- 2.0.0.0
-
+
+
ru.kirillius
json-rpc-servlet
@@ -89,12 +75,17 @@
common-logging
1.3.0.0
-
- ru.kirillius
- hibernate-commons
- 2.2.0.0
-
+
+ org.slf4j
+ slf4j-api
+ 2.0.9
+
+
+ org.slf4j
+ slf4j-jdk14
+ 2.0.9
+