I need to make about 60 HTTP requests.
In the first case, I did not use an asynchronous request and the speed was about 1.5 minutes.
In the second case, I used an asynchronous request and the speed did not change either and was about 1.5 minutes.
Please see my code. Maybe I'm not doing the asynchronous request correctly or is there some other way to quickly make a lot of HTTP requests?
public class Main {
public static int page = 0;
public static void main(String[] args) throws IOException {
int totalPages = Util.getTotalPages();
page = 0;
while(page < totalPages) {
// Function does not work
new GetAuctions();
page++;
}
}
}
public class Util {
public static final String API_KEY = "***";
public static final OkHttpClient httpClient = new OkHttpClient();
public static final List<JSONObject> auctions = new ArrayList<>();
public static int getTotalPages() throws IOException {
Request request = new Request.Builder().url("https://api.hypixel.net/skyblock/auctions?key=" + Util.API_KEY + "&page=0").build();
Response response = httpClient.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Error: " + response);
assert response.body() != null;
String jsonData = response.body().string();
JSONObject object = new JSONObject(jsonData);
return object.getInt("totalPages");
}
}
public class GetAuctions {
public static void main(String[] args) throws Exception {
new GetAuctions().run();
}
public void run() throws Exception {
Request request = new Request.Builder().url("https://api.hypixel.net/skyblock/auctions?key=" + Util.API_KEY + "&page=" + Main.page).build();
Util.httpClient.newCall(request).enqueue(new Callback() {
#Override public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
assert response.body() != null;
String jsonData = response.body().string();
JSONObject object = new JSONObject(jsonData);
JSONArray array = object.getJSONArray("auctions");
for (int i=0; i<array.length(); i++) {
JSONObject jsonObject = array.getJSONObject(i);
if (jsonObject.has("bin")) {
Util.auctions.add(jsonObject);
}
}
System.out.println(Util.auctions.size());
}
});
}
}
It doesn't look like your example is asynchronous at all. Look at the example from https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/AsynchronousGet.java and try with that.
Specifically you should be calling enqueue instead of execute.
client.newCall(request).enqueue(new Callback() {
#Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(responseBody.string());
}
}
});
}
Related
textView = findViewById(R.id.textVieww);
String url = "https://zenquotes.io/api/random";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = response.body().string();
try {
JSONArray jsonarray = new JSONArray(myResponse);
for(int i=0; i<jsonarray.length(); i++) {
JSONObject obj = jsonarray.getJSONObject(i);
}
} catch (JSONException e) {
e.printStackTrace();
}
Quote.this.runOnUiThread(() ->
textView.setText(myResponse));
}
}
});
}
This is the part im stuck on i think im on the right track but not sure where to go from here im trying to get the "q" information from the returned url and the "a" information but it just outputs everything any suggestions?
What was your problem
Even when you parsed JSON string, you were still using the myResponse string in your textView.setText() method.
Continuing your code snippet
your code snippet is quite short, but i do think i can quite understand what you mean.
So let's say that we have Activity, which is called MainActivity and in that activity we have two views, one TextView called that has an id of tv_author_and_quote and one Button which has a xml id btn_request_quote.
The button has an OnClickListener which calls method requestForQuote().
Our onCreate + the variables of Button and TextView looks like this:
TextView tvAuthorAndQuote;
Button btnRequestQuote;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvAuthorAndQuote = findViewById(R.id.tv_author_and_quote);
btnRequestQuote = findViewById(R.id.btn_request_quote);
btnRequestQuote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
requestForQuote();
}
});
}
And then we have a code itself for method requestForQuote():
public void requestForQuote() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = Objects.requireNonNull(response.body()).string();
String myFormattedQuote = "";
try {
JSONArray jsonarray = new JSONArray(myResponse);
for(int i=0; i<jsonarray.length(); i++) {
JSONObject obj = jsonarray.getJSONObject(i);
String quote = obj.getString("q");
String author = obj.getString("a");
Log.d(TAG, "onResponse: quote:" + quote);
Log.d(TAG, "onResponse: author:" + author);
myFormattedQuote = author + ": " + quote;
}
} catch (JSONException e) {
e.printStackTrace();
}
final String myFinalQuote = myFormattedQuote;
MainActivity.this.runOnUiThread(() -> {
if (!myFinalQuote.equals("")) {
tvAuthorAndQuote.setText(myFinalQuote);
} else {
tvAuthorAndQuote.setText(myResponse);
}
});
}
}
});
}
The code above basically uses your existing solution, but instead of setting the text of textView with myResponse string, it parses the json array and gets a quote and an author from it. Then it just logs it (just for testing purposes), then it constructs the string which gets displayed to the if there is any, otherwise it prints the response. That it is.
Using Gson library
import it into your gradle dependecies
implementation 'com.google.code.gson:gson:2.8.7'
Write short "holder" class called Quote
public class Quote {
public Quote() {
}
String q;
String a;
String h;
public String getQ() {
return q;
}
public void setQ(String q) {
this.q = q;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getH() {
return h;
}
public void setH(String h) {
this.h = h;
}
#NonNull
#NotNull
#Override
public String toString() {
return a + ": " + q;
}
}
Then the requestForQuote() method could look something like this:
public void requestForQuoteWithGson() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = Objects.requireNonNull(response.body()).string();
Type listType = new TypeToken<ArrayList<Quote>>(){}.getType();
List<Quote> yourClassList = new Gson().fromJson(myResponse, listType);
if (yourClassList != null && yourClassList.size() > 0) {
final Quote quote = yourClassList.get(0);
if (quote != null) {
myQuotes.add(quote);
MainActivity.this.runOnUiThread(() ->
tvAuthorAndQuote.setText(quote.toString())
);
}
}
}
}
});
}
I am trying to write unit tests for repository while using MVVM pattern in android.
What i have is a repository which fetched data from the network using retrofit
public class ValidateCbuRepository {
private static ValidateCbuRepository single_instance = null;
private MutableLiveData<CBUValidationImageResponse> data = new MutableLiveData<>();
public static ValidateCbuRepository getInstance() {
if (single_instance == null)
single_instance = new ValidateCbuRepository();
return single_instance;
}
public MutableLiveData<CBUValidationImageResponse> processImage(String encodedString) {
JsonObject postParam = new JsonObject();
postParam.addProperty("image", encodedString);
Api service = RetrofitClientInstance.getRetrofitInstance().create(Api.class);
data.setValue(null);
HttpUrl httpUrl = HttpUrl.parse("some url");
Call<CBUValidationImageResponse> responseCall = service.getProcessedImage_cbu_validation(httpUrl.toString(),postParam);
responseCall.enqueue(new Callback<CBUValidationImageResponse>() {
#Override
public void onResponse(Call<CBUValidationImageResponse> call, Response<CBUValidationImageResponse> response) {
if(response.isSuccessful()) {
CBUValidationImageResponse res = response.body();
CBUValidationImageResponse cbuValidationImageResponse = res;
Log.i("CBU response ",""+cbuValidationImageResponse.toString());
cbuValidationImageResponse.setSuccess(true);
cbuValidationImageResponse.setShowProgres(false);
cbuValidationImageResponse.setError(false);
data.setValue(cbuValidationImageResponse);
}
}
#Override
public void onFailure(Call<CBUValidationImageResponse> call, Throwable t) {
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setError(true);
cbuValidationImageResponse.setShowProgres(false);
data.setValue(cbuValidationImageResponse);
t.printStackTrace();
}
});
return data;
}
}
The unit test part
#Mock
private Observer<CBUValidationImageResponse> observer;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testApiResponse_success() {
Api mockedApiInterface = Mockito.mock(Api.class);
Call<CBUValidationImageResponse> mockedCall = Mockito.mock(Call.class);
Mockito.when(mockedApiInterface.getProcessedImage_cbu_validation(any(),any())).thenReturn(mockedCall);
try {
Mockito.doAnswer(new Answer() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Callback<CBUValidationImageResponse> callback = invocation.getArgument(0);
CBUValidationImageResponse cbuValidationImageResponse = new CBUValidationImageResponse();
cbuValidationImageResponse.setCBU_code("some code");
cbuValidationImageResponse.setHeight(7);
cbuValidationImageResponse.setBreadth(7);
cbuValidationImageResponse.setLength(7);
callback.onResponse(mockedCall, Response.success(cbuValidationImageResponse));
// or callback.onResponse(mockedCall, Response.error(404. ...);
// or callback.onFailure(mockedCall, new IOException());
return null;
}
}).when(mockedCall).enqueue(any(Callback.class));
ValidateCbuRepository validateCbuRepository = new ValidateCbuRepository();
String encodedString= "";
validateCbuRepository.processImage(encodedString).observeForever(observer);
Getting a null pointer exception at validateCbuRepository.processImage(encodedString).observeForever(observer). Next step is to verify the observer.
I expect the test to pass. What am i doing wrong here?. I did something similar foe view model and the test passes with 100% code coverage.
The retrofit call is asynchronous. Is that the reason why it fails?
Edit : It seems livedata is null while testing causing NPE.
I create Java Application using HttpServer as bellow:
public class Application
{
public static void main(String args[])
{
HttpServer httpPaymentServer;
httpPaymentServer = HttpServer.create(new InetSocketAddress(Config.portPayment), 0);
httpPaymentServer.createContext("/json", new Payment("json"));
}
public class Payment implements HttpHandler
{
public Payment(String dataType)
{
}
public void handle(HttpExchange httpExchange) throws IOException
{
String body = "";
if(httpExchange.getRequestMethod().equalsIgnoreCase("POST"))
{
try
{
Headers requestHeaders = httpExchange.getRequestHeaders();
Set<Map.Entry<String, List<String>>> entries = requestHeaders.entrySet();
int contentLength = Integer.parseInt(requestHeaders.getFirst("Content-length"));
InputStream inputStream = httpExchange.getRequestBody();
byte[] postData = new byte[contentLength];
int length = inputStream.read(postData, 0, contentLength);
if(length < contentLength)
{
}
else
{
String fullBody = new String(postData);
Map<String, String> query = Utility.splitQuery(fullBody);
body = query.getOrDefault("data", "").toString();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
}
On my server (Centos 7), on the first request, it is no problem. But on next request, not all of the request body can be read.
But on my PC (Windows 10) no problem.
What is the problem.
For your InputStream you call read only once - it may not return all the data. That data may even be not received at that time.
Instead you should call read in a loop until you get all the bytes (when you reach end of stream read returns -1). Or use one of the approaches suggested here How to read / convert an InputStream into a String in Java?
Thank you. This work for me
public void handle(HttpExchange httpExchange) throws IOException
{
String body = "";
if(httpExchange.getRequestMethod().equalsIgnoreCase("POST"))
{
try
{
Headers requestHeaders = httpExchange.getRequestHeaders();
Set<Map.Entry<String, List<String>>> entries = requestHeaders.entrySet();
int contentLength = Integer.parseInt(requestHeaders.getFirst("Content-length"));
InputStream inputStream = httpExchange.getRequestBody();
int j;
String fullBody = "";
for(j = 0; j < contentLength; j++)
{
byte b = (byte) httpExchange.getRequestBody().read();
fullBody += String.format("%c", b);
}
Map<String, String> query = Utility.splitQuery(fullBody);
body = query.getOrDefault("data", "").toString();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The error happened when i upload a 115KB image file to server.(the most answer of stackoverflow is about download the file.I do not know if it is the same to those)
the error information is below:
onFailure : java.net.ProtocolException: unexpected end of stream
Relevant Code:
public void upLoadImageFile(String uploadUrl, File file, Map<String, String> maps, final HWUploadListener listener) {
final CallbackHandler handler = new CallbackHandler(listener);
try {
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
if (maps == null) {
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\";filename=\"file.jpg\""),
RequestBody.create(MediaType.parse("image/jpeg"), file)).build();
} else {
for (String key : maps.keySet()) {
builder.addFormDataPart(key, maps.get(key));
}
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\";filename=" + file.getName()), RequestBody.create(MediaType.parse("image/jpeg"), file)
);
}
RequestBody body = builder.build();
final Request request = new Request.Builder().url(uploadUrl).post(body).build();
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
UtilUtils1.log("HuowuSdk", "onFailure :" + e.toString());
handler.uploadFailure(e.toString());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String result = response.body().string();
handler.uploadSuccess(result);
} else {
handler.uploadFailure(response.message());
}
}
});
} catch (Exception e) {
UtilUtils1.log("HuowuSdk", e.toString());
handler.uploadError(e.toString());
}
}
Appreciate your answer!!
Here in this line below you have to increase write timeout because while uploading your write timeout expires that may be the reason so in below line increase writeTimeout limit:
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
I've implemented a asynchronous Servlet, which needs to parse the body of request and store the parsed result in cache. Should I implement the parseBody() function in Servlet or implement a new class, which will do the parsing? What is the best practice?
Here is my current code snippet:
public class DocFeedServlet extends FeedServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(DocFeedServlet.class);
private static final ObjectMapper OBJECTMAPPER = new ObjectMapper();
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
final AsyncContext asyncContext = req.startAsync();
asyncContext.start(new Runnable() {
#Override
public void run() {
String bodyStr = getBody(req);
if (bodyStr.isEmpty()) {
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
asyncContext.complete();
return;
}
int ignoreTime = Integer.parseInt(req.getParameter(Constant.PARAM_IGNORE_TIME));
List<MockDocCacheKeyVal> mockDocCacheKeyVals = new ArrayList<>();
List<String> docUpdateFields = new ArrayList<>();
List<List<String>> docKeepFields = new ArrayList<List<String>>();
List<String> uuidsToRemove = new ArrayList<>();
int parseRet = parseBody(bodyStr, mockDocCacheKeyVals, docUpdateFields, docKeepFields, uuidsToRemove, ignoreTime);
if (parseRet != 0) {
resp.setStatus(HttpServletResponse.SC_OK);
} else {
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
asyncContext.complete();
}
});
}
protected int parseBody(String body, List<MockDocCacheKeyVal> mockDocCacheKeyVals, List<String> docUpdateFields, List<List<String>> docKeepFields, List<String> uuidsToRemove, int ignoreTime) {
try {
ObjectReader reader = OBJECTMAPPER.reader(new TypeReference<List<Document>>() { });
List<Document> documents = reader.readValue(body);
for (Document doc : documents) {
if (doc.getAction() != null && doc.getAction().equalsIgnoreCase(Constant.DOC_FEED_ACTION_DELETE)) {
if (doc.getUuid() != null) {
uuidsToRemove.add(doc.getUuid());
}
continue;
}
if (doc.getA() != null) {
} else if (doc.getB() != null) {
} else {
DocumentUtils.pruneWeightSet(doc.getC(), cPruneSize);
DocumentUtils.pruneWeightSet(doc.getD(), dPruneSize);
DocumentUtils.pruneWeightSet(doc.getE(), ePruneSize);
}
}
return documents.size();
} catch (Exception e) {
LOGGER.error(e.getMessage());
}
return 0;
}
}
Thanks.
Asynchronous Request Body read is accomplished with the HttpServletRequest.getInputStream().setReadListener(ReadListener) concepts introduced in Servlet 3.1
You will only read based on events from your ReadListener, and you will only read enough to not block. (so no reading multi-megabyte buffers!).
This API is what you are looking for, however there be land mines here, so be sure you fully understand the API before you finish it.