Quite often during implementation of a REST client, it is nice to have a handful and flexible REST server, and sometimes just one port per app is not enough.
An example of such situation: your application is designed to send requests to different hosts simultaneously. So a multi-port web server, which could be easily and quickly set up could help with that. However, even if we have a multi-port web server, what should be the port configuration, to have it really “easy and quick” ?
So here is the essentials of a REST server, which is:
multi-port
configuration based
implemented on Kotlin
This quick note does not contain the whole project code. The idea: to focus on the conception. The particular implementation today is just a matter of some hours or even minutes.
@IgnoredBean @RestController classController{ privateval logger = getLogger() /* use of the RequestMapping is one of the key point here. The annotation is a kind of pointer to a function, which should be taken into account. */ @RequestMapping("/api/endpoint-0", method = [RequestMethod.POST]) funpostEndpoint0( @RequestBody request: String ): Response { logger.info("POST, got the request: $request") return Response(request) } companionobject { /* the collection contains all endpoint handlers the controller is designed to expose. */ val HANDLER_METHODS = getMethodsListWithAnnotation(Controller::class.java, RequestMapping::class.java).map { handlerMethod -> handlerMethod.getAnnotation(RequestMapping::class.java).method.map { EndpointHandler( handlerMethod.getAnnotation(RequestMapping::class.java).value[0], it, handlerMethod ) } }.flatten() } @ExceptionHandler funonError(exception: Exception): ResponseEntity<ErrorMessage> { logger.warn("Failed to handle a request: ${exception.message}") return ResponseEntity.status(400).body(ErrorMessage(400, exception.message, Instant.now())) } } dataclassRequest( val parameter: String ) dataclassResponse( val result: String ) classErrorMessage( val status: Int, val message: String?, val `when`: Instant )
The main part of the conception is a service, which builds new port listeners:
tomcat.service.removeConnector(connector) val rootCause = exception.cause
throw IllegalArgumentException(rootCause) }
val port = connector.port val mapping = RequestMappingInfo .paths(configuration.path) .methods(configuration.type) .customCondition(PortRequestCondition(port)) .build()
val controller = Controller() requestHandlerMapper.registerMapping( mapping, controller, handlerMethod )
mappingByPort[port] = mapping logger.info("Added request mapping ${configuration.type}${configuration.path} on port ${port}") } }
internalclassPortRequestCondition( privatevarargval ports: Int ) : RequestCondition<PortRequestCondition> {