Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
71d621e
Replace insecure Random with SecureRandom for IV generation
google-labs-jules[bot] Feb 11, 2026
1491443
Refactor AlertMaker.java to remove code duplication
google-labs-jules[bot] Feb 11, 2026
fe3368b
Merge pull request #1 from G30RG3-GJ/fix-insecure-iv-1184645223294922…
G30RG3-GJ Feb 11, 2026
73b94f8
Merge pull request #2 from G30RG3-GJ/refactor-alertmaker-code-duplica…
G30RG3-GJ Feb 11, 2026
8c8e348
Remove unused main methods from Loader classes
google-labs-jules[bot] Feb 11, 2026
161832d
Add unit test for DataHelper.insertNewBook with dependency injection
google-labs-jules[bot] Feb 12, 2026
3d742fb
Add integration test for EmailUtil using GreenMail
google-labs-jules[bot] Feb 12, 2026
df8ce53
Merge pull request #16 from G30RG3-GJ/fix/remove-unused-main-methods-…
G30RG3-GJ Feb 13, 2026
308d65b
Merge pull request #49 from G30RG3-GJ/email-integration-test-32658710…
G30RG3-GJ Feb 13, 2026
f3ef177
Merge pull request #48 from G30RG3-GJ/test-datahelper-insertbook-2693…
G30RG3-GJ Feb 13, 2026
08d5dfe
Optimize email validation regex compilation
google-labs-jules[bot] Feb 19, 2026
c2af987
Merge pull request #62 from G30RG3-GJ/perf/optimize-email-validation-…
G30RG3-GJ Feb 19, 2026
dac0284
Refactor DatabaseExporter to avoid broad exception catching
google-labs-jules[bot] Feb 19, 2026
848adc4
Merge pull request #103 from G30RG3-GJ/fix-broad-exception-catching-d…
G30RG3-GJ Feb 19, 2026
818bf2d
Fix fxml startup crashes
kitishe Feb 19, 2026
9791754
Implement data export, email validation and add packaging script
kitishe Feb 19, 2026
235b5a4
Update README with new features and build instructions
kitishe Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Material Design Library Management Software using JavaFX
This is a library management software developed using JavaFX programming language. The entire development video with explanation of each and every part (in realtime) is available in my YouTube Channel [Genuine Coder Youtube Channel](https://www.youtube.com/playlist?list=PLhs1urmduZ29jTcE1ca8Z6bZNvH_39ayL).

### New Features
### New Features (v1.1.0)
* **Data Export**: Export Book and Member lists to CSV format for easy reporting.
* **Email Validation**: Enhanced validation for member email addresses during registration.
* **Release Automation**: One-click packaging using `package_app.ps1`.

### Previous Features
* One-click database export
<p align="center">
<img src=https://i.imgur.com/ufZOqkn.png>
Expand All @@ -18,6 +23,26 @@ This is a library management software developed using JavaFX programming languag
<p align="center">
<img src=https://i.imgur.com/WrWZqLr.png>
</p>

### Getting Started

#### Prerequisites
* Java 8 (JDK 1.8)
* Maven (optional, for dependency management if not using included libs)

#### Running the Application
To run the application locally:
```powershell
.\run_app.ps1
```

#### Building from Source
To create a release package (ZIP with JAR and dependencies):
```powershell
.\package_app.ps1
```
The output will be available in the `dist` folder.

### Default Login Credentials
| Username | Password |
| ------------- | ------------- |
Expand All @@ -26,7 +51,7 @@ This is a library management software developed using JavaFX programming languag
### Libraries Used
* [JFoenix](https://github.com/jfoenixadmin/JFoenix) - JavaFX Material Design Library
* [Apache Derby](https://db.apache.org/derby/) - Standalone Relational database
* [Apache Commons](https://commons.apache.org/) - For creating SHA hash
* [Apache Commons](https://commons.apache.org/) - For creating SHA hash and CSV Export
* [GSon](https://github.com/google/gson) - JSON Library. Used for storing configuration
* [FontawesomeFX](https://bitbucket.org/Jerady/fontawesomefx) - Icon library
* [Apache PDFBox](https://pdfbox.apache.org/) - PDF Export
Expand Down
Binary file added libs/test/byte-buddy-1.12.19.jar
Binary file not shown.
Binary file added libs/test/byte-buddy-agent-1.12.19.jar
Binary file not shown.
Binary file added libs/test/greenmail-standalone-1.6.15.jar
Binary file not shown.
Binary file added libs/test/hamcrest-core-1.3.jar
Binary file not shown.
Binary file added libs/test/junit-4.13.2.jar
Binary file not shown.
Binary file added libs/test/mockito-core-4.11.0.jar
Binary file not shown.
Binary file added libs/test/objenesis-3.3.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion manifest.mf
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build
Main-Class: library.assistant.ui.main.Main
Class-Path: libs/boxable-1.5-RC.jar libs/commons-codec-1.11.jar libs/commons-csv-1.4.jar libs/commons-lang3-3.9.jar libs/commons-logging-1.2.jar libs/derby.jar libs/derbyclient.jar libs/fontawesomefx-commons-8.15.jar libs/fontawesomefx-fontawesome-4.7.0-5.jar libs/fontbox-2.0.4.jar libs/gson-2.8.1.jar libs/guava-21.0.jar libs/javax.mail-1.6.1.jar libs/jfoenix-8.0.4.jar libs/log4j-api-2.11.0.jar libs/log4j-core-2.11.0.jar libs/pdfbox-2.0.3.jar libs/slf4j-api-1.7.25.jar libs/slf4j-simple-1.7.25.jar

5 changes: 4 additions & 1 deletion nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ javac.source=1.8
javac.target=1.8
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
${build.classes.dir}:\
libs/test/junit-4.13.2.jar:\
libs/test/hamcrest-core-1.3.jar:\
libs/test/greenmail-standalone-1.6.15.jar
javac.test.modulepath=\
${javac.modulepath}
javac.test.processorpath=\
Expand Down
69 changes: 69 additions & 0 deletions package_app.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

# Set paths
$PROJECT_ROOT = "d:\lib\Library-Assistant"
$SRC_DIR = "$PROJECT_ROOT\src"
$LIB_DIR = "$PROJECT_ROOT\libs"
$BUILD_DIR = "$PROJECT_ROOT\build\classes"
$DIST_DIR = "$PROJECT_ROOT\dist"
$VERSION = "1.0.0"

# Clean build and dist
if (Test-Path $BUILD_DIR) { Remove-Item -Recurse -Force $BUILD_DIR }
if (Test-Path $DIST_DIR) { Remove-Item -Recurse -Force $DIST_DIR }
New-Item -ItemType Directory -Force -Path $BUILD_DIR | Out-Null
New-Item -ItemType Directory -Force -Path $DIST_DIR | Out-Null

# Construct classpath for compilation
$JFXRT = "$LIB_DIR\jfxrt.jar"
$LIBS = Get-ChildItem "$LIB_DIR\*.jar" | Where-Object { $_.Name -ne "jfxrt.jar" } | ForEach-Object { $_.FullName }
$CLASSPATH = "$JFXRT;" + ($LIBS -join ";") + ";$BUILD_DIR"

# Copy resources
Write-Host "Copying resources..."
Copy-Item "$SRC_DIR\*" -Destination $BUILD_DIR -Recurse -Force -Exclude "*.java"

# Compile
Write-Host "Compiling..."
$JAVA_FILES = Get-ChildItem -Path $SRC_DIR -Recurse -Filter "*.java" | ForEach-Object { $_.FullName }
$JAVA_FILES | Out-File "$PROJECT_ROOT\sources.txt" -Encoding ASCII
& javac -d "$BUILD_DIR" -cp "$CLASSPATH" "@$PROJECT_ROOT\sources.txt"

if ($LASTEXITCODE -ne 0) {
Write-Error "Compilation failed!"
exit 1
}

# Create Manifest
$MANIFEST_PATH = "$PROJECT_ROOT\manifest.mf"
$MANIFEST_CONTENT = "Manifest-Version: 1.0`nMain-Class: library.assistant.ui.main.Main`nClass-Path: "
# Add libs to Class-Path in manifest (relative path)
$LIB_NAMES = Get-ChildItem "$LIB_DIR\*.jar" | ForEach-Object { "libs/" + $_.Name }
$MANIFEST_CONTENT += ($LIB_NAMES -join " ") + "`n"
$MANIFEST_CONTENT | Out-File $MANIFEST_PATH -Encoding ASCII

# Create JAR
Write-Host "Creating JAR..."
$JAR_NAME = "LibraryAssistant.jar"
& jar camf "$MANIFEST_PATH" "$DIST_DIR\$JAR_NAME" -C "$BUILD_DIR" .

# Package into ZIP
Write-Host "Packaging into ZIP..."
$ZIP_NAME = "LibraryAssistant-v$VERSION.zip"
$ZIP_PATH = "$DIST_DIR\$ZIP_NAME"

# Create a temporary folder for zipping
$TEMP_ZIP_DIR = "$DIST_DIR\LibraryAssistant"
New-Item -ItemType Directory -Force -Path $TEMP_ZIP_DIR | Out-Null

# Copy JAR and Libs
Copy-Item "$DIST_DIR\$JAR_NAME" -Destination $TEMP_ZIP_DIR
New-Item -ItemType Directory -Force -Path "$TEMP_ZIP_DIR\libs" | Out-Null
Copy-Item "$LIB_DIR\*" -Destination "$TEMP_ZIP_DIR\libs" -Recurse

# Zip it
Compress-Archive -Path "$TEMP_ZIP_DIR\*" -DestinationPath $ZIP_PATH -Force

# Cleanup temp
Remove-Item -Recurse -Force $TEMP_ZIP_DIR

Write-Host "Build complete: $ZIP_PATH"
69 changes: 69 additions & 0 deletions run_app.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

# Set paths
$PROJECT_ROOT = "d:\lib\Library-Assistant"
$SRC_DIR = "$PROJECT_ROOT\src"
$LIB_DIR = "$PROJECT_ROOT\libs"
$BUILD_DIR = "$PROJECT_ROOT\build\classes"

$JAVA_EXE = "java"
$JAVAC_EXE = "javac"

# Create build directory
if (!(Test-Path $BUILD_DIR)) {
New-Item -ItemType Directory -Force -Path $BUILD_DIR | Out-Null
}

# Construct classpath
# Note: specific order might matter - put jfxrt.jar first if possible
$JFXRT = "$LIB_DIR\jfxrt.jar"
if (Test-Path $JFXRT) {
Write-Host "Found jfxrt.jar in libs."
$CLASSPATH = "$JFXRT"
}
else {
Write-Warning "jfxrt.jar NOT FOUND in libs. Compilation may fail."
$CLASSPATH = ""
}

$LIBS = Get-ChildItem "$LIB_DIR\*.jar" | Where-Object { $_.Name -ne "jfxrt.jar" } | ForEach-Object { $_.FullName }
if ($CLASSPATH -ne "") {
$CLASSPATH = "$CLASSPATH;" + ($LIBS -join ";") + ";$BUILD_DIR"
}
else {
$CLASSPATH = ($LIBS -join ";") + ";$BUILD_DIR"
}

Write-Host "--- Debug Info ---"
Write-Host "CLASSPATH length: $($CLASSPATH.Length)"
Write-Host "------------------"

# Copy resources (FXML, CSS, images, etc.) to build directory
Write-Host "Copying resources..."
Copy-Item "$SRC_DIR\*" -Destination $BUILD_DIR -Recurse -Force -Exclude "*.java"

# Compile Java files
Write-Host "Compiling Java files..."
$JAVA_FILES = Get-ChildItem -Path $SRC_DIR -Recurse -Filter "*.java" | ForEach-Object { $_.FullName }

# Write sources to file for compilation to avoid command line length limits
$JAVA_FILES | Out-File "$PROJECT_ROOT\sources.txt" -Encoding ASCII

# Compile
Write-Host "Running javac..."
& $JAVAC_EXE -d "$BUILD_DIR" -cp "$CLASSPATH" "@$PROJECT_ROOT\sources.txt"

if ($LASTEXITCODE -eq 0) {
Write-Host "Compilation successful."
Write-Host "Running application..."

# Run application
& $JAVA_EXE -cp "$CLASSPATH" library.assistant.ui.main.Main
}
else {
Write-Host "Compilation failed."
Write-Warning "If compilation failed due to JavaFX, ensure jfxrt.jar is in libs folder."
Write-Warning "User confirmed they have it, checking again..."
if (Test-Path $JFXRT) {
Write-Host "CONFIRMED: jfxrt.jar IS in libs."
}
}
13 changes: 13 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
mkdir -p build/test/stubs
mkdir -p build/test/classes

javac -d build/test/stubs test/stubs/library/assistant/database/DatabaseHandler.java test/stubs/library/assistant/ui/listmember/MemberListController.java

javac -sourcepath "" -cp "libs/*:libs/test/*:build/test/stubs" -d build/test/classes \
src/library/assistant/data/model/Book.java \
src/library/assistant/data/model/MailServerInfo.java \
src/library/assistant/database/DataHelper.java \
test/library/assistant/database/DataHelperTest.java

java -cp "build/test/classes:build/test/stubs:libs/*:libs/test/*" org.junit.runner.JUnitCore library.assistant.database.DataHelperTest
40 changes: 40 additions & 0 deletions sources.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
D:\lib\Library-Assistant\src\library\assistant\alert\AlertMaker.java
D:\lib\Library-Assistant\src\library\assistant\data\callback\GenericCallback.java
D:\lib\Library-Assistant\src\library\assistant\data\model\Book.java
D:\lib\Library-Assistant\src\library\assistant\data\model\MailServerInfo.java
D:\lib\Library-Assistant\src\library\assistant\data\model\Member.java
D:\lib\Library-Assistant\src\library\assistant\database\DatabaseHandler.java
D:\lib\Library-Assistant\src\library\assistant\database\DataHelper.java
D:\lib\Library-Assistant\src\library\assistant\database\export\DatabaseExporter.java
D:\lib\Library-Assistant\src\library\assistant\email\EmailUtil.java
D:\lib\Library-Assistant\src\library\assistant\encryption\CipherSpec.java
D:\lib\Library-Assistant\src\library\assistant\encryption\EncryptionUtil.java
D:\lib\Library-Assistant\src\library\assistant\exceptions\DefaultExceptionHandler.java
D:\lib\Library-Assistant\src\library\assistant\exceptions\ExceptionUtil.java
D:\lib\Library-Assistant\src\library\assistant\export\pdf\ListToPDF.java
D:\lib\Library-Assistant\src\library\assistant\ui\about\AboutController.java
D:\lib\Library-Assistant\src\library\assistant\ui\addbook\BookAddController.java
D:\lib\Library-Assistant\src\library\assistant\ui\addbook\LibraryAssistant.java
D:\lib\Library-Assistant\src\library\assistant\ui\addmember\MemberAddController.java
D:\lib\Library-Assistant\src\library\assistant\ui\addmember\MemberAddLoader.java
D:\lib\Library-Assistant\src\library\assistant\ui\callback\BookReturnCallback.java
D:\lib\Library-Assistant\src\library\assistant\ui\issuedlist\IssuedListController.java
D:\lib\Library-Assistant\src\library\assistant\ui\issuedlist\IssuedListLoader.java
D:\lib\Library-Assistant\src\library\assistant\ui\listbook\BookListController.java
D:\lib\Library-Assistant\src\library\assistant\ui\listbook\BookListLoader.java
D:\lib\Library-Assistant\src\library\assistant\ui\listmember\MemberListController.java
D:\lib\Library-Assistant\src\library\assistant\ui\listmember\MemberListLoader.java
D:\lib\Library-Assistant\src\library\assistant\ui\login\LoginController.java
D:\lib\Library-Assistant\src\library\assistant\ui\mail\TestMailController.java
D:\lib\Library-Assistant\src\library\assistant\ui\main\Main.java
D:\lib\Library-Assistant\src\library\assistant\ui\main\MainController.java
D:\lib\Library-Assistant\src\library\assistant\ui\main\toolbar\ToolbarController.java
D:\lib\Library-Assistant\src\library\assistant\ui\notifoverdue\NotificationItem.java
D:\lib\Library-Assistant\src\library\assistant\ui\notifoverdue\OverdueNotificationController.java
D:\lib\Library-Assistant\src\library\assistant\ui\notifoverdue\OverdueNotificationLoader.java
D:\lib\Library-Assistant\src\library\assistant\ui\notifoverdue\emailsender\EmailSenderController.java
D:\lib\Library-Assistant\src\library\assistant\ui\settings\Preferences.java
D:\lib\Library-Assistant\src\library\assistant\ui\settings\SettingsController.java
D:\lib\Library-Assistant\src\library\assistant\ui\settings\SettingsLoader.java
D:\lib\Library-Assistant\src\library\assistant\util\ExportUtils.java
D:\lib\Library-Assistant\src\library\assistant\util\LibraryAssistantUtil.java
32 changes: 2 additions & 30 deletions src/library/assistant/alert/AlertMaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,36 +47,7 @@ public static void showErrorMessage(String title, String content) {
}

public static void showErrorMessage(Exception ex) {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error occured");
alert.setHeaderText("Error Occured");
alert.setContentText(ex.getLocalizedMessage());

StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
String exceptionText = sw.toString();

Label label = new Label("The exception stacktrace was:");

TextArea textArea = new TextArea(exceptionText);
textArea.setEditable(false);
textArea.setWrapText(true);

textArea.setMaxWidth(Double.MAX_VALUE);
textArea.setMaxHeight(Double.MAX_VALUE);
GridPane.setVgrow(textArea, Priority.ALWAYS);
GridPane.setHgrow(textArea, Priority.ALWAYS);

GridPane expContent = new GridPane();
expContent.setMaxWidth(Double.MAX_VALUE);
expContent.add(label, 0, 0);
expContent.add(textArea, 0, 1);

alert.getDialogPane().setExpandableContent(expContent);

styleAlert(alert);
alert.showAndWait();
showErrorMessage(ex, "Error Occured", ex.getLocalizedMessage());
}

public static void showErrorMessage(Exception ex, String title, String content) {
Expand Down Expand Up @@ -107,6 +78,7 @@ public static void showErrorMessage(Exception ex, String title, String content)
expContent.add(textArea, 0, 1);

alert.getDialogPane().setExpandableContent(expContent);
styleAlert(alert);
alert.showAndWait();
}

Expand Down
7 changes: 6 additions & 1 deletion src/library/assistant/database/DataHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package library.assistant.database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand All @@ -20,8 +21,12 @@ public class DataHelper {
private final static Logger LOGGER = LogManager.getLogger(DatabaseHandler.class.getName());

public static boolean insertNewBook(Book book) {
return insertNewBook(book, DatabaseHandler.getInstance().getConnection());
}

public static boolean insertNewBook(Book book, Connection conn) {
try {
PreparedStatement statement = DatabaseHandler.getInstance().getConnection().prepareStatement(
PreparedStatement statement = conn.prepareStatement(
"INSERT INTO BOOK(id,title,author,publisher,isAvail) VALUES(?,?,?,?,?)");
statement.setString(1, book.getId());
statement.setString(2, book.getTitle());
Expand Down
14 changes: 5 additions & 9 deletions src/library/assistant/encryption/EncryptionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Random;
import java.security.SecureRandom;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.crypto.Cipher;
Expand Down Expand Up @@ -117,11 +114,10 @@ private static byte[] generateSecureKey() throws NoSuchAlgorithmException {
return data;
}

private static byte[] prepareIV() throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
String randomVal = String.valueOf(new Random(System.currentTimeMillis()).nextLong());
byte[] hash = digest.digest(randomVal.getBytes(StandardCharsets.UTF_8));
return Arrays.copyOfRange(hash, 0, 16);
private static byte[] prepareIV() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return iv;
}

private static void writeKey(CipherSpec spec) throws Exception {
Expand Down
9 changes: 9 additions & 0 deletions src/library/assistant/ui/addmember/MemberAddController.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ private void addMember(ActionEvent event) {
return;
}

if (!isValidEmail(mEmail)) {
AlertMaker.showMaterialDialog(rootPane, mainContainer, new ArrayList<>(), "Invalid Email", "Please enter a valid email address.");
return;
}

if (isInEditMode) {
handleUpdateMember();
return;
Expand Down Expand Up @@ -111,4 +116,8 @@ private void handleUpdateMember() {
}
}

private boolean isValidEmail(String email) {
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
return email.matches(emailRegex);
}
}
5 changes: 0 additions & 5 deletions src/library/assistant/ui/addmember/MemberAddLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package library.assistant.ui.addmember;

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
Expand All @@ -25,8 +24,4 @@ public void start(Stage stage) throws Exception {
stage.show();
}

public static void main(String[] args) {
launch(args);
}

}
Loading