I have REST web service which I test with Jersey Test, Mockito, Junit. When web service method is executed successfully, I get correct response. In case of invalid data, custom exception is thrown which must be handled by ExceptionMapper. It should return the same structure response but with different codes. ExceptionMapper works well in not test environment. However, after test execution logs show:
1 < 500
1 < Connection: close
1 < Content-Length: 1033
1 < Content-Type: text/html;charset=ISO-8859-1
1 < Date: Wed, 30 Mar 2016 11:55:37 GMT
javax.ws.rs.InternalServerErrorException: HTTP 500 Request failed.
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1020)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:816)
at org.glassfish.jersey.client.JerseyInvocation.access$700(JerseyInvocation.java:92)
I use:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.22.2</version>
</dependency>
Shortened version of mocked service which is extented by test classes:
public class MockedService extends JerseyTest {
#Override
public ResourceConfig configure() {
forceSet(TestProperties.CONTAINER_PORT, "8080");
enable(TestProperties.LOG_TRAFFIC);
return new ResourceConfig().register(new Service());
}
}
How to get response from ExceptionMapper?
You still need to register the ExceptionMapper
return new ResourceConfig()
.register(new Service())
.register(new YourMapper());
In your real environment, the mapper is probably scanned for, either with package scanning or classpath scanning.
Related
I'm working on a spring-boot (1.4.0-RELEASE) MVC Groovy app which will present an XML api. By default Spring seems to wire up Jackson which marshalls my response objects to JSON, however I want it to default to responding in XML without requiring any Accept header from clients, hence I configured the default content type as follows:
#Configuration
class SpringWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_XML);
}
}
This works just fine, however when running our tests I discovered that calling /health now returns a 406 status code and no content (it previously returned a 200 and a JSON response).
Having reverted the above change I thought perhaps I could force each controller to explicitly set the response content type via the use of a ResponseEntity, in doing so I tried the following in my controller method:
#RequestMapping(value = "/blah",
method = RequestMethod.GET)
ResponseEntity<MyResponseObject> getProgrammeRestrictions(#PathVariable String coreNumber) {
// Generate response object (code snipped)...
new ResponseEntity<MyResponseObject>(myResponseObject,
new HttpHeaders(contentType: MediaType.APPLICATION_XML),
HttpStatus.OK)
}
However this doesn't seem to influence the response type, which still defaults to JSON.
In a nutshell it seems that setting a default non-json content type breaks the actuator healthcheck. Is there someway to force the healthcheck bits and bobs to disregard the default setting and always be generated in JSON?
Has anyone else experienced this? Grateful for any pointers as I'm a bit stuck here.
Many thanks,
Edd
You need to add jackson-dataformat-xml dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Then, make its XmlMapper available:
#Autowired
private MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter;
#Bean
public ObjectMapper objectMapper(){
// this returns an XmlMapper, which is a subclass of ObjectMapper
return mappingJackson2XmlHttpMessageConverter.getObjectMapper();
}
It works when sending a request from the browser (http://localhost:8080/health), the returned result is in XML (chrome sends the header Accept: */*).
When sending the request programmatically, you still have to pass Accept: application/json in your header since the service expects this media type, but the returned result will be XML.
I have a Spring MVC 4 app with Spring Security 4 and is deployed on Tomcat 8 running under jdk 1.8. The web-service has the controller defined as such:
#RequestMapping(value = "/build", method = RequestMethod.POST, produces = "application/json", headers =
{ "Accept=*/*", "Content-Type=*/*" })
public SubjectEntity build(#RequestBody SubjectImportDTO subjectImportDTO)
{
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = null;
if (principal instanceof User)
{
user = ((User) principal);
}
SubjectEntity entity = service.build(subjectImportDTO);
System.out.println("FINISH: build");
return entity;
}
I am getting a csrf token, I have that setup correctly. I know the url is getting called correctly because I can see that in the logs when I get there. The service on the back-end is running, data is correctly entered into the database, I correctly get the write object, and using the Jackson Mapper, the object 'SubjectEntity' should be translated into JSON and sent back to the requesting client. This web-service has been unit tested under the Spring Web Test framework, and it works great!
So, I am familiar with an HTTP 404 error in not finding a URL when the wrong parameters are passed in, or you're trying to do a POST when it's a GET, etc. So many reasons why we can get a 404 error ...
BUT ... IN THIS CASE ... We've already gotten to the URL, executed the code, and then it has the data it needs. Since the Controller says we have content-type / and it produces application/json, I don't know what else could be wrong?
Any ideas?
You should add #ResponseBodyto your method. without this, Spring mvc tries to find another handler method which can send a response.
NB: #RestController automatically add #ResponseBody on each method in a controller.
I develop some unmanaged extensions for neo4j 2.3.0. Now I want to test the functionality of my code with junit. Is there a way to test my methods locally without an neo4j instance running on my pc?
I want something like this:
- create a temporary instance of neo4j before executing test
- fill instance with data
- call my extension via rest
- check the results
I have created a Neo4JTestServer class (using neo4j-harness):
public final class Neo4jTestServer {
public static final String EXTENSION_MOUNT_POINT = "/v1";
public static final String EXTENSION_RESOURCES = "my.company.neo4j.extension";
private static Neo4jTestServer INSTANCE = null;
public static synchronized Neo4jTestServer getInstance() {
if (INSTANCE == null) {
INSTANCE = new Neo4jTestServer();
}
return INSTANCE;
}
private final ServerControls serverControls;
private Neo4jTestServer() {
serverControls = TestServerBuilders.newInProcessBuilder()
.withExtension(EXTENSION_MOUNT_POINT, EXTENSION_RESOURCES)
.newServer();
}
public ServerControls getServerControls() {
return serverControls;
}
public void shutdown() {
serverControls.close();
}
}
And my test class is looking like this:
public class TestResource {
private Neo4jTestServer server;
#Before
public void prepare(){
this.server = Neo4jTestServer.getInstance();
}
#After
public void endup(){
// Shutdown server
this.server.shutdown();
}
#Test
public void test(){
//TODO fill neo4j with data
HTTP.Response response = HTTP.GET(this.server.getServerControls().httpURI().resolve("/v1/calculation/test").toString());
}
}
Here is a link! on the resource i have used.
I also checked the questions from here! but i don't understand if they are running the tests on a 'real' neo4j instance or not.
Can this only be executed on the server or is there a way to run such tests locally?
EDIT:
I always get a 500 response calling the rest method.
And this is the output when executing the test:
Nov 04, 2015 10:33:17 AM com.sun.jersey.api.core.PackagesResourceConfig init
INFORMATION: Scanning for root resource and provider classes in the packages:my.company.neo4j.extension
Nov 04, 2015 10:33:17 AM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFORMATION: Root resource classes found: class my.company.neo4j.extension.Resource
Nov 04, 2015 10:33:17 AM com.sun.jersey.api.core.ScanningResourceConfig init
INFORMATION: No provider classes found.
Nov 04, 2015 10:33:17 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFORMATION: Initiating Jersey application, version 'Jersey: 1.19 02/11/2015 03:25 AM'
Nov 04, 2015 10:33:17 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFORMATION: Initiating Jersey application, version 'Jersey: 1.19 02/11/2015 03:25 AM'
Nov 04, 2015 10:33:17 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFORMATION: Initiating Jersey application, version 'Jersey: 1.19 02/11/2015 03:25 AM'
Nov 04, 2015 10:33:17 AM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFORMATION: Initiating Jersey application, version 'Jersey: 1.19 02/11/2015 03:25 AM'
Is this "INFORMATION: No provider classes found." a problem maybe?
EDIT - this is my extension (it is just for testing)
#Path("/calculation")
public class ResourceV1 {
#Path("/test")
#GET
public Response test() throws Exception{
return Response.ok().build();
}
}
I also found the neo4j.log file from the temporary database:
2015-11-04 11:52:39.876+0100 INFO [o.n.s.d.LifecycleManagingDatabase] Successfully started database
2015-11-04 11:52:39.896+0100 INFO [o.n.s.CommunityNeoServer] Starting HTTP on port 7474 (4 threads available)
2015-11-04 11:52:40.087+0100 INFO [o.n.s.m.ThirdPartyJAXRSModule] Mounted unmanaged extension [my.company.neo4j.extension] at [/v1]
2015-11-04 11:52:40.156+0100 INFO [o.n.s.w.Jetty9WebServer] Mounting static content at /webadmin
2015-11-04 11:52:40.233+0100 WARN [o.n.s.w.Jetty9WebServer] No static content available for Neo Server at port 7474, management console may not be available.
2015-11-04 11:52:40.252+0100 INFO [o.n.s.w.Jetty9WebServer] Mounting static content at /browser
2015-11-04 11:52:41.267+0100 INFO [o.n.s.CommunityNeoServer] Remote interface ready and available at http://localhost:7474/
What you are trying is following
- Neo4JTestServer class is a factory for an in-memory Neo4j server. It's basically same as "real" one.
- TestResource class is a simple test for your extension.
Problem could be how do you load your extension into the test. Is your extension in the package name "my.company.neo4j.extension"?
Could you please show us code of your extension?
My suggestions are following
- read http://neo4j.com/docs/stable/server-unmanaged-extensions-testing.html
- look on GraphUnit which provide much better possibilities how to test extensions - https://github.com/graphaware/neo4j-framework/tree/master/tests
After setting up a new project I found the solution for my problem. In my maven dependencies I had both
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>${neo4j.version}</version>
<scope>test</scope>
</dependency>
and
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
And in my case, here was the problem - after removing the dependency for javax ws the testcase was successful and I can call the rest methods.
EDIT: After removing the jax rs dependency it was not possible for me to build the extension because of the missing dependencies.
My solution: Adding following dependency instead of javax.ws.rs-api.
<dependency>
<groupId>org.neo4j.3rdparty.javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<scope>provided</scope>
<version>1.1.2.r612</version>
</dependency>
It seems that it is working fine with neo4j-harness.
I am adding JSONP support to a REST Service in SPRING4 + JDK 8 + STS 3.6.4
Versions:
Spring 4.1.6.RELEASE
My implementation is based on these links:
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-jsonp
The REST service returns ResponseEntity or ResponseBody and use case is to return data in JSONP format.
Added a ControllerAdvice
#ControllerAdvice
public class JsonpCallbackAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpCallbackAdvice(){
super("Callback");
}
}
Here is the Controller of the REST Service
#Controller
public class AcctController {
...
#RequestMapping(value = "/act/{actNum}", method = RequestMethod.GET)
public ResponseEntity<Account> getAccount(#PathVariable("actNum") Integer accountNum) throws Exception {
...
return new ResponseEntity<account>();
}
Here is the relevant web application context configuration
...
<context:component-scan base-package="com.controllers" />
<bean name="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
...
The controller and ControllerAdvice are in same package.
When deployment of the project is initiated following exception is observed
Caused by: java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.lang.String
at org.springframework.context.annotation.AnnotationBeanNameGenerator.determineBeanNameFromAnnotation(AnnotationBeanNameGenerator.java:91)
at org.springframework.context.annotation.AnnotationBeanNameGenerator.generateBeanName(AnnotationBeanNameGenerator.java:69)
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:246)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:84)
at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:74)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1427)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1417)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:174)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:144)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:100)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:510)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
This exception was not happening before the ControllerAdvice was added.
Also, it does not happens when #ControllerAdvice annotation is removed
or
the component scan excludes scanning package of the ControllerAdvice class
I tried with Spring version 4.2.0.RC1, and the exception still happens.
Kindly help with resolution of this exception, since not much help is available online.
It seems like a bug in SPRING 4, however am not sure.
I am using Azure service-bus queues (AMQP Protocol) with Apache Qpid (0.3) as Java client.
I am also using Spring JmsTemplate to produce messages and DefaultMessageListenerContainer to manage my consumers, spring JMS 4.0.6.
Spring configurations:
#PostConstruct
private void JndiLookup() throws NamingException {
// Configure JNDI environment
Hashtable<String, String> envPrp = new Hashtable<String, String>();
envPrp.put(Context.INITIAL_CONTEXT_FACTORY,
PropertiesFileInitialContextFactory.class.getName());
envPrp.put("connectionfactory.SBCF", "amqps://owner:{parimeryKey}#{namespace}.servicebus.windows.net");
envPrp.put("queue.STORAGE_NEW_QUEUE", "QueueName");
context = new InitialContext(envPrp);
}
#Bean
public ConnectionFactory connectionFactory() throws NamingException {
ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
return cf;
}
#Bean
public DefaultMessageListenerContainer messageListenerContainer() throws NamingException {
DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer();
messageListenerContainer.setConnectionFactory(connectionFactory());
Destination queue = (Destination) context.lookup("QueueName");
messageListenerContainer.setDestination(queue);
messageListenerContainer.setConcurrency("3-10");
MessageListenerAdapter adapter = new MessageListenerAdapter();
adapter.setDelegate(new MessageWorker());
adapter.setDefaultListenerMethod("onMessage");
messageListenerContainer.setMessageListener(adapter);
return messageListenerContainer;
}
#Bean
public JmsTemplate jmsTemplate() throws NamingException {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
Nothing fancy in the configurations just straight forward.
Running the code and everything seems to be working .. but after few minutes without traffic in the queue it seems like the consumers are losing connection with the queue and not taking messages.
I dont know if it is related but every 5 minutes in am getting the following warning:
Fri Nov 07 15:23:53 +0000 2014, (DefaultMessageListenerContainer.java:842) WARN : Setup of JMS message listener invoker failed for destination 'org.apache.qpid.amqp_1_0.jms.impl.QueueImpl#8fb0427b' - trying to recover. Cause: Force detach the link because the session is remotely ended.
Fri Nov 07 15:23:56 +0000 2014, (DefaultMessageListenerContainer.java:891) INFO : Successfully refreshed JMS Connection
I have messages being in the queue for hours and not being handled by the consumers only when I restart the app the consumers renewing the connection properly and taking the messages.
Is it possible that the problem is with the Spring Listener container properties or qpid connection factory or is it an issue with Azure service bus??
Couldn't find related post to my situation will appreciate the help!!