From b3db1ad5ea5a74c87e8917729e66ba89f209c9f2 Mon Sep 17 00:00:00 2001 From: Shinsuke Sugaya Date: Wed, 9 Sep 2026 22:32:00 +0900 Subject: [PATCH] build!: move the GCS client into the fess-lib-gcs plugin storage.type=gcs and gcs: crawling now need the fess-lib-gcs plugin. Installing it with bin/fess-setup install plugin fess-lib-gcs restores both; without it the storage factory reports the missing component by name and gcs: is not a file protocol, so neither fails with a missing class. This takes 29 jars and 16.9 MiB out of WEB-INF/lib for a backend most installations never use, and stops the distribution tracking a Google SDK that releases far more often than Fess does. Nothing is left behind in core except the name the plugin registers under: StorageClientFactory has resolved storage clients as "StorageClient" since the DI change, so fess_storage++.xml in the plugin is enough to reach GcsStorageClient, and the endpoint detection that maps storage.googleapis.com to gcs stays here. The exclusion on fess-crawler-lasta is what actually removes the jars. fess-crawler declares google-cloud-storage at compile scope for GcsClient, which stays in that library, so dropping the direct declaration here would have left the whole tree in the war through the transitive path. Three dependencies are declared to replace what the SDK used to supply: - google-api-client and google-auth-library-oauth2-http are unused here. fess-ds-gsuite declares both with scope provided and does not shade them, so it has been running on the copies google-cloud-storage happened to bring. Removing them silently would break a Google Drive crawl at run time, not at build time. - httpclient has no caller here either, but leaving it to the resolver hands the decision to io.rest-assured, a test dependency that brings 4.5.13 at a shallower depth than fess-crawler's ${httpcomponents.version}. fess-ds-sharepoint compiles against the latter with scope provided. Three more pins close version splits the replacements opened, all of the shape that left the GCS client calling a google-http-client method that did not exist: google-auth-library-credentials (google-api-client wins the tie with 1.30.0 against the 1.50.0 the library it serves asks for), google-http-client-apache-v2 (the fifth member of that family, and the only one that arrives at 1.45.2), and an exclusion of the javax flavour of the annotation API that api-common pulls. Apart from the GCS set, three artifacts change version, each back to what its remaining consumer asks for: woodstox and stax2 to what xmlsec wants, grpc-api to what google-http-client wants. Verified against a running Fess with the plugin installed and a fake-gcs-server bucket: the storage client resolves from the plugin and lists objects, gcs joins the file protocols, and fess-crawler's GcsClient in WEB-INF/lib fetches an object using the SDK that now lives in WEB-INF/plugin. Without the plugin, the storage factory raises StorageException naming fess-lib-gcs and no crawler client is registered for gcs:. This depends on the matching fess-crawler change; merging it first avoids a window where crawler/client.xml still references a component whose SDK is gone. --- pom.xml | 201 ++++------- .../fess/mylasta/direction/FessConfig.java | 6 +- .../fess/storage/GcsStorageClient.java | 336 ------------------ src/main/resources/fess_config.properties | 2 +- src/main/resources/fess_storage.xml | 6 +- .../admin/wizard/AdminWizardActionTest.java | 2 +- .../fess/app/web/go/GoActionTest.java | 2 +- .../transformer/FessFileTransformerTest.java | 2 +- .../fess/helper/ProtocolHelperTest.java | 9 +- .../fess/storage/GcsStorageClientTest.java | 45 --- .../storage/StorageClientFactoryTest.java | 21 +- 11 files changed, 99 insertions(+), 533 deletions(-) delete mode 100644 src/main/java/org/codelibs/fess/storage/GcsStorageClient.java delete mode 100644 src/test/java/org/codelibs/fess/storage/GcsStorageClientTest.java diff --git a/pom.xml b/pom.xml index 3629efc24b..a87ea1d3a6 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 88536a882e..549b46ae8a 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 cf0c320687..0000000000 --- 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 f1891f20b2..930757415e 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 7ac0b426ce..45abc7bcb3 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 2b4dce88bc..3c7568eb32 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 a14456b3cc..97166ee3f5 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 a291e981b0..ae37d5ab22 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 73c07d4e33..c86c02adc8 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 e590e332eb..0000000000 --- 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 2d037f4bb5..7a8de6f031 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.