Handling invalid enum values while doing JAXB Unmarshalling - web-services

My Jaxb has created a Enum class based on the XML schema set up.
**enum Fruit {
APPLE,ORANGE;
}**
I am using a SOAP UI to check my web service. Since it is a free form entry, if i give a wrong fruit say "Guva" then instead of throwing an exception it is returning it as null after doing the UnMarshalling.
How can i avoid this? Should i use custom enum class instead of JAXB generated one. Please give some example. i.e.
regards
sri

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
By default your JAXB (JSR-222) implementation will not fail on any conversion exceptions. If you are using the JAXB APIs to do the unmarshalling then you can set a ValidationEventHandler to catch any problems. Below is an example.
Root
package forum12147306;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private int number;
private Fruit fruit;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Fruit getFruit() {
return fruit;
}
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}
Fruit
package forum12147306;
public enum Fruit {
APPLE,
ORANGE;
}
Demo
package forum12147306;
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
private static final String XML = "<root><number>ABC</number><fruit>Guva</fruit></root>";
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent validationEvent) {
System.out.println(validationEvent.getMessage());
//validationEvent.getLinkedException().printStackTrace();
return true;
}
});
Root root = (Root) unmarshaller.unmarshal(new StringReader(XML));
}
}
JAXB REFERENCE IMPLEMENTATION
Unfortunately there appears to be a bug in the JAXB RI as a validation event is not being through for the invalid enum value.
Not a number: ABC
Work Around
Write your own XmlAdapter to handle to conversion to/from the Fruit enum:
FruitAdapter
package forum12147306;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FruitAdapter extends XmlAdapter<String, Fruit> {
#Override
public String marshal(Fruit fruit) throws Exception {
return fruit.name();
}
#Override
public Fruit unmarshal(String string) throws Exception {
try {
return Fruit.valueOf(string);
} catch(Exception e) {
throw new JAXBException(e);
}
}
}
Fruit
Use the #XmlJavaTypeAdapter annotation to associate the XmlAdapter with the Fruit enumb.
package forum12147306;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlJavaTypeAdapter(FruitAdapter.class)
public enum Fruit {
APPLE,
ORANGE;
}
New Output
Not a number: ABC
javax.xml.bind.JAXBException
- with linked exception:
[java.lang.IllegalArgumentException: No enum const class forum12147306.Fruit.Guva]
EclipseLink JAXB (MOXy)
Using MOXy both validation events are thrown. To specify MOXy as your JAXB provider see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html.
Exception Description: The object [ABC], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[number-->number/text()]] with descriptor [XMLDescriptor(forum12147306.Root --> [DatabaseTable(root)])], could not be converted to [class java.lang.Integer].
Internal Exception: java.lang.NumberFormatException: For input string: "ABC"
Exception Description: No conversion value provided for the value [Guva] in field [fruit/text()].
Mapping: org.eclipse.persistence.oxm.mappings.XMLDirectMapping[fruit-->fruit/text()]
Descriptor: XMLDescriptor(forum12147306.Root --> [DatabaseTable(root)])

Short answer based on original reply. You need to do 2 things
implement custom adapter to raise and exception
add event handler to fail unmarshalling
Fruit.java defines and uses the adapter
package forum12147306;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlJavaTypeAdapter(FruitAdapter.class)
public enum Fruit {
APPLE,
ORANGE;
}
class FruitAdapter extends XmlAdapter<String, Fruit> {
#Override
public String marshal(Fruit fruit) throws Exception {
return fruit.name();
}
#Override
public Fruit unmarshal(String string) throws Exception {
try {
return Fruit.valueOf(string);
} catch(Exception e) {
throw new JAXBException(e);
}
}
}
The event handler for unmarshaller that fails parsing on error - i.e it returns false (you might need to decide when to fail and when not to fail)
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent validationEvent) {
return false;
}
});

An alternative consists in generating XSD schemas as presented in here: how can i unmarshall in jaxb and enjoy the schema validation without using an explicit schema file.
Here is the snippet I stole from dolbysurnd:
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Result;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.SAXException;
private static List<DOMResult> generateJaxbSchemas(JAXBContext context) throws IOException {
final List<DOMResult> domResultList = new ArrayList<>();
context.generateSchema(new SchemaOutputResolver() {
#Override
public Result createOutput(String ns, String file) throws IOException {
DOMResult domResult = new DOMResult();
domResult.setSystemId(file);
domResultList.add(domResult);
return domResult;
}
});
return domResultList;
}
private static Unmarshaller createUnmarshaller(JAXBContext context) throws SAXException, IOException, JAXBException {
Unmarshaller unmarshaller = context.createUnmarshaller();
List<DOMSource> domSourceList = new ArrayList<>();
for (DOMResult domResult : generateJaxbSchemas(context)) {
domSourceList.add(new DOMSource(domResult.getNode()));
}
SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = schemaFactory.newSchema(domSourceList.toArray(new DOMSource[0]));
unmarshaller.setSchema(schema);
return unmarshaller;
}
public void unmarshal(JAXBContext context, Reader reader) throws JAXBException, SAXException, IOException {
Unmarshaller unmarshaller = createUnmarshaller(context);
Object result = unmarshaller.unmarshal(reader);
}

Related

lambda handler for multiple triggers in aws lambda

How will the Lambda handler of multiple different types of trigger look like in java?
I want to set a trigger for Cloudwatch Event Rule, S3. How can I do this?
public class App implements RequestHandler<S3Event, Context>
{
public Context handleRequest(S3Event s3event, Context context) {
System.out.println("welcome to lambda");
}
}
You can use Map<String,String> as input
public class Handler implements RequestHandler<Map<String,String>, String>{
#Override
public String handleRequest(Map<String,String> event, Context context) {
}
}
Or use RequestStreamHandler and parse the InputStream to the correct object.
public class HandlerStream implements RequestStreamHandler {
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
}
}
using a generic Object could work, I use Serverless Framework with:
package com.serverless;
import java.util.Collections;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class Handler implements RequestHandler<Object, Object> {
private static final Logger LOG = LogManager.getLogger(Handler.class);
#Override
public Object handleRequest(final Object input, final Context context) {
LOG.info("received: {}", input);
return input;
}
}

Getting [javax.xml.bind.JAXBException: "com.mta.example" doesnt contain ObjectFactory.class or jaxb.index]

I am trying to consume Soap webservice in my Springboot application. I am getting [javax.xml.bind.JAXBException: "com.mta.example" doesnt contain ObjectFactory.class or jaxb.index] error in my code. I am not sure what configuration is wrong here. what should be the value of the contextPath? Does it refer to any package or should match some element from the WSDL? Sorry I am not too much familiar with SOAP webservices.
Spring boot Main class as below.
package com.mta;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan(basePackages = {"com.mta"})
public class MTApplication {
public static void main(String[] args) {
SpringApplication.run(MTApplication.class, args);
LoginClient logClient = new LoginClient();
logClient.getLoginDetails();
}
}
Soap Configuration class as below
package com.mta;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class SoapConfiguration {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.mta.example");
return marshaller;
}
#Bean
public SoapConnector soapConnector(Jaxb2Marshaller marshaller) {
SoapConnector client = new SoapConnector();
client.setDefaultUri("https://test.platform.ws");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
SoapConnector class to call webservice
package com.mta;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
public class SoapConnector extends WebServiceGatewaySupport {
public Object callWebService(String url, Object request) {
// CREDENTIALS and REQUEST SETTINGS...
return getWebServiceTemplate().marshalSendAndReceive(url, request);
}
}
Client class
public class LoginClient extends WebServiceGatewaySupport{
#Autowired
SoapConnector soapConnector;
private static final Logger log = LoggerFactory.getLogger(LoginClient.class);
public LoginResponse getLoginDetails() {
LoginRequest request = new LoginRequest();
request.setUserId("mtatest");
request.setPassword("test");
LoginResponse response = (LoginResponse) soapConnector.callWebService("http://www.mta.com/gp/Login", request);
System.out.println(response.getOpCode());
return response;
}

Unit testing middleware with NUnit and NSubstitute

I've written a bit of middleware in an ASP.NET Core site and I'm trying to unit test it, mainly by following this guide that uses Moq.
My problem is finding an NUnit/NSubstitute equivalent for new DefaultHttpContext(). Substituting HttpContext will trigger the middleware, but it passes the try. I presume this is because of the issue quoted below. Does NUnit have a function to create a real HttpContext, or am I looking at a lot more infrastructure to achieve this?
I am sending an instance of DefaultHttpContext to the Invoke method. I can't use a mocked HttpContext in this scenario because the first middleware (the lambda function that we passed to the constructor) will need to write to the response. Hence the HttpResponse needs to be a real object not mocked.
Here is the code for my Test
[TestFixture]
public class ExceptionHelperTests
{
private IErrorRepository errorRepository;
private ExceptionHandler handler;
[SetUp]
public void Setup()
{
errorRepository = Substitute.For<IErrorRepository>();
}
[Test]
public async void Given_AnExceptionHappens_Then_ItShouldBeLogged()
{
// Arrange
const string username = "aUser";
var user = Substitute.For<ClaimsPrincipal>();
user.Identity.Name.Returns(username);
handler = new ExceptionHandler(
next: async (innerHttpContext) =>
{
innerHttpContext.User = user;
},
repository: errorRepository);
// Act
await handler.Invoke(new DefaultHttpContext());
// Assert
errorRepository.Received().LogException(Arg.Any<string>(), Arg.Any<Exception>(), Arg.Is(username));
}
}
Here is the IErrorRepository
public interface IErrorRepository
{
Exception LogException(string message, Exception ex, string userId);
void LogMessage(string message, string errorDetail, string userId);
}
And here is the middleware (with a simplified HandleException):
public sealed class ExceptionHandler
{
private readonly RequestDelegate _next;
private readonly IErrorRepository repository;
public ExceptionHandler(RequestDelegate next, IErrorRepository repository)
{
_next = next;
this.repository = repository;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
HandleException(ex, context.User.Identity.Name);
}
}
public void HandleException(Exception ex, string userId)
{
repository.LogException("An unhandled exception has occurred.", ex, userId);
}
}
DefaultHttpContext is just the default implementation of HttpContext abstract class.
You just could do
var HttpContextSub = Substitute.For<HttpContext>();

Testing Katharsis JsonApi with MockMvc and Mockito

I would like to test the behaviour configured by my Katharsis ResourceRepository (katharsis-spring 2.1.7):
import io.katharsis.queryParams.QueryParams;
import io.katharsis.repository.ResourceRepository;
import org.springframework.stereotype.Component;
#Component
public class UserResourceRepository implements ResourceRepository<UserDTO, String> {
#Autowired
private UserRepository databaseRepository;
#Override
public UserDTO findOne(String email, QueryParams queryParams) {
return null;
}
#Override
public Iterable<UserDTO> findAll(QueryParams queryParams) {
return null;
}
#Override
public Iterable<UserDTO> findAll(Iterable<String> iterable, QueryParams queryParams) {
return null;
}
#Override
public void delete(String email) {
}
#Override
public UserDTO save(UserDTO s) {
return null;
}
}
I would like to test it in a similar way as I do it with normal, Spring Controllers, using Mockito to mock database repository and using MockMvc:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Optional;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#RunWith(MockitoJUnitRunner.class)
public class UserJsonApiTest {
#InjectMocks
private UserResourceRepository resourceRepository;
#Mock
private UserRepository databaseRepository;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(resourceRepository).build();
}
#Test
public void first() throws Exception {
Optional<UserEntity> user = Optional.of(new UserEntity().
id(1).
email("test#test").
firstName("test first name").
lastName("test last name").
pass("test pass"));
when(
databaseRepository.
findOneByEmail(user.get().getEmail())).
thenReturn(user);
mockMvc.perform(
get("/users/" + user.get().email())).
andExpect(status().isOk())
;
}
}
Obviously, this code doesn't work because my Katharsis UserResourceRepository is not really a Controller. So far I have learned (from logs) that it is actually using some filters mappings and class named io.katharsis.spring.KatharsisFilterV2.
Is there any way to use MockMvc for such test? If not - is there any other way I could test it without starting the whole server (with mocking)?
You could use an embedded Server - like UndertowJaxrsServer - and inject the KatharsisFeature:
Create a class (MyApp) which extends Application public static class MyApp extends Application { and deploy it to the embedded server server.deploy(MyApp.class);
in this Class, overwrite getClasses and add a second class (KatharsisFeatureTest) which implements Feature KatharsisFeatureTest implements Feature
in KatharsisFeatureTest you can then register a KatharsisFeature and there you can overwrite JsonServiceLocator and inject the mock.
Sound a little bit complicated, but works like charm :)
Have a look at my implementation.
.
#RunWith(MockitoJUnitRunner.class)
public class EndpointResourceTest {
#Mock
private EndpointService endpointService;
#InjectMocks
private final static EndpointResourceV1 endpointRessource = new EndpointResourceV1();
private static UndertowJaxrsServer server;
#BeforeClass
public static void beforeClass() throws Exception {
server = new UndertowJaxrsServer();
server.deploy(MyApp.class);
server.start();
}
#Test
public void testGetEndpoint() throws URISyntaxException {
Mockito.when(endpointService.getEndpoint("SUBMIT")).thenReturn(new EndpointDTO("SUBMIT", "a", "b"));
Client client = ClientBuilder.newClient();
Response response = client.target(TestPortProvider.generateURL("/api/endpoints/SUBMIT"))
.request(JsonApiMediaType.APPLICATION_JSON_API)
.get();
Assert.assertEquals(200, response.getStatus());
String json = response.readEntity(String.class);
Assert.assertTrue(json.contains("SUBMIT"));
Assert.assertTrue(json.contains("a"));
Assert.assertTrue(json.contains("b"));
Mockito.verify(endpointService, Mockito.times(1)).getEndpoint("SUBMIT");
}
#AfterClass
public static void afterClass() throws Exception {
server.stop();
}
#ApplicationPath("/api")
public static class MyApp extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> classes = new HashSet<Class<?>>();
classes.add(KatharsisFeatureTest.class);
return classes;
}
}
public static class KatharsisFeatureTest implements Feature {
#Override
public boolean configure(FeatureContext featureContext) {
featureContext
.property(KatharsisProperties.RESOURCE_SEARCH_PACKAGE, "...")
.register(new io.katharsis.rs.KatharsisFeature(
new ObjectMapper(), new QueryParamsBuilder(new DefaultQueryParamsParser()), new SampleJsonServiceLocator() {
#Override
public <T> T getInstance(Class<T> clazz) {
try {
if (clazz.equals(EndpointResourceV1.class)) {
return (T) endpointRessource;
}
return clazz.newInstance();
}
catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}));
return true;
}
}
}

Auto marshalling in JAX RS

I have worked with Jax WS and had used wsgen and wsimport for auto marshalling of custom types. Can I use wsgen with JaxRS as well? If so where should I place my wsgen generated files and how to reference them? I just wish not to deal with using JAXB myself and use wsgen as a shortcut.
By default a JAX-RS implementation will use JAXB to convert domain objects to/from XML for the application/xml media type. In the example below a JAXBContext will be created on the Customer class since it appears as a parameter and/or return type in the RESTful operations.
package org.example;
import java.util.List;
import javax.ejb.*;
import javax.persistence.*;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
#Stateless
#LocalBean
#Path("/customers")
public class CustomerService {
#PersistenceContext(unitName="CustomerService",
type=PersistenceContextType.TRANSACTION)
EntityManager entityManager;
#POST
#Consumes(MediaType.APPLICATION_XML)
public void create(Customer customer) {
entityManager.persist(customer);
}
#GET
#Produces(MediaType.APPLICATION_XML)
#Path("{id}")
public Customer read(#PathParam("id") long id) {
return entityManager.find(Customer.class, id);
}
}
The JAXBContext created on a single class will also create metadata for all transitively reference classes but that may not bring in everything that was generated from your XML schema. You will need to leverage the JAX-RS context resolver mechanism.
package org.example;
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
#Provider
#Produces("application/xml")
public class CustomerContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext jc;
public CustomerContextResolver() {
try {
jc = JAXBContext.newInstance("com.example.customer" , Customer.class.getClassLoader());
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
public JAXBContext getContext(Class<?> clazz) {
if(Customer.class == clazz) {
return jc;
}
return null;
}
}