I have an app that uses CXF as a SOAP client and Jersey to present REST services, with the Jersey classes managed by Spring. This works fine running in Tomcat; however when attempting to test with JerseyTest I get Spring conflicts; it appears JerseyTest doesn't shut-down the Spring context correctly.
The test initialisation for Jersey looks like:
public MailProviderTest()
throws Exception
{
super(new WebAppDescriptor.Builder("net.haltcondition.service.rest")
.contextPath("")
.contextParam("contextConfigLocation", "classpath:applicationContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.build());
}
The CXF tests (which talk to our upstream provider's test servers) looks like:
#Before
public void setup()
{
// We need to do this the hard way to set the test endpoint rather than the default
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(Soap.class);
factory.setAddress("https://webservice.test.provider.com/Service.asmx");
soap = (Soap) factory.create();
Map<String,Object> outProps= new HashMap<String,Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, "TESTUSER");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new WSAuthHandler("XXXX"));
Client cxfClient = ClientProxy.getClient(soap);
Endpoint cxfEndpoint = cxfClient.getEndpoint();
cxfEndpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
}
When Maven runs the tests the Jersey class is run first; this results in the following error when running the CXF tests:
Caused by: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:153)
at org.springframework.context.support.AbstractApplicationContext.containsBean(AbstractApplicationContext.java:892)
at org.apache.cxf.configuration.spring.ConfigurerImpl.configureBean(ConfigurerImpl.java:143)
at org.apache.cxf.configuration.spring.ConfigurerImpl.configureBean(ConfigurerImpl.java:113)
at org.apache.cxf.transport.http.AbstractHTTPTransportFactory.configure(AbstractHTTPTransportFactory.java:228)
Unfortunately there doesn't seem to be any way to force the shutdown of the Spring application-context at the end of the Jersey tests and forcing per-test forks hasn't helped. It looks like I need to reset the Spring application-context as part of setting-up the CXF test, but I can't see how I would do this. Any pointers would be appreciated.
Related
We have a couple of Spring tests that call a secured controller endpoints. Our goal is to assure that absence of particular user roles will result into HTTP 403 status.
Our issue is that execution of those tests also bootstraps DB connection which we don't actually need.
I've already tried countless number of all kind of annotations and manual configurations to avoid initialization of DB connection but so far without luck. Can you please share example how to do that?
We use Spring Boot 2.7.
Yes, you can use #WebMvcTest, take a look at the docs. In summary, using #WebMvcTest will only bootstrap the Spring MVC components and avoid loading other application's layers. This annotation also automatically configures Spring Security for you, therefore you can test authentication/authorization rules.
Example:
#WebMvcTest(UserVehicleController.class)
class MyControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private UserVehicleService userVehicleService;
#Test
#WithMockUser(roles = "ADMIN")
void testAdminSuccess() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().string("Honda Civic"));
}
#Test
#WithMockUser(roles = "USER")
void testUserForbidden() throws Exception {
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isForbidden());
}
}
Metrics http_server_requests_seconds_count in Spring Boot application with version 2.0.8.Release exposed using spring actuator contains URI as:
"UNKNOWN".
Spring Boot application is using cxf-spring-boot-starter-jaxrs for exposing rest endpoints.
I have added micrometer-registry-prometheus dependency in my project.
http_server_requests_seconds_count{exception="None",method="POST",status="200",uri="UNKNOWN",} 2.0
I have tried adding micrometer-jersey2 dependency in my project.
Actual
http_server_requests_seconds_count{exception="None",method="POST",status="200",uri="UNKNOWN",} 2.0
Expected:
http_server_requests_seconds_count{exception="None",method="GET",status="200",uri="/sayHello",} 2.0
After the clarification in OP comments (CXF being another JAX-RS implementation): There's currently no support in Micrometer to handle CXF requests. It (Spring WebMvc) can't extract the optionally parameterized request url and in that case falls back to UNKNOWN. (Otherwise this could lead to a metrics explosion if your CXF endpoints provide some highly parameterizable URLs which get a lot of traffic.)
So you could have a look at the micrometer-jersey2 implementation and derive a micrometer-cxf implementation ;) (Or if not already the case (use the search) - open up an issue with the Micrometer or CXF project. I am mentioning the latter, because they might be interessted in taking care of that implementation.)
If you need cxf statistics for micrometer report, you can try
<dependency>
<groupId>io.github.kdprog</groupId>
<artifactId>cxf-micrometer-metrics</artifactId>
<version>1.0.0</version>
</dependency>
The following statistics will be reported
cxf_requests_processed_total - total number of cxf requests ,
cxf_requests_seconds_sum - total execution time of cxf requests,
cxf_requests_seconds_max - maximum execution time of cxf request,
cxf_requests_success_total - total number of successfully processed cxf requests,
cxf_requests_failed_total - total number of failed cxf requests
for each web service method of every client or server cxf endpoint.
For spring applications add the following bean to your application configuration.
#Bean
public FactoryBeanListener cxfMicrometerBean(final MeterRegistry registry) {
return new MicrometerFactoryBeanListener(registry);
}
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.supported
You can make custom tag provider to override it:
#Bean
WebMvcTagsProvider webMvcTagsProvider() {
return new DefaultWebMvcTagsProvider() {
#Override
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response,
Object handler, Throwable exception) {
return Tags.concat(
super.getTags(request, response, handler, exception),
Tags.of(Tag.of("uri",request.getRequestURI()))
);
}
};
}
More examples.
You can also collect cxf metrics for prometheus using io.github.ddk-prog:cxf-prometheus-metrics.
Add dependency to your pom.xml
<dependency>
<groupId>io.github.ddk-prog</groupId>
<artifactId>cxf-prometheus-metrics</artifactId>
<version>1.0.0</version>
</dependency>
and the following bean to your application configuration.
#Bean
public FactoryBeanListener cxfPrometheusFeatureBean(final CollectorRegistry registry) {
return new PrometheusFactoryBeanListener(registry);
}
You will get cxf_requests_total, cxf_requests_success, cxf_requests_failed, cxf_requests_seconds for each endpoint and operation in your spring boot actuator prometheus report.
For example,
cxf_requests_seconds{endpoint="server1",operation="server1Method",} 0.0157349
If you are using WebFlux on your project, you can make your custom tag provider by overriding:
#Bean
WebFluxTagsProvider webFluxTagsProvider() {
return new DefaultWebFluxTagsProvider() {
#Override
public Iterable<Tag> httpRequestTags(ServerWebExchange exchange, Throwable exception) {
return Tags.concat(super.httpRequestTags(exchange, exception), Tags.of(Tag.of("uri", exchange.getRequest().getPath().value())));
}
};
}
It works for me.
I'm working on migrating from Jersey 1.16 to Jersey 2.7. I have the application running and working, but I'm having trouble with the tests.
Some things to note:
The application uses Spring and is configured by a class, ApplicationConfiguration.class.
The application does not have a web.xml - the Jersey servlet and filters are configured programmatically.
I am using the jersey-spring3 library.
Related to using the jersey-spring3 library, I have to add a workaround to my onStartup method
// The following line is required to avoid having jersey-spring3 registering it's own Spring root context.
// https://java.net/jira/browse/JERSEY-2038
servletContext.setInitParameter("contextConfigLocation", "");
Here's the issue:
When the test is starting up, SpringComponentProvider, tries to initialize Spring with a dangerous assumption that I can't figure out how to correct - xml based configuration. Looking at the code, the trouble is this block
ServletContext sc = locator.getService(ServletContext.class);
if(sc != null) {
// servlet container
ctx = WebApplicationContextUtils.getWebApplicationContext(sc);
} else {
// non-servlet container
ctx = createSpringContext();
}
Running a JUnit test, ServletContext is null, and createSpringContext is called.
Here's the question:
Is there a way to run a test and specify a ServletContext/ServletContainer?
I believe this issue is covered by https://java.net/jira/browse/JERSEY-2259.
In short: they removed this functionality from Jersey 2.x and are treating it as a Feature Request (instead of regression) so it's not considered a high-priority item.
I have a Spring 3.1 based application hosted under Tomcat 7.x (latest version). The application is configured exclusively with Java (no web.xml, no Spring XML configuration). All unit tests are passing, including ones using the Spring Java configuration (#ContextConfiguration).
The problem is that when the application is deployed, the WebApplicationInitializer implementation is being called multiple times. Repeated registrations of filters and listeners causes exceptions and the application never starts.
I was not expecting WebApplicationInitializer.onStartup() to be called repeatedly and would like to eliminate that behavior if possible. If anyone has suggestions on why this might be happening, and how to stop it, I'd really appreciate it.
Update I believe the problem is external to the initialization class itself, but here it is in case I am mistaken...
public class DeploymentDescriptor implements WebApplicationInitializer {
private static final Logger LOGGER = LoggerFactory.getLogger("org.ghc.web-app-initializer");
#Override
public void onStartup (ServletContext servletContext) throws ServletException {
// This is the programmatic way of declaring filters. This allows you to order
// Filters. The order of these security filters DOES MATTER!
FilterRegistration.Dynamic mockSecurityFilter = servletContext.addFilter ("mockSecurityFilter", "org.ghc.security.MockSecurityFilter");
mockSecurityFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
FilterRegistration.Dynamic siteMinderSecurityFilter = servletContext.addFilter ("siteMinderSecurityFilter", "org.ghc.security.SiteMinderSecurityFilter");
siteMinderSecurityFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
FilterRegistration.Dynamic userDetailsStoreFilter = servletContext.addFilter ("userDetailsStoreFilter", "org.ghc.security.UserDetailsStoreFilter");
userDetailsStoreFilter.addMappingForUrlPatterns (EnumSet.of (REQUEST), true, "/*");
// Static resource handling using "default" servlet
servletContext.getServletRegistration ("default").addMapping ("*.js", "*.css", "*.jpg", "*.gif", "*.png");
// Map jspf files to jsp servlet
servletContext.getServletRegistration ("jsp").addMapping ("*.jspf");
// Spin up the Spring 3.1 class that can scan a package tree for classes
// annotated with #Configuration. See org.ghc.spring3.ControllerConfiguration for
// this example.
final AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext ();
dispatcherContext.setServletContext (servletContext);
dispatcherContext.register(ScProviderDirectory.class);
dispatcherContext.refresh ();
// Spin up the Spring DispatcherServlet (just like before) passing the just built
// application context. Load it like the regular Servlet that it is!
final ServletRegistration.Dynamic servlet = servletContext.addServlet ("spring", new DispatcherServlet(dispatcherContext));
servlet.setLoadOnStartup (1);
servlet.addMapping ("/"); // Make sure this is NOT "/*"!
}
}
Update 2 This is just weird. The Tomcat logs appear to identify two instances of my DeploymentDescriptor class. I verified that there is only one instance of this class in my .war file though. I have no idea where the second (phantom) instance is coming from, but at least this explains why the class is being scanned twice...
logs/localhost.2012-10-09.log:INFO: Spring WebApplicationInitializers detected on classpath: [org.ghc.configuration.DeploymentDescriptor#3b29642c]
logs/localhost.2012-10-09.log:INFO: Spring WebApplicationInitializers detected on classpath: [org.ghc.configuration.DeploymentDescriptor#432c4c7a]
The problem here was a Maven Overlay dumping crap a Spring xml configuration file into my application. For whatever reason this caused the WebApplicationInitializer.onStartup() to be called twice. Probably an initialization for the application context, and for the servlet context. Killed off the overlay, application is initializing as expected.
I had the same problem. The issue was that I had multiple spring-web*.jar like Biju K. suggested (one as part of the war, the other in shared/tomcat library).
I had the same problem. Running mvn clean in the web app module's directory and then starting up Tomcat solved it for me.
I'm writing some JUnit-based integration tests for a RESTful web service using JerseyTest. The JAX-RS resource classes use Spring and I'm currently wiring everything together with a test case like the following code example:
public class HelloResourceTest extends JerseyTest
{
#Override
protected AppDescriptor configure()
{
return new WebAppDescriptor.Builder("com.helloworld")
.contextParam( "contextConfigLocation", "classpath:helloContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
#Test
public void test()
{
// test goes here
}
}
This works for wiring the servlet, however, I'd like to be able to share the same context in my test case so that my tests can have access to mock objects, DAOs, etc., which seems to call for SpringJUnit4ClassRunner. Unfortunately, SpringJUnit4ClassRunner creates a separate, parallel application context.
So, anyone know how can I create an application context that is shared between the SpringServlet and my test case?
Thanks!
Override JerseyTest.configure like so:
#Override
protected Application configure() {
ResourceConfig rc = new JerseyConfig();
rc.register(SpringLifecycleListener.class);
rc.register(RequestContextFilter.class);
rc.property("contextConfigLocation", "classpath:helloContext.xml");
return rc;
}
For me the SpringServlet was not required, but if you need that you may be able to call rc.register for that too.
I found a couple of ways to resolve this problem.
First up, over at the geek#riffpie blog there is an excellent description of this problem along with an elegant extension of JerseyTest to solve it:
Unit-testing RESTful Jersey services glued together with Spring
Unfortunately, I'm using a newer version of Spring and/or Jersey (forget which) and couldn't quite get it to work.
In my case, I ended up avoiding the problem by dropping the Jersey Test Framework and using embedded Jetty along with the Jersey Client. This actually made better sense in my situation anyway since I was already using embedded Jetty in my application. yves amsellem has a nice example of unit testing with the Jersey Client and embedded Jetty. For Spring integration, I used a variation of Trimbo's Jersey Tests with Embedded Jetty and Spring