mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
40347: GERMAN: Translation update based on EN 40202 40348: SPANISH: Translation update based on EN 40202, fixes: ALF-15360 and ALF-15361 40353: RUSSIAN: Adds official support for Russian (locale: ru) to Alfresco. Initial translation based on EN r38926. 40361: ALF-15453: Incorrect manage permissions working for a file/folder 40362: CHINESE: renames an incorrectly named file. 40382: Fix for ALF-15491 SOLR is generating queries for lucene style cross-language support 40389: ALF-14399 : Added method removeTimeZoneIfIsAllDay to AbstractCalendarWebScript. Used to strip time zone information from all day events, was forced to handle date patterns in the same method due to legacy date format requirements. java.util.Date has been replaced with iso8061 date String in the FTL model to ensure local server time zone is not added when we have explicitly removed it. 40401: Part for for ALF-15406 Index Tracker seems not to gracefully stop upon shutdown keeping all other threads in waiting 40406: A slash was missing (the sites in the folder picker couldn't be shown) 40410: ALF-13190 and ALF-13287: both bugs related to the iCal SETPOS attribute. SETPOS (for outlook) specifies the week number with in a month. Possible values are -1 to 4. The positive values count forward while negative count backwards, -1 should represent the last week in a month. The method toDayOfWeekInMonth within CalendarReccurenceHelper was not setup to handle -1, this has been corrected. buildRecurrenceString within CalendarEntryGet was incorrectly using SETPOS to lookup day name. 40412: GERMAN: Translation updates based on EN r40357 40413: SPANISH: Translation updates based on EN r40357 40414: FRENCH: Translation updates based on EN r40357 40415: ITALIAN: Translation updates based on EN r40357 40416: JAPANESE: Translation updates based on EN r40357 40417: DUTCH: Translation updates based on EN r40357 40418: CHINESE: Translation updates based on EN r40357 40420: First half of fix for ALF-12803 - No user feedback: Cannot transformed content with password. (Failure of synchronous rule causes upload to fail with unhelpful message) - reporting error information inline when DnD upload is used - TODO: push JSON error response through the Flash Adaptor uploader component (requires swf uploader modifications) 40428: Merged V4.1 to V4.1-BUG-FIX 40349: CloudSync: fix move out (=> target delete) - add unit tests ( ALF-14655 / ALF-15011 ) 40364: Testcase for ALF-15178, which is not reproduced. 40381: ALF-15295: Cannot access folders beyond first page of document library 40393: BDE-93 cleanup notice file, especially updating outdated URLs 40419: Fix for the MLText parts of ALF-15502. 40427: Merged DEV/COMMUNITY-4.0-BUGFIX to V4.1 40425: ALF-15470: Solr distribution changes - Added scripts for regenerating keystores to repository keystore directory - Included this directory in the solr distribution zip under alf_data/keystore for easier installation to tomcat git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@40429 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
400 lines
11 KiB
Java
400 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2005-2011 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
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.Properties;
|
|
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.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.quartz.impl.StdSchedulerFactory;
|
|
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
|
|
{
|
|
private String solrHost;
|
|
private int solrPort;
|
|
private int solrSSLPort;
|
|
private String solrUrl;
|
|
private String solrUser;
|
|
private String solrPassword;
|
|
private String solrPingCronExpression;
|
|
private CommonsHttpSolrServer server;
|
|
private int solrConnectTimeout; // ms
|
|
|
|
private ApplicationEventPublisher applicationEventPublisher;
|
|
private SolrTracker solrTracker;
|
|
|
|
private HttpClientFactory httpClientFactory;
|
|
|
|
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 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("/solr");
|
|
this.solrUrl = sb.toString();
|
|
HttpClient httpClient = httpClientFactory.getHttpClient();
|
|
|
|
server = new CommonsHttpSolrServer(solrUrl, httpClient);
|
|
// 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);
|
|
|
|
this.solrTracker = new SolrTracker();
|
|
}
|
|
catch(MalformedURLException e)
|
|
{
|
|
throw new AlfrescoRuntimeException("Cannot initialise Solr admin http client", e);
|
|
}
|
|
}
|
|
|
|
public void shutdown()
|
|
{
|
|
this.solrTracker.shutdown();
|
|
}
|
|
|
|
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<String> 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<String> cores;
|
|
|
|
SolrTracker()
|
|
{
|
|
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
|
writeLock = lock.writeLock();
|
|
|
|
cores = new ArrayList<String>(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<Object> results = response.getResponse();
|
|
@SuppressWarnings("unchecked")
|
|
NamedList<Object> report = (NamedList<Object>)results.get("status");
|
|
Iterator<Map.Entry<String, Object>> coreIterator = report.iterator();
|
|
List<String> cores = new ArrayList<String>(report.size());
|
|
while(coreIterator.hasNext())
|
|
{
|
|
Map.Entry<String, Object> core = coreIterator.next();
|
|
cores.add(core.getKey());
|
|
}
|
|
|
|
registerCores(cores);
|
|
setSolrActive(true);
|
|
}
|
|
else
|
|
{
|
|
setSolrActive(false);
|
|
}
|
|
}
|
|
|
|
void setSolrActive(boolean active)
|
|
{
|
|
boolean statusChanged = false;
|
|
|
|
try
|
|
{
|
|
writeLock.lock();
|
|
try
|
|
{
|
|
if(solrActive != active)
|
|
{
|
|
solrActive = active;
|
|
statusChanged = true;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
writeLock.unlock();
|
|
}
|
|
|
|
if(statusChanged)
|
|
{
|
|
// do this outside the write lock
|
|
if(solrActive)
|
|
{
|
|
stopTimer();
|
|
applicationEventPublisher.publishEvent(new SolrActiveEvent(this));
|
|
}
|
|
else
|
|
{
|
|
startTimer();
|
|
applicationEventPublisher.publishEvent(new SolrInactiveEvent(this));
|
|
}
|
|
}
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
throw new AlfrescoRuntimeException("", e);
|
|
}
|
|
}
|
|
|
|
boolean isSolrActive()
|
|
{
|
|
return solrActive;
|
|
}
|
|
|
|
protected void setupTimer()
|
|
{
|
|
try
|
|
{
|
|
StdSchedulerFactory factory = new StdSchedulerFactory();
|
|
Properties properties = new Properties();
|
|
properties.setProperty("org.quartz.scheduler.instanceName", "SolrWatcherScheduler");
|
|
properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
|
|
properties.setProperty("org.quartz.threadPool.threadCount", "3");
|
|
properties.setProperty("org.quartz.threadPool.makeThreadsDaemons", "true");
|
|
properties.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon", "true");
|
|
properties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
|
|
factory.initialize(properties);
|
|
scheduler = factory.getScheduler();
|
|
|
|
scheduler.start();
|
|
|
|
final String jobName = "SolrWatcher";
|
|
final String jobGroup = "Solr";
|
|
|
|
// If a Quartz job already exists with this name and group then we want to replace it.
|
|
// It is not expected that this will occur during production, but it is possible during automated testing
|
|
// where application contexts could be rebuilt between test cases, leading to multiple creations of
|
|
// equivalent Quartz jobs. Quartz disallows the scheduling of multiple jobs with the same name and group.
|
|
JobDetail existingJob = scheduler.getJobDetail(jobName, jobGroup);
|
|
if (existingJob != null)
|
|
{
|
|
scheduler.deleteJob(jobName, jobGroup);
|
|
}
|
|
|
|
JobDetail job = new JobDetail(jobName, jobGroup, SOLRWatcherJob.class);
|
|
JobDataMap jobDataMap = new JobDataMap();
|
|
jobDataMap.put("SOLR_TRACKER", this);
|
|
job.setJobDataMap(jobDataMap);
|
|
|
|
trigger = new CronTrigger("SolrWatcherTrigger", jobGroup, solrPingCronExpression);
|
|
scheduler.scheduleJob(job, trigger);
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
throw new AlfrescoRuntimeException("Unable to set up SOLRTracker timer", e);
|
|
}
|
|
}
|
|
|
|
protected void startTimer() throws SchedulerException
|
|
{
|
|
scheduler.resumeTrigger(trigger.getName(), trigger.getGroup());
|
|
}
|
|
|
|
protected void stopTimer() throws SchedulerException
|
|
{
|
|
scheduler.pauseTrigger(trigger.getName(), trigger.getGroup());
|
|
}
|
|
|
|
void shutdown()
|
|
{
|
|
try
|
|
{
|
|
scheduler.shutdown();
|
|
}
|
|
catch(SchedulerException e)
|
|
{
|
|
throw new AlfrescoRuntimeException("Unable to shut down Solr Tracker cleanly", e);
|
|
}
|
|
}
|
|
|
|
void registerCores(List<String> cores)
|
|
{
|
|
writeLock.lock();
|
|
try
|
|
{
|
|
this.cores = cores;
|
|
}
|
|
finally
|
|
{
|
|
writeLock.unlock();
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
List<String> getRegisteredCores()
|
|
{
|
|
writeLock.lock();
|
|
try
|
|
{
|
|
return (cores != null ? cores : Collections.EMPTY_LIST);
|
|
}
|
|
finally
|
|
{
|
|
writeLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|