Spring websocket messaging template isn't publishing data from Controller without #Scheduler annotation - spring-websocket

#Controller
public class WebServiceController {
private static Logger logger = LoggerFactory.getLogger(WebServiceController.class.getName());
#Autowired
private SimpMessagingTemplate template;
public static Response response;
private final MessageSendingOperations<String> messagingTemplate;
#Autowired
private XmlCommandFactory xmlCommandFactory;
#Autowired
public WebServiceController(final MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#RequestMapping(value = "/handleResponse.htm/{sessionId}")
public void handleRequest(final HttpServletRequest request, final HttpServletResponse httpResponse,
#PathVariable final String sessionId) throws Exception {
httpResponse.setStatus(200);
final StringBuilder buffer = new StringBuilder();
final BufferedReader reader = request.getReader();
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line.trim());
}
if (!buffer.toString().isEmpty()) {
try {
response = xmlCommandFactory.createCommand(buffer.toString(), sessionId);
publishData();
} catch (final Exception ex) {
logger.warn(" Error while processing response ", ex);
}
}
}
#Scheduled(fixedDelay = 2000)
public void publishData() {
try {
if (null != response && !response.getData().isEmpty()) {
this.messagingTemplate.convertAndSend("/update/config/" + response.getSessionId(), response);
}
} catch (final Exception e) {
logger.warn("exception " + e);
}
}
The above code publishes data to the UI at the scheduled delay. But if I comment out #Scheduler annotation and call this method from handleRequest, it doesn't publish data to UI. What could be the reason?

Related

How to use Filter in Spring Cloud Data Flow?

I would like to add a Filter in Spring Cloud Data Flow (SCDF) to debug and see how it works when receiving some requests.
I have tried to implements javax.servlet.Filter and override doFilter method but It's seem not work because there are some default Filter of SCDF had been started before my Filter class.
Are there any way do write a filter for SCDF? Is it possible if I apply this link for this purpose?
This is my Filter class:
#Component
public class CustomFilter implements Filter {
#Override
public void destroy() {
System.out.println("init fillter ");
}
#Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
try {
HttpServletRequest req = (HttpServletRequest) arg0;
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(
(HttpServletResponse) arg1);
ResettableStreamHttpServletRequest wrappedRequest = new ResettableStreamHttpServletRequest(
(HttpServletRequest) req);
logger.info(">>>>>link: " + req.getServletPath() + "?");
for (Entry<String, String[]> entry : req.getParameterMap().entrySet()) {
logger.info(">>>>>param: " + entry.getKey() + ":" + entry.getValue()[0]);
}
String bodyRequest = IOUtils.toString(wrappedRequest.getReader());
logger.info(">>>>>link: " + req.getServletPath() + "?");
for (Entry<String, String[]> entry : req.getParameterMap().entrySet()) {
logger.info(">>>>>param: " + entry.getKey() + ":" + entry.getValue()[0]);
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", 2);
javax.xml.transform.Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
try {
logger.info("Request: " + bodyRequest);
} catch (Exception e) {
logger.info("Request: " + bodyRequest);
}
wrappedRequest.resetInputStream();
arg2.doFilter(wrappedRequest, responseWrapper);
logger.info("Response: " + IOUtils.toString(responseWrapper.getContentInputStream()));
responseWrapper.copyBodyToResponse();
} catch (Exception ex) {
logger.info("doFilter: " + ex);
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("init fillter " + arg0);
}
private static class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {
private byte[] rawData;
private HttpServletRequest request;
private ResettableServletInputStream servletStream;
public ResettableStreamHttpServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
this.servletStream = new ResettableServletInputStream();
}
public void resetInputStream() {
servletStream.stream = new ByteArrayInputStream(rawData);
}
#Override
public ServletInputStream getInputStream() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return servletStream;
}
#Override
public BufferedReader getReader() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return new BufferedReader(new InputStreamReader(servletStream));
}
private class ResettableServletInputStream extends ServletInputStream {
private InputStream stream;
#Override
public int read() throws IOException {
return stream.read();
}
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return false;
}
#Override
public void setReadListener(ReadListener arg0) {
}
}
}
}
Spring Cloud Data Flow server is inherently a Spring Boot application and hence it allows you to inject specific configuration beans into Data Flow configuration to extend/customize.
In your case, you can create a bean that extends javax.servlet.http.HttpFilter or implements javax.servlet.Filter and have that bean injected into SCDF configuration.

Feign ErrorDecoder : retrieve the original message

I use a ErrorDecoder to return the right exception rather than a 500 status code.
Is there a way to retrieve the original message inside the decoder. I can see that it is inside the FeignException, but not in the decode method. All I have is the 'status code' and a empty 'reason'.
public class CustomErrorDecoder implements ErrorDecoder {
private final ErrorDecoder errorDecoder = new Default();
#Override
public Exception decode(String s, Response response) {
switch (response.status()) {
case 404:
return new FileNotFoundException("File no found");
case 403:
return new ForbiddenAccessException("Forbidden access");
}
return errorDecoder.decode(s, response);
}
}
Here the original message : "message":"Access to the file forbidden"
feign.FeignException: status 403 reading ProxyMicroserviceFiles#getUserRoot(); content:
{"timestamp":"2018-11-28T17:34:05.235+0000","status":403,"error":"Forbidden","message":"Access to the file forbidden","path":"/root"}
Also I use my FeignClient interface like a RestController so I don't use any other Controler populated with the proxy that could encapsulate the methods calls.
#RestController
#FeignClient(name = "zuul-server")
#RibbonClient(name = "microservice-files")
public interface ProxyMicroserviceFiles {
#GetMapping(value = "microservice-files/root")
Object getUserRoot();
#GetMapping(value = "microservice-files/file/{id}")
Object getFileById(#PathVariable("id") int id);
}
Here is a solution, the message is actually in the response body as a stream.
package com.clientui.exceptions;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.CharStreams;
import feign.Response;
import feign.codec.ErrorDecoder;
import lombok.*;
import java.io.*;
public class CustomErrorDecoder implements ErrorDecoder {
private final ErrorDecoder errorDecoder = new Default();
#Override
public Exception decode(String s, Response response) {
String message = null;
Reader reader = null;
try {
reader = response.body().asReader();
//Easy way to read the stream and get a String object
String result = CharStreams.toString(reader);
//use a Jackson ObjectMapper to convert the Json String into a
//Pojo
ObjectMapper mapper = new ObjectMapper();
//just in case you missed an attribute in the Pojo
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//init the Pojo
ExceptionMessage exceptionMessage = mapper.readValue(result,
ExceptionMessage.class);
message = exceptionMessage.message;
} catch (IOException e) {
e.printStackTrace();
}finally {
//It is the responsibility of the caller to close the stream.
try {
if (reader != null)
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
switch (response.status()) {
case 404:
return new FileNotFoundException(message == null ? "File no found" :
message);
case 403:
return new ForbiddenAccessException(message == null ? "Forbidden
access" : message);
}
return errorDecoder.decode(s, response);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public static class ExceptionMessage{
private String timestamp;
private int status;
private String error;
private String message;
private String path;
}
}
If you want to get the response payload body, with the Feign exception, just use this method:
feignException.contentUTF8();
Example:
try {
itemResponse = call(); //method with the feign call
} catch (FeignException e) {
logger.error("ResponseBody: " + e.contentUTF8());
}
It is suggested to use input stream instead of reader and map it to your object.
package com.clientui.exceptions;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.CharStreams;
import feign.Response;
import feign.codec.ErrorDecoder;
import lombok.*;
import java.io.*;
public class CustomErrorDecoder implements ErrorDecoder {
private final ErrorDecoder errorDecoder = new Default();
#Override
public Exception decode(String s, Response response) {
String message = null;
InputStream responseBodyIs = null;
try {
responseBodyIs = response.body().asInputStream();
ObjectMapper mapper = new ObjectMapper();
ExceptionMessage exceptionMessage = mapper.readValue(responseBodyIs, ExceptionMessage.class);
message = exceptionMessage.message;
} catch (IOException e) {
e.printStackTrace();
// you could also return an exception
return new errorMessageFormatException(e.getMessage());
}finally {
//It is the responsibility of the caller to close the stream.
try {
if (responseBodyIs != null)
responseBodyIs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
switch (response.status()) {
case 404:
return new FileNotFoundException(message == null ? "File no found" :
message);
case 403:
return new ForbiddenAccessException(message == null ? "Forbidden access" : message);
}
return errorDecoder.decode(s, response);
}
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
public static class ExceptionMessage{
private String timestamp;
private int status;
private String error;
private String message;
private String path;
}
}
If you're like me and really just want the content out of a failed Feign call without all these custom decoders and boilerplate, there is a hacky way do this.
If we look at FeignException when it is being created and a response body exists, it assembles the exception message like so:
if (response.body() != null) {
String body = Util.toString(response.body().asReader());
message += "; content:\n" + body;
}
Therefore if you're after the response body, you can just pull it out by parsing the Exception message since it is delimited by a newline.
String[] feignExceptionMessageParts = e.getMessage().split("\n");
String responseContent = feignExceptionMessageParts[1];
And if you want the object, you can use something like Jackson:
MyResponseBodyPojo errorBody = objectMapper.readValue(responseContent, MyResponseBodyPojo.class);
I do not claim this is a smart approach or a best practice.
The original message is within the Response body, as already answered. However, we can reduce the amount of boilerplate using Java 8 Streams to read it:
public class CustomErrorDecoder implements ErrorDecoder {
private final ErrorDecoder errorDecoder = new Default();
#Override
public Exception decode(String s, Response response) {
String body = "4xx client error";
try {
body = new BufferedReader(response.body().asReader(StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
} catch (IOException ignore) {}
switch (response.status()) {
case 404:
return new FileNotFoundException(body);
case 403:
return new ForbiddenAccessException(body);
}
return errorDecoder.decode(s, response);
}
}
Some refactoring and code style on accepted answer:
#Override
#SneakyThrows
public Exception decode(String methodKey, Response response) {
String message;
try (Reader reader = response.body().asReader()) {
String result = StringUtils.toString(reader);
message = mapper.readValue(result, ErrorResponse.class).getMessage();
}
if (response.status() == 401) {
return new UnauthorizedException(message == null ? response.reason() : message);
}
if (response.status() == 403) {
return new ForbiddenException(message == null ? response.reason() : message);
}
return defaultErrorDecoder.decode(methodKey, response);
}

FeignClient throws instead of returning ResponseEntity with error http status

As I'm using ResponseEntity<T> as return value for my FeignClient method, I was expecting it to return a ResponseEntity with 400 status if it's what the server returns. But instead it throws a FeignException.
How can I get a proper ResponseEntity instead of an Exception from FeignClient ?
Here is my FeignClient:
#FeignClient(value = "uaa", configuration = OauthFeignClient.Conf.class)
public interface OauthFeignClient {
#RequestMapping(
value = "/oauth/token",
method = RequestMethod.POST,
consumes = MULTIPART_FORM_DATA_VALUE,
produces = APPLICATION_JSON_VALUE)
ResponseEntity<OauthTokenResponse> token(Map<String, ?> formParams);
class Conf {
#Value("${oauth.client.password}")
String oauthClientPassword;
#Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
#Bean
public Contract feignContract() {
return new SpringMvcContract();
}
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("web-client", oauthClientPassword);
}
}
}
and here how I use it:
#PostMapping("/login")
public ResponseEntity<LoginTokenPair> getTokens(#RequestBody #Valid LoginRequest userCredentials) {
Map<String, String> formData = new HashMap<>();
ResponseEntity<OauthTokenResponse> response = oauthFeignClient.token(formData);
//code never reached if contacted service returns a 400
...
}
By the way, solution I gave before works, but my initial intention is bad idea: an error is an error and should not be handled on nominal flow. Throwing an exception, like Feign does, and handling it with an #ExceptionHandler is a better way to go in Spring MVC world.
So two solutions:
add an #ExceptionHandler for FeignException
configure the FeignClient with an ErrorDecoder to translate the error in an Exception your business layer knows about (and already provide #ExceptionHandler for)
I prefer second solution because received error message structure is likely to change from a client to an other, so you can extract finer grained data from those error with a per-client error decoding.
FeignClient with conf (sorry for the noise introduced by feign-form)
#FeignClient(value = "uaa", configuration = OauthFeignClient.Config.class)
public interface OauthFeignClient {
#RequestMapping(
value = "/oauth/token",
method = RequestMethod.POST,
consumes = MULTIPART_FORM_DATA_VALUE,
produces = APPLICATION_JSON_VALUE)
DefaultOAuth2AccessToken token(Map<String, ?> formParams);
#Configuration
class Config {
#Value("${oauth.client.password}")
String oauthClientPassword;
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
#Bean
public Decoder springDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(messageConverters));
}
#Bean
public Contract feignContract() {
return new SpringMvcContract();
}
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("web-client", oauthClientPassword);
}
#Bean
public ErrorDecoder uaaErrorDecoder(Decoder decoder) {
return (methodKey, response) -> {
try {
OAuth2Exception uaaException = (OAuth2Exception) decoder.decode(response, OAuth2Exception.class);
return new SroException(
uaaException.getHttpErrorCode(),
uaaException.getOAuth2ErrorCode(),
Arrays.asList(uaaException.getSummary()));
} catch (Exception e) {
return new SroException(
response.status(),
"Authorization server responded with " + response.status() + " but failed to parse error payload",
Arrays.asList(e.getMessage()));
}
};
}
}
}
Common business exception
public class SroException extends RuntimeException implements Serializable {
public final int status;
public final List<String> errors;
public SroException(final int status, final String message, final Collection<String> errors) {
super(message);
this.status = status;
this.errors = Collections.unmodifiableList(new ArrayList<>(errors));
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SroException)) return false;
SroException sroException = (SroException) o;
return status == sroException.status &&
Objects.equals(super.getMessage(), sroException.getMessage()) &&
Objects.equals(errors, sroException.errors);
}
#Override
public int hashCode() {
return Objects.hash(status, super.getMessage(), errors);
}
}
Error handler (extracted from a ResponseEntityExceptionHandler extension)
#ExceptionHandler({SroException.class})
public ResponseEntity<Object> handleSroException(SroException ex) {
return new SroError(ex).toResponse();
}
Error response DTO
#XmlRootElement
public class SroError implements Serializable {
public final int status;
public final String message;
public final List<String> errors;
public SroError(final int status, final String message, final Collection<String> errors) {
this.status = status;
this.message = message;
this.errors = Collections.unmodifiableList(new ArrayList<>(errors));
}
public SroError(final SroException e) {
this.status = e.status;
this.message = e.getMessage();
this.errors = Collections.unmodifiableList(e.errors);
}
protected SroError() {
this.status = -1;
this.message = null;
this.errors = null;
}
public ResponseEntity<Object> toResponse() {
return new ResponseEntity(this, HttpStatus.valueOf(this.status));
}
public ResponseEntity<Object> toResponse(HttpHeaders headers) {
return new ResponseEntity(this, headers, HttpStatus.valueOf(this.status));
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SroError)) return false;
SroError sroException = (SroError) o;
return status == sroException.status &&
Objects.equals(message, sroException.message) &&
Objects.equals(errors, sroException.errors);
}
#Override
public int hashCode() {
return Objects.hash(status, message, errors);
}
}
Feign client usage notice how errors are transparently handled (no try / catch) thanks to #ControllerAdvice & #ExceptionHandler({SroException.class})
#RestController
#RequestMapping("/uaa")
public class AuthenticationController {
private static final BearerToken REVOCATION_TOKEN = new BearerToken("", 0L);
private final OauthFeignClient oauthFeignClient;
private final int refreshTokenValidity;
#Autowired
public AuthenticationController(
OauthFeignClient oauthFeignClient,
#Value("${oauth.ttl.refresh-token}") int refreshTokenValidity) {
this.oauthFeignClient = oauthFeignClient;
this.refreshTokenValidity = refreshTokenValidity;
}
#PostMapping("/login")
public ResponseEntity<LoginTokenPair> getTokens(#RequestBody #Valid LoginRequest userCredentials) {
Map<String, String> formData = new HashMap<>();
formData.put("grant_type", "password");
formData.put("client_id", "web-client");
formData.put("username", userCredentials.username);
formData.put("password", userCredentials.password);
formData.put("scope", "openid");
DefaultOAuth2AccessToken response = oauthFeignClient.token(formData);
return ResponseEntity.ok(new LoginTokenPair(
new BearerToken(response.getValue(), response.getExpiresIn()),
new BearerToken(response.getRefreshToken().getValue(), refreshTokenValidity)));
}
#PostMapping("/logout")
public ResponseEntity<LoginTokenPair> revokeTokens() {
return ResponseEntity
.ok(new LoginTokenPair(REVOCATION_TOKEN, REVOCATION_TOKEN));
}
#PostMapping("/refresh")
public ResponseEntity<BearerToken> refreshToken(#RequestHeader("refresh_token") String refresh_token) {
Map<String, String> formData = new HashMap<>();
formData.put("grant_type", "refresh_token");
formData.put("client_id", "web-client");
formData.put("refresh_token", refresh_token);
formData.put("scope", "openid");
DefaultOAuth2AccessToken response = oauthFeignClient.token(formData);
return ResponseEntity.ok(new BearerToken(response.getValue(), response.getExpiresIn()));
}
}
So, looking at source code, it seams that only solution is actually using feign.Response as return type for FeignClient methods and hand decoding the body with something like new ObjectMapper().readValue(response.body().asReader(), clazz) (with a guard on 2xx status of course because for error statuses, it's very likely that body is an error description and not a valid payload ;).
This makes possible to extract and forward status, header, body, etc. even if status is not in 2xx range.
Edit:
Here is a way to forward status, headers and mapped JSON body (if possible):
public static class JsonFeignResponseHelper {
private final ObjectMapper json = new ObjectMapper();
public <T> Optional<T> decode(Response response, Class<T> clazz) {
if(response.status() >= 200 && response.status() < 300) {
try {
return Optional.of(json.readValue(response.body().asReader(), clazz));
} catch(IOException e) {
return Optional.empty();
}
} else {
return Optional.empty();
}
}
public <T, U> ResponseEntity<U> toResponseEntity(Response response, Class<T> clazz, Function<? super T, ? extends U> mapper) {
Optional<U> payload = decode(response, clazz).map(mapper);
return new ResponseEntity(
payload.orElse(null),//didn't find a way to feed body with original content if payload is empty
convertHeaders(response.headers()),
HttpStatus.valueOf(response.status()));
}
public MultiValueMap<String, String> convertHeaders(Map<String, Collection<String>> responseHeaders) {
MultiValueMap<String, String> responseEntityHeaders = new LinkedMultiValueMap<>();
responseHeaders.entrySet().stream().forEach(e ->
responseEntityHeaders.put(e.getKey(), new ArrayList<>(e.getValue())));
return responseEntityHeaders;
}
}
that can be used as follow:
#PostMapping("/login")
public ResponseEntity<LoginTokenPair> getTokens(#RequestBody #Valid LoginRequest userCredentials) throws IOException {
Response response = oauthFeignClient.token();
return feignHelper.toResponseEntity(
response,
OauthTokenResponse.class,
oauthTokenResponse -> new LoginTokenPair(
new BearerToken(oauthTokenResponse.access_token, oauthTokenResponse.expires_in),
new BearerToken(oauthTokenResponse.refresh_token, refreshTokenValidity)));
}
This saves headers and status code, but error message is lost :/

Posting to HTTPS in Java

I want to post to an url https://host:0101 with a request body. I have done this with non-ssl, http, but having trouble with https.
I have both the trust store (JKS) and keystore (PKCS12)'s full path, and their passwords as properties in a .properties file.
What i have to far is:
public sendPost () throws Exception {
SSLContext sslContext = getContext();
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(factory).build();
HttpPost post = new HttpPost("https://host:0101/post");
StringEntity entity = new StringEntity(jsonData);
post.setEntity(entity);
response=client.execute(request);
responseCode = response.getStatusLine().getStatusCode();
}
public SSLContext getContext() throws Exception {
KeyStore keyStore = KeyStore.getInstance("JKS");
FileInputStream instream = new FileInputStream(new File(PATH_TO_KEYSTORE));
try {
keyStore.load(instream, "password".toCharArray());
}finally {instream.close()}
return SSLContexts.custom().loadTrustMaterial(keyStore).build();
}
I am testing this by running a Junit test just to verify that it works before deploying.
#Test
public void test() throws Exception {
int responseCode = entityUnderTest.sendPost();
assertEquals(200, responseCode);
}
The error is at line: response=client.execute(request); with the HttpHostConnectException
Failed: Connection Refused
Is the service available through HTTPS? The connection refused error occurs when there is some problem with the availability of your service.
This could be caused by a lot of problems:
your service is not running
your service is not available through HTTPS
a proxy/routing error (does your test system reach the host and port?)
If there was a problem with the authentication you would get a different message (eg: SSLPeerUnverifiedException, SSLHandshakeException).
This is working for me.. try it
Here is the post method
public static CookieManager msCookieManager = null;
private boolean isPinBuild=false;
public static String[] post(Context context, String Url, List<NameValuePair> values, List<NameValuePair> header_list)
throws ConnectTimeoutException {
String[] result = { "", "" };
try {
URL url = new URL(Url);
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
Log.i("TAG_URL : ", Url);
if (isPinBuild) {
KeyPinStore keystore = KeyPinStore.getInstance(context);
javax.net.ssl.SSLSocketFactory factory = keystore.getContext().getSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket();
socket.setEnabledCipherSuites(new String[]{"RC4-MD5", "DES-CBC-SHA", "DES-CBC3-SHA"});
socket.setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
socket.setSoTimeout(60000);
urlConnection.setSSLSocketFactory(factory);
} else {
KeyUnPinStore keystore = KeyUnPinStore.getInstance(context);
javax.net.ssl.SSLSocketFactory factory = keystore.getContext().getSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket();
socket.setEnabledCipherSuites(new String[]{"RC4-MD5", "DES-CBC-SHA", "DES-CBC3-SHA"});
socket.setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
socket.setSoTimeout(60000);
urlConnection.setSSLSocketFactory(factory);
}
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
urlConnection.setRequestMethod("POST");
urlConnection.setConnectTimeout(connection_timeout);
urlConnection.setHostnameVerifier(hostnameVerifier);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
for (NameValuePair header : header_list) {
urlConnection.addRequestProperty(header.getName(), header.getValue());
}
if (msCookieManager == null) {
msCookieManager = new CookieManager();
}
if (msCookieManager != null && msCookieManager.getCookieStore().getCookies().size() > 0) {
urlConnection.setRequestProperty(COOKIE, TextUtils.join(";", msCookieManager.getCookieStore().getCookies()));
}
Log.i("TAG_POST_COOKIE : ", msCookieManager.getCookieStore().getCookies().toString());
String postData = "";
for (NameValuePair value : values) {
postData = postData + value.getName() + "=" + URLEncoder.encode(value.getValue(), "UTF-8") + "&";
}
if (!TextUtils.isEmpty(postData) && postData.length() > 2) {
postData = postData.substring(0, postData.length() - 1);
}
Log.i("TAG_POSTDATA : ", postData);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setFixedLengthStreamingMode(postData.getBytes().length);
PrintWriter out = new PrintWriter(urlConnection.getOutputStream());
out.print(postData);
out.close();
// always check HTTP response code first
int responseCode = urlConnection.getResponseCode();
result[0] = responseCode + "";
if (responseCode == HttpURLConnection.HTTP_OK) {
// Get Response
InputStream is = urlConnection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer response = new StringBuffer();
while ((line = rd.readLine()) != null) {
response.append(line);
response.append('\r');
}
rd.close();
Map<String, List<String>> headerFields = urlConnection.getHeaderFields();
List<String> cookiesHeader = headerFields.get(COOKIES_HEADER);
/*if (cookiesHeader != null) {
for (String cookie : cookiesHeader) {
if (HttpCookie.parse(cookie).get(0).toString().contains(JSESSIONID)) {
msCookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
}
}
}*/
if (!TextUtils.isEmpty(response)) {
result[0] = HttpConnectionUrl.RESPONSECODE_REQUESTSUCCESS;
result[1] = response.toString();
Log.i("TAG_RESPONSE : ", result[1]);
}
}
} catch (UnsupportedEncodingException e) {
result[0] = HttpConnectionUrl.RESPONSECODE_CONNECTIONTIMEOUT;
e.printStackTrace();
} catch (ClientProtocolException e) {
result[0] = HttpConnectionUrl.RESPONSECODE_CONNECTIONTIMEOUT;
e.printStackTrace();
} catch (ConnectTimeoutException e) {
result[0] = HttpConnectionUrl.RESPONSECODE_CONNECTIONTIMEOUT;
e.printStackTrace();
} catch (IOException e) {
result[0] = HttpConnectionUrl.RESPONSECODE_CONNECTIONTIMEOUT;
e.printStackTrace();
} catch (Exception e) {
result[0] = HttpConnectionUrl.RESPONSECODE_CONNECTIONTIMEOUT;
e.printStackTrace();
}
return result;
}
here isPinBuild variable use for you have any certificate file or not. If you use any trusted certificate then set true.
Here the KeyPinStore class
public class KeyPinStore {
private static Context context = null;
private static KeyPinStore instance = null;
private SSLContext sslContext = SSLContext.getInstance("SSLv3");
//private SSLContext sslContext = SSLContext.getInstance("TLS");
public static synchronized KeyPinStore getInstance(Context mContext) throws CertificateException, IOException, KeyStoreException,
NoSuchAlgorithmException, KeyManagementException {
if (instance == null) {
context = mContext;
instance = new KeyPinStore();
}
return instance;
}
private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// randomCA.crt should be in the Assets directory
InputStream caInput = new BufferedInputStream(context.getAssets().open("yourCertificate.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
// SSLContext context = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
}
public SSLContext getContext() {
return sslContext;
}
}
and KeyUnpinStore class
public class KeyUnPinStore {
private static Context context = null;
private static KeyUnPinStore instance = null;
private SSLContext sslContext = SSLContext.getInstance("TLS");
public static synchronized KeyUnPinStore getInstance(Context mContext) throws CertificateException, IOException, KeyStoreException,
NoSuchAlgorithmException, KeyManagementException {
if (instance == null) {
context = mContext;
instance = new KeyUnPinStore();
}
return instance;
}
private KeyUnPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
// Create all-trusting host name verifier
// Create an SSLContext that uses our TrustManager
// SSLContext context = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
}
public SSLContext getContext() {
return sslContext;
}
}
Why don't you try using httpbin? It's an external resource that's guaranteed to work. For example:
import java.io.IOException;
import java.net.URI;
import net.minidev.json.JSONObject;
import net.minidev.json.JSONValue;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
public class Post {
public static void main(String[] args) throws Exception {
HttpClient client = new DefaultHttpClient();
int result = post(client, new URI("https://httpbin.org/post"), (JSONObject)JSONValue.parse("{'answer':42}"));
System.err.println(result);
}
public static int post(HttpClient httpClient, URI url, JSONObject content) throws IOException {
final HttpPost post = new HttpPost(url);
post.addHeader("Content-type", ContentType.APPLICATION_JSON.getMimeType());
post.setEntity(new StringEntity(content.toString(), ContentType.APPLICATION_JSON));
final HttpResponse response = httpClient.execute(post);
EntityUtils.consumeQuietly(response.getEntity());
return response.getStatusLine().getStatusCode();
}
}
Assuming that you have generated public/private keypairs using java "keytool" and both trustStore & keyStores are configured properly at client-side & in HOST container(in case of Tomcat server.xml).
In Method:"sendPost()", you may like to get SSL-Connection as follows:
try {
String endpoint="https://host:0101/post";
URL url = new URL(endpoint);
HttpsURLConnection conn = getConnection(url, "POST", uname, passwd);
conn.connect();
writeBody(conn);
catch(Exception e) { throw new RuntimeException(e); }
conn.disconnect();
}
catch(Exception e) { throw new RuntimeException(e); }
private void writeBody(HttpsURLConnection conn) {
try {
String pairs = "who=Freddy&what=Avoid Friday nights if possible.";
OutputStream out = conn.getOutputStream();
out.write(pairs.getBytes());
out.flush();
}
catch(Exception e) { throw new RuntimeException(e); }
}

BlackBerry read json string from an URL

I tried to read a json string loading from an URL, but I could not find complete code sample. Can any one provide or point me to a complete client code. I'm newer to BB development.
this is what I have done but still can't get it work please help me.
Thanks!
To read and parse data from an URL you need to implement two routines. First one of them will handle reading data from the specified URL over HTTP connection, and the second one will parse the data.
Check the following application HttpUrlReaderDemoApp, which will first read the data from specified URL and then parse the retrieved data.
URL used to retrieve data: http://codeincloud.tk/json_android_example.php
Sample data format: {"name":"Froyo", "version":"Android 2.2"}
Classes:
HttpUrlReaderDemoApp - UiApplication instance
HttpResponseListener - Interface used to notify other classes about HTTP request status
HttpUrlReader - Reads the data from given url
AppMainScreen - MainScreen instance
DataParser - Parse data
DataModel - Data definition
Screenshots:
Request for data
When data retrieved successfully
Parsed data
Implementation:
HttpUrlReaderDemoApp
public class HttpUrlReaderDemoApp extends UiApplication {
public static void main(String[] args) {
HttpUrlReaderDemoApp theApp = new HttpUrlReaderDemoApp();
theApp.enterEventDispatcher();
}
public HttpUrlReaderDemoApp() {
pushScreen(new AppMainScreen("HTTP Url Reader Demo Application"));
}
}
HttpResponseListener
public interface HttpResponseListener {
public void onHttpResponseFail(String message, String url);
public void onHttpResponseSuccess(byte bytes[], String url);
}
HttpUrlReader
public class HttpUrlReader implements Runnable {
private String url;
private HttpResponseListener listener;
public HttpUrlReader(String url, HttpResponseListener listener) {
this.url = url;
this.listener = listener;
}
private String getConncetionDependentUrlSuffix() {
// Not implemented
return "";
}
private void notifySuccess(byte bytes[], String url) {
if (listener != null) {
listener.onHttpResponseSuccess(bytes, url);
}
}
private void notifyFailure(String message, String url) {
if (listener != null) {
listener.onHttpResponseFail(message, url);
}
}
private boolean isValidUrl(String url) {
return (url != null && url.length() > 0);
}
public void run() {
if (!isValidUrl(url) || listener == null) {
String message = "Invalid parameters.";
message += !isValidUrl(url) ? " Invalid url." : "";
message += (listener == null) ? " Invalid HttpResponseListerner instance."
: "";
notifyFailure(message, url);
return;
}
// update URL depending on connection type
url += DeviceInfo.isSimulator() ? ";deviceside=true"
: getConncetionDependentUrlSuffix();
// Open the connection and retrieve the data
try {
HttpConnection httpConn = (HttpConnection) Connector.open(url);
int status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK) {
InputStream input = httpConn.openInputStream();
byte[] bytes = IOUtilities.streamToBytes(input);
input.close();
notifySuccess(bytes, url);
} else {
notifyFailure("Failed to retrieve data, HTTP response code: "
+ status, url);
return;
}
httpConn.close();
} catch (Exception e) {
notifyFailure("Failed to retrieve data, Exception: ", e.toString());
return;
}
}
}
AppMainScreen
public class AppMainScreen extends MainScreen implements HttpResponseListener {
private final String URL = "http://codeincloud.tk/json_android_example.php";
public AppMainScreen(String title) {
setTitle(title);
}
private MenuItem miReadData = new MenuItem("Read data", 0, 0) {
public void run() {
requestData();
}
};
protected void makeMenu(Menu menu, int instance) {
menu.add(miReadData);
super.makeMenu(menu, instance);
}
public void close() {
super.close();
}
public void showDialog(final String message) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(message);
}
});
}
private void requestData() {
Thread urlReader = new Thread(new HttpUrlReader(URL, this));
urlReader.start();
showDialog("Request for data from\n \"" + URL + "\"\n started.");
}
public void onHttpResponseFail(String message, String url) {
showDialog("Failure Mesage:\n" + message + "\n\nUrl:\n" + url);
}
public void onHttpResponseSuccess(byte bytes[], String url) {
showDialog("Data retrived from:\n" + url + "\n\nData:\n"
+ new String(bytes));
// now parse response
DataModel dataModel = DataParser.getData(bytes);
if (dataModel == null) {
showDialog("Failed to parse data: " + new String(bytes));
} else {
showDialog("Parsed Data:\nName: " + dataModel.getName()
+ "\nVersion: " + dataModel.getVersion());
}
}
}
DataParser
public class DataParser {
private static final String NAME = "name";
private static final String VERSION = "version";
public static DataModel getData(byte data[]) {
String rawData = new String(data);
DataModel dataModel = new DataModel();
try {
JSONObject jsonObj = new JSONObject(rawData);
if (jsonObj.has(NAME)) {
dataModel.setName(jsonObj.getString(NAME));
}
if (jsonObj.has(VERSION)) {
dataModel.setVersion(jsonObj.getString(VERSION));
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return dataModel;
}
}
DataModel
public class DataModel {
private String name;
private String version;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String model) {
this.version = model;
}
}

Resources