diff --git a/pom.xml b/pom.xml
index 3629efc24..a87ea1d3a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1290,147 +1290,66 @@
- com.google.cloud
- google-cloud-storage
- ${google.cloud.storage.version}
-
+ org.apache.httpcomponents.client5
+ httpclient5
+ ${httpclient5.version}
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ ${httpcomponents.version}
+
+
+
+ com.google.api-client
+ google-api-client
+ 2.7.2
+
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 1.50.0
- io.grpc
- grpc-inprocess
-
-
- io.grpc
- grpc-alts
-
-
- io.grpc
- grpc-auth
-
-
- io.grpc
- grpc-core
-
-
- io.grpc
- grpc-protobuf
-
-
- io.grpc
- grpc-protobuf-lite
-
-
- io.grpc
- grpc-opentelemetry
-
-
- io.grpc
- grpc-grpclb
-
-
- io.grpc
- grpc-netty-shaded
-
-
- io.grpc
- grpc-util
-
-
- io.grpc
- grpc-stub
-
-
- io.grpc
- grpc-googleapis
-
-
- io.grpc
- grpc-xds
-
-
- io.grpc
- grpc-services
-
-
- io.grpc
- grpc-rls
-
-
- com.google.api
- gax-grpc
-
-
- com.google.cloud
- google-cloud-core-grpc
-
-
- com.google.cloud
- google-cloud-monitoring
-
-
- com.google.api.grpc
- proto-google-cloud-monitoring-v3
-
-
- com.google.api.grpc
- grpc-google-cloud-storage-v2
-
-
- com.google.api.grpc
- gapic-google-cloud-storage-v2
-
-
- com.google.cloud.opentelemetry
- exporter-metrics
-
-
- io.opentelemetry.contrib
- opentelemetry-gcp-resources
-
-
- io.opentelemetry
- opentelemetry-sdk
-
-
- io.opentelemetry
- opentelemetry-sdk-trace
-
-
- io.opentelemetry
- opentelemetry-sdk-logs
-
-
- io.opentelemetry
- opentelemetry-sdk-metrics
-
-
- io.opentelemetry
- opentelemetry-sdk-common
-
-
- io.opentelemetry
- opentelemetry-sdk-extension-autoconfigure-spi
+
+ javax.annotation
+ javax.annotation-api
- org.apache.httpcomponents.client5
- httpclient5
- ${httpclient5.version}
+
+ com.google.auth
+ google-auth-library-credentials
+ 1.50.0
+
+
+
+ com.google.http-client
+ google-http-client-apache-v2
+ ${google.http.client.version}
com.squareup.okhttp3
@@ -1547,6 +1466,18 @@
org.bouncycastle
bcprov-jdk15on
+
+
+ com.google.cloud
+ google-cloud-storage
+
diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java
index 88536a882..549b46ae8 100644
--- a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java
+++ b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java
@@ -443,7 +443,7 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
/** The key of the configuration. e.g. http,https */
String CRAWLER_WEB_PROTOCOLS = "crawler.web.protocols";
- /** The key of the configuration. e.g. file,smb,smb1,ftp,s3,gcs */
+ /** The key of the configuration. e.g. file,smb,smb1,ftp,s3 */
String CRAWLER_FILE_PROTOCOLS = "crawler.file.protocols";
/** The key of the configuration. e.g. ^FESS_ENV_.* */
@@ -3571,7 +3571,7 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction
/**
* Get the value for the key 'crawler.file.protocols'.
- * The value is, e.g. file,smb,smb1,ftp,s3,gcs
+ * The value is, e.g. file,smb,smb1,ftp,s3
* comment: Supported file protocols for crawling.
* @return The value of found property. (NotNull: if not found, exception but basically no way)
*/
@@ -14560,7 +14560,7 @@ protected java.util.Map prepareGeneratedDefaultMap() {
defaultMap.put(FessConfig.CRAWLER_DOCUMENT_FULLSTOP_CHARS, "u002eu06d4u2e3cu3002");
defaultMap.put(FessConfig.CRAWLER_CRAWLING_DATA_ENCODING, "UTF-8");
defaultMap.put(FessConfig.CRAWLER_WEB_PROTOCOLS, "http,https");
- defaultMap.put(FessConfig.CRAWLER_FILE_PROTOCOLS, "file,smb,smb1,ftp,s3,gcs");
+ defaultMap.put(FessConfig.CRAWLER_FILE_PROTOCOLS, "file,smb,smb1,ftp,s3");
defaultMap.put(FessConfig.CRAWLER_DATA_ENV_PARAM_KEY_PATTERN, "^FESS_ENV_.*");
defaultMap.put(FessConfig.CRAWLER_IGNORE_ROBOTS_TXT, "false");
defaultMap.put(FessConfig.CRAWLER_IGNORE_ROBOTS_TAGS, "false");
diff --git a/src/main/java/org/codelibs/fess/storage/GcsStorageClient.java b/src/main/java/org/codelibs/fess/storage/GcsStorageClient.java
deleted file mode 100644
index cf0c32068..000000000
--- a/src/main/java/org/codelibs/fess/storage/GcsStorageClient.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright 2012-2025 CodeLibs Project and the Others.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-package org.codelibs.fess.storage;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.codelibs.core.lang.StringUtil;
-import org.codelibs.fess.crawler.Constants;
-import org.codelibs.fess.exception.StorageException;
-import org.codelibs.fess.mylasta.direction.FessConfig;
-import org.codelibs.fess.util.ComponentUtil;
-
-import com.google.api.gax.paging.Page;
-import com.google.auth.oauth2.GoogleCredentials;
-import com.google.cloud.NoCredentials;
-import com.google.cloud.storage.Blob;
-import com.google.cloud.storage.BlobId;
-import com.google.cloud.storage.BlobInfo;
-import com.google.cloud.storage.Bucket;
-import com.google.cloud.storage.BucketInfo;
-import com.google.cloud.storage.Storage;
-import com.google.cloud.storage.Storage.BlobListOption;
-import com.google.cloud.storage.StorageOptions;
-
-/**
- * Google Cloud Storage client implementation.
- */
-public class GcsStorageClient implements StorageClient {
-
- private static final Logger logger = LogManager.getLogger(GcsStorageClient.class);
-
- private static final String DEFAULT_GCS_HOST = "storage.googleapis.com";
-
- private Storage storage;
- private String bucket;
-
- /**
- * Constructor for subclasses that customize behavior without initializing the GCS client.
- * Subclasses using this constructor must override any method that depends on
- * {@code storage} or {@code bucket}.
- */
- protected GcsStorageClient() {
- // configured by init(), or by a subclass that overrides everything using storage
- }
-
- /**
- * Creates a new GcsStorageClient instance.
- *
- * @param projectId the GCS project ID
- * @param bucket the bucket name
- * @param endpoint the custom endpoint URL (optional, for fake-gcs-server etc.)
- * @param credentialsPath the path to the credentials JSON file (optional)
- */
- public GcsStorageClient(final String projectId, final String bucket, final String endpoint, final String credentialsPath) {
- configure(projectId, bucket, endpoint, credentialsPath);
- }
-
- @Override
- public void init() {
- final FessConfig fessConfig = ComponentUtil.getFessConfig();
- configure(fessConfig.getStorageProjectId(), fessConfig.getStorageBucket(), fessConfig.getStorageEndpoint(),
- fessConfig.getStorageCredentialsPath());
- }
-
- private void configure(final String projectId, final String bucket, final String endpoint, final String credentialsPath) {
- this.bucket = bucket;
-
- final StorageOptions.Builder builder = StorageOptions.newBuilder();
-
- if (StringUtil.isNotBlank(projectId)) {
- builder.setProjectId(projectId);
- }
-
- if (StringUtil.isNotBlank(endpoint) && !isDefaultEndpoint(endpoint)) {
- // For fake-gcs-server or custom endpoint
- builder.setHost(endpoint);
- builder.setCredentials(NoCredentials.getInstance());
- if (logger.isDebugEnabled()) {
- logger.debug("Using custom GCS endpoint: {}", endpoint);
- }
- } else // Production: use credentials file or default credentials
- if (StringUtil.isNotBlank(credentialsPath)) {
- try (FileInputStream fis = new FileInputStream(credentialsPath)) {
- final GoogleCredentials credentials = GoogleCredentials.fromStream(fis);
- builder.setCredentials(credentials);
- } catch (final IOException e) {
- throw new StorageException("Failed to load GCS credentials from " + credentialsPath, e);
- }
- }
- // If no credentials path, uses default credentials (GOOGLE_APPLICATION_CREDENTIALS env var)
-
- this.storage = builder.build().getService();
- }
-
- /**
- * Determines whether the given endpoint refers to the default GCS host
- * ({@code storage.googleapis.com}). Subclasses may override this to
- * recognize additional hosts as default.
- *
- * @param endpoint the endpoint to check (URL or host)
- * @return {@code true} if the endpoint resolves to the default GCS host
- */
- protected boolean isDefaultEndpoint(final String endpoint) {
- if (StringUtil.isBlank(endpoint)) {
- return false;
- }
- final String value = endpoint.trim();
- try {
- final URI uri = URI.create(value.contains("://") ? value : "https://" + value);
- return "https".equalsIgnoreCase(uri.getScheme()) && DEFAULT_GCS_HOST.equalsIgnoreCase(uri.getHost())
- && (uri.getPort() == -1 || uri.getPort() == 443);
- } catch (final IllegalArgumentException e) {
- return DEFAULT_GCS_HOST.equalsIgnoreCase(value);
- }
- }
-
- @Override
- public void uploadObject(final String objectName, final InputStream inputStream, final long size, final String contentType) {
- try {
- final BlobId blobId = BlobId.of(bucket, objectName);
- final BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(contentType).build();
- storage.createFrom(blobInfo, inputStream);
- } catch (final Exception e) {
- throw new StorageException("Failed to upload " + objectName, e);
- }
- }
-
- @Override
- public void downloadObject(final String objectName, final OutputStream outputStream) {
- try {
- final Blob blob = storage.get(BlobId.of(bucket, objectName));
- if (blob == null) {
- throw new StorageException("Object not found: " + objectName);
- }
- blob.downloadTo(outputStream);
- } catch (final StorageException e) {
- throw e;
- } catch (final Exception e) {
- throw new StorageException("Failed to download " + objectName, e);
- }
- }
-
- @Override
- public void deleteObject(final String objectName) {
- try {
- final boolean deleted = storage.delete(BlobId.of(bucket, objectName));
- if (!deleted && logger.isDebugEnabled()) {
- logger.debug("Object may not exist: {}", objectName);
- }
- } catch (final Exception e) {
- throw new StorageException("Failed to delete " + objectName, e);
- }
- }
-
- @Override
- public List listObjects(final String prefix, final int maxItems) {
- final List items = new ArrayList<>();
- final List fileItems = new ArrayList<>();
-
- try {
- final String searchPrefix = StringUtil.isNotBlank(prefix) ? (prefix.endsWith("/") ? prefix : prefix + "/") : "";
-
- final Page blobs = storage.list(bucket, BlobListOption.prefix(searchPrefix), BlobListOption.currentDirectory(),
- BlobListOption.pageSize(maxItems));
-
- for (final Blob blob : blobs.iterateAll()) {
- final String blobName = blob.getName();
-
- // Skip the prefix itself
- if (blobName.equals(searchPrefix)) {
- continue;
- }
-
- final boolean isDirectory = blobName.endsWith("/");
- final String name = getName(blobName);
-
- if (StringUtil.isBlank(name)) {
- continue;
- }
-
- final ZonedDateTime lastModified =
- blob.getUpdateTimeOffsetDateTime() != null ? blob.getUpdateTimeOffsetDateTime().toZonedDateTime() : null;
-
- final StorageItem item = new StorageItem(name, prefix, isDirectory, isDirectory ? 0 : blob.getSize(),
- isDirectory ? null : lastModified, encodeId(blobName));
-
- if (isDirectory) {
- items.add(item);
- } else {
- fileItems.add(item);
- }
-
- if (items.size() + fileItems.size() >= maxItems) {
- break;
- }
- }
- } catch (final Exception e) {
- if (logger.isDebugEnabled()) {
- logger.debug("Failed to list objects in {}", bucket, e);
- }
- }
-
- items.addAll(fileItems);
- return items;
- }
-
- @Override
- public Map getObjectTags(final String objectName) {
- try {
- final Blob blob = storage.get(BlobId.of(bucket, objectName));
- if (blob == null) {
- return Collections.emptyMap();
- }
- // GCS uses metadata instead of tags
- final Map metadata = blob.getMetadata();
- return metadata != null ? new HashMap<>(metadata) : Collections.emptyMap();
- } catch (final Exception e) {
- throw new StorageException("Failed to get tags from " + objectName, e);
- }
- }
-
- @Override
- public void setObjectTags(final String objectName, final Map tags) {
- try {
- final Blob blob = storage.get(BlobId.of(bucket, objectName));
- if (blob == null) {
- throw new StorageException("Object not found: " + objectName);
- }
- // GCS uses metadata instead of tags
- blob.toBuilder().setMetadata(tags).build().update();
- } catch (final StorageException e) {
- throw e;
- } catch (final Exception e) {
- throw new StorageException("Failed to update tags for " + objectName, e);
- }
- }
-
- @Override
- public void ensureBucketExists() {
- try {
- final Bucket existingBucket = storage.get(bucket);
- if (existingBucket == null) {
- storage.create(BucketInfo.newBuilder(bucket).build());
- logger.info("Created storage bucket: {}", bucket);
- }
- } catch (final Exception e) {
- logger.warn("Failed to ensure bucket exists: {}", bucket, e);
- }
- }
-
- @Override
- public boolean isAvailable() {
- try {
- return storage.get(bucket) != null;
- } catch (final Exception e) {
- return false;
- }
- }
-
- @Override
- public void close() {
- // GCS Storage client doesn't require explicit close
- // but we can try to close it if needed
- if (storage == null) {
- return;
- }
- try {
- storage.close();
- } catch (final Exception e) {
- if (logger.isDebugEnabled()) {
- logger.debug("Failed to close GCS storage client", e);
- }
- }
- }
-
- /**
- * Extracts the file/directory name from a full object path.
- *
- * @param objectName the full object path
- * @return the name portion of the path
- */
- private String getName(final String objectName) {
- if (StringUtil.isBlank(objectName)) {
- return StringUtil.EMPTY;
- }
- // Remove trailing slash if present
- String name = objectName;
- if (name.endsWith("/")) {
- name = name.substring(0, name.length() - 1);
- }
- final String[] values = name.split("/");
- if (values.length == 0) {
- return StringUtil.EMPTY;
- }
- return values[values.length - 1];
- }
-
- /**
- * Encodes an object name to a URL-safe base64 string.
- *
- * @param objectName the object name to encode
- * @return base64 encoded string
- */
- private String encodeId(final String objectName) {
- if (objectName == null) {
- return StringUtil.EMPTY;
- }
- return new String(Base64.getUrlEncoder().encode(objectName.getBytes(Constants.UTF_8_CHARSET)), Constants.UTF_8_CHARSET);
- }
-}
diff --git a/src/main/resources/fess_config.properties b/src/main/resources/fess_config.properties
index f1891f20b..930757415 100644
--- a/src/main/resources/fess_config.properties
+++ b/src/main/resources/fess_config.properties
@@ -383,7 +383,7 @@ crawler.crawling.data.encoding=UTF-8
# Supported web protocols for crawling.
crawler.web.protocols=http,https
# Supported file protocols for crawling.
-crawler.file.protocols=file,smb,smb1,ftp,s3,gcs
+crawler.file.protocols=file,smb,smb1,ftp,s3
# Pattern for environment variable keys in crawling data.
crawler.data.env.param.key.pattern=^FESS_ENV_.*
# Whether to ignore robots.txt during crawling.
diff --git a/src/main/resources/fess_storage.xml b/src/main/resources/fess_storage.xml
index 7ac0b426c..45abc7bcb 100644
--- a/src/main/resources/fess_storage.xml
+++ b/src/main/resources/fess_storage.xml
@@ -6,7 +6,9 @@
The mapping from a storage.type value to a class lives here rather than in Java so that
the clients can ship as fess-lib-* plugins: a plugin contributes its component through
fess_storage++.xml, which merges from every jar on the classpath, and setting
- storage.type to its name is enough to reach it.
+ storage.type to its name is enough to reach it. fess-lib-gcs registers gcsStorageClient
+ that way; storage.type=gcs without that plugin installed is a configuration error the
+ factory reports rather than a missing class.
They are prototypes, not singletons: every caller takes the client in a
try-with-resources and closes it, which would shut down a shared instance. -->
@@ -17,6 +19,4 @@
keeps the type-to-class mapping in one place. -->
-
-
diff --git a/src/test/java/org/codelibs/fess/app/web/admin/wizard/AdminWizardActionTest.java b/src/test/java/org/codelibs/fess/app/web/admin/wizard/AdminWizardActionTest.java
index 2b4dce88b..3c7568eb3 100644
--- a/src/test/java/org/codelibs/fess/app/web/admin/wizard/AdminWizardActionTest.java
+++ b/src/test/java/org/codelibs/fess/app/web/admin/wizard/AdminWizardActionTest.java
@@ -42,7 +42,7 @@ public String getCrawlerWebProtocols() {
@Override
public String getCrawlerFileProtocols() {
- return "file,smb,smb1,ftp,s3,gcs";
+ return "file,smb,smb1,ftp,s3";
}
});
final ProtocolHelper protocolHelper = new ProtocolHelper();
diff --git a/src/test/java/org/codelibs/fess/app/web/go/GoActionTest.java b/src/test/java/org/codelibs/fess/app/web/go/GoActionTest.java
index a14456b3c..97166ee3f 100644
--- a/src/test/java/org/codelibs/fess/app/web/go/GoActionTest.java
+++ b/src/test/java/org/codelibs/fess/app/web/go/GoActionTest.java
@@ -48,7 +48,7 @@ public String getCrawlerWebProtocols() {
@Override
public String getCrawlerFileProtocols() {
- return "file,smb,smb1,ftp,s3,gcs";
+ return "file,smb,smb1,ftp,s3";
}
});
final ProtocolHelper protocolHelper = new ProtocolHelper();
diff --git a/src/test/java/org/codelibs/fess/crawler/transformer/FessFileTransformerTest.java b/src/test/java/org/codelibs/fess/crawler/transformer/FessFileTransformerTest.java
index a291e981b..ae37d5ab2 100644
--- a/src/test/java/org/codelibs/fess/crawler/transformer/FessFileTransformerTest.java
+++ b/src/test/java/org/codelibs/fess/crawler/transformer/FessFileTransformerTest.java
@@ -46,7 +46,7 @@ public String getCrawlerWebProtocols() {
@Override
public String getCrawlerFileProtocols() {
- return "file,smb,smb1,ftp,s3,gcs";
+ return "file,smb,smb1,ftp,s3";
}
@Override
diff --git a/src/test/java/org/codelibs/fess/helper/ProtocolHelperTest.java b/src/test/java/org/codelibs/fess/helper/ProtocolHelperTest.java
index 73c07d4e3..c86c02adc 100644
--- a/src/test/java/org/codelibs/fess/helper/ProtocolHelperTest.java
+++ b/src/test/java/org/codelibs/fess/helper/ProtocolHelperTest.java
@@ -738,14 +738,14 @@ public String getCrawlerWebProtocols() {
@Override
public String getCrawlerFileProtocols() {
- return "file,smb,smb1,ftp,s3,gcs";
+ return "file,smb,smb1,ftp,s3";
}
});
final ProtocolHelper protocolHelper = new ProtocolHelper();
protocolHelper.init();
- assertEquals(6, protocolHelper.getFileProtocols().length);
+ assertEquals(5, protocolHelper.getFileProtocols().length);
// All file protocols should be valid
assertTrue(protocolHelper.isValidFileProtocol("file:///path/to/file"));
@@ -753,7 +753,10 @@ public String getCrawlerFileProtocols() {
assertTrue(protocolHelper.isValidFileProtocol("smb1://server/share"));
assertTrue(protocolHelper.isValidFileProtocol("ftp://ftp.example.com/file"));
assertTrue(protocolHelper.isValidFileProtocol("s3://bucket/key"));
- assertTrue(protocolHelper.isValidFileProtocol("gcs://bucket/object"));
+
+ // gcs is not in the shipped default: fess-lib-gcs adds it with addFileProtocol when the
+ // plugin is installed, which test_s3_gcs_protocols_add_dynamically covers.
+ assertFalse(protocolHelper.isValidFileProtocol("gcs://bucket/object"));
// Web protocols should NOT be valid as file protocols
assertFalse(protocolHelper.isValidFileProtocol("http://example.com"));
diff --git a/src/test/java/org/codelibs/fess/storage/GcsStorageClientTest.java b/src/test/java/org/codelibs/fess/storage/GcsStorageClientTest.java
deleted file mode 100644
index e590e332e..000000000
--- a/src/test/java/org/codelibs/fess/storage/GcsStorageClientTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2012-2025 CodeLibs Project and the Others.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- * either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-package org.codelibs.fess.storage;
-
-import org.codelibs.fess.unit.UnitFessTestCase;
-import org.junit.jupiter.api.Test;
-
-public class GcsStorageClientTest extends UnitFessTestCase {
-
- private static final class TestableGcsStorageClient extends GcsStorageClient {
- TestableGcsStorageClient() {
- super();
- }
- }
-
- @Test
- public void test_isDefaultEndpoint() {
- try (GcsStorageClient client = new TestableGcsStorageClient()) {
- assertTrue(client.isDefaultEndpoint("https://storage.googleapis.com"));
- assertTrue(client.isDefaultEndpoint("https://storage.googleapis.com/"));
- assertTrue(client.isDefaultEndpoint("storage.googleapis.com"));
- assertTrue(client.isDefaultEndpoint("https://storage.googleapis.com/storage/v1"));
- assertTrue(client.isDefaultEndpoint("https://storage.googleapis.com:443"));
-
- assertFalse(client.isDefaultEndpoint(""));
- assertFalse(client.isDefaultEndpoint("http://storage.googleapis.com"));
- assertFalse(client.isDefaultEndpoint("https://storage.googleapis.com:4443"));
- assertFalse(client.isDefaultEndpoint("http://localhost:4443"));
- assertFalse(client.isDefaultEndpoint("https://example.com/storage.googleapis.com"));
- }
- }
-}
diff --git a/src/test/java/org/codelibs/fess/storage/StorageClientFactoryTest.java b/src/test/java/org/codelibs/fess/storage/StorageClientFactoryTest.java
index 2d037f4bb..7a8de6f03 100644
--- a/src/test/java/org/codelibs/fess/storage/StorageClientFactoryTest.java
+++ b/src/test/java/org/codelibs/fess/storage/StorageClientFactoryTest.java
@@ -49,19 +49,32 @@ public void test_componentName_detectsFromTheEndpoint() {
}
/**
- * Every name {@link #test_componentName_detectsFromTheEndpoint} and the shipped
- * storage.type values can produce has to exist in fess_storage.xml, or the admin storage
+ * Every name {@link #test_componentName_detectsFromTheEndpoint} and the storage.type values
+ * the distribution serves can produce has to exist in fess_storage.xml, or the admin storage
* screen fails at runtime for a configuration the UI offers.
*/
@Test
- public void test_everyShippedTypeHasAComponent() {
- for (final StorageType type : StorageType.values()) {
+ public void test_everyTypeTheDistributionServesHasAComponent() {
+ for (final StorageType type : new StorageType[] { StorageType.S3, StorageType.S3_COMPAT }) {
final String name = StorageClientFactory.componentName(type.name(), null);
assertTrue(org.codelibs.fess.util.ComponentUtil.hasComponent(name), name + " is not registered in fess_storage.xml");
}
assertTrue(org.codelibs.fess.util.ComponentUtil.hasComponent(StorageClientFactory.componentName("auto", null)));
}
+ /**
+ * GCS is deliberately not in that list. Its client ships in fess-lib-gcs, together with the
+ * Google Cloud Storage SDK the distribution no longer carries, so what core keeps is the name
+ * the plugin registers under and the endpoint detection that produces it. Registering a
+ * component here again would pull the SDK back into the war.
+ */
+ @Test
+ public void test_gcsIsServedByAPluginRatherThanCore() {
+ assertEquals("gcsStorageClient", StorageClientFactory.componentName(StorageType.GCS.name(), null));
+ assertFalse(org.codelibs.fess.util.ComponentUtil.hasComponent("gcsStorageClient"),
+ "gcsStorageClient belongs to fess-lib-gcs, which contributes it through fess_storage++.xml");
+ }
+
/**
* A prototype, not a singleton: every caller closes the client it was handed, so a shared
* instance would be shut down for everybody by the first caller to finish.