001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.search.solr; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Optional; 021import java.util.concurrent.TimeUnit; 022 023import javax.jcr.RepositoryException; 024 025import org.apache.avalon.framework.activity.Disposable; 026import org.apache.avalon.framework.activity.Initializable; 027import org.apache.avalon.framework.component.Component; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.io.IOUtils; 032import org.apache.commons.lang3.ArrayUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.apache.solr.client.solrj.SolrClient; 035import org.apache.solr.client.solrj.impl.Http2SolrClient; 036 037import org.ametys.plugins.repository.provider.AbstractRepository; 038import org.ametys.plugins.repository.provider.JackrabbitRepository; 039import org.ametys.plugins.repository.provider.WorkspaceSelector; 040import org.ametys.runtime.config.Config; 041import org.ametys.runtime.plugin.component.AbstractLogEnabled; 042 043/** 044 * Component acting as a single entry point to get access to Solr clients. 045 */ 046public class DefaultSolrClientProvider extends AbstractLogEnabled implements SolrClientProvider, Component, Serviceable, Initializable, Disposable 047{ 048 private static final String __SOLR_URL_CONFIG = "cms.solr.core.url"; 049 private static final String __SOLR_LOGIN_CONFIG = "cms.solr.login"; 050 private static final String __SOLR_PASSWORD_CONFIG = "cms.solr.password"; 051 private static final String __SOLR_SOCKET_TIMEOUT_CONFIG = "cms.solr.socket.timeout"; 052 private static final String __SOLR_CORE_PREFIX_CONFIG = "cms.solr.core.prefix"; 053 054 /** The workspace selector. */ 055 protected WorkspaceSelector _workspaceSelector; 056 057 /** The JCR repository */ 058 protected JackrabbitRepository _repository; 059 060 /** The Solr read client. */ 061 protected SolrClient _solrReadClient; 062 063 /** The Solr "default" update clients, per workspace */ 064 protected Map<String, SolrClient> _solrDefaultUpdateClients; 065 066 /** The Solr "no auto commit" update clients, per workspace */ 067 protected Map<String, SolrClient> _solrNoAutoCommitUpdateClients; 068 069 /** The solr URL. */ 070 protected String _solrUrl; 071 072 /** The solr socket timeout (in millis). */ 073 protected Optional<Integer> _solrSocketTimeout; 074 075 /** The solr core prefix. */ 076 protected String _solrCorePrefix; 077 078 /** The solr login **/ 079 private String _solrLogin; 080 /** The solr auth token. */ 081 private String _solrPassword; 082 083 @Override 084 public void service(ServiceManager serviceManager) throws ServiceException 085 { 086 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 087 _repository = (JackrabbitRepository) serviceManager.lookup(AbstractRepository.ROLE); 088 } 089 090 @Override 091 public void initialize() throws Exception 092 { 093 _solrUrl = Config.getInstance().getValue(__SOLR_URL_CONFIG); 094 _solrSocketTimeout = Optional.of(__SOLR_SOCKET_TIMEOUT_CONFIG) 095 .map(Config.getInstance()::<Long>getValue) 096 .map(Long::intValue); 097 _solrCorePrefix = Config.getInstance().getValue(__SOLR_CORE_PREFIX_CONFIG); 098 099 Http2SolrClient.Builder solrReadClientBuilder = new Http2SolrClient.Builder(_solrUrl); 100 if (_solrSocketTimeout.isPresent()) 101 { 102 solrReadClientBuilder.withIdleTimeout(_solrSocketTimeout.get(), TimeUnit.SECONDS); 103 } 104 105 _solrLogin = Config.getInstance().getValue(__SOLR_LOGIN_CONFIG); 106 if (StringUtils.isNotBlank(_solrLogin)) 107 { 108 _solrPassword = Config.getInstance().getValue(__SOLR_PASSWORD_CONFIG); 109 solrReadClientBuilder.withBasicAuthCredentials(_solrLogin, _solrPassword); 110 } 111 112 // "Disable" the feature entirely 113 System.setProperty("solr.cloud.client.stallTime", Integer.toString(Integer.MAX_VALUE)); 114 115 _solrReadClient = solrReadClientBuilder.build(); 116 117 String[] workspaces = _repository.getWorkspaces(); 118 _solrDefaultUpdateClients = new HashMap<>(); 119 _solrNoAutoCommitUpdateClients = new HashMap<>(); 120 for (String workspaceName : workspaces) 121 { 122 _solrDefaultUpdateClients.put(workspaceName, _createDefaultUpdateClient(workspaceName)); 123 _solrNoAutoCommitUpdateClients.put(workspaceName, _createNoAutoCommitUpdateClient(workspaceName)); 124 } 125 } 126 127 private SolrClient _createDefaultUpdateClient(String workspaceName) 128 { 129 return new DefaultUpdateClient(_solrUrl, _solrLogin, _solrPassword, _solrSocketTimeout, getCollectionName(workspaceName), 10, 4, getLogger()); 130 } 131 132 private SolrClient _createNoAutoCommitUpdateClient(String workspaceName) 133 { 134 return new NoAutoCommitUpdateClient(_solrUrl, _solrLogin, _solrPassword, _solrSocketTimeout, getCollectionName(workspaceName), 10, 4, getLogger()); 135 } 136 137 @Override 138 public void dispose() 139 { 140 // Release the solr clients (as a Closeable). 141 IOUtils.closeQuietly(_solrReadClient); 142 _solrReadClient = null; 143 144 for (SolrClient solrDefaultUpdateClient : _solrDefaultUpdateClients.values()) 145 { 146 IOUtils.closeQuietly(solrDefaultUpdateClient); 147 } 148 _solrDefaultUpdateClients.clear(); 149 _solrDefaultUpdateClients = null; 150 151 for (SolrClient solrNoAutoCommitUpdateClient : _solrNoAutoCommitUpdateClients.values()) 152 { 153 IOUtils.closeQuietly(solrNoAutoCommitUpdateClient); 154 } 155 _solrNoAutoCommitUpdateClients.clear(); 156 _solrNoAutoCommitUpdateClients = null; 157 } 158 159 @Override 160 public SolrClient getReadClient() 161 { 162 return _solrReadClient; 163 } 164 165 @Override 166 public SolrClient getUpdateClient(String workspaceName, boolean autoCommit) 167 { 168 Map<String, SolrClient> updateClients = autoCommit ? _solrDefaultUpdateClients : _solrNoAutoCommitUpdateClients; 169 170 SolrClient updateClient = updateClients.get(_nonNullWorkspaceName(workspaceName)); 171 if (updateClient == null) 172 { 173 // Perhaps the workspace was created after initializing this component, try to check if JCR workspace exist 174 try 175 { 176 if (ArrayUtils.contains(_repository.getWorkspaces(), workspaceName)) 177 { 178 updateClient = autoCommit ? _createDefaultUpdateClient(workspaceName) : _createNoAutoCommitUpdateClient(workspaceName); 179 updateClients.put(workspaceName, updateClient); 180 } 181 } 182 catch (RepositoryException e) 183 { 184 getLogger().error("An error occurs while trying to return all JCR workspaces", e); 185 } 186 } 187 188 return updateClient; 189 } 190 191 @Override 192 public String getCollectionName() 193 { 194 return getCollectionName(_workspaceSelector.getWorkspace()); 195 } 196 197 @Override 198 public String getCollectionName(String workspaceName) 199 { 200 return _solrCorePrefix + _nonNullWorkspaceName(workspaceName); 201 } 202 203 private String _nonNullWorkspaceName(String workspaceName) 204 { 205 if (workspaceName == null) 206 { 207 getLogger().debug("Passing null workspace name. Switching to current workspace."); 208 return _workspaceSelector.getWorkspace(); 209 } 210 else 211 { 212 return workspaceName; 213 } 214 } 215}