Spring's PayloadValidatingInterceptor evaluates XML (uncorrect) invalid - validation

I'm developing in a Spring project using Sping-WS (2.1).
Using the PayloadValidatingInterceptor the messages we're sending via Spring Mock or soapUI are rated invalid.
We checked the XSD several times and we're sure, the data and the schema are correct.
I implemented two test cases inside our SpringJUnit4ClassRunner using the springframework.xml-validator engine and javax.xml.validation classes to validate the payload outside of the WS against the schema. There they're valid.
Does anybody knows a reason why the validation inside the WS behaves different to the manual validation?

I've had some problems with the PayloadValidatingInterceptor as well. I created a simple alternative, based on what I read here. Sometimes the interceptor will not work as expected - making it StringReader-based instead of String-based solved my problems. Perhaps this will help solving your issue as well!
To detail the solution given there, you can replace the interceptor with a proxy class like this:
import java.io.StringReader;
import java.io.StringWriter;
import org.springframework.ws.client.WebServiceTransformerException;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.ws.WebServiceMessage;
public class PayloadValidatingInterceptorWithSourceFix extends
PayloadValidatingInterceptor {
#Override
protected Source getValidationRequestSource(WebServiceMessage request) {
return transformSourceToStreamSourceWithStringReader(request
.getPayloadSource());
}
#Override
protected Source getValidationResponseSource(WebServiceMessage response) {
return transformSourceToStreamSourceWithStringReader(response
.getPayloadSource());
}
Source transformSourceToStreamSourceWithStringReader(
Source notValidatableSource) {
final Source source;
try {
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
"yes");
transformer.setOutputProperty(OutputKeys.INDENT, "no");
StringWriter writer = new StringWriter();
transformer.transform(notValidatableSource,
new StreamResult(writer));
String transformed = writer.toString();
StringReader reader = new StringReader(transformed);
source = new StreamSource(reader);
} catch (TransformerException transformerException) {
throw new WebServiceTransformerException(
"Could not convert the source to a StreamSource with a StringReader",
transformerException);
}
return source;
}
}
and use it like
<bean id="payloadValidatingInterceptorWithSourceFix"
class="path.to.your.PayloadValidatingInterceptorWithSourceFix">
<property name="schema"
value="file:WebContent/WEB-INF/schemas/account-balance-service.xsd" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="true" />
</bean>

Related

Unable to create file upload service - Receiving ModelValidationException (No injection source ) during startup [duplicate]

I am using Jersey based restful Service implementation strategy to build a service which will be used to upload files.
My service class name is : UploadFileService.java (See Code below)
package com.jerser.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;
#Path("/fileUpload")
public class UploadFileService {
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
#FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition fileDetail) {
String uploadedFileLocation = "d://uploaded/" + fileDetail.getFileName();
// save it
writeToFile(uploadedInputStream, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
return Response.status(200).entity(output).build();
}
// save uploaded file to new location
private void writeToFile(InputStream uploadedInputStream,
String uploadedFileLocation) {
try {
OutputStream out = new FileOutputStream(new File(
uploadedFileLocation));
int read = 0;
byte[] bytes = new byte[1024];
out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
These are the JAR files I have inside my lib:
aopalliance-repackaged-2.4.0-b10.jar
asm-debug-all-5.0.2.jar
hk2-api-2.4.0-b10.jar
hk2-locator-2.4.0-b10.jar
hk2-utils-2.4.0-b10.jar
javassist-3.18.1-GA.jar
javax.annotation-api-1.2.jar
javax.inject-2.4.0-b10.jar
javax.servlet-api-3.0.1.jar
javax.ws.rs-api-2.0.1.jar
jaxb-api-2.2.7.jar
jersey-client.jar
jersey-common.jar
jersey-container-servlet-core.jar
jersey-container-servlet.jar
jersey-core-1.11.jar
jersey-guava-2.17.jar
jersey-media-jaxb.jar
jersey-multipart-1.18.jar
jersey-server.jar
org.osgi.core-4.2.0.jar
osgi-resource-locator-1.0.1.jar
persistence-api-1.0.jar
validation-api-1.1.0.Final.jar
I am getting the following error when I am trying to up my tomcat server :
org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
[[FATAL] No injection source found for a parameter of type public javax.ws.rs.core.Response com.jerser.service.UploadFileService.uploadFile(java.io.InputStream,com.sun.jersey.core.header.FormDataContentDisposition) at index 0.; source='ResourceMethod{httpMethod=POST, consumedTypes=[multipart/form-data], producedTypes=[], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class com.jerser.service.UploadFileService, handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor#d3e2d4]}, definitionMethod=public javax.ws.rs.core.Response com.jerser.service.UploadFileService.uploadFile(java.io.InputStream,com.sun.jersey.core.header.FormDataContentDisposition), parameters=[Parameter [type=class java.io.InputStream, source=file, defaultValue=null], Parameter [type=class com.sun.jersey.core.header.FormDataContentDisposition, source=file, defaultValue=null]], responseType=class javax.ws.rs.core.Response}, nameBindings=[]}']
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:528)
at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:166)
at org.glassfish.jersey.server.ApplicationHandler$3.run(ApplicationHandler.java:327)
at org.glassfish.jersey.internal.Errors$2.call(Errors.java:289)
at org.glassfish.jersey.internal.Errors$2.call(Errors.java:286)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:286)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:324)
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:338)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:171)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:363)
at javax.servlet.GenericServlet.init(GenericServlet.java:160)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1176)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1102)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1009)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4885)
at org.apache.catalina.core.StandardContext$3.call(StandardContext.java:5212)
at org.apache.catalina.core.StandardContext$3.call(StandardContext.java:5207)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Over the internet I found there are plenty of example which shows How to upload MULTIPART file using RESTFul API. But with same solution. I am not able to run those code as well.
I think I am doing something wrong with the JAR files. Could anyone please help me on this?
Get rid of jersey-multipart-1.18.jar. That is for Jersey 1.x. Add these two
jersey-media-multipart-2.17
mimepull-1.9.3
For Maven you would use the following dependency (you don't need to explicitly add the mimepull dependency, as this one will pull it in).
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.17</version> <!-- Make sure the Jersey version matches
the one you are currently using -->
</dependency>
Then you need to register the MultiPartFeature. If you are using a ResourceConfig for configuration, you can simply do
register(MultiPartFeature.class);
If you are using web.xml, then you can add the class as an <init-param> to the Jersey servlet
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
Note that if you have multiple providers that you want to register, then you can delimit each provider class with a comma, semicolon, or space/newline. You cannot use this same param-name twice. See Suarabh's answer
UPDATE
Also, once you get rid of jersey-multipart-1.18.jar you will have compile errors for the missing imported classes. For the most part, the class names are still the same, just the packages have changed, i.e.
org.glassfish.jersey.media.multipart.FormDataParam
org.glassfish.jersey.media.multipart.FormDataContentDisposition
For Dropwizard
If you're using Dropwizard, instead of adding the jersey-media-multipart, they document for your to add dropwizard-forms instead. And instead of registering the MultiPartFeature, you should register the MultiPartBundle
#Override
public void initialize(Bootstrap<ExampleConfiguration> bootstrap) {
bootstrap.addBundle(new MultiPartBundle());
}
Really doesn't make much difference though as all the Dropwizard bundle does is register the MultiPartFeature with the ResourceConfig.
Aside
If you are here for a different ModelValidationException, here are some links for information on other causes of the exception.
1
2
3
Yet another possible cause for this very generic error is that Jersey only searches for factories associated with the last annotation when multiple ones are declared on a param. (See bug report)
Until this is fixed, if you are using any other annotations besides #FormDataParam, it has to come last.
This works:
#NotEmpty #FormDataParam("myParam") String myParam
This does not:
#FormDataParam("myParam") #NotEmpty String myParam
I too got the same exception.I did the following changes in web.xml
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.filter.LoggingFilter;org.glassfish.jersey.moxy.json.MoxyFeature;org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
and changed jersey 2.7 to 2.9 .I do not know what change of this 2 has solved the issue.
Register MultiPartFeature.
In web.xml add to the Jersey servlet:
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
Below code worked for me:
Class ->>> add it
Class Property --->> add it
Public Class userREST () {
#POST
#Path("upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response uploadImageFile(#FormDataParam("uploadFile") InputStream fileInputStream,
#FormDataParam("uploadFile") FormDataContentDisposition fileFormDataContentDisposition,
#FormDataParam("FIR_REG_NUM") String FIR_REG_NUM, #FormDataParam("LOGIN_ID") String LOGIN_ID) {
final_json_result = WriteFileInFolder.fileAnalysis(fileInputStream, fileFormDataContentDisposition, FIR_REG_NUM,
LOGIN_ID);
return Response.ok(final_json_result).build();
}// uploadImageFile
Public Class FileJAXRSConfig () {
package ####.jaxrs.jwt;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import ####.helper.Common###;
import ####.jaxrs.jwt.filters.JWTRequestFilter;
import ####.jaxrs.jwt.filters.JWTResponseFilter;
import ####.service.FileServicesREST;
#ApplicationPath("fileservice")
public class FileJAXRSConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Common###.logging("#ApplicationPath#FileServicesREST...");
Set<Class<?>> clazzes = new HashSet<Class<?>>();
clazzes.add(JWTRequestFilter.class);
clazzes.add(FileServicesREST.class);
clazzes.add(JWTResponseFilter.class);
return clazzes;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("jersey.config.server.provider.packages", "####.service");
properties.put("jersey.config.server.provider.classnames", "org.glassfish.jersey.media.multipart.MultiPartFeature");
return properties;
}
}
Don't need to add following in web.xml
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>mha.###.service</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
If someone is using #FormDataParam with #ApiOperation swagger annotation, it won't work(as per swagger latest version at this time) as mentioned here:
https://github.com/swagger-api/swagger-ui/issues/169
I had this same problem with Scala and this helped me solve it. Just want to add some Scala specific details to help anyone using Dropwizard with Scala. Here is an example of how to "register" the MultiPartFeature in a Scala and Dropwizard project.
package org.research.s3.service
import io.dropwizard.Application
import io.dropwizard.setup.Environment
import org.research.s3.service.resource._
import org.research.service.s3.resource.UploadResource
import org.glassfish.jersey.media.multipart.{FormDataParam,MultiPartFeature}
class CmdaaApp() extends Application[CmdaaAppConfig] {
override def run(t: CmdaaAppConfig, env: Environment): Unit = {
env.jersey().register(new RootResource)
//Need this to make the file upload code work in
env.jersey().register(new MultiPartFeature)
env.jersey().register(new UploadResource(curBucket))
}
}
object CmdaaApp {
def main(args: Array[String]): Unit = new CmdaaApp().run(args: _*)
}
and here is the code for the UploadResource that does the upload:
package org.research.service.s3.resource
import java.io.{FileInputStream, InputStream}
import com.google.gson.{Gson, GsonBuilder}
import javax.ws.rs.core.MediaType.APPLICATION_JSON
import javax.ws.rs._
import javax.ws.rs.core.Response
import javax.ws.rs.core.MediaType
import org.research.util.OptionSerializer
import org.research.s3.service.resource.s3Bucket
import org.glassfish.jersey.media.multipart.{FormDataParam,MultiPartFeature}
#Path("/file")
class UploadResource(currentBucket: s3Bucket) {
val gsonb = new GsonBuilder()
gsonb.registerTypeAdapter(classOf[Option[Any]], new OptionSerializer)
val gson = gsonb.create
#POST
#Path("upload")
#Produces(Array(APPLICATION_JSON))
#Consumes(Array(MediaType.MULTIPART_FORM_DATA))
// def uploadFile(): Response = {
def uploadFile(#FormDataParam("file") uploadedInputStream: InputStream): Response = {
/* Need code here to get a uuid for the file name
Then return the uuid if we have success and of course 200
*/
Response.ok.entity(currentBucket.upload("testName",uploadedInputStream,false)).build()
//Response.ok().build()
}
}
This code refers to an s3 bucket but you don't need that. You can just replace that call with code do download your incoming file data to a regular file.
scala
I had the same problem when I tried to upload the file.
I spent a lot of time until I found a solution to the problem.
1.If you changed version of your JARs files you may have a version conflicts!
Clean up your artifacts/libs and rebuild project.
2.You need to register your UploadFileService class too:
register(MultiPartFeature.class);
register(UploadFileService.class);
Hope it will help someone and save your time.
in case you are getting this error while writing Dropwizard tests for the upload Resource this is the solution:
add dependency on dropwizard-forms
2.add this in the application file:
#Override
public void initialize(Bootstrap<ExampleConfiguration> bootstrap) {
bootstrap.addBundle(new MultiPartBundle());
}
in the test file add:
ResourceExtension.builder()
.addResource(new FileResource())
.addProvider(new MultiPartFeature())
.build();
I had a very similar problem and the answer that helped me was this https://stackoverflow.com/a/30407999/6801721
I was trying to use a user defined object as a query parameter and from the answer that's usually not allowed unless it meets some conditions.
In case someone comes across this in the future and is running into the same problem I was running into. Make sure that the annotations you are importing are from the correct packages. In my case I was importing javax.websocket.server.PathParam instead of javax.ws.rs.PathParam.

Running cucumber-groovy features against a spring boot api

I've been attempting to get cucumber-groovy working with spring-boot, but it's not been going well. I get the error org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/applicants": Connection refused; nested exception is java.net.ConnectException: Connection refused which seems to indicate that it's hitting the endpoint, but that the service isn't running.
I've read that I need to have a cucumber.xml file, but my project is not using any xml config, it's all annotations, so instead I've got this:
package support
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.base.package")
public class CucumberConfiguration {}
I've added it to the World, but this seems to be the wrong way of doing things (i.e. I don't know how to add an annotation on groovy step defs).
package support
import com.thing.app.Application
import org.junit.runner.RunWith
import org.springframework.boot.test.SpringApplicationContextLoader
import org.springframework.boot.test.WebIntegrationTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.test.context.web.WebAppConfiguration
import static cucumber.api.groovy.Hooks.*
//#RunWith(SpringJUnit4ClassRunner)
//#ContextConfiguration(classes = Application, loader = SpringApplicationContextLoader)
//#WebAppConfiguration
//#WebIntegrationTest
#ContextConfiguration(classes = CucumberConfiguration)
public class AbstractTest {
}
World() {
new AbstractTest()
}
Before() {}
After() {}
I left in my other annotations to kind of show what I've done so far. None of it has worked.
I've also tried setting up an AbstractDefs class as seen here https://github.com/jakehschwartz/spring-boot-cucumber-example/tree/master/src/test/java/demo, but that also hasn't worked, mostly because I'm not using the cucumber-java style of things, but instead the cucumber-groovy style, which doesn't use step definition classes.
Edit: Just discovered I was doing things wrong by having an env.groovy, I'm used to the ruby cucumber, so I'm having trouble finding all the little problems. Still am having the same issue though, I don't know how to execute in a Spring context.
You can instantiate Spring test context with io.cucumber.spring.SpringFactory and register adapter in World to allow groovy script has access to Spring beans:
env.groovy:
#ContextConfiguration(classes = TestConfiguration, loader = SpringBootContextLoader)
class CucumberContextConfiguration {
}
//adapter bypassing World properties to Spring context
class SpringFactoryWorldAdapter {
private final SpringFactory factory;
SpringFactoryWorldAdapter(SpringFactory factory) {
this.factory = factory;
}
#Override
Object getProperty(String s) {
return factory.testContextManager.getContext().getBean(s);
}
}
def factory; //Keep state to prevent repeated context initialization
World { args ->
if (factory == null) {
factory = new SpringFactory()
factory.addClass(CucumberContextConfiguration)
factory.start()
}
new SpringFactoryWorldAdapter(factory)
}

How to make empty configuration if file not found?

Using Apache Commons Configurations 1.9, how to avoid ConfigurationException upon loading a configuration file if the provided file cannot be found?
The Spring app context resembles:
<bean name="foo.config" class="org.apache.commons.configuration.PropertiesConfiguration" init-method="load">
<property name="fileName" value="foo.properties" />
</bean>
However my config file is optional, so I want to make sure the application starts correctly even the file doesn't exist.
How can I achieve this with Commons Configurations? A FactoryBean works, but is there another way?
if (!file.exists()) return new PropertiesConfiguration();
Or using try/catch syntax using an XML configuration:
import org.apache.commons.configuration2.XMLConfiguration;
import org.apache.commons.configuration2.builder.fluent.Configurations;
public class Workspace {
private final XMLConfiguration mConfig;
public Workspace() {
final var configs = new Configurations();
XMLConfiguration config;
try {
config = configs.xml( "filename.xml" );
} catch( final Exception e ) {
config = new XMLConfiguration();
}
mConfig = config;
}
Using a regular properties configuration will work the same way.

How to register my custom MessageBodyReader in my CLIENT?

Maybe somebody can help me find out how to solve this.
I am using jersey-apache-client 1.17
I tried to use Jersey client to build a standalone application (no Servlet container or whatever, just the Java classes) which communicates with a RESTFUL API, and everything worked fine until I tried to handle the mediatype "text/csv; charset=utf-8" which is a CSV stream sent by the server.
The thing is that I can read this stream with the following code:
InputStreamReader reader = new InputStreamReader(itemExportBuilder
.get(ClientResponse.class).getEntityInputStream());
Csv csv = new Csv();
Input input = csv.createInput(reader);
try {
String[] readLine;
while ((readLine = input.readLine()) != null) {
LOG.debug("Reading CSV: {}", readLine);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
But I'd like to encapsulate it and put it into a MessageBodyReader. But after writing this code, I just can't make the client use the following class:
package client.response;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Provider
public class ItemExportMessageBodyReader implements MessageBodyReader<ItemExportResponse> {
private static final Logger LOG = LoggerFactory.getLogger(ItemExportMessageBodyReader.class);
private static final Integer SKU = 0;
private static final Integer BASE_SKU = 1;
public boolean isReadable(Class<?> paramClass, Type type, Annotation[] annotations,
MediaType mediaType) {
LOG.info("Cheking if content is readable or not");
return paramClass == ItemExportResponse.class && !mediaType.isWildcardType()
&& !mediaType.isWildcardSubtype()
&& mediaType.isCompatible(MediaType.valueOf("text/csv; charset=utf-8"));
}
public ItemExportResponse readFrom(Class<ItemExportResponse> paramClass, Type paramType,
Annotation[] paramArrayOfAnnotation, MediaType paramMediaType,
MultivaluedMap<String, String> paramMultivaluedMap, InputStream entityStream)
throws IOException, WebApplicationException {
InputStreamReader reader = new InputStreamReader(entityStream);
Csv csv = new Csv();
Input input = csv.createInput(reader);
List<Item> items = new ArrayList<Item>();
try {
String[] readLine;
while ((readLine = input.readLine()) != null) {
LOG.trace("Reading CSV: {}", readLine);
Item item = new Item();
item.setBaseSku(readLine[BASE_SKU]);
items.add(item);
}
} catch (IOException e) {
LOG.warn("Item export HTTP response handling failed", e);
} finally {
try {
input.close();
} catch (IOException e) {
LOG.warn("Could not close the HTTP response stream", e);
}
}
ItemExportResponse response = new ItemExportResponse();
response.setItems(items);
return response;
}
}
The following documentation says that the preferred way of making this work in a JAX-RS client to register the message body reader with the code below:
Using Entity Providers with JAX-RS Client API
Client client = ClientBuilder.newBuilder().register(MyBeanMessageBodyReader.class).build();
Response response = client.target("http://example/comm/resource").request(MediaType.APPLICATION_XML).get();
System.out.println(response.getStatus());
MyBean myBean = response.readEntity(MyBean.class);
System.out.println(myBean);
Now the thing is that I can't use the ClientBuilder. I have to extend from a specific class which constructs the client another way, and I have no access to change the construction.
So when I receive the response from the server, the client fails with the following Exception:
com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java class client.response.ItemExportResponse, and Java type class client.response.ItemExportResponse, and MIME media type text/csv; charset=utf-8 was not found
Any other way to register my MessageBodyReader?
OK. If anybody would bump into my question I solved this mystery by upgrading from Jersey 1.17 to version 2.9. The documentation I linked above also covers this version not the old one, this is where the confusion stems from.
Jersey introduced backward INCOMPATIBLE changes starting from version 2, so I have no clue how to configure it in version 1.17.
In version 2 the proposed solution worked fine.

Spring Integration with Twitter Issue with Authentication

I am trying to post a message to my twitter account using Spring Integration with Twitter with a standalone program on my windows XP machine. But I am getting the following error -
WARNING: POST request for "https://api.twitter.com/1/statuses/update.json" resulted in 401 (Unauthorized); invoking error handler
Exception in thread "main" org.springframework.integration.MessageHandlingException: error occurred in message handler [org.springframework.integration.twitter.outbound.StatusUpdatingMessageHandler#0]
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:79)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:115)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:102)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:157)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:128)
at com.skilledmonster.spring.integration.twitter.TwitterOutbound.main(TwitterOutbound.java:20)
Caused by: org.springframework.social.RevokedAuthorizationException: The authorization has been revoked. Reason: Unknown
at org.springframework.social.twitter.api.impl.TwitterErrorHandler.handleClientErrors(TwitterErrorHandler.java:96)
at org.springframework.social.twitter.api.impl.TwitterErrorHandler.handleError(TwitterErrorHandler.java:58)
at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:486)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:443)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:415)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:294)
at org.springframework.social.twitter.api.impl.TimelineTemplate.updateStatus(TimelineTemplate.java:236)
at org.springframework.social.twitter.api.impl.TimelineTemplate.updateStatus(TimelineTemplate.java:224)
at org.springframework.integration.twitter.outbound.StatusUpdatingMessageHandler.handleMessageInternal(StatusUpdatingMessageHandler.java:57)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)
... 6 more
Here is my code -
twitter-outbound.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:twitter="http://www.springframework.org/schema/integration/twitter"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/integration/twitter
http://www.springframework.org/schema/integration/twitter/spring-integration-twitter-2.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan
base-package="com.apress.prospringintegration.social.twitter" />
<context:property-placeholder location="/twitter.properties" />
<int:channel id="twitterOutbound" />
<twitter:outbound-channel-adapter twitter-template="twitterTemplate" channel="twitterOutbound" />
<bean id="twitterTemplate"
class="org.springframework.social.twitter.api.impl.TwitterTemplate">
<constructor-arg value="${twitter.consumer-key}" />
<constructor-arg value="${twitter.consumer-secret}" />
<constructor-arg value="${twitter.access-token}" />
<constructor-arg value="${twitter.access-token-secret}" />
</bean>
</beans>
TwitterConfigurationTemplate.java
package com.skilledmonster.spring.integration.twitter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.social.twitter.api.impl.TwitterTemplate;
#Configuration
public class TwitterConfigurationTemplate {
#Value("${consumer-key}")
private String consumerKey;
#Value("${consumer-secret}")
private String consumerSecret;
#Value("${access-token}")
private String accessToken;
#Value("${access-token-secret}")
private String accessTokenSecret;
#Bean
public TwitterTemplate twitterTemplate() {
TwitterTemplate twitterOperations =
new TwitterTemplate(
consumerKey, consumerSecret, accessToken, accessTokenSecret);
return twitterOperations;
}
}
TwitterOutbound.java
package com.skilledmonster.spring.integration.twitter;
import java.util.Calendar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.message.GenericMessage;
public class TwitterOutbound {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/twitter-outbound.xml", TwitterOutbound.class);
MessageChannel input = context.getBean("twitterOutbound", MessageChannel.class);
Message<String> message = new GenericMessage<String>("Testing new Twitter samples for #springintegration"+Calendar.getInstance().getTimeInMillis());
input.send(message);
}
}
twitter.properties
twitter.consumer-key=onj5cG1P9pe7n9qA8UI4EA
twitter.consumer-secret=2l7hqMafKYaTkVBW3YfuBfGdzCtmICOJwjOOCEeQ
twitter.access-token=792995125-dXmN1Pbw7sE4WttvAbX7ssxEn4lHaVd6uOX3IMxk
twitter.access-token-secret=a1EuvvONcphdqXpfJVjCdaIBDlMZSUL5pgimWuEtg
FYI - I did test my twitter token and key with twitter4j and it seems to be posting the message to twitter successfully.
Here is my code with twitter4j -
OAuthSetup.java
package com.skilledmonster.spring.integration.twitter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Calendar;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
public class OAuthSetup {
/**
* #param args
*/
public static void main(String args[]) throws Exception {
// The factory instance is re-useable and thread safe.
Twitter twitter = new TwitterFactory().getInstance();
//insert the appropriate consumer key and consumer secret here
twitter.setOAuthConsumer("onj5cG1P9pe7n9qA8UI4EA",
"2l7hqMafKYaTkVBW3YfuBfGdzCtmICOJwjOOCEeQ");
RequestToken requestToken = twitter.getOAuthRequestToken();
AccessToken accessToken = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (null == accessToken) {
System.out.println("Open the following URL and grant access to your account:");
System.out.println(requestToken.getAuthorizationURL());
System.out.print("Enter the PIN(if aviailable) or just hit enter.[PIN]:");
String pin = br.readLine();
try{
if(pin.length() > 0){
accessToken = twitter.getOAuthAccessToken(requestToken, pin);
}else{
accessToken = twitter.getOAuthAccessToken();
}
} catch (TwitterException te) {
if(401 == te.getStatusCode()){
System.out.println("Unable to get the access token.");
}else{
te.printStackTrace();
}
}
}
//persist to the accessToken for future reference.
System.out.println(twitter.verifyCredentials().getId());
System.out.println("token : " + accessToken.getToken());
System.out.println("tokenSecret : " + accessToken.getTokenSecret());
//storeAccessToken(twitter.verifyCredentials().getId() , accessToken);
Status status = twitter.updateStatus("Testing new Twitter samples for springintegration # "+Calendar.getInstance().getTimeInMillis());
System.out.println("Successfully updated the status to [" + status.getText() + "].");
System.exit(0);
}
}
Can anyone suggest me what is going wrong here?
The token you are using to access Twitter has been revoked, or possibly not valid for the consumer id / application id you are using.
org.springframework.social.RevokedAuthorizationException: The authorization has been revoked
You will need to perform a new authorisation to obtain a token and secret for use with your application, or alternatively, on the twitter application configuration page (https://dev.twitter.com/apps/) you can generate a token specifically for your personal usage. This is useful for testing an app conveniently without having to perform OAuth.
I figured the issue was with the empty spaces in the key values in the properties file :-)
Here is a full example on my blog.

Resources