Spring Integration with Twitter Issue with Authentication - spring

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.

Related

Payload format Apache CXF SOAP messages

I have a SOAP client created in spring boot and I'm logging all SOAP messages, but the new version of LoggingFeature (org.apache.cxf.ext.logging.LoggingFeature) shows payload on one line even though I'm using prettyLogging and deprecated version of this class (org.apache.cxf.feature.LoggingFeature) formats payload when used prettyLogging.
When used deprecated version of class - format I want (random example):
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Envelope xmlns="URL" xmlns:ns2="URL">
<Service ID="SERVICE"/>
<Inquirer ID="ID" CorrelationID="ID" Version="1.0" Timestamp="Timestamp"/>
<Data Content="xml">
<ns2:Request>
<ns2:FILE>ID</ns2:FILE>
<ns2:ACTION>ACTION</ns2:ACTION>
</ns2:Request>
</Data>
<File>
<FileDescription ID="ID"/>
</File>
</Envelope>
</soap:Body>
</soap:Envelope>
When used new version:
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Envelope xmlns="URL" xmlns:ns2="URL"><Service ID="SERVICE"/><Inquirer ID="ID" CorrelationID="ID" Version="1.0" Timestamp="Timestamp"/><Data Content="xml"><ns2:Request><ns2:FILE>ID</ns2:FILE> <ns2:ACTION>ACTION</ns2:ACTION></ns2:Request></Data><File><FileDescription ID="ID"/></File></Envelope></soap:Body></soap:Envelope>
My configuration class:
import javax.annotation.PostConstruct;
import org.apache.cxf.ext.logging.AbstractLoggingInterceptor;
import org.apache.cxf.ext.logging.LoggingFeature;
import org.apache.cxf.ext.logging.LoggingInInterceptor;
import org.apache.cxf.ext.logging.LoggingOutInterceptor;
import org.apache.cxf.bus.spring.SpringBus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class CxfConfig {
#Autowired
private SpringBus springBus;
#PostConstruct
public void activateLoggingFeature() {
springBus.getInInterceptors().add(logInInterceptor());
springBus.getInFaultInterceptors().add(logInInterceptor());
springBus.getOutInterceptors().add(logOutInterceptor());
springBus.getOutFaultInterceptors().add(logOutInterceptor());
}
#Bean
public LoggingFeature loggingFeature() {
LoggingFeature logFeature = new LoggingFeature();
logFeature.setPrettyLogging(true);
logFeature.initialize(springBus);
springBus.getFeatures().add(logFeature);
return logFeature;
}
public AbstractLoggingInterceptor logInInterceptor() {
LoggingInInterceptor logInInterceptor = new LoggingInInterceptor();
logInInterceptor.setLimit(-1);
logInInterceptor.setPrettyLogging(true);
logInInterceptor.setLogBinary(true);
logInInterceptor.setLogMultipart(true);
return logInInterceptor;
}
public AbstractLoggingInterceptor logOutInterceptor() {
LoggingOutInterceptor logOutInterceptor = new LoggingOutInterceptor();
logOutInterceptor.setPrettyLogging(true);
logOutInterceptor.setLimit(-1);
logOutInterceptor.setLogBinary(true);
logOutInterceptor.setLogMultipart(true);
return logOutInterceptor;
}
}

how can I get the response message from Axis2 in spring integration

I need to integrate my webservice (Axis2) in spring integration: I have spring-axis2-message.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xmlns:ws="http://www.springframework.org/schema/integration/ws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd
http://www.springframework.org/schema/integration/ws
http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
<chain input-channel="messageChannelIN" output-channel="messageChannelOUT">
<ws:header-enricher >
<ws:soap-action value="getMessageService"/>
</ws:header-enricher>
<ws:outbound-gateway uri="http://localhost:8080/axis2-webservice/services/wservices?wsdl" reply-channel="messageChannelOUT"/>
</chain>
<!-- The response from the service is logged to the console. -->
<stream:stdout-channel-adapter id="messageChannelOUT" append-newline="true" />
</beans:beans>
And a TestAxis2.java
package org.neos.spring.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.core.DestinationResolver;
public class TestAxis2 {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"/META-INF/spring/integration/spring-axis2-message.xml");
DestinationResolver<MessageChannel> channelResolver = new BeanFactoryChannelResolver(context);
String requestXml =
"<getMessageService xmlns=\"http://service.ws.axis2.neos.org\">" +
"<name>HUGO</name>"
+ "</getMessageService>";
// Create the Message object
Message<String> message = MessageBuilder.withPayload(requestXml).build();
// Send the Message to the handler's input channel
MessageChannel channel = channelResolver.resolveDestination("messageChannelIN");
channel.send(message);
}
}
The program run very well and I can see in the console the next response:
<?xml version="1.0" encoding="UTF-8"?><ns:getMessageServiceResponse xmlns:ns="http://service.ws.axis2.neos.org"><ns:return>HELLO HUGO!, WELCOME TO WEBSERVICE AXIS1 hola</ns:return></ns:getMessageServiceResponse>
My question is how Can I manipulate/How can get the response in Java program because I need the response. I tried to do a lot of things but unfortunately did not work anything I only can see the response in the console but I need to manipulate the response.
I do not how can I access this configuration or if I need to configurate other things.
access<stream:stdout-channel-adapter id="messageChannelOUT" append-newline="true" />
Can Anyone help me please?
Use a Messaging Gateway.
public interface Gateway
String sendAndReceive(String out);
}
<int:gateway service-interface="foo.Gateway"
default-request-channel="messageChannelIN" />
Remove the output-channel from the chain
The reply will be returned to the caller via the gateway
Gatweway gw = context.getBean(Gateway.class);
...
String reply = gw.sendAndReceive(requestXml);
This has the added bonus of not exposing your application to the messaging infrastructure.
It's working my program right now!!. Thanks for your help Gary Russell!!! your comments were very useful.
The final code was:
xml configuration
........
<chain input-channel="messageChannelIN">
<ws:header-enricher>
<ws:soap-action value="getMessageService"/>
</ws:header-enricher>
<ws:outbound-gateway uri="http://localhost:8080/axis2-webservice/services/wservices?wsdl" />
</chain>
<gateway id="messageChannelOUT" service-interface="org.neos.spring.ws.service.GatewayAxis" default-request-channel="messageChannelIN"/>
Java Code:
public interface GatewayAxis {
#Gateway
String sendAndReceive(String out);}
TestAxis2
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"/META-INF/spring/integration/spring-axis2-message.xml");
GatewayAxis gateway = context.getBean(GatewayAxis.class);
String requestXml =
"<getMessageService xmlns=\"http://service.ws.axis2.neos.org\">" +
"<name>HUGO</name>"
+ "</getMessageService>";
String reply = gateway.sendAndReceive(requestXml);
System.out.println(reply);
}

How to parse Spring Integration HTTP gateway response into a Bean?

I've been trying to get a simple integration workflow for practice purposes. The thing is I just started working with Spring and I'm having a hard time understanding Integration and how it works.
Right now I've got a really simple backend app with Spring MVC that returns
[
{"id":1,"name":"Series test","synopsis":"Testing reading items from JSON.","imageUrl":"http://some.where/images/some_image.png"},
{"id":2,"name":"Arrow","synopsis":"Some guy in a hood shooting arrows to some guys with superpowers.","imageUrl":"http://some.where/images/some_image.png"},
{"id":3,"name":"Primeval","synopsis":"Some guys with guns killing dinosaurs and lots of infidelity.","imageUrl":"http://some.where/images/some_image.png"},
{"id":4,"name":"Dr. Who","synopsis":"It's bigger on the inside.","imageUrl":"http://some.where/images/some_image.png"},
{"id":5,"name":"Fringe","synopsis":"Weird things happen.","imageUrl":"http://some.where/images/some_image.png"},
{"id":6,"name":"Monster Hunter Freedom Unite","synopsis":"Wait. This is a game.","imageUrl":"http://some.where/images/some_image.png"}
]
to http://localhost:9000/api/series/findAll and a runnable Spring project that, with Integration, attempts to recover that data and convert it to a Series (bean with the same properties as the JSON) array.
If I don't add a reply-channel to the outbound-gateway everything works just fine. But when I send it to another channel to parse it into a Series object I start getting "Dispatcher has no subscribers" on the new channel. It makes sense but it leaves me not knowing how to proceed now.
My project files, apart from Series, look like this right now:
Startup.java
package com.txus.integration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
import java.util.concurrent.Future;
public class Startup {
#Autowired
RequestGateway requestGateway;
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration-components.xml");
RequestGateway requestGateway = context.getBean(RequestGateway.class);
Future<String> promise = requestGateway.getSeries("");
while (!promise.isDone()) {
Thread.sleep(1000);
}
String response = promise.get();
printReadable(response);
context.close();
System.exit(0);
}
public static void printReadable(String string) {
String separator = "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =";
System.out.println("\n" + separator + "\n" + string + "\n" + separator + "\n");
}
public static void printReadable(List<String> strings) {
for (String string : strings) printReadable(string);
}
}
RequestGateway
package com.txus.integration;
import com.txus.entities.Series;
import org.springframework.integration.annotation.Gateway;
import java.util.concurrent.Future;
public interface RequestGateway {
#Gateway(requestChannel="responseChannel")
Future<String> getSeries(String jsonString);
#Gateway(requestChannel="responseChannel")
Future<String> getSeries(Series series);
}
integration-components.xml
<beans:beans
xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:http="http://www.springframework.org/schema/integration/http"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.txus"/>
<!-- START: Spring Integration -->
<!-- Integration: Channels -->
<channel id="requestChannel"/>
<channel id="responseChannel"/>
<channel id="failedChannel"/>
<!-- Integration: Loggers -->
<logging-channel-adapter
id="payloadLogger" level="DEBUG" expression="'### Message [' + headers.id + '] payload: ' + payload"/>
<logging-channel-adapter
id="headersLogger" level="DEBUG" expression="'### Message [' + headers.id + '] headers: ' + headers"/>
<!-- Integration: Flow -->
<gateway
service-interface="com.txus.integration.RequestGateway"
default-request-timeout="5000" async-executor="executor">
<method name="getSeries" request-channel="inputChannel"/>
</gateway>
<task:executor id="executor" pool-size="100"/>
<payload-type-router input-channel="inputChannel" default-output-channel="failedChannel">
<mapping type="java.lang.String" channel="requestChannel"/>
<mapping type="com.txus.entities.Series" channel="objectToJSONChannel"/>
</payload-type-router>
<object-to-json-transformer
id="objectToJsonTransformer" input-channel="objectToJSONChannel" output-channel="requestChannel"/>
<http:outbound-gateway
http-method="GET"
expected-response-type="java.lang.String"
url="http://localhost:9000/api/series/findAll"
request-channel="requestChannel"
reply-channel="jsonToSeries"
reply-timeout="30000"/>
<map-to-object-transformer
input-channel="jsonToSeries" output-channel="responseChannel" type="com.txus.entities.Series"/>
<!-- END: Spring Integration -->
</beans:beans>
You have an issue with the last component where you configure output-channel="responseChannel". There is just nothing which is subscribed to that channel.
I see your #Gateway config on the matter, but it is a bit wrong. XML configuration has a precedence over annotations there. Hence the requestChannel in the end is exactly inputChannel.
If you'd like to send the result of <map-to-object-transformer> to the responseChannel and accept it as a return from the RequestGateway invocation, you should specify that responseChannel as a reply-channel on the gateway configuration.
From other side you just don't need it there and the TemporaryReplyChannel comes to the rescue.
Please, refer for more information to the Spring Integration Manual.

Spring's PayloadValidatingInterceptor evaluates XML (uncorrect) invalid

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>

HBase put fails at second insert

I'm using spring data + hbase to write some values into a HBase database.
Unfortunately the HbaseTemplate seems to close the connection after the first call.
I'm new to Spring and HBase/Hadoop, so i don't know if this is a Spring/HBase Configuration issue or another stupidity
Testclass:
package org.springframework.data.hadoop.samples;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.hadoop.hbase.HbaseTemplate;
import org.springframework.data.hadoop.hbase.TableCallback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/hbase-context.xml")
public class WordCountWorkflowTest {
#Autowired
private ApplicationContext ctx;
#Autowired
private HbaseTemplate hbaseTemplate;
#Test
public void testWorkflowNS() throws Exception {
if (hbaseTemplate == null) {
throw new NullPointerException("template null!");
}
// Write to HBase
InnerTableCallback itc = new InnerTableCallback("JustaString", 42);
hbaseTemplate.execute("Wordcount", itc);
itc = new InnerTableCallback("Anotherstring", 23);
// Here the HBase insert fails
hbaseTemplate.execute("Wordcount", itc);
}
#Test
public void testWorkflowNSSucess() throws Exception {
System.out.println("done");
}
/**
* This is a Inner class providing access to the HBase Table to store the
* counted words and number of matches.
*
* */
class InnerTableCallback implements TableCallback<Object> {
String foundStr;
int no;
/**
* The constructor just saved the given foundStr/no tuple in inner
* variables.
*
* #param foundstr
* string found in the text
* #param no
* number of found matches
* #return null
* */
public InnerTableCallback(String foundStr, int no) {
this.foundStr = foundStr;
this.no = no;
}
/**
* This Method puts the given String and number of found matches into
* the HBase table the column family is "cf1" and the column is
* "matches". The rowname is the found string.
* */
#Override
public Object doInTable(HTable table) throws Throwable {
Put p = new Put(Bytes.toBytes(foundStr));
// Put operation on hbase shell:
// hbase(main):005:0> put 'testtable', 'myrow-2', 'colfam1:q2',
// 'value-2'
// add(byte[] family, byte[] qualifier, byte[] value)
p.add(Bytes.toBytes("cf1"), Bytes.toBytes("matches"),
Bytes.toBytes(new Integer(no).toString()));
table.put(p);
return null;
}
}
}
hbase-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:hdp="http://www.springframework.org/schema/hadoop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/hadoop http://www.springframework.org/schema/hadoop/spring-hadoop.xsd">
<context:property-placeholder location="classpath:batch.properties,classpath:hadoop.properties"
ignore-resource-not-found="true" ignore-unresolvable="true" />
<context:component-scan base-package="org.springframework.data.hadoop.samples" />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
<bean id="hbaseTemplate" class="org.springframework.data.hadoop.hbase.HbaseTemplate" p:configuration-ref="hbaseConfiguration"/>
<hdp:hbase-configuration>
</hdp:hbase-configuration>
</beans>
Stacktrace:
org.springframework.data.hadoop.hbase.HbaseSystemException: org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation#61bb0cc0 closed; nested exception is java.io.IOException: org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation#61bb0cc0 closed
at org.springframework.data.hadoop.hbase.HbaseUtils.convertHbaseException(HbaseUtils.java:42)
at org.springframework.data.hadoop.hbase.HbaseTemplate.convertHbaseAccessException(HbaseTemplate.java:111)
at org.springframework.data.hadoop.hbase.HbaseTemplate.execute(HbaseTemplate.java:82)
at org.springframework.data.hadoop.samples.WordCountWorkflowTest.testWorkflowNS(WordCountWorkflowTest.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
....
Caused by: java.io.IOException: org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation#61bb0cc0 closed
at org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.locateRegion(HConnectionManager.java:822)
at org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.locateRegion(HConnectionManager.java:810)
at org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.processBatchCallback(HConnectionManager.java:1492)
at org.apache.hadoop.hbase.client.HConnectionManager$HConnectionImplementation.processBatch(HConnectionManager.java:1377)
at org.apache.hadoop.hbase.client.HTable.flushCommits(HTable.java:916)
at org.apache.hadoop.hbase.client.HTable.doPut(HTable.java:772)
at org.apache.hadoop.hbase.client.HTable.put(HTable.java:747)
at org.springframework.data.hadoop.samples.WordCountWorkflowTest$InnerTableCallback.doInTable(WordCountWorkflowTest.java:83)
at org.springframework.data.hadoop.hbase.HbaseTemplate.execute(HbaseTemplate.java:72)
Cheers,R
Solved!
I've used a older and deprecated version of the spring-data-hadoop package (Milestone) in my maven pom.xml. I switched to the Snapshot repository, which fixes the incorrect handling of HBase tables.
If you use spring-batch: I had to change the <tasklet> definitions for hadoop to <job-tasklet> in the *context.xml with the new version.
The error message from the XML parser was:
The matching wildcard is strict, but no declaration can be found for element 'tasklet'
Hope this helps someone :-)

Resources