/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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
* (at your option) any later version.
*
* Alfresco 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
*/
package org.alfresco.repo.solr;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.httpclient.HttpClientFactory;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.quartz.CronTrigger;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
/**
* Provides an interface to the Solr admin APIs, used by the Alfresco Enterprise JMX layer.
* Also tracks whether Solr is available, sending Spring events when its availability changes.
*
* @since 4.0
*
*/
public class SOLRAdminClient implements ApplicationEventPublisherAware, DisposableBean
{
private String solrHost;
private int solrPort;
private int solrSSLPort;
private String solrUrl;
private String solrUser;
private String solrPassword;
private String solrPingCronExpression;
private String baseUrl;
private CommonsHttpSolrServer server;
private int solrConnectTimeout = 30000; // ms
private ApplicationEventPublisher applicationEventPublisher;
private SolrTracker solrTracker;
private HttpClientFactory httpClientFactory;
private Scheduler scheduler;
public SOLRAdminClient()
{
}
public void setSolrHost(String solrHost)
{
this.solrHost = solrHost;
}
public void setSolrPort(String solrPort)
{
this.solrPort = Integer.parseInt(solrPort);
}
public void setSolrsslPort(int solrSSLPort)
{
this.solrSSLPort = solrSSLPort;
}
public void setSolrUser(String solrUser)
{
this.solrUser = solrUser;
}
public void setSolrPassword(String solrPassword)
{
this.solrPassword = solrPassword;
}
public void setSolrConnectTimeout(String solrConnectTimeout)
{
this.solrConnectTimeout = Integer.parseInt(solrConnectTimeout);
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher)
{
this.applicationEventPublisher = applicationEventPublisher;
}
public void setSolrPingCronExpression(String solrPingCronExpression)
{
this.solrPingCronExpression = solrPingCronExpression;
}
public void setHttpClientFactory(HttpClientFactory httpClientFactory)
{
this.httpClientFactory = httpClientFactory;
}
public void setBaseUrl(String baseUrl)
{
this.baseUrl = baseUrl;
}
/**
* @param scheduler the scheduler to set
*/
public void setScheduler(Scheduler scheduler)
{
this.scheduler = scheduler;
}
public void init()
{
ParameterCheck.mandatory("solrHost", solrHost);
ParameterCheck.mandatory("solrPort", solrPort);
ParameterCheck.mandatory("solrUser", solrUser);
ParameterCheck.mandatory("solrPassword", solrPassword);
ParameterCheck.mandatory("solrPingCronExpression", solrPingCronExpression);
ParameterCheck.mandatory("solrConnectTimeout", solrConnectTimeout);
try
{
StringBuilder sb = new StringBuilder();
sb.append(httpClientFactory.isSSL() ? "https://" : "http://");
sb.append(solrHost);
sb.append(":");
sb.append(httpClientFactory.isSSL() ? solrSSLPort: solrPort);
sb.append(baseUrl);
this.solrUrl = sb.toString();
HttpClient httpClient = httpClientFactory.getHttpClient();
server = new CommonsHttpSolrServer(solrUrl, httpClient);
server.setParser(new XMLResponseParser());
// TODO remove credentials because we're using SSL?
Credentials defaultcreds = new UsernamePasswordCredentials(solrUser, solrPassword);
server.getHttpClient().getState().setCredentials(new AuthScope(solrHost, solrPort, AuthScope.ANY_REALM),
defaultcreds);
server.setConnectionTimeout(solrConnectTimeout);
server.setSoTimeout(20000);
this.solrTracker = new SolrTracker(scheduler);
}
catch(MalformedURLException e)
{
throw new AlfrescoRuntimeException("Cannot initialise Solr admin http client", e);
}
}
public QueryResponse basicQuery(ModifiableSolrParams params)
{
try
{
QueryResponse response = server.query(params);
return response;
}
catch(SolrServerException e)
{
return null;
}
}
public QueryResponse query(ModifiableSolrParams params) throws SolrServerException
{
try
{
QueryResponse response = server.query(params);
if(response.getStatus() != 0)
{
solrTracker.setSolrActive(false);
}
return response;
}
catch(SolrServerException e)
{
solrTracker.setSolrActive(false);
throw e;
}
}
public List getRegisteredCores()
{
return solrTracker.getRegisteredCores();
}
/**
* Tracks the availability of Solr.
*
* @since 4.0
*
*/
class SolrTracker
{
private final WriteLock writeLock;
private boolean solrActive = false;
private Scheduler scheduler = null;
private Trigger trigger;
private List cores;
SolrTracker(Scheduler scheduler)
{
this.scheduler = scheduler;
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
writeLock = lock.writeLock();
cores = new ArrayList(5);
setupTimer();
}
protected void pingSolr()
{
ModifiableSolrParams params = new ModifiableSolrParams();
params.set("qt", "/admin/cores");
params.set("action", "STATUS");
QueryResponse response = basicQuery(params);
if(response != null && response.getStatus() == 0)
{
NamedList