Skip to content

Commit 6e1e56b

Browse files
author
edu
committed
refactor: simplificación y limpieza de código en la configuración de la aplicación
- Se simplificaron los scripts de lanzamiento y se mejoró la gestión de directorios de datos, eliminando complejidades innecesarias. - Se actualizó `LoggerConfig` para utilizar rutas absolutas desde `AppDataDirectory`, asegurando una configuración de logging más robusta. - Se corrigió la inicialización de directorios en `Main` para garantizar que existan antes de cargar el logger. - Se mejoró la configuración de VS Code para establecer el directorio de trabajo correcto en `launch.json`. - Se actualizó el changelog.md con un resumen de los cambios realizados y problemas resueltos. Fixes: problemas de configuración y gestión de directorios en la aplicación.
1 parent 0f33c7c commit 6e1e56b

File tree

6 files changed

+457
-1065
lines changed

6 files changed

+457
-1065
lines changed

.vscode/launch.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"mainClass": "com.example.forevernote.Main",
99
"projectName": "forevernote",
1010
"preLaunchTask": "maven-exec-java",
11+
"cwd": "${workspaceFolder}/Forevernote",
1112
"noDebug": true
1213
},
1314
{
@@ -17,6 +18,7 @@
1718
"mainClass": "com.example.forevernote.Main",
1819
"projectName": "forevernote",
1920
"preLaunchTask": "maven-compile",
21+
"cwd": "${workspaceFolder}/Forevernote",
2022
"vmArgs": "-Dfile.encoding=UTF-8"
2123
}
2224
]
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package com.example.forevernote;
2+
3+
import java.io.File;
4+
5+
/**
6+
* Utility class to determine the application data directory.
7+
*
8+
* Strategy:
9+
* 1. First, try to use relative paths (data/, logs/) - works for development and portable apps
10+
* 2. If that fails (read-only directory), use platform-specific user data directory
11+
*
12+
* Standard directories by platform (fallback):
13+
* - Windows: %APPDATA%\Forevernote
14+
* - macOS: ~/Library/Application Support/Forevernote
15+
* - Linux: ~/.config/Forevernote
16+
*/
17+
public class AppDataDirectory {
18+
19+
private static final String APP_NAME = "Forevernote";
20+
private static String baseDir = null;
21+
22+
/**
23+
* Gets the base directory for application data.
24+
* First tries relative path, falls back to platform-specific directory.
25+
*/
26+
public static String getBaseDirectory() {
27+
if (baseDir != null) {
28+
return baseDir;
29+
}
30+
31+
// Strategy 1: Try relative path (works for development and portable apps)
32+
String userDir = System.getProperty("user.dir", ".");
33+
File testDir = new File(userDir, "data");
34+
35+
if (canWriteToDirectory(testDir)) {
36+
baseDir = userDir;
37+
return baseDir;
38+
}
39+
40+
// Strategy 2: Use platform-specific directory (for packaged apps in read-only locations)
41+
baseDir = getPlatformAppDataDirectory();
42+
return baseDir;
43+
}
44+
45+
/**
46+
* Checks if we can write to a directory (exists and writable, or can be created).
47+
*/
48+
private static boolean canWriteToDirectory(File dir) {
49+
try {
50+
if (dir.exists()) {
51+
return dir.isDirectory() && dir.canWrite();
52+
}
53+
// Try to create and delete to test write permission
54+
if (dir.mkdirs()) {
55+
dir.delete();
56+
return true;
57+
}
58+
return false;
59+
} catch (Exception e) {
60+
return false;
61+
}
62+
}
63+
64+
/**
65+
* Gets the platform-specific application data directory.
66+
*/
67+
private static String getPlatformAppDataDirectory() {
68+
String osName = System.getProperty("os.name", "").toLowerCase();
69+
String home = System.getProperty("user.home");
70+
File appDataDir;
71+
72+
if (osName.contains("win")) {
73+
// Windows: %APPDATA%\Forevernote
74+
String appData = System.getenv("APPDATA");
75+
if (appData != null && !appData.isEmpty()) {
76+
appDataDir = new File(appData, APP_NAME);
77+
} else {
78+
appDataDir = new File(home, "AppData\\Roaming\\" + APP_NAME);
79+
}
80+
} else if (osName.contains("mac")) {
81+
// macOS: ~/Library/Application Support/Forevernote
82+
appDataDir = new File(home, "Library/Application Support/" + APP_NAME);
83+
} else {
84+
// Linux: ~/.config/Forevernote (XDG Base Directory)
85+
String xdgConfig = System.getenv("XDG_CONFIG_HOME");
86+
if (xdgConfig != null && !xdgConfig.isEmpty()) {
87+
appDataDir = new File(xdgConfig, APP_NAME);
88+
} else {
89+
appDataDir = new File(home, ".config/" + APP_NAME);
90+
}
91+
}
92+
93+
return appDataDir.getAbsolutePath();
94+
}
95+
96+
/**
97+
* Gets the data directory path (baseDir/data).
98+
*/
99+
public static String getDataDirectory() {
100+
return new File(getBaseDirectory(), "data").getAbsolutePath();
101+
}
102+
103+
/**
104+
* Gets the logs directory path (baseDir/logs).
105+
*/
106+
public static String getLogsDirectory() {
107+
return new File(getBaseDirectory(), "logs").getAbsolutePath();
108+
}
109+
110+
/**
111+
* Ensures data and logs directories exist.
112+
* Called by Main at startup before logger initialization.
113+
*/
114+
public static boolean ensureDirectoriesExist() {
115+
try {
116+
File dataDir = new File(getDataDirectory());
117+
File logsDir = new File(getLogsDirectory());
118+
119+
if (!dataDir.exists()) {
120+
dataDir.mkdirs();
121+
}
122+
if (!logsDir.exists()) {
123+
logsDir.mkdirs();
124+
}
125+
126+
return dataDir.exists() && dataDir.canWrite() &&
127+
logsDir.exists() && logsDir.canWrite();
128+
} catch (Exception e) {
129+
return false;
130+
}
131+
}
132+
}
Lines changed: 28 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.forevernote;
22

33
import javafx.application.Application;
4+
import javafx.application.Platform;
45
import javafx.fxml.FXMLLoader;
56
import javafx.scene.Scene;
67
import javafx.scene.image.Image;
@@ -9,32 +10,19 @@
910
import com.example.forevernote.config.LoggerConfig;
1011
import com.example.forevernote.data.SQLiteDB;
1112

13+
import java.io.File;
1214
import java.io.IOException;
1315
import java.util.logging.Logger;
1416

1517
/**
1618
* Main application class for Forevernote.
17-
* This class initializes the JavaFX application and sets up the primary stage.
1819
*/
1920
public class Main extends Application {
2021

21-
// Static block to ensure directories exist before logger initialization
22+
// Create directories BEFORE logger loads (logger needs logs/ to exist)
2223
static {
23-
// Create data and logs directories before LoggerConfig initializes FileHandler
24-
// This must happen before any logger is created
25-
try {
26-
java.io.File dataDir = new java.io.File("data");
27-
if (!dataDir.exists()) {
28-
dataDir.mkdirs();
29-
}
30-
31-
java.io.File logsDir = new java.io.File("logs");
32-
if (!logsDir.exists()) {
33-
logsDir.mkdirs();
34-
}
35-
} catch (Exception e) {
36-
// If directory creation fails, log to console since logger may not be ready
37-
System.err.println("Warning: Could not create data/logs directories: " + e.getMessage());
24+
if (!AppDataDirectory.ensureDirectoriesExist()) {
25+
System.err.println("Warning: Could not create data/logs directories");
3826
}
3927
}
4028

@@ -43,80 +31,67 @@ public class Main extends Application {
4331
@Override
4432
public void start(Stage primaryStage) {
4533
try {
46-
// Initialize database
4734
initializeDatabase();
4835

49-
// Load main view
50-
FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/example/forevernote/ui/view/MainView.fxml"));
36+
FXMLLoader loader = new FXMLLoader(getClass().getResource(
37+
"/com/example/forevernote/ui/view/MainView.fxml"));
5138
Scene scene = new Scene(loader.load(), 1200, 800);
5239

53-
// Apply CSS styling
54-
var cssResource = getClass().getResource("/com/example/forevernote/ui/css/modern-theme.css");
40+
var cssResource = getClass().getResource(
41+
"/com/example/forevernote/ui/css/modern-theme.css");
5542
if (cssResource != null) {
5643
scene.getStylesheets().add(cssResource.toExternalForm());
57-
} else {
58-
logger.warning("Could not load CSS stylesheet: modern-theme.css not found");
5944
}
6045

61-
// Configure primary stage
6246
primaryStage.setTitle("Forevernote - Free Note Taking");
6347

64-
// Try to load app icon (optional)
6548
try {
66-
var iconStream = getClass().getResourceAsStream("/com/example/forevernote/ui/images/app-icon.png");
49+
var iconStream = getClass().getResourceAsStream(
50+
"/com/example/forevernote/ui/images/app-icon.png");
6751
if (iconStream != null) {
6852
primaryStage.getIcons().add(new Image(iconStream));
6953
}
7054
} catch (Exception e) {
71-
logger.warning("Could not load app icon: " + e.getMessage());
55+
// Icon is optional
7256
}
7357

7458
primaryStage.setScene(scene);
7559
primaryStage.setMinWidth(800);
7660
primaryStage.setMinHeight(600);
77-
78-
// Show the stage
7961
primaryStage.show();
8062

81-
logger.info("Forevernote application started successfully");
63+
// macOS focus workaround
64+
if (System.getProperty("os.name", "").toLowerCase().contains("mac")) {
65+
Platform.runLater(() -> {
66+
primaryStage.toFront();
67+
primaryStage.requestFocus();
68+
});
69+
}
70+
71+
logger.info("Forevernote started. Data: " + AppDataDirectory.getDataDirectory());
8272

8373
} catch (IOException e) {
8474
logger.severe("Failed to load main view: " + e.getMessage());
8575
throw new RuntimeException("Failed to start application", e);
8676
}
8777
}
8878

89-
/**
90-
* Initializes the SQLite database.
91-
* Uses data/database.db relative to the working directory.
92-
* Scripts ensure execution from Forevernote/ directory.
93-
* Note: data/ and logs/ directories are already created in the static block.
94-
*/
9579
private void initializeDatabase() {
9680
try {
97-
// Directories are already created in static block, but ensure they exist as a safety check
98-
java.io.File dataDir = new java.io.File("data");
99-
if (!dataDir.exists()) {
100-
dataDir.mkdirs();
101-
logger.warning("Data directory was missing and recreated");
102-
}
81+
String dbPath = new File(AppDataDirectory.getDataDirectory(), "database.db")
82+
.getAbsolutePath();
83+
84+
SQLiteDB.configure(dbPath);
85+
SQLiteDB.getInstance().initDatabase();
10386

104-
SQLiteDB.configure("data/database.db");
105-
SQLiteDB db = SQLiteDB.getInstance();
106-
db.initDatabase();
107-
logger.info("Database initialized successfully");
87+
logger.info("Database initialized at: " + dbPath);
10888
} catch (Exception e) {
10989
logger.severe("Failed to initialize database: " + e.getMessage());
11090
throw new RuntimeException("Database initialization failed", e);
11191
}
11292
}
11393

114-
/**
115-
* Main method to launch the application.
116-
*
117-
* @param args Command line arguments
118-
*/
11994
public static void main(String[] args) {
12095
launch(args);
12196
}
122-
}
97+
}

Forevernote/src/main/java/com/example/forevernote/config/LoggerConfig.java

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,81 @@
11
package com.example.forevernote.config;
22

3+
import java.io.File;
34
import java.io.IOException;
45
import java.io.InputStream;
6+
import java.util.logging.ConsoleHandler;
7+
import java.util.logging.FileHandler;
8+
import java.util.logging.Level;
59
import java.util.logging.LogManager;
610
import java.util.logging.Logger;
11+
import java.util.logging.SimpleFormatter;
12+
13+
import com.example.forevernote.AppDataDirectory;
714

815
/**
9-
* LoggerConfig is responsible for configuring the logging system for the application.
10-
* It reads the logging configuration from a properties file and sets up the logging system accordingly.
16+
* Configures the logging system for the application.
17+
*
18+
* Reads base configuration from logging.properties, then configures
19+
* the FileHandler to use the correct logs directory from AppDataDirectory.
20+
*
21+
* Note: AppDataDirectory.ensureDirectoriesExist() must be called before
22+
* this class is loaded (done in Main's static block).
1123
*/
1224
public class LoggerConfig {
1325

14-
// Static block to configure the logger
1526
static {
16-
// Crear una carpeta resource e incluirla en el classpath para que getResourceAsStream lo encuentre
17-
// Attempt to load the logging configuration from the 'logging.properties' file
18-
try (InputStream configFile = LoggerConfig.class.getClassLoader().getResourceAsStream("logging.properties")) {
27+
try (InputStream configFile = LoggerConfig.class.getClassLoader()
28+
.getResourceAsStream("logging.properties")) {
29+
1930
if (configFile == null) {
20-
// Throw an exception if the configuration file is not found
2131
throw new IOException("Could not find logging.properties file");
2232
}
23-
// Load the logging configuration from the properties file
33+
34+
// Load base configuration
2435
LogManager.getLogManager().readConfiguration(configFile);
25-
36+
37+
// Configure FileHandler with absolute path from AppDataDirectory
38+
configureFileHandler();
39+
2640
} catch (IOException e) {
27-
// Log a severe error if there is an issue loading the configuration file
28-
Logger.getLogger(LoggerConfig.class.getName()).severe("Could not load logging configuration: " + e.getMessage());
41+
Logger.getLogger(LoggerConfig.class.getName())
42+
.severe("Could not load logging configuration: " + e.getMessage());
43+
}
44+
}
45+
46+
/**
47+
* Configures the FileHandler with the correct absolute path.
48+
*/
49+
private static void configureFileHandler() {
50+
try {
51+
String logsDir = AppDataDirectory.getLogsDirectory();
52+
String logFile = new File(logsDir, "app.log").getAbsolutePath();
53+
54+
// Reset and reconfigure with absolute path
55+
LogManager.getLogManager().reset();
56+
57+
// Console handler
58+
ConsoleHandler consoleHandler = new ConsoleHandler();
59+
consoleHandler.setLevel(Level.INFO);
60+
consoleHandler.setFormatter(new SimpleFormatter());
61+
Logger.getLogger("").addHandler(consoleHandler);
62+
63+
// File handler with absolute path
64+
FileHandler fileHandler = new FileHandler(logFile, true);
65+
fileHandler.setLevel(Level.ALL);
66+
fileHandler.setFormatter(new SimpleFormatter());
67+
Logger.getLogger("").addHandler(fileHandler);
68+
69+
// Root logger level
70+
Logger.getLogger("").setLevel(Level.ALL);
71+
72+
} catch (Exception e) {
73+
System.err.println("Warning: Could not configure file logging: " + e.getMessage());
2974
}
3075
}
3176

3277
/**
3378
* Returns the logger for the given class.
34-
*
35-
* @param clazz the class for which to get the logger
36-
* @return the logger instance
3779
*/
3880
public static Logger getLogger(Class<?> clazz) {
3981
return Logger.getLogger(clazz.getName());

0 commit comments

Comments
 (0)