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.