001/*
002 *  Copyright 2017 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.io.IOException;
019import java.io.InputStream;
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.List;
023import java.util.Optional;
024import java.util.concurrent.TimeUnit;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.commons.lang3.Strings;
028import org.apache.solr.client.solrj.SolrServerException;
029import org.apache.solr.client.solrj.impl.ConcurrentUpdateHttp2SolrClient;
030import org.apache.solr.client.solrj.impl.Http2SolrClient;
031import org.apache.solr.client.solrj.response.UpdateResponse;
032import org.apache.solr.common.SolrException;
033import org.apache.solr.common.SolrInputDocument;
034import org.eclipse.jetty.client.api.Response;
035import org.slf4j.Logger;
036
037/**
038 * Concurrent update solr client specific for Ametys.<br>
039 * Allow operations on only one collection (specified by the constructor),
040 * and forbid operations on all other collections.
041 * Delegate the processing to the ConcurrentUpdateSolrClient (superclass) if the collection is the one in use
042 */
043public abstract class AbstractAmetysConcurrentUpdateClient extends ConcurrentUpdateHttp2SolrClient
044{
045    /** Name of the collection, on which operation are allowed */
046    private String _collectionName;
047    
048    private Logger _logger;
049    
050    /**
051     * Constructor
052     * @param solrServerUrl The Solr server URL
053     * @param login The login to authenticate on the Solr app
054     * @param password The password to authenticate on the Solr app
055     * @param solrSocketTimeout The Solr socket timeout (in millis)
056     * @param collectionName The name of the collection, on which operations will be allowed. Operations on other collections will be forbidden.
057     * @param queueSize The buffer size before the documents are sent to the server
058     * @param threadCount The number of background threads used to empty the queue
059     * @param logger internal logger
060     */
061    public AbstractAmetysConcurrentUpdateClient(String solrServerUrl, String login, String password, Optional<Integer> solrSocketTimeout, String collectionName,
062            int queueSize, int threadCount, Logger logger)
063    {
064        super(_getBuilder(solrServerUrl, login, password, solrSocketTimeout, queueSize, threadCount));
065        
066        if (collectionName == null)
067        {
068            throw new IllegalArgumentException("The collection name cannot be null.");
069        }
070        else
071        {
072            _collectionName = collectionName;
073        }
074        
075        _logger = logger;
076    }
077
078    private static Builder _getBuilder(String solrServerUrl, String login, String password, Optional<Integer> solrSocketTimeout, int queueSize, int threadCount)
079    {
080        Http2SolrClient.Builder solrReadClientBuilder = new Http2SolrClient.Builder(solrServerUrl);
081        if (solrSocketTimeout.isPresent())
082        {
083            solrReadClientBuilder.withIdleTimeout(solrSocketTimeout.get(), TimeUnit.SECONDS);
084        }
085        
086        if (StringUtils.isNotBlank(login))
087        {
088            solrReadClientBuilder.withBasicAuthCredentials(login, password);
089        }
090        
091        Http2SolrClient solrReadClient = solrReadClientBuilder.build();
092        
093        Builder builder = new Builder(solrServerUrl, solrReadClient)
094            .withQueueSize(queueSize)
095            .withThreadCount(threadCount)
096            .setPollQueueTime(10, TimeUnit.MILLISECONDS);
097        
098        return builder;
099    }
100    
101    @Override
102    public void handleError(Throwable t)
103    {
104        StringBuilder msg = new StringBuilder("Solr client indexing error");
105        if (t instanceof SolrException)
106        {
107            msg.append(" caused by the following errors: ").append(((SolrException) t).getMetadata());
108        }
109        
110        msg.append("\nPlease check the Solr logs for more details.");
111        _logger.error(msg.toString(), t);
112    }
113    
114    @Override
115    public void onSuccess(Response resp, InputStream respBody)
116    {
117        if (_logger.isDebugEnabled())
118        {
119            String reason = resp.getReason();
120            _logger.debug("Successfully processed solr client request and obtained the response : " + StringUtils.defaultIfEmpty(reason, "unknown"));
121        }
122        
123        super.onSuccess(resp, respBody);
124    }
125    
126    private void _checkCollectionInUse(String collection) throws SolrServerException
127    {
128        if (collection == null)
129        {
130            throw new UnsupportedOperationException("Collection cannot be null");
131        }
132        
133        if (!Strings.CS.equals(_collectionName, collection))
134        {
135            String msg = String.format("Cannot process this update operation for collection '%s' because operations for this client can only be done in '%s'", collection, _collectionName);
136            throw new SolrServerException(msg);
137        }
138    }
139    
140    @Override
141    public UpdateResponse add(String collection, Collection<SolrInputDocument> docs) throws SolrServerException, IOException
142    {
143        _checkCollectionInUse(collection);
144        return super.add(collection, docs);
145    }
146    
147    @Override
148    public UpdateResponse add(Collection<SolrInputDocument> docs) throws SolrServerException, IOException
149    {
150        _checkCollectionInUse(null);
151        return super.add(docs);
152    }
153    
154    @Override
155    public UpdateResponse add(String collection, Collection<SolrInputDocument> docs, int commitWithinMs) throws SolrServerException, IOException
156    {
157        _checkCollectionInUse(collection);
158        return super.add(collection, docs, commitWithinMs);
159    }
160    
161    @Override
162    public UpdateResponse add(Collection<SolrInputDocument> docs, int commitWithinMs) throws SolrServerException, IOException
163    {
164        _checkCollectionInUse(null);
165        return super.add(docs, commitWithinMs);
166    }
167    
168    @Override
169    public UpdateResponse add(String collection, SolrInputDocument doc) throws SolrServerException, IOException
170    {
171        _checkCollectionInUse(collection);
172        return super.add(collection, doc);
173    }
174    
175    @Override
176    public UpdateResponse add(SolrInputDocument doc) throws SolrServerException, IOException
177    {
178        _checkCollectionInUse(null);
179        return super.add(doc);
180    }
181    
182    @Override
183    public UpdateResponse add(String collection, SolrInputDocument doc, int commitWithinMs) throws SolrServerException, IOException
184    {
185        _checkCollectionInUse(collection);
186        return super.add(collection, doc, commitWithinMs);
187    }
188    
189    @Override
190    public UpdateResponse add(SolrInputDocument doc, int commitWithinMs) throws SolrServerException, IOException
191    {
192        _checkCollectionInUse(null);
193        return super.add(doc, commitWithinMs);
194    }
195    
196    @Override
197    public UpdateResponse add(String collection, Iterator<SolrInputDocument> docIterator) throws SolrServerException, IOException
198    {
199        _checkCollectionInUse(collection);
200        return super.add(collection, docIterator);
201    }
202    
203    @Override
204    public UpdateResponse add(Iterator<SolrInputDocument> docIterator) throws SolrServerException, IOException
205    {
206        _checkCollectionInUse(null);
207        return super.add(docIterator);
208    }
209    
210    @Override
211    public UpdateResponse addBean(String collection, Object obj) throws IOException, SolrServerException
212    {
213        _checkCollectionInUse(collection);
214        return super.addBean(collection, obj);
215    }
216    
217    @Override
218    public UpdateResponse addBean(Object obj) throws IOException, SolrServerException
219    {
220        _checkCollectionInUse(null);
221        return super.addBean(obj);
222    }
223    
224    @Override
225    public UpdateResponse addBean(String collection, Object obj, int commitWithinMs) throws IOException, SolrServerException
226    {
227        _checkCollectionInUse(collection);
228        return super.addBean(collection, obj, commitWithinMs);
229    }
230    
231    @Override
232    public UpdateResponse addBean(Object obj, int commitWithinMs) throws IOException, SolrServerException
233    {
234        _checkCollectionInUse(null);
235        return super.addBean(obj, commitWithinMs);
236    }
237    
238    @Override
239    public UpdateResponse addBeans(String collection, Collection<?> beans) throws SolrServerException, IOException
240    {
241        _checkCollectionInUse(collection);
242        return super.addBeans(collection, beans);
243    }
244    
245    @Override
246    public UpdateResponse addBeans(Collection<?> beans) throws SolrServerException, IOException
247    {
248        _checkCollectionInUse(null);
249        return super.addBeans(beans);
250    }
251    
252    @Override
253    public UpdateResponse addBeans(String collection, Collection<?> beans, int commitWithinMs) throws SolrServerException, IOException
254    {
255        _checkCollectionInUse(collection);
256        return super.addBeans(collection, beans, commitWithinMs);
257    }
258    
259    @Override
260    public UpdateResponse addBeans(Collection<?> beans, int commitWithinMs) throws SolrServerException, IOException
261    {
262        _checkCollectionInUse(null);
263        return super.addBeans(beans, commitWithinMs);
264    }
265    
266    @Override
267    public UpdateResponse addBeans(String collection, final Iterator<?> beanIterator) throws SolrServerException, IOException
268    {
269        _checkCollectionInUse(collection);
270        return super.addBeans(collection, beanIterator);
271    }
272    
273    @Override
274    public UpdateResponse addBeans(final Iterator<?> beanIterator) throws SolrServerException, IOException
275    {
276        _checkCollectionInUse(null);
277        return super.addBeans(beanIterator);
278    }
279    
280    @Override
281    public UpdateResponse commit(String collection) throws SolrServerException, IOException
282    {
283        _checkCollectionInUse(collection);
284        return super.commit(collection);
285    }
286    
287    @Override
288    public UpdateResponse commit() throws SolrServerException, IOException
289    {
290        _checkCollectionInUse(null);
291        return super.commit();
292    }
293    
294    @Override
295    public UpdateResponse commit(String collection, boolean waitFlush, boolean waitSearcher) throws SolrServerException, IOException
296    {
297        _checkCollectionInUse(collection);
298        return super.commit(collection, waitFlush, waitSearcher);
299    }
300    
301    @Override
302    public UpdateResponse commit(boolean waitFlush, boolean waitSearcher) throws SolrServerException, IOException
303    {
304        _checkCollectionInUse(null);
305        return super.commit(waitFlush, waitSearcher);
306    }
307    
308    @Override
309    public UpdateResponse commit(String collection, boolean waitFlush, boolean waitSearcher, boolean softCommit) throws SolrServerException, IOException
310    {
311        _checkCollectionInUse(collection);
312        return super.commit(collection, waitFlush, waitSearcher, softCommit);
313    }
314    
315    @Override
316    public UpdateResponse commit(boolean waitFlush, boolean waitSearcher, boolean softCommit) throws SolrServerException, IOException
317    {
318        _checkCollectionInUse(null);
319        return super.commit(waitFlush, waitSearcher, softCommit);
320    }
321    
322    @Override
323    public UpdateResponse optimize(String collection) throws SolrServerException, IOException
324    {
325        _checkCollectionInUse(collection);
326        return super.optimize(collection);
327    }
328    
329    @Override
330    public UpdateResponse optimize() throws SolrServerException, IOException
331    {
332        _checkCollectionInUse(null);
333        return super.optimize();
334    }
335    
336    @Override
337    public UpdateResponse optimize(String collection, boolean waitFlush, boolean waitSearcher) throws SolrServerException, IOException
338    {
339        _checkCollectionInUse(collection);
340        return super.optimize(collection, waitFlush, waitSearcher);
341    }
342    
343    @Override
344    public UpdateResponse optimize(boolean waitFlush, boolean waitSearcher) throws SolrServerException, IOException
345    {
346        _checkCollectionInUse(null);
347        return super.optimize(waitFlush, waitSearcher);
348    }
349    
350    @Override
351    public UpdateResponse optimize(String collection, boolean waitFlush, boolean waitSearcher, int maxSegments) throws SolrServerException, IOException
352    {
353        _checkCollectionInUse(collection);
354        return super.optimize(collection, waitFlush, waitSearcher, maxSegments);
355    }
356    
357    @Override
358    public UpdateResponse optimize(boolean waitFlush, boolean waitSearcher, int maxSegments) throws SolrServerException, IOException
359    {
360        _checkCollectionInUse(null);
361        return super.optimize(waitFlush, waitSearcher, maxSegments);
362    }
363    
364    @Override
365    public UpdateResponse rollback(String collection) throws SolrServerException, IOException
366    {
367        _checkCollectionInUse(collection);
368        return super.rollback(collection);
369    }
370    
371    @Override
372    public UpdateResponse rollback() throws SolrServerException, IOException
373    {
374        _checkCollectionInUse(null);
375        return super.rollback();
376    }
377    
378    @Override
379    public UpdateResponse deleteById(String collection, String id) throws SolrServerException, IOException
380    {
381        _checkCollectionInUse(collection);
382        return super.deleteById(collection, id);
383    }
384    
385    @Override
386    public UpdateResponse deleteById(String id) throws SolrServerException, IOException
387    {
388        _checkCollectionInUse(null);
389        return super.deleteById(id);
390    }
391    
392    @Override
393    public UpdateResponse deleteById(String collection, String id, int commitWithinMs) throws SolrServerException, IOException
394    {
395        _checkCollectionInUse(collection);
396        return super.deleteById(collection, id, commitWithinMs);
397    }
398    
399    @Override
400    public UpdateResponse deleteById(String id, int commitWithinMs) throws SolrServerException, IOException
401    {
402        _checkCollectionInUse(null);
403        return super.deleteById(id, commitWithinMs);
404    }
405    
406    @Override
407    public UpdateResponse deleteById(String collection, List<String> ids) throws SolrServerException, IOException
408    {
409        _checkCollectionInUse(collection);
410        return super.deleteById(collection, ids);
411    }
412    
413    @Override
414    public UpdateResponse deleteById(List<String> ids) throws SolrServerException, IOException
415    {
416        _checkCollectionInUse(null);
417        return super.deleteById(ids);
418    }
419    
420    @Override
421    public UpdateResponse deleteById(String collection, List<String> ids, int commitWithinMs) throws SolrServerException, IOException
422    {
423        _checkCollectionInUse(collection);
424        return super.deleteById(collection, ids, commitWithinMs);
425    }
426    
427    @Override
428    public UpdateResponse deleteById(List<String> ids, int commitWithinMs) throws SolrServerException, IOException
429    {
430        _checkCollectionInUse(null);
431        return super.deleteById(ids, commitWithinMs);
432    }
433    
434    @Override
435    public UpdateResponse deleteByQuery(String collection, String query) throws SolrServerException, IOException
436    {
437        _checkCollectionInUse(collection);
438        return super.deleteByQuery(collection, query);
439    }
440    
441    @Override
442    public UpdateResponse deleteByQuery(String query) throws SolrServerException, IOException
443    {
444        _checkCollectionInUse(null);
445        return super.deleteByQuery(query);
446    }
447    
448    @Override
449    public UpdateResponse deleteByQuery(String collection, String query, int commitWithinMs) throws SolrServerException, IOException
450    {
451        _checkCollectionInUse(collection);
452        return super.deleteByQuery(collection, query, commitWithinMs);
453    }
454    
455    @Override
456    public UpdateResponse deleteByQuery(String query, int commitWithinMs) throws SolrServerException, IOException
457    {
458        _checkCollectionInUse(null);
459        return super.deleteByQuery(query, commitWithinMs);
460    }
461}