0
# Main Class Detection
1
2
Automatic discovery of classes with public static main methods using breadth-first search algorithms. The main class finder can analyze both directory structures and JAR files, with support for annotation-based filtering and validation.
3
4
## Capabilities
5
6
### Directory-Based Detection
7
8
Find main classes by scanning directory structures containing compiled Java classes.
9
10
```java { .api }
11
public abstract class MainClassFinder {
12
/**
13
* Find the first main class in the given directory.
14
* Uses breadth-first search to locate classes with public static main methods.
15
*
16
* @param rootDirectory the root directory to search
17
* @return the fully qualified name of the first main class found, or null if none found
18
* @throws IOException if directory scanning fails
19
*/
20
public static String findMainClass(File rootDirectory) throws IOException;
21
22
/**
23
* Find a single main class in the given directory.
24
* Throws an exception if multiple main classes are found.
25
*
26
* @param rootDirectory the root directory to search
27
* @return the fully qualified name of the single main class
28
* @throws IOException if directory scanning fails
29
* @throws IllegalStateException if multiple main classes are found
30
*/
31
public static String findSingleMainClass(File rootDirectory) throws IOException;
32
33
/**
34
* Find a single main class with a specific annotation.
35
* Filters main classes to only include those with the specified annotation.
36
*
37
* @param rootDirectory the root directory to search
38
* @param annotationName the fully qualified annotation name to filter by
39
* @return the fully qualified name of the annotated main class
40
* @throws IOException if directory scanning fails
41
* @throws IllegalStateException if multiple annotated main classes are found
42
*/
43
public static String findSingleMainClass(File rootDirectory, String annotationName) throws IOException;
44
}
45
```
46
47
### JAR-Based Detection
48
49
Find main classes by analyzing compiled classes within JAR files.
50
51
```java { .api }
52
public abstract class MainClassFinder {
53
/**
54
* Find the first main class in the given JAR file.
55
* Searches within the specified classes location inside the JAR.
56
*
57
* @param jarFile the JAR file to search
58
* @param classesLocation the location within the JAR where classes are stored (e.g., "BOOT-INF/classes/")
59
* @return the fully qualified name of the first main class found, or null if none found
60
* @throws IOException if JAR reading fails
61
*/
62
public static String findMainClass(JarFile jarFile, String classesLocation) throws IOException;
63
64
/**
65
* Find a single main class in the given JAR file.
66
* Throws an exception if multiple main classes are found.
67
*
68
* @param jarFile the JAR file to search
69
* @param classesLocation the location within the JAR where classes are stored
70
* @return the fully qualified name of the single main class
71
* @throws IOException if JAR reading fails
72
* @throws IllegalStateException if multiple main classes are found
73
*/
74
public static String findSingleMainClass(JarFile jarFile, String classesLocation) throws IOException;
75
76
/**
77
* Find a single main class with a specific annotation in a JAR file.
78
* Filters main classes to only include those with the specified annotation.
79
*
80
* @param jarFile the JAR file to search
81
* @param classesLocation the location within the JAR where classes are stored
82
* @param annotationName the fully qualified annotation name to filter by
83
* @return the fully qualified name of the annotated main class
84
* @throws IOException if JAR reading fails
85
* @throws IllegalStateException if multiple annotated main classes are found
86
*/
87
public static String findSingleMainClass(JarFile jarFile, String classesLocation, String annotationName) throws IOException;
88
}
89
```
90
91
## Usage Examples
92
93
### Basic Directory Scanning
94
95
```java
96
import org.springframework.boot.loader.tools.MainClassFinder;
97
import java.io.File;
98
import java.io.IOException;
99
100
// Find any main class in the compiled classes directory
101
File classesDir = new File("target/classes");
102
try {
103
String mainClass = MainClassFinder.findMainClass(classesDir);
104
if (mainClass != null) {
105
System.out.println("Found main class: " + mainClass);
106
} else {
107
System.out.println("No main class found");
108
}
109
} catch (IOException e) {
110
System.err.println("Error scanning directory: " + e.getMessage());
111
}
112
```
113
114
### Single Main Class Detection
115
116
```java
117
import org.springframework.boot.loader.tools.MainClassFinder;
118
import java.io.File;
119
120
// Ensure only one main class exists
121
File classesDir = new File("target/classes");
122
try {
123
String mainClass = MainClassFinder.findSingleMainClass(classesDir);
124
System.out.println("Application main class: " + mainClass);
125
} catch (IllegalStateException e) {
126
System.err.println("Multiple main classes found: " + e.getMessage());
127
} catch (IOException e) {
128
System.err.println("Error scanning directory: " + e.getMessage());
129
}
130
```
131
132
### Annotation-Based Filtering
133
134
```java
135
import org.springframework.boot.loader.tools.MainClassFinder;
136
import java.io.File;
137
138
// Find main class with Spring Boot annotation
139
File classesDir = new File("target/classes");
140
try {
141
String mainClass = MainClassFinder.findSingleMainClass(
142
classesDir,
143
"org.springframework.boot.autoconfigure.SpringBootApplication"
144
);
145
System.out.println("Spring Boot main class: " + mainClass);
146
} catch (IllegalStateException e) {
147
System.err.println("Multiple Spring Boot applications found: " + e.getMessage());
148
} catch (IOException e) {
149
System.err.println("Error scanning directory: " + e.getMessage());
150
}
151
```
152
153
### JAR File Analysis
154
155
```java
156
import org.springframework.boot.loader.tools.MainClassFinder;
157
import java.io.File;
158
import java.util.jar.JarFile;
159
160
// Analyze an existing JAR file
161
File jarFile = new File("myapp.jar");
162
try (JarFile jar = new JarFile(jarFile)) {
163
// Standard location for classes in a Spring Boot JAR
164
String classesLocation = "BOOT-INF/classes/";
165
166
String mainClass = MainClassFinder.findMainClass(jar, classesLocation);
167
if (mainClass != null) {
168
System.out.println("JAR main class: " + mainClass);
169
}
170
171
// For regular JAR files, classes are typically at the root
172
String regularLocation = "";
173
String regularMainClass = MainClassFinder.findMainClass(jar, regularLocation);
174
if (regularMainClass != null) {
175
System.out.println("Regular JAR main class: " + regularMainClass);
176
}
177
} catch (IOException e) {
178
System.err.println("Error reading JAR file: " + e.getMessage());
179
}
180
```
181
182
### Integration with Repackaging
183
184
```java
185
import org.springframework.boot.loader.tools.*;
186
import java.io.File;
187
188
// Automatic main class detection during repackaging
189
File sourceJar = new File("myapp.jar");
190
Repackager repackager = new Repackager(sourceJar);
191
192
// Add timeout warning for main class detection
193
repackager.addMainClassTimeoutWarningListener((duration, mainClass) -> {
194
if (duration.toSeconds() > 30) {
195
System.out.println("Main class detection is taking longer than expected...");
196
}
197
if (mainClass == null) {
198
System.err.println("Warning: No main class found after " + duration.toMillis() + "ms");
199
} else {
200
System.out.println("Detected main class: " + mainClass + " (took " + duration.toMillis() + "ms)");
201
}
202
});
203
204
// Let repackager automatically detect main class
205
repackager.repackage(Libraries.NONE);
206
```
207
208
### Manual Main Class Override
209
210
```java
211
import org.springframework.boot.loader.tools.*;
212
import java.io.File;
213
214
// Override automatic detection with explicit main class
215
File sourceJar = new File("myapp.jar");
216
Repackager repackager = new Repackager(sourceJar);
217
218
// Manually specify main class to skip detection
219
repackager.setMainClass("com.example.MyApplication");
220
221
// Repackage with specified main class
222
repackager.repackage(Libraries.NONE);
223
```
224
225
## Search Algorithm
226
227
The main class finder uses a breadth-first search algorithm that:
228
229
1. **Scans class files** in the target location (directory or JAR)
230
2. **Analyzes bytecode** to identify classes with `public static void main(String[] args)` methods
231
3. **Filters by annotations** when specified, checking class-level annotations
232
4. **Returns results** in discovery order, with validation for single-class requirements
233
5. **Handles errors** gracefully, continuing search even if individual class files are corrupted
234
235
The breadth-first approach ensures that classes in the root package are found before classes in nested packages, which typically aligns with application structure conventions.