From 48b02fc07eda10f6e36439b97fc0da84a2adf450 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 1 Nov 2021 14:23:24 +0200 Subject: [PATCH] Apply micro-optimizations to RESTEasy Reactive startup --- .../startup/RuntimeDeploymentManager.java | 50 ++++---- .../startup/RuntimeMappingDeployment.java | 107 +++++++++++------- 2 files changed, 95 insertions(+), 62 deletions(-) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java index 152bae4e719b5..707246808a5d3 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeDeploymentManager.java @@ -58,6 +58,8 @@ public class RuntimeDeploymentManager { private final ThreadSetupAction threadSetupAction; private final String rootPath; + private List> classMappers; + public RuntimeDeploymentManager(DeploymentInfo info, Supplier executorSupplier, CustomServerRestHandlers customServerRestHandlers, @@ -102,17 +104,20 @@ public BeanFactory.BeanInstance apply(Class aClass) { interceptorDeployment, dynamicEntityWriter, resourceLocatorHandler, requestContextFactory.isDefaultBlocking()); List 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>>> 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> mappersByMethod = RuntimeMappingDeployment.buildClassMapper(templates); + Map> mappersByMethod = new RuntimeMappingDeployment(templates) + .buildClassMapper(); resourceLocatorHandler.addResource(loadClass(clazz.getClassName()), mappersByMethod); } @@ -120,13 +125,15 @@ public BeanFactory.BeanInstance apply(Class aClass) { //we use this map to merge them Map>>>> 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); @@ -135,25 +142,8 @@ public BeanFactory.BeanInstance apply(Class aClass) { } } - List> 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 abortHandlingChain = new ArrayList<>(3); @@ -206,6 +196,18 @@ public BeanFactory.BeanInstance apply(Class aClass) { runtimeConfigurableServerRestHandlers, info.isResumeOn404()); } + private void forEachMapperEntry(URITemplate path, + Map>>> 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 runtimeConfigurableServerRestHandlers) { for (ServerRestHandler serverRestHandler : runtimeResource.getHandlerChain()) { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeMappingDeployment.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeMappingDeployment.java index fd23c10477c34..1d4821dc0e4fd 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeMappingDeployment.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/startup/RuntimeMappingDeployment.java @@ -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> buildClassMapper( - Map>>> perClassMappers) { - Map> mappersByMethod = new HashMap<>(); - SortedMap>> nullMethod = perClassMappers.get(null); - if (nullMethod == null) { - nullMethod = Collections.emptySortedMap(); + private final Map>>> classTemplates; + + private final SortedMap>> nullMethod; + + private String currentHttpMethod; + private List> currentMapperPerMethodTemplates; + + private Map> classMapper; + private int maxMethodTemplateNameCount = -1; + + RuntimeMappingDeployment( + Map>>> 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>>> i : perClassMappers - .entrySet()) { - for (Map.Entry>> nm : nullMethod.entrySet()) { - TreeMap>> templateMap = i.getValue(); - if (!templateMap.containsKey(nm.getKey())) { + return maxMethodTemplateNameCount; + } + + Map> buildClassMapper() { + classMapper = new HashMap<>(); + maxMethodTemplateNameCount = 0; + classTemplates.forEach(this::forEachClassTemplate); + return classMapper; + } + + private void forEachClassTemplate(String httpMethod, + TreeMap>> 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> result = new ArrayList<>(); - for (Map.Entry>> entry : i.getValue().entrySet()) { - List> requestPaths = entry.getValue(); - if (requestPaths.size() == 1) { - //simple case, only one match - result.addAll(requestPaths); - } else { - List 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> requestPaths) { + int methodTemplateNameCount = path.countPathParamNames(); + if (methodTemplateNameCount > maxMethodTemplateNameCount) { + maxMethodTemplateNameCount = methodTemplateNameCount; + } + if (requestPaths.size() == 1) { + //simple case, only one match + currentMapperPerMethodTemplates.addAll(requestPaths); + } else { + List 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>>> perClassMappers, ResourceMethod method, RuntimeResource runtimeResource) { TreeMap>> templateMap = perClassMappers