0

I'm making an auto-clicker app that runs in the background with jnativehook in the tray, which can open up a java.awt app when the icon is clicked. That whole part works when run from vscode with run java. When I packaged the project with mvn clean package to get the shaded executable JAR, the icon image for the app looks weird. (it was working fine when run from vscode). I also tried the exe application in the target folder, but I got the same results.

I saw that the icon in the pom.xml had to be an ICO file instead of a PNG, so I converted the 16x16 image with https://convertico.com/ and it still doesn't work.

I also tried just taking out the icon in the pom.xml since it was already defined in the tray part of the Java files, but I saw no change.

When run from my main file in vscode from run java picture

when run from the auto-clicker-1.0-SNAPSHOT-shaded.jar or the auto-clicker-1.0-SNAPSHOT.exe file in the target folder of the maven project picture

My Tray.java file where I handle the tray and set the icon image for the app that works perfectly when run from the main file:

package com.autoclicker;
import java.awt.*;
import java.awt.event.*;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
public class Tray {
 private TrayIcon trayIcon;
 private App app;
 private SettingsManager settingsManager;
 public Tray() {
 setupTray();
 app = new App();
 settingsManager = new SettingsManager();
 // Initialize global key listener using current settings
 new KeyListener();
 }
 /**
 * Sets up the system tray icon and its associated context menu.
 * If system tray is not supported, this method does nothing.
 * The system tray icon is set to the image located at
 * src/main/res/img/icon.png.
 * The context menu has one item: "Exit", which exits the application
 * and unregisters the native hook.
 */
 private void setupTray() {
 if (!SystemTray.isSupported()) {
 System.err.println("System tray is not supported.");
 return;
 }
 SystemTray tray = SystemTray.getSystemTray();
 Image image = Toolkit.getDefaultToolkit().getImage("src\\main\\res\\img\\icon.png");
 trayIcon = new TrayIcon(image, "Auto Clicker");
 trayIcon.setImageAutoSize(true);
 trayIcon.addMouseListener(new MouseAdapter() {
 @Override
 public void mouseClicked(MouseEvent e) {
 // Left-click to open the settings window
 if (e.getButton() == MouseEvent.BUTTON1) {
 app.showSettingsWindow();
 }
 }
 });
 PopupMenu popup = new PopupMenu();
 MenuItem exitItem = new MenuItem("Exit");
 exitItem.addActionListener(e -> {
 try {
 GlobalScreen.unregisterNativeHook();
 } catch (NativeHookException ex) {
 ex.printStackTrace();
 }
 System.exit(0);
 });
 popup.add(exitItem);
 trayIcon.setPopupMenu(popup);
 try {
 tray.add(trayIcon);
 } catch (AWTException e) {
 System.err.println("TrayIcon could not be added.");
 e.printStackTrace();
 }
 }
}

My pom.xml, which is where I set the icon for the project (I don't know very much about pom.xml):

<?xml version="1.0" encoding="UTF-8"?>
<project 
 xmlns="http://maven.apache.org/POM/4.0.0" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
 <modelVersion>4.0.0</modelVersion>
 
 <groupId>com.autoclicker</groupId>
 <artifactId>auto-clicker</artifactId>
 <version>1.0-SNAPSHOT</version>
 
 <name>auto-clicker</name>
 <!-- FIXME: change to your project's website -->
 <url>http://www.example.com</url>
 
 <properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <maven.compiler.source>1.8</maven.compiler.source>
 <maven.compiler.target>1.8</maven.compiler.target>
 </properties>
 
 <dependencies>
 <!-- Test Dependency -->
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.11</version>
 <scope>test</scope>
 </dependency>
 <!-- JNativeHook (global mouse/keyboard listeners) -->
 <dependency>
 <groupId>com.1stleg</groupId>
 <artifactId>jnativehook</artifactId>
 <version>2.1.0</version>
 </dependency>
 <!-- Gson for JSON parsing -->
 <dependency>
 <groupId>com.google.code.gson</groupId>
 <artifactId>gson</artifactId>
 <version>2.12.1</version>
 </dependency>
 </dependencies>
 
 <build>
 <pluginManagement>
 <!-- Lock down plugin versions for standard lifecycle plugins -->
 <plugins>
 <plugin>
 <artifactId>maven-clean-plugin</artifactId>
 <version>3.1.0</version>
 </plugin>
 <plugin>
 <artifactId>maven-resources-plugin</artifactId>
 <version>3.0.2</version>
 </plugin>
 <plugin>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>3.8.0</version>
 </plugin>
 <plugin>
 <artifactId>maven-surefire-plugin</artifactId>
 <version>2.22.1</version>
 </plugin>
 <plugin>
 <artifactId>maven-jar-plugin</artifactId>
 <version>3.0.2</version>
 </plugin>
 <plugin>
 <artifactId>maven-install-plugin</artifactId>
 <version>2.5.2</version>
 </plugin>
 <plugin>
 <artifactId>maven-deploy-plugin</artifactId>
 <version>2.8.2</version>
 </plugin>
 <plugin>
 <artifactId>maven-site-plugin</artifactId>
 <version>3.7.1</version>
 </plugin>
 <plugin>
 <artifactId>maven-project-info-reports-plugin</artifactId>
 <version>3.0.0</version>
 </plugin>
 </plugins>
 </pluginManagement>
 
 <plugins>
 <!-- Maven Shade Plugin: creates an uber-jar -->
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-shade-plugin</artifactId>
 <version>3.2.4</version>
 <executions>
 <execution>
 <phase>package</phase>
 <goals>
 <goal>shade</goal>
 </goals>
 <configuration>
 <!-- Attach the shaded JAR with classifier "shaded" -->
 <shadedArtifactAttached>true</shadedArtifactAttached>
 <shadedClassifierName>shaded</shadedClassifierName>
 <transformers>
 <!-- Add the manifest specifying your main class (change this if necessary) -->
 <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
 <mainClass>com.autoclicker.Main</mainClass>
 </transformer>
 </transformers>
 </configuration>
 </execution>
 </executions>
 </plugin>
 
 <!-- Launch4j Maven Plugin: wraps the shaded JAR into a Windows EXE -->
 <plugin>
 <groupId>com.akathist.maven.plugins.launch4j</groupId>
 <artifactId>launch4j-maven-plugin</artifactId>
 <version>2.5.3</version>
 <executions>
 <execution>
 <id>l4j-wrapper</id>
 <phase>package</phase>
 <goals>
 <goal>launch4j</goal>
 </goals>
 </execution>
 </executions>
 <configuration>
 <jar>${project.build.directory}/${project.build.finalName}-shaded.jar</jar>
 <outfile>${project.build.directory}/${project.build.finalName}.exe</outfile>
 <headerType>gui</headerType>
 <chdir>.</chdir>
 <icon>src/main/res/img/icon.ico</icon>
 <jre>
 <minVersion>1.8.0</minVersion>
 </jre>
 </configuration>
 </plugin>
 
 </plugins>
 </build>
 
</project>

The main file is simply running the Tray.java file which runs the App.java file when clicked on in the system tray:

package com.autoclicker;
import javax.swing.SwingUtilities;
public class Main {
 /**
 * Main entry point of the application.
 *
 * This method creates a new instance of the Tray class, which sets up the
 * system tray and the settings window.
 *
 * @param args Command line arguments (not used)
 */
 public static void main(String[] args) {
 SwingUtilities.invokeLater(() -> new Tray());
 }
}

Here's the repo: https://github.com/JeromeyDynamics/Auto-Clicker-App

Minimal reproducible example

import java.awt.*;
import java.awt.event.*;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
public class Tray {
 public static void main(String[] args) {
 SystemTray tray = SystemTray.getSystemTray();
 Image image = Toolkit.getDefaultToolkit().getImage("src\\main\\res\\img\\icon.png");
 TrayIcon trayIcon = new TrayIcon(image, "Auto Clicker");
 trayIcon.setImageAutoSize(true);
 PopupMenu popup = new PopupMenu();
 MenuItem exitItem = new MenuItem("Exit");
 exitItem.addActionListener(e -> {
 try {
 GlobalScreen.unregisterNativeHook();
 } catch (NativeHookException ex) {
 ex.printStackTrace();
 }
 System.exit(0);
 });
 popup.add(exitItem);
 trayIcon.setPopupMenu(popup);
 try {
 tray.add(trayIcon);
 } catch (AWTException e) {
 System.err.println("TrayIcon could not be added.");
 e.printStackTrace();
 }
 }
}
Mark Rotteveel
110k241 gold badges160 silver badges233 bronze badges
asked Apr 15, 2025 at 4:36
4
  • 1
    When you create a JAR file that I assume contains file icon.png, the path to that file is not src\main\res\img\icon.png Commented Apr 15, 2025 at 4:38
  • How to bundle images in jar file Commented Apr 15, 2025 at 4:44
  • from target folder, it is trying to get the absolute path - src\\main\\res\\img\\icon.png. can you try adding a relative path or creating the folder structure inside the target folder where you are running the .exe Commented Apr 15, 2025 at 4:46
  • Load your icon as a resource as Abra says. You should use ImageIO Commented Apr 15, 2025 at 7:43

1 Answer 1

1

https://github.com/JeromeyDynamics/Auto-Clicker-App

Project Diectory

Your version

Auto-Clicker-App-main
├── pom.xml
└── src
 ├── main
 │  ├── java
...
 │  └── res
 │  └── img
 │  ├── icon.ico
 │  └── icon.png

Change your icon directory to follow Maven conventions.

Auto-Clicker-App-main
├── pom.xml
└── src
 ├── main
 │  ├── java
...
 │  └── resources
 │  ├── icon.ico
 │  └── icon.png
...

pom.xml

I also made some adjustments to the pom.xml based on my usual Maven configuration practice: if the default settings work, there's no need to override them. The goal is to keep the pom.xml as minimal and clean as possible.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.autoclicker</groupId>
 <artifactId>auto-clicker</artifactId>
 <version>1.0-SNAPSHOT</version>
 <name>auto-clicker</name>
 <description>Demo App Project</description>
 
 <properties>
 <maven.compiler.source>1.8</maven.compiler.source>
 <maven.compiler.target>1.8</maven.compiler.target>
 <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 </properties>
 <dependencies>
 <!-- Test Dependency -->
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.11</version>
 <scope>test</scope>
 </dependency>
 
 <!-- JNativeHook (global mouse/keyboard listeners) -->
 <dependency>
 <groupId>com.1stleg</groupId>
 <artifactId>jnativehook</artifactId>
 <version>2.1.0</version>
 </dependency>
 
 <!-- Gson for JSON parsing -->
 <dependency>
 <groupId>com.google.code.gson</groupId>
 <artifactId>gson</artifactId>
 <version>2.12.1</version>
 </dependency>
 </dependencies>
 <build>
 <plugins>
 <!-- Maven Shade Plugin: creates an uber-jar -->
 <plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-shade-plugin</artifactId>
 <version>3.6.0</version>
 <executions>
 <execution>
 <phase>package</phase>
 <goals>
 <goal>shade</goal>
 </goals>
 <configuration>
 <!-- Attach the shaded JAR with classifier "shaded" -->
 <shadedArtifactAttached>true</shadedArtifactAttached>
 <shadedClassifierName>shaded</shadedClassifierName>
 <transformers>
 <!-- Add the manifest specifying your main class (change this if necessary) -->
 <transformer
 implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
 <mainClass>com.autoclicker.Main</mainClass>
 </transformer>
 </transformers>
 </configuration>
 </execution>
 </executions>
 </plugin>
 <!-- Launch4j Maven Plugin: wraps the shaded JAR into a Windows EXE -->
 <plugin>
 <groupId>com.akathist.maven.plugins.launch4j</groupId>
 <artifactId>launch4j-maven-plugin</artifactId>
 <version>2.5.3</version>
 <executions>
 <execution>
 <id>l4j-wrapper</id>
 <phase>package</phase>
 <goals>
 <goal>launch4j</goal>
 </goals>
 </execution>
 </executions>
 <configuration>
 <jar>${project.build.directory}/${project.build.finalName}-shaded.jar</jar>
 <outfile>${project.build.directory}/${project.build.finalName}.exe</outfile>
 <!--
 <headerType>console</headerType>
 -->
 <headerType>gui</headerType>
 <chdir>.</chdir>
 <icon>src/main/resources/icon.ico</icon>
 <jre>
 <minVersion>1.8.0</minVersion>
 </jre>
 </configuration>
 </plugin>
 </plugins>
 </build>
</project>

Also, when debugging, please change the headerType of the launch4j-maven-plugin to console by setting <headerType>console</headerType>.

When headerType is set to gui, any runtime errors may not be shown because the application runs as a GUI, which hides console output.

Therefore, during development and debugging, it's recommended to set headerType to console.

Main.java

Add a shared method named loadIcon in Main.java to load the icon.

package com.autoclicker;
import javax.swing.*;
import java.awt.*;
import java.net.URL;
public class Main {
 public static Image loadIcon(String path) {
 URL imageUrl = Main.class.getResource(path);
 if (imageUrl != null) {
 return Toolkit.getDefaultToolkit().getImage(imageUrl);
 } else {
 System.err.println("Icon not found:" + path);
 return null;
 }
 }
 /**
 * Main entry point of the application.
 * <p>
 * This method creates a new instance of the Tray class, which sets up the
 * system tray and the settings window.
 *
 * @param args Command line arguments (not used)
 */
 public static void main(String[] args) {
 SwingUtilities.invokeLater(() -> new Tray());
 }
}

App.java

There are two changes made here:

  1. The icon is now loaded by calling Main.loadIcon() instead of loading it directly.

  2. The frame icon is explicitly set to your icon.png. (If not set, it defaults to the standard Java coffee cup icon.)

Additionally, the original code attempted to load the icon using the incorrect file name pixil-frame-0.png, whereas the actual file is named icon.png. This mismatch was the reason the file couldn't be found.

App.java

// Image icon = Toolkit.getDefaultToolkit().getImage("auto-clicker\\src\\main\\res\\img\\pixil-frame-0.png");
 
Image icon = Main.loadIcon("/icon.png");
if (icon != null) {
 settingsFrame.setIconImage(icon); // ✅ SET ICON
}

Tray.java

Tray.java

// Image image = Toolkit.getDefaultToolkit().getImage("src\\main\\res\\img\\icon.png");
Image image = Main.loadIcon("/icon.png");

Build

mvn clean package

Run exe

enter image description here

You can see that the tray icon in the bottom-right corner is now displayed correctly. The frame icon in the top-left corner has been updated to icon.png, and the icon for the label has also been changed.

answered Apr 15, 2025 at 9:45
Sign up to request clarification or add additional context in comments.

1 Comment

@JeromeHillock , If this answer solved your problem, you can mark it as accepted.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.