Different behavior of OkHttp3 on Linux and Windows - java

I have some code which works ok on windows platform, however, the code gives a different behavior on Linux.
I have used the following code to submit a request to an HTTP server to get some messages. what I have done as follows
deploy the code on my local windows machine, then trigger a request and get the server response.
parameters:
{"articleid":"","endtime":"2019-10-29T18:00:00","starttime":"2019-10-29T16:00:00","areaid":"","title":"","pageIndex":"1"}
server response:
{"result":1,"errorcode":"","message":"","pageindex":1,"nextpage":2,"pagesize":100,"data":[...
some data here ...]}
deploy the code on a Linux server, trigger the request with the same parameters in step 1, however, the server response is different.
parameters:
{"articleid":"","endtime":"2019-10-29T18:00:00","starttime":"2019-10-29T16:00:00","areaid":"","title":"","pageIndex":"1"}
server response:
{"result":1,"errorcode":"","message":"","pageindex":1,"nextpage":null,"pagesize":0,"data":[]}
We have looked through the code but can not find what causes the different behaviors.
I suppose there may exist one/some java class files with the same name in different jars, and windows/Linux load different class files then cause the problem, but after looking through the jar file, I also have no ideas. the okhttp related jar files are as following:
okhttp-3.10.0.jar
okio-1.14.0.jar
netty-codec-http-4.1.31.Final.jar
httpcore-nio-4.4.10.jar
httpcore-4.4.10.jar
httpclient-4.5.6.jar
httpasyncclient-4.1.4.jar
public static String okHttpPost(String requestUrl,Map<String,String> map,String orgId,String taskID) throws IOException {
String exceptionMessage="";
String responseResult="";
try {
FormBody.Builder newFormBody = new FormBody.Builder();
Set<String> keys = map.keySet();
for(String key:keys){
newFormBody.add(key,map.get(key));
}
RequestBody body = newFormBody.build();
log.info("server url : "+requestUrl+";paramters:"+new ObjectMapper().writeValueAsString(map));
Request request = new Request.Builder()
.url(requestUrl)
.post(body)
.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
if (response.code() != 200) {
exceptionMessage = "request failed, taskID:" + taskID + "orgid:" + orgId + "response mesage:"+response.toString();
log.info(exceptionMessage);
}
responseResult = response.body().string();
log.info("server url : " + requestUrl + ", reponse messages:"+responseResult);
return responseResult;
} catch (IOException e) {
e.printStackTrace();
}finally {
if (!responseResult.contains("token")) {
do some thing;
}
}
return null;
}
Can give any ideas on why the same code behaviors different on windows and Linux platform?
How to change the code to let it works well on Linux?

Related

How to create a POST request for a file upload

I am currently uploading some files via the API from Opentext Content Server 21.2. I have already implemented most of the method calls. I can upload files, create folders, delete files and so on. However, I am currently failing with the file upload. Mainly only PDFs or images (Jpeg, PNG etc.) should be uploaded.
The current API documentation can be found here:
https://developer.opentext.com/apis/14ba85a7-4693-48d3-8c93-9214c663edd2/d7540c64-7da2-4554-9966-069c56a9341d/a35ce271-5bb7-4bcf-b672-0c8bcf747091#operation/createNode2
My current code looks like this:
#Override
public ClientResponse saveFile(String sessionId, String folderId, File document, String filename) throws DmsException, IOException {
client = ClientHelper.createClient();
client.addFilter(new LoggingFilter());
Builder webResource = client.resource(getRestUri() + REST_CREATE).header("otcsticket", sessionId);
MultivaluedMap<String, String> postBody = new MultivaluedMapImpl();
postBody.add("name", filename);
postBody.add("type", TYPE_FILE);
postBody.add("parent_id", folderId);
postBody.add("file", FileUtils.readFileToString(document, StandardCharsets.UTF_8));
ClientResponse response = webResource.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).post(ClientResponse.class, postBody);
if (response.getStatus() == 200) {
return response;
} else {
System.out.println(response.toString());
System.out.println(response.getEntity(String.class));
throw new DmsException("XYZ-001", XYZ: HTTP-CODE "
+ response.getStatusInfo().getStatusCode() + " - " + response.getStatusInfo().getReasonPhrase());
}
}
The code returns a HTTP-STATUS 200 OK. However, no file is created, but a folder is created. The API description for this is identical only with the difference that no file is passed. Therefore I assume that the file parameter is skipped.
PS: I am using Jersey 1.19.1
I ask for help and am grateful for any answer

How to perform system property operations in WildFly via REST?

This documentation states that one can perform certain operations for a WildFly server via REST: https://docs.jboss.org/author/display/WFLY10/The%20HTTP%20management%20API.html
However, there is no example how to add/remove/read a system property. I have no idea how the HTTP body has to look for those calls.
The answer of the following StackOverflow question says that the class SimpleOperation used in the example does not really exist: Wildfly 10 management Rest API
I would like to do the following operations:
/system-property=BLA:remove
/system-property=BLA:add(value="1,2,3,4")
and to read it.
How can I perform these operations via REST with the WildFly HTTP management API? Ideally, I would use a Java API if there was one.
With the org.wildfly.core:wildfly-controller-client API you could do something like this:
try (ModelControllerClient client = ModelControllerClient.Factory.create("localhost", 9990)) {
final ModelNode address = Operations.createAddress("system-property", "test.property");
ModelNode op = Operations.createRemoveOperation(address);
ModelNode result = client.execute(op);
if (!Operations.isSuccessfulOutcome(result)) {
throw new RuntimeException("Failed to remove property: " + Operations.getFailureDescription(result).asString());
}
op = Operations.createAddOperation(address);
op.get("value").set("test-value");
result = client.execute(op);
if (!Operations.isSuccessfulOutcome(result)) {
throw new RuntimeException("Failed to add property: " + Operations.getFailureDescription(result).asString());
}
}
You can use the REST API too, however you'll need to have a way to do digest authentication.
Client client = null;
try {
final JsonObject json = Json.createObjectBuilder()
.add("address", Json.createArrayBuilder()
.add("system-property")
.add("test.property.2"))
.add("operation", "add")
.add("value", "test-value")
.build();
client = ClientBuilder.newClient();
final Response response = client.target("http://localhost:9990/management/")
.request()
.header(HttpHeaders.AUTHORIZATION, "Digest <settings>")
.post(Entity.json(json));
System.out.println(response.getStatusInfo());
} finally {
if (client != null) client.close();
}

How to really clean the property from System class?

I am trying to do a request on a REST service on two different web servers. On both servers it is necessary to present a keyStore, obviously they are different keyStores. Running the code below I am having successful only in the first request (for the test environment), but when I do the second request (for the staging environment), the request presents the first keyStore, in this case testKeyStore.jks.
I tried to clear the keyStore property of the System class and set a new value. In println it is displayed as if the property was changed, but when I see the log on the staging server, the testKeyStore was presented, not the stagingKeyStore.
If I change de order, first STAGING and then TEST, in doStaff() method, the second request fail because the stagingKeyStore is present in the test-server.
Is there some solution to solve this problem?
public void doStaff() {
callServer("TEST");
callServer("STAGING");
}
private void callServer(String enviroment) {
String url = "";
if ("TEST".equalsIgnoreCase(enviroment))
url = "https://test-server/dostaff";
else if("STAGING".equalsIgnoreCase(enviroment))
url = "https://staging-server/dostaff";
try {
System.clearProperty("javax.net.ssl.keyStore");
System.out.println(enviroment + " -> " + System.getProperty("javax.net.ssl.keyStore"));
if ("TEST".equalsIgnoreCase(enviroment)) {
System.setProperty("javax.net.ssl.keyStore", "C:\\temp\\testKeyStore.jks");
} else if("STAGING".equalsIgnoreCase(enviroment)) {
System.setProperty("javax.net.ssl.keyStore", "C:\\temp\\stagingKeyStore.jks");
}
System.out.println(enviroment + " -> " + System.getProperty("javax.net.ssl.keyStore"));
//...
} catch (Exception e) {
e.printStackTrace();
}
}

Get URL content with Basic Authentication with Java and async-http-client

I am writing a Java lib and need to perform a request to a URL - currently using async-http-client from ning - and fetch its content. So I have a get method that returns a String
of the content of the fetched document. However, to be able to get it, I must perform a HTTP basic authentication and I'm not succeeding at this in my Java code:
public String get(String token) throws IOException {
String fetchURL = "https://www.eventick.com.br/api/v1/events/492";
try {
String encoded = URLEncoder.encode(token + ":", "UTF-8");
return this.asyncClient.prepareGet(fetchURL)
.addHeader("Authorization", "Basic " + encoded).execute().get().getResponseBody();
}
}
The code returns no error, it just doesn't fetch the URL because the authentication header is not being properly set, somehow.
With curl -u option I can easily get what I want:
curl https://www.eventick.com.br/api/v1/events/492 -u 'xxxxxxxxxxxxxxx:'
Returns:
{"events":[{"id":492,"title":"Festa da Bagaceira","venue":"Mangueirão de Paulista",
"slug":"bagaceira-fest", "start_at":"2012-07-29T16:00:00-03:00",
"links":{"tickets":[{"id":738,"name":"Normal"}]}}]}
How can this be done in Java? With the async-http-client lib? Or if you know how to do it using another way..
Any help is welcome!
You're close. You need to base 64 encode rather than URL encode. That is, you need
String encoded = Base64.getEncoder().encodeToString((user + ':' + password).getBytes(StandardCharsets.UTF_8));
rather than
String encoded = URLEncoder.encode(token + ":", "UTF-8");
(Note that for the benefit of others, since I'm answering 2 years later, in my answer I'm using the more standard "user:password" whereas your question has "token:". If "token:" is what you needed, then stick with that. But maybe that was part of the problem, too?)
Here is a short, self-contained, correct example
package so17380731;
import com.ning.http.client.AsyncHttpClient;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.ws.rs.core.HttpHeaders;
public class BasicAuth {
public static void main(String... args) throws Exception {
try(AsyncHttpClient asyncClient = new AsyncHttpClient()) {
final String user = "StackOverflow";
final String password = "17380731";
final String fetchURL = "https://www.eventick.com.br/api/v1/events/492";
final String encoded = Base64.getEncoder().encodeToString((user + ':' + password).getBytes(StandardCharsets.UTF_8));
final String body = asyncClient
.prepareGet(fetchURL)
.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + encoded)
.execute()
.get()
.getResponseBody(StandardCharsets.UTF_8.name());
System.out.println(body);
}
}
}
The documentation is very sketchy, but I think that you need to use a RequestBuilder following the pattern shown in the Request javadoc:
Request r = new RequestBuilder().setUrl("url")
.setRealm((new Realm.RealmBuilder()).setPrincipal(user)
.setPassword(admin)
.setRealmName("MyRealm")
.setScheme(Realm.AuthScheme.DIGEST).build());
r.execute();
(Obviously, this example is not Basic Auth, but there are clues as to how you would do it.)
FWIW, one problem with your current code is that a Basic Auth header uses base64 encoding not URL encoding; see the RFC2617 for details.
basically, do it like this:
BoundRequestBuilder request = asyncHttpClient
.preparePost(getUrl())
.setHeader("Accept", "application/json")
.setHeader("Content-Type", "application/json")
.setRealm(org.asynchttpclient.Dsl.basicAuthRealm(getUser(), getPassword()))
// ^^^^^^^^^^^-- this is the important part
.setBody(json);
Test can be found here:
https://github.com/AsyncHttpClient/async-http-client/blob/master/client/src/test/java/org/asynchttpclient/BasicAuthTest.java
This is also another way of adding Basic Authorization,
you can use any of two the classes for your use AsyncHttpClient,HttpClient,in this case i will use AsyncHttpClient
AsyncHttpClient client=new AsyncHttpClient();
Request request = client.prepareGet("https://www.eventick.com.br/api/v1/events/492").
setHeader("Content-Type","application/json")
.setHeader("Authorization","Basic b2pAbml1LXR2LmNvbTpnMGFRNzVDUnhzQ0ZleFQ=")
.setBody(jsonObjectRepresentation.toString()).build();
after adding header part
ListenableFuture<Response> r = null;
//ListenableFuture<Integer> f= null;
try{
r = client.executeRequest(request);
System.out.println(r.get().getResponseBody());
}catch(IOException e){
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
client.close();
it may be useful for you

Async urlfetch Http post on App engine using Future

My goal is to rapidly make posts to a server from appengine(java). I am attempting to do this using UrlFetchService.fetchAsync. I have been basing my code after this blog post. I have been able to make the request using the code below, however I get some strange behavior:
private void futureRequests() {
URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
URL url = new URL("https://someserver.com");
FetchOptions fetchOptions = FetchOptions.Builder.withDefaults();
fetchOptions.doNotValidateCertificate();
fetchOptions.setDeadline(60D);
ArrayList<Future<HTTPResponse>> asyncResponses = new ArrayList<Future<HTTPResponse>>();
for (int i = 0; i < postDatas.size(); i++) {
HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST, fetchOptions);
request.setPayload(postDatas.get(i).getBytes(UTF8));
HTTPHeader header = new HTTPHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
request.setHeader(header);
header = new HTTPHeader("Content-Length", Integer.toString(postDatas.get(i).getBytes().length));
request.setHeader(header);
header = new HTTPHeader("Authorization", "auth=" + authToken);
request.setHeader(header);
Future<HTTPResponse> responseFuture = fetcher.fetchAsync(request);
asyncResponses.add(responseFuture);
}
for (Future<HTTPResponse> future : asyncResponses) {
HTTPResponse response;
try {
response = future.get();
int responseCode = response.getResponseCode();
resp.getWriter().println("response: " + responseCode);
logger.warning("Response: " + responseCode);
} catch (Exception e) {
}
}
}
The strange behavior is that I get duplicate posts on the server, and according to my appstats page I use 10x-20x more urlFetches than what was added with the code above. Below is my appstats screen:
There are more urlFetch calls that could not fit on the screen. It appears that the requests are still completing in a synchronous fashion(circled items), but there are many urlFetches that appear to go on at the same time. My question is how am I getting all this calls to urlFetch when I only had 14 Future ?? Could the server be giving an error or 503 and urlFetch retrying until it goes through? And how can I be getting 2 posts for each request??
I understand that I could use the task queue to do asyc request, however I am dealing with a relatively low number of request(20-100) and the cold start time of ramping up another instance would probably make this not a good option for my situation. Can anyone explain this behavior or have experience with this?
This was simply a mistake in my code that was causing my app to make more request than I thought..

Categories

Resources