We're a Spring Boot shop and rely heavily on Spring MVC for our REST endpoints. We use Boot and embedded Tomcat to create a self-hosting JAR. Is it possible to replace Tomcat with Ratback while still keeping all my Spring MVC code in place? I am afraid that Spring MVC is tied into the servlet specification somehow and will not run without a servlet container. I am aware of dsyer/spring-boot-ratpack work but after skimming the code couldn't decide if Spring MVC would play well using the bridge. Is anyone aware of any work that will allow us to retain our investment in Spring MVC and have Spring Boot use Ratpack to manage HTTP traffic?
I suspect the crux of your question can be distilled to: "can we put our Spring controllers on top of Ratpack's non-blocking HTTP layer?" and the simplest answer to that question is no, for reason that the MVC programming model doesn't fit well into the reactive/NIO model very well.
However, if your application has followed some common model-view-controller-(and service) patterns, then your controllers should really just be performing data binding and parsing and delegating out to a service layer. If that's the case, then likely the code in your controller is already non-blocking, and you could easily translate it to Ratpack code.
As an example, consider the following #RestController in a Spring Boot app:
#RestController
#RequestMapping("/user")
class UserController {
#Autowired
UserService userService
#RequestMapping(method = RequestMethod.POST)
Long create(#RequestBody #Valid User user) {
User savedUser = userService.save(user)
return savedUser.id
}
}
Spring's data binding aspect is a computation process (ie isn't I/O bound), so we can easily translate this into a Ratpack handler:
import app.SpringConfig
import app.User
import app.UserService
import org.springframework.boot.SpringApplication
import org.springframework.context.ApplicationContext
import ratpack.jackson.JacksonModule
import static ratpack.groovy.Groovy.ratpack
import static ratpack.jackson.Jackson.fromJson
import static ratpack.jackson.Jackson.json
import static ratpack.spring.Spring.spring
ratpack {
bindings {
add(new JacksonModule())
bindInstance(ApplicationContext, SpringApplication.run(SpringConfig))
}
handlers { ApplicationContext ctx ->
register(spring(ctx))
prefix("user") {
handler { UserService userService ->
byMethod {
post {
def user = parse(fromJson(User))
blocking {
userService.save(user)
} then { User savedUser ->
render(json(savedUser))
}
}
}
}
}
}
}
Where SpringConfig looks like this:
package app
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class SpringConfig {
#Bean
UserService userService() {
new UserService()
}
}
And here's a functional test to prove it:
package app
import com.fasterxml.jackson.databind.ObjectMapper
import ratpack.groovy.test.GroovyRatpackMainApplicationUnderTest
import ratpack.test.ApplicationUnderTest
import ratpack.test.http.TestHttpClient
import spock.lang.Shared
import spock.lang.Specification
import static groovy.json.JsonOutput.toJson
class FuncSpec extends Specification {
#Shared ApplicationUnderTest aut = new GroovyRatpackMainApplicationUnderTest()
#Shared ObjectMapper mapper = new ObjectMapper()
#Delegate TestHttpClient client = aut.httpClient
def "should parse and save user"() {
given:
def user = new User(username: "dan", email: "danielpwoods#gmail.com")
when:
requestSpec { spec ->
spec.body { b ->
b.type("application/json")
b.text(toJson(user))
}
}
post('user')
then:
def savedUser = mapper.readValue(response.body.text, User)
and:
savedUser.id
}
}
Hope this helps!
The Spring MVC programming model is not very heavily dependent on Servlet APIs, but it's not supported in any other containers (i.e. not in Ratpack). There is some async stuff there now and Servlet 3.1 enhances it some more, so if that's the part of Ratpack that attracts you, maybe just using that would be a better approach. You won't get all the way to reactive and non-blocking IO that way though.
Related
I wonder how I can invoke a custom health indicator:
in the same application
of another Spring Boot application
My application is split into a base application (rather a configuration) A which implements nearly all the functionality (having no main method) and another application B (having a main method ;-) ) having the base configuration as a dependency in the POM.
In A I have implemented a custom HealthIndicator:
#Component
#RequiredArgsConstructor
public class AdapterDownstreamHealthIndicator implements HealthIndicator {
private RestTemplate restTemplate;
private String downStreamUrl = "http://localhost:8081/actuator";
public AdapterDownstreamHealthIndicator(RestTemplate restTemplate, String downStreamUrl) {
this.restTemplate = restTemplate;
this.downStreamUrl = downStreamUrl;
}
#Override
public Health health() {
// try {
// JsonNode resp = restTemplate.getForObject(downStreamUrl + "/health", JsonNode.class);
// if (resp.get("status").asText().equalsIgnoreCase("UP")) {
// System.out.println("JUHUUUUUUUUUUU!!!!");
// return Health.up().build();
// }
// } catch (Exception ex) {
// return Health.down(ex).build();
// }
return Health.down().build();
}
}
In my application.properties I have some actuator properties:
management.endpoints.web.exposure.include=health,info,prometheus,adapterDownstream
spring.jackson.serialization.INDENT_OUTPUT=true
management.endpoint.health.show-details=always
When I enter http://localhost:9091/actuator/health/adapterDownstream in a browser the debugger does not stop in the health() method and I simply get an empty page displayed.
I already tried to extend AbstractHealthIndicator instead of implementing HealthIndicator interface.
What am I doing wrong that the custom health indicator is not recognized?
In the end I want to make some kind of deep health check to test all components being used in my application. Maybe using CompositeHealthContributor should be used???
As I described I have a dependency A which has NO main method which is loaded into my application B as a dependency in the POM. So far I tried to implement the custom healthcheck class/the health indicator in this dependency/module A.
The simple solution is to add a
#ComponentScan(basePackages = "path.to.actuator") to the main method of the application.
Requirement
For Cloud, datastore needs to change namespace dynamically. (example store kind as per company Name)
Used Spring cloud DataRepository with Springboot for same
Issue
We need to declare spring.cloud.gcp.datastore.namespace in application.properties which is static.
Is there any way to change this dynamically with CRUDReposity of spring cloud
Thanks in advance
You can change anything you want in your application.properties at runtime using Spring Cloud Config.
Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments. The concepts on both client and server map identically to the Spring Environment and PropertySource abstractions, so they fit very well with Spring applications but can be used with any application running in any language.
Just as a quick example on how you can use this , you should firstly add the dependency : eg gradlecompile group: 'org.springframework.cloud', name: 'spring-cloud-starter', version: '1.1.1.RELEASE', then you need to add the #RefreshScope on the desired configuration bean.
You will be able to view your current config at a certain endpoint, like "applicationConfig: [classpath:/application.properties]": {
"my.property": "value1",
etc
And then you can change the properties as you wish doing a POST request like :
curl -X POST http://localhost:8080 -d my.property=value2
There is also a nice article about dynamically reloading the properties in a Spring application here. It is nice because they actually display more ways that you can achieve that.
You can use DatastoreNamespaceProvider which can dynamically return needed namespace.
Was added in this PR PR
Also see this discussion here and this recommendation
#Component
#RequiredArgsConstructor
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class HeaderValueProvider implements Supplier<String>, DatastoreNamespaceProvider {
private final HttpServletRequest httpServletRequest;
#Override
public String get() {
return httpServletRequest.getHeader("someHeader");
}
}
And this
#Component
public class UserContextProvider implements DatastoreNamespaceProvider, Consumer<UUID> {
private static final ThreadLocal<UUID> USER_CONTEXT = new ThreadLocal<>();
#Override
public String get() {
return ofNullable(USER_CONTEXT.get())
.map(UUID::toString)
.orElse(null);
}
#Override
public void accept(UUID uuid) {
USER_CONTEXT.set(uuid);
}
}
I'm trying to plug my set of RESTful endpoints (which rely on Jersey 2 JAX-RS implementation) into a 3rd party Spring MVC web application running on top of Tomcat 9.
I'm not allowed to modify web.xml, nor is my plug-in available when the container starts, so annotation-based configuration is not an option, either. This means I can't deploy Jersey's ServletContainer directly.
What I am allowed is plugging into Spring MVC, namely
creating a custom partial Spring application context and
implementing a custom Controller, so I have written this naïve implementation which is actually close to the standard ServletWrappingController:
package com.example
import org.glassfish.jersey.server.ResourceConfig
import org.glassfish.jersey.servlet.ServletContainer
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.servlet.mvc.AbstractController
import java.io.IOException
import java.util.Collections
import java.util.Enumeration
import javax.servlet.ServletConfig
import javax.servlet.ServletContext
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class JerseyServletInitializer(servletContext: ServletContext,
componentClasses: List<Class<*>>,
components: List<Any>): AbstractController() {
private val servletContainer: ServletContainer
init {
val resourceConfig = ResourceConfig()
.apply {
// Register org.glassfish.jersey.media.multipart.MultiPartFeature
componentClasses.forEach { clazz ->
register(clazz)
}
// Register REST endpoints
components.forEach { component ->
register(component)
}
}
// Provide a dummy ServletConfig
val servletConfig = object : ServletConfig {
override fun getInitParameter(name: String): String? = null
override fun getInitParameterNames(): Enumeration<String> = emptyList<String>().let {
Collections.enumeration(it)
}
override fun getServletName(): String = ServletContainer::class.java.name
override fun getServletContext(): ServletContext = servletContext
}
// Create and initialize Jersey's ServletContainer
servletContainer = ServletContainer(resourceConfig).apply {
init(servletConfig)
}
setSupportedMethods("GET", "POST", "PUT", "OPTIONS")
}
#Throws(IOException::class)
override fun handleRequestInternal(request: HttpServletRequest,
response: HttpServletResponse): ModelAndView? {
// Delegate all HTTP requests to Jersey
servletContainer.service(request, response)
return null
}
}
The above works fine with the only exception of file upload scenario (i. e. HTTP POST with Content-Type: multipart/form-data).
Spring detects such requests and converts a regular HttpServletRequest into a MultipartHttpServletRequest, reading out and exhausting its input stream (mark()/reset() are not supported). The web application has a custom MultipartResolver which I can't affect in any way:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
This means Jersey receives a multipart HTTP POST with an empty request body, throws a MIMEParsingException and responds with HTTP 400 Bad Request.
As far as I understand, multipart resolution can only be disabled globally, and not on a per-controller basis (1, 2, 3).
Questions:
What is the best way to work the MultipartResolver around, so that Jersey receives an non-modified request?
Alternatively, can you recommend an API to re-construct the request body (i. e. do the opposite of what MultipartResolver does and feed the new HttpServletRequest to Jersey)? This can, of course, be done by hand, but I'd rather rely on the existing libraries.
I made a project like this sample. So the controllers are like this
package mypackagename.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
#RequestMapping("/")
public class StoresRestController {
#RequestMapping(method = RequestMethod.GET)
public String stores() {
return ...
}
}
I like to handle all throwables and make my customized unified response. The problem is I cannot find a guide or a sample to do this correctly.
First of all, I tried ExceptionHandler, with Throwable, but it didn't work, so I decided to move on. Then, the most close approach that I found is this, so I tried jersey, by adding something like this. But it's not working for all throwables. Also, it's ignoring my controllers, by complaining
o.g.jersey.internal.inject.Providers : A provider mypackagename.controller.StoresRestController registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider mypackagename.controller.StoresRestController will be ignored.
I searched for this error and found this, which I'm not using ContainerResponseFilter in my project as I provided the sample above. So I'm clueless. The main problem is how to handle all throwables, but if you can give me some suggestions about how to solve Providers problem, I'll be so appreciated.
In my project I use #ControllerAdvice to handle my exceptions. Here's an example. Hope this helps. Just make sure this class is on your component scan so it gets picked up.
#RestController
#ControllerAdvice
public class StoresExceptionHandler {
#ExceptionHandler(Throwable.class)
public ResponseEntity<Object> handleThrowable(final Throwable ex) {
return new ResponseEntity<Object>("Unable to process request.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Finally this post helped me to handle all Throwables, except authentication exceptions. The important part was to use #EnableWebMvc and ResponseEntityExceptionHandler. To handle authentication exceptions I used this answer. Hope it's helping someone.
as #carlos-cook said, you could use a #ControllerAdvice and, ProblemDetail defined in RFC 7807 which could look like:
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ProblemDetail handleUnexpectedException(RuntimeException rte, WebRequest wr) {
ProblemDetail pd = this.createProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, rte);
pd.setType(URI.create("http://your-site.com/internal-server-error"));
pd.setTitle("Internal server error");
return pd;
}
#ExceptionHandler(YourCustomeException.class)
public ProblemDetail handleUnexpectedException(YourCustomException rte, WebRequest wr) {
ProblemDetail pd = this.createProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, rte);
pd.setType(URI.create("http://your-site.com/custom-error-page"));
pd.setTitle("Internal server error");
return pd;
}
}
Then in your controller you could simply throw YourCustomException
This controller advice will handle every exception and YourCustomException separately.
In our project I have modules scout.client, scout.server, scout.shared and backend.
Backend has no dependencies to scout.server and scout.shared, but scout.server has dependencies to backend.
Inside backend project I have all business logic and calling all outside services.
My problem is when I try to test scout services that use some service from backend.
Because scout provide some great tool for mocking beans, we defined our service inside backend as beans as :
BEANS.getBeanManager().registerClass(CarService.class);
BEANS.getBeanManager().registerClass(PartnerService.class);
Both, CarService.class and PartnerService.class are in backend.
When I try to write some tests and I add #BeanMock to service in test
#BeanMock
private IPartnerService partnerService;
I get mock, but then every return every function is null, even if I write
doReturn(PartnerBuilder.standardPartnerListWithOneElement()).when(this.partnerService)
.getPartners(any(Set.class));
If I debug in my test, before this test is called with debugger I can get :
partnerService.getPartners(...) -> return a list of person
what is right, but when class that is tested calles this service it return null.
I understand that this could be due to missing annotation on interface #ApplicationScoped. Without this there is no guarantee that only one bean is created, and when statement react on another copy of that bean...?
I could not add annotation on interface because backend has no dependencies to scout modules.
How could I handle this kind of cases?
Tested class is :
public class UtilityPartner {
/**
* Method return service bean for getting partners by ids.
*
* #return
*/
private static IPartnerService getPartnerService() {
return BEANS.get(IPartnerService.class);
}
public static String getPartnerName(final Long partnerId) {
if (partnerId == null) {
return "";
}
final List<Partner> partners =
(List<Partner>) getPartnerService().getPartners(Sets.newHashSet(partnerId));
if (partners == null || partners.isEmpty()) {
return "";
}
final Partner partner = partners.get(0);
return LookupUtil.createLookupDescription(partner.getId(), partner.getName());
}
}
test class is :
#RunWith(ServerTestRunner.class)
#RunWithSubject("anonymous")
#RunWithServerSession(ServerSession.class)
public class TestUtilityPartner {
#BeanMock
private IPartnerService partnerService;
#Before
public void init() {
doReturn(PartnerBuilder.standardPartnerListWithOneElement()).when(this.partnerService).getPartners(any(Set.class));
}
#Test
public void getPartnerName() {
final String name = UtilityPartner.getPartnerName(10L);
Assert.assertEquals("My name", name); // NAME IS ""
}
}
Using #BeanMock does not help here, because you are not using an application scoped service:
In the init method you are changing the local field partnerService. However, in your test you call UtilityPartner.getPartnerService, which is creating a new instance (with BEANS.get(IPartnerService.class)).
#BeanMock is more useful for convenience for mocking application scoped beans.
You can always register your beans manually as shown by Jmini. Please do not forget to unregister the bean again after the test!
We recommend using org.eclipse.scout.rt.testing.shared.TestingUtility.registerBean(BeanMetaData), which is automatically adding a testing order and removing #TunnelToServer annotations.
I think that you should register your mock instance in the Bean manager (See bean registration in the Scout Architecture Document). You should use a small order (-10 000 is recommended for tests), in order for your mock to win over the productive registration. The best approach is to use the TestingUtility class to register/unregister your mock. Do not forget to call the unregisterBean() method (in the method annotated with #After):
import java.util.Collections;
import org.eclipse.scout.rt.platform.BeanMetaData;
import org.eclipse.scout.rt.platform.IBean;
import org.eclipse.scout.rt.testing.shared.TestingUtility;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class TestUtilityPartner {
private IBean<?> beanRegistration;
#Before
public void init() {
partnerService = Mockito.mock(IPartnerService.class);
// Register the mock using the Bean meta information:
BeanMetaData beanData = new BeanMetaData(IPartnerService.class)
.withInitialInstance(partnerService)
.withApplicationScoped(true);
this.beanRegistration = TestingUtility.registerBean(beanData);
// Mockito behavior:
Mockito.doReturn(Collections.singletonList(new Partner(34L, "John Smith")))
.when(partnerService).getPartners(Mockito.any(Set.class));
}
#After
public void after() {
// Unregister the mocked services:
TestingUtility.unregisterBean(this.beanRegistration);
}
#Test
public void getPartnerName() {
String name = UtilityPartner.getPartnerName(10L);
Assert.assertEquals("10 - John Smith", name);
}
}
I am not sure what #BeanMock (org.eclipse.scout.rt.testing.platform.mock.BeanMock) is doing, but according to Judith Gull's answer it will not work:
Using #BeanMock does not help here, because you are not using an application scoped service:
In the init method you are changing the local field partnerService. However, in your test you call UtilityPartner.getPartnerService, which is creating a new instance (with BEANS.get(IPartnerService.class)).
#BeanMock is more useful for convenience for mocking application scoped beans.