001/*
002 * File   : $Source$
003 * Date   : $Date$
004 * Version: $Revision$
005 *
006 * This library is part of OpenCms -
007 * the Open Source Content Management System
008 *
009 * Copyright (C) 2002 - 2009 Alkacon Software (http://www.alkacon.com)
010 *
011 * This library is free software; you can redistribute it and/or
012 * modify it under the terms of the GNU Lesser General Public
013 * License as published by the Free Software Foundation; either
014 * version 2.1 of the License, or (at your option) any later version.
015 *
016 * This library is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 * Lesser General Public License for more details.
020 *
021 * For further information about Alkacon Software, please see the
022 * company website: http://www.alkacon.com
023 *
024 * For further information about OpenCms, please see the
025 * project website: http://www.opencms.org
026 *
027 * You should have received a copy of the GNU Lesser General Public
028 * License along with this library; if not, write to the Free Software
029 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
030 */
031
032package org.opencms.search.solr;
033
034import org.opencms.configuration.CmsConfigurationException;
035import org.opencms.main.CmsLog;
036import org.opencms.main.OpenCms;
037import org.opencms.util.CmsStringUtil;
038
039import java.io.File;
040import java.io.FileInputStream;
041import java.io.FileNotFoundException;
042import java.io.IOException;
043import java.nio.file.Path;
044import java.nio.file.Paths;
045
046import org.apache.commons.logging.Log;
047import org.apache.solr.core.ConfigSetService.ConfigResource;
048import org.apache.solr.core.SolrConfig;
049import org.apache.solr.core.SolrResourceLoader;
050import org.apache.solr.schema.IndexSchema;
051import org.apache.solr.schema.IndexSchemaFactory;
052
053/**
054 * The Solr configuration class.<p>
055 *
056 * @since 8.5.0
057 */
058public class CmsSolrConfiguration {
059
060    /** The default config set path. */
061    public static final String DEFAULT_CONFIGSET_FOLDER = File.separatorChar
062        + "configsets"
063        + File.separatorChar
064        + "default";
065    /** The 'conf' folder inside the Solr home directory. */
066    public static final String CONF_FOLDER = File.separatorChar + "conf" + File.separatorChar;
067
068    /** The Solr configuration file name. */
069    public static final String SOLR_CONFIG_FILE = "solr.xml";
070
071    /** The default maximum number of results to return in a Solr search. */
072    public static final int DEFAULT_MAX_PROCESSED_RESULTS = 400;
073
074    /**
075     * The default max time in ms before a commit will happen (10 seconds by default).<p>
076     *
077     * Can be configured in 'opencms-search.xml'.<p>
078     */
079    public static final long SOLR_DEFAULT_COMMIT_MS = 10000;
080
081    /** The default name of the Solr home directory. */
082    public static final String SOLR_HOME_DEFAULT = "solr" + File.separatorChar;
083
084    /** The system property name for the Solr home directory. */
085    public static final String SOLR_HOME_PROPERTY = "solr.solr.home";
086
087    /** The Solr schema name. */
088    public static final String SOLR_SCHEMA_NAME = "OpenCms SOLR schema";
089
090    /** The Solr configuration name. */
091    public static final String SOLR_CONFIG_NAME = "OpenCms SOLR configuration";
092
093    /** The log object for this class. */
094    private static final Log LOG = CmsLog.getLog(CmsSolrConfiguration.class);
095
096    /** Max time (in ms) before a commit will happen. */
097    private long m_commitMs = SOLR_DEFAULT_COMMIT_MS;
098
099    /** Signals whether the server is enabled or disabled. */
100    private boolean m_enabled;
101
102    /** The Solr home. */
103    private String m_home;
104
105    /** The configured path to the Solr home. */
106    private String m_homeFolderPath;
107
108    /** The schema file. */
109    private IndexSchema m_schema;
110
111    /** The servers URL, must be set if embedded is false. */
112    private String m_serverUrl;
113
114    /** The Solr configuration. */
115    private SolrConfig m_solrConfig;
116
117    /** The Solr configuration file "solr.xml". */
118    private File m_solrFile;
119
120    /** The file name of the Solr configuration. */
121    private String m_solrFileName;
122
123    /** The maximal number of results to be processed in a search request to a Solr index. */
124    private int m_maxProcessedResults = DEFAULT_MAX_PROCESSED_RESULTS;
125
126    /**
127     * Default constructor.<p>
128     */
129    public CmsSolrConfiguration() {
130
131        // needed for the digester
132    }
133
134    /**
135     * Returns the home directory of Solr as String.<p>
136     *
137     * @return the home directory of Solr as String
138     */
139    public String getHome() {
140
141        if (m_homeFolderPath == null) {
142            if (CmsStringUtil.isNotEmpty(System.getProperty(SOLR_HOME_PROPERTY))) {
143                m_home = System.getProperty(SOLR_HOME_PROPERTY);
144            } else {
145                m_home = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf(SOLR_HOME_DEFAULT);
146            }
147            m_home = (m_home.endsWith(File.separator)
148            ? m_home.substring(0, m_home.lastIndexOf(File.separator))
149            : m_home);
150        } else {
151            m_home = m_homeFolderPath;
152        }
153        return m_home;
154    }
155
156    /**
157     * Returns the configured Solr home.<p>
158     *
159     * @return the configured Solr home
160     */
161    public String getHomeFolderPath() {
162
163        return m_homeFolderPath;
164    }
165
166    /**
167     * Returns the maximal number of results processed when querying a Solr index.
168     *
169     * Each index has a configuration option to overwrite this global value.
170     *
171     * @return the maximal number of results processed when querying a Solr index.
172     */
173    public int getMaxProcessedResults() {
174
175        return m_maxProcessedResults;
176    }
177
178    /**
179     * Returns the servers URL if embedded is set to <code>false</code>.<p>
180     *
181     * @return the external servers URL
182     */
183    public String getServerUrl() {
184
185        return m_serverUrl;
186    }
187
188    /**
189     * Returns the max time (in ms) before a commit will happen.<p>
190     *
191     * @return the max time (in ms) before a commit will happen
192     */
193    public long getSolrCommitMs() {
194
195        return m_commitMs;
196    }
197
198    /**
199     * Returns the Solr configuration (object representation of <code>'solrconfig.xml'</code>).<p>
200     *
201     * @return the Solr configuration
202     *
203     */
204    @SuppressWarnings("deprecation")
205    public SolrConfig getSolrConfig() {
206
207        if (m_solrConfig == null) {
208            try (FileInputStream fis = new FileInputStream(getSolrConfigFile())) {
209                Path instanceDir = Paths.get(getHome(), DEFAULT_CONFIGSET_FOLDER);
210                @SuppressWarnings("resource")
211                SolrResourceLoader loader = new SolrResourceLoader(instanceDir);
212                m_solrConfig = SolrConfig.readFromResourceLoader(
213                    loader,
214                    getSolrConfigFile().getName(),
215                    true,
216                    null); // the former loader.getCoreProperties() yielded null anyway
217            } catch (FileNotFoundException e) {
218                CmsConfigurationException ex = new CmsConfigurationException(
219                    Messages.get().container(Messages.LOG_SOLR_ERR_CONFIG_XML_NOT_FOUND_1, getSolrConfigFile()),
220                    e);
221                LOG.error(ex.getLocalizedMessage(), ex);
222            } catch (Exception e) {
223                CmsConfigurationException ex = new CmsConfigurationException(
224                    Messages.get().container(Messages.LOG_SOLR_ERR_CONFIG_XML_NOT_READABLE_1, getSolrConfigFile()),
225                    e);
226                LOG.error(ex.getLocalizedMessage(), ex);
227            }
228        }
229        return m_solrConfig;
230    }
231
232    /**
233     * Returns the solr configuration file, default: <code>'conf/solrconfig.xml'</code>.<p>
234     *
235     * @return the solr configuration file
236     */
237    public File getSolrConfigFile() {
238
239        return new File(getHome() + DEFAULT_CONFIGSET_FOLDER + CONF_FOLDER + SolrConfig.DEFAULT_CONF_FILE);
240    }
241
242    /**
243     * Returns the Solr xml file, default: <code>'solr.xml'</code>.<p>
244     *
245     * @return the Solr xml file
246     */
247    public File getSolrFile() {
248
249        if (m_solrFile == null) {
250            String solrFileName = m_solrFileName != null ? m_solrFileName : SOLR_CONFIG_FILE;
251            m_solrFile = new File(getHome() + File.separator + solrFileName);
252        }
253        return m_solrFile;
254    }
255
256    /**
257     * Returns the Solr xml file name, default: <code>'solr.xml'</code>.<p>
258     *
259     * @return the Solr xml file name
260     */
261    public String getSolrFileName() {
262
263        return m_solrFileName;
264    }
265
266    /**
267     * Returns the Solr index schema.<p>
268     *
269     * @return the Solr index schema
270     */
271    @SuppressWarnings("resource")
272    public IndexSchema getSolrSchema() {
273
274        if (m_schema == null) {
275            try (FileInputStream fis = new FileInputStream(getSolrSchemaFile())) {
276                ConfigResource configRes = IndexSchemaFactory.getConfigResource(
277                    null /* only used if it's a CloudConfigSetService */,
278                    fis,
279                    getSolrConfig().getResourceLoader(),
280                    SOLR_CONFIG_NAME);
281                m_schema = new IndexSchema(
282                    SOLR_SCHEMA_NAME,
283                    configRes,
284                    getSolrConfig().luceneMatchVersion,
285                    getSolrConfig().getResourceLoader(),
286                    getSolrConfig().getSubstituteProperties());
287            } catch (IOException e) {
288                CmsConfigurationException ex = new CmsConfigurationException(
289                    Messages.get().container(
290                        Messages.LOG_SOLR_ERR_SCHEMA_XML_NOT_FOUND_1,
291                        getSolrSchemaFile().getPath()),
292                    e);
293                LOG.error(ex.getLocalizedMessage(), ex);
294            }
295        }
296        return m_schema;
297    }
298
299    /**
300     * Returns the Solr index schema file.<p>
301     *
302     * @return the Solr index schema file
303     */
304    public File getSolrSchemaFile() {
305
306        final String dir = getHome() + DEFAULT_CONFIGSET_FOLDER + CONF_FOLDER;
307        //SOLR7 Schema took a new name, also removed the file extension.
308        File file = new File(dir, "managed-schema");
309        if (file.exists()) {
310            return file;
311        }
312
313        //If use the old Schema.xml, it will automatically "upgrade" to a new filename.
314        file = new File(dir, IndexSchema.DEFAULT_SCHEMA_FILE);
315        return file;
316    }
317
318    /**
319     * Returns <code>true</code> if the Solr server is embedded, <code>false</code> otherwise.<p>
320     *
321     * @return <code>true</code> if the Solr server is embedded, <code>false</code> otherwise
322     */
323    public boolean isEnabled() {
324
325        return m_enabled;
326    }
327
328    /**
329     * Sets the enabled flag.<p>
330     *
331     * @param isEnabled <code>true</code>, if the Solr server should be used, <code>false</code> otherwise
332     */
333    public void setEnabled(String isEnabled) {
334
335        m_enabled = Boolean.valueOf(isEnabled).booleanValue();
336    }
337
338    /**
339     * Sets the home folder for Solr.<p>
340     *
341     * @param homeFolderPath the Solr home folder to set
342     */
343    public void setHomeFolderPath(String homeFolderPath) {
344
345        m_homeFolderPath = homeFolderPath;
346    }
347
348    /**
349     * Sets the maximal number of results processed for a query to a Solr index.<p>
350     *
351     * The globally set value can be overwritten for each index.
352     *
353     * @param maxProcessedResults the maximal number of results processed for a query to a Solr index.
354     */
355    public void setMaxProcessedResults(String maxProcessedResults) {
356
357        try {
358            m_maxProcessedResults = Integer.parseInt(maxProcessedResults);
359        } catch (Exception e) {
360            LOG.warn(
361                "Could not parse value "
362                    + maxProcessedResults
363                    + " as Integer to set the limit for the number of results a Solr index can return.");
364        }
365        if (m_maxProcessedResults <= 0) {
366            m_maxProcessedResults = DEFAULT_MAX_PROCESSED_RESULTS;
367            LOG.warn(
368                "The maximal number of results to return by a Solr index should be greater than 0. Reset it to the default value "
369                    + DEFAULT_MAX_PROCESSED_RESULTS
370                    + ".");
371        }
372    }
373
374    /**
375     * Sets the external servers URL, should be not null if the embedded falg is <code>false</code>.<p>
376     *
377     * @param url the URL
378     */
379    public void setServerUrl(String url) {
380
381        m_serverUrl = url;
382    }
383
384    /**
385     * Sets the max time (in ms) before a commit will happen.<p>
386     *
387     * @param time the time as long value
388     */
389    public void setSolrCommitMs(String time) {
390
391        m_commitMs = Long.parseLong(time);
392    }
393
394    /**
395     * Sets the Solr file name.<p>
396     *
397     * @param name the file name to set
398     */
399    public void setSolrFileName(String name) {
400
401        m_solrFileName = name;
402    }
403}