はじめに
ブラウザだけでなく操作リモート先端末の別アプリケーションを動かすなど、ちょっと複雑なことが必要な感じになってきました。
あーもうこれよくわかんねぇなと思い、いっそのこと画像イメージで動かせるようにしたれと作ってみました
コード内でStream使っていなかったり、いろいろやっつけているところはありますが備忘録として記録します。
イメージセレクタの関係で、Nodeは64bit OSのみ対応となります。
利用したソフトウェア
検証するときに利用したソフトウェアです。
Windowsで検証構築を構築しています。
ソフト | バージョン | 用途 |
---|---|---|
java 64bit | jdk-8.0.212.03-hotspot(AdoptOpenJDK) | |
selenium-server-standalone.jar | 3.141.59 | |
sikulixapi | 1.1.4-SNAPSHOT | Nodeのイメージセレクタ 64bit限定で動作 |
gson | 2.8.5 | jsonファイルの入出力 |
httpclient | 4.5.8 | RESTを実行 |
環境構築
Mavenで一瞬です。
Mavenファイル
Mavenファイル(展開)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>selenium-grid-extend</groupId>
<artifactId>selenium-grid-extend</artifactId>
<version>0.0.1</version>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>sonatype</id>
<name>sonatype Repository</name>
<url>http://oss.sonatype.org/content/groups/public</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>sonatype</id>
<name>sonatype Repository</name>
<url>http://oss.sonatype.org/content/groups/public</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>src</testSourceDirectory>
<resources>
<resource>
<directory>resource</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>resource</directory>
</testResource>
</testResources>
</build>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-server</artifactId>
<version>3.141.59</version>
</dependency>
<dependency>
<groupId>com.sikulix</groupId>
<artifactId>sikulixapi</artifactId>
<version>1.1.4-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.8</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
<file.encoding>UTF-8</file.encoding>
<project.build.sourceEncoding>${file.encoding}</project.build.sourceEncoding>
<project.reporting.outputEncoding>${file.encoding}</project.reporting.outputEncoding>
<maven.compiler.encoding>${file.encoding}</maven.compiler.encoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
</project>
プログラム
今回は /grid/admin/RequestToSessionMachine ~ のリクエストをURLに指定されているセッションの端末に送ります。
URL以外のやり取りは全てJSON形式で送りあいます。
要注意ですがNodeを接続しただけではsessionはありません。
ブラウザなりなんなりを操作できる状態になって初めてsessionがHubに作られます。
最終的には 以下のようにHubに送られてきたリクエストを
http://HubIP:HubPort/grid/admin/RequestToSessionMachine/session/99999XXXXX99999/extra/ImageSelector/doubleclick
『99999XXXXX99999』セッションが動いているNodeに以下のようにリクエストして結果を待ち、呼出しもとにそのまま戻します。
http://NodeIP:NodePort//extra/ImageSelector/doubleclick
Hub側プログラム
TestSession.forward で送れるようにするのがベストなんでしょうが、SeleniumBasedRequestが引数に必要だったりで、調査が面倒だったので動けば良いと思い使うのをやめました。
sessionタイムアウトの時間にかからないようにアクセス時間を更新していますが、タイムアウト時間によってはうまくいかないかもしれません。
Hub側プログラム(展開)
package selenium.extend.hub.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.openqa.grid.common.exception.GridException;
import org.openqa.grid.internal.GridRegistry;
import org.openqa.grid.internal.TestSession;
import org.openqa.grid.internal.TestSlot;
import org.openqa.grid.web.servlet.RegistryBasedServlet;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class RequestToSessionMachine extends RegistryBasedServlet {
private static final Pattern SESSION_ID_PATTERN = Pattern.compile("/grid/admin/RequestToSessionMachine/session/([^/]+).*");
public RequestToSessionMachine() {
this(null);
}
public RequestToSessionMachine(GridRegistry registry) {
super(registry);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
process(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
process(request, response);
}
protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("Start RequestToSessionMachine");
response.setContentType("application/json");
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
JsonObject json = new JsonObject();
CloseableHttpClient client = null;
CloseableHttpResponse res = null;
try {
// URLのセッションIDに紐付く情報を取得
TestSession session = getActiveTestSession(getSessionIdFromPath(request.getRequestURI()));
if (session != null) {
// セッションのアクセス時間再設定(タイムアウト時間を伸ばす)
session.setIgnoreTimeout(false);
// セッションがあるノードへの接続URL生成
TestSlot slot = session.getSlot();
URL remoteRequestURL = new URL(slot.getRemoteURL(), trimSessionPath(request.getRequestURI()));
// bodyのJsonをそのままノードにリクエスト
client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(remoteRequestURL.toURI());
httpPost.setHeader("Content-type", "application/json; charset=UTF-8");
BufferedReader bufferReaderBody = new BufferedReader(request.getReader());
StringBuilder jsonBody = new StringBuilder();
String line = null;
while ((line = bufferReaderBody.readLine()) != null) {
jsonBody.append(line);
}
StringEntity entity = new StringEntity(jsonBody.toString(), StandardCharsets.UTF_8);
httpPost.setEntity(entity);
res = client.execute(httpPost);
// セッションのアクセス時間再設定(タイムアウト時間を伸ばす)
session.setIgnoreTimeout(false);
int status = res.getStatusLine().getStatusCode();
response.setStatus(status);
if (status == 200) {
Gson gson = new Gson();
json = gson.fromJson(EntityUtils.toString(res.getEntity(), StandardCharsets.UTF_8), JsonObject.class);
} else {
json.addProperty("error", "Response Code " + status);
}
} else {
json.addProperty("error", "No Match Active Test Session for Session ID");
}
} catch (MalformedURLException e) {
e.printStackTrace();
json.addProperty("error", e.getMessage());
} catch (URISyntaxException e) {
e.printStackTrace();
json.addProperty("error", e.getMessage());
} catch (IOException e) {
e.printStackTrace();
json.addProperty("error", e.getMessage());
} finally {
try {
if (res != null) {
res.close();
}
} catch (IOException e) {
e.printStackTrace();
throw new GridException(e.getMessage());
} finally {
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
throw new GridException(e.getMessage());
}
}
}
}
System.out.println("ResponseJson:" + json.toString());
response.getWriter().print(json);
response.getWriter().close();
System.out.println("End RequestToSessionMachine");
}
private TestSession getActiveTestSession(String sessionId) {
Iterator<TestSession> itr = super.getRegistry().getActiveSessions().iterator();
TestSession session = null;
System.out.println("Active Session Size:" + super.getRegistry().getActiveSessions().size());
System.out.println("Search Session ID:" + sessionId);
while (itr.hasNext()) {
TestSession s = itr.next();
if (s.getExternalKey().getKey().equals(sessionId)) {
session = s;
break;
}
}
return session;
}
private String getSessionIdFromPath(String pathInfo) {
Matcher matcher = SESSION_ID_PATTERN.matcher(pathInfo);
if (matcher.matches()) {
return matcher.group(1);
}
throw new IllegalArgumentException("Invalid request. Session Id is not present");
}
private String trimSessionPath(String pathInfo) {
return pathInfo.replaceFirst("/grid/admin/RequestToSessionMachine/session/" + getSessionIdFromPath(pathInfo), "");
}
}
Node側プログラム
sikulixのAPIを利用し、送られてきたJSONのBase64化したイメージ画像からデスクトップ内の一致する部分をダブルクリックします。
sikulix-api 操作できる内容は公式ページをみてください。
Node側プログラム(展開)
package selenium.extend.node.servlet;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sikuli.script.FindFailed;
import org.sikuli.script.Image;
import org.sikuli.script.Screen;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class ImageSelector extends HttpServlet {
private static final String DOUBLECLICK = "/extra/ImageSelector/doubleclick";
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
JsonObject jsonResponse = null;
if (request.getRequestURI().startsWith(DOUBLECLICK)) {
jsonResponse = doubleClick(request, response);
} else {
jsonResponse = new JsonObject();
jsonResponse.addProperty("error", "Request Command is No Support");
}
response.getWriter().print(jsonResponse);
response.getWriter().close();
}
protected JsonObject doubleClick(HttpServletRequest request, HttpServletResponse response) {
System.out.println("Start ImageSelector");
response.setContentType("application/json");
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.setStatus(200);
JsonObject json = new JsonObject();
try {
// jsonから選択するbodyの情報を取り出す
BufferedReader bufferReaderBody = new BufferedReader(request.getReader());
StringBuilder jsonBody = new StringBuilder();
String line = null;
while ((line = bufferReaderBody.readLine()) != null) {
jsonBody.append(line);
}
Gson gson = new Gson();
JsonObject reqJson = gson.fromJson(jsonBody.toString(), JsonObject.class);
String imageBase64 = reqJson.get("imageBase64").getAsString();
String[] parts = imageBase64.split(",");
String imageString = parts[1];
byte[] imageByte = Base64.getDecoder().decode(imageString);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
Image image = new Image(ImageIO.read(bis));
bis.close();
// スクリーン全体からイメージを探しダブルクリック
Screen sc = new Screen();
// 待機時間設定
sc.setAutoWaitTimeout(30);
sc.doubleClick(image);
json.addProperty("info", "Done DoubleClick");
} catch (IOException e) {
e.printStackTrace();
json.addProperty("error", e.getMessage());
} catch (FindFailed e) {
e.printStackTrace();
json.addProperty("error", e.getMessage());
}
System.out.println("End ImageSelector");
return json;
}
}
jarファイル作成
Selenium Grid に読み込ませるためにjar化します。
以下のようなディレクトリ構成です。
以前の記事で作ったAllNodes系がありますが、今回は必要ありません。
以下のファイルだけjarにエクスポートします。
名前は『extend.jar』にしています。
実行環境起動
『extend.jar』をHubとNodeのディレクトリに配置して読み込ませます。
Hub起動
Hubのディレクトリ構成とコマンド(展開)
C:.
│ start-hub.bat
│
└─lib
commons-logging-1.2.jar
extend.jar
gson-2.8.5.jar
httpclient-4.5.8.jar
httpcore-4.4.11.jar
selenium-server-standalone-3.141.59.jar
java -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets "selenium.extend.hub.servlet.RequestToSessionMachine"
起動すると4行目のように RequestToSessionMachine へのアクセスパスが表示されます。
C:\selenium>java -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role hub -servlets "selenium.extend.hub.servlet.RequestToSessionMachine"
15:57:41.639 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
15:57:41.764 INFO [GridLauncherV3.lambda$buildLaunchers$5] - Launching Selenium Grid hub on port XXXX
15:57:41.858 INFO [Hub.<init>] - binding selenium.extend.hub.servlet.RequestToSessionMachine to /grid/admin/RequestToSessionMachine/*
2019-05-31 15:57:42.242:INFO::main: Logging initialized @1617ms to org.seleniumhq.jetty9.util.log.StdErrLog
15:57:42.757 INFO [Hub.start] - Selenium Grid hub is up and running
15:57:42.773 INFO [Hub.start] - Nodes should register to http://XXX.XXX.XXX.XXX:XXXX/grid/register/
15:57:42.773 INFO [Hub.start] - Clients should connect to http://XXX.XXX.XXX.XXX:XXXX/wd/hub
Node起動
Hubのディレクトリ構成とコマンド(展開)
C:.
│ chromedriver.exe
│ NodeConfigBrowser.json
│ start-node.bat
│
└─lib
commons-logging-1.2.jar
extend.jar
gson-2.8.5.jar
httpclient-4.5.8.jar
httpcore-4.4.11.jar
jna-4.5.2.jar
jna-platform-4.5.2.jar
selenium-server-standalone-3.141.59.jar
sikulix2tigervnc-2.0.0-SNAPSHOT.jar
sikulixapi-1.1.4-SNAPSHOT.jar
{
"capabilities": [
{
"platform": "WINDOWS",
"browserName": "chrome",
"maxInstances": 1,
"seleniumProtocol": "WebDriver"
}
],
"hub": "http://XXXXXXXX:XXXX/grid/register",
"register": true
}
java -Dwebdriver.chrome.driver=chromedriver.exe -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role node -servlets "selenium.extend.node.servlet.ImageSelector" -nodeConfig NodeConfigBrowser.json
起動すると4行目のように ImageSelector へのアクセスパスが表示されます。
C:\selenium-node>java -Dwebdriver.chrome.driver=chromedriver.exe -cp lib/* org.openqa.grid.selenium.GridLauncherV3 -role node -servlets "selenium.extend.node.servlet.ImageSelector" -nodeConfig NodeConfigBrowser.json
16:01:08.578 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358
16:01:08.704 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Launching a Selenium Grid node on port XXXX
16:01:09.126 INFO [SelfRegisteringRemote.addExtraServlets] - binding selenium.extend.node.servlet.ImageSelector to /extra/ImageSelector/*
2019-05-31 16:01:09.220:INFO::main: Logging initialized @981ms to org.seleniumhq.jetty9.util.log.StdErrLog
16:01:09.485 INFO [WebDriverServlet.<init>] - Initialising WebDriverServlet
16:01:09.594 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 42345
16:01:09.594 INFO [GridLauncherV3.lambda$buildLaunchers$7] - Selenium Grid node is up and ready to register to the hub
16:01:09.750 INFO [SelfRegisteringRemote$1.run] - Starting auto registration thread. Will try to register every 5000 ms.
16:01:10.237 INFO [SelfRegisteringRemote.registerToHub] - Registering the node to the hub: http://XXX.XXX.XXX.XXX:XXXX/grid/register
16:01:10.549 INFO [SelfRegisteringRemote.registerToHub] - The node is registered to the hub and ready to use
動かしてみる
curlでもなんでも良いのですが、sessionを生成させるのが手間なので以前の記事で作った環境を利用します。
テストプログラム
操作先の画像を適当なwebサービスでbase64化して、その情報をRESTしています。
base64化プログラムに組んでしまった方が今後の確認で楽だったかも。
テストプログラム(展開)
package test;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.openqa.selenium.Point;
import com.google.gson.JsonObject;
import page.Google;
public class GoogleTest extends TestBase {
@Test
public void image() throws Exception {
driver.get(Google._url);
// ブラウザを見えない位置に移動
driver.manage().window().setPosition(new Point(-2000, 0));
CloseableHttpClient client = null;
CloseableHttpResponse res = null;
JsonObject json = new JsonObject();
try {
// ノードの接続URL生成
URL remoteRequestURL = new URL("http://localhost:4444/grid/admin/RequestToSessionMachine/session/" + driver.getSessionId() + "/extra/ImageSelector/doubleclick");
// bodyのJsonをそのままノードにリクエスト
client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(remoteRequestURL.toURI());
httpPost.setHeader("Content-type", "application/json; charset=UTF-8");
json.addProperty("imageBase64", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGcAAAAhCAIAAABLIiLfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABCCSURBVGhD5Zp7nFTFlcdnhRg0xmX9Z1f5EIzRDBgREEXAx+pG4oOgKEFdld3oRjSrsAgOYHg/xSCPgJGnCArDQ1BwVeQT3U1Q9BNlJVE3BkQZgRlmpqfft2/fvs/9nqrqnp5hJIDZzR97Pqdv1606derUr06dU9UzFdFR5Ps+z9mzZ3/44YebN29es2bN/v37d+3a5ThOLpejyfO8TCazYMGCKIyiIIpCL4pcU5ZXRJJRlI1QI5rSUdREp6Lwn2KtxOiBSu9/inR3IUtGp4vWJpWOqnSl0SgLwsilBQNNP/nQBTuLXBTVYjKVIrVGDUSABlDGjx//1FNPTZgwYdq0aWPGjBk9evTPFU2ZMgVAFy5cSGVxQAZQoMDGSpCyZBwZlAnElUGq6YT4RMn0shVGx0bN+7OhFijSZdd1C4WCdq5ysm07DGWEMjoaNZCyi4MyB0BU9SfKJ0qmFxgxqCobe8CLmiJqUiP7g+/jR03ai9Ta1wCLJ3jpV1xPb9hyogYBja/ShbdLWV6MbmWiGVRbXDasEdPNivWrrjetJ0WmIyMCXLk2XQMQpRrGxd3KREzl0VwmU6TWqOXzeZCikEwSm4RAB+dSXhhoNEs4FtUJakav0Y2VXnFQWVUjqsmUS2a15JLYSZDpy+hwcSCpVDZ8OWpFammJsNBRYi1RAw5Cvi6X3A0cj3Y3di6SKKLBjwK4pFqxeg2FFRk/0q1F0mYxk1ZsLC6TPDYZefMmpPUoKhlgNKua5sqTpNZxTRfSaYlElmWVhzAd4zSsSOrRFWpm27fJhtRL6xozmVaMZs3HSSgpsabycomOrjl5aoGazgC6vHLlyg0bNjyniMPHihUrlixZsmrVqnXr1j399NMk2YLnBngn4Dp5jZrrG5+HXNcHcfyU9wAoVC0PJDUersNAgZe3iLueQ/Zwc1ZKo5Yv2CIZyjzzNjWkIFmq0goWfT+wrIyCA5aOnmeMz2ZNEgtDGbBQECUQ9gRijWgmEOlYpD2DueuYfjzUOq6BGrpwqI0bNzJGNpstOSD16AW1AwcOLF26NJFIuH4BozQzcjaXAaJc3hYYlSWwVwgLtuxQ+lJHBy0M2VYuAhrJVm7okTEoeEDGfk/nLMQK9NCi4ul5XWDBJMSKVcKgkEzG/UAHe0GBeEEvgAYyp6COIJwYYyyJNBZcABUxeSlOjYOBLkDHg10L1MCIp14BHK0U43SK0IAuX768oaFh7969+KCJXzI9yapq5V1ZTKDwMYXDrY2FTpZ8in2eXQjBIe9GWauQTmelJ6gF5Aovk4jJyH4+mU4wayywfZ/2RDzjMQuFXSKhZy728NR+pGSF44kGsMNMO6eOQUL4fsFlWMcEViWPl2eZSs4qUJMz3iqUd9S6skuUrIgbQkCzoda+Bi54LFuA/cjz5ZdfXr169datW9euXfviiy+CI7cFHG3mzJnz5s3zQ1xDcoKH/ap33lG7LIjyGRBPW+mDMhateYttyJZmCzFFcdEw8t0gm0yYw5Ew8MgyWAQKJqftVuarGCCEx+lFFUkx083ZaTvPzKkU15a5AZVa7qyVTKUbpJKlskOUsKKZLMuDmNHsy6YWRMBRVbRm9TkmatpRddQnrmGf9mHtdKQIvDcej+tcsWjRIp5CYYQ75HN2MtE4fvzIn40bLVYV6Ng4eODl0x+b7KXdwIrXHtzb+dzKdS/sOP3Ms+saMvTyFTCh49hpwc4t5HHezVu39erTr+KU9pdddc1r29/AF885+1vpFHvZRCucGtKOhh8pvHC0es+3Pd+SGuYZSGBV6CAgqAk4YJdHAzVOrDHJq+96ElgVIuxoVixjt4g5zaiZQ6+h1r5W2uE4FMZt2rSJAIejrV+//pVXXnn99dfxPtwNgTlz5gArwBr1ot2dMa3qzqE3i7VB9MePf3Xet9oNueGHal6pdWt+OWjwHWxW3ojOEoIBIpFRNge+w3yCffs/7dTlvHf3/J508MEf9q5eU43Xda28qP5Ik083GcKkAoWaVwpnQAYWEydVLVm6WOYoIhB2WA+PuH/t8y/IKHISosHyQx0chGdMmfjMyuUqRUgFzXlPhGAtoj4atdLGb4kaMJWehC2eHD5K0REfpKxjH3GNrCpaTFxS2gPn3be2Dx44IGT98v6cGcOH3dn/pquv8ZNEt6aqf7v30XGTZZZkBrIF8phG93xBol4YNDXG3n5n16V9rziSzGABzMbPpPOX9u73+WeH0E+MU7FOiPlbOYkGfpDPZLnnulmrCdRWrFgmAKEWDp2Cl6gaO7J63RYJX3hy4BW8eDZXLxoUjqMe/unGDdUatcO1RwhsGrITQA3SK4kLLVu2DMheffVVHK26upoC7obrbdu2Dadj586dO1c66IOFWMA5hAXP3n7L9R+8/V9khbGP3L373W33DLk9djgWhfYdPxr4zjsfWPmo83d6NDY56Vju4soeDwx/6BunndGh4q9mTZyCMsf3OnXqPHHSlMZUylZrbll+t26X/eS+ER07djy1/ddmz5jvFyJCZE3NHwcNHFxR0f6Mb3Z4vvpZjkE/vHlgxSkVp51+St9e/ZINZIQok08NvbVvh1MqKirOuPLqIfcN+5d777mTLJUP7QEDBjXUxW++8funVlScfurXu190SVPSZp3IMtlCMYhq2ARdtZ/KqAVquBKogQjEDjUHLgUiBUCkHk/klefs2bMJxmgkoLLBopCUlIu89KMj7p86bnw+nb780u61h2vGjps49rHpnx9q6Hflta4TsNe+872LjxBWMuGF51xw06135IJoy7K1fc/vnpHwSUDKT6ka89cdO6xat4ppZAvR2Z0uvXXwfY7TtKn6mT7dBoTpqPHQR397VsXH79ewBQ/VHj63ssubb3+A8E8f+clLW1ezKYKMg7s7NOfemzX6tjVbdn3B/dANht1y3X/ufmvEtOkjH5mu3Kl+yqP/tOn5F1xP8KIL8YJeeIL4gXIvcYpQcpT2Pk2tfU0ToHDCAB3OH+RQfO2NN954SRGux5PdOn/+fHKoHD4CDGaXMmWOrPaW6tX3DfvHTz/5eNjd9zDM8lXrb7j1rsXPbBx6z3CxJwwqe/aorau3a+L9KnvXxJKyLHbU79sXH6g/kgrsIJvmiPLR73cNGnLjmEkTa+pz53TqL/srSgeFeM9zr4vtc3e9uenO264scLDB7wJ/ZfWqGXOXppzowTH3V29YHBWyAFTvRfVOPArfm/6v181f9XpCZuV99v6bfW+8dsjDowpYhtXhoQkjblu7cj2hIJaPavOCWi4iqCrUcAmYgBrKERAA20YNV8LX9CZ98sknS5WkiFgsBpQUeGoHnDZtGlo4qCmFCjWPTOfE62rO79Jp0fy5E382gTX6ZP/By66+/sHRE8dNmBkWgtovar59Ybd0xsLAHp27xR0/zZ6piV/53UvSbsFmRfFf2enW1tc2X3JFf+LgxT0GflEDbNlM4tCFnfo6jdHvfrv9x3ffINMmtwT+omWLRo2djmTV1NHLVzwhB7J4WkKr/M6xZ8pDN6z/9/c/lRNIkDzwYc9r+gy4a5igBiLh4WlVdz+/ci0pDfkmcihhNuIsVYaaCkDHQg3Shwy8jJMHhcWLF+NZ27dv37FjB/cqHdS4V9H0+OOPy4LJGVR5M6ctUGPQ0Llt0I1nnfmNnb/+TSot8eWfh4/s0rXXf++rEWMKTrdLeh2oOcicenX53qGEnOvwtf7n96xpqN/x9n9srV4bpuKBm161dvn1g2851Gh9t+tV8SaueoR8u3flVbHPM3UH9pzevmLPO59Yyagh1njzjwbv/ugzMsjwkfdu3vIM1j+35Nnze/RusmNR9r2ZVbcvefYVQEkcrPvxkJvefO/XY38+Z/LMp4gokbX/sRFDX33xtawVpTwgi+ry6UyUE9SYm3KG40KtRPgabqXL7Mfa2lqcjtCGJyaTScpTp04VzSQ12XYeIRrgAuJJ6I566IGeF3WTwYIoZeWrJk3t3ueKuliykLOpO69r1/qGGH7QvXPXfYfrxG9Tfr8Leu4/eJBNPvrBB/6mXUXHb7YfeteQHPE0jCov7Ldv32FJFfl0zwt6Jw7Lz+u/+dVL551T2a7izK+f1mHlc6sxFP7tnrfatavo273Xil8s7/8PAzIO+/LA7p0bz/q7rj37DH5sVNUtP/j7QuTs/vQPvS//fobMHjb+bte2r1Wces21Q2oTUVJ5HFumGTWYOSjU4LZR03sT4s6ErwGN/rsBR7adO3eSRglzW7Zs4ciGS86fvxAt7EE5CAMcJyvSs2Q4cGCNPLKE48q9mRemREGGlSgowVWq1BaSleGTkGs6TYV0kk3qOCk/cpM5cVVtrpUFAreAR/lRLlEn/kkbeRtWymOpLFcSmbUfZWN2YyzJnQLviSLO5YFc/8VO7KHoOYQFjo5hPPJjxA2WnvYGm4McKtTpho/WK6ci8yb1itrwNf0bANuwsbFR15BbuTNT0DdnCvX19fPmLUAO1gddLlMgooFzrLRkVYUOJjG0RcBE2Cd1yO1LBrACwi5wZTi8gSu3C3CWRUaDG4bc4clsEQGIW4bI6yY5hlFgwi5X82Q8RVOuQHKWWfmE8rCQjTNxCTLcBcKA+5PFhPTpTC5hkZ2ym/SVLgqzTkpOgqgEQyCHMUmG40MX1uR4UNPuBnALFy7kzsTxYsGCBaTLWbNmcRl44oknqJk8eTI1EyZMknHFAURbLmsxDQMcTJjmSCmXJDKnxAbRKzAGtssFmR0duSlH5KjnQw5Q31ywCCV2DjjwCIHYsuXagElKDelHflyyUuxTXuUkZOcFBMtRjgaggUQLNUVHrgGoDqJ0Um7milkPi4IsposqNqWXtbSnRxywxQaBAN0aMoOaVBbpS+PacZFWpvWZAgvUgllI9WOvalQyWlxA0g6iy+oXJClr3CW4wLqHVqVeZCdpp6asXNTIaDFeVfqDxbtASv2wQHNpLLmHyu93qhI9ooS+MqRiZKSVDnr5/1KoKVaN6ksKRUFTXyyrWVEqddekp6NJo6bLBrUiIc8k1bfolVeDlLyWUEODumsbST4cNQh0xZBvLKRe6VcvreirofalxJCtuJmMGWom6tFGuSUBWUkDflFCivpSWY2i/KKZix4k1Iya6qUF6CRichEEOKnDjFaotUV/AdTaID2Ho0kqmzUon9U7V5y3rKnI5aiph3GfMm4hSVGqCG1ESZ7FRqlmCLyyiFqxs6avjhrjHAe1HPV4SbqoSQiZna6AkFhZ1NcSL/2qiiW8NHZC+l1YyeufXuRv3tzOeJrgp0QENe2ApqNSq+kroqZNNIYei5QhhlTBVKivNsrmtQiHkHiZnj/nCrgoydAqnJl3XnEQkZS9qSrNPi3JCEvSCElBMiJ41cs/CBD7tahUooFs+7+Fmh5H7xpZfzNMiXRVeYMqKGehu7BpVF+mbF7LUTMJUY1HwZwkjA2U+JZ35GVncYyAtYTKIKqsZYTLUUurw7D8KmJm0wo13UU6G/qzoMZTQ3ZM1FqSli/2Ve3qq1lWSuWoteFrqknZQIFv8wpE8scFWCkR+AyC5ax0K9SIaGqH4mt6AKkkCJBYZW2E5AvNhv6v4trJkZ6eIQYy+BYRL5FCti1JTc3lVlVqkRS8AnQrPUeN0kxfHbX/fxRF/wOunCxTNJb7LAAAAABJRU5ErkJggg==");
StringEntity entity = new StringEntity(json.toString(), StandardCharsets.UTF_8);
httpPost.setEntity(entity);
res = client.execute(httpPost);
System.out.println(res.getStatusLine().getStatusCode());
System.out.println(EntityUtils.toString(res.getEntity(), StandardCharsets.UTF_8));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (res != null) {
res.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
- Chromeを起動
- googleにアクセス
- Chromeを見えない位置に移動
- 以下の画像をダブルクリック