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;
}
}
Related
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;
}
}
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;
}
I have a JSF-Project with one class called Label and one xhtml-page, called label.xhtml. In label.xhtml the method Label.getValue() is called via injection. The test LabelTest runs a embedded container and in a test method label.xhtml is requested and the body content will be checked. So far everything is fine, but I would like to change the value of the attribute Label.value in my test so the test can assert his own set value and not the value of the Postconstruct-method of class Label.
I put a breakpoint in the constructor of class Label. So I can see the stacktrace and I read the code of many of these methods. Maybe it is possible to change the produces class, so I can put there my own AbstractProducer in some way?
Stacktrace Label constructor
Code on GitHub or scroll down.
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named
#RequestScoped
public class Label {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#PostConstruct
public void fillValue() {
setValue("HELLO");
}
}
import org.apache.tomee.embedded.EmbeddedTomEEContainer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import javax.ejb.embeddable.EJBContainer;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class LabelTest {
private WebDriver driver;
#Before
public void setup() {
driver = new HtmlUnitDriver();
Map<Object, Object> properties = new HashMap();
properties.put(EJBContainer.PROVIDER, EmbeddedTomEEContainer.class);
properties.put(EJBContainer.MODULES, new File[]{new File("src/main/webapp/")});
properties.put(EJBContainer.APP_NAME, "hfe");
System.setProperty("tomee.webapp.externalRepositories", "build/classes/main,build/classes/test");
EmbeddedTomEEContainer.createEJBContainer(properties);
}
#After
public void cleanup() {
driver.close();
}
#Test
public void requestHtmlPage_ThenBodyContainsPostConstructValue() {
assertEquals("HELLO", getBodyValue());
}
#Test
public void manipulateInjectedObjectAndRequestHtmlPage_ThenBodyContainsValueOfManipulatedInjectedObject() {
// how is it possible to manipulate the injected object with value=MY_VALUE?
assertEquals("MY_VALUE", getBodyValue());
}
private String getBodyValue() {
driver.get("http://localhost:8080/hfe/faces/label.xhtml");
WebElement body = driver.findElement(By.tagName("body"));
return body.getText();
}
}
label.xhtml
<html>
<h:body>
#{label.value}
</h:body>
</html>
I got a solution from the TomEE User MailingList: Using the Annotation #Specializes or using a ServletFilter. You can find the example code at the GitHub project.
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;
}
}
}
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);
}