Custom Web Service Using Servlet - web-services

I would like to implement a servlet representing a web service endpoint. That servlet should intercept the XML sent to it and read its content among other information it receives. I know that is possible since Spring-WS takes a similar approach. Can you suggest how I can implement a servlet like that?

Here is a naive way that is on top of my head to achieve what you need, hopefully it will get you started.
Scenario
You have a library app. Suppose you want to have an API endpoint a client can use to create a book that is described by an xml file. The xml file will be sent to a Servlet.
The example endpoint API url is:
http://localhost:8080/api/books
Step 1
Create a Servlet called CreateBookServlet
// servlet mapping
<servlet-mapping>
<servlet-name>CreateBookServlet</servlet-name>
<url-pattern>/api/books</url-pattern>
</servlet-mapping>
Step 2
// ...
public class CreateBookServlet extends HttpServlet {
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String xmlStr = request.getParameter("xmlbook");
// better use some xml library to parse xml to Book object
Book book = xmlLibrary.parse(xmlStr);
//save the Book object to database
bookService.create(book);
}
// since it's a creation of an entity, we use POST instead of GET method
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
Step 3
Send a post request to CreateBookServlet (http://localhost:8080/api/books) with a parameter whose name is "xmlbook" and value is a simple string containing the xml code of the new book to create. The xml string can be created by reading an xml file, of course.
Summary
A lot of important things are omitted here such as security, xml parsing and so on, but this simple approach will do what you ask, I hope.

Related

aem-mocks property test a servlet

Trying to write some proper AEM integration tests using the aem-mocks framework. The goal is to try and test a servlet by calling its path,
E.g. an AEM servlet
#SlingServlet(
paths = {"/bin/utils/emailSignUp"},
methods = {"POST"},
selectors = {"form"}
)
public class EmailSignUpFormServlet extends SlingAllMethodsServlet {
#Reference
SubmissionAgent submissionAgent;
#Reference
XSSFilter xssFilter;
public EmailSignUpFormServlet(){
}
public EmailSignUpFormServlet(SubmissionAgent submissionAgent, XSSFilter xssFilter) {
this.submissionAgent = submissionAgent;
this.xssFilter = xssFilter;
}
#Override
public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
}
}
Here is the corresponding test to try and do the integration testing. Notice how I've called the servlet's 'doPost' method, instead of 'POST'ing via some API.
public class EmailSignUpFormServletTest {
#Rule
public final AemContext context = new AemContext();
#Mock
SubmissionAgent submissionAgent;
#Mock
XSSFilter xssFilter;
private EmailSignUpFormServlet emailSignUpFormServlet;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
Map<String,String> report = new HashMap<>();
report.put("statusCode","302");
when(submissionAgent.saveForm(any(String.class)).thenReturn(report);
}
#Test
public void emailSignUpFormDoesNotRequireRecaptchaChallenge() throws IOException {
// Setup test email value
context.request().setQueryString("email=test.only#mail.com");
//===================================================================
/*
* WHAT I END UP DOING:
*/
// instantiate a new class of the servlet
emailSignUpFormServlet = new EmailSignUpFormServlet(submissionAgent, xssFilter);
// call the post method (Simulate the POST call)
emailSignUpFormServlet.doPost(context.request(),context.response());
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
// assert response is internally redirected, hence expected status is a 302
assertEquals(302,context.response().getStatus());
}
}
I've done a lot of research on how this could be done (here) and (here), and these links show a lot about how you can set various parameters for context.request() object. However, they just don't show how to finally execute the 'post' call.
What you are trying to do is mix a UT with IT so this won't be easy at least with the aem-mocks framework. Let me explain why.
Assuming that you are able to call your required code
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
Your test will end up executing all the logic in SlingAllMethodsServlet class and its parent classes. I am assuming that this is not what you want to test as these classes are not part of your logic and they already have other UT/IT (under respective Apache projects) to cater for testing requirements.
Also, looking at your code, bulk of your core logic resides in following snipper
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
Your UT criteria is already met by the following line of your code:
emailSignUpFormServlet.doPost(context.request(),context.response());
as it covers most of that logic.
Now, if you are looking for proper IT for posting the parameters and parsing them all the way down to doPost method then aem-mocks is not the framework for that because it does not provide it in a simple way.
You can, in theory, mock all the layers from resource resolver, resource provider and sling servlet executors to pass the parameters all the way to your core logic. This can work but it won't benefit your cause because:
Most of the code is already tested via other UT
Too many internal mocking dependencies might make the tests flaky or version dependant.
If you really want to do pure IT, then it will be easier to host the servlet in an instance and access it via HttpClient. This will ensure that all the layers are hit. A lot of tests are done this way but it feels a bit heavy handed for the functionality you want to test and there are better ways of doing it.
Also the reason why context.request().POST doesn't exist is because context.request() for is a mocked state for the sake of testing. You want to actually bind and mock Http.Post operations which needs some way to resolve to your servlet and that is not supported by the framework.
Hope this helps.

Unit test for web service (Service Reference) - xml deserialization

In Summary
I need a way to deserialize an XML string into an object normally returned by a 3rd party webservice.
Using C#.
In Detail
I have code that consumes a 3rd party Service Reference (Web Service) - so the usual stuff: we pass in a Request object and it returns a Response object.
Regarding unit testing - I'm not interested in the inner workings of the Service Reference since this is a 3rd party service. I'm only interested in two things:
Does my code generate the correct Request object?
When the Service Reference returns it's response, do I process this response correctly?
Taking each in turn:
Does my code generate the correct Request object?
This I can do. If anyone's interested in this, what I do is to replace my service reference with a RhinoMocks Mock object. In my unit test I call the method on my Mock and then check the arguments passed in, comparing the actual Request object against the expected Request object.
When the Service Reference returns it's response, do I process this response correctly?
What I want to do here is to create a RhinoMocks Stub of my service reference, so that when it's called this stub returns a response object populated with my test data.
The problem that I face is that the response objects returned by this particular 3rd party service are extremely complex. If I were to attempt to create one by hard-coding all the property values by hand then this would probably take me the best part of a whole day.
However, what I can very easily do is to capture the XML serialized response from this service. I could then easily edit it's values and store this XML in one of my unit tests.
What I'm after is an easy way to then "deserialize" this "test" XML into a response object and use this to program the response from my Stub.
Any help would be much appreciated.
Thanks
Griff
Turns out that this is quite simple:
public static object Deserialize(string xml, Type toType)
{
using(Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var s = new XmlSerializer(toType, "http://^your url^");
return s.Deserialize(stream);
}
}
Note that if you're using XML from a SOAP request, strip the SOAP envelop off first.
Griff

Jetty UserRealm redirect on 3th failed login

If I have a custom Jetty UserRealm implementation and its configured for basic authentication (with SSL), is there any way to get it to go to an specific page after the 3rd failed login?
Well really I just want to display some contact information to the user if they cannot login after 3 attempts.
Alternatively is it possible to display the exception which I throw from the
public Principal authenticate(final String username, final Object credentials, final Request request)
method when its configured as basic authentication?
Thanks
Neil
The BasicAuthenticator is responsible for sending the 403 response when there's no valid credentials in the request.
Looking at the Jetty 6 source, you're best bet is probably to subclass the BasicAuthenticator and override public void sendChallenge(UserRealm realm,Response response)
public class MyAuthenticator extends BasicAuthenticator {
#Override
public void sendChallenge(UserRealm realm, Response response) {
int numberOfAttempts = getNumberOfAuthenticationAttempts();
if (numberOfAttempts > 3) {
sendContactDetails(realm, response);
}
else
super.sendChallenge(realm, response);
}
protected int getNumberOfAuthenticationAttempts() { ... }
protected void sendContactDetails(Response response) { ... }
}
Obviously the problem doing this is that you don't have access to the HttpServletRequest which may make tracking request attempts more difficult. You could probably gain access to this via HttpConnection.getCurrentConnection(). Otherwise the code for BasicAuthenticator doesn't lend itself to extension without a blob of copy/paste, but that may be OK in your case.
I'm ignoring the issue of how you track the number of requests have been made in the same authentication attempt, that's going to be dependent upon how your clients are connecting.
Alternatively you can set the ErrorHandler on the context, which is used when HttpResponse.sendError is called, which will be the case when you throw an exception in your realm.
I'd probably opt to use the first method as it more clearly separates responsibilities.

Exposing existing API as a Web service

I am currently working on a task to expose an API as a Web service. The idea here is to package the existing business logic in JAR files into a WAR file and expose the WAR file as a Web service that would return a free-form XML string. When we expose an existing API as a Web service, is it sufficient that we make available an XSD & WSDL file of the returned XML string data? Is that the convention or the standard practice?
It depends on whether or not you are using SOAP or REST. SOAP is more restrictive; as a result, it's more expected that you'll have a WSDL file to generate the classes that interface with the API.
On the other hand, if you are using REST, just exposing a RESTful URI would be considered enough to meet the constraint of a RESTful web service having a uniform interface.
REST tends to be gaining more ground over SOAP since it is a permissive architectural style. I would prefer this method, and I would recommend this method if you're new to developing web services.
Depending on what language you are using, I'm assuming Java, you can use Restlets or Spring 3.0's REST framework to help you build a RESTful web service. These tools really make that job a lot easier and help you conform to the 6 Constraints of a RESTful Web Service and meet the 4 Key Goals.
UPDATE:
Assuming you already have existing, object-oriented code, and assuming you want to expose that code as a REST API, using Spring 3.0 MVC, create a Controller subclass that will wrap around your existing package:
Example GET:
Resource: Javadocs for Jackson's ObjectMapper POJO/JSON Marshaller
// this is the wrapper around your existing Java packages.
#Controller
public class UserController {
protected static final DATA_TYPE = "json";
// In REST, GET method is used to retrieve data with no side effects,
// meaning that no changes are made to the data on the server.
#RequestMapping(value="/users/{username}", method=RequestMethod.GET)
public void getUserData(#PathVariable("username") String userName, Model model) {
// this is your existing class
UserDataService userDataService = new UserDataService();
// assume you have a class User, and getUserDetails gives you that POJO object.
User user = userDataService.getUserDetails(username);
// marshal the User object to JSON, using Jackson, and write as output in response
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getWriter(), user);
}
}
// assume you have an existing POJO class called User
class User implements Serializable {
String username;
String age;
String birthday;
String mood;
String getMood() { return this.mood; }
String getBirthday() { return this.birthday; }
String getAge() { return this.age; }
String getUsername() { return this.username; }
String setMood(String mood) { this.mood = mood; }
String setBirthday(String birthday) { this.birthday = birthday; }
String setAge(String age) { this.age = age; }
String setUsername(String username) { this.username = username; }
}
Request:
http://api.example.com:8080/users/jmort253/
Response:
{
"username":"jmort253",
"mood":"good",
"age":"not too old and not too young",
"birthday","Jan 1, 1900"
}
XML instead of JSON:
The main difference between returning XML and returning JSON is in the marshaller used. Using javax.xml.bind.annotations, you can place annotations on the POJO class so the marshaller can convert it to XML, freeing you up from the details of having to manually code XML by hand:
Using javax.xml.bind.annotations to convert Java Objects to XML and XSD. This resource also explains how to generate the XML Schema, if you deem that as a requirement to your REST Web service.
#XmlRootElement
class User implements Serializable {
String username;
String age;
String birthday;
String mood;
String getMood() { return this.mood; }
String getBirthday() { return this.birthday; }
String getAge() { return this.age; }
String getUsername() { return this.username; }
String setMood(String mood) { this.mood = mood; }
String setBirthday(String birthday) { this.birthday = birthday; }
String setAge(String age) { this.age = age; }
String setUsername(String username) { this.username = username; }
}
Instead of using the Jackson API's ObjectMapper class to marshal the POJO class to JSON, use the javax.xml.bind.annotations package in place of ObjectMapper:
JAXBContext context = JAXBContext.newInstance(User.class);
Marshaller marshaller = context.createMarshaller();
// pretty print XML
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(user, System.out);
Aside from the other resources, this article has a few examples that use JAXB to deserialize an ArrayList of POJO objects to XML.
My final suggestion when working on the REST Web service wrappers is to set your logging levels to "ALL" or "DEBUG". I find that this helps me more easily determine the root cause of any problems I face when setting up a Web service. The libraries themselves will output helpful debug messages to help you resolve configuration issues, such as missing dependencies, missing annotations, and other issues that you'll likely encounter when dealing with the conversion to XML/JSON process or in setting up Spring 3.0.
Once you're uniform interfaces are setup and you can make GET requests and receive responses, you can then set the logging levels back to the previous INFO or WARN levels.
First of all, I would hesitate before exposing an existing API as a web service on a one-for-one basis. Was the existing API written to be accessed over a network? If not, then it was probably not designed with networking constraints in mind.
It may include method calls which will involve larger numbers of small operations - the kind that cost nothing when used within a single process. Over a network, each call has an associated latency that is much larger than the overhead of caling a method within the same process.
Instead, I would design a service to meet the functional requirements of the API. The service would likely be designed to have a smaller number of operations which perform more work per operation, thereby minimizing the overhead associated with network traffic. The service would likely be implemented by calling on the API (assuming it is written to handle multi-threaded environments like a service).
In terms of WSDL, the toolkit you are using may very well construct a WSDL for you. I know that WCF in .NET does that, and I have done the same using IBM Rational Web Developer, so I know that the Java world can do the same.
Otherwise, it's not actually that hard to hand-write a WSDL and corresponding schema. In either case, they do need to be provided so that your clients can consume the service.
There's nothing wrong with using REST for this if your API can be cleanly expressed as a set of operations on resources. In this case, yes, provide the schema to make it easier for your clients to process the XML. I would beware of forcing your API to fit the REST model if it is not cleanly expressible as operations on resources.

Mocking Spring MVC BindingResult when using annotations

I'm migrating a Spring MVC controller to use the newer style annotations, and want to unit test a controller method that validates a command object (see simple example below).
#RequestMapping(method = RequestMethod.POST)
public String doThing(Command command, BindingResult result,
HttpServletRequest request, HttpServletResponse response,
Map<String, Object> model){
ThingValidator validator = new ThingValidator();
validator.validate(command, result);
... other logic here
}
My problem is I have to call the controller's method in my unit test, and provide mock values to satisfy its signature to exercise the code properly, and I cannot work out how to mock a BindingResult.
In the old style Controller the signature simply took a HttpServletRequest and HttpServletResponse, which were easily mockable, but due to the flexibility of the new annotation style, one has to pass a lot more in via the signature.
How can one mock a Spring BindingResult for use in a unit test??
You could also use something like Mockito to create a mock of the BindingResult and pass that to your controller method, ie
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verifyZeroInteractions;
#Test
public void createDoesNotCreateAnythingWhenTheBindingResultHasErrors() {
// Given
SomeDomainDTO dto = new SomeDomainDTO();
ModelAndView mv = new ModelAndView();
BindingResult result = mock(BindingResult.class);
when(result.hasErrors()).thenReturn(true);
// When
controller.create(dto, result, mv);
// Then
verifyZeroInteractions(lockAccessor);
}
This can give you more flexibility and simplify scaffolding.
BindingResult is an interface so can you not simply pass in one of Springs implementations of that interface?
I don't use annotations in my Spring MVC code but when I want to test the validate method of a validator I just pass in an instance of BindException and then use the values it returns in assertEquals etc.
As mentioned in this answer
A BindingResult is created for you by Spring MVC for each incoming HTTP request.
Thus, you don't want to mock the BindingResult.
Instead of calling the methods directly you should either do a real http call to the controller or perform a simulation using MockMvc.perform. Here is a tutorial on the official documentation that shows how to do it.