| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.jackrabbit.oak.plugins.index.solr.server; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.jackrabbit.oak.plugins.index.solr.configuration.OakSolrConfigurationDefaults; |
| import org.apache.jackrabbit.oak.plugins.index.solr.configuration.RemoteSolrServerConfiguration; |
| import org.apache.solr.client.solrj.SolrClient; |
| import org.apache.solr.client.solrj.SolrServerException; |
| import org.apache.solr.client.solrj.impl.CloudSolrClient; |
| import org.apache.solr.client.solrj.impl.ConcurrentUpdateSolrClient; |
| import org.apache.solr.client.solrj.impl.HttpSolrClient; |
| import org.apache.solr.client.solrj.request.CollectionAdminRequest; |
| import org.apache.solr.client.solrj.response.CollectionAdminResponse; |
| import org.apache.solr.client.solrj.response.SolrPingResponse; |
| import org.apache.solr.common.cloud.SolrZkClient; |
| import org.apache.solr.common.cloud.ZkStateReader; |
| import org.jetbrains.annotations.Nullable; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * {@link org.apache.jackrabbit.oak.plugins.index.solr.server.SolrServerProvider} for remote Solr installations. |
| */ |
| public class RemoteSolrServerProvider implements SolrServerProvider { |
| |
| private final Logger log = LoggerFactory.getLogger(RemoteSolrServerProvider.class); |
| |
| private final RemoteSolrServerConfiguration remoteSolrServerConfiguration; |
| |
| public RemoteSolrServerProvider(RemoteSolrServerConfiguration remoteSolrServerConfiguration) { |
| this.remoteSolrServerConfiguration = remoteSolrServerConfiguration; |
| } |
| |
| @Nullable |
| @Override |
| public SolrClient getSolrServer() throws Exception { |
| |
| SolrClient solrServer = null; |
| if (remoteSolrServerConfiguration.getSolrZkHost() != null && remoteSolrServerConfiguration.getSolrZkHost().length() > 0) { |
| try { |
| solrServer = initializeWithCloudSolrServer(); |
| } catch (Exception e) { |
| log.warn("unable to initialize SolrCloud client for {}", remoteSolrServerConfiguration.getSolrZkHost(), e); |
| } |
| } |
| if (solrServer == null && remoteSolrServerConfiguration.getSolrHttpUrls() != null && remoteSolrServerConfiguration.getSolrHttpUrls().length == 1 |
| && remoteSolrServerConfiguration.getSolrHttpUrls()[0] != null && remoteSolrServerConfiguration.getSolrHttpUrls()[0].length() > 0) { |
| try { |
| solrServer = initializeWithExistingHttpServer(); |
| } catch (Exception e1) { |
| log.warn("unable to initialize Solr HTTP client for {}", remoteSolrServerConfiguration.getSolrHttpUrls(), e1); |
| } |
| } |
| if (solrServer == null) { |
| throw new IOException("could not connect to any remote Solr server"); |
| } |
| |
| return solrServer; |
| } |
| |
| @Nullable |
| @Override |
| public SolrClient getIndexingSolrServer() throws Exception { |
| SolrClient server = getSolrServer(); |
| |
| if (server instanceof HttpSolrClient) { |
| String url = ((HttpSolrClient) server).getBaseURL(); |
| ConcurrentUpdateSolrClient concurrentUpdateSolrServer = new ConcurrentUpdateSolrClient(url, 1000, |
| Runtime.getRuntime().availableProcessors()); |
| concurrentUpdateSolrServer.setConnectionTimeout(remoteSolrServerConfiguration.getConnectionTimeout()); |
| concurrentUpdateSolrServer.setSoTimeout(remoteSolrServerConfiguration.getSocketTimeout()); |
| concurrentUpdateSolrServer.blockUntilFinished(); |
| server = concurrentUpdateSolrServer; |
| } |
| return server; |
| } |
| |
| @Nullable |
| @Override |
| public SolrClient getSearchingSolrServer() throws Exception { |
| return getSolrServer(); |
| } |
| |
| private SolrClient initializeWithExistingHttpServer() throws IOException, SolrServerException { |
| // try basic Solr HTTP client |
| HttpSolrClient httpSolrServer = new HttpSolrClient(remoteSolrServerConfiguration.getSolrHttpUrls()[0]); |
| httpSolrServer.setConnectionTimeout(remoteSolrServerConfiguration.getConnectionTimeout()); |
| httpSolrServer.setSoTimeout(remoteSolrServerConfiguration.getSocketTimeout()); |
| SolrPingResponse ping = httpSolrServer.ping(); |
| if (ping != null && 0 == ping.getStatus()) { |
| return httpSolrServer; |
| } else { |
| httpSolrServer.close(); |
| throw new IOException("the found HTTP Solr server is not alive"); |
| } |
| |
| } |
| |
| private SolrClient initializeWithCloudSolrServer() throws IOException { |
| // try SolrCloud client |
| CloudSolrClient cloudSolrServer = new CloudSolrClient(remoteSolrServerConfiguration.getSolrZkHost()); |
| cloudSolrServer.setZkConnectTimeout(remoteSolrServerConfiguration.getConnectionTimeout()); |
| cloudSolrServer.setZkClientTimeout(remoteSolrServerConfiguration.getSocketTimeout()); |
| cloudSolrServer.setIdField(OakSolrConfigurationDefaults.PATH_FIELD_NAME); |
| |
| if (connectToZK(cloudSolrServer)) { |
| log.debug("CloudSolrServer connected"); |
| cloudSolrServer.setDefaultCollection("collection1"); // workaround for first request when the needed collection may not exist |
| |
| // create specified collection if it doesn't exists |
| try { |
| createCollectionIfNeeded(cloudSolrServer); |
| } catch (Throwable t) { |
| if (log.isWarnEnabled()) { |
| log.warn("could not create the collection on {}", remoteSolrServerConfiguration.getSolrZkHost(), t); |
| } |
| } |
| cloudSolrServer.setDefaultCollection(remoteSolrServerConfiguration.getSolrCollection()); |
| |
| log.debug("waiting for CloudSolrServer to come alive"); |
| // SolrCloud may need some time to sync on collection creation (to spread it over the shards / replicas) |
| int i = 0; |
| while (i < 3) { |
| try { |
| SolrPingResponse ping = cloudSolrServer.ping(); |
| if (ping != null && 0 == ping.getStatus()) { |
| return cloudSolrServer; |
| } else { |
| cloudSolrServer.close(); |
| throw new IOException("the found SolrCloud server is not alive"); |
| } |
| } catch (Exception e) { |
| // wait a bit |
| try { |
| if (log.isDebugEnabled()) { |
| log.debug("server is not alive yet, wait a bit", e); |
| } |
| Thread.sleep(3000); |
| } catch (InterruptedException e1) { |
| // do nothing |
| } |
| } |
| i++; |
| } |
| cloudSolrServer.close(); |
| throw new IOException("the found SolrCloud server is not alive"); |
| } else { |
| cloudSolrServer.close(); |
| throw new IOException("could not connect to Zookeeper hosted at " + remoteSolrServerConfiguration.getSolrZkHost()); |
| } |
| |
| } |
| |
| private boolean connectToZK(CloudSolrClient cloudSolrServer) { |
| log.debug("connecting to {}", cloudSolrServer.getZkHost()); |
| boolean connected = false; |
| for (int i = 0; i < 3; i++) { |
| try { |
| cloudSolrServer.connect(); |
| connected = true; |
| break; |
| } catch (Exception e) { |
| log.warn("could not connect to ZK", e); |
| try { |
| Thread.sleep(3000); |
| } catch (InterruptedException e1) { |
| // do nothing |
| } |
| } |
| } |
| return connected; |
| } |
| |
| private void createCollectionIfNeeded(CloudSolrClient cloudSolrServer) throws SolrServerException { |
| String solrCollection = remoteSolrServerConfiguration.getSolrCollection(); |
| ZkStateReader zkStateReader = cloudSolrServer.getZkStateReader(); |
| SolrZkClient zkClient = zkStateReader.getZkClient(); |
| log.debug("creating {} collection if needed", solrCollection); |
| try { |
| if (zkClient.isConnected() && !zkClient.exists("/configs/" + solrCollection, true)) { |
| String solrConfDir = remoteSolrServerConfiguration.getSolrConfDir(); |
| Path dir; |
| if (solrConfDir != null && solrConfDir.length() > 0) { |
| log.info("uploading config from {}", solrConfDir); |
| dir = Paths.get(solrConfDir); |
| } else { |
| Path tempDirectory = Files.createTempDirectory("oak-solr-conf"); |
| |
| copy("schema", tempDirectory); |
| copy("solrconfig", tempDirectory); |
| |
| log.info("uploading config from {}", tempDirectory); |
| dir = tempDirectory; |
| } |
| log.debug("uploading config from {}", dir); |
| cloudSolrServer.uploadConfig(dir, solrCollection); |
| |
| log.debug("creating collection {}", solrCollection); |
| |
| CollectionAdminRequest.Create req = new CollectionAdminRequest.Create(); |
| CollectionAdminResponse response = req.setCollectionName(solrCollection) |
| .setReplicationFactor(remoteSolrServerConfiguration.getSolrReplicationFactor()) |
| .setConfigName(solrCollection) |
| .setNumShards(remoteSolrServerConfiguration.getSolrShardsNo()) |
| .process(cloudSolrServer); |
| |
| log.info("collection creation response {}", response); |
| |
| cloudSolrServer.request(req); |
| } |
| } catch (Exception e) { |
| log.warn("could not create collection {}", solrCollection); |
| throw new SolrServerException(e); |
| } |
| } |
| |
| private void copy(String name, Path tempDirectory) throws IOException { |
| InputStream inputStream = getClass().getResourceAsStream("/solr/oak/conf/" + name + ".xml"); |
| File dir = tempDirectory.toFile(); |
| File newFile = new File(dir, name+".xml"); |
| assert newFile.createNewFile(); |
| FileOutputStream outputStream = new FileOutputStream(newFile); |
| IOUtils.copy(inputStream, outputStream); |
| inputStream.close(); |
| outputStream.flush(); |
| outputStream.close(); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| // do nothing |
| } |
| } |