I have generated a fat Jar with intellij and for some reason it cannot find the main class, outputting the error:
HappyBirthdayWisher\build\libs>java -jar HappyBirthdayWisher-1.0-SNAPSHOT.jar
Error: Could not find or load main class org.example.Main
Caused by: java.lang.ClassNotFoundException: org.example.Main
HappyBirthdayWisher\build\libs>java -cp HappyBirthdayWisher-1.0-SNAPSHOT.jar org.example.Main
Error: Could not find or load main class org.example.Main
Caused by: java.lang.ClassNotFoundException: org.example.Main
even though when I checked the files in the jar its clearly there: image of my Main.class location in the jar file
here is my manifest:
Manifest-Version: 1.0
Main-Class: org.example.Main
I would really appreciate any help!
Edit: here is the link to the contents of my jar file, its a lotta text https://jmp.sh/s/RPGx1PMdJHu8RWIj4V68 Edit: Also here is the original Main file
package org.example;
import io.github.cdimascio.dotenv.Dotenv;
import org.apache.poi.ss.usermodel.*;
import org.example.email.EmailSender;
import org.example.entity.Employee;
import org.example.mappings.TemplateMappings;
import org.example.thymeleaf.ThymeleafTemplateEngine;
import org.example.utils.AzureBlobUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.mail.MessagingException;
import java.io.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static void main(String[] args) throws MessagingException {
Dotenv dotenv = Dotenv.load();
String employeeInfoFileName = "birthdayWisherFiles/Employee Sample Data.xlsx";
String imageOrderFileName = "birthdayWisherFiles/logicFiles/birthday_image_order.txt";
String emailSentLogFileName = "birthdayWisherFiles/logicFiles/email_sent_log.txt";
String imageCounterFileName = "birthdayWisherFiles/logicFiles/image_counter.txt";
String birthDateColumn = "Birth Date";
String fullNameColumn = "Full Name";
String emailColumn = "Email";
// Azure Blob Storage SAS Token URL
String azureBlobUrl = dotenv.get("AZURE_BLOB_URL");
// Get today's month and day
MonthDay todayMonthDay = MonthDay.now();
Month currentMonth = todayMonthDay.getMonth();
try {
// Get the birthday havers of the day
List<Employee> employees = getTodaysBirthdayHavers(azureBlobUrl,employeeInfoFileName, birthDateColumn, fullNameColumn, emailColumn, todayMonthDay);
// Fetch and save image order if needed
List<String> imageFileNames = getOrUpdateImageOrder(imageOrderFileName, azureBlobUrl, currentMonth);
System.out.println("Employees with birthdays today: " + employees);
if (employees.isEmpty()) {
System.out.println("No birthdays today.");
return;
}
if (imageFileNames.isEmpty()) {
System.out.println("No images found in Azure Blob Storage.");
return;
}
// Initialize Thymeleaf template engine
TemplateEngine templateEngine = ThymeleafTemplateEngine.initializeTemplateEngine();
// Load or initialize the image counter
int[] imageCounter = loadImageCounter(azureBlobUrl,imageCounterFileName);
// Save into the list to later log it to a file
List<String> emailsSent = new ArrayList<>();
// Send email to each employee
for (Employee employee : employees) {
// Get image for employee according to the current image order
String selectedImage = getImageForEmail(imageFileNames, imageCounter);
//increment the counter after getting the image
incrementImageCounter(imageCounter,imageFileNames);
String fileNameWithoutExtension = selectedImage.substring(selectedImage.lastIndexOf("/") + 1, selectedImage.lastIndexOf("."));
String templateName = determineTemplate(fileNameWithoutExtension);
// Create the Thymeleaf context and render the appropriate template
assert azureBlobUrl != null;
String selectedImageURl = AzureBlobUtils.getAzureBlobImageUrl(azureBlobUrl,selectedImage);
Context context = ThymeleafTemplateEngine.createContext(selectedImageURl,employee.getFullName());
String htmlContent = ThymeleafTemplateEngine.renderTemplate(templateEngine, templateName, context);
String recipient = employee.getEmail();
String subject = "Happy Birthday, " + employee.getFullName() + "!";
// Send the email
EmailSender.sendEmail(recipient, subject, htmlContent);
// add to the list the emails with a timestamp
saveEmailSentToList(employee.getFullName(),recipient,selectedImage, emailsSent);
}
// Append the list to file
appendEmailSentListToFile(azureBlobUrl,emailSentLogFileName, emailsSent);
// Save the updated image counter
saveImageCounterToFile(azureBlobUrl,imageCounterFileName, imageCounter[0]);
} catch (IOException e) {
e.printStackTrace();
}
}
//Excel stuffs
private static List<Employee> getTodaysBirthdayHavers(String blobURL, String filePath, String birthDateColumn,
String fullNameColumn, String emailColumn, MonthDay todayMonthDay) throws IOException {
try (InputStream inputStream = AzureBlobUtils.getFileInputStream(blobURL, filePath);
Workbook workbook = WorkbookFactory.create(inputStream)) {
Sheet sheet = workbook.getSheetAt(0); // Get the first sheet
if (sheet == null) {
System.out.println("No sheets found in the workbook.");
return new ArrayList<>();
}
int birthDateColumnIndex = findColumnIndex(sheet, birthDateColumn);
int fullNameColumnIndex = findColumnIndex(sheet, fullNameColumn);
int emailColumnIndex = findColumnIndex(sheet, emailColumn);
if (birthDateColumnIndex == -1 || fullNameColumnIndex == -1 || emailColumnIndex == -1) {
System.out.println("One or more required columns not found.");
return new ArrayList<>();
}
return getMatchingEmployees(sheet, birthDateColumnIndex, fullNameColumnIndex, emailColumnIndex, todayMonthDay);
}
}
private static int findColumnIndex(Sheet sheet, String targetColumn) {
Row headerRow = sheet.getRow(0);
if (headerRow == null) {
return -1;
}
for (Cell cell : headerRow) {
if (cell.getCellType() == CellType.STRING && targetColumn.equals(cell.getStringCellValue())) {
return cell.getColumnIndex();
}
}
return -1;
}
private static List<Employee> getMatchingEmployees(Sheet sheet, int birthDateColumnIndex, int fullNameColumnIndex, int emailColumnIndex, MonthDay todayMonthDay) {
List<Employee> employees = new ArrayList<>();
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (row != null) {
Cell birthDateCell = row.getCell(birthDateColumnIndex);
Cell fullNameCell = row.getCell(fullNameColumnIndex);
Cell emailCell = row.getCell(emailColumnIndex);
if (birthDateCell != null && isDateCell(birthDateCell)) {
LocalDate birthDate = convertToLocalDate(birthDateCell.getDateCellValue());
MonthDay birthMonthDay = MonthDay.from(birthDate);
if (birthMonthDay.equals(todayMonthDay)) {
String fullName = fullNameCell != null ? fullNameCell.getStringCellValue() : "Unknown";
String email = emailCell != null ? emailCell.getStringCellValue() : "Unknown";
employees.add(new Employee(fullName, email, birthDate));
}
}
}
}
return employees;
}
private static boolean isDateCell(Cell cell) {
return cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell);
}
private static LocalDate convertToLocalDate(java.util.Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
private static List<String> getOrUpdateImageOrder(String imageOrderFileName,
String azureBlobUrl,
Month currentMonth) throws IOException {
String fileContent = AzureBlobUtils.readTextFileFromBlob(azureBlobUrl, imageOrderFileName);
List<String> fileNames;
if (fileContent != null && !fileContent.isEmpty()) {
// Split the string into lines
List<String> lines = new ArrayList<>(List.of(fileContent.split("\\R"))); // "\\R" matches line breaks
if (!lines.isEmpty() && Month.valueOf(lines.get(0).toUpperCase()).equals(currentMonth)) {
System.out.println("Current month's image order is already saved.");
return lines.subList(1, lines.size());
}
}
// Fetch new randomized image order
fileNames = AzureBlobUtils.listBlobFiles(azureBlobUrl, "birthdayWisherFiles/birthdayImages/");
Collections.shuffle(fileNames);
// Save the current month and image order to a new string
List<String> newContent = new ArrayList<>();
newContent.add(currentMonth.name());
newContent.addAll(fileNames);
// Convert newContent to a single string (e.g., to save it back to a file or database)
String updatedFileContent = String.join(System.lineSeparator(), newContent);
// Replace this with actual logic to save updatedFileContent somewhere
AzureBlobUtils.writeTextFileToBlob(azureBlobUrl,imageOrderFileName, updatedFileContent);
return fileNames;
}
private static int[] loadImageCounter(String azureBlobUrl,String counterFilePath) throws IOException {
String fileContent = AzureBlobUtils.readTextFileFromBlob(azureBlobUrl, counterFilePath);
if (fileContent != null && !fileContent.isEmpty()) {
String counterValue = fileContent.trim();
return new int[]{Integer.parseInt(counterValue)};
}
return new int[]{0};
}
private static void saveEmailSentToList(String employeeName, String employeeEmail, String image, List<String> emailsSent) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String timestamp = LocalDateTime.now().format(dateTimeFormatter);
String emailLog = "[" + timestamp + "] " + employeeName+ " (" + employeeEmail + ") -> " + image;
emailsSent.add(emailLog);
}
private static void appendEmailSentListToFile(String azureBlobUrl,String emailLogPath,List<String> emailsSent) throws IOException {
String emailLog = String.join(System.lineSeparator(), emailsSent);
AzureBlobUtils.appendToFileInBlob(azureBlobUrl,emailLogPath, emailLog);
}
private static void saveImageCounterToFile(String azureBlobUrl, String counterFilePath, int counter) throws IOException {
AzureBlobUtils.writeTextFileToBlob(azureBlobUrl,counterFilePath, Integer.toString(counter));
}
private static String getImageForEmail(List<String> imageFileNames, int[] imageCounter) {
// Get the current index
int currentIndex = imageCounter[0];
// Get the image at the current index
return imageFileNames.get(currentIndex);
}
private static void incrementImageCounter(int[] imageCounter,List<String> imageFileNames) {
int currentIndex = imageCounter[0];
imageCounter[0] = (currentIndex + 1) % imageFileNames.size();
}
private static String determineTemplate(String imageName){
for (String key: TemplateMappings.TEMPLATE_MAP.keySet()) {
if(imageName.endsWith(key)){
return TemplateMappings.TEMPLATE_MAP.get(key);
}
}
return "birthdayTemplateM1";
}
}
Edit: here is my build.gradle:
plugins {
id 'java'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
sourceSets {
main {
java {
srcDirs = ['src/main/java']
}
}
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
// Apache POI for Excel manipulation
implementation group: 'org.apache.poi', name: 'poi', version: '5.4.0'
implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '5.4.0'
// Thymeleaf for email templates
implementation group: 'org.thymeleaf', name: 'thymeleaf', version: '3.1.3.RELEASE'
// Javax Mail for email sending
implementation group: 'javax.mail', name: 'mail', version: '1.5.0-b01'
// AWS SDK for S3
implementation group: 'software.amazon.awssdk', name: 's3', version: '2.30.17'
// AWS SDK Core (v2)
implementation group: 'software.amazon.awssdk', name: 'core', version: '2.30.17'
// https://mvnrepository.com/artifact/com.azure/azure-storage-blob
implementation group: 'com.azure', name: 'azure-storage-blob', version: '12.29.0'
// https://mvnrepository.com/artifact/io.github.cdimascio/java-dotenv
implementation group: 'io.github.cdimascio', name: 'java-dotenv', version: '5.2.2'
implementation 'org.slf4j:slf4j-api:2.0.9' // Ensure you have SLF4J API
implementation 'ch.qos.logback:logback-classic:1.4.11' // Use Logback as the provider
}
jar{
manifest {
attributes('Main-Class' : 'org.example.Main')
}
from {
configurations.runtimeClasspath.collect{it.isDirectory() ? it : zipTree(it)}
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
test {
useJUnitPlatform()
}
as for how I build the jar file, all I did was ./gradlew jar EDIT: Sorry for late reply guys it was super late yesterday and I just kinda checked out
A big thank you for all your help, after configuring the shadow plug in and running ./gradlew shadowJar, the issue was resolved. Added these to the build.gradle
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
shadowJar {
archiveClassifier.set('')
manifest {
attributes('Main-Class': 'org.example.Main')
}
}