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 (https://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: https://www.alkacon.com
023 *
024 * For further information about OpenCms, please see the
025 * project website: https://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    public SolrConfig getSolrConfig() {
205
206        if (m_solrConfig == null) {
207            try (FileInputStream fis = new FileInputStream(getSolrConfigFile())) {
208                Path instanceDir = Paths.get(getHome(), DEFAULT_CONFIGSET_FOLDER);
209                @SuppressWarnings("resource")
210                SolrResourceLoader loader = new SolrResourceLoader(instanceDir);
211                m_solrConfig = SolrConfig.readFromResourceLoader(loader, getSolrConfigFile().getName(), null); // the former loader.getCoreProperties() yielded null anyway
212            } catch (FileNotFoundException e) {
213                CmsConfigurationException ex = new CmsConfigurationException(
214                    Messages.get().container(Messages.LOG_SOLR_ERR_CONFIG_XML_NOT_FOUND_1, getSolrConfigFile()),
215                    e);
216                LOG.error(ex.getLocalizedMessage(), ex);
217            } catch (Exception e) {
218                CmsConfigurationException ex = new CmsConfigurationException(
219                    Messages.get().container(Messages.LOG_SOLR_ERR_CONFIG_XML_NOT_READABLE_1, getSolrConfigFile()),
220                    e);
221                LOG.error(ex.getLocalizedMessage(), ex);
222            }
223        }
224        return m_solrConfig;
225    }
226
227    /**
228     * Returns the solr configuration file, default: <code>'conf/solrconfig.xml'</code>.<p>
229     *
230     * @return the solr configuration file
231     */
232    public File getSolrConfigFile() {
233
234        return new File(getHome() + DEFAULT_CONFIGSET_FOLDER + CONF_FOLDER + SolrConfig.DEFAULT_CONF_FILE);
235    }
236
237    /**
238     * Returns the Solr xml file, default: <code>'solr.xml'</code>.<p>
239     *
240     * @return the Solr xml file
241     */
242    public File getSolrFile() {
243
244        if (m_solrFile == null) {
245            String solrFileName = m_solrFileName != null ? m_solrFileName : SOLR_CONFIG_FILE;
246            m_solrFile = new File(getHome() + File.separator + solrFileName);
247        }
248        return m_solrFile;
249    }
250
251    /**
252     * Returns the Solr xml file name, default: <code>'solr.xml'</code>.<p>
253     *
254     * @return the Solr xml file name
255     */
256    public String getSolrFileName() {
257
258        return m_solrFileName;
259    }
260
261    /**
262     * Returns the Solr index schema.<p>
263     *
264     * @return the Solr index schema
265     */
266    @SuppressWarnings("resource")
267    public IndexSchema getSolrSchema() {
268
269        if (m_schema == null) {
270            try (FileInputStream fis = new FileInputStream(getSolrSchemaFile())) {
271                ConfigResource configRes = IndexSchemaFactory.getConfigResource(
272                    null /* only used if it's a CloudConfigSetService */,
273                    fis,
274                    getSolrConfig().getResourceLoader(),
275                    SOLR_CONFIG_NAME);
276                m_schema = new IndexSchema(
277                    SOLR_SCHEMA_NAME,
278                    configRes,
279                    getSolrConfig().luceneMatchVersion,
280                    getSolrConfig().getResourceLoader(),
281                    getSolrConfig().getSubstituteProperties());
282            } catch (IOException e) {
283                CmsConfigurationException ex = new CmsConfigurationException(
284                    Messages.get().container(
285                        Messages.LOG_SOLR_ERR_SCHEMA_XML_NOT_FOUND_1,
286                        getSolrSchemaFile().getPath()),
287                    e);
288                LOG.error(ex.getLocalizedMessage(), ex);
289            }
290        }
291        return m_schema;
292    }
293
294    /**
295     * Returns the Solr index schema file.<p>
296     *
297     * @return the Solr index schema file
298     */
299    public File getSolrSchemaFile() {
300
301        final String dir = getHome() + DEFAULT_CONFIGSET_FOLDER + CONF_FOLDER;
302        //SOLR7 Schema took a new name, also removed the file extension.
303        File file = new File(dir, "managed-schema");
304        if (file.exists()) {
305            return file;
306        }
307
308        //If use the old Schema.xml, it will automatically "upgrade" to a new filename.
309        file = new File(dir, IndexSchema.DEFAULT_SCHEMA_FILE);
310        return file;
311    }
312
313    /**
314     * Returns <code>true</code> if the Solr server is embedded, <code>false</code> otherwise.<p>
315     *
316     * @return <code>true</code> if the Solr server is embedded, <code>false</code> otherwise
317     */
318    public boolean isEnabled() {
319
320        return m_enabled;
321    }
322
323    /**
324     * Sets the enabled flag.<p>
325     *
326     * @param isEnabled <code>true</code>, if the Solr server should be used, <code>false</code> otherwise
327     */
328    public void setEnabled(String isEnabled) {
329
330        m_enabled = Boolean.valueOf(isEnabled).booleanValue();
331    }
332
333    /**
334     * Sets the home folder for Solr.<p>
335     *
336     * @param homeFolderPath the Solr home folder to set
337     */
338    public void setHomeFolderPath(String homeFolderPath) {
339
340        m_homeFolderPath = homeFolderPath;
341    }
342
343    /**
344     * Sets the maximal number of results processed for a query to a Solr index.<p>
345     *
346     * The globally set value can be overwritten for each index.
347     *
348     * @param maxProcessedResults the maximal number of results processed for a query to a Solr index.
349     */
350    public void setMaxProcessedResults(String maxProcessedResults) {
351
352        try {
353            m_maxProcessedResults = Integer.parseInt(maxProcessedResults);
354        } catch (Exception e) {
355            LOG.warn(
356                "Could not parse value "
357                    + maxProcessedResults
358                    + " as Integer to set the limit for the number of results a Solr index can return.");
359        }
360        if (m_maxProcessedResults <= 0) {
361            m_maxProcessedResults = DEFAULT_MAX_PROCESSED_RESULTS;
362            LOG.warn(
363                "The maximal number of results to return by a Solr index should be greater than 0. Reset it to the default value "
364                    + DEFAULT_MAX_PROCESSED_RESULTS
365                    + ".");
366        }
367    }
368
369    /**
370     * Sets the external servers URL, should be not null if the embedded falg is <code>false</code>.<p>
371     *
372     * @param url the URL
373     */
374    public void setServerUrl(String url) {
375
376        m_serverUrl = url;
377    }
378
379    /**
380     * Sets the max time (in ms) before a commit will happen.<p>
381     *
382     * @param time the time as long value
383     */
384    public void setSolrCommitMs(String time) {
385
386        m_commitMs = Long.parseLong(time);
387    }
388
389    /**
390     * Sets the Solr file name.<p>
391     *
392     * @param name the file name to set
393     */
394    public void setSolrFileName(String name) {
395
396        m_solrFileName = name;
397    }
398}