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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## 1.6.3 - 2026-04-13

- Support new regions af-south-1
- Fix timeout issue

## 1.6.2 - 2026-01-19

- Support new regions eu-west-2 and ca-central-1
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ Creates a new instance of the `AmaasClient` class, and provisions essential sett

| Parameter | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| region | The region you obtained your api key. Value provided must be one of the Vision One regions, e.g. `us-east-1`, `eu-central-1`, `ap-northeast-1`, `ap-southeast-2`, `ap-southeast-1`, `ap-south-1`, `me-central-1`,`eu-west-2`,`ca-central-1`, etc. If host is given, region will be ignored. |
| region | The region you obtained your api key. Value provided must be one of the Vision One regions, e.g. `us-east-1`, `eu-central-1`, `ap-northeast-1`, `ap-southeast-2`, `ap-southeast-1`, `ap-south-1`, `me-central-1`,`eu-west-2`,`ca-central-1`,`af-south-1`, etc. If host is given, region will be ignored. |
| host | The host ip address of self hosted AMaaS scanner. Ignore if to use Trend AMaaS service |
| apikey | Your own Vision One API Key. |
| timeoutInSecs | Timeout to cancel the connection to server in seconds. Valid value is 0, 1, 2, ... ; default to 300 seconds. |
Expand All @@ -205,7 +205,7 @@ Creates a new instance of the `AmaasClient` class, and provisions essential sett

| Parameter | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| region | The region you obtained your api key. Value provided must be one of the Vision One regions, e.g. `us-east-1`, `eu-central-1`, `ap-northeast-1`, `ap-southeast-2`, `ap-southeast-1`, `ap-south-1`, `me-central-1`,`eu-west-2`,`ca-central-1` ,etc. |
| region | The region you obtained your api key. Value provided must be one of the Vision One regions, e.g. `us-east-1`, `eu-central-1`, `ap-northeast-1`, `ap-southeast-2`, `ap-southeast-1`, `ap-south-1`, `me-central-1`,`eu-west-2`,`ca-central-1`,`af-south-1` ,etc. |
| apikey | Your own Vision One API Key. |
| timeoutInSecs | Timeout to cancel the connection to server in seconds. Valid value is 0, 1, 2, ... ; default to 300 seconds. |

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.6.2
1.6.3
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.trend</groupId>
<artifactId>file-security-java-sdk</artifactId>
<version>1.6.2</version>
<version>1.6.3</version>

<name>file-security-java-sdk</name>
<url>https://github.com/trendmicro/tm-v1-fs-java-sdk</url>
Expand Down
1 change: 1 addition & 0 deletions protos/scan.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum Stage {
STAGE_INIT = 0;
STAGE_RUN = 1;
STAGE_FINI = 2;
STAGE_HEARTBEAT = 3;
}

message C2S {
Expand Down
105 changes: 96 additions & 9 deletions src/main/java/com/trend/cloudone/amaas/AMaasClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -304,6 +306,7 @@ private static void log(final Level level, final String msg, final Object... par
*/
static class AMaasServerCallback implements StreamObserver<ScanOuterClass.S2C> {
private static final int POLL_TIME_MILLIS = 200;
static final int DEFAULT_HEARTBEAT_INTERVAL_MS = 30 * 1000;

private StreamObserver<ScanOuterClass.C2S> requestObserver;
private AMaasReader reader;
Expand All @@ -316,10 +319,57 @@ static class AMaasServerCallback implements StreamObserver<ScanOuterClass.S2C> {
private boolean bulk = true;
private long start = System.currentTimeMillis();
private long timeoutSecs;
private final Object streamLock = new Object();
private volatile boolean streamClosed = false;
private ScheduledExecutorService heartbeatExecutor = null;
private int heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS;

AMaasServerCallback() {
}

void setHeartbeatIntervalMs(final int intervalMs) {
this.heartbeatIntervalMs = intervalMs;
}

protected void startHeartbeat() {
heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(new java.util.concurrent.ThreadFactory() {
@Override
public Thread newThread(final Runnable r) {
Thread t = new Thread(r, "amaas-heartbeat");
t.setDaemon(true);
return t;
}
});
heartbeatExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (streamClosed) {
return;
}
ScanOuterClass.C2S hb = ScanOuterClass.C2S.newBuilder()
.setStage(Stage.STAGE_HEARTBEAT)
.build();
synchronized (streamLock) {
if (!streamClosed) {
try {
requestObserver.onNext(hb);
log(Level.FINE, "Sent heartbeat");
} catch (Exception e) {
log(Level.WARNING, "Heartbeat send failed: {0}", e.getMessage());
}
}
}
}
}, heartbeatIntervalMs, heartbeatIntervalMs, TimeUnit.MILLISECONDS);
}

protected void stopHeartbeat() {
if (heartbeatExecutor != null) {
heartbeatExecutor.shutdownNow();
heartbeatExecutor = null;
}
}

private AMaasException processError() {
AMaasException err = null;
if (this.grpcStatus == Status.Code.UNAUTHENTICATED) {
Expand Down Expand Up @@ -363,7 +413,13 @@ public void onNext(final ScanOuterClass.S2C s2cMsg) {
case CMD_RETR:
if (s2cMsg.getStage() != Stage.STAGE_RUN) {
log(Level.INFO, "Received unexpected command RETR at stage {0}", s2cMsg.getStage());
requestObserver.onError(new StatusRuntimeException(Status.ABORTED));
synchronized (streamLock) {
if (!streamClosed) {
streamClosed = true;
requestObserver.onError(new StatusRuntimeException(Status.ABORTED));
}
}
return;
}
java.util.List<java.lang.Integer> bulkLength;
java.util.List<java.lang.Integer> bulkOffset;
Expand Down Expand Up @@ -401,30 +457,55 @@ public void onNext(final ScanOuterClass.S2C s2cMsg) {

if (TimeUnit.MILLISECONDS.toSeconds(duration) > this.timeoutSecs) {
log(Level.INFO, "DEADLINE_EXCEEDED {0}", duration);
requestObserver.onError(new StatusRuntimeException(Status.DEADLINE_EXCEEDED));
synchronized (streamLock) {
if (!streamClosed) {
streamClosed = true;
requestObserver.onError(new StatusRuntimeException(Status.DEADLINE_EXCEEDED));
}
}
return;
}
}
requestObserver.onNext(request);
synchronized (streamLock) {
if (!streamClosed) {
requestObserver.onNext(request);
}
}
} catch (IOException e) {
log(Level.SEVERE, "Exception when processing server message", e.getMessage());
requestObserver.onError(new StatusRuntimeException(Status.ABORTED));
synchronized (streamLock) {
if (!streamClosed) {
streamClosed = true;
requestObserver.onError(new StatusRuntimeException(Status.ABORTED));
}
}
}
}
break;
case CMD_QUIT:
this.scanResult = s2cMsg.getResult();
log(Level.INFO, "Scan succeeded: result={0} fetchCount={1} fetchSize={2}.", this.scanResult, this.fetchCount, this.fetchSize);
requestObserver.onCompleted();
synchronized (streamLock) {
streamClosed = true;
requestObserver.onCompleted();
}
break;
default:
log(Level.WARNING, "Unknown command");
requestObserver.onError(new StatusRuntimeException(Status.INVALID_ARGUMENT));
synchronized (streamLock) {
if (!streamClosed) {
streamClosed = true;
requestObserver.onError(new StatusRuntimeException(Status.INVALID_ARGUMENT));
}
}
}
}

@Override
public void onError(final Throwable t) {
synchronized (streamLock) {
streamClosed = true;
}
log(Level.WARNING, "scan Failed: {0}", Status.fromThrowable(t));
this.done = true;
this.grpcStatus = Status.fromThrowable(t).getCode();
Expand All @@ -433,6 +514,9 @@ public void onError(final Throwable t) {

@Override
public void onCompleted() {
synchronized (streamLock) {
streamClosed = true;
}
log(Level.INFO, "File successfully scanned.");
this.done = true;
this.grpcStatus = Status.Code.OK;
Expand Down Expand Up @@ -528,10 +612,13 @@ public String scanRun(final AMaasReader reader, final AMaasScanOptions options)
ScanOuterClass.C2S request = builder.build();

requestObserver.onNext(request);
serverCallback.startHeartbeat();

String scanResult = serverCallback.waitTilExit();

return scanResult;
try {
return serverCallback.waitTilExit();
} finally {
serverCallback.stopHeartbeat();
}
}

/**
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/com/trend/cloudone/amaas/AMaasRegion.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ final class AMaasRegion {
static final String AWS_TREND_REGION = "us-east-2";
static final String AWS_GB_REGION = "eu-west-2";
static final String AWS_AE_REGION = "me-central-1";
static final String AWS_ZA_REGION = "af-south-1";
static final String C1_JP_REGION = "jp-1";
static final String C1_SG_REGION = "sg-1";
static final String C1_AU_REGION = "au-1";
Expand All @@ -35,11 +36,12 @@ final class AMaasRegion {
static final String C1_TREND_REGION = "trend-us-1";
static final String C1_GB_REGION = "gb-1";
static final String C1_AE_REGION = "ae-1";
static final String C1_ZA_REGION = "za-1";

static final List<String> C1_REGIONS = Arrays.asList(new String[]{C1_AU_REGION, C1_CA_REGION, C1_DE_REGION, C1_GB_REGION, C1_IN_REGION, C1_JP_REGION, C1_SG_REGION, C1_US_REGION, C1_TREND_REGION});
static final List<String> V1_REGIONS = Arrays.asList(new String[]{AWS_AU_REGION, AWS_CA_REGION, AWS_DE_REGION, AWS_GB_REGION, AWS_IN_REGION, AWS_JP_REGION, AWS_SG_REGION, AWS_US_REGION, AWS_AE_REGION});
static final List<String> SUPPORTED_V1_REGIONS = Arrays.asList(new String[]{AWS_AU_REGION, AWS_DE_REGION, AWS_JP_REGION, AWS_SG_REGION, AWS_US_REGION, AWS_IN_REGION, AWS_AE_REGION, AWS_CA_REGION, AWS_GB_REGION});
static final List<String> SUPPORTED_C1_REGIONS = Arrays.asList(new String[]{C1_AU_REGION, C1_CA_REGION, C1_DE_REGION, C1_GB_REGION, C1_IN_REGION, C1_JP_REGION, C1_SG_REGION, C1_US_REGION});
static final List<String> C1_REGIONS = Arrays.asList(new String[]{C1_AU_REGION, C1_CA_REGION, C1_DE_REGION, C1_GB_REGION, C1_IN_REGION, C1_JP_REGION, C1_SG_REGION, C1_US_REGION, C1_TREND_REGION, C1_ZA_REGION});
static final List<String> V1_REGIONS = Arrays.asList(new String[]{AWS_AU_REGION, AWS_CA_REGION, AWS_DE_REGION, AWS_GB_REGION, AWS_IN_REGION, AWS_JP_REGION, AWS_SG_REGION, AWS_US_REGION, AWS_AE_REGION, AWS_ZA_REGION});
static final List<String> SUPPORTED_V1_REGIONS = Arrays.asList(new String[]{AWS_AU_REGION, AWS_DE_REGION, AWS_JP_REGION, AWS_SG_REGION, AWS_US_REGION, AWS_IN_REGION, AWS_AE_REGION, AWS_CA_REGION, AWS_GB_REGION, AWS_ZA_REGION});
static final List<String> SUPPORTED_C1_REGIONS = Arrays.asList(new String[]{C1_AU_REGION, C1_CA_REGION, C1_DE_REGION, C1_GB_REGION, C1_IN_REGION, C1_JP_REGION, C1_SG_REGION, C1_US_REGION, C1_ZA_REGION});

static final Map<String, String> V1_TO_C1_REGION_MAPPING = new HashMap<String, String>() {
{
Expand All @@ -52,6 +54,7 @@ final class AMaasRegion {
put(AWS_AE_REGION, C1_AE_REGION);
put(AWS_CA_REGION, C1_CA_REGION);
put(AWS_GB_REGION, C1_GB_REGION);
put(AWS_ZA_REGION, C1_ZA_REGION);
}
};

Expand All @@ -67,6 +70,7 @@ final class AMaasRegion {
put(C1_CA_REGION, "antimalware.ca-1.cloudone.trendmicro.com");
put(C1_TREND_REGION, "antimalware.trend-us-1.cloudone.trendmicro.com");
put(C1_AE_REGION, "antimalware.ae-1.cloudone.trendmicro.com");
put(C1_ZA_REGION, "antimalware.za-1.cloudone.trendmicro.com");
}
};

Expand Down
Loading
Loading