mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2026-09-16 18:13:17 +00:00
Detect search engine by reusing the elasticsearch subsystem client
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Set root logger level to error
|
||||
rootLogger.level=error
|
||||
rootLogger.level=info
|
||||
rootLogger.appenderRef.stdout.ref=ConsoleAppender
|
||||
rootLogger.appenderRef.rolling.ref=RollingAppender
|
||||
|
||||
@@ -473,4 +473,4 @@ logger.alfresco-rest-api-probes-ProbeEntityResource.level=info
|
||||
# ActiveMQ
|
||||
|
||||
logger.apache-activemq-transport-failover.name=org.apache.activemq.transport.failover
|
||||
logger.apache-activemq-transport-failover.level=warn
|
||||
logger.apache-activemq-transport-failover.level=warn
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*-
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.rest.api.search;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
public class DetectSearchEngine extends AbstractLifecycleBean {
|
||||
private static final Log logger = LogFactory.getLog(DetectSearchEngine.class);
|
||||
private static final String[] searchEngineUrls = {
|
||||
"http://elasticsearch:9200",
|
||||
"http://elasticsearch:9200/_license"
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event) {
|
||||
findSearchEngineInfos();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShutdown(ApplicationEvent event) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
private static SearchEngineInfo searchEngineInfo(){
|
||||
// make http request to find search engine info
|
||||
return new SearchEngineInfo();
|
||||
}
|
||||
|
||||
private static void findSearchEngineInfos(){
|
||||
for(int i = 0; i < searchEngineUrls.length; i++){
|
||||
HttpResponse<String> response = makeHttpRequest(searchEngineUrls[i]);
|
||||
logger.info("Search Engine response: " + response);
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpResponse<String> makeHttpRequest(String url) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
try (HttpClient client = HttpClient.newHttpClient()){
|
||||
return client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("HTTP request to get the search engine info failed", e);
|
||||
} catch (InterruptedException e){
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("HTTP request to get the search engine info was interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*-
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.rest.api.search;
|
||||
|
||||
public class SearchEngineInfo {
|
||||
String searchEngineName;
|
||||
String searchEngineVersion;
|
||||
String searchEngineLuceneVersion;
|
||||
|
||||
public String getSearchEngineName() {
|
||||
return searchEngineName;
|
||||
}
|
||||
|
||||
public void setSearchEngineName(String searchEngineName) {
|
||||
this.searchEngineName = searchEngineName;
|
||||
}
|
||||
|
||||
public String getSearchEngineVersion() {
|
||||
return searchEngineVersion;
|
||||
}
|
||||
|
||||
public void setSearchEngineVersion(String searchEngineVersion) {
|
||||
this.searchEngineVersion = searchEngineVersion;
|
||||
}
|
||||
|
||||
public String getSearchEngineLuceneVersion() {
|
||||
return searchEngineLuceneVersion;
|
||||
}
|
||||
|
||||
public void setSearchEngineLuceneVersion(String searchEngineLuceneVersion) {
|
||||
this.searchEngineLuceneVersion = searchEngineLuceneVersion;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<bean id="detectSearchEngine" class="org.alfresco.rest.api.search.DetectSearchEngine" />
|
||||
<bean id="publicapi.webscripts.store" parent="webscripts.repoclasspathstore">
|
||||
<property name="mustExist"><value>true</value></property>
|
||||
<property name="classPath"><value>alfresco/templates/publicapi</value></property>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search;
|
||||
|
||||
/**
|
||||
* Holder for the search engine details detected when the search subsystem starts (ACS-12415).
|
||||
* <p>
|
||||
* The values are populated by {@link org.alfresco.repo.search.impl.elasticsearch.SearchEngineInfoDetector}
|
||||
* which issues a single {@code GET /} against the configured engine using the elasticsearch
|
||||
* subsystem's existing REST client. This bean is defined in the parent (repository) application
|
||||
* context and exposed under the id {@code searchEngineInfo} so that the pagination API and other
|
||||
* components can inject it directly:
|
||||
* <ul>
|
||||
* <li>{@code searchengine.name} → {@link #getSearchEngineName()} ("opensearch" or "elasticsearch")</li>
|
||||
* <li>{@code searchengine.version} → {@link #getSearchEngineVersion()} (for example "2.11.1")</li>
|
||||
* <li>{@code searchengine.lucene.version} → {@link #getSearchEngineLuceneVersion()} (for example "9.7.0")</li>
|
||||
* </ul>
|
||||
* The fields are {@code volatile} because they are written by the subsystem start-up thread and
|
||||
* read by request-processing threads.
|
||||
*/
|
||||
public class SearchEngineInfo
|
||||
{
|
||||
/** Provider name reported for an Elasticsearch backend. */
|
||||
public static final String PROVIDER_ELASTICSEARCH = "elasticsearch";
|
||||
|
||||
/** Provider name reported for an OpenSearch backend. */
|
||||
public static final String PROVIDER_OPENSEARCH = "opensearch";
|
||||
|
||||
/** Value used when a detail has not been (or could not be) determined. */
|
||||
public static final String UNKNOWN = "unknown";
|
||||
|
||||
private volatile String searchEngineName = UNKNOWN;
|
||||
private volatile String searchEngineVersion = UNKNOWN;
|
||||
private volatile String searchEngineLuceneVersion = UNKNOWN;
|
||||
|
||||
public String getSearchEngineName()
|
||||
{
|
||||
return searchEngineName;
|
||||
}
|
||||
|
||||
public void setSearchEngineName(String searchEngineName)
|
||||
{
|
||||
this.searchEngineName = searchEngineName;
|
||||
}
|
||||
|
||||
public String getSearchEngineVersion()
|
||||
{
|
||||
return searchEngineVersion;
|
||||
}
|
||||
|
||||
public void setSearchEngineVersion(String searchEngineVersion)
|
||||
{
|
||||
this.searchEngineVersion = searchEngineVersion;
|
||||
}
|
||||
|
||||
public String getSearchEngineLuceneVersion()
|
||||
{
|
||||
return searchEngineLuceneVersion;
|
||||
}
|
||||
|
||||
public void setSearchEngineLuceneVersion(String searchEngineLuceneVersion)
|
||||
{
|
||||
this.searchEngineLuceneVersion = searchEngineLuceneVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "SearchEngineInfo{name=" + searchEngineName
|
||||
+ ", version=" + searchEngineVersion
|
||||
+ ", luceneVersion=" + searchEngineLuceneVersion + "}";
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.search.impl.elasticsearch;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.alfresco.repo.search.SearchEngineInfo;
|
||||
import org.alfresco.repo.search.impl.elasticsearch.client.ElasticsearchHttpClientFactory;
|
||||
|
||||
/**
|
||||
* Detects the configured search engine (Elasticsearch vs OpenSearch) and its version when the
|
||||
* elasticsearch subsystem starts, storing the result in the shared {@link SearchEngineInfo} bean.
|
||||
* <p>
|
||||
* This replaces the previous standalone {@code DetectSearchEngine} lifecycle bean, which used a raw
|
||||
* {@link java.net.http.HttpClient} with hardcoded {@code http://elasticsearch:9200} URLs. Instead,
|
||||
* it reuses the subsystem's existing REST client ({@link ElasticsearchHttpClientFactory}, already
|
||||
* configured via {@code elasticsearch.host}, {@code elasticsearch.port}, {@code elasticsearch.baseUrl}
|
||||
* and the associated TLS / authentication properties) and issues a single {@code GET /} through
|
||||
* {@link org.opensearch.client.opensearch.OpenSearchClient#info()}.
|
||||
* <p>
|
||||
* Detection rules (from the {@code version} object in the response):
|
||||
* <ul>
|
||||
* <li>{@code version.distribution == "opensearch"} → provider is OpenSearch</li>
|
||||
* <li>{@code version.distribution} absent → provider is Elasticsearch</li>
|
||||
* <li>{@code version.number} → engine version</li>
|
||||
* <li>{@code version.lucene_version} → Lucene version</li>
|
||||
* </ul>
|
||||
* If detection fails (for example the engine is unreachable at start-up), a warning is logged and
|
||||
* safe default values are used so that subsystem start-up is never blocked.
|
||||
*/
|
||||
public class SearchEngineInfoDetector
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(SearchEngineInfoDetector.class);
|
||||
|
||||
private final ElasticsearchHttpClientFactory httpClientFactory;
|
||||
private final SearchEngineInfo searchEngineInfo;
|
||||
|
||||
public SearchEngineInfoDetector(ElasticsearchHttpClientFactory httpClientFactory, SearchEngineInfo searchEngineInfo)
|
||||
{
|
||||
this.httpClientFactory = httpClientFactory;
|
||||
this.searchEngineInfo = searchEngineInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the configured search engine and populate {@link SearchEngineInfo}. Invoked as the
|
||||
* Spring {@code init-method} when the elasticsearch subsystem context starts.
|
||||
*/
|
||||
public void detect()
|
||||
{
|
||||
try
|
||||
{
|
||||
var version = httpClientFactory.getElasticsearchClient().info().version();
|
||||
|
||||
String distribution = version.distribution();
|
||||
String provider = (distribution != null && distribution.equalsIgnoreCase(SearchEngineInfo.PROVIDER_OPENSEARCH))
|
||||
? SearchEngineInfo.PROVIDER_OPENSEARCH
|
||||
: SearchEngineInfo.PROVIDER_ELASTICSEARCH;
|
||||
|
||||
searchEngineInfo.setSearchEngineName(provider);
|
||||
searchEngineInfo.setSearchEngineVersion(defaultIfBlank(version.number()));
|
||||
searchEngineInfo.setSearchEngineLuceneVersion(defaultIfBlank(version.luceneVersion()));
|
||||
|
||||
logger.info("Detected search engine: name=" + searchEngineInfo.getSearchEngineName()
|
||||
+ ", version=" + searchEngineInfo.getSearchEngineVersion()
|
||||
+ ", luceneVersion=" + searchEngineInfo.getSearchEngineLuceneVersion());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
searchEngineInfo.setSearchEngineName(SearchEngineInfo.PROVIDER_ELASTICSEARCH);
|
||||
searchEngineInfo.setSearchEngineVersion(SearchEngineInfo.UNKNOWN);
|
||||
searchEngineInfo.setSearchEngineLuceneVersion(SearchEngineInfo.UNKNOWN);
|
||||
|
||||
logger.warn("Unable to detect the search engine via GET / using the elasticsearch subsystem client; "
|
||||
+ "defaulting to name=" + SearchEngineInfo.PROVIDER_ELASTICSEARCH
|
||||
+ ", version=" + SearchEngineInfo.UNKNOWN
|
||||
+ ". Cause: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String defaultIfBlank(String value)
|
||||
{
|
||||
return (value == null || value.isBlank()) ? SearchEngineInfo.UNKNOWN : value;
|
||||
}
|
||||
}
|
||||
@@ -1038,6 +1038,12 @@
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- ACS-12415: holder for the search engine details detected at subsystem start-up. -->
|
||||
<!-- It is populated by searchEngineInfoDetector, which lives in the elasticsearch subsystem -->
|
||||
<!-- (child) context, and is declared here in the parent context so that remote-api components -->
|
||||
<!-- (for example the pagination API) can inject it directly. -->
|
||||
<bean id="searchEngineInfo" class="org.alfresco.repo.search.SearchEngineInfo" />
|
||||
|
||||
<bean id="solr" class="org.alfresco.repo.search.impl.solr.SolrChildApplicationContextFactory" parent="abstractPropertyBackedBean">
|
||||
<property name="autoStart">
|
||||
|
||||
+11
@@ -91,6 +91,17 @@
|
||||
<constructor-arg value="${elasticsearch.createIndexIfNotExists}" />
|
||||
</bean>
|
||||
|
||||
<!-- ACS-12415: detect the search engine (Elasticsearch vs OpenSearch) and its version at -->
|
||||
<!-- subsystem start-up by reusing this subsystem's OpenSearch REST client (a single GET /). -->
|
||||
<!-- The result is stored in the parent-context searchEngineInfo bean so that other components -->
|
||||
<!-- (for example the pagination API) can inject it. -->
|
||||
<bean id="searchEngineInfoDetector"
|
||||
class="org.alfresco.repo.search.impl.elasticsearch.SearchEngineInfoDetector"
|
||||
init-method="detect">
|
||||
<constructor-arg ref="elasticsearchHttpClientFactory" />
|
||||
<constructor-arg ref="searchEngineInfo" />
|
||||
</bean>
|
||||
|
||||
<bean id="elasticsearchIndexService"
|
||||
class="org.alfresco.repo.search.impl.elasticsearch.contentmodelsync.ElasticsearchIndexService">
|
||||
<constructor-arg ref="elasticsearchHttpClientFactory" />
|
||||
|
||||
Reference in New Issue
Block a user