diff --git a/12-http-rest/snippets/AsyncSongLyricsRetriever.java b/12-http-rest/snippets/AsyncSongLyricsRetriever.java new file mode 100644 index 00000000..0c3f4e94 --- /dev/null +++ b/12-http-rest/snippets/AsyncSongLyricsRetriever.java @@ -0,0 +1,43 @@ +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class AsyncSongLyricsRetriever { + + public static void getLyricsAsync(String artist, String song) throws Exception { + ExecutorService executor = Executors.newCachedThreadPool(); + HttpClient client = + HttpClient.newBuilder().executor(executor).build(); // configure custom executor or use the default + + URI uri = new URI("https", "api.lyrics.ovh", "/v1/" + artist + "/" + song, null); + System.out.println(uri); + + HttpRequest request = HttpRequest.newBuilder().uri(uri).build(); + System.out.println("Thread calling sendAsync(): " + Thread.currentThread().getName()); + + CompletableFuture future = client.sendAsync(request, BodyHandlers.ofString()) + .thenApply(x -> { + System.out.println("Thread executing thenApply(): " + Thread.currentThread().getName()); + return x.body(); + }); + future.thenAcceptAsync(x -> { + System.out.println("Thread executing thenAccept(): " + Thread.currentThread().getName()); + System.out.println(x); + }, executor); + System.out.println("The HTTP call is fired. Performing some other work..."); + + // wait the async HTTP call which is executed in daemon thread + future.join(); + + executor.shutdown(); + } + + public static void main(String... args) throws Exception { + AsyncSongLyricsRetriever.getLyricsAsync("Billie Eilish", "Lovely"); + } + +} diff --git a/12-http-rest/snippets/CollectionsMain.java b/12-http-rest/snippets/CollectionsMain.java new file mode 100644 index 00000000..8c237793 --- /dev/null +++ b/12-http-rest/snippets/CollectionsMain.java @@ -0,0 +1,22 @@ +import java.lang.reflect.Type; +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +public class CollectionsMain { + + public static void main(String[] args) { + List devs = List.of( + new Developer("Kelsey", 28, "Google, Inc"), + new Developer("Wesley", 20, "Google, Inc")); + + Gson gson = new Gson(); + String json = gson.toJson(devs); + + Type type = new TypeToken>(){}.getType(); + List devsAgain = gson.fromJson(json, type); + + System.out.println(devsAgain.size()); + } +} diff --git a/12-http-rest/snippets/DevManager.java b/12-http-rest/snippets/DevManager.java new file mode 100644 index 00000000..05c69d92 --- /dev/null +++ b/12-http-rest/snippets/DevManager.java @@ -0,0 +1,5 @@ +import com.google.gson.annotations.SerializedName; + +public record DevManager(String name, @SerializedName("unit") String department, int level) { + // Gson's @SerializedName annotation is also supported for record components +} diff --git a/12-http-rest/snippets/Developer.java b/12-http-rest/snippets/Developer.java new file mode 100644 index 00000000..6c2d03ed --- /dev/null +++ b/12-http-rest/snippets/Developer.java @@ -0,0 +1,40 @@ +import com.google.gson.annotations.SerializedName; + +public class Developer { + + private String name; + private int age; + @SerializedName("employer") // this sets custom name to the serialized field + private transient String company; // remove transient keyword to serialize + + public Developer(String name, int age, String company) { + this.name = name; + this.age = age; + this.company = company; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + +} diff --git a/12-http-rest/snippets/FileDownload.java b/12-http-rest/snippets/FileDownload.java new file mode 100644 index 00000000..be8844fe --- /dev/null +++ b/12-http-rest/snippets/FileDownload.java @@ -0,0 +1,21 @@ +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Path; + +public class FileDownload { + + public static void main(String... args) throws Exception { + var url = "https://www.7-zip.org/a/7z1806-x64.exe"; + + var client = HttpClient.newBuilder().build(); + var request = HttpRequest.newBuilder().uri(URI.create(url)).build(); + + Path localFile = Path.of("7z.exe"); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofFile(localFile)); + } + +} diff --git a/12-http-rest/snippets/FromJsonMain.java b/12-http-rest/snippets/FromJsonMain.java new file mode 100644 index 00000000..c0b81af2 --- /dev/null +++ b/12-http-rest/snippets/FromJsonMain.java @@ -0,0 +1,14 @@ +import com.google.gson.Gson; + +public class FromJsonMain { + + public static void main(String[] args) { + String json = "{\"name\": \"Wesley\", \"age\": 20 }"; + + Gson gson = new Gson(); + Developer dev = gson.fromJson(json, Developer.class); + + System.out.printf("%s, %d years old, %s%n", dev.getName(), dev.getAge(), dev.getCompany()); + } + +} diff --git a/12-http-rest/snippets/JsonReaderMain.java b/12-http-rest/snippets/JsonReaderMain.java new file mode 100644 index 00000000..886b53df --- /dev/null +++ b/12-http-rest/snippets/JsonReaderMain.java @@ -0,0 +1,17 @@ +import com.google.gson.Gson; + +import java.io.FileReader; +import java.io.IOException; +import java.util.List; + +public class JsonReaderMain { + + public static void main(String[] args) throws IOException { + Gson gson = new Gson(); + + FileReader reader = new FileReader("src/devs.json"); + List devs = gson.fromJson(reader, List.class); + System.out.println(devs); + } + +} diff --git a/12-http-rest/snippets/ParallelHttpRequestsSender.java b/12-http-rest/snippets/ParallelHttpRequestsSender.java new file mode 100644 index 00000000..135b862f --- /dev/null +++ b/12-http-rest/snippets/ParallelHttpRequestsSender.java @@ -0,0 +1,67 @@ +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ParallelHttpRequestsSender { + + private static final String WEB_SITE = "http://www.google.com"; + private static final int PARALLEL_REQUESTS = 100; + + public long executeRequestsSync(HttpClient client, HttpRequest request) throws Exception { + // send some synchronous requests and measure time + long startTime = System.currentTimeMillis(); + + for (int i = 0; i < PARALLEL_REQUESTS; i++) { + client.send(request, BodyHandlers.ofString()).body(); + } + + return System.currentTimeMillis() - startTime; + } + + public long executeRequestsAsync(HttpClient client, HttpRequest request) { + List> futures = new ArrayList<>(); + + // send some asynchronous requests and measure time + long startTime = System.currentTimeMillis(); + + for (int i = 0; i < 100; i++) { + futures.add(client.sendAsync(request, BodyHandlers.ofString()).thenApply(x -> { + System.out.println("thenApply() thread: " + Thread.currentThread().getName()); + return x.body(); + })); + } + + // uncomment to dump responses to console + // futures.stream().map(f -> f.join()).forEach(System.out::println); + + // wait for all futures to complete + CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join(); + + return System.currentTimeMillis() - startTime; + } + + public static void main(String... args) throws Exception { + ExecutorService executor = Executors.newCachedThreadPool(); + HttpClient client = + HttpClient.newBuilder().executor(executor).build(); // configure custom executor or use the default + + // build a request + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(WEB_SITE)).build(); + + var sender = new ParallelHttpRequestsSender(); + + long syncExecutionTime = sender.executeRequestsSync(client, request); + long asyncExecutionTime = sender.executeRequestsAsync(client, request); + + System.out.println("Async: " + asyncExecutionTime + " Sync: " + syncExecutionTime); + + executor.shutdown(); + } + +} diff --git a/12-http-rest/snippets/SimpleHttpCaller.java b/12-http-rest/snippets/SimpleHttpCaller.java new file mode 100644 index 00000000..7e518c17 --- /dev/null +++ b/12-http-rest/snippets/SimpleHttpCaller.java @@ -0,0 +1,32 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; + +public class SimpleHttpCaller { + + private static final String HOST = "google.com"; + private static final int HTTP_PORT = 80; + private static final String HTTP_REQUEST = "GET / HTTP/1.1" + System.lineSeparator(); + + public static void main(String[] args) { + + try (Socket socket = new Socket(HOST, HTTP_PORT); + PrintWriter writer = new PrintWriter(socket.getOutputStream(), true); // autoflush on + BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { + + writer.println(HTTP_REQUEST); + + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + + } catch (IOException e) { + throw new RuntimeException("Could not access web site", e); + } + + } + +} diff --git a/12-http-rest/snippets/SyncSongLyricsRetriever.java b/12-http-rest/snippets/SyncSongLyricsRetriever.java new file mode 100644 index 00000000..b83a891b --- /dev/null +++ b/12-http-rest/snippets/SyncSongLyricsRetriever.java @@ -0,0 +1,22 @@ +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; + +public class SyncSongLyricsRetriever { + + public String getLyricsSync(String artist, String song) throws Exception { + HttpClient client = HttpClient.newBuilder().build(); + + URI uri = new URI("https", "api.lyrics.ovh", "/v1/" + artist + "/" + song, null); + System.out.println(uri); + + HttpRequest request = HttpRequest.newBuilder().uri(uri).build(); + return client.send(request, BodyHandlers.ofString()).body(); + } + + public static void main(String... args) throws Exception { + System.out.println(new SyncSongLyricsRetriever().getLyricsSync("Billie Eilish", "Lovely")); + } + +} diff --git a/12-http-rest/snippets/TextToSpeech.java b/12-http-rest/snippets/TextToSpeech.java new file mode 100644 index 00000000..132adb57 --- /dev/null +++ b/12-http-rest/snippets/TextToSpeech.java @@ -0,0 +1,29 @@ +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.file.Path; + +public class TextToSpeech { + + // signup here to get your API key: https://account.cloudmersive.com/signup + private static final String API_KEY = "YOUR_API_KEY_HERE"; + + private static final String TEXT = + "\"I was skeptical about Java 21 in the beginning but I am starting to appreciate it. \""; + + public static void main(String... args) throws Exception { + HttpClient client = HttpClient.newHttpClient(); + + URI uri = new URI("https", "api.cloudmersive.com", "/speech/speak/text/basicVoice/wav", null); + HttpRequest request = HttpRequest.newBuilder() + .header("Apikey", API_KEY) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(TEXT)) + .uri(uri) + .build(); + + client.send(request, BodyHandlers.ofFile(Path.of("speech.wav"))); + } + +} diff --git a/12-http-rest/snippets/ToJsonMain.java b/12-http-rest/snippets/ToJsonMain.java new file mode 100644 index 00000000..20948125 --- /dev/null +++ b/12-http-rest/snippets/ToJsonMain.java @@ -0,0 +1,21 @@ +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class ToJsonMain { + + public static void main(String[] args) { + Developer dev = new Developer("Kelsey", 28, "Google, Inc"); + DevManager devManager = new DevManager("Mike", "Google AI R&D", 7); + + Gson gson = new Gson(); + String devJson = gson.toJson(dev); + System.out.println(devJson); + + // Gson version 2.10 and later also supports records + // Note that the & character in the department value would be replaced by default with its unicode escape, \u0026 + Gson gsonNonEscaping = new GsonBuilder().disableHtmlEscaping().create(); + String devManagerJson = gsonNonEscaping.toJson(devManager); + System.out.println(devManagerJson); + } + +} diff --git a/12-http-rest/snippets/VirusScanner.java b/12-http-rest/snippets/VirusScanner.java new file mode 100644 index 00000000..566fca19 --- /dev/null +++ b/12-http-rest/snippets/VirusScanner.java @@ -0,0 +1,86 @@ +import com.google.gson.Gson; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; + +public class VirusScanner { + + // signup here to get your API key: https://developers.virustotal.com/v3.0/reference#getting-started + private static final String VIRUS_TOTAL_API_KEY = "YOUR_API_KEY_HERE"; + + public static void main(String[] args) throws Exception { + + var client = HttpClient.newBuilder().build(); + + Path localFile = Paths.get("7z.exe"); + + Map data = new LinkedHashMap<>(); + data.put("apikey", VIRUS_TOTAL_API_KEY); + data.put("file", localFile); + String boundary = new BigInteger(256, new Random()).toString(); + + var request = HttpRequest.newBuilder() + .header("Content-Type", "multipart/form-data;boundary=" + boundary) + .POST(ofMimeMultipartData(data, boundary)) + .uri(URI.create("https://www.virustotal.com/vtapi/v2/file/scan")) + .build(); + + HttpResponse vtResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); + + System.out.println(vtResponse.body()); + + Gson gson = new Gson(); + Map map = gson.fromJson(vtResponse.body(), Map.class); + String resource = map.get("resource"); + String link = map.get("permalink"); + + System.out.println(link); + + URI uri = new URI("https", "www.virustotal.com", "/vtapi/v2/file/report", + "apikey=" + VIRUS_TOTAL_API_KEY + "&resource=" + resource, null); + + HttpResponse status = client.send(HttpRequest.newBuilder(uri).build(), + HttpResponse.BodyHandlers.ofString()); + + System.out.println(status.body()); + } + + public static HttpRequest.BodyPublisher ofMimeMultipartData(Map data, + String boundary) throws IOException { + var byteArrays = new ArrayList(); + byte[] separator = ("--" + boundary + System.lineSeparator() + "Content-Disposition: form-data; name=") + .getBytes(StandardCharsets.UTF_8); + for (Map.Entry entry : data.entrySet()) { + byteArrays.add(separator); + + if (entry.getValue() instanceof Path) { + var path = (Path) entry.getValue(); + String mimeType = Files.probeContentType(path); + byteArrays.add(("\"" + entry.getKey() + "\"; file=\"" + path.getFileName() + + "\"" + System.lineSeparator() + "Content-Type: " + mimeType + System.lineSeparator() + + System.lineSeparator()).getBytes(StandardCharsets.UTF_8)); + byteArrays.add(Files.readAllBytes(path)); + byteArrays.add(System.lineSeparator().getBytes(StandardCharsets.UTF_8)); + } else { + byteArrays.add(("\"" + entry.getKey() + "\"" + System.lineSeparator() + System.lineSeparator() + + entry.getValue() + System.lineSeparator()) + .getBytes(StandardCharsets.UTF_8)); + } + } + byteArrays.add(("--" + boundary + "--").getBytes(StandardCharsets.UTF_8)); + return HttpRequest.BodyPublishers.ofByteArrays(byteArrays); + } + +} diff --git a/12-http-rest/snippets/devs.json b/12-http-rest/snippets/devs.json new file mode 100644 index 00000000..ed52e261 --- /dev/null +++ b/12-http-rest/snippets/devs.json @@ -0,0 +1,10 @@ +[ + { + "name": "Kelsey", + "age": 28 + }, + { + "name": "Wesley", + "age": 20 + } +]