Skip to content

Commit

Permalink
Merge pull request #21127 from geoand/rr-startup
Browse files Browse the repository at this point in the history
Apply micro-optimizations to RESTEasy Reactive startup
  • Loading branch information
stuartwdouglas authored Nov 4, 2021
2 parents 1c07c1e + 48b02fc commit 7c22995
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class RuntimeDeploymentManager {
private final ThreadSetupAction threadSetupAction;
private final String rootPath;

private List<RequestMapper.RequestPath<RestInitialHandler.InitialMatch>> classMappers;

public RuntimeDeploymentManager(DeploymentInfo info,
Supplier<Executor> executorSupplier,
CustomServerRestHandlers customServerRestHandlers,
Expand Down Expand Up @@ -102,31 +104,36 @@ public BeanFactory.BeanInstance<?> apply(Class<?> aClass) {
interceptorDeployment, dynamicEntityWriter, resourceLocatorHandler, requestContextFactory.isDefaultBlocking());
List<ResourceClass> possibleSubResource = new ArrayList<>(locatableResourceClasses);
possibleSubResource.addAll(resourceClasses); //the TCK uses normal resources also as sub resources
for (ResourceClass clazz : possibleSubResource) {
for (int i = 0; i < possibleSubResource.size(); i++) {
ResourceClass clazz = possibleSubResource.get(i);
Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> templates = new HashMap<>();
URITemplate classPathTemplate = clazz.getPath() == null ? null : new URITemplate(clazz.getPath(), true);
for (ResourceMethod method : clazz.getMethods()) {
for (int j = 0; j < clazz.getMethods().size(); j++) {
ResourceMethod method = clazz.getMethods().get(j);
RuntimeResource runtimeResource = runtimeResourceDeployment.buildResourceMethod(
clazz, (ServerResourceMethod) method, true, classPathTemplate, info);
addRuntimeConfigurableHandlers(runtimeResource, runtimeConfigurableServerRestHandlers);

RuntimeMappingDeployment.buildMethodMapper(templates, method, runtimeResource);
}
Map<String, RequestMapper<RuntimeResource>> mappersByMethod = RuntimeMappingDeployment.buildClassMapper(templates);
Map<String, RequestMapper<RuntimeResource>> mappersByMethod = new RuntimeMappingDeployment(templates)
.buildClassMapper();
resourceLocatorHandler.addResource(loadClass(clazz.getClassName()), mappersByMethod);
}

//it is possible that multiple resource classes use the same path
//we use this map to merge them
Map<URITemplate, Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>>> mappers = new TreeMap<>();

for (ResourceClass clazz : resourceClasses) {
for (int i = 0; i < resourceClasses.size(); i++) {
ResourceClass clazz = resourceClasses.get(i);
URITemplate classTemplate = new URITemplate(clazz.getPath(), true);
var perClassMappers = mappers.get(classTemplate);
if (perClassMappers == null) {
mappers.put(classTemplate, perClassMappers = new HashMap<>());
}
for (ResourceMethod method : clazz.getMethods()) {
for (int j = 0; j < clazz.getMethods().size(); j++) {
ResourceMethod method = clazz.getMethods().get(j);
RuntimeResource runtimeResource = runtimeResourceDeployment.buildResourceMethod(
clazz, (ServerResourceMethod) method, false, classTemplate, info);
addRuntimeConfigurableHandlers(runtimeResource, runtimeConfigurableServerRestHandlers);
Expand All @@ -135,25 +142,8 @@ public BeanFactory.BeanInstance<?> apply(Class<?> aClass) {
}

}
List<RequestMapper.RequestPath<RestInitialHandler.InitialMatch>> classMappers = new ArrayList<>(mappers.size());
for (var entry : mappers.entrySet()) {
URITemplate classTemplate = entry.getKey();
int classTemplateNameCount = classTemplate.countPathParamNames();
var perClassMappers = entry.getValue();
var mappersByMethod = RuntimeMappingDeployment.buildClassMapper(perClassMappers);
ClassRoutingHandler classRoutingHandler = new ClassRoutingHandler(mappersByMethod, classTemplateNameCount,
info.isResumeOn404());

int maxMethodTemplateNameCount = 0;
for (var i : perClassMappers.values()) {
for (URITemplate j : i.keySet()) {
maxMethodTemplateNameCount = Math.max(maxMethodTemplateNameCount, j.countPathParamNames());
}
}
classMappers.add(new RequestMapper.RequestPath<>(true, classTemplate,
new RestInitialHandler.InitialMatch(new ServerRestHandler[] { classRoutingHandler },
maxMethodTemplateNameCount + classTemplateNameCount)));
}
classMappers = new ArrayList<>(mappers.size());
mappers.forEach(this::forEachMapperEntry);

List<ServerRestHandler> abortHandlingChain = new ArrayList<>(3);

Expand Down Expand Up @@ -206,6 +196,18 @@ public BeanFactory.BeanInstance<?> apply(Class<?> aClass) {
runtimeConfigurableServerRestHandlers, info.isResumeOn404());
}

private void forEachMapperEntry(URITemplate path,
Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> classTemplates) {
int classTemplateNameCount = path.countPathParamNames();
RuntimeMappingDeployment runtimeMappingDeployment = new RuntimeMappingDeployment(classTemplates);
ClassRoutingHandler classRoutingHandler = new ClassRoutingHandler(runtimeMappingDeployment.buildClassMapper(),
classTemplateNameCount,
info.isResumeOn404());
classMappers.add(new RequestMapper.RequestPath<>(true, path,
new RestInitialHandler.InitialMatch(new ServerRestHandler[] { classRoutingHandler },
runtimeMappingDeployment.getMaxMethodTemplateNameCount() + classTemplateNameCount)));
}

private void addRuntimeConfigurableHandlers(RuntimeResource runtimeResource,
List<RuntimeConfigurableServerRestHandler> runtimeConfigurableServerRestHandlers) {
for (ServerRestHandler serverRestHandler : runtimeResource.getHandlerChain()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,86 @@
import org.jboss.resteasy.reactive.server.mapping.URITemplate;
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;

public class RuntimeMappingDeployment {
class RuntimeMappingDeployment {

public static Map<String, RequestMapper<RuntimeResource>> buildClassMapper(
Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> perClassMappers) {
Map<String, RequestMapper<RuntimeResource>> mappersByMethod = new HashMap<>();
SortedMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> nullMethod = perClassMappers.get(null);
if (nullMethod == null) {
nullMethod = Collections.emptySortedMap();
private final Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> classTemplates;

private final SortedMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> nullMethod;

private String currentHttpMethod;
private List<RequestMapper.RequestPath<RuntimeResource>> currentMapperPerMethodTemplates;

private Map<String, RequestMapper<RuntimeResource>> classMapper;
private int maxMethodTemplateNameCount = -1;

RuntimeMappingDeployment(
Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> classTemplates) {
this.classTemplates = classTemplates;
this.nullMethod = classTemplates.get(null);
}

int getMaxMethodTemplateNameCount() {
if (maxMethodTemplateNameCount == -1) {
throw new IllegalStateException("Method can only be called after 'buildClassMapper'");
}
for (Map.Entry<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> i : perClassMappers
.entrySet()) {
for (Map.Entry<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> nm : nullMethod.entrySet()) {
TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> templateMap = i.getValue();
if (!templateMap.containsKey(nm.getKey())) {
return maxMethodTemplateNameCount;
}

Map<String, RequestMapper<RuntimeResource>> buildClassMapper() {
classMapper = new HashMap<>();
maxMethodTemplateNameCount = 0;
classTemplates.forEach(this::forEachClassTemplate);
return classMapper;
}

private void forEachClassTemplate(String httpMethod,
TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> perMethodTemplateMap) {
currentHttpMethod = httpMethod;

if (nullMethod != null) {
for (var nm : nullMethod.entrySet()) {
if (!perMethodTemplateMap.containsKey(nm.getKey())) {
//resource methods take precedence
//just skip sub resource locators for now
//may need to be revisited if we want to pass the TCK 100%
templateMap.put(nm.getKey(), nm.getValue());
perMethodTemplateMap.put(nm.getKey(), nm.getValue());
}
}
//now we have all our possible resources
List<RequestMapper.RequestPath<RuntimeResource>> result = new ArrayList<>();
for (Map.Entry<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> entry : i.getValue().entrySet()) {
List<RequestMapper.RequestPath<RuntimeResource>> requestPaths = entry.getValue();
if (requestPaths.size() == 1) {
//simple case, only one match
result.addAll(requestPaths);
} else {
List<RuntimeResource> resources = new ArrayList<>(requestPaths.size());
for (int j = 0; j < requestPaths.size(); j++) {
resources.add(requestPaths.get(j).value);
}
MediaTypeMapper mapper = new MediaTypeMapper(resources);
//now we just create a fake RuntimeResource
//we could add another layer of indirection, however this is not a common case
//so we don't want to add any extra latency into the common case
RuntimeResource fake = new RuntimeResource(i.getKey(), entry.getKey(), null, null, Collections.emptyList(),
null, null,
new ServerRestHandler[] { mapper }, null, new Class[0], null, false, null, null, null, null, null,
Collections.emptyMap());
result.add(new RequestMapper.RequestPath<>(false, fake.getPath(), fake));
}
}

//now we have all our possible resources
currentMapperPerMethodTemplates = new ArrayList<>();
perMethodTemplateMap.forEach(this::forEachMethodTemplateMap);

classMapper.put(httpMethod, new RequestMapper<>(currentMapperPerMethodTemplates));
}

private void forEachMethodTemplateMap(URITemplate path, List<RequestMapper.RequestPath<RuntimeResource>> requestPaths) {
int methodTemplateNameCount = path.countPathParamNames();
if (methodTemplateNameCount > maxMethodTemplateNameCount) {
maxMethodTemplateNameCount = methodTemplateNameCount;
}
if (requestPaths.size() == 1) {
//simple case, only one match
currentMapperPerMethodTemplates.addAll(requestPaths);
} else {
List<RuntimeResource> resources = new ArrayList<>(requestPaths.size());
for (int j = 0; j < requestPaths.size(); j++) {
resources.add(requestPaths.get(j).value);
}
mappersByMethod.put(i.getKey(), new RequestMapper<>(result));
MediaTypeMapper mapper = new MediaTypeMapper(resources);
//now we just create a fake RuntimeResource
//we could add another layer of indirection, however this is not a common case
//so we don't want to add any extra latency into the common case
RuntimeResource fake = new RuntimeResource(currentHttpMethod, path, null, null, Collections.emptyList(),
null, null,
new ServerRestHandler[] { mapper }, null, new Class[0], null, false, null, null, null, null, null,
Collections.emptyMap());
currentMapperPerMethodTemplates.add(new RequestMapper.RequestPath<>(false, fake.getPath(), fake));
}
return mappersByMethod;
}

public static void buildMethodMapper(
static void buildMethodMapper(
Map<String, TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>>> perClassMappers,
ResourceMethod method, RuntimeResource runtimeResource) {
TreeMap<URITemplate, List<RequestMapper.RequestPath<RuntimeResource>>> templateMap = perClassMappers
Expand Down

0 comments on commit 7c22995

Please sign in to comment.