Search code examples
javajar

Jar can't find the main class


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


Solution

  • 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')
        }
    }