Unable to get collection response from cxf rest web service - web-services

I am trying to get java.util.List as response from cxf rest web service.
I have tried with WebClient class's method postObjectGetCollection method but no luck.
I am getting - org.apache.cxf.jaxrs.client.ClientWebApplicationException: .No message body reader has been found for class : interface java.util.Collection, ContentType : application/json.
Below are my client code-
String mediaType = "application/json";
if (url != null) {
List<DataTypeDTO> resultdtos = new ArrayList<DataTypeDTO>();
WebClient client = WebClient.create(url);
client = client.accept(mediaType).type(mediaType).path(uri);
resultdtos = (List<DataTypeDTO>)client.getCollection(DataTypeDTO.class);
System.out.println(resultdtos);
}
Please help me out if i am missing any configuration or other things.

You need to provide the provider list while creating the webClient object in your rest client.
You can use the below code to resolve your issue:
final String url = "http://localhost:10227/someService";
final String uri = "/manageXyz/fetchAllDataTypes";
final String mediaType = "application/json";
Object response = null;
List<Object> providers = new ArrayList<Object>();
providers.add( new JacksonJaxbJsonProvider() );
WebClient client = WebClient.create(url, providers);
client = client.accept(mediaType).type(mediaType).path(uri);
response = (List<Object>)client.post(oemUser, List.class);
If you are using maven, you also need to provide below required jars to resolve maven dependency in your project:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.13</version>
</dependency>

Related

Amazon AppConfig from Spring Boot

How can I access configurations from aws appconfig, in my spring boot application?
Since appconfig is a new service, is there any java sdk that we can use, cos i dont see anything for appconfig yet in https://github.com/aws/aws-sdk-java/tree/master/src/samples
Here's how I've integrated AWS AppConfig into my Spring Boot project.
First, let’s make sure we have this dependency in our pom.xml:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-appconfig</artifactId>
<version>1.12.134</version>
</dependency>
Next, let’s create a simple configuration class of our own AWS AppConfig Client:
#Configuration
public class AwsAppConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(AwsAppConfiguration.class);
private final AmazonAppConfig appConfig;
private final GetConfigurationRequest request;
public AwsAppConfiguration() {
appConfig = AmazonAppConfigClient.builder().build();
request = new GetConfigurationRequest();
request.setClientId("clientId");
request.setApplication("FeatureProperties");
request.setConfiguration("JsonProperties");
request.setEnvironment("dev");
}
public JSONObject getConfiguration() throws UnsupportedEncodingException {
GetConfigurationResult result = appConfig.getConfiguration(request);
String message = String.format("contentType: %s", result.getContentType());
LOGGER.info(message);
if (!Objects.equals("application/json", result.getContentType())) {
throw new IllegalStateException("config is expected to be JSON");
}
String content = new String(result.getContent().array(), "ASCII");
return new JSONObject(content).getJSONObject("feature");
}
}
Lastly, let’s create a scheduled task that polls the configuration from AWS AppConfig:
#Configuration
#EnableScheduling
public class AwsAppConfigScheduledTask {
private static final Logger LOGGER = LoggerFactory.getLogger(AwsAppConfigScheduledTask.class);
#Autowired
private FeatureProperties featureProperties;
#Autowired
private AwsAppConfiguration appConfiguration;
#Scheduled(fixedRate = 5000)
public void pollConfiguration() throws UnsupportedEncodingException {
LOGGER.info("polls configuration from aws app config");
JSONObject externalizedConfig = appConfiguration.getConfiguration();
featureProperties.setEnabled(externalizedConfig.getBoolean("enabled"));
featureProperties.setLimit(externalizedConfig.getInt("limit"));
}
}
I came across this question, as I was also trying to figure out how to best integrate AWS AppConfig into Spring Boot.
Here's an article I created. You can visit it here: https://levelup.gitconnected.com/create-features-toggles-using-aws-appconfig-in-spring-boot-7454b122bf91
Also, the source code is available on github: https://github.com/emyasa/medium-articles/tree/master/aws-spring-boot/app-config
First I added dependencies
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfig</artifactId>
<version>2.18.41</version>
</dependency>
<!-- https://mvnrepository.com/artifact/software.amazon.awssdk/appconfigdata -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>appconfigdata</artifactId>
<version>2.19.4</version>
</dependency>
Then build the client
client = AppConfigDataClient.builder()
.credentialsProvider(() -> AwsBasicCredentials.create("<your id>", "your secret key"))
.region(Region.<your region>)
.build();
Use the client to start the configuration session
StartConfigurationSessionRequest startConfigurationSessionRequest = StartConfigurationSessionRequest.builder()
.applicationIdentifier("<your application id>")
.environmentIdentifier("your environment id")
.configurationProfileIdentifier("your config id")
.build();
Get the session token in the beginning and use it for the initial call.
String sessionToken = client.startConfigurationSession(startConfigurationSessionRequest).initialConfigurationToken();
GetLatestConfigurationRequest latestConfigurationRequest = GetLatestConfigurationRequest.builder()
.configurationToken(sessionToken)
.build();
GetLatestConfigurationResponse latestConfigurationResponse = client.getLatestConfiguration(latestConfigurationRequest);
String response = latestConfigurationResponse.configuration().asUtf8String();
You can use the next token available in the response to make the next call. The token can be cached as required.
2023, use aws-java-sdk-appconfigdata
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-appconfigdata</artifactId>
<version>1.12.394</version>
</dependency>
example:
#Slf4j
#Configuration
#EnableScheduling
public class AWSAppConfig {
private String token;
private final AWSAppConfigData client;
public AWSAppConfig() {
log.info("init app config");
var client = AWSAppConfigDataClient.builder().build();
var request = new StartConfigurationSessionRequest();
request.setEnvironmentIdentifier("prod");
request.setApplicationIdentifier("my-app");
request.setConfigurationProfileIdentifier("my-config");
request.setRequiredMinimumPollIntervalInSeconds(15);
var result = client.startConfigurationSession(request);
this.client = client;
this.token = result.getInitialConfigurationToken();
}
#Scheduled(fixedRate = 20000)
public void pollConfiguration() {
var request = new GetLatestConfigurationRequest();
request.setConfigurationToken(token);
var result = client.getLatestConfiguration(request);
this.token = result.getNextPollConfigurationToken();
var configuration = StandardCharsets.UTF_8.decode(result.getConfiguration()).toString();
log.info("content type: {}", result.getContentType());
log.info("configuration: {}", configuration);
}
}

CrossOriginFilter with Jersey and embedded Jetty

I know this question and that on however they were not answered and asked 4 years ago. Further, non of the answers worked for me.
I am unable to add a crossOriginFilter to my embedded jetty server.
My pom
<!-- Jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.2.11.v20150529</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.2.11.v20150529</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>9.2.11.v20150529</version>
</dependency>
My code - unfortunatly I do not get any Header field in the responses!
ServletContextHandler dynamicResourceContext = new ServletContextHandler();
dynamicResourceContext.setContextPath("/rest");
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
dynamicResourceContext.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));
ServletContextHandler staticResourceContext = new ServletContextHandler();
staticResourceContext.setContextPath("/resources");
DefaultServlet defaultServlet = new DefaultServlet();
ServletHolder holderPwd = new ServletHolder("default", defaultServlet);
holderPwd.setInitParameter("resourceBase", "./src/webapp/");
staticResourceContext.addServlet(holderPwd, "/*");
HandlerList handlers = new HandlerList();
handlers.addHandler(dynamicResourceContext);
handlers.addHandler(staticResourceContext);
server = new Server(port);
server.setHandler(handlers);
// set logging to console
StdErrLog logger = new StdErrLog();
logger.setDebugEnabled(webserverLogging);
Log.setLog(logger);
ServletHolder jerseyServlet = dynamicResourceContext
.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames", getMyClasses());
try {
server.start();
} catch (Exception e) {
e.printStackTrace();
} finally {
// server.destroy();
}
What do I wrong? I do not get any error message!
EDIT
Also the following tutorial is not working. Neither with Postman nor with chrome I see an additional response head entry.
The response looks like the following:
HTTP/1.1 200 OK
Date: Tue, 26 Mar 2019 19:41:36 GMT
Content-Length: 0
Server: Jetty(9.4.15.v20190215)
EDIT
I was able to create the header fields using a Resource Configuration but I am still unable to create them with the CrossOriginFilter.
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
Server jettyServer = new Server(9998);
jettyServer.setHandler(context);
ResourceConfig webapiResourceConfig = new ResourceConfig();
webapiResourceConfig.register(CorsFilter.class);
ServletHolder jerseyServlet = new ServletHolder(new ServletContainer(webapiResourceConfig));
context.addServlet(jerseyServlet, "/*");
//context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
jerseyServlet.setInitParameter( "jersey.config.server.provider.classnames",MyServerConfig.class.getCanonicalName());
You shouldn't see the headers in Postman, as Postman doesn't require CORS support. And in Chrome (or any browser), you should only see them if you are actually making a cross origin request. If the filter is implemented correctly, it should only spit out CORS response headers if there is an Origin request header. And that should only happen on cross origin requests made from a browser.
The reason your Jersey filter worked is probably because it is not implemented correctly, according to the CORS protocol; it is probably just a lazy version where headers are added for all requests. In this answer, I originally also implemented the same "lazy" CORS support, but if you look at the UPDATE, I explain how it should be implemented. If you want to learn more about CORS, that UPDATE is a good read.

How do I set the WS-Addressing MessageId header when using CXF with Apache Camel?

I'm invoking a web service that requires WS-Addressing SOAP headers. I'm using Apache Camel with CXF to invoke the web service. When I configure the CXF endpoint with the web service's WSDL, it's smart enough to automatically add WS-Adressing SOAP headers, but I need to set a custom MessageId.
Here is the message that is currently being sent:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<ws:international xmlns:ws="http://www.w3.org/2005/09/ws-i18n">
<ws:locale xmlns:ws="http://www.w3.org/2005/09/ws-i18n">en_CA</ws:locale>
</ws:international>
<fram:user wsa:IsReferenceParameter="true" xmlns:fram="http://wsbo.webservice.ephs.pdc.ibm.com/Framework/" xmlns:wsa="http://www.w3.org/2005/08/addressing">BESTSystem</fram:user>
<Action soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">http://webservice.ephs.pdc.ibm.com/Client/QueryHumanSubjects</Action>
<MessageID soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6</MessageID>
<To soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">https://panweb5.panorama.gov.bc.ca:8081/ClientWebServicesWeb/ClientProvider</To>
<ReplyTo soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
</soap:Header>
<soap:Body>
<ns2:queryHumanSubjectsRequest xmlns:ns2="http://wsbo.webservice.ephs.pdc.ibm.com/Client/" xmlns:ns3="http://wsbo.webservice.ephs.pdc.ibm.com/FamilyHealth/">
<!-- stuff -->
</ns2:queryHumanSubjectsRequest>
</soap:Body>
</soap:Envelope>
As you can see, the MessageId value is "urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6". I need to set a custom value.
I tried adding the MessageId header they way I add the other headers like "international" and "user", but some part of the framework overrides the value.
// Note this doesn't work! Something overrides the value. It works for other headers.
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
List<SoapHeader> headers = CastUtils.cast((List<?>) in.getHeader(Header.HEADER_LIST));
SOAPFactory sf = SOAPFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
QName MESSAGE_ID_HEADER = new QName("http://www.w3.org/2005/08/addressing", "MessageID", "wsa");
SOAPElement messageId = sf.createElement(MESSAGE_ID_HEADER);
messageId.setTextContent("customValue");
SoapHeader soapHeader = new SoapHeader(MESSAGE_ID_HEADER, messageId);
headers.add(soapHeader);
}
The CXF website has some documentation on how to set WS-Addressing headers, but I don't see how to apply it to Apache Camel. The Apache Camel CXF documentation doesn't specifically mention WS-Addressing either.
The documentation links you posted actually do have the information you need, although it's not immediately obvious how to apply it to Camel.
The CXF documentation says that:
The CXF org.apache.cxf.ws.addressing.impl.AddressingPropertiesImpl object can be used to control many aspects of WS-Addressing including the Reply-To:
AddressingProperties maps = new AddressingPropertiesImpl();
EndpointReferenceType ref = new EndpointReferenceType();
AttributedURIType add = new AttributedURIType();
add.setValue("http://localhost:9090/decoupled_endpoint");
ref.setAddress(add);
maps.setReplyTo(ref);
maps.setFaultTo(ref);
((BindingProvider)port).getRequestContext()
.put("javax.xml.ws.addressing.context", maps);
Note that it sets the addressing properties on the "RequestContext".
The Apache Camel documentation says that:
How to propagate a camel-cxf endpoint’s request and response context
CXF client API provides a way to invoke the operation with request and response context. If you are using a camel-cxf endpoint producer to invoke the outside web service, you can set the request context and get response context with the following code:
CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
public void process(final Exchange exchange) {
final List<String> params = new ArrayList<String>();
params.add(TEST_MESSAGE);
// Set the request context to the inMessage
Map<String, Object> requestContext = new HashMap<String, Object>();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
exchange.getIn().setBody(params);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
}
});
The above example has some stuff we don't need, but the important thing is that it shows us how to set the CXF Request Context.
Put them together and you get:
#Override
public void process(Exchange exchange) throws Exception {
AttributedURIType messageIDAttr = new AttributedURIType();
messageIDAttr.setValue("customValue");
AddressingProperties maps = new AddressingProperties();
maps.setMessageID(messageIDAttr);
Map<String, Object> requestContext = new HashMap<>();
requestContext.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT, requestContext);
}
// org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES = "javax.xml.ws.addressing.context"
// org.apache.cxf.endpoint.Client.REQUEST_CONTEXT = "RequestContext"
Warning: In my route, I invoke multiple different web services sequentially. I discovered that after setting the RequestContext as shown above, Camel started using the same RequestContext for all web services, which resulted in an error: "A header representing a Message Addressing Property is not valid and the message cannot be processed". This is because the incorrect "Action" header was used for all web service invocations after the first.
I traced this back to Apache Camel using a "RequestContext" Exchange property, separate from the header we set, which apparently takes priority over the header. If I remove this property prior to calling subsequent web services, CXF automatically fills in the correct Action header.
if your problem not solved, I suggest you to combine your cxf service with custom interceptor. it easy to work with your soap message. like this:
<bean id="TAXWSS4JOutInterceptorBean" name="TAXWSS4JOutInterceptorBean" class="com.javainuse.beans.SetDetailAnswerInterceptor " />
<cxf:cxfEndpoint id="CXFTest" address="/javainuse/learn"
endpointName="a:SOATestEndpoint" serviceName="a:SOATestEndpointService"
serviceClass="com.javainuse.SOATestEndpoint"
xmlns:a ="http://javainuse.com">
<cxf:binding>
<soap:soapBinding mtomEnabled="false" version="1.2" />
</cxf:binding>
<cxf:features>
<wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
</cxf:features>
<cxf:inInterceptors>
<ref bean="TAXWSS4JInInterceptorBean" />
</cxf:inInterceptors>
<cxf:inFaultInterceptors>
</cxf:inFaultInterceptors>
<cxf:outInterceptors>
<ref bean="TAXWSS4JOutInterceptorBean" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
</cxf:outFaultInterceptors>
</cxf:cxfEndpoint>
and in the interceptor you can set soap headers like this:
public class SetDetailAnswerInterceptor extends WSS4JOutInterceptor {
public SetDetailAnswerInterceptor() {
}
#Override
public void handleMessage(SoapMessage mc) {
AttributedURIType value = new AttributedURIType();
value.setValue("test");
((AddressingProperties) mc.get("javax.xml.ws.addressing.context.outbound")).setMessageID(value);
}
}

Jersey client exception: A message body writer was not found

I am using Jersey client to hit a PHP web service for image uploading functionality. I am getting the following exception:
Caused by: com.sun.jersey.api.client.ClientHandlerException:
A message body writer for Java type, class
com.sun.jersey.multipart.FormDataMultiPart, and MIME media type,
multipart/form-data, was not found
at com.sun.jersey.api.client.RequestWriter.writeRequestEntity(RequestWriter.java:288)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:204)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:147)
... 63 more
This is the code I am using:
WebResource webResource = Client.create().resource(HTTP_REST_URI);
JSONObject jSONObj = webResource.queryParams(queryParams)
.type(MediaType.MULTIPART_FORM_DATA)
.post(JSONObject.class, formDataMultiPart);
How can this exception be resolved?
Register the MultiPartWriter provider when creating the Client:
ClientConfig cc = new DefaultClientConfig();
Client client;
cc.getClasses().add(MultiPartWriter.class);
client = Client.create(cc);
If using Maven, these are the dependencies you need in your pom.xml:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>1.17.1</version>
</dependency>
Jersey (server or client) has providers that support the conversion of a Java type to a stream and vice versa.
Your code returns (or receives) a Java object and based on the type of the object and the content type you are using,
Jersey looks for an appropriate provider to do the marshalling (or unmarshalling).
The providers implement the MessageBodyReader or MessageBodyWriter interfaces and for every Java type and content type
combination your application uses you must have a provider that knows how to handle the combination.
The messages you are getting is telling you that Jersey can't find a provider that knows how to marshal a FormDataMultiPart object with a multipart/form-data mime type. You need to provide one, and if I'm not mistaken the default implementation is found in the jersey-multipart.jar and mimepull.jar files.
I faced the same issue. It got solved by changing maven dependency for jersey-multipart jar from 1.0.2 to 1.8 version (Used the same dependency in client side as well as provider side.
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>1.8</version>
</dependency>
Here's the complete piece of code I'm using
File file = new File("E:/Goodies/tmp/sparrow.jpg");
byte[] logo = FileUtils.readFileToByteArray(file);
MultiPart multiPart = new MultiPart().bodyPart(new BodyPart(logo, MediaType.APPLICATION_OCTET_STREAM_TYPE));
// POST the request
try{
ClientResponse response = service.type("multipart/mixed").post(ClientResponse.class, multiPart);
System.out.println("Response Status : " + response.getEntity(String.class));
}catch(Exception e){
e.printStackTrace();
}
and in the webservice:
#POST
#Consumes("multipart/mixed")
#Path("/upload")
public Response post(MultiPart multiPart) {
BodyPartEntity bpe = (BodyPartEntity) multiPart.getBodyParts().get(0)
.getEntity();
boolean isProcessed = false;
String message = null;
try {
InputStream source = bpe.getInputStream();
BufferedImage bi = ImageIO.read(source);
File file = new File("E:/Goodies/tmp" + "123.jpg");
// storing the image to file system.
if (file.isDirectory()) {
ImageIO.write(bi, "jpg", file);
} else {
file.mkdirs();
ImageIO.write(bi, "jpg", file);
}
isProcessed = true;
} catch (Exception e) {
message = e.getMessage();
}
there are few things you need to check
add mimepull.jar to your lib or with Maven
`<dependency>
<groupId>org.jvnet.mimepull</groupId>
<artifactId>mimepull</artifactId>
<version>1.9.5</version>
</dependency>`
And if you are working with file, make sure you send at the header the content-length content-type accept-encoding
I added this in web.xml. Problem solved.
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Here's my work around:
WebResource webResource =
jerseyClient.resource("www.api.com");
WebResource.Builder requestBuilder = webResource.getRequestBuilder();
requestBuilder.header("content-type", "application/json");
ClientResponse response = requestBuilder
.post(ClientResponse.class, mObjectMapper.writeValueAsString(new RequestObject(longUrl)));
String text = response.getEntity(String.class);
ResponseObject outcome = mObjectMapper.readValue(text, ResponseObject.class);
I have used Jackson ObjectMapper to serialize the request payload and likewise deserialized the outcome into a ResponseObject instance using ObjectMapper.

A message body writer for Java type, class myPackage.B, and MIME media type, application/octet-stream, was not found

I am new at RESTful webservices and was trying to update my #OneToMany relationship from a standalone client application, but I am not able to do that. I am using the Jersey implementation of JAX-RS that ships with Glassfish 3.1.1.
I have a class A that has a #OneToMany relationship with class B.
MyRestClient is my standalone client that is calling my RESTful webservice which has been deployed on Glassfish 3.1.1.
MyRestClient.java
public class MyRestClient {
public static void main(String[] args) {
Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/myapp/rest/a/update/123");
B b1 = new B("debris");
ClientResponse response = resource.put(ClientResponse.class, b1);
System.out.println(response.getEntity(A.class).getTitle() + " has " + response.getEntity(A.class).getBList().size() + " Bs.");
}
}
AResource is an EJB session bean which I am using as RESTful webservice.
AResource.java
#Stateless
#Path("/a")
public class AResource {
#EJB
private AManager aManager;
#PUT
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
#Path("/update/{id}")
public Response updateA(B b, #PathParam("id") int id) {
A a = aManager.findAById(id);
a.addB(b);
return Response.status(Status.OK).entity(a).build();
}
}
When I run the client I get the following error message:
com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class myPackage.B, and MIME media type, application/octet-stream, was not found.
Following are the domain objects in my standalone client application which is making a call to the AResource EJB session bean which I am using as the RESTful webservice.
A.java
#XmlRootElement
public class A implements Serializable{
private List<B> bList = new ArrayList<B>();
public List<B> getBList() {
return bList;
}
//remaining code
}
B.java
public class B implements Serializable {
private String text;
private A a;
#XmlTransient
public A getA() {
return a;
}
public void afterUnmarshal(Unmarshaller u, Object parent) {
this.a = (A) parent;
}
//remaining code
}
Could someone help me understand why this is happening and how I should solve this problem?
In your client code you are not specifying the content type of the data you are sending - so Jersey is not able to locate the right MessageBodyWritter to serialize the b1 object.
Modify the last line of your main method as follows:
ClientResponse response = resource.type(MediaType.APPLICATION_XML).put(ClientResponse.class, b1);
And add #XmlRootElement annotation to class B on both the server as well as the client sides.
Include this dependencies in your POM.xml and run Maven -> Update
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>0.99</version>
</dependency>
You need to specify the #Provider that #Produces(MediaType.APPLICATION_XML) from B.class
An add the package of your MessageBodyWriter<B.class> to your /WEB_INF/web.xml as:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
your.providers.package
</param-value>
</init-param>
You have to do two things to remove this error.
The #xmlElement mapping in the model
The client side:
response = resource.type(MediaType.APPLICATION_XML).put(ClientResponse.class, b1); //consume
or
response = resource.accept(MediaType.APPLICATION_XML).put(ClientResponse.class, b1); //produce
Adding reference to:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey1.version}</version>
</dependency>
As long as adding clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true); on client creation solved the issue for me:
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
Client client = Client.create(clientConfig);
This can also happen if you've recently upgraded Ant. I was using Ant 1.8.4 on a project, and upgraded Ant to 1.9.4, and started to get this error when building a fat jar using Ant.
The solution for me was to downgrade back to Ant 1.8.4 for the command line and Eclipse using the process detailed here
i was facing the same problem for a get method i was returning an "int" for the #get method Strangely when i change the return type to String the error was gone.Give it a try and if someone knows the logic behind it kindly share it
This solved my issue.
http://www.markhneedham.com/blog/2012/11/28/jersey-com-sun-jersey-api-client-clienthandlerexception-a-message-body-reader-for-java-class-and-mime-media-type-applicationjson-was-not-found/
Including following dependencies in your POM.xml and run Maven -> Update also fixed my issue.
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.19.1</version>
</dependency>
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>0.99</version>
</dependency>
This also happens if you're missing an empty public constructor for the Entity (could be for JSON, XML etc)..
Make sure that all these libs are in your class path:
compile(group: 'com.sun.jersey', name: 'jersey-core', version: '1.19.4')
compile(group: 'com.sun.jersey', name: 'jersey-server', version: '1.19.4')
compile(group: 'com.sun.jersey', name: 'jersey-servlet', version: '1.19.4')
compile(group: 'com.sun.jersey', name: 'jersey-json', version: '1.19.4')
compile(group: 'com.sun.jersey', name: 'jersey-client', version: '1.19.4')
compile(group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1')
compile(group: 'org.codehaus.jackson', name: 'jackson-core-asl', version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-mapper-asl', version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-core-asl', version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-jaxrs', version: '1.9.2')
compile(group: 'org.codehaus.jackson', name: 'jackson-xc', version: '1.9.2')
Add "Pojo Mapping" and "Jackson Provider" to the jersey client config:
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
clientConfig.getClasses().add(JacksonJsonProvider.class);
This solve to me!
ClientResponse response = null;
response = webResource
.type(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.get(ClientResponse.class);
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
MyClass myclass = response.getEntity(MyClass.class);
System.out.println(myclass);
}