package io.dropwizard.jackson;
import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class DiscoverableSubtypeResolver extends StdSubtypeResolver {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(DiscoverableSubtypeResolver.class);
private final List<Class<?>> discoveredSubtypes;
public DiscoverableSubtypeResolver() {
this(Discoverable.class);
}
public DiscoverableSubtypeResolver(Class<?> rootKlass) {
final List<Class<?>> subtypes = new ArrayList<>();
for (Class<?> klass : discoverServices(rootKlass)) {
for (Class<?> subtype : discoverServices(klass)) {
subtypes.add(subtype);
registerSubtypes(subtype);
}
}
this.discoveredSubtypes = subtypes;
}
public List<Class<?>> getDiscoveredSubtypes() {
return discoveredSubtypes;
}
protected ClassLoader getClassLoader() {
return this.getClass().getClassLoader();
}
protected List<Class<?>> discoverServices(Class<?> klass) {
final List<Class<?>> serviceClasses = new ArrayList<>();
try {
final Enumeration<URL> resources = getClassLoader().getResources("META-INF/services/" + klass.getName());
while (resources.hasMoreElements()) {
final URL url = resources.nextElement();
try (InputStream input = url.openStream();
InputStreamReader streamReader = new InputStreamReader(input, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)) {
String line;
while ((line = reader.readLine()) != null) {
final Class<?> loadedClass = loadClass(line);
if (loadedClass != null) {
serviceClasses.add(loadedClass);
}
}
}
}
} catch (IOException e) {
LOGGER.warn("Unable to load META-INF/services/{}", klass.getName(), e);
}
return serviceClasses;
}
@Nullable
private Class<?> loadClass(String line) {
try {
return getClassLoader().loadClass(line.trim());
} catch (ClassNotFoundException e) {
LOGGER.info("Unable to load {}", line);
return null;
}
}
}