package io.dropwizard.jackson;
import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
import com.google.common.collect.ImmutableList;
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;
A subtype resolver which discovers subtypes via META-INF/services/io.dropwizard.jackson.Discoverable
. /**
* A subtype resolver which discovers subtypes via
* {@code META-INF/services/io.dropwizard.jackson.Discoverable}.
*/
public class DiscoverableSubtypeResolver extends StdSubtypeResolver {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(DiscoverableSubtypeResolver.class);
private final ImmutableList<Class<?>> discoveredSubtypes;
public DiscoverableSubtypeResolver() {
this(Discoverable.class);
}
public DiscoverableSubtypeResolver(Class<?> rootKlass) {
final ImmutableList.Builder<Class<?>> subtypes = ImmutableList.builder();
for (Class<?> klass : discoverServices(rootKlass)) {
for (Class<?> subtype : discoverServices(klass)) {
subtypes.add(subtype);
registerSubtypes(subtype);
}
}
this.discoveredSubtypes = subtypes.build();
}
public ImmutableList<Class<?>> getDiscoveredSubtypes() {
return discoveredSubtypes;
}
protected ClassLoader getClassLoader() {
return this.getClass().getClassLoader();
}
protected List<Class<?>> discoverServices(Class<?> klass) {
final List<Class<?>> serviceClasses = new ArrayList<>();
try {
// use classloader that loaded this class to find the service descriptors on the classpath
// better than ClassLoader.getSystemResources() which may not be the same classloader if ths app
// is running in a container (e.g. via maven exec:java)
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;
}
}
}