/*******************************************************************************
* Copyright (c) 2013 aegif.
*
* This file is part of NemakiWare.
*
* NemakiWare is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NemakiWare 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with NemakiWare.
* If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* linzhixing(https://github.com/linzhixing) - initial API and implementation
******************************************************************************/
package jp.aegif.nemaki;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import jp.aegif.nemaki.tracker.CoreTracker;
import jp.aegif.nemaki.tracker.CoreTrackerJob;
import jp.aegif.nemaki.util.CmisSessionFactory;
import jp.aegif.nemaki.util.Constant;
import jp.aegif.nemaki.util.PropertyKey;
import jp.aegif.nemaki.util.PropertyManager;
import jp.aegif.nemaki.util.StringPool;
import jp.aegif.nemaki.util.impl.PropertyManagerImpl;
import jp.aegif.nemaki.util.yaml.RepositorySetting;
import jp.aegif.nemaki.util.yaml.RepositorySettings;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
/**
* Solr core handler classs
* Called on server start up & user request via RESTful
* @author linzhixing
*
*/
public class NemakiCoreAdminHandler extends CoreAdminHandler {
Logger logger = Logger.getLogger(NemakiCoreAdminHandler.class);
ConcurrentHashMap<String, CoreTracker> trackers = new ConcurrentHashMap<String, CoreTracker>();
Scheduler scheduler = null;
public NemakiCoreAdminHandler() {
super();
}
public NemakiCoreAdminHandler(CoreContainer coreContainer) {
super(coreContainer);
PropertyManager pm = new PropertyManagerImpl(
StringPool.PROPERTIES_NAME);
String repositoryCorename = pm.readValue(PropertyKey.SOLR_CORE_MAIN);
String tokenCoreName = pm.readValue(PropertyKey.SOLR_CORE_TOKEN);
SolrServer repositoryServer = new EmbeddedSolrServer(coreContainer, repositoryCorename);
SolrServer tokenServer = new EmbeddedSolrServer(coreContainer, tokenCoreName);
SolrCore core = getCoreContainer().getCore(repositoryCorename);
CoreTracker tracker = new CoreTracker(this, core, repositoryServer, tokenServer);
logger.info("NemakiCoreAdminHandler successfully instantiated");
String jobEnabled = pm.readValue(PropertyKey.SOLR_TRACKING_CRON_ENABLED);
if("true".equals(jobEnabled)){
// Configure Job
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("ADMIN_HANDLER", this);
jobDataMap.put("TRACKER", tracker);
JobDetail job = newJob(CoreTrackerJob.class)
.withIdentity("CoreTrackerJob", "Solr")
.usingJobData(jobDataMap).build();
// Configure Trigger
// Cron expression is set in a property file
String cron = pm.readValue(PropertyKey.SOLR_TRACKING_CRON_EXPRESSION);
Trigger trigger = newTrigger().withIdentity("TrackTrigger", "Solr")
.withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
// Configure Scheduler
StdSchedulerFactory factory = new StdSchedulerFactory();
Properties properties = new Properties();
properties.setProperty("org.quartz.scheduler.instanceName",
"NemakiSolrTrackerScheduler");
properties.setProperty("org.quartz.threadPool.class",
"org.quartz.simpl.SimpleThreadPool");
properties.setProperty("org.quartz.threadPool.threadCount", "1");
properties.setProperty("org.quartz.threadPool.makeThreadsDaemons",
"true");
properties.setProperty(
"org.quartz.scheduler.makeSchedulerThreadDaemon", "true");
properties.setProperty("org.quartz.jobStore.class",
"org.quartz.simpl.RAMJobStore");
// Start quartz scheduler
try {
factory.initialize(properties);
scheduler = factory.getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
/**
* Switch actions on REST API
*
* Boolean return value is used as "doPersist" parameter,
* which relate to the persistence of action results to the core.
*/
@Override
protected void handleCustomAction(SolrQueryRequest req,
SolrQueryResponse rsp) {
SolrParams params = req.getParams();
// Get Server & Tracker info
String indexCoreName = params.get(CoreAdminParams.CORE);
String tokenCoreName = "token";
SolrServer indexServer = new EmbeddedSolrServer(coreContainer, indexCoreName);
SolrServer tokenServer = new EmbeddedSolrServer(coreContainer, tokenCoreName);
SolrCore core = getCoreContainer().getCore(indexCoreName);
CoreTracker tracker = new CoreTracker(this, core, indexServer, tokenServer);
// Stop cron when executing action
try {
if(scheduler != null) scheduler.standby();
} catch (SchedulerException e) {
e.printStackTrace();
}
// Action
doAction(rsp, tracker, params);
// Restart cron
try {
if(scheduler != null) scheduler.start();
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void doAction(SolrQueryResponse rsp, CoreTracker tracker, SolrParams params){
String action = params.get(CoreAdminParams.ACTION);
String repositoryId = params.get("repositoryId");
if (action.equalsIgnoreCase("INDEX")) {
index(rsp, tracker, params, repositoryId);
} else if (action.equalsIgnoreCase("INIT")) {
init(rsp, tracker, repositoryId);
} else if (action.equalsIgnoreCase("CHANGE_PASSWORD")) {
changePassword(rsp, tracker, repositoryId, params);
}
}
private void index(SolrQueryResponse rsp, CoreTracker tracker, SolrParams params, String repositoryId){
// Get tracking mode: FULL or DELTA
String tracking = params.get("tracking"); // tracking mode
if (tracking == null || !tracking.equals(Constant.MODE_FULL)) {
tracking = Constant.MODE_DELTA; // default to DELTA
}
// Action=INDEX: track documents(by FULL or DELTA)
if(tracking.equals(Constant.MODE_FULL)){
// Init
if(StringUtils.isBlank(repositoryId)){
tracker.initCore();
}else{
tracker.initCore(repositoryId);
}
}
// Index
if(StringUtils.isBlank(repositoryId)){
tracker.index(tracking);
}else{
tracker.index(tracking, repositoryId);
}
// TODO More info
rsp.add("Result", "Successfully tracked!");
}
private void init(SolrQueryResponse rsp, CoreTracker tracker, String repositoryId){
// Action=INIT: initialize core
if(StringUtils.isBlank(repositoryId)){
tracker.initCore();
}else{
tracker.initCore(repositoryId);
}
rsp.add("Result", "Successfully initialized!");
}
private void changePassword(SolrQueryResponse rsp, CoreTracker tracker, String repositoryId, SolrParams params){
//Validation
if(StringUtils.isEmpty(repositoryId)){
rsp.setException(new Exception("repositoryId is not set."));
return;
}
String password = params.get("password");
if(StringUtils.isEmpty(password)){
rsp.setException(new Exception("New password is not set."));
return;
}
String currentPassword = params.get("currentPassword");
if(StringUtils.isEmpty(password)){
rsp.setException(new Exception("Current password is not set."));
return;
}
//Execute
RepositorySettings settings = CmisSessionFactory.getRepositorySettings();
RepositorySetting setting = settings.get(repositoryId);
if(setting == null){
rsp.setException(new Exception("Specified repository does not exist."));
return;
}
if(!currentPassword.equals(setting.getPassword())){
rsp.setException(new Exception("Current password does not match."));
return;
}
setting.setPassword(password);
CmisSessionFactory.modifyRepositorySettings(settings);
rsp.add("Result", "Successfully password changed!");
}
/**
* @return the trackers
*/
public ConcurrentHashMap<String, CoreTracker> getTrackers() {
return trackers;
}
}