Injecting a custom response header in RESTEasy JAX-RS - web-services

I have RESTEasy (JAX-RS) server with about 60 services (so far). I would like to automatically inject a custom response header to provider callers with the server build time: X-BuildTime: 20100335.1130.
Is there an easy way to do this without modifying each of my services?
I am trying to use a class that implements org.jboss.resteasy.spi.interception.PostProcessInterceptor with annotations #Provider and #ServerInterceptor, but I can't figure out how to modify the ServerResponse that is passed into my postProcess() method.

Although MessageBodyWriterInterceptor does the trick, it is better to use PostProcessInterceptor, as it will intercept responses that do not call MessageBodyWriters (such as Response.created(URI.create("/rest/justcreated")).build()).
For more info, see the official documentation.
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.interception.PostProcessInterceptor;
#Provider
#ServerInterceptor
public class MyPostProcessInterceptor implements PostProcessInterceptor {
#Override
public void postProcess(ServerResponse response) {
MultivaluedMap<String, Object> headers = response.getMetadata();
List<Object> domains = headers.get("X-BuildTime");
if (domains == null) { domains = new ArrayList<Object>(); }
domains.add("20100335.1130");
headers.put("X-BuildTime", domains);
}
}

I think using javax.servlet.Filter will be a much easier solution:
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.setHeadder(header, headerValue);
chain.doFilter(request, response);
}
configure it in web.xml for the relevant urls, and you are done.

How about using javax.ws.rs.core.Response ; this way you can set the header in the same place where you create the response-data.
#GET
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public Response test( ){
HashMap<String,String> ret = new HashMap<String,String>();
ret.put("foo","bar");
return Response
.status(Response.Status.OK)
.entity(ret)
.header("X-say", "Hello world!")
.build();
}

You can also change header by MessageBodyInterceptors
( check the example at the end of section 30.1 )
#Provider
#ServerInterceptor
public class MyHeaderDecorator implements MessageBodyWriterInterceptor {
public void write(MessageBodyWriterContext context) throws IOException, WebApplicationException
{
context.getHeaders().add("My-Header", "custom");
context.proceed();
}
}

Related

Extract SOAP custom header information

I want to read request header from SOAP incoming request in my Java code for some authorization purpose. I found few work-arounds like using SOAPHandlers and . Code as below :
`package com.cerillion.ccs.framework;
import java.util.HashSet;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.apache.log4j.Logger;
public class ApiSoapHandler implements SOAPHandler<SOAPMessageContext> {
private static final Logger logger = Logger.getLogger(ApiSoapHandler.class.getName());
#Override
public void close(MessageContext arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean handleMessage(SOAPMessageContext context) {
logger.debug("Inside ApiSoapHandler");
try {
SOAPMessage message = context.getMessage();
SOAPHeader header = message.getSOAPHeader();
message.saveChanges();
} catch (SOAPException e) {
logger.error("Error occurred while adding credentials to SOAP header.",
e);
}
return true;
}
#Override
public Set<QName> getHeaders() {
/* QName securityTokenHeader = new QName("urn:com.intertech.secty", "token");
//new QName(“urn:com.intertech.secty”,“username”);
HashSet<QName> headers = new HashSet<QName>();
headers.add(securityTokenHeader);
return headers;*/
return null;
} }`
I ma really curious about to have some other simple alternative rather than writing entire handler just for fetching custom header tag. Is this the only way to read SOAP request header ? Any leads are really appreciated

DELETE giving 405 in postman

I am new to restful webservices and following java restful api tutorials.
all the HTTP requests are working fine except the DELETE request.
i am facing the same issue as described in this link.
REST - HTTP Status 405 - Method Not Allowed
also , in the predefined header values postman is showing
allow →GET,OPTIONS,PUT(image link is below)
i am following the correct syntax and url format as per the specification(pasted in image) but delete is not working.
allowed : GET,OPTIONS,PUT.img
please let me know where i am missing.
Edit : Source code :
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.koushik.javabrains.messenger.model.Message;
import org.koushik.javabrains.messenger.service.MessageService;
#Path("/messages")
public class MessageResource {
MessageService messageService = new MessageService();
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Message> getMessages(){
System.out.println("Hello There");
List<Message> returnedList = messageService.getAllMessages();
System.out.println(returnedList);
return returnedList;
}
#GET
#Path("/{messageId}")
#Produces(MediaType.APPLICATION_JSON)
public Message getMessage(#PathParam("messageId") long messageId){
System.out.println("Message returned");
return messageService.getMessage(messageId);
}
#POST
#Produces(MediaType.APPLICATION_JSON)
public Message addMessage(Message message){
return messageService.addMessage(message);
}
#PUT
#Path("/{messageId}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Message updateMessage(#PathParam("messageId") long id , Message message){
message.setId(id);
return messageService.updateMessage(message);
}
#DELETE
#Path("/messageId")
#Produces(MediaType.APPLICATION_JSON)
public void deleteMessage(#PathParam("messageId") long id){
System.out.println("Hello There");
messageService.removeMessage(id);
}
}
package org.koushik.javabrains.messenger.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.koushik.javabrains.messenger.database.DatabaseClass;
import org.koushik.javabrains.messenger.model.Message;
public class MessageService {
private Map<Long , Message> messages = DatabaseClass.getMessages();
public MessageService(){
messages.put(1L, new Message(1, "Hello World" , "koushik"));
messages.put(2L, new Message(2, "Hello Jersey" , "Koushik"));
}
public List<Message> getAllMessages(){
return new ArrayList<Message>(messages.values());
}
public Message getMessage(long id){
Message m = messages.get(id);
System.out.println("Value of message "+m);
return m;
}
public Message addMessage(Message message){
message.setId(messages.size()+1);
messages.put(message.getId(), message);
return message;
}
public Message updateMessage(Message message){
if(message.getId() <=0){
return null;
}
messages.put(message.getId(), message);
return message;
}
public Message removeMessage(long id){
return messages.remove(id);
}
}
package org.koushik.javabrains.messenger.database;
import java.util.HashMap;
import java.util.Map;
import org.koushik.javabrains.messenger.model.Message;
import org.koushik.javabrains.messenger.model.Profile;
public class DatabaseClass {
private static Map<Long , Message> messages = new HashMap();
private static Map<String , Profile> profiles = new HashMap();
public static Map<Long , Message> getMessages() {
return messages;
}
public static Map<String, Profile> getProfiles() {
return profiles;
}
}
except delete other requests are working fine.
on sending DELETE the server is returning HTTP -405 method not allowed.
also on sending delete request then in the header values postman is showing
allow →GET,OPTIONS,PUT
Thanks
Make sure you do not have any URL Parameters entered in Postman, also make sure you do not pass any headers in postman.
The delete request should be plain without and of these.
Also, the string message ID need to be put in curly brackets. That would be the reason for your 405 mostly.
Change path annotation as below and try.
#Path("/{messageId}")

How to use Tuckey urlrewrite in spring boot to access service using ?wsdl

My clients wanted to access wsdl using ?wsdl notation, but the springboot service i have created is working with only '.wsdl' format. I need a working example/sample how to configure Tuckey urlrewrite in the springboot application.
I have tried using below code, but the application complaints as it cant find urlrewrite.xml (which i have placed in src/main/resources folder.
Q1: How can i make my service to be accessible using url below
http://localhost:8080/ws/organisation?wsdl
I have tried using below code, but tuckey cannot find the urlrewrite.xml which is under src/java/resources.
#Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
final FilterRegistrationBean registrationBean = new ilterRegistrationBean();
registrationBean.setFilter(new UrlRewriteFilter());
registrationBean.addInitParameter("confPath", "urlrewrite.xml");
return registrationBean;
}
Finally I could figure out a solution. This is now reading urlrewrite.xml from the src/main/resources folder.
No need to declare above mentioned bean definition in the question post (public FilterRegistrationBean tuckeyRegistrationBean()), as the below code declared as #Component will automatically register with context and url-rewriting is performed.
#Component
public class WsdlUrlRewriteFilter extends UrlRewriteFilter {
private static final String CONFIG_LOCATION = "classpath:/urlrewrite.xml";
#Value(CONFIG_LOCATION)
private Resource resource;
#Override
protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
Conf conf = new Conf(filterConfig.getServletContext(), resource.getInputStream(), resource.getFilename(), "");
checkConf(conf);
} catch (IOException ex) {
throw new ServletException("Unable to load URL-rewrite configuration file from " + CONFIG_LOCATION, ex);
}
}
}
Best option is to write your own filter as follows.You can make use of HttpServletRequestWrapper to handle the ?wsdl extension and let the server handle the request.
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
#Component
public class WSDLQuestionMarkReplaceFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
//put init logs
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if ("wsdl".equalsIgnoreCase(httpRequest.getQueryString())) {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
#Override
public String getQueryString() {
return null;
}
#Override
public String getRequestURI() {
return super.getRequestURI() + ".wsdl";
}
};
chain.doFilter(requestWrapper, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void destroy() {
//put destroy logs
}
}
It worked for me. Hope it works for others too. SpringBoot+JAR
Please use the following dependency
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.4</version>
</dependency>
Created urlrewrite.xml in resource folder
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite
PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN"
"http://www.tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite>
<rule>
<name>Domain Name Check</name>
<condition name="host" operator="notequal">www.userdomain.com</condition>
<from>^(.*)$</from>
<to type="redirect">http://www.userdomain.com$1</to>
</rule>
Added in main ApplicationRunner.java
#Bean
public FilterRegistrationBean tuckeyRegistrationBean() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CustomURLRewriter());
return registrationBean;
}
And created a CustomURLRewriter
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.tuckey.web.filters.urlrewrite.Conf;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;
import org.tuckey.web.filters.urlrewrite.UrlRewriter;
import javax.servlet.*;
import java.io.InputStream;
public class CustomURLRewriter extends UrlRewriteFilter {
private UrlRewriter urlRewriter;
#Autowired
Environment env;
#Override
public void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
try {
ClassPathResource classPathResource = new ClassPathResource("urlrewrite.xml");
InputStream inputStream = classPathResource.getInputStream();
Conf conf1 = new Conf(filterConfig.getServletContext(), inputStream, "urlrewrite.xml", "");
urlRewriter = new UrlRewriter(conf1);
} catch (Exception e) {
throw new ServletException(e);
}
}
#Override
public UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#Override
public void destroyUrlRewriter() {
if(urlRewriter != null)
urlRewriter.destroy();
}
}

No 'Access-Control-Allow-Origin' header is present on the requested resource.

I'm using SpringMVC.I want to call an XML file with web service in order to parse it later.The problem is that I can't access the XML file, I have got this error:No 'Access-Control-Allow-Origin' header is present on the requested resource.I have tried the solution below:
I created a new class which purpose is to add Access-Control-Allow-Origin' header on the requested ressource.This is the class
package com.mycompany.myapp;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JsonpFilter implements Filter {
private String functionName;
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse servletResponse,
FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("This filter can "
+ " only process HttpServletRequest requests");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (isJSONPRequest(httpRequest)) {
ServletOutputStream out = response.getOutputStream();
out.println(getCallbackMethod(httpRequest) + "(");
chain.doFilter(request, response);
out.println(");");
response.setContentType("text/javascript");
} else {
response.addHeader("Access-Control-Allow-Origin", "*");
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
this.functionName = filterConfig.getInitParameter("encoding");
if(this.functionName == null || this.functionName.length() <= 0) {
this.functionName = "callback";
}
}
private String getCallbackMethod(HttpServletRequest httpRequest) {
return httpRequest.getParameter(this.functionName);
}
private boolean isJSONPRequest(HttpServletRequest httpRequest) {
String callbackMethod = getCallbackMethod(httpRequest);
return (callbackMethod != null && callbackMethod.length() > 0);
}
}
then I add the these two lines in my web.xml file:
<display-name>DataServices</display-name>
<filter>
<filter-name>JSONPRequestFilter</filter-name>
<filter-class> com.mycompany.myapp.JsonpFilter</filter-class>
<init-param>
<param-name>functionName</param-name>
<param-value>callback</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>JSONPRequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I don't have any complilation problem, but I still have the same error in the console at the execution time.So, the 'Access-Control-Allow-Origin' header isn't taken into account.
Please,If you can find out what is wrong with my program, or suggest me another solution, I will be thankful
To do this implement this interface
org.springframework.web.servlet.HandlerInterceptor
here is an example
#Component
public class CORSInterceptor implements HandlerInterceptor{
private static final Log LOG = LogFactory.getLog(CORSInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
LOG.trace("sending headers");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
LOG.trace("afterCompletion is called");
}
}
Then add this line to your application context
<mvc:interceptors>
<bean class="com.elm.mb.rest.interceptors.CORSInterceptor" />
</mvc:interceptors>

How to test my servlet using JUnit

I have created a web system using Java Servlets and now want to make JUnit testing. My dataManager is just a basic piece of code that submits it to the database. How would you test a Servlet with JUnit?
My code example that allows a user to register/sign up, which is submitted from my main page via AJAX:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// Get parameters
String userName = request.getParameter("username");
String password = request.getParameter("password");
String name = request.getParameter("name");
try {
// Load the database driver
Class.forName("com.mysql.jdbc.Driver");
//pass reg details to datamanager
dataManager = new DataManager();
//store result as string
String result = dataManager.register(userName, password, name);
//set response to html + no cache
response.setContentType("text/html");
response.setHeader("Cache-Control", "no-cache");
//send response with register result
response.getWriter().write(result);
} catch(Exception e){
System.out.println("Exception is :" + e);
}
}
You can do this using Mockito to have the mock return the correct params, verify they were indeed called (optionally specify number of times), write the 'result' and verify it's correct.
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.*;
import javax.servlet.http.*;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
public class TestMyServlet extends Mockito{
#Test
public void testServlet() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
when(request.getParameter("username")).thenReturn("me");
when(request.getParameter("password")).thenReturn("secret");
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(writer);
new MyServlet().doPost(request, response);
verify(request, atLeast(1)).getParameter("username"); // only if you want to verify username was called...
writer.flush(); // it may not have been flushed yet...
assertTrue(stringWriter.toString().contains("My expected string"));
}
}
First off, in a real application, you would never get database connection info in a servlet; you would configure it in your app server.
There are ways, however, of testing Servlets without having a container running. One is to use mock objects. Spring provides a set of very useful mocks for things like HttpServletRequest, HttpServletResponse, HttpServletSession, etc:
http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/mock/web/package-summary.html
Using these mocks, you could test things like
What happens if username is not in the request?
What happens if username is in the request?
etc
You could then do stuff like:
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class MyServletTest {
private MyServlet servlet;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Before
public void setUp() {
servlet = new MyServlet();
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
#Test
public void correctUsernameInRequest() throws ServletException, IOException {
request.addParameter("username", "scott");
request.addParameter("password", "tiger");
servlet.doPost(request, response);
assertEquals("text/html", response.getContentType());
// ... etc
}
}
I find Selenium tests more useful with integration or functional (end-to-end) testing. I am working with trying to use org.springframework.mock.web, but I am not very far along. I am attaching a sample controller with a jMock test suite.
First, the Controller:
package com.company.admin.web;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import com.company.admin.domain.PaymentDetail;
import com.company.admin.service.PaymentSearchService;
import com.company.admin.service.UserRequestAuditTrail;
import com.company.admin.web.form.SearchCriteria;
/**
* Controls the interactions regarding to the refunds.
*
* #author slgelma
*
*/
#Controller
#SessionAttributes({"user", "authorization"})
public class SearchTransactionController {
public static final String SEARCH_TRANSACTION_PAGE = "searchtransaction";
private PaymentSearchService searchService;
//private Validator searchCriteriaValidator;
private UserRequestAuditTrail notifications;
#Autowired
public void setSearchService(PaymentSearchService searchService) {
this.searchService = searchService;
}
#Autowired
public void setNotifications(UserRequestAuditTrail notifications) {
this.notifications = notifications;
}
#RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE)
public String setUpTransactionSearch(Model model) {
SearchCriteria searchCriteria = new SearchCriteria();
model.addAttribute("searchCriteria", searchCriteria);
notifications.transferTo(SEARCH_TRANSACTION_PAGE);
return SEARCH_TRANSACTION_PAGE;
}
#RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="cancel")
public String cancelSearch() {
notifications.redirectTo(HomeController.HOME_PAGE);
return "redirect:/" + HomeController.HOME_PAGE;
}
#RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="execute")
public String executeSearch(
#ModelAttribute("searchCriteria") #Valid SearchCriteria searchCriteria,
BindingResult result, Model model,
SessionStatus status) {
//searchCriteriaValidator.validate(criteria, result);
if (result.hasErrors()) {
notifications.transferTo(SEARCH_TRANSACTION_PAGE);
return SEARCH_TRANSACTION_PAGE;
} else {
PaymentDetail payment =
searchService.getAuthorizationFor(searchCriteria.geteWiseTransactionId());
if (payment == null) {
ObjectError error = new ObjectError(
"eWiseTransactionId", "Transaction not found");
result.addError(error);
model.addAttribute("searchCriteria", searchCriteria);
notifications.transferTo(SEARCH_TRANSACTION_PAGE);
return SEARCH_TRANSACTION_PAGE;
} else {
model.addAttribute("authorization", payment);
notifications.redirectTo(PaymentDetailController.PAYMENT_DETAIL_PAGE);
return "redirect:/" + PaymentDetailController.PAYMENT_DETAIL_PAGE;
}
}
}
}
Next, the test:
package test.unit.com.company.admin.web;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.support.SessionStatus;
import com.company.admin.domain.PaymentDetail;
import com.company.admin.service.PaymentSearchService;
import com.company.admin.service.UserRequestAuditTrail;
import com.company.admin.web.HomeController;
import com.company.admin.web.PaymentDetailController;
import com.company.admin.web.SearchTransactionController;
import com.company.admin.web.form.SearchCriteria;
/**
* Tests the behavior of the SearchTransactionController.
* #author slgelma
*
*/
#RunWith(JMock.class)
public class SearchTransactionControllerTest {
private final Mockery context = new JUnit4Mockery();
private final SearchTransactionController controller = new SearchTransactionController();
private final PaymentSearchService searchService = context.mock(PaymentSearchService.class);
private final UserRequestAuditTrail notifications = context.mock(UserRequestAuditTrail.class);
private final Model model = context.mock(Model.class);
/**
* #throws java.lang.Exception
*/
#Before
public void setUp() throws Exception {
controller.setSearchService(searchService);
controller.setNotifications(notifications);
}
#Test
public void setUpTheSearchForm() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
context.checking(new Expectations() {{
oneOf(model).addAttribute(
with(any(String.class)), with(any(Object.class)));
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.setUpTransactionSearch(model);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void cancelSearchTest() {
final String target = HomeController.HOME_PAGE;
context.checking(new Expectations(){{
never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
oneOf(notifications).redirectTo(with(any(String.class)));
}});
String nextPage = controller.cancelSearch();
assertThat("Controller is not requesting the correct form",
nextPage, containsString(target));
}
#Test
public void executeSearchWithNullTransaction() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId(null);
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(true));
never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId());
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void executeSearchWithEmptyTransaction() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId("");
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(true));
never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId());
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void executeSearchWithTransactionNotFound() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
final String badTransactionId = "badboy";
final PaymentDetail transactionNotFound = null;
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId(badTransactionId);
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(false));
atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class)));
oneOf(searchService).getAuthorizationFor(with(any(String.class)));
will(returnValue(transactionNotFound));
oneOf(result).addError(with(any(ObjectError.class)));
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void executeSearchWithTransactionFound() {
final String target = PaymentDetailController.PAYMENT_DETAIL_PAGE;
final String goodTransactionId = "100000010";
final PaymentDetail transactionFound = context.mock(PaymentDetail.class);
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId(goodTransactionId);
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(false));
atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class)));
oneOf(searchService).getAuthorizationFor(with(any(String.class)));
will(returnValue(transactionFound));
oneOf(notifications).redirectTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
nextPage, containsString(target));
}
}
I hope this might help.
Updated Feb 2018: OpenBrace Limited has closed down, and its ObMimic product is no longer supported.
Here's another alternative, using OpenBrace's ObMimic library of Servlet API test-doubles (disclosure: I'm its developer).
package com.openbrace.experiments.examplecode.stackoverflow5434419;
import static org.junit.Assert.*;
import com.openbrace.experiments.examplecode.stackoverflow5434419.YourServlet;
import com.openbrace.obmimic.mimic.servlet.ServletConfigMimic;
import com.openbrace.obmimic.mimic.servlet.http.HttpServletRequestMimic;
import com.openbrace.obmimic.mimic.servlet.http.HttpServletResponseMimic;
import com.openbrace.obmimic.substate.servlet.RequestParameters;
import org.junit.Before;
import org.junit.Test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Example tests for {#link YourServlet#doPost(HttpServletRequest,
* HttpServletResponse)}.
*
* #author Mike Kaufman, OpenBrace Limited
*/
public class YourServletTest {
/** The servlet to be tested by this instance's test. */
private YourServlet servlet;
/** The "mimic" request to be used in this instance's test. */
private HttpServletRequestMimic request;
/** The "mimic" response to be used in this instance's test. */
private HttpServletResponseMimic response;
/**
* Create an initialized servlet and a request and response for this
* instance's test.
*
* #throws ServletException if the servlet's init method throws such an
* exception.
*/
#Before
public void setUp() throws ServletException {
/*
* Note that for the simple servlet and tests involved:
* - We don't need anything particular in the servlet's ServletConfig.
* - The ServletContext isn't relevant, so ObMimic can be left to use
* its default ServletContext for everything.
*/
servlet = new YourServlet();
servlet.init(new ServletConfigMimic());
request = new HttpServletRequestMimic();
response = new HttpServletResponseMimic();
}
/**
* Test the doPost method with example argument values.
*
* #throws ServletException if the servlet throws such an exception.
* #throws IOException if the servlet throws such an exception.
*/
#Test
public void testYourServletDoPostWithExampleArguments()
throws ServletException, IOException {
// Configure the request. In this case, all we need are the three
// request parameters.
RequestParameters parameters
= request.getMimicState().getRequestParameters();
parameters.set("username", "mike");
parameters.set("password", "xyz#zyx");
parameters.set("name", "Mike");
// Run the "doPost".
servlet.doPost(request, response);
// Check the response's Content-Type, Cache-Control header and
// body content.
assertEquals("text/html; charset=ISO-8859-1",
response.getMimicState().getContentType());
assertArrayEquals(new String[] { "no-cache" },
response.getMimicState().getHeaders().getValues("Cache-Control"));
assertEquals("...expected result from dataManager.register...",
response.getMimicState().getBodyContentAsString());
}
}
Notes:
Each "mimic" has a "mimicState" object for its logical state. This provides a clear distinction between the Servlet API methods and the configuration and inspection of the mimic's internal state.
You might be surprised that the check of Content-Type includes "charset=ISO-8859-1". However, for the given "doPost" code this is as per the Servlet API Javadoc, and the HttpServletResponse's own getContentType method, and the actual Content-Type header produced on e.g. Glassfish 3. You might not realise this if using normal mock objects and your own expectations of the API's behaviour. In this case it probably doesn't matter, but in more complex cases this is the sort of unanticipated API behaviour that can make a bit of a mockery of mocks!
I've used response.getMimicState().getContentType() as the simplest way to check Content-Type and illustrate the above point, but you could indeed check for "text/html" on its own if you wanted (using response.getMimicState().getContentTypeMimeType()). Checking the Content-Type header the same way as for the Cache-Control header also works.
For this example the response content is checked as character data (with this using the Writer's encoding). We could also check that the response's Writer was used rather than its OutputStream (using response.getMimicState().isWritingCharacterContent()), but I've taken it that we're only concerned with the resulting output, and don't care what API calls produced it (though that could be checked too...). It's also possible to retrieve the response's body content as bytes, examine the detailed state of the Writer/OutputStream etc.
There are full details of ObMimic and a free download at the OpenBrace website. Or you can contact me if you have any questions (contact details are on the website).
EDIT: Cactus is now a dead project: http://attic.apache.org/projects/jakarta-cactus.html
You may want to look at cactus.
http://jakarta.apache.org/cactus/
Project Description
Cactus is a simple test framework for unit testing server-side java code (Servlets, EJBs, Tag Libs, Filters, ...).
The intent of Cactus is to lower the cost of writing tests for server-side code. It uses JUnit and extends it.
Cactus implements an in-container strategy, meaning that tests are executed inside the container.
Another approach would be to create an embedded server to "host" your servlet, allowing you to write calls against it with libraries meant to make calls to actual servers (the usefulness of this approach somewhat depends on how easily you can make "legitimate" programatic calls to the server - I was testing a JMS (Java Messaging Service) access point, for which clients abound).
There are a couple of different routes you can go - the usual two are tomcat and jetty.
Warning: something to be mindful of when choosing the server to embed is the version of servlet-api you are using (the library which provides classes like HttpServletRequest). If you are using 2.5, I found Jetty 6.x to work well (which is the example I'll give below). If you're using servlet-api 3.0, the tomcat-7 embedded stuff seems to be a good option, however I had to abandon my attempt to use it, as the application I was testing used servlet-api 2.5. Trying to mix the two will result in NoSuchMethod and other such exceptions when attempting to configure or start the server.
You can set up such a server like this (Jetty 6.1.26, servlet-api 2.5):
public void startServer(int port, Servlet yourServletInstance){
Server server = new Server(port);
Context root = new Context(server, "/", Context.SESSIONS);
root.addServlet(new ServletHolder(yourServletInstance), "/servlet/context/path");
//If you need the servlet context for anything, such as spring wiring, you coudl get it like this
//ServletContext servletContext = root.getServletContext();
server.start();
}
Use Selenium for webbased unit tests. There's a Firefox plugin called Selenium IDE which can record actions on the webpage and export to JUnit testcases which uses Selenium RC to run the test server.
First you should probably refactor this a bit so that the DataManager is not created in the doPost code.. you should try Dependency Injection to get an instance. (See the Guice video for a nice intro to DI.). If you're being told to start unit testing everything, then DI is a must-have.
Once your dependencies are injected you can test your class in isolation.
To actually test the servlet, there are other older threads that have discussed this.. try here and here.
public class WishServletTest {
WishServlet wishServlet;
HttpServletRequest mockhttpServletRequest;
HttpServletResponse mockhttpServletResponse;
#Before
public void setUp(){
wishServlet=new WishServlet();
mockhttpServletRequest=createNiceMock(HttpServletRequest.class);
mockhttpServletResponse=createNiceMock(HttpServletResponse.class);
}
#Test
public void testService()throws Exception{
File file= new File("Sample.txt");
File.createTempFile("ashok","txt");
expect(mockhttpServletRequest.getParameter("username")).andReturn("ashok");
expect(mockhttpServletResponse.getWriter()).andReturn(new PrintWriter(file));
replay(mockhttpServletRequest);
replay(mockhttpServletResponse);
wishServlet.doGet(mockhttpServletRequest, mockhttpServletResponse);
FileReader fileReader=new FileReader(file);
int count = 0;
String str = "";
while ( (count=fileReader.read())!=-1){
str=str+(char)count;
}
Assert.assertTrue(str.trim().equals("Helloashok"));
verify(mockhttpServletRequest);
verify(mockhttpServletResponse);
}
}