Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ API client library for Kintone REST APIs on Java.
Add dependency declaration in `build.gradle` of your project.
```
dependencies {
implementation 'com.kintone:kintone-java-client:1.4.0'
implementation 'com.kintone:kintone-java-client:2.0.0'
}
```
- For projects using Maven
Expand All @@ -17,7 +17,7 @@ API client library for Kintone REST APIs on Java.
<dependency>
<groupId>com.kintone</groupId>
<artifactId>kintone-java-client</artifactId>
<version>1.4.0</version>
<version>2.0.0</version>
</dependency>
```

Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
id 'com.github.hierynomus.license' version '0.16.1'
}

version = '1.4.1'
version = '2.0.0'
sourceCompatibility = 1.8
targetCompatibility = 1.8

Expand All @@ -27,6 +27,7 @@ dependencies {
implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1'
implementation 'org.slf4j:slf4j-api:1.7.36'

compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
Expand Down
31 changes: 29 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ client.close();
Add dependency declaration in `build.gradle` of your project.
```groovy
dependencies {
implementation 'com.kintone:kintone-java-client:1.4.0'
implementation 'com.kintone:kintone-java-client:2.0.0'
}
```

Expand All @@ -39,7 +39,7 @@ Add dependency declaration in `pom.xml` of your project.
<dependency>
<groupId>com.kintone</groupId>
<artifactId>kintone-java-client</artifactId>
<version>1.4.0</version>
<version>2.0.0</version>
</dependency>
```

Expand Down Expand Up @@ -152,6 +152,33 @@ Record newRecord = new Record().putField("text", new SingleLineTextFieldValue(va
clientB.record().addRecord(app2, newRecord);
```

#### Debug Logging

Kintone Java Client outputs the contents of requests as debug logs through the logging interface
provided by the [Simple Logging Facade for Java (SLF4J)](https://slf4j.org/) package.
Therefore, you can check the logs by enabling the logger according to the configuration instructions
of the logging implementation you are using. The logger name is `com.kintone.client.requestLog`.

For example, if you are using [Logback](https://logback.qos.ch/) as your logging backend,
the configuration file (`logback.xml`) would look like follows:

```
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %logger %msg%n</pattern>
</encoder>
</appender>

<!-- Enable debug logs for Kintone Java Client -->
<logger name="com.kintone.client.requestLog" level="debug" />

<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
```

### Record Operations

`KintoneClient` supports record related operations through the `RecordClient` subcomponent.
Expand Down
88 changes: 78 additions & 10 deletions src/main/java/com/kintone/client/InternalClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.kintone.client.model.BulkRequestContent;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
Expand Down Expand Up @@ -41,13 +42,21 @@
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.*;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.ssl.TLS;
import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
import org.apache.hc.core5.pool.PoolReusePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class InternalClientImpl extends InternalClient {

Expand All @@ -61,6 +70,9 @@ class InternalClientImpl extends InternalClient {
private final HttpHost proxyHost;
private final BasicScheme proxyAuth;

private static final String REQUEST_LOGGER_NAME = "com.kintone.client.requestLog";
private final Logger logger = LoggerFactory.getLogger(REQUEST_LOGGER_NAME);

InternalClientImpl(
String baseUrl,
Auth auth,
Expand Down Expand Up @@ -152,6 +164,20 @@ private HttpContext createHttpContext() {
return context;
}

private String readInputStream(InputStream in) {
try (InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
StringBuilder sb = new StringBuilder();
char[] buffer = new char[1024];
int size;
while ((size = reader.read(buffer)) > 0) {
sb.append(buffer, 0, size);
}
return sb.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Override
<T extends KintoneResponseBody> T call(
KintoneApi api, KintoneRequest body, List<ResponseHandler> handlers) {
Expand All @@ -168,6 +194,11 @@ <T extends KintoneResponseBody> T call(
KintoneRequest body,
Class<T> clazz,
List<ResponseHandler> handlers) {
if (logger.isDebugEnabled()) {
String json = mapper.formatString(body);
logger.debug("request: {} {} {}", method, path, json);
}

HttpUriRequest request = createJsonRequest(method, path, body);
try {
return httpClient.execute(
Expand Down Expand Up @@ -215,12 +246,19 @@ private List<KintoneResponseBody> parseBulkRequestResponse(
return resultBodies;
}

@SuppressWarnings("unchecked")
@Override
BulkRequestsResponseBody bulkRequest(BulkRequestsRequest body, List<ResponseHandler> handlers) {
KintoneHttpMethod method = KintoneApi.BULK_REQUESTS.getMethod();
String path = getApiPath(KintoneApi.BULK_REQUESTS);
Map<String, Object> bulkRequestBody = createBulkRequestBody(body);

HttpUriRequest request =
createJsonRequest(KintoneApi.BULK_REQUESTS.getMethod(), path, createBulkRequestBody(body));
if (logger.isDebugEnabled()) {
String json = mapper.formatString(bulkRequestBody);
logger.debug("request: {} {} {}", method, path, json);
}

HttpUriRequest request = createJsonRequest(method, path, bulkRequestBody);
try {
return httpClient.execute(
request,
Expand All @@ -230,9 +268,17 @@ BulkRequestsResponseBody bulkRequest(BulkRequestsRequest body, List<ResponseHand
parseResponse(
response,
stream -> {
@SuppressWarnings("unchecked")
Map<String, Object> responseMap = mapper.parse(stream, Map.class);
@SuppressWarnings("unchecked")
Map<String, Object> responseMap;
if (logger.isDebugEnabled()) {
String responseBody = readInputStream(stream);
logger.debug(
"response status: {}, response body: {}",
response.getCode(),
responseBody);
responseMap = mapper.parseString(responseBody, Map.class);
} else {
responseMap = mapper.parse(stream, Map.class);
}
List<Object> results = (List<Object>) responseMap.get("results");
List<KintoneResponseBody> bodies =
parseBulkRequestResponse(body.getRequests(), results);
Expand All @@ -248,11 +294,19 @@ BulkRequestsResponseBody bulkRequest(BulkRequestsRequest body, List<ResponseHand

@Override
DownloadFileResponseBody download(DownloadFileRequest request, List<ResponseHandler> handlers) {
KintoneHttpMethod method = KintoneApi.DOWNLOAD_FILE.getMethod();
String path = getApiPath(KintoneApi.DOWNLOAD_FILE);
HttpUriRequest req = createJsonRequest(KintoneApi.DOWNLOAD_FILE.getMethod(), path, request);

if (logger.isDebugEnabled()) {
String json = mapper.formatString(request);
logger.debug("request: {} {} {}", method, path, json);
}

HttpUriRequest req = createJsonRequest(method, path, request);
KintoneResponse<DownloadFileResponseBody> r;
try {
ClassicHttpResponse response = httpClient.executeOpen(null, req, createHttpContext());
logger.debug("response status: {}", response.getCode());
com.kintone.client.model.HttpResponse resp = new HttpResponseImpl(response);
r = parseResponse(response, stream -> new DownloadFileResponseBody(resp));
} catch (IOException e) {
Expand Down Expand Up @@ -281,9 +335,12 @@ KintoneResponse<UploadFileResponseBody> upload(
"file", new InputStreamBody(content, ContentType.create(contentType), filename));

String headerContentType = "multipart/form-data; boundary=" + boundary;
KintoneHttpMethod method = KintoneApi.UPLOAD_FILE.getMethod();
String path = getApiPath(KintoneApi.UPLOAD_FILE);
HttpUriRequest httpRequest =
createRequest(KintoneApi.UPLOAD_FILE.getMethod(), path, headerContentType, builder.build());

logger.debug("request: {} {}, file: {}", method, path, filename);

HttpUriRequest httpRequest = createRequest(method, path, headerContentType, builder.build());
try {
return httpClient.execute(
httpRequest,
Expand Down Expand Up @@ -314,6 +371,7 @@ private <T extends KintoneResponseBody> KintoneResponse<T> parseResponse(
result = converter.apply(response.getEntity().getContent());
} else {
errorBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
logger.debug("response status: {}, response body: {}", statusCode, errorBody);
}
} catch (IOException | ParseException e) {
throw new KintoneRuntimeException("Failed to request", e);
Expand All @@ -323,7 +381,17 @@ private <T extends KintoneResponseBody> KintoneResponse<T> parseResponse(

private <T extends KintoneResponseBody> KintoneResponse<T> parseJsonResponse(
ClassicHttpResponse response, Class<T> responseClass) {
return parseResponse(response, stream -> mapper.parse(stream, responseClass));
return parseResponse(
response,
stream -> {
if (logger.isDebugEnabled()) {
String body = readInputStream(stream);
logger.debug("response status: {}, response body: {}", response.getCode(), body);
return mapper.parseString(body, responseClass);
} else {
return mapper.parse(stream, responseClass);
}
});
}

private void applyHandlers(KintoneResponse<?> response, List<ResponseHandler> handlers) {
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/kintone/client/JsonMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ byte[] format(Object obj) {
}
}

String formatString(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new KintoneRuntimeException("Failed to format request JSON", e);
}
}

<T> T parse(InputStream stream, Class<T> clazz) {
try {
return mapper.readValue(stream, clazz);
Expand All @@ -59,6 +67,14 @@ <T> T parse(InputStream stream, Class<T> clazz) {
}
}

<T> T parseString(String input, Class<T> clazz) {
try {
return mapper.readValue(input, clazz);
} catch (IOException e) {
throw new KintoneRuntimeException("Failed to parse response JSON", e);
}
}

<T> T convert(Object object, Class<T> clazz) {
return mapper.convertValue(object, clazz);
}
Expand Down