/*
* 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.stanbol.entityhub.yard.solr.impl;
import static org.apache.stanbol.entityhub.core.yard.AbstractYard.DEFAULT_QUERY_RESULT_NUMBER;
import static org.apache.stanbol.entityhub.core.yard.AbstractYard.MAX_QUERY_RESULT_NUMBER;
import static org.apache.stanbol.entityhub.servicesapi.yard.Yard.DESCRIPTION;
import static org.apache.stanbol.entityhub.servicesapi.yard.Yard.ID;
import static org.apache.stanbol.entityhub.servicesapi.yard.Yard.NAME;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.ALLOW_INITIALISATION_STATE;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.DEFAULT_ALLOW_INITIALISATION_STATE;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.DEFAULT_MAX_BOOLEAN_CLAUSES;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.DEFAULT_SOLR_INDEX_CONFIGURATION_NAME;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.MAX_BOOLEAN_CLAUSES;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.MULTI_YARD_INDEX_LAYOUT;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.SOLR_INDEX_CONFIGURATION_NAME;
import static org.apache.stanbol.entityhub.yard.solr.impl.SolrYardConfig.SOLR_SERVER_LOCATION;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.ReferenceStrategy;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.core.SolrCore;
import org.apache.stanbol.commons.namespaceprefix.NamespacePrefixService;
import org.apache.stanbol.commons.solr.IndexReference;
import org.apache.stanbol.commons.solr.RegisteredSolrServerTracker;
import org.apache.stanbol.commons.solr.managed.IndexMetadata;
import org.apache.stanbol.commons.solr.managed.ManagedIndexState;
import org.apache.stanbol.commons.solr.managed.ManagedSolrServer;
import org.apache.stanbol.entityhub.servicesapi.yard.Yard;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* OSGI Component that configures and registers the {@link SolrYard} service
* as soon as all services referenced by the Configuration are available<p>
* <b>NOTE:</b> This component uses the name <code>org.apache.stanbol.entityhub.yard.solr.impl.SolrYard</code>
* to make it backward compatible with SolrYard configurations of version
* <code>0.11.0</code>!
* @author Rupert Westenthaler
* @since 0.12.0
*
*/
@Component(
//set the name to the old class name of the SolrYard so that old
//configurations do still work
name="org.apache.stanbol.entityhub.yard.solr.impl.SolrYard",
metatype = true,
immediate = true,
configurationFactory = true,
policy = ConfigurationPolicy.REQUIRE,
specVersion = "1.1")
@Properties(
value = {
// NOTE: Added propertied from AbstractYard to fix ordering!
@Property(name = ID),
@Property(name = NAME),
@Property(name = DESCRIPTION),
@Property(name = DEFAULT_QUERY_RESULT_NUMBER, intValue = -1),
@Property(name = MAX_QUERY_RESULT_NUMBER, intValue = -1),
// BEGIN SolrYard specific Properties
@Property(name = SOLR_SERVER_LOCATION),
@Property(name = ALLOW_INITIALISATION_STATE,boolValue=DEFAULT_ALLOW_INITIALISATION_STATE),
@Property(name = SOLR_INDEX_CONFIGURATION_NAME, value=DEFAULT_SOLR_INDEX_CONFIGURATION_NAME),
@Property(name = MULTI_YARD_INDEX_LAYOUT,boolValue=false),
@Property(name = MAX_BOOLEAN_CLAUSES, intValue = DEFAULT_MAX_BOOLEAN_CLAUSES)})
public class SolrYardComponent {
private final Logger log = LoggerFactory.getLogger(SolrYardComponent.class);
private BundleContext context;
private SolrYardConfig config;
/**
* The SolrServer used for the registered SorYard
*/
private SolrServer solrServer;
/**
* The {@link ServiceRegistration} for the {@link SolrYard}
*/
private ServiceRegistration yardRegistration;
/**
* Optionally a {@link ManagedSolrServer} that is used to create new
* Solr indexes based on parsed configurations.
*/
@Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY,
bind="bindManagedSolrServer",
unbind="unbindManagedSolrServer",
strategy=ReferenceStrategy.EVENT,
policy=ReferencePolicy.DYNAMIC)
private ManagedSolrServer managedSolrServer;
private RegisteredSolrServerTracker registeredServerTracker;
protected void bindManagedSolrServer(ManagedSolrServer manager){
SolrYardConfig config = this.config;
log.info(" ... bind ManagedSolrServer '{}' to SolrYardConfig '{}'",
manager.getServerName(),config != null ? config.getId() : "<not yet activated>");
this.managedSolrServer = manager;
if(config != null){ //if activated
try {
initManagedSolrIndex(manager, config);
} catch (Exception e) {
log.error("Exception while checking SolrIndex '"+ config.getSolrServerLocation()
+"' on ManagedSolrServer '"+manager.getServerName()+"'!",e);
}
}
}
protected void unbindManagedSolrServer(ManagedSolrServer manager){
SolrYardConfig config = this.config;
log.info(" ... unbind ManagedSolrServer '{}' from SolrYard '{}'",
manager.getServerName(),config != null ? config.getId() : "<not yet activated>");
this.managedSolrServer = null;
}
/**
* Optionally a {@link NamespacePrefixService} that is set to the SolrYard
*/
@Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY,
bind="bindNamespacePrefixService",
unbind="unbindNamespacePrefixService",
strategy=ReferenceStrategy.EVENT,
policy=ReferencePolicy.DYNAMIC)
private NamespacePrefixService nsPrefixService;
private SolrYard yard;
protected void bindNamespacePrefixService(NamespacePrefixService nsPrefixService){
this.nsPrefixService = nsPrefixService;
updateSolrYardRegistration(solrServer, config);
}
protected void unbindNamespacePrefixService(NamespacePrefixService nsPrefixService){
this.nsPrefixService = null;
updateSolrYardRegistration(solrServer, config);
}
@Activate
protected void activate(ComponentContext ctx) throws ConfigurationException {
this.context = ctx.getBundleContext();
@SuppressWarnings("unchecked")
SolrYardConfig config = new SolrYardConfig((Dictionary<String,Object>) ctx.getProperties());
log.info("activate {} (name:{})",getClass().getSimpleName(),config.getId());
String indexLocation = config.getSolrServerLocation();
if(indexLocation.startsWith("http") && indexLocation.indexOf("://") > 0){
solrServer = new HttpSolrServer(indexLocation);
//directly register configs that use a remote server
updateSolrYardRegistration(solrServer, config);
} else { //locally managed Server
IndexReference solrServerRef = IndexReference.parse(config.getSolrServerLocation());
//We do not (yet) support creating SolrIndexes on ManagedSolrServers other than the
//default
if(config.isAllowInitialisation() && solrServerRef.getServer() != null){
throw new ConfigurationException(SolrYardConfig.SOLR_SERVER_LOCATION,
"The SolrServerLocation ({server-name}:{index-name}) MUST NOT define a "
+ "{server-name} if '"+SolrYardConfig.ALLOW_INITIALISATION_STATE
+ "' is enabled. Change the cofiguration to use just a {index-name}");
}
//check if we need to init the SolrIndex
initManagedSolrIndex(managedSolrServer, config);
}
//globally set the config
this.config = config;
}
@Deactivate
protected void deactivate(ComponentContext ctx){
log.info("deactivate {} (name:{})",getClass().getSimpleName(),config.getId());
if(registeredServerTracker != null){
registeredServerTracker.close();
}
updateSolrYardRegistration(null, null); //unregister
this.config = null;
this.context = null;
}
/**
* initialise ManagedSolrIndex and that starts tracking for the {@link SolrCore}
* @param managedServer the managedServer to init the SolrCore (if necessary)
* @param config the {@link SolrYardConfig}
* @throws IllegalStateException if the initialization fails
*/
private void initManagedSolrIndex(final ManagedSolrServer managedServer, final SolrYardConfig config) {
if(managedServer == null || config == null){
//this means that either no ManagedSolrServer is present or this
//component was not yet activated ... will be called again
return;
}
IndexReference solrIndexRef = IndexReference.parse(config.getSolrServerLocation());
if(config.isAllowInitialisation()){ //are we allowed to create the SolrServer
//get the name of the config to be used (default: default.solrindex.zip")
String configName = config.getIndexConfigurationName();
IndexMetadata metadata = managedServer.getIndexMetadata(solrIndexRef.getIndex());
if(metadata == null){ //create a new index
log.info(" ... creating Managed SolrIndex {} (configName: {}) on Server {}",
new Object[]{solrIndexRef.getIndex(),configName,managedServer.getServerName()});
try {
metadata = managedServer.createSolrIndex(solrIndexRef.getIndex(), configName, null);
} catch (IOException e) {
throw new IllegalStateException("Unable to create Managed SolrIndex "
+ solrIndexRef.getIndex()+" (configName: "+configName+") on Server "
+ managedServer.getServerName()+"!",e);
}
} else if(metadata.getState() != ManagedIndexState.ACTIVE){
log.info(" ... activating Managed SolrIndex {} on Server {} (current state: {})",
new Object[]{solrIndexRef.getIndex(),managedServer.getServerName(),metadata.getState()});
try {
managedServer.activateIndex(metadata.getIndexName());
} catch (IOException e) {
throw new IllegalStateException("Unable to activate Managed SolrIndex "
+ solrIndexRef.getIndex()+" (configName: "+configName+") on Server "
+ managedServer.getServerName()+"!",e);
} catch (SAXException e) {
throw new IllegalStateException("Unable to activate Managed SolrIndex "
+ solrIndexRef.getIndex()+" (configName: "+configName+") on Server "
+ managedServer.getServerName()+"!",e);
}
} //else already active nothing todo
solrIndexRef = metadata.getIndexReference();
} //else the SolrServer will be supplied (e.g. created by installing a full index)
try {
registeredServerTracker = new RegisteredSolrServerTracker(
context, solrIndexRef,null){
@Override
public void removedService(ServiceReference reference, Object service) {
updateSolrYardRegistration(registeredServerTracker.getService(),config);
super.removedService(reference, service);
}
@Override
public void modifiedService(ServiceReference reference, Object service) {
updateSolrYardRegistration(registeredServerTracker.getService(),config);
super.modifiedService(reference, service);
}
@Override
public SolrServer addingService(ServiceReference reference) {
SolrServer server = super.addingService(reference);
if(solrServer != null){
log.warn("Multiple SolrServer for IndexLocation {} available!",
config.getSolrServerLocation());
} else {
updateSolrYardRegistration(server,config);
}
return server;
}
};
log.info(" ... start tracking for SolrCore based on {}",solrIndexRef);
registeredServerTracker.open(); //start tracking
} catch (InvalidSyntaxException e) {
throw new IllegalStateException("Unable to track Managed SolrIndex "
+ solrIndexRef.getIndex() + "on Server "
+ managedServer.getServerName()+"!",e);
}
}
protected synchronized void updateSolrYardRegistration(SolrServer solrServer, SolrYardConfig config) {
if(solrServer == null || config ==null){
return; //ignore call
}
if(yardRegistration != null){
log.info(" ... unregister SolrYard (name:{})",config.getId());
yardRegistration.unregister();
yardRegistration = null;
}
if(yard != null){
yard.close();
yard = null;
}
if(solrServer != null && config != null){
log.info(" ... register SolrYard (name:{})",config.getId());
yard = new SolrYard(solrServer,config, nsPrefixService);
Dictionary<String,Object> properties = new Hashtable<String,Object>();
//copy over configuration from the component (to make it easier to filter)
for(Enumeration<String> keys = config.getDictionary().keys();keys.hasMoreElements();){
String key = keys.nextElement();
if(key.contains("stanbol.entityhub.yard") || Constants.SERVICE_RANKING.equals(key)){
properties.put(key,config.getDictionary().get(key));
}
}
yardRegistration = context.registerService(Yard.class.getName(), yard, properties);
}
}
}