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