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; 021 022import javax.jcr.RepositoryException; 023 024import org.apache.avalon.framework.activity.Disposable; 025import org.apache.avalon.framework.activity.Initializable; 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.commons.io.IOUtils; 031import org.apache.commons.lang3.ArrayUtils; 032import org.apache.solr.client.solrj.SolrClient; 033import org.apache.solr.client.solrj.impl.HttpSolrClient; 034 035import org.ametys.plugins.repository.provider.AbstractRepository; 036import org.ametys.plugins.repository.provider.JackrabbitRepository; 037import org.ametys.plugins.repository.provider.WorkspaceSelector; 038import org.ametys.runtime.config.Config; 039import org.ametys.runtime.plugin.component.AbstractLogEnabled; 040 041/** 042 * Component acting as a single entry point to get access to Solr clients. 043 */ 044public class DefaultSolrClientProvider extends AbstractLogEnabled implements SolrClientProvider, Component, Serviceable, Initializable, Disposable 045{ 046 private static final String __SOLR_URL_CONFIG = "cms.solr.core.url"; 047 private static final String __SOLR_SOCKET_TIMEOUT_CONFIG = "cms.solr.socket.timeout"; 048 private static final String __SOLR_CORE_PREFIX_CONFIG = "cms.solr.core.prefix"; 049 050 /** The workspace selector. */ 051 protected WorkspaceSelector _workspaceSelector; 052 053 /** The JCR repository */ 054 protected JackrabbitRepository _repository; 055 056 /** The Solr read client. */ 057 protected SolrClient _solrReadClient; 058 059 /** The Solr "default" update clients, per workspace */ 060 protected Map<String, SolrClient> _solrDefaultUpdateClients; 061 062 /** The Solr "no auto commit" update clients, per workspace */ 063 protected Map<String, SolrClient> _solrNoAutoCommitUpdateClients; 064 065 /** The solr URL. */ 066 protected String _solrUrl; 067 068 /** The solr socket timeout (in millis). */ 069 protected Optional<Integer> _solrSocketTimeout; 070 071 /** The solr core prefix. */ 072 protected String _solrCorePrefix; 073 074 @Override 075 public void service(ServiceManager serviceManager) throws ServiceException 076 { 077 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 078 _repository = (JackrabbitRepository) serviceManager.lookup(AbstractRepository.ROLE); 079 } 080 081 @Override 082 public void initialize() throws Exception 083 { 084 _solrUrl = Config.getInstance().getValue(__SOLR_URL_CONFIG); 085 _solrSocketTimeout = Optional.of(__SOLR_SOCKET_TIMEOUT_CONFIG) 086 .map(Config.getInstance()::<Long>getValue) 087 .map(Long::intValue) 088 .map(s -> s * 1000); 089 _solrCorePrefix = Config.getInstance().getValue(__SOLR_CORE_PREFIX_CONFIG); 090 091 HttpSolrClient.Builder solrReadClientBuilder = new HttpSolrClient.Builder(_solrUrl); 092 if (_solrSocketTimeout.isPresent()) 093 { 094 solrReadClientBuilder.withSocketTimeout(_solrSocketTimeout.get()); 095 } 096 _solrReadClient = solrReadClientBuilder.build(); 097 098 String[] workspaces = _repository.getWorkspaces(); 099 _solrDefaultUpdateClients = new HashMap<>(); 100 _solrNoAutoCommitUpdateClients = new HashMap<>(); 101 for (String workspaceName : workspaces) 102 { 103 _solrDefaultUpdateClients.put(workspaceName, _createDefaultUpdateClient(workspaceName)); 104 _solrNoAutoCommitUpdateClients.put(workspaceName, _createNoAutoCommitUpdateClient(workspaceName)); 105 } 106 } 107 108 private SolrClient _createDefaultUpdateClient(String workspaceName) 109 { 110 AbstractAmetysConcurrentUpdateClient updateClient = new DefaultUpdateClient(_solrUrl, _solrSocketTimeout, getCollectionName(workspaceName), 10, 4, getLogger()); 111 updateClient.setPollQueueTime(10); 112 return updateClient; 113 } 114 115 private SolrClient _createNoAutoCommitUpdateClient(String workspaceName) 116 { 117 AbstractAmetysConcurrentUpdateClient updateClient = new NoAutoCommitUpdateClient(_solrUrl, _solrSocketTimeout, getCollectionName(workspaceName), 10, 4, getLogger()); 118 updateClient.setPollQueueTime(10); 119 return updateClient; 120 } 121 122 @SuppressWarnings("deprecation") 123 @Override 124 public void dispose() 125 { 126 // Release the solr clients (as a Closeable). 127 IOUtils.closeQuietly(_solrReadClient); 128 _solrReadClient = null; 129 130 for (SolrClient solrDefaultUpdateClient : _solrDefaultUpdateClients.values()) 131 { 132 IOUtils.closeQuietly(solrDefaultUpdateClient); 133 } 134 _solrDefaultUpdateClients.clear(); 135 _solrDefaultUpdateClients = null; 136 137 for (SolrClient solrNoAutoCommitUpdateClient : _solrNoAutoCommitUpdateClients.values()) 138 { 139 IOUtils.closeQuietly(solrNoAutoCommitUpdateClient); 140 } 141 _solrNoAutoCommitUpdateClients.clear(); 142 _solrNoAutoCommitUpdateClients = null; 143 } 144 145 @Override 146 public SolrClient getReadClient() 147 { 148 return _solrReadClient; 149 } 150 151 @Override 152 public SolrClient getUpdateClient(String workspaceName, boolean autoCommit) 153 { 154 Map<String, SolrClient> updateClients = autoCommit ? _solrDefaultUpdateClients : _solrNoAutoCommitUpdateClients; 155 156 SolrClient updateClient = updateClients.get(_nonNullWorkspaceName(workspaceName)); 157 if (updateClient == null) 158 { 159 // Perhaps the workspace was created after initializing this component, try to check if JCR workspace exist 160 try 161 { 162 if (ArrayUtils.contains(_repository.getWorkspaces(), workspaceName)) 163 { 164 updateClient = autoCommit ? _createDefaultUpdateClient(workspaceName) : _createNoAutoCommitUpdateClient(workspaceName); 165 updateClients.put(workspaceName, updateClient); 166 } 167 } 168 catch (RepositoryException e) 169 { 170 getLogger().error("An error occurs while trying to return all JCR workspaces", e); 171 } 172 } 173 174 return updateClient; 175 } 176 177 @Override 178 public String getCollectionName() 179 { 180 return getCollectionName(_workspaceSelector.getWorkspace()); 181 } 182 183 @Override 184 public String getCollectionName(String workspaceName) 185 { 186 return _solrCorePrefix + _nonNullWorkspaceName(workspaceName); 187 } 188 189 private String _nonNullWorkspaceName(String workspaceName) 190 { 191 if (workspaceName == null) 192 { 193 getLogger().debug("Passing null workspace name. Switching to current workspace."); 194 return _workspaceSelector.getWorkspace(); 195 } 196 else 197 { 198 return workspaceName; 199 } 200 } 201}