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