/** * Copyright (c) Codice Foundation * <p/> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p/> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.catalog.solr.external; import java.io.IOException; import java.util.Properties; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.solr.client.solrj.SolrServer; import org.codice.solr.factory.ConfigurationStore; import org.codice.solr.factory.SolrServerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ddf.catalog.data.ContentType; import ddf.catalog.filter.FilterAdapter; import ddf.catalog.operation.CreateRequest; import ddf.catalog.operation.CreateResponse; import ddf.catalog.operation.DeleteRequest; import ddf.catalog.operation.DeleteResponse; import ddf.catalog.operation.QueryRequest; import ddf.catalog.operation.SourceResponse; import ddf.catalog.operation.UpdateRequest; import ddf.catalog.operation.UpdateResponse; import ddf.catalog.source.CatalogProvider; import ddf.catalog.source.IngestException; import ddf.catalog.source.SourceMonitor; import ddf.catalog.source.UnsupportedQueryException; import ddf.catalog.source.solr.DynamicSchemaResolver; import ddf.catalog.source.solr.SolrCatalogProvider; import ddf.catalog.source.solr.SolrFilterDelegateFactory; import ddf.catalog.util.impl.MaskableImpl; /** * Catalog Provider that interfaces with a Standalone external (HTTP) Solr Server */ public class SolrHttpCatalogProvider extends MaskableImpl implements CatalogProvider { private static final String PING_ERROR_MESSAGE = "Solr Server ping failed."; private static final String OK_STATUS = "OK"; private static final String DESCRIBABLE_PROPERTIES_FILE = "/describable.properties"; private static final String SOLR_CATALOG_CORE_NAME = "catalog"; private static final Logger LOGGER = LoggerFactory.getLogger(SolrHttpCatalogProvider.class); private static final String SOLR_CATALOG_CONFIG_FILE = "solrcatalogconfig.xml"; private static Properties describableProperties = new Properties(); static { try { describableProperties.load(SolrHttpCatalogProvider.class .getResourceAsStream(DESCRIBABLE_PROPERTIES_FILE)); } catch (IOException e) { LOGGER.info("Did not load properties properly.", e); } } private String url = SolrServerFactory.DEFAULT_HTTPS_ADDRESS; private CatalogProvider provider = new UnconfiguredCatalogProvider(); private SolrServer server; private FilterAdapter filterAdapter; private boolean firstUse; private SolrFilterDelegateFactory solrFilterDelegateFactory; private DynamicSchemaResolver resolver; /** * Simple constructor * * @param filterAdapter * @param server - {@link SolrServer} to handle requests */ public SolrHttpCatalogProvider(FilterAdapter filterAdapter, SolrServer server, SolrFilterDelegateFactory solrFilterDelegateFactory, DynamicSchemaResolver resolver) { this.filterAdapter = filterAdapter; this.server = server; this.firstUse = true; this.solrFilterDelegateFactory = solrFilterDelegateFactory; this.resolver = resolver; } public SolrHttpCatalogProvider(FilterAdapter filterAdapter, SolrServer server, SolrFilterDelegateFactory solrFilterDelegateFactory) { this(filterAdapter, server, solrFilterDelegateFactory, null); } public SolrHttpCatalogProvider(FilterAdapter filterAdapter, SolrFilterDelegateFactory solrFilterDelegateFactory) { this(filterAdapter, null, solrFilterDelegateFactory, null); server = SolrServerFactory .getHttpSolrServer(url, SOLR_CATALOG_CORE_NAME, SOLR_CATALOG_CONFIG_FILE); } @Override public void maskId(String id) { super.maskId(id); if (provider != null && !(provider instanceof UnconfiguredCatalogProvider)) { provider.maskId(id); } } /** * Used to signal to the Solr server to commit on every transaction. Updates * the underlying ConfigurationStore so that the property is propagated * throughout the Solr Catalog Provider code * * @param forceAutoCommit */ public void setForceAutoCommit(boolean forceAutoCommit) { ConfigurationStore.getInstance().setForceAutoCommit(forceAutoCommit); } public void setDisableTextPath(boolean disableTextPath) { ConfigurationStore.getInstance().setDisableTextPath(disableTextPath); } @Override public Set<ContentType> getContentTypes() { return getProvider().getContentTypes(); } @Override public boolean isAvailable() { return getProvider().isAvailable(); } @Override public boolean isAvailable(SourceMonitor arg0) { return getProvider().isAvailable(arg0); } @Override public SourceResponse query(QueryRequest queryRequest) throws UnsupportedQueryException { return getProvider().query(queryRequest); } @Override public String getDescription() { return describableProperties.getProperty("description"); } @Override public String getOrganization() { return describableProperties.getProperty("organization"); } @Override public String getTitle() { return describableProperties.getProperty("name"); } @Override public String getVersion() { return describableProperties.getProperty("version"); } @Override public CreateResponse create(CreateRequest createRequest) throws IngestException { return getProvider().create(createRequest); } @Override public DeleteResponse delete(DeleteRequest deleteRequest) throws IngestException { return getProvider().delete(deleteRequest); } @Override public UpdateResponse update(UpdateRequest updateRequest) throws IngestException { return getProvider().update(updateRequest); } /** * Shutdown the connection to the Solr Server and releases resources. */ public void shutdown() { LOGGER.info("Releasing connection to solr server."); if (server != null) { server.shutdown(); } } public String getUrl() { return url; } /** * This method exists only as a workaround to a Aries Blueprint bug. If Blueprint is upgraded or * fixed, this method should be removed and a different update(Map properties) method should be * called directly. * * @param url */ public void setUrl(String url) { updateServer(url); } /** * Updates the configuration of the Solr Server if necessary * * @param urlValue - url to the Solr Server */ public void updateServer(String urlValue) { LOGGER.info("New url {}", urlValue); if (urlValue != null) { if (!StringUtils.equalsIgnoreCase(urlValue.trim(), url) || server == null) { url = urlValue.trim(); if (server != null) { LOGGER.info( "Shutting down the connection manager to the Solr Server and releasing allocated resources."); server.shutdown(); LOGGER.info("Shutdown complete."); } server = SolrServerFactory .getHttpSolrServer(url, SOLR_CATALOG_CORE_NAME, SOLR_CATALOG_CONFIG_FILE); firstUse = true; } } else { // sets to null url = urlValue; } } private CatalogProvider getProvider() { if (firstUse) { if (isServerUp(this.server)) { if (resolver == null) { provider = new SolrCatalogProvider(server, filterAdapter, solrFilterDelegateFactory); } else { provider = new SolrCatalogProvider(server, filterAdapter, solrFilterDelegateFactory, resolver); } provider.maskId(getId()); this.firstUse = false; return provider; } return new UnconfiguredCatalogProvider(); } return provider; } private boolean isServerUp(SolrServer solrServer) { if (solrServer == null) { return false; } try { return OK_STATUS.equals(solrServer.ping().getResponse().get("status")); } catch (Exception e) { /* * if we get any type of exception, whether declared by Solr or not, we do not want to * fail, we just want to return false */ LOGGER.warn(PING_ERROR_MESSAGE, e); } return false; } /** * This class is used to signify an unconfigured CatalogProvider instance. If a user tries to * unsuccessfully connect to a Solr Server, then a message will be displayed to check the * connection. * * @author Ashraf Barakat, Lockheed Martin * @author ddf.isgs@lmco.com */ private static class UnconfiguredCatalogProvider implements CatalogProvider { private static final String SERVER_DISCONNECTED_MESSAGE = "Solr Server is not connected. Please check the Solr Server status or url, and then retry."; @Override public Set<ContentType> getContentTypes() { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public boolean isAvailable() { return false; } @Override public boolean isAvailable(SourceMonitor arg0) { return false; } @Override public SourceResponse query(QueryRequest arg0) throws UnsupportedQueryException { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public String getDescription() { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public String getId() { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public String getOrganization() { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public String getTitle() { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public String getVersion() { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public void maskId(String arg0) { // no op } @Override public CreateResponse create(CreateRequest arg0) throws IngestException { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public DeleteResponse delete(DeleteRequest arg0) throws IngestException { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } @Override public UpdateResponse update(UpdateRequest arg0) throws IngestException { throw new IllegalArgumentException(SERVER_DISCONNECTED_MESSAGE); } } }