org.jboss.resteasy.reactive.common.model.ResourceReader Maven / Gradle / Ivy
package org.jboss.resteasy.reactive.common.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.RuntimeType;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.ext.MessageBodyReader;
import org.jboss.resteasy.reactive.common.util.MediaTypeHelper;
import org.jboss.resteasy.reactive.spi.BeanFactory;
@SuppressWarnings("ForLoopReplaceableByForEach")
public class ResourceReader {
private BeanFactory> factory;
private List mediaTypeStrings = new ArrayList<>();
private RuntimeType constraint;
private boolean builtin = true;
private Integer priority = Priorities.USER;
private volatile List mediaTypes;
private volatile MessageBodyReader> instance;
public ResourceReader setFactory(BeanFactory> factory) {
this.factory = factory;
return this;
}
public BeanFactory> getFactory() {
return factory;
}
public List getMediaTypeStrings() {
return mediaTypeStrings;
}
public ResourceReader setMediaTypeStrings(List mediaTypeStrings) {
this.mediaTypeStrings = mediaTypeStrings;
return this;
}
public RuntimeType getConstraint() {
return constraint;
}
public ResourceReader setConstraint(RuntimeType constraint) {
this.constraint = constraint;
return this;
}
public boolean isBuiltin() {
return builtin;
}
public ResourceReader setBuiltin(boolean builtin) {
this.builtin = builtin;
return this;
}
public Integer getPriority() {
return priority;
}
public ResourceReader setPriority(Integer priority) {
this.priority = priority;
return this;
}
public MessageBodyReader> instance() {
if (instance == null) {
synchronized (this) {
if (instance == null) {
//todo: manage lifecycle of bean
instance = factory.createInstance().getInstance();
}
}
}
return instance;
}
public List mediaTypes() {
if (mediaTypes == null) {
//todo: does this actually need to be threadsafe?
synchronized (this) {
List mts = new ArrayList<>(mediaTypeStrings.size());
for (int i = 0; i < mediaTypeStrings.size(); i++) {
mts.add(MediaType.valueOf(mediaTypeStrings.get(i)));
}
mediaTypes = Collections.unmodifiableList(mts);
}
}
return mediaTypes;
}
public boolean matchesRuntimeType(RuntimeType runtimeType) {
if (runtimeType == null) {
return true;
}
if (constraint == null) {
return true;
}
return runtimeType == constraint;
}
/**
* The comparison for now is simple:
* 1) Application provided writers come first
* 2) Readers with lower priority come first (same as reader interceptors)
* 3) Then the more specific the media type, the higher the priority
* 4) Finally we compare the number of media types
*
* The spec doesn't seem to mention this sorting being explicitly needed, but there are tests
* in the TCK that only pass reliably if the Readers are sorted like this
*/
public static class ResourceReaderComparator implements Comparator {
private final List produces;
public ResourceReaderComparator(List produces) {
this.produces = produces;
}
@Override
public int compare(ResourceReader o1, ResourceReader o2) {
int builtInCompare = Boolean.compare(o1.isBuiltin(), o2.isBuiltin());
if (builtInCompare != 0) {
return builtInCompare;
}
int priorityCompare = Integer.compare(o1.getPriority(), o2.getPriority());
if (priorityCompare != 0) {
return priorityCompare;
}
List mediaTypes1 = o1.mediaTypes();
List mediaTypes2 = o2.mediaTypes();
if (mediaTypes1.isEmpty() && mediaTypes2.isEmpty()) {
return 0;
}
if (mediaTypes1.isEmpty()) {
return 1;
}
if (mediaTypes2.isEmpty()) {
return -1;
}
int mediaTypeCompare = MediaTypeHelper.compareWeight(mediaTypes1.get(0), mediaTypes2.get(0));
if (mediaTypeCompare != 0) {
return mediaTypeCompare;
}
// try to compare using the number of matching produces media types
if (!produces.isEmpty()) {
mediaTypeCompare = MediaTypeHelper.compareMatchingMediaTypes(produces, mediaTypes1, mediaTypes2);
if (mediaTypeCompare != 0) {
return mediaTypeCompare;
}
}
// done to make the sorting result deterministic
return Integer.compare(mediaTypes1.size(), mediaTypes2.size());
}
}
}