Why is JsonHttpContent's output empty? - java

I am using Google Http Client library (1.20) on Google App Engine (1.9.30) to submit a POST request to Google Cloud Messaging (GCM) servers. Here's the code:
public static HttpRequestFactory getGcmRequestFactory() {
if (null == gcmFactory) {
gcmFactory = (new UrlFetchTransport())
.createRequestFactory(new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
request.getHeaders().setAuthorization(
"key=" + Config.get(Config.Keys.GCM_SERVER_API_KEY).orNull());
request.getHeaders().setContentType("application/json");
request.getHeaders().setAcceptEncoding(null);
}
});
}
return gcmFactory;
}
public static JsonFactory getJsonFactory() {
return jacksonFactory;
}
public static String sendGcmMessage(GcmDownstreamDto message) {
HttpRequestFactory factory = getGcmRequestFactory();
JsonHttpContent content = new JsonHttpContent(getJsonFactory(), message);
String response = EMPTY;
try {
HttpRequest req = factory.buildPostRequest(gcmDownstreamUrl, content);
log.info("req headers = " + req.getHeaders());
System.out.print("req content = ");
content.writeTo(System.out); // prints out "{}"
System.out.println(EMPTY);
HttpResponse res = req.execute(); // IOException here
response = IOUtils.toString(res.getContent());
} catch (IOException e) {
log.log(Level.WARNING, "IOException...", e);
}
return response;
}
Now the content.writeTo() always prints out empty JSON. Why is that? What am I doing wrong? The GcmDownstreamDto class (using Lombok to generate getters and setters):
#Data
#Accessors(chain = true)
public class GcmDownstreamDto {
private String to;
private Object data;
private List<String> registration_ids;
private GcmNotificationDto notification;
public GcmDownstreamDto addRegistrationId(String regId) {
if (null == this.registration_ids) {
this.registration_ids = new ArrayList<>();
}
if (isNotBlank(regId)) {
this.registration_ids.add(regId);
}
return this;
}
}
The immediate goal is to generate the same response as (from Checking the validity of an API key):
api_key=YOUR_API_KEY
curl --header "Authorization: key=$api_key" \
--header Content-Type:"application/json" \
https://gcm-http.googleapis.com/gcm/send \
-d "{\"registration_ids\":[\"ABC\"]}"
{"multicast_id":6782339717028231855,"success":0,"failure":1,
"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
I've already tested using curl so I know the API key is valid, I just want to do the same thing in Java code to build up my base classes.
sendGcmMessage() is being invoked as follows:
#Test
public void testGcmDownstreamMessage() {
GcmDownstreamDto message = new GcmDownstreamDto().addRegistrationId("ABC");
System.out.println("message = " + message);
String response = NetCall.sendGcmMessage(message);
System.out.println("Response: " + response);
}
All help appreciated.

Found out the problem: it's the way JacksonFactory().createJsonGenerator().searialize() works (I was expecting it to serialize the way ObjectMapper serializes). This is the code for JsonHttpContent.writeTo() (from JsonHttpContent.java in google-http-java-client):
public void writeTo(OutputStream out) throws IOException {
JsonGenerator generator = jsonFactory.createJsonGenerator(out, getCharset());
generator.serialize(data);
generator.flush();
}
The Jackson JsonGenerator expects a key-value pairing (represented in Java as Map) which is not obvious from the constructor signature of the JsonHttpContent constructor: JsonHttpContent(JsonFactory, Object).
So if instead of passing a GcmDownstreamDto (as defined in the question, which is what would have worked with an ObjectMapper), I were to do the following:
Map<String, Object> map = new HashMap<>();
List<String> idList = Arrays.asList("ABC");
map.put("registration_ids", idList);
everything works as expected and the output is:
{"registration_ids":["ABC"]}
So just remember to pass the JsonHttpContent(JsonFactory, Object) constructor a Map<String, Object> as the second parameter, and everything will work as you would expect it to.

You need to annotate the POJO fields with #Key:
import com.google.api.client.util.Key;
// ...
#Key private String to;
#Key private Object data;
#Key private List<String> registration_ids;
// ...

Related

Unable to get mocked response from Feign Client in Spring Boot

I am unable to get the mocked response from Feign Client. I provide below the code.
In the service class, it has been written like this.
public String getInfo(HttpServletRequest request, String id, String type) {
.... other code .....
try {
statusAsJsonString = myFeignClient.getStatus(cookie, id, type);
System.out.println("statusAsJsonString--------->"+statusAsJsonString);
ObjectNode node = new ObjectMapper().readValue(statusAsJsonString, ObjectNode.class);
if (node.has(CommonConstants.STATUS)) {
statusValue = node.get(CommonConstants.STATUS).asText();
}
} catch (FeignException fe) {
byte[] contents = fe.content();
String jsonContents = null;
if(contents != null) {
jsonContents = new String(contents);
}
statusValue = getErrorParsedStatusValue(jsonContents);
} catch (Exception ex) {
ex.printStackTrace();
}
log.debug("status: " + statusValue);
return statusValue;
}
In the unit test, I am trying to write in the following manner.
String responseBody = "[]";
when(myFeignClient.getStatus("cookievalue", "id", "SOme-Value")).thenReturn(responseBody);
I have also used, WireMock to achieve it.
wireMockServer.stubFor(WireMock.get("/rest/v1/somna/{id}/phase").withRequestBody(WireMock.equalToJson("{ \"name\": \"Phone\", \"initialStock\": 3}"))
.willReturn(WireMock.okJson(responseBody)));
The following piece of code is never covered and executed.
statusAsJsonString = myFeignClient.getStatus(cookie, id, type);
System.out.println("statusAsJsonString--------->"+statusAsJsonString);
Also the invocation of Feign client is inside a service method, first want to get the mocked result of that Feign client.
PLease help me.
I provide below my Feign CLient
#FeignClient(name = CommonConstants.FEIGN_CLIENT_NAME, url = "${feign.service.url}", primary = false)
public interface MyFeignClient {
#GetMapping(value = "/rest/v1/project/{id}/phaseName")
String getStatus(#RequestHeader("Cookie") String cookie,
#PathVariable("id") Stringid, #RequestParam("type") String type);
}
In my test class, I have added the followings.
#Autowired
private MyServiceImpl readyService = new MyServiceImpl();
#Mock
private MyFeignClient myFeignClient;
#ClassRule
public static WireMockServer wireMockServer = new WireMockServer(new WireMockConfiguration().port(8088));
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
httpServletRequest = Mockito.mock(HttpServletRequest.class);
ReflectionTestUtils.setField(someService, "cookieName", "cookieName");
wireMockServer.start();
}

#FormParameter data becomes null after reading and setting the same data in ContainerRequestContext entityStream

I have implemented filter and I have called getEntityStream of ContainerRequestContext and set the exact value back by using setEntitystream. If i use this filter then #FormParameter data becomes null and if i don't use filter then everything will be fine (as I am not calling getEntityStream) and i have to use filter to capture request data.
Note: I am getting form params from MultivaluedMap formParams but not from #FormParameter.
Environment :- Rest Easy API with Jboss Wildfly 8 server.
#Provider
#Priority(Priorities.LOGGING)
public class CustomLoggingFilter implements ContainerRequestFilter, ContainerResponseFilter{
final static Logger log = Logger.getLogger(CustomLoggingFilter.class);
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
MDC.put("start-time", String.valueOf(System.currentTimeMillis()));
String entityParameter = readEntityStream(requestContext);
log.info("Entity Parameter :"+entityParameter);
}
private String readEntityStream(ContainerRequestContext requestContext){
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
final InputStream inputStream = requestContext.getEntityStream();
final StringBuilder builder = new StringBuilder();
int read=0;
final byte[] data = new byte[4096];
try {
while ((read = inputStream.read(data)) != -1) {
outStream.write(data, 0, read);
}
} catch (IOException e) {
e.printStackTrace();
}
byte[] requestEntity = outStream.toByteArray();
if (requestEntity.length == 0) {
builder.append("");
} else {
builder.append(new String(requestEntity));
}
requestContext.setEntityStream(new ByteArrayInputStream(requestEntity) );
return builder.toString();
}
return null;
}
}
class customResource
{
//// This code is not working
#POST
#Path("voiceCallBack")
#ApiOperation(value = "Voice call back from Twilio")
public void voiceCallback(#FormParam("param") String param)
{
log.info("param:" + param);
}
// This code is working
#POST
#Path("voiceCallBackMap")
#ApiOperation(value = "Voice call back from Twilio")
public void voiceCallbackMap(final MultivaluedMap<String, String> formParams)
{
String param = formParams.getFirst("param");
}
}
please suggest me solution & Thanks in Advance.
I found during run time that instance of the entity stream (from http request) is of type org.apache.catalina.connector.CoyoteInputStream (I am using jboss-as-7.1.1.Final). But we are setting entity stream with the instance of java.io.ByteArrayInputStream. So Resteasy is unable to bind individual formparmeters.
There are two solutions for this you can use any one of them :
Use this approach How to read JBoss Resteasy's servlet request twice while maintaing #FormParam binding?
Get form parameters like this:
#POST
#Path("voiceCallBackMap")
#ApiOperation(value = "Voice call back from Twilio")
public void voiceCallbackMap(final MultivaluedMap<String, String> formParams)
{
String param = formParams.getFirst("param");
}

Spring Controller always produce json

Is there anyway to force spring to always produce json, even an empty json object if there's no data to return.
Our services go through another service that rejects any response that isn't valid json (regardless of status code). It's not nice but we have no control of this.
With spring controllers you can tell them to produce json, but this only works when there's content to return. Is there a quick and elegant way to make all responses be json?
#RequestMapping(value = "/test", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public
#ResponseBody
ResponseEntity<String> test(){
// if this returns null or an empty string the response body will be emtpy
// and the content-type header will not be set.
return service.getData();
}
The simply fix here is to simply add an if statement to check for null. But that's ugly as I'll have to manually set the header and the response body.
I'm hoping someone knows of a nicer way?
Thanks
If you want all responses to return application/json, then you can set this at a single place by overriding postHandle() from HandlerInterceptorAdapter:
#Component
public class ResponseInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws IOException {
if (response.getContentType() == null || response.getContentType().equals("")) {
response.setContentType("application/json");
}
}
}
You can look here
You may wrap the response in a "Container" object
For example I use this BaseAjaxResponse:
public class BaseAjaxResponse implements Serializable
{
private static final long serialVersionUID = 9087132709920851138L;
private int codiceOperazione;
private String descrizioneEsitoOperazione;
private long numeroTotaleOggetti;
private long numeroOggettiRestituiti;
private List<? extends Object> payload;
//Constructors and getter/setter
}
Then in my controllers I use this strategy:
#RequestMapping(method = { RequestMethod.POST }, value = { "/find" })
public ResponseEntity<BaseAjaxResponse> createCandidato(#RequestBody CandidatoDto candidato){
BaseAjaxResponse bar = new BaseAjaxResponse();
HttpStatus statusCode = null;
List<Object> payload = null;
StopWatch sw = new StopWatch("Find");
try
{
sw.start();
payload = myService.find();
sw.stop();
if( payload == null || payload.isEmpty() )
{
statusCode = HttpStatus.NO_CONTENT;
bar.setCodiceOperazione(statusCode.value());
bar.setDescrizioneEsitoOperazione("No result");
}
else
{
statusCode = HttpStatus.OK;
bar.setCodiceOperazione(statusCode.value());
bar.setDescrizioneEsitoOperazione("Got result");
//Set the object count and the number of found objects
}
}
catch (Exception e)
{
String message = "Errore nell'inserimento di un candidato; "+e.getMessage();
statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
bar.setCodiceOperazione(statusCode.value());
bar.setDescrizioneEsitoOperazione(message);
logger.error(message, e);
}
finally
{
if( sw.isRunning() )
{
sw.stop();
if( logger.isDebugEnabled() )
{
logger.debug("CHIUSURA STOPWATCH FORZATA. "+sw.toString());
}
}
}
return new ResponseEntity<BaseAjaxResponse>(bar, statusCode);
}
I hope this can be useful
Angelo

Parsing GET JSON reply without keys to object in Spring

I am currently consuming content in the style of
"potter",["potter harry","potter","potter hallows 2","potter collection","potter hallows 1","potter dvd box","potter 7","potter hallows","potter blue ray","potter feniks"],[{"xcats":[{"name":"dvd_all"},{"name":"books_nl"}]},{},{},{},{},{},{},{},{},{}],[]]
Using the following code in Spring
RestTemplate restTemplate = new RestTemplate();
String information = restTemplate.getForObject(URL, String.class);
//further parsing of information, using String utilities
Obviously this is not the way to go, because I should be able to automatically parse it somehow. I will also only need the content of the second element as well (the array, from potter harry to potter feniks).
What is the best way to parse a GET response like that, when its json contents aren't name-valued?
#Test
public void testJson(){
RestTemplate template = new RestTemplate();
ResponseEntity<ArrayNode> entity = template.
exchange("https://api.myjson.com/bins/2rl7m", HttpMethod.GET, null, new ParameterizedTypeReference<ArrayNode>() {
});
ArrayNode body = entity.getBody();
body.get(1).forEach(m->{
System.out.println(m.asText());});
}
But my advice is if you can change the response type not to be json array with mixed value types in it will be better
The following helper class enabled me to easily parse the response and get the list I needed
public class JSONHelper {
private static final JsonFactory factory = new JsonFactory();
private static final ObjectMapper mapper = new ObjectMapper(factory);
public static List<String> getListOnPosition(int i, String inputWithFullList) throws JsonProcessingException, IOException {
List<String> result = new ArrayList<String>();
JsonNode rootNode = mapper.readTree(inputWithFullList);
ArrayNode node = (ArrayNode) rootNode.get(i);
if (!node.isArray()) {
result.add(node.asText());
} else {
for (final JsonNode subNode : node) {
result.add(subNode.asText());
}
}
return result;
}
}
Some JUnit tests for this scenario
public class JSONHelperTest {
#Test
public void parseListOnPositionFullListTest() throws JsonProcessingException, IOException {
String inputWithFullList = "[\"a\",[\"b\", \"c\", \"d\"],[],[]]";
List<String> result = JSONHelper.getListOnPosition(1, inputWithFullList);
assertEquals(3, result.size());
assertEquals(Arrays.asList("b", "c", "d"), result);
}
#Test
public void parseListOnPositionOneElementListTest() throws JsonProcessingException, IOException {
String inputWithFullList = "[\"a\",[\"b\"],[],[]]";
List<String> result = JSONHelper.getListOnPosition(1, inputWithFullList);
assertEquals(1, result.size());
assertEquals(Arrays.asList("b"), result);
}
#Test
public void parseListOnPositionEmptyListTest() throws JsonProcessingException, IOException {
String inputWithFullList = "[\"a\",[],[],[]]";
assertTrue(JSONHelper.getListOnPosition(1, inputWithFullList).isEmpty());
}
}

Retrieve a list of a given user's tweets using Twitter API 1.1 and Retrofit

I'm trying to obtain a list of a user's tweets and I've run into some trouble when trying to authenticate my call to the API. I currently get a 401 when executing the code below:
public interface TwitterApi {
String API_URL = "https://api.twitter.com/1.1";
String CONSUMER_KEY = "<CONSUMER KEY GOES HERE>";
String CONSUMER_SECRET = "<CONSUMER SECRET GOES HERE>";
String ACCESS_TOKEN = "<ACCESS TOKEN GOES HERE>";
String ACCESS_TOKEN_SECRET = "<ACCESS TOKEN SECRET GOES HERE>";
#GET("/statuses/user_timeline.json")
List<Tweet> fetchUserTimeline(
#Query("count") final int count,
#Query("screen_name") final String screenName);
}
The following throws a 401 Authorisation error when calling fetchUserTimeline()
RetrofitHttpOAuthConsumer consumer = new RetrofitHttpOAuthConsumer(TwitterApi.CONSUMER_KEY, TwitterApi.CONSUMER_SECRET);
consumer.setTokenWithSecret(TwitterApi.ACCESS_TOKEN, TwitterApi.ACCESS_TOKEN_SECRET);
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(TwitterApi.API_URL)
.setClient(new SigningOkClient(consumer))
.build();
TwitterApi twitterApi = restAdapter.create(TwitterApi.class)
tweets = twitterApi.fetchUserTimeline(2, screenName);
I've also included the relevant code from the signpost-retrofit plugin:
public class SigningOkClient extends OkClient {
private final RetrofitHttpOAuthConsumer mOAuthConsumer;
public SigningOkClient(RetrofitHttpOAuthConsumer consumer) {
mOAuthConsumer = consumer;
}
public SigningOkClient(OkHttpClient client, RetrofitHttpOAuthConsumer consumer) {
super(client);
mOAuthConsumer = consumer;
}
#Override
public Response execute(Request request) throws IOException {
Request requestToSend = request;
try {
HttpRequestAdapter signedAdapter = (HttpRequestAdapter) mOAuthConsumer.sign(request);
requestToSend = (Request) signedAdapter.unwrap();
} catch (OAuthMessageSignerException | OAuthExpectationFailedException | OAuthCommunicationException e) {
// Fail to sign, ignore
e.printStackTrace();
}
return super.execute(requestToSend);
}
}
The signpost-retrofit plugin can be found here: https://github.com/pakerfeldt/signpost-retrofit
public class RetrofitHttpOAuthConsumer extends AbstractOAuthConsumer {
private static final long serialVersionUID = 1L;
public RetrofitHttpOAuthConsumer(String consumerKey, String consumerSecret) {
super(consumerKey, consumerSecret);
}
#Override
protected HttpRequest wrap(Object request) {
if (!(request instanceof retrofit.client.Request)) {
throw new IllegalArgumentException("This consumer expects requests of type " + retrofit.client.Request.class.getCanonicalName());
}
return new HttpRequestAdapter((Request) request);
}
}
Any help here would be great. The solution doesn't have to include the use of signpost but I do want to use Retrofit. I also do not want to show the user an 'Authenticate with Twitter' screen in a WebView - I simply want to display a handful of relevant tweets as part of a detail view.
Are you certain the signpost-retrofit project works for twitter oauth? I've used twitter4j successfully in the past - and if you don't want the full library you can use their code for reference. twitter4j

Categories

Resources