or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-integration.mdimage-packaging.mdindex.mdjar-writing.mdlaunch-scripts.mdlayer-support.mdlayout-management.mdlibrary-management.mdmain-class-detection.mdrepackaging.md

main-class-detection.mddocs/

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.