mirror of
https://github.com/mcraftbbs/Ollama-Chat.git
synced 2025-11-18 06:44:22 +01:00
Fix 'java.lang.NoClassDefFoundError: com/zaxxer/hikari/HikariConfig'
This commit is contained in:
parent
eca5397805
commit
253dd3ecb1
@ -4,7 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'com'
|
||||
version = '1.1.5'
|
||||
version = '1.1.6'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.ollamachat;
|
||||
|
||||
import com.ollamachat.core.Ollamachat;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
@ -16,14 +15,12 @@ public class DatabaseManager {
|
||||
private final JavaPlugin plugin;
|
||||
private final Logger logger;
|
||||
private String databaseType; // "sqlite" or "mysql"
|
||||
private Connection sqliteConnection; // For SQLite, maintain a single connection
|
||||
private HikariDataSource dataSource; // For MySQL connection pooling
|
||||
private final ClassLoader dependencyClassLoader;
|
||||
private Connection sqliteConnection; // For SQLite
|
||||
private HikariDataSource dataSource; // For MySQL (HikariCP)
|
||||
|
||||
public DatabaseManager(JavaPlugin plugin, ClassLoader dependencyClassLoader) {
|
||||
public DatabaseManager(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.logger = plugin.getLogger();
|
||||
this.dependencyClassLoader = dependencyClassLoader;
|
||||
initializeDatabase();
|
||||
}
|
||||
|
||||
@ -47,10 +44,10 @@ public class DatabaseManager {
|
||||
|
||||
private void initializeSQLite() {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC", true, dependencyClassLoader);
|
||||
databaseType = "sqlite";
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:plugins/OllamaChat/chat_history.db");
|
||||
sqliteConnection.setAutoCommit(true);
|
||||
databaseType = "sqlite";
|
||||
logger.info("SQLite database initialized successfully.");
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.severe("Failed to initialize SQLite database: " + e.getMessage());
|
||||
@ -60,14 +57,15 @@ public class DatabaseManager {
|
||||
|
||||
private void initializeMySQL() {
|
||||
try {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver", true, dependencyClassLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.severe("MySQL JDBC driver not found. Falling back to SQLite.");
|
||||
initializeSQLite();
|
||||
return;
|
||||
}
|
||||
// 确认 MySQL 驱动是否存在
|
||||
try {
|
||||
Class.forName("com.mysql.cj.jdbc.Driver");
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.severe("MySQL JDBC driver not found! Falling back to SQLite.");
|
||||
initializeSQLite();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
String host = config.getString("database.mysql.host", "localhost");
|
||||
int port = config.getInt("database.mysql.port", 3306);
|
||||
@ -75,11 +73,15 @@ public class DatabaseManager {
|
||||
String username = config.getString("database.mysql.username", "root");
|
||||
String password = config.getString("database.mysql.password", "");
|
||||
|
||||
// 创建 Hikari 配置
|
||||
HikariConfig hikariConfig = new HikariConfig();
|
||||
hikariConfig.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s?useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true", host, port, database));
|
||||
hikariConfig.setJdbcUrl(String.format(
|
||||
"jdbc:mysql://%s:%d/%s?useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true",
|
||||
host, port, database));
|
||||
hikariConfig.setUsername(username);
|
||||
hikariConfig.setPassword(password);
|
||||
// Load HikariCP settings from config
|
||||
|
||||
// 从配置文件读取连接池参数
|
||||
hikariConfig.setMaximumPoolSize(config.getInt("database.mysql.hikari.maximum-pool-size", 10));
|
||||
hikariConfig.setMinimumIdle(config.getInt("database.mysql.hikari.minimum-idle", 2));
|
||||
hikariConfig.setConnectionTimeout(config.getLong("database.mysql.hikari.connection-timeout", 30000));
|
||||
@ -90,7 +92,7 @@ public class DatabaseManager {
|
||||
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", config.getString("database.mysql.hikari.prep-stmt-cache-sql-limit", "2048"));
|
||||
|
||||
dataSource = new HikariDataSource(hikariConfig);
|
||||
logger.info("MySQL database initialized with HikariCP successfully.");
|
||||
logger.info("MySQL database initialized successfully using HikariCP.");
|
||||
} catch (Exception e) {
|
||||
logger.severe("Failed to initialize MySQL database: " + e.getMessage());
|
||||
logger.warning("Falling back to SQLite due to MySQL initialization failure.");
|
||||
@ -99,45 +101,57 @@ public class DatabaseManager {
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
if (databaseType.equals("sqlite")) {
|
||||
if ("sqlite".equals(databaseType)) {
|
||||
if (sqliteConnection == null || sqliteConnection.isClosed()) {
|
||||
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:plugins/OllamaChat/chat_history.db");
|
||||
sqliteConnection.setAutoCommit(true);
|
||||
}
|
||||
return sqliteConnection;
|
||||
} else {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
|
||||
private void createTables() throws SQLException {
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS players (" +
|
||||
"uuid TEXT PRIMARY KEY," +
|
||||
"username TEXT NOT NULL)");
|
||||
// 根据数据库类型选择不同的列类型
|
||||
String uuidType = databaseType.equals("mysql") ? "VARCHAR(36)" : "TEXT";
|
||||
String textType = databaseType.equals("mysql") ? "VARCHAR(255)" : "TEXT";
|
||||
String aiModelType = databaseType.equals("mysql") ? "VARCHAR(100)" : "TEXT"; // 减少长度
|
||||
String longTextType = databaseType.equals("mysql") ? "TEXT" : "TEXT";
|
||||
|
||||
// Players 表
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS players (" +
|
||||
"uuid " + uuidType + " PRIMARY KEY," +
|
||||
"username " + textType + " NOT NULL)");
|
||||
|
||||
// Conversations 表 - 减少 ai_model 长度
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS conversations (" +
|
||||
"conversation_id TEXT NOT NULL," +
|
||||
"player_uuid TEXT NOT NULL," +
|
||||
"ai_model TEXT NOT NULL," +
|
||||
"conversation_name TEXT NOT NULL," +
|
||||
"conversation_id " + uuidType + " NOT NULL," +
|
||||
"player_uuid " + uuidType + " NOT NULL," +
|
||||
"ai_model " + aiModelType + " NOT NULL," +
|
||||
"conversation_name " + textType + " NOT NULL," +
|
||||
"created_at DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
||||
"PRIMARY KEY (conversation_id, player_uuid, ai_model)," +
|
||||
"FOREIGN KEY (player_uuid) REFERENCES players(uuid))");
|
||||
|
||||
// Chat history 表
|
||||
stmt.execute("CREATE TABLE IF NOT EXISTS chat_history (" +
|
||||
"id INTEGER PRIMARY KEY " + (databaseType.equals("sqlite") ? "AUTOINCREMENT" : "AUTO_INCREMENT") + "," +
|
||||
"player_uuid TEXT NOT NULL," +
|
||||
"ai_model TEXT NOT NULL," +
|
||||
"conversation_id TEXT," +
|
||||
"player_uuid " + uuidType + " NOT NULL," +
|
||||
"ai_model " + aiModelType + " NOT NULL," +
|
||||
"conversation_id " + uuidType + "," +
|
||||
"timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," +
|
||||
"prompt TEXT NOT NULL," +
|
||||
"response TEXT NOT NULL," +
|
||||
"prompt " + longTextType + " NOT NULL," +
|
||||
"response " + longTextType + " NOT NULL," +
|
||||
"FOREIGN KEY (player_uuid) REFERENCES players(uuid)," +
|
||||
"FOREIGN KEY (conversation_id, player_uuid, ai_model) REFERENCES conversations(conversation_id, player_uuid, ai_model))");
|
||||
|
||||
logger.info("Database tables created successfully for " + databaseType.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void savePlayerInfo(UUID uuid, String username) {
|
||||
String sql = databaseType.equals("sqlite")
|
||||
? "INSERT OR REPLACE INTO players (uuid, username) VALUES (?, ?)"
|
||||
|
||||
@ -3,19 +3,17 @@ package com.ollamachat;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class DependencyLoader {
|
||||
private final JavaPlugin plugin;
|
||||
private final Logger logger;
|
||||
private final File libDir;
|
||||
private URLClassLoader dependencyClassLoader;
|
||||
|
||||
public DependencyLoader(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -25,44 +23,31 @@ public class DependencyLoader {
|
||||
|
||||
public boolean loadDependencies() {
|
||||
try {
|
||||
// Create libs directory if it doesn't exist
|
||||
if (!libDir.exists()) {
|
||||
libDir.mkdirs();
|
||||
}
|
||||
|
||||
// Define dependencies to download
|
||||
List<Dependency> dependencies = new ArrayList<>();
|
||||
dependencies.add(new Dependency("com.mysql", "mysql-connector-j", "8.0.33"));
|
||||
dependencies.add(new Dependency("org.xerial", "sqlite-jdbc", "3.46.0.0"));
|
||||
dependencies.add(new Dependency("com.zaxxer", "HikariCP", "5.1.0")); // Add HikariCP
|
||||
|
||||
// Download each dependency
|
||||
List<URL> jarUrls = new ArrayList<>();
|
||||
for (Dependency dep : dependencies) {
|
||||
File jarFile = downloadDependency(dep);
|
||||
if (jarFile != null) {
|
||||
jarUrls.add(jarFile.toURI().toURL());
|
||||
logger.info("Found dependency: " + jarFile.getName());
|
||||
} else {
|
||||
logger.warning("Failed to download dependency: " + dep.artifactId + "-" + dep.version);
|
||||
}
|
||||
}
|
||||
|
||||
if (jarUrls.isEmpty()) {
|
||||
logger.warning("No dependencies were successfully downloaded.");
|
||||
if (!libDir.exists() && !libDir.mkdirs()) {
|
||||
logger.severe("Failed to create libs directory: " + libDir.getAbsolutePath());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new classloader with the dependencies
|
||||
dependencyClassLoader = new URLClassLoader(
|
||||
jarUrls.toArray(new URL[0]),
|
||||
plugin.getClass().getClassLoader()
|
||||
// 定义依赖
|
||||
List<Dependency> dependencies = List.of(
|
||||
new Dependency("com.mysql", "mysql-connector-j", "8.0.33"),
|
||||
new Dependency("org.xerial", "sqlite-jdbc", "3.46.0.0"),
|
||||
new Dependency("com.zaxxer", "HikariCP", "5.1.0")
|
||||
);
|
||||
|
||||
// Set this classloader as the thread context classloader
|
||||
Thread.currentThread().setContextClassLoader(dependencyClassLoader);
|
||||
logger.info("Successfully loaded " + jarUrls.size() + " dependencies");
|
||||
int loadedCount = 0;
|
||||
|
||||
for (Dependency dep : dependencies) {
|
||||
File jarFile = downloadDependency(dep);
|
||||
if (jarFile != null && injectToPluginClassLoader(jarFile)) {
|
||||
loadedCount++;
|
||||
logger.info("Loaded dependency: " + jarFile.getName());
|
||||
} else {
|
||||
logger.warning("Failed to load dependency: " + dep.artifactId);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Successfully loaded " + loadedCount + " dependencies.");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.severe("Failed to load dependencies: " + e.getMessage());
|
||||
@ -71,14 +56,11 @@ public class DependencyLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getDependencyClassLoader() {
|
||||
return dependencyClassLoader;
|
||||
}
|
||||
|
||||
private File downloadDependency(Dependency dep) {
|
||||
String fileName = dep.artifactId + "-" + dep.version + ".jar";
|
||||
File file = new File(libDir, fileName);
|
||||
|
||||
// 已存在则直接返回
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
@ -87,16 +69,56 @@ public class DependencyLoader {
|
||||
dep.groupId.replace(".", "/"), dep.artifactId, dep.version, dep.artifactId, dep.version);
|
||||
String url = "https://repo1.maven.org/maven2/" + mavenPath;
|
||||
|
||||
logger.info("Downloading dependency: " + fileName + " from " + url);
|
||||
|
||||
try (InputStream in = new URL(url).openStream()) {
|
||||
Files.copy(in, file.toPath());
|
||||
Path temp = Files.createTempFile(libDir.toPath(), dep.artifactId, ".tmp");
|
||||
Files.copy(in, temp, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(temp, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
logger.info("Downloaded dependency: " + fileName);
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
logger.severe("Failed to download dependency " + fileName + ": " + e.getMessage());
|
||||
logger.severe("Failed to download " + fileName + ": " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JAR 动态注入到 Bukkit 的 PluginClassLoader。
|
||||
*/
|
||||
private boolean injectToPluginClassLoader(File jarFile) {
|
||||
try {
|
||||
ClassLoader pluginClassLoader = plugin.getClass().getClassLoader();
|
||||
|
||||
// 仅在类加载器是 URLClassLoader 或包含 addURL 方法时注入
|
||||
Method addURLMethod = null;
|
||||
Class<?> current = pluginClassLoader.getClass();
|
||||
while (current != null && addURLMethod == null) {
|
||||
try {
|
||||
addURLMethod = current.getDeclaredMethod("addURL", URL.class);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
current = current.getSuperclass();
|
||||
}
|
||||
|
||||
if (addURLMethod == null) {
|
||||
logger.severe("Unable to find addURL method in plugin classloader. Injection failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
addURLMethod.setAccessible(true);
|
||||
addURLMethod.invoke(pluginClassLoader, jarFile.toURI().toURL());
|
||||
logger.info("Injected dependency into classloader: " + jarFile.getName());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.severe("Failed to inject dependency " + jarFile.getName() + ": " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部依赖描述类
|
||||
*/
|
||||
private static class Dependency {
|
||||
String groupId;
|
||||
String artifactId;
|
||||
@ -109,7 +131,3 @@ public class DependencyLoader {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -28,14 +28,9 @@ public class Ollamachat extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
DependencyLoader dependencyLoader = new DependencyLoader(this);
|
||||
ClassLoader dependencyClassLoader;
|
||||
try {
|
||||
dependencyLoader.loadDependencies();
|
||||
dependencyClassLoader = dependencyLoader.getClass().getClassLoader();
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Failed to load dependencies: " + e.getMessage());
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
DependencyLoader loader = new DependencyLoader(this);
|
||||
if (!loader.loadDependencies()) {
|
||||
getLogger().severe("Failed to load dependencies. ");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -43,9 +38,10 @@ public class Ollamachat extends JavaPlugin {
|
||||
configManager.initialize();
|
||||
|
||||
try {
|
||||
databaseManager = new DatabaseManager(this, dependencyClassLoader);
|
||||
databaseManager = new DatabaseManager(this);
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Failed to initialize DatabaseManager: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
@ -106,13 +102,4 @@ public class Ollamachat extends JavaPlugin {
|
||||
public Map<UUID, Boolean> getPlayerSuggestionToggles() {
|
||||
return playerSuggestionToggles;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user