or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdjackson-modules.mdobject-mapper-factory.mdproperty-naming.mdsubtype-discovery.md

subtype-discovery.mddocs/

0

# Subtype Discovery

1

2

Dropwizard Jackson provides a service-based discovery mechanism for polymorphic configurations, allowing runtime loading of Jackson subtypes through META-INF/services files.

3

4

## DiscoverableSubtypeResolver

5

6

A Jackson subtype resolver that discovers subtypes via META-INF/services configuration files.

7

8

```java { .api }

9

public class DiscoverableSubtypeResolver extends StdSubtypeResolver {

10

public DiscoverableSubtypeResolver()

11

public DiscoverableSubtypeResolver(Class<?> rootKlass)

12

public List<Class<?>> getDiscoveredSubtypes()

13

protected ClassLoader getClassLoader()

14

protected List<Class<?>> discoverServices(Class<?> klass)

15

}

16

```

17

18

### Constructors

19

20

**Default Constructor**:

21

```java { .api }

22

public DiscoverableSubtypeResolver()

23

```

24

Constructs a resolver that scans for subtypes of the `Discoverable` interface.

25

26

**Custom Root Class Constructor**:

27

```java { .api }

28

public DiscoverableSubtypeResolver(Class<?> rootKlass)

29

```

30

Constructs a resolver that scans for subtypes of the provided root class.

31

32

**Parameters**:

33

- `rootKlass` (`Class<?>`): The class to use for selecting the correct META-INF/services file

34

35

### Instance Methods

36

37

**getDiscoveredSubtypes()**:

38

```java { .api }

39

public List<Class<?>> getDiscoveredSubtypes()

40

```

41

Returns the list of subtypes discovered from META-INF configuration files.

42

43

**Returns**: `List<Class<?>>` of discovered subtype classes

44

45

**getClassLoader()**:

46

```java { .api }

47

protected ClassLoader getClassLoader()

48

```

49

Returns the ClassLoader from the current class for resource loading.

50

51

**Returns**: Current `ClassLoader` instance

52

53

**discoverServices()**:

54

```java { .api }

55

protected List<Class<?>> discoverServices(Class<?> klass)

56

```

57

Discovers services in META-INF/services folder for the provided class.

58

59

**Parameters**:

60

- `klass` (`Class<?>`): The class to lookup services for

61

62

**Returns**: `List<Class<?>>` of discovered service classes

63

64

## Discoverable Interface

65

66

A tag interface that allows Dropwizard to load Jackson subtypes at runtime for polymorphic configurations.

67

68

```java { .api }

69

public interface Discoverable {

70

}

71

```

72

73

**Purpose**: Marker interface for classes that should be automatically discovered as Jackson subtypes.

74

75

## Usage Examples

76

77

### Basic Polymorphic Configuration

78

79

```java

80

// Base configuration class

81

public abstract class DatabaseConfig implements Discoverable {

82

public abstract String getType();

83

}

84

85

// PostgreSQL implementation

86

public class PostgreSQLConfig extends DatabaseConfig {

87

private String host;

88

private int port;

89

private String database;

90

91

@Override

92

public String getType() {

93

return "postgresql";

94

}

95

96

// constructors, getters, setters...

97

}

98

99

// MySQL implementation

100

public class MySQLConfig extends DatabaseConfig {

101

private String connectionUrl;

102

private String driver;

103

104

@Override

105

public String getType() {

106

return "mysql";

107

}

108

109

// constructors, getters, setters...

110

}

111

```

112

113

### META-INF/services Configuration

114

115

Create a file at `META-INF/services/io.dropwizard.jackson.Discoverable`:

116

117

```

118

com.example.config.PostgreSQLConfig

119

com.example.config.MySQLConfig

120

```

121

122

### JSON Deserialization

123

124

```java

125

ObjectMapper mapper = Jackson.newObjectMapper();

126

// DiscoverableSubtypeResolver is automatically configured

127

128

// JSON with type information

129

String json = """

130

{

131

"type": "postgresql",

132

"host": "localhost",

133

"port": 5432,

134

"database": "myapp"

135

}

136

""";

137

138

DatabaseConfig config = mapper.readValue(json, DatabaseConfig.class);

139

// Automatically deserializes to PostgreSQLConfig instance

140

```

141

142

### Custom Root Class Discovery

143

144

```java

145

// Custom base class for plugin discovery

146

public abstract class Plugin {

147

public abstract String getName();

148

}

149

150

// Plugin implementation

151

public class LoggingPlugin extends Plugin {

152

private String logLevel;

153

154

@Override

155

public String getName() {

156

return "logging";

157

}

158

159

// implementation...

160

}

161

162

// Custom resolver for Plugin subtypes

163

DiscoverableSubtypeResolver pluginResolver = new DiscoverableSubtypeResolver(Plugin.class);

164

List<Class<?>> pluginTypes = pluginResolver.getDiscoveredSubtypes();

165

166

// Register with ObjectMapper

167

ObjectMapper mapper = new ObjectMapper();

168

mapper.setSubtypeResolver(pluginResolver);

169

```

170

171

### Service File for Custom Root Class

172

173

Create `META-INF/services/com.example.Plugin`:

174

175

```

176

com.example.plugins.LoggingPlugin

177

com.example.plugins.SecurityPlugin

178

com.example.plugins.CachePlugin

179

```

180

181

## Error Handling

182

183

The resolver handles common issues gracefully:

184

185

- **Missing Service Files**: Logs warnings but continues processing

186

- **Class Not Found**: Logs info messages for unavailable implementations

187

- **IO Errors**: Logs warnings when service files cannot be read

188

- **Invalid Class Files**: Skips classes that cannot be loaded

189

190

## Integration with ObjectMapper

191

192

The resolver is automatically configured with Jackson factory methods:

193

194

```java

195

ObjectMapper mapper = Jackson.newObjectMapper();

196

// DiscoverableSubtypeResolver is automatically set

197

198

ObjectMapper minimal = Jackson.newMinimalObjectMapper();

199

// Also includes DiscoverableSubtypeResolver for polymorphic support

200

```

201

202

## Advanced Usage

203

204

### Conditional Discovery

205

206

```java

207

// Discover subtypes at runtime

208

DiscoverableSubtypeResolver resolver = new DiscoverableSubtypeResolver();

209

List<Class<?>> subtypes = resolver.getDiscoveredSubtypes();

210

211

// Filter subtypes based on runtime conditions

212

List<Class<?>> enabledSubtypes = subtypes.stream()

213

.filter(clazz -> isFeatureEnabled(clazz))

214

.collect(Collectors.toList());

215

216

// Create custom resolver with filtered types

217

ObjectMapper mapper = new ObjectMapper();

218

mapper.setSubtypeResolver(resolver);

219

```

220

221

**Note**: The discovery process occurs during resolver construction and scans the entire classpath for applicable service files.